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.dtx331
1 files changed, 202 insertions, 129 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3fp-random.dtx b/Master/texmf-dist/source/latex/l3kernel/l3fp-random.dtx
index e2708e193fc..a8e3f199dba 100644
--- a/Master/texmf-dist/source/latex/l3kernel/l3fp-random.dtx
+++ b/Master/texmf-dist/source/latex/l3kernel/l3fp-random.dtx
@@ -1,42 +1,28 @@
% \iffalse meta-comment
%
%% File: l3fp-random.dtx Copyright (C) 2016 The LaTeX3 Project
-%%
-%% It may be distributed and/or modified under the conditions of the
-%% LaTeX Project Public License (LPPL), either version 1.3c of this
-%% license or (at your option) any later version. The latest version
-%% of this license is in the file
-%%
-%% http://www.latex-project.org/lppl.txt
-%%
-%% This file is part of the "l3kernel bundle" (The Work in LPPL)
-%% and all files in that bundle must be distributed together.
-%%
-%% The released version of this bundle is available from CTAN.
-%%
-%% -----------------------------------------------------------------------
-%%
-%% The development version of the bundle can be found at
-%%
-%% http://www.latex-project.org/svnroot/experimental/trunk/
-%%
-%% for those people who are interested.
-%%
-%%%%%%%%%%%
-%% NOTE: %%
-%%%%%%%%%%%
-%%
-%% Snapshots taken from the repository represent work in progress and may
-%% not work or may contain conflicting material! We therefore ask
-%% people _not_ to put them into distributions, archives, etc. without
-%% prior consultation with the LaTeX Project Team.
-%%
-%% -----------------------------------------------------------------------
-%%
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% http://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
%
%<*driver>
\documentclass[full]{l3doc}
-\GetIdInfo$Id: l3fp-random.dtx 6750 2016-11-21 01:24:26Z bruno $
+\GetIdInfo$Id: l3fp-random.dtx 6805 2016-12-28 22:15:52Z joseph $
{L3 Floating-point random numbers}
\begin{document}
\DocInput{\jobname.dtx}
@@ -76,49 +62,141 @@
%<@@=fp>
% \end{macrocode}
%
-% \subsection{Random floating point}
+% \subsection{Engine support}
%
-% \begin{macro}[EXP, int]{\@@_rand_o:Nw}
-% \begin{macro}[EXP, aux]{\@@_rand_o:}
-% Check that |random| was called without argument. Random numbers are
-% only available if the engine defines a primitive equivalent to
-% \pdfTeX{}'s \tn{pdfuniformdeviate}. A random floating point number
-% in $[0,1)$, uniformly distributed with equal-spaced steps of
-% $10^{-16}$ is obtained by combining two integers in $[0,10^8-1]$.
+% 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
+% order to write the \texttt{false} branch first.
% \begin{macrocode}
-\cs_new:Npn \@@_rand_o:Nw ? #1 @
+\cs_if_exist:NF \pdftex_uniformdeviate:D
{
- \tl_if_empty:nTF {#1}
- { \@@_rand_o: }
+ \__msg_kernel_new:nnn { kernel } { fp-no-random }
+ { Random~numbers~unavailable }
+ \cs_new:Npn \@@_rand_o:Nw ? #1 @
{
- \__msg_kernel_expandable_error:nnnnn
- { kernel } { fp-num-args } { rand() } { 0 } { 0 }
+ \__msg_kernel_expandable_error:nn { kernel } { fp-no-random }
\exp_after:wN \c_nan_fp
}
+ \cs_new_eq:NN \@@_randint_o:Nw \@@_rand_o:Nw
}
-\cs_if_exist:NTF \pdftex_uniformdeviate:D
+\cs_if_exist:NT \pdftex_uniformdeviate:D
{
- \cs_new:Npn \@@_rand_o:
- {
- \@@_parse_o:n
- {
- .
- \exp_after:wN \use_none:n
- \__int_value:w \__int_eval:w 1 0000 0000 +
- \pdftex_uniformdeviate:D 1 0000 0000 \__int_eval_end:
- \exp_after:wN \use_none:n
- \__int_value:w \__int_eval:w 1 0000 0000 +
- \pdftex_uniformdeviate:D 1 0000 0000 \__int_eval_end:
- }
- }
+% \end{macrocode}
+%
+% \begin{macro}[EXP, int]{\@@_rand_uniform:}
+% \begin{variable}
+% {
+% \c_@@_rand_size_int,
+% \c_@@_rand_four_int,
+% \c_@@_rand_eight_int,
+% }
+% The \cs{pdftex_uniformdeviate:D} primitive gives a pseudo-random
+% integer in a range $[0,n-1]$ of the user's choice. This number is
+% meant to be uniformly distributed, but is produced by rescaling a
+% uniform pseudo-random integer in $[0,2^{28}-1]$. For instance,
+% setting~$n$ to (any multiple of) $2^{29}$ gives only even values.
+% Thus it is only safe to call \cs{pdftex_uniformdeviate:D} with
+% argument $2^{28}$. This integer is also used in the implementation
+% of \cs{int_rand:nn}. We will 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 }
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+%
+% \begin{macro}[EXP, int]{\@@_rand_myriads:n}
+% \begin{macro}[EXP, aux]
+% {
+% \@@_rand_myriads_loop:nn,
+% \@@_rand_myriads_get:w,
+% \@@_rand_myriads_last:,
+% \@@_rand_myriads_last:w,
+% }
+% Used as \cs{@@_rand_myriads:n} |{XXX}| with one input character per
+% block of four digit we want. Given a pseudo-random integer from the
+% primitive, we extract $2$ blocks of digits if possible, namely if
+% the integer is less than $2\times 10^8$. If that's not possible,
+% we try to extract $1$~block, which succeeds in the range $[2\times
+% 10^8, 26843\times 10^4)$. For the $5456$ remaining possible values
+% we just throw away the random integer and get a new one. Depending
+% on whether we got $2$, $1$, or~$0$ blocks, remove the same number of
+% 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 ;
{
- \cs_new:Npn \@@_rand_o:
+ \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}
+%
+% \subsection{Random floating point}
+%
+% \begin{macro}[EXP, int]{\@@_rand_o:Nw}
+% \begin{macro}[EXP, aux]{\@@_rand_o:, \@@_rand_o:w}
+% 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: }
{
- \__msg_kernel_expandable_error:nn { kernel } { fp-no-random }
+ \__msg_kernel_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 } } }
% \end{macrocode}
% \end{macro}
% \end{macro}
@@ -130,29 +208,33 @@
% {
% \@@_randint_badarg:w,
% \@@_randint_e:w,
-% \@@_randint_narrow_e:nn,
-% \@@_randint_wide_e:nn,
-% \@@_randint_wide_e:nnn,
+% \@@_randint_e:wnn,
+% \@@_randint_e:wwNnn,
+% \@@_randint_e:wwwNnn,
+% \@@_randint_narrow_e:nnnn,
% \@@_randint_wide_e:nnnn,
+% \@@_randint_wide_e:wnnn,
% }
% Enforce that there is one argument (then add first argument~$1$)
% or two arguments. Enforce that they are integers in
-% $(-10^{16},10^{16})$ and ordered.
-%
-% When the range is narrow (less than $2\times 10^9$) we can
-% directly use the primitive. Otherwise we use the primitive twice.
-% To ensure a uniform distribution we combine two uniformly
-% distributed integers into a total range greater or equal to our
-% target range and try again if the random number ended up being too
-% big.
-%
-% The strange |+\c_six| ensures that the first argument of
-% \cs{@@_randint_wide_e:nnn} is larger than $10^{-8}$ times the
-% difference between the end-points even if these are near
-% $-10^{16}$ and $+10^{16}$ (their difference is rounded to sixteen
-% digits). Note that we cannot just concatenate the two random
-% numbers provided by the engine as that may give an integer up to
-% (almost) $2\times 10^{16}$ which incurs rounding.
+% $(-10^{16},10^{16})$ and ordered. We distinguish narrow ranges
+% (less than $2^{28}$) from wider ones.
+%
+% For narrow ranges, compute the number~$n$ of possible outputs as
+% an integer using \cs{fp_to_int:n}, and reduce a pseudo-random
+% $28$-bit integer~$r$ modulo~$n$. On its own, this is not uniform
+% when $[0,2^{28}-1]$ does not divide evenly into intervals of
+% size~$n$. The auxiliary \cs{@@_randint_e:wwwNnn} discards the
+% pseudo-random integer if it lies in an incomplete interval, and
+% repeats.
+%
+% For wide ranges we use the same code except for the last eight
+% digits which use \cs{@@_rand_myriads:n}. It is not safe to
+% combine the first digits with the last eight as a single string of
+% digits, as this may exceed $16$~digits and be rounded. Instead,
+% we first add the first few digits (times $10^8$) to the lower
+% bound. The result is compared to the upper bound and the process
+% repeats if needed.
% \begin{macrocode}
\cs_new:Npn \@@_randint_o:Nw ? #1 @
{
@@ -186,73 +268,64 @@
\@@_randint_badarg:w #1;
\@@_randint_badarg:w #2;
\fp_compare:nNnTF { #1; } > { #2; } { \c_one } { \c_zero }
- \fp_compare:nNnTF { #1; + 2e9 } < { #2; }
- { \@@_randint_wide_e:nn }
- { \@@_randint_narrow_e:nn }
- { #1; } { #2; }
+ \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:
}
-\cs_if_exist:NTF \pdftex_uniformdeviate:D
+\cs_new:Npn \@@_randint_e:wnn #1;
{
- \cs_new:Npn \@@_randint_narrow_e:nn #1#2
- {
- \@@_parse_o:n
- {
- #1 +
- \pdftex_uniformdeviate:D
- \int_eval:n { \fp_to_int:n { #2 - #1 } + \c_one }
- }
- \exp:w
- }
- \cs_new:Npn \@@_randint_wide_e:nn #1 #2
- {
- \exp_args:Nf \@@_randint_wide_e:nnn
- { \fp_to_int:n { ceil ( ( #2 - #1 + \c_six ) * 1e-8 ) } }
- {#1} {#2}
- }
- \cs_new:Npn \@@_randint_wide_e:nnn #1#2#3
- {
- \exp_args:Nf \@@_randint_wide_e:nnnn
- {
- \@@_parse:n
- {
- #2 +
- \pdftex_uniformdeviate:D #1 e 8 +
- \pdftex_uniformdeviate:D 1 0000 0000
- }
- }
- {#1} {#2} {#3}
- }
- \cs_new:Npn \@@_randint_wide_e:nnnn #1#2#3#4
- {
- \fp_compare:nNnTF {#1} > {#4}
- { \@@_randint_wide_e:nnn {#2} {#3} {#4} }
- { \@@_exp_after_o:w #1 \exp:w }
- }
+ \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 ;
{
- \cs_new:Npn \@@_randint_narrow_e:nn #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
{
- \__msg_kernel_expandable_error:nn { kernel } { fp-no-random }
- \exp_after:wN \c_nan_fp \exp:w
+ \exp_after:wN \@@_randint_e:wwNnn
+ \__int_value:w \@@_rand_uniform: ; #3 ; #4
}
- \cs_new:Npn \@@_randint_wide_e:nn #1#2
+ { #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}
{
- \__msg_kernel_expandable_error:nn { kernel } { fp-no-random }
- \exp_after:wN \c_nan_fp \exp:w
+ \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 }
}
% \end{macrocode}
% \end{macro}
% \end{macro}
%
+% End the initial conditional that ensures these commands are only
+% defined in \pdfTeX{} and \LuaTeX{}.
% \begin{macrocode}
-\__msg_kernel_new:nnn { kernel } { fp-no-random }
- { Random~numbers~unavailable }
+ }
% \end{macrocode}
%
% \begin{macrocode}