% \iffalse meta-comment % %% File: l3fp-random.dtx Copyright (C) 2016-2018 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 % % https://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,kernel]{l3doc} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \title{The \textsf{l3fp-random} package\\ % Floating point random numbers} % \author{^^A % The \LaTeX3 Project\thanks % {^^A % E-mail: % \href{mailto:latex-team@latex-project.org} % {latex-team@latex-project.org}^^A % }^^A % } % \date{Released 2018/03/05} % % \maketitle % % \begin{documentation} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{l3fp-random} Implementation} % % \begin{macrocode} %<*initex|package> % \end{macrocode} % % \begin{macrocode} %<@@=fp> % \end{macrocode} % % \begin{macro}[EXP]{\@@_parse_word_rand:N , \@@_parse_word_randint:N} % Those functions may receive a variable number of arguments. We % won't use the argument~|?|. % \begin{macrocode} \cs_new:Npn \@@_parse_word_rand:N { \@@_parse_function:NNN \@@_rand_o:Nw ? } \cs_new:Npn \@@_parse_word_randint:N { \@@_parse_function:NNN \@@_randint_o:Nw ? } % \end{macrocode} % \end{macro} % % \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 % order to write the \texttt{false} branch first. % \begin{macrocode} \cs_if_exist:NF \pdftex_uniformdeviate:D { \__kernel_msg_new:nnn { kernel } { fp-no-random } { Random~numbers~unavailable } \cs_new:Npn \@@_rand_o:Nw ? #1 @ { \__kernel_msg_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:NT \pdftex_uniformdeviate:D { % \end{macrocode} % % \begin{macro}[EXP]{\@@_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 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]{\@@_rand_myriads:n} % \begin{macro}[EXP] % { % \@@_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 ; { \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]{\@@_rand_o:Nw} % \begin{macro}[EXP]{\@@_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: } { \__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 } } } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Random integer} % % \begin{macro}[EXP]{\@@_randint_o:Nw} % \begin{macro}[EXP] % { % \@@_randint_default:w, % \@@_randint_badarg:w, % \@@_randint_o:w, % \@@_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. 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 ? { \@@_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: } \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 { \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 } } % \end{macrocode} % \end{macro} % \end{macro} % % End the initial conditional that ensures these commands are only % defined in \pdfTeX{} and \LuaTeX{}. % \begin{macrocode} } % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintChanges % % \PrintIndex