% \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.
%%
%% -----------------------------------------------------------------------
%%
%
%<*driver>
\documentclass[full]{l3doc}
\GetIdInfo$Id: l3fp-random.dtx 6750 2016-11-21 01:24:26Z bruno $
  {L3 Floating-point random numbers}
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
%
% \title{The \textsf{l3fp-random} package\thanks{This file
%         has version number \ExplFileVersion, last
%         revised \ExplFileDate.}\\
% 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 \ExplFileDate}
%
% \maketitle
%
% \begin{documentation}
%
% \end{documentation}
%
% \begin{implementation}
%
% \section{\pkg{l3fp-random} Implementation}
%
%    \begin{macrocode}
%<*initex|package>
%    \end{macrocode}
%
%    \begin{macrocode}
%<@@=fp>
%    \end{macrocode}
%
% \subsection{Random floating point}
%
% \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]$.
%    \begin{macrocode}
\cs_new:Npn \@@_rand_o:Nw ? #1 @
  {
    \tl_if_empty:nTF {#1}
      { \@@_rand_o: }
      {
        \__msg_kernel_expandable_error:nnnnn
          { kernel } { fp-num-args } { rand() } { 0 } { 0 }
        \exp_after:wN \c_nan_fp
      }
  }
\cs_if_exist:NTF \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:
          }
      }
  }
  {
    \cs_new:Npn \@@_rand_o:
      {
        \__msg_kernel_expandable_error:nn { kernel } { fp-no-random }
        \exp_after:wN \c_nan_fp
      }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \subsection{Random integer}
%
% \begin{macro}[EXP, int]{\@@_randint_o:Nw}
% \begin{macro}[EXP, aux]
%   {
%     \@@_randint_badarg:w,
%     \@@_randint_e:w,
%     \@@_randint_narrow_e:nn,
%     \@@_randint_wide_e:nn,
%     \@@_randint_wide_e:nnn,
%     \@@_randint_wide_e:nnnn,
%   }
%     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.
%    \begin{macrocode}
\cs_new:Npn \@@_randint_o:Nw ? #1 @
  {
    \if_case:w
      \__int_eval:w \@@_array_count:n {#1} - \c_one \__int_eval_end:
         \exp_after:wN \@@_randint_e:w \c_one_fp #1
    \or: \@@_randint_e:w #1
    \else:
      \__msg_kernel_expandable_error:nnnnn
        { kernel } { fp-num-args } { randint() } { 1 } { 2 }
      \exp_after:wN \c_nan_fp \exp:w
    \fi:
    \exp_after:wN \exp_end:
  }
\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_sixteen
            \exp_after:wN \c_one
          \fi:
        \fi:
      }
      { \c_one }
  }
\cs_new:Npn \@@_randint_e:w #1; #2;
  {
    \if_case:w
        \@@_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; }
    \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_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 }
      }
  }
  {
    \cs_new:Npn \@@_randint_narrow_e:nn #1#2
      {
        \__msg_kernel_expandable_error:nn { kernel } { fp-no-random }
        \exp_after:wN \c_nan_fp \exp:w
      }
    \cs_new:Npn \@@_randint_wide_e:nn #1#2
      {
        \__msg_kernel_expandable_error:nn { kernel } { fp-no-random }
        \exp_after:wN \c_nan_fp \exp:w
      }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%    \begin{macrocode}
\__msg_kernel_new:nnn { kernel } { fp-no-random }
  { Random~numbers~unavailable }
%    \end{macrocode}
%
%    \begin{macrocode}
%</initex|package>
%    \end{macrocode}
%
% \end{implementation}
%
% \PrintChanges
%
% \PrintIndex
