summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/l3kernel/l3fp-random.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3fp-random.dtx')
-rw-r--r--Master/texmf-dist/source/latex/l3kernel/l3fp-random.dtx355
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}