[latex3-commits] [git/LaTeX3-latex3-latex3] master: Shuffle using toks (see #456) (d4ebe94)

Bruno Le Floch bruno at le-floch.fr
Sun Apr 29 22:37:39 CEST 2018


Repository : https://github.com/latex3/latex3
On branch  : master
Link       : https://github.com/latex3/latex3/commit/d4ebe946ba4600d1321f55e90d6a44ad62712ab2

>---------------------------------------------------------------

commit d4ebe946ba4600d1321f55e90d6a44ad62712ab2
Author: Bruno Le Floch <bruno at le-floch.fr>
Date:   Sun Apr 29 16:04:57 2018 -0400

    Shuffle using toks (see #456)


>---------------------------------------------------------------

d4ebe946ba4600d1321f55e90d6a44ad62712ab2
 l3kernel/l3candidates.dtx |  188 ++++++++++-----------------------------------
 1 file changed, 42 insertions(+), 146 deletions(-)

diff --git a/l3kernel/l3candidates.dtx b/l3kernel/l3candidates.dtx
index 00a8e04..ca3146b 100644
--- a/l3kernel/l3candidates.dtx
+++ b/l3kernel/l3candidates.dtx
@@ -2413,164 +2413,60 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}{\seq_shuffle:N, \seq_gshuffle:N, \@@_shuffle:NN,
-%   \@@_shuffle:w, \@@_shuffle_one:w, \@@_shuffle_two:w,
-%   \@@_shuffle_cycle:w, \@@_shuffle_end:, \@@_shuffle_few:,
-%   \@@_shuffle_item_few:n, \@@_shuffle_item_a_few:n,
-%   \@@_shuffle_item_b_few:n, \@@_shuffle_end_few:, \@@_shuffle_aux:w,
-%   \@@_shuffle_many:, \@@_shuffle_item_many:n,
-%   \@@_shuffle_item_a_many:n, \@@_shuffle_item_b_many:n,
-%   \@@_shuffle_end_many:}
-% \begin{variable}{\c_@@_shuffle_int, \g_@@_internal_tl, \l_@@_a_tl,
-%   \l_@@_b_tl, \l_@@_len_int, \l_@@_b_int, \l_@@_goal_a_int,
-%   \l_@@_goal_b_int}
-%   The auxiliary \cs{@@_shuffle:w} expects a number of items and a
-%   sequence with that number of items (the \cs{s_@@} start marker
-%   serves as a delimiter between the two arguments).  It shuffles the
-%   sequence and appends the resulting items to \cs{g_@@_internal_tl} as
-%   they are determined.
-%
-%   We use the primitive \cs{pdftex_uniformdeviate:D} for speed reasons.
-%   Its non-uniformity is not too important for small arguments, and in
-%   any case we cannot shuffle huge lists.
-%
-%   The idea is to randomly split the input into two equal-sized parts
-%   \cs{l_@@_a_tl} and \cs{l_@@_b_tl} then apply \cs{@@_shuffle:w} to
-%   each part.  To preserve uniformity, the probability for each item to
-%   go on one side or the other is proportional to the remaining number
-%   of slots on that side, which we keep track of as \cs{l_@@_len_int}
-%   minus \cs{l_@@_b_int} and as \cs{l_@@_b_int}.  When there are many
-%   items to split (controlled by \cs{c_@@_shuffle_int}, whose value was
-%   chosen somewhat arbitrarily and could easily be fine-tuned), the
-%   token lists \cs{l_@@_a_tl} and \cs{l_@@_b_tl} are filled using the
-%   \cs[no-index]{tl_build_\ldots{}} machinery.
+% \begin{macro}{\seq_shuffle:N, \seq_gshuffle:N, \@@_shuffle:NN, \@@_shuffle_item:n}
+% \begin{variable}{\g_@@_internal_seq, \l_@@_internal_a_int, \l_@@_internal_b_int}
+%   We apply the Fisher–Yates shuffle, storing items in \tn{toks}
+%   registers.  We use the primitive \cs{pdftex_uniformdeviate:D} for
+%   speed reasons.  Its non-uniformity is of order its argument divided
+%   by $2^{28}$, not too bad for small lists.  For sequences with more
+%   than $13$ elements there are more possible permutations than
+%   possible seeds ($13!>2^{28}$) so the question of uniformity is
+%   somewhat moot.
 %    \begin{macrocode}
 \cs_if_exist:NTF \pdftex_uniformdeviate:D
   {
-    \int_const:Nn \c_@@_shuffle_int { 12 }
-    \tl_new:N \g_@@_internal_tl
-    \tl_new:N \l_@@_a_tl
-    \tl_new:N \l_@@_b_tl
-    \int_new:N \l_@@_len_int
-    \int_new:N \l_@@_b_int
-    \int_new:N \l_@@_goal_a_int
-    \int_new:N \l_@@_goal_b_int
+    \int_new:N \l_@@_internal_a_int
+    \int_new:N \l_@@_internal_b_int
+    \seq_new:N \g_@@_internal_seq
     \cs_new_protected:Npn \seq_shuffle:N { \@@_shuffle:NN \seq_set_eq:NN }
     \cs_new_protected:Npn \seq_gshuffle:N { \@@_shuffle:NN \seq_gset_eq:NN }
     \cs_new_protected:Npn \@@_shuffle:NN #1#2
       {
-        \group_begin:
-          \tl_build_gbegin:N \g_@@_internal_tl
-          \tl_build_gput_right:Nn \g_@@_internal_tl { \s_@@ }
-          \cs_set_eq:NN \@@_item:n \@@_shuffle_item_few:n
-          \exp_after:wN \@@_shuffle:w
-          \int_value:w \seq_count:N #2 #2 \@@_shuffle_end:
-          \tl_build_gend:N \g_@@_internal_tl
-        \group_end:
-        #1 #2 \g_@@_internal_tl
-      }
-    \cs_new_protected:Npn \@@_shuffle:w #1 \s_@@
-      {
-        \if_case:w #1 \exp_stop_f:
-          \exp_after:wN \use_none:n
-        \or: \exp_after:wN \@@_shuffle_one:w
-        \or: \exp_after:wN \@@_shuffle_two:w
-        \else:
-          \int_set:Nn \l_@@_len_int {#1}
-          \if_int_compare:w \l_@@_len_int < \c_@@_shuffle_int
-            \@@_shuffle_few:
-          \else:
-            \@@_shuffle_many:
-          \fi:
-        \fi:
-      }
-    \cs_new_protected:Npn \@@_shuffle_one:w #1 \@@_shuffle_end:
-      { \tl_build_gput_right:Nn \g_@@_internal_tl {#1} }
-    \cs_new_protected:Npn \@@_shuffle_two:w
-      {
-        \if_int_odd:w \pdftex_uniformdeviate:D 2 \exp_stop_f:
-            \exp_stop_f:
-          \exp_after:wN \@@_shuffle_one:w
-        \else:
-          \exp_after:wN \@@_shuffle_cycle:w
-        \fi:
-      }
-    \cs_new_protected:Npn \@@_shuffle_cycle:w #1#2#3 \@@_shuffle_end:
-      { \tl_build_gput_right:Nn \g_@@_internal_tl { #3 #1 {#2} } }
-    \cs_new_protected:Npn \@@_shuffle_few:
-      {
-        \int_set:Nn \l_@@_goal_a_int { \l_@@_len_int / 2 }
-        \int_set:Nn \l_@@_goal_b_int { \l_@@_len_int - \l_@@_goal_a_int }
-        \int_set_eq:NN \l_@@_b_int \l_@@_goal_b_int
-        \tl_clear:N \l_@@_a_tl
-        \tl_clear:N \l_@@_b_tl
-      }
-    \cs_new_protected:Npn \@@_shuffle_item_few:n
-      {
-        \int_decr:N \l_@@_len_int
-        \if_int_compare:w
-          \pdftex_uniformdeviate:D \l_@@_len_int < \l_@@_b_int
-          \int_decr:N \l_@@_b_int
-          \exp_after:wN \@@_shuffle_item_b_few:n
-        \else:
-          \exp_after:wN \@@_shuffle_item_a_few:n
-        \fi:
-      }
-    \cs_new_protected:Npn \@@_shuffle_item_a_few:n #1
-      { \tl_put_right:Nn \l_@@_a_tl { \@@_item:n {#1} } }
-    \cs_new_protected:Npn \@@_shuffle_item_b_few:n #1
-      { \tl_put_right:Nn \l_@@_b_tl { \@@_item:n {#1} } }
-    \cs_new_protected:Npn \@@_shuffle_end_few:
-      {
-        \exp_after:wN \@@_shuffle_aux:w
-        \int_value:w \int_use:N \l_@@_goal_b_int
-        \exp_after:wN \s_@@ \l_@@_b_tl \@@_shuffle_end:
-      }
-    \cs_new_protected:Npn \@@_shuffle_aux:w
-      {
-        \exp_after:wN \@@_shuffle:w
-        \int_value:w \int_use:N \l_@@_goal_a_int
-        \exp_after:wN \s_@@ \l_@@_a_tl \@@_shuffle_end:
-        \@@_shuffle:w
-      }
-    \cs_new_protected:Npn \@@_shuffle_many:
-      {
-        \int_set:Nn \l_@@_goal_a_int { \l_@@_len_int / 2 }
-        \int_set:Nn \l_@@_goal_b_int
-          { \l_@@_len_int - \l_@@_goal_a_int }
-        \int_set_eq:NN \l_@@_b_int \l_@@_goal_b_int
-        \tl_build_begin:N \l_@@_a_tl
-        \tl_build_begin:N \l_@@_b_tl
-        \cs_set_eq:NN \@@_item:n \@@_shuffle_item_many:n
-        \cs_set_eq:NN \@@_shuffle_end: \@@_shuffle_end_many:
+        \int_compare:nNnTF { \seq_count:N #2 } > \c_max_register_int
+          {
+            \__kernel_msg_error:nnx { kernel } { shuffle-too-large }
+              { \token_to_str:N #2 }
+          }
+          {
+            \group_begin:
+              \cs_set_eq:NN \@@_item:n \@@_shuffle_item:n
+              \int_zero:N \l_@@_internal_a_int
+              #2
+              \seq_gset_from_inline_x:Nnn \g_@@_internal_seq
+                { \int_step_function:nN { \l_@@_internal_a_int } }
+                { \tex_the:D \tex_toks:D ##1 }
+            \group_end:
+            #1 #2 \g_@@_internal_seq
+            \seq_gclear:N \g_@@_internal_seq
+          }
       }
-    \cs_new_protected:Npn \@@_shuffle_item_many:n
+    \cs_new_protected:Npn \@@_shuffle_item:n
       {
-        \if_int_compare:w
-            \pdftex_uniformdeviate:D \l_@@_len_int < \l_@@_b_int
-          \int_decr:N \l_@@_len_int
-          \int_decr:N \l_@@_b_int
-          \exp_after:wN \@@_shuffle_item_b_many:n
-        \else:
-          \int_decr:N \l_@@_len_int
-          \exp_after:wN \@@_shuffle_item_a_many:n
-        \fi:
+        \int_incr:N \l_@@_internal_a_int
+        \int_set:Nn \l_@@_internal_b_int
+          { 1 + \pdftex_uniformdeviate:D \l_@@_internal_a_int }
+        \tex_toks:D \l_@@_internal_a_int
+          = \tex_toks:D \l_@@_internal_b_int
+        \tex_toks:D \l_@@_internal_b_int
       }
-    \cs_new_protected:Npn \@@_shuffle_item_a_many:n #1
-      { \tl_build_put_right:Nn \l_@@_a_tl { \@@_item:n {#1} } }
-    \cs_new_protected:Npn \@@_shuffle_item_b_many:n #1
-      { \tl_build_put_right:Nn \l_@@_b_tl { \@@_item:n {#1} } }
-    \cs_new_protected:Npn \@@_shuffle_end_many:
+    \__kernel_msg_new:nnnn { kernel } { shuffle-too-large }
+      { The~sequence~#1~is~too~long~to~be~shuffled~by~TeX. }
       {
-        \cs_set_eq:NN \@@_shuffle_end: \@@_shuffle_end_few:
-        \cs_set_eq:NN \@@_item:n \@@_shuffle_item_few:n
-        \tl_build_end:N \l_@@_b_tl
-        \tl_build_end:N \l_@@_a_tl
-        \exp_after:wN \@@_shuffle_aux:w
-        \int_value:w \int_use:N \l_@@_goal_b_int
-        \exp_after:wN \s_@@ \l_@@_b_tl \@@_shuffle_end:
+        TeX~has~ \int_eval:n { \c_max_register_int + 1 } ~
+        toks~registers:~this~only~allows~to~shuffle~up~to~
+        \int_use:N \c_max_register_int \ items.~
+        The~list~will~not~be~shuffled.
       }
-    \cs_new_eq:NN \@@_shuffle_end: \@@_shuffle_end_few:
   }
   {
     \cs_new_protected:Npn \seq_shuffle:N #1





More information about the latex3-commits mailing list