diff options
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3fp-random.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/l3kernel/l3fp-random.dtx | 355 |
1 files changed, 221 insertions, 134 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3fp-random.dtx b/Master/texmf-dist/source/latex/l3kernel/l3fp-random.dtx index 565c61dbcc8..20c90ce692d 100644 --- a/Master/texmf-dist/source/latex/l3kernel/l3fp-random.dtx +++ b/Master/texmf-dist/source/latex/l3kernel/l3fp-random.dtx @@ -38,7 +38,7 @@ % {latex-team@latex-project.org}^^A % }^^A % } -% \date{Released 2018/03/05} +% \date{Released 2018-04-30} % % \maketitle % @@ -71,10 +71,9 @@ % % \subsection{Engine support} % -% At present, \XeTeX{}, \pTeX{} and \upTeX{} do not provide random -% numbers, while \LuaTeX{} and \pdfTeX{} provide the primitive -% \cs{pdftex_uniformdeviate:D} (\tn{pdfuniformdeviate} in \pdfTeX{} and -% \tn{uniformdeviate} in \LuaTeX{}). We write the test twice simply in +% At present, \XeTeX{} does not provide random +% numbers, while \pdfTeX{}, \LuaTeX{}, \pTeX{} and \upTeX{} provide the primitive +% \cs{pdftex_uniformdeviate:D}. We write the test twice simply in % order to write the \texttt{false} branch first. % \begin{macrocode} \cs_if_exist:NF \pdftex_uniformdeviate:D @@ -87,6 +86,11 @@ \exp_after:wN \c_nan_fp } \cs_new_eq:NN \@@_randint_o:Nw \@@_rand_o:Nw + \cs_new:Npn \int_rand:nn #1#2 + { + \__kernel_msg_expandable_error:nn { kernel } { fp-no-random } + \int_eval:n {#1} + } } \cs_if_exist:NT \pdftex_uniformdeviate:D { @@ -109,11 +113,11 @@ % of \cs{int_rand:nn}. We also use variants of this number % rounded down to multiples of $10^4$ and $10^8$. % \begin{macrocode} -\cs_new:Npn \@@_rand_uniform: - { \pdftex_uniformdeviate:D \c_@@_rand_size_int } -\int_const:Nn \c_@@_rand_size_int { 268 435 456 } -\int_const:Nn \c_@@_rand_four_int { 268 430 000 } -\int_const:Nn \c_@@_rand_eight_int { 200 000 000 } + \cs_new:Npn \@@_rand_uniform: + { \pdftex_uniformdeviate:D \c_@@_rand_size_int } + \int_const:Nn \c_@@_rand_size_int { 268 435 456 } + \int_const:Nn \c_@@_rand_four_int { 268 430 000 } + \int_const:Nn \c_@@_rand_eight_int { 200 000 000 } % \end{macrocode} % \end{variable} % \end{macro} @@ -137,50 +141,50 @@ % characters from the input stream with \cs{use_i:nnn}, \cs{use_i:nn} % or nothing. % \begin{macrocode} -\cs_new:Npn \@@_rand_myriads:n #1 - { - \@@_rand_myriads_loop:nn #1 - { ? \use_i_delimit_by_q_stop:nw \@@_rand_myriads_last: } - { ? \use_none_delimit_by_q_stop:w } \q_stop - } -\cs_new:Npn \@@_rand_myriads_loop:nn #1#2 - { - \use_none:n #2 - \exp_after:wN \@@_rand_myriads_get:w - \__int_value:w \@@_rand_uniform: ; {#1}{#2} - } -\cs_new:Npn \@@_rand_myriads_get:w #1 ; - { - \if_int_compare:w #1 < \c_@@_rand_eight_int - \exp_after:wN \use_none:n - \__int_value:w \__int_eval:w - \c_@@_rand_eight_int + #1 \__int_eval_end: - \exp_after:wN \use_i:nnn - \else: - \if_int_compare:w #1 < \c_@@_rand_four_int - \exp_after:wN \use_none:nnnnn - \__int_value:w \__int_eval:w - \c_@@_rand_four_int + #1 \__int_eval_end: - \exp_after:wN \exp_after:wN \exp_after:wN \use_i:nn - \fi: - \fi: - \@@_rand_myriads_loop:nn - } -\cs_new:Npn \@@_rand_myriads_last: - { - \exp_after:wN \@@_rand_myriads_last:w - \__int_value:w \@@_rand_uniform: ; - } -\cs_new:Npn \@@_rand_myriads_last:w #1 ; - { - \if_int_compare:w #1 < \c_@@_rand_four_int - \exp_after:wN \use_none:nnnnn - \__int_value:w \__int_eval:w - \c_@@_rand_four_int + #1 \__int_eval_end: - \else: - \exp_after:wN \@@_rand_myriads_last: - \fi: - } + \cs_new:Npn \@@_rand_myriads:n #1 + { + \@@_rand_myriads_loop:nn #1 + { ? \use_i_delimit_by_q_stop:nw \@@_rand_myriads_last: } + { ? \use_none_delimit_by_q_stop:w } \q_stop + } + \cs_new:Npn \@@_rand_myriads_loop:nn #1#2 + { + \use_none:n #2 + \exp_after:wN \@@_rand_myriads_get:w + \int_value:w \@@_rand_uniform: ; {#1}{#2} + } + \cs_new:Npn \@@_rand_myriads_get:w #1 ; + { + \if_int_compare:w #1 < \c_@@_rand_eight_int + \exp_after:wN \use_none:n + \int_value:w \@@_int_eval:w + \c_@@_rand_eight_int + #1 \@@_int_eval_end: + \exp_after:wN \use_i:nnn + \else: + \if_int_compare:w #1 < \c_@@_rand_four_int + \exp_after:wN \use_none:nnnnn + \int_value:w \@@_int_eval:w + \c_@@_rand_four_int + #1 \@@_int_eval_end: + \exp_after:wN \exp_after:wN \exp_after:wN \use_i:nn + \fi: + \fi: + \@@_rand_myriads_loop:nn + } + \cs_new:Npn \@@_rand_myriads_last: + { + \exp_after:wN \@@_rand_myriads_last:w + \int_value:w \@@_rand_uniform: ; + } + \cs_new:Npn \@@_rand_myriads_last:w #1 ; + { + \if_int_compare:w #1 < \c_@@_rand_four_int + \exp_after:wN \use_none:nnnnn + \int_value:w \@@_int_eval:w + \c_@@_rand_four_int + #1 \@@_int_eval_end: + \else: + \exp_after:wN \@@_rand_myriads_last: + \fi: + } % \end{macrocode} % \end{macro} % \end{macro} @@ -192,18 +196,18 @@ % First we check that |random| was called without argument. Then get % four blocks of four digits. % \begin{macrocode} -\cs_new:Npn \@@_rand_o:Nw ? #1 @ - { - \tl_if_empty:nTF {#1} - { \@@_rand_o: } + \cs_new:Npn \@@_rand_o:Nw ? #1 @ { - \__kernel_msg_expandable_error:nnnnn - { kernel } { fp-num-args } { rand() } { 0 } { 0 } - \exp_after:wN \c_nan_fp + \tl_if_empty:nTF {#1} + { \@@_rand_o: } + { + \__kernel_msg_expandable_error:nnnnn + { kernel } { fp-num-args } { rand() } { 0 } { 0 } + \exp_after:wN \c_nan_fp + } } - } -\cs_new:Npn \@@_rand_o: - { \@@_parse_o:n { . \@@_rand_myriads:n { xxxx } } } + \cs_new:Npn \@@_rand_o: + { \@@_parse_o:n { . \@@_rand_myriads:n { xxxx } } } % \end{macrocode} % \end{macro} % \end{macro} @@ -244,89 +248,172 @@ % bound. The result is compared to the upper bound and the process % repeats if needed. % \begin{macrocode} -\cs_new:Npn \@@_randint_o:Nw ? - { - \@@_parse_function_one_two:nnw - { randint } - { \@@_randint_default:w \@@_randint_o:w } - } -\cs_new:Npn \@@_randint_default:w #1 { \exp_after:wN #1 \c_one_fp } -\cs_new:Npn \@@_randint_badarg:w \s_@@ \@@_chk:w #1#2#3; - { - \@@_int:wTF \s_@@ \@@_chk:w #1#2#3; + \cs_new:Npn \@@_randint_o:Nw ? { - \if_meaning:w 1 #1 - \if_int_compare:w - \use_i_delimit_by_q_stop:nw #3 \q_stop > \c_@@_prec_int - 1 \exp_stop_f: - \fi: + \@@_parse_function_one_two:nnw + { randint } + { \@@_randint_default:w \@@_randint_o:w } + } + \cs_new:Npn \@@_randint_default:w #1 { \exp_after:wN #1 \c_one_fp } + \cs_new:Npn \@@_randint_badarg:w \s_@@ \@@_chk:w #1#2#3; + { + \@@_int:wTF \s_@@ \@@_chk:w #1#2#3; + { + \if_meaning:w 1 #1 + \if_int_compare:w + \use_i_delimit_by_q_stop:nw #3 \q_stop > \c_@@_prec_int + 1 \exp_stop_f: + \fi: + \fi: + } + { 1 \exp_stop_f: } + } + \cs_new:Npn \@@_randint_o:w #1; #2; @ + { + \if_case:w + \@@_randint_badarg:w #1; + \@@_randint_badarg:w #2; + \fp_compare:nNnTF { #1; } > { #2; } { 1 } { 0 } \exp_stop_f: + \exp_after:wN \exp_after:wN \exp_after:wN \@@_randint_e:wnn + \@@_parse:n { #2; - #1; } { #1; } { #2; } + \or: + \@@_invalid_operation_tl_o:ff + { randint } { \@@_array_to_clist:n { #1; #2; } } + \exp:w \fi: + \exp_after:wN \exp_end: } - { 1 \exp_stop_f: } - } -\cs_new:Npn \@@_randint_o:w #1; #2; @ - { - \if_case:w - \@@_randint_badarg:w #1; - \@@_randint_badarg:w #2; - \fp_compare:nNnTF { #1; } > { #2; } { 1 } { 0 } \exp_stop_f: - \exp_after:wN \exp_after:wN \exp_after:wN \@@_randint_e:wnn - \@@_parse:n { #2; - #1; } { #1; } { #2; } - \or: - \@@_invalid_operation_tl_o:ff - { randint } { \@@_array_to_clist:n { #1; #2; } } - \exp:w - \fi: - \exp_after:wN \exp_end: - } -\cs_new:Npn \@@_randint_e:wnn #1; - { - \exp_after:wN \@@_randint_e:wwNnn - \__int_value:w \@@_rand_uniform: \exp_after:wN ; - \exp:w \exp_end_continue_f:w - \fp_compare:nNnTF { #1 ; } < \c_@@_rand_size_int - { \fp_to_int:n { #1 ; + 1 } ; \@@_randint_narrow_e:nnnn } - { \fp_to_int:n { floor(#1 ; * 1e-8 + 1) } ; \@@_randint_wide_e:nnnn } - } -\cs_new:Npn \@@_randint_e:wwNnn #1 ; #2 ; - { - \exp_after:wN \@@_randint_e:wwwNnn - \__int_value:w \int_mod:nn {#1} {#2} ; #1 ; #2 ; - } -\cs_new:Npn \@@_randint_e:wwwNnn #1 ; #2 ; #3 ; #4 - { - \int_compare:nNnTF { #2 - #1 + #3 } > \c_@@_rand_size_int + \cs_new:Npn \@@_randint_e:wnn #1; { \exp_after:wN \@@_randint_e:wwNnn - \__int_value:w \@@_rand_uniform: ; #3 ; #4 + \int_value:w \@@_rand_uniform: \exp_after:wN ; + \exp:w \exp_end_continue_f:w + \fp_compare:nNnTF { #1 ; } < \c_@@_rand_size_int + { \fp_to_int:n { #1 ; + 1 } ; \@@_randint_narrow_e:nnnn } + { + \fp_to_int:n { floor(#1 ; * 1e-8 + 1) } ; + \@@_randint_wide_e:nnnn + } } - { #4 {#1} {#3} } - } -\cs_new:Npn \@@_randint_narrow_e:nnnn #1#2#3#4 - { \@@_parse_o:n { #3 + #1 } \exp:w } -\cs_new:Npn \@@_randint_wide_e:nnnn #1#2#3#4 - { - \exp_after:wN \exp_after:wN - \exp_after:wN \@@_randint_wide_e:wnnn - \@@_parse:n { #3 + #1e8 + \@@_rand_myriads:n { xx } } - {#2} {#3} {#4} - } -\cs_new:Npn \@@_randint_wide_e:wnnn #1 ; #2#3#4 - { - \fp_compare:nNnTF { #1 ; } > {#4} + \cs_new:Npn \@@_randint_e:wwNnn #1 ; #2 ; { - \exp_after:wN \@@_randint_e:wwNnn - \__int_value:w \@@_rand_uniform: ; #2 ; - \@@_randint_wide_e:nnnn {#3} {#4} + \exp_after:wN \@@_randint_e:wwwNnn + \int_value:w \int_mod:nn {#1} {#2} ; #1 ; #2 ; + } + \cs_new:Npn \@@_randint_e:wwwNnn #1 ; #2 ; #3 ; #4 + { + \int_compare:nNnTF { #2 - #1 + #3 } > \c_@@_rand_size_int + { + \exp_after:wN \@@_randint_e:wwNnn + \int_value:w \@@_rand_uniform: ; #3 ; #4 + } + { #4 {#1} {#3} } + } + \cs_new:Npn \@@_randint_narrow_e:nnnn #1#2#3#4 + { \@@_parse_o:n { #3 + #1 } \exp:w } + \cs_new:Npn \@@_randint_wide_e:nnnn #1#2#3#4 + { + \exp_after:wN \exp_after:wN + \exp_after:wN \@@_randint_wide_e:wnnn + \@@_parse:n { #3 + #1e8 + \@@_rand_myriads:n { xx } } + {#2} {#3} {#4} + } + \cs_new:Npn \@@_randint_wide_e:wnnn #1 ; #2#3#4 + { + \fp_compare:nNnTF { #1 ; } > {#4} + { + \exp_after:wN \@@_randint_e:wwNnn + \int_value:w \@@_rand_uniform: ; #2 ; + \@@_randint_wide_e:nnnn {#3} {#4} + } + { \@@_exp_after_o:w #1 ; \exp:w } } - { \@@_exp_after_o:w #1 ; \exp:w } - } % \end{macrocode} % \end{macro} % \end{macro} % +% \begin{macro}{\int_rand:nn} +% \begin{macro}{\@@_randint:ww} +% \begin{macro}{\@@_randint_narrow:n} +% \begin{macro}{\@@_randint_narrow:nn} +% \begin{macro}{\@@_randint_narrow:nnn} +% Evaluate the argument and filter out the case where the lower +% bound~|#1| is more than the upper bound~|#2|. Then determine +% whether the range is narrower than \cs{c_@@_rand_size_int}; |#2-#1| +% may overflow for very large positive~|#2| and negative~|#1|. If the +% range is wide, use slower code from \pkg{l3fp}. If the range is +% narrow, call \cs{@@_randint_narrow:n} \Arg{choices} where +% \meta{choices} is the number of possible outcomes. +% +% One option then would be to reduce a random number modulo +% \meta{choices}, but the low-order bits of random numbers provided by +% the primitive have poor randomness: empirical tests indicate that +% taking random numbers modulo~$2$ gives only two possible sequences +% of bits depending on the seed's parity. Instead, fit in the output +% range $[0,\cs{c_@@_rand_size_int}-1]$ of the random number generator +% \meta{choices} equal-size intervals of maximum possible size +% (obtained using \cs{int_div_truncate:nn}). Given a random number, +% divide it by the size to determine which interval it falls into; if +% it is beyond \meta{choices}, try another random number. +% \begin{macrocode} + \cs_new:Npn \int_rand:nn #1#2 + { + \exp_after:wN \@@_randint:ww + \int_value:w \int_eval:n {#1} \exp_after:wN ; + \int_value:w \int_eval:n {#2} ; + } + \cs_new:Npn \@@_randint:ww #1; #2; + { + \int_compare:nNnTF {#1} > {#2} + { + \__kernel_msg_expandable_error:nnnn + { kernel } { randint-backward-range } {#1} {#2} + \@@_randint:ww #2; #1; + } + { + \int_compare:nNnTF {#1} > 0 + { \int_compare:nNnTF { #2 - #1 } < \c_@@_rand_size_int } + { \int_compare:nNnTF {#2} < { #1 + \c_@@_rand_size_int } } + { + \int_eval:n + { + #1 + + \exp_args:Nf \@@_randint_narrow:n + { \int_eval:n { #2 - #1 + 1 } } + } + } + { \fp_to_int:n { randint(#1,#2) } } + } + } + \cs_new:Npn \@@_randint_narrow:n #1 + { + \exp_args:Nf \@@_randint_narrow:nn + { \int_div_truncate:nn \c_@@_rand_size_int {#1} } {#1} + } + \cs_new:Npn \@@_randint_narrow:nn #1 + { + \exp_args:Nf \@@_randint_narrow:nnn + { + \int_div_truncate:nn + { \pdftex_uniformdeviate:D \c_@@_rand_size_int } {#1} + } + {#1} + } + \cs_new:Npn \@@_randint_narrow:nnn #1#2#3 + { + \int_compare:nNnTF {#1} < {#3} + {#1} + { \@@_randint_narrow:nn {#2} {#3} } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% % End the initial conditional that ensures these commands are only -% defined in \pdfTeX{} and \LuaTeX{}. +% defined in engines that support random numbers. % \begin{macrocode} } % \end{macrocode} |