[latex3-commits] [git/LaTeX3-latex3-latex3] master: Make all functions robust against tuples (d5cfca5)

Bruno Le Floch bruno at le-floch.fr
Mon Feb 12 03:02:34 CET 2018


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

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

commit d5cfca5c9279d4d0475b0b273bc7bc2fa2848ff4
Author: Bruno Le Floch <bruno at le-floch.fr>
Date:   Sun Feb 11 11:42:32 2018 -0500

    Make all functions robust against tuples
    
    Previously things like atan((1,2),3) would break horribly; now it
    rightfully errors out and cleanly recovers with a nan result


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

d5cfca5c9279d4d0475b0b273bc7bc2fa2848ff4
 l3kernel/l3fp-aux.dtx            |   77 ++++++++++++++++++++----
 l3kernel/l3fp-convert.dtx        |    2 +-
 l3kernel/l3fp-logic.dtx          |   13 ++++-
 l3kernel/l3fp-parse.dtx          |  120 ++++++++++++++++++++++++++++++++++----
 l3kernel/l3fp-random.dtx         |   24 ++++----
 l3kernel/l3fp-round.dtx          |   11 +++-
 l3kernel/l3fp-trig.dtx           |   52 +++++++----------
 l3kernel/testfiles/m3rand001.tlg |    4 +-
 8 files changed, 229 insertions(+), 74 deletions(-)

diff --git a/l3kernel/l3fp-aux.dtx b/l3kernel/l3fp-aux.dtx
index 4f427f4..5cf11db 100644
--- a/l3kernel/l3fp-aux.dtx
+++ b/l3kernel/l3fp-aux.dtx
@@ -468,20 +468,20 @@
 %
 % \subsection{Other floating point types}
 %
-% \begin{macro}{\s_@@_tuple, \@@_chk_tuple:w}
+% \begin{macro}{\s_@@_tuple, \@@_tuple_chk:w}
 % \begin{variable}{\c_@@_empty_tuple_fp}
 %   Floating point tuples take the form \cs{s_@@_tuple}
-%   \cs{@@_chk_tuple:w} |{| \meta{fp 1} \meta{fp 2} \dots |}| |;| where
+%   \cs{@@_tuple_chk:w} |{| \meta{fp 1} \meta{fp 2} \dots |}| |;| where
 %   each \meta{fp} is a floating point number or tuple, hence ends with
-%   |;| itself.  When a tuple is typeset, \cs{@@_chk_tuple:w} produces
+%   |;| itself.  When a tuple is typeset, \cs{@@_tuple_chk:w} produces
 %   an error, just like usual floating point numbers.
 %   Tuples may have zero or one element.
 %    \begin{macrocode}
 \__scan_new:N \s_@@_tuple
-\cs_new_protected:Npn \@@_chk_tuple:w #1 ;
-  { \@@_misused:n { \s_@@_tuple \@@_chk_tuple:w #1 ; } }
+\cs_new_protected:Npn \@@_tuple_chk:w #1 ;
+  { \@@_misused:n { \s_@@_tuple \@@_tuple_chk:w #1 ; } }
 \tl_const:Nn \c_@@_empty_tuple_fp
-  { \s_@@_tuple \@@_chk_tuple:w { } ; }
+  { \s_@@_tuple \@@_tuple_chk:w { } ; }
 %    \end{macrocode}
 % \end{variable}
 % \end{macro}
@@ -494,8 +494,8 @@
 %   with the |\use_none:n #1| construction.
 %    \begin{macrocode}
 \cs_new:Npn \@@_array_count:n #1
-  { \@@_tuple_count:w \s_@@_tuple \@@_chk_tuple:w {#1} ; }
-\cs_new:Npn \@@_tuple_count:w \s_@@_tuple \@@_chk_tuple:w #1 ;
+  { \@@_tuple_count:w \s_@@_tuple \@@_tuple_chk:w {#1} ; }
+\cs_new:Npn \@@_tuple_count:w \s_@@_tuple \@@_tuple_chk:w #1 ;
   {
     \__int_value:w \__int_eval:w 0
       \@@_tuple_count_loop:Nw #1 { ? \__prg_break: } ;
@@ -519,6 +519,24 @@
 %    \end{macrocode}
 % \end{macro}
 %
+% \begin{macro}[EXP]{\@@_array_if_all_fp:nTF, \@@_array_if_all_fp_loop:w}
+%   True if all items are floating point numbers.  Used for |min|.
+%    \begin{macrocode}
+\cs_new:Npn \@@_array_if_all_fp:nTF #1
+  {
+    \@@_array_if_all_fp_loop:w #1 { \s_@@ \__prg_break: } ;
+    \__prg_break_point: \use_i:nn
+  }
+\cs_new:Npn \@@_array_if_all_fp_loop:w #1#2 ;
+  {
+    \@@_if_type_fp:NTwFw
+      #1 \@@_array_if_all_fp_loop:w
+      \s_@@ { \__prg_break:n \use_iii:nnn }
+      \q_stop
+  }
+%    \end{macrocode}
+% \end{macro}
+%
 % \begin{macro}[EXP]
 %   {\@@_type_from_scan:N, \@@_type_from_scan_other:N, \@@_type_from_scan:w}
 %   Used as \cs{@@_type_from_scan:N} \meta{token}.
@@ -548,6 +566,43 @@
 %    \end{macrocode}
 % \end{macro}
 %
+% \begin{macro}[EXP]{\@@_change_func_type:NNN}
+% \begin{macro}[EXP]{\@@_change_func_type_aux:w, \@@_change_func_type_chk:NNN}
+%   Arguments are \meta{type marker} \meta{function} \meta{recovery}.
+%   This gives the function obtained by placing the type after |@@|.  If
+%   the function is not defined then \meta{recovery} \meta{function} is
+%   used instead; however that test is not run when the \meta{type
+%   marker} is \cs{s_@@}.
+%    \begin{macrocode}
+\cs_new:Npn \@@_change_func_type:NNN #1#2#3
+  {
+    \@@_if_type_fp:NTwFw
+      #1 #2
+      \s_@@
+        {
+          \exp_after:wN \@@_change_func_type_chk:NNN
+          \cs:w
+            @@ \@@_type_from_scan_other:N #1
+            \exp_after:wN \@@_change_func_type_aux:w \token_to_str:N #2
+          \cs_end:
+          #2 #3
+        }
+      \q_stop
+  }
+\exp_last_unbraced:NNNNo
+  \cs_new:Npn \@@_change_func_type_aux:w #1 { \tl_to_str:n { @@ } } { }
+\cs_new:Npn \@@_change_func_type_chk:NNN #1#2#3
+  {
+    \if_meaning:w \scan_stop: #1
+      \exp_after:wN #3 \exp_after:wN #2
+    \else:
+      \exp_after:wN #1
+    \fi:
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
 % \begin{macro}[EXP]{\@@_exp_after_any_f:Nnw, \@@_exp_after_any_f:nw}
 % \begin{macro}[EXP]{\@@_exp_after_stop_f:nw}
 %   The |Nnw| function simply dispatches to the appropriate
@@ -587,10 +642,10 @@
 %       \cs{s_@@_stop}
 %   \end{quote}
 %    \begin{macrocode}
-\cs_new:Npn \@@_exp_after_tuple_f:nw #1 \s_@@_tuple \@@_chk_tuple:w #2 ;
+\cs_new:Npn \@@_exp_after_tuple_f:nw #1 \s_@@_tuple \@@_tuple_chk:w #2 ;
   {
     \exp_after:wN \s_@@_tuple
-    \exp_after:wN \@@_chk_tuple:w
+    \exp_after:wN \@@_tuple_chk:w
     \exp_after:wN {
       \exp:w \exp_end_continue_f:w
       \@@_exp_after_array_f:w #2 \s_@@_stop
@@ -1175,7 +1230,7 @@
   { \exp_last_unbraced:Nf \@@_func_to_name_aux:w { \cs_to_str:N #1 } X }
 \cs_set_protected:Npn \@@_tmp:w #1 #2
   { \cs_new:Npn \@@_func_to_name_aux:w ##1 #1 ##2 #2 ##3 X {##2} }
-\exp_args:Nff \@@_tmp:w { \tl_to_str:n { @@_ } } { \tl_to_str:n { _o:w } }
+\exp_args:Nff \@@_tmp:w { \tl_to_str:n { @@_ } } { \tl_to_str:n { _o: } }
 %    \end{macrocode}
 % \end{macro}
 %
diff --git a/l3kernel/l3fp-convert.dtx b/l3kernel/l3fp-convert.dtx
index 11fab52..ee0d9a2 100644
--- a/l3kernel/l3fp-convert.dtx
+++ b/l3kernel/l3fp-convert.dtx
@@ -329,7 +329,7 @@
   }
 \cs_new:Npn \@@_to_tl_other:w #1
   { \cs:w @@ \@@_type_from_scan_other:N #1 _to_tl:w \cs_end: #1 }
-\cs_new:Npn \@@_tuple_to_tl:w \s_@@_tuple \@@_chk_tuple:w #1 ;
+\cs_new:Npn \@@_tuple_to_tl:w \s_@@_tuple \@@_tuple_chk:w #1 ;
   {
     \int_case:nnF { \@@_array_count:n {#1} }
       {
diff --git a/l3kernel/l3fp-logic.dtx b/l3kernel/l3fp-logic.dtx
index 19cad2b..ead5868 100644
--- a/l3kernel/l3fp-logic.dtx
+++ b/l3kernel/l3fp-logic.dtx
@@ -1,6 +1,6 @@
 % \iffalse meta-comment
 %
-%% File: l3fp-logic.dtx Copyright (C) 2011-2017 The LaTeX3 Project
+%% File: l3fp-logic.dtx Copyright (C) 2011-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
@@ -418,7 +418,8 @@
 %
 % \subsection{Extrema}
 %
-% \begin{macro}[EXP]{\@@_minmax_o:Nw}
+% \begin{macro}[EXP]{\@@_minmax_o:Nw, \@@_minmax_aux_o:Nw}
+%   First check all operands are floating point numbers.
 %   The argument~|#1| is $2$~to find the maximum of an array~|#2| of
 %   floating point numbers, and $0$~to find the minimum.  We read
 %   numbers sequentially, keeping track of the largest (smallest) number
@@ -430,7 +431,13 @@
 %   fp-like trailing marker breaks the loop correctly: see the precise
 %   definition of \cs{@@_minmax_loop:Nww}.
 %    \begin{macrocode}
-\cs_new:Npn \@@_minmax_o:Nw #1#2 @
+\cs_new:Npn \@@_minmax_o:Nw #1
+  {
+    \@@_parse_function_all_fp_o:fnw
+      { \token_if_eq_meaning:NNTF 0 #1 { min } { max } }
+      { \@@_minmax_aux_o:Nw #1 }
+  }
+\cs_new:Npn \@@_minmax_aux_o:Nw #1#2 @
   {
     \if_meaning:w 0 #1
       \exp_after:wN \@@_minmax_loop:Nww \exp_after:wN +
diff --git a/l3kernel/l3fp-parse.dtx b/l3kernel/l3fp-parse.dtx
index 541749a..e1517c5 100644
--- a/l3kernel/l3fp-parse.dtx
+++ b/l3kernel/l3fp-parse.dtx
@@ -1777,38 +1777,55 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[EXP]{\@@_parse_apply_unary:NNNwN, \@@_parse_apply_unary_aux:NwNw, \@@_parse_apply_unary_aux:nNNNw}
+% \begin{macro}[EXP]{\@@_parse_apply_unary:NNNwN}
+% \begin{macro}[EXP]{\@@_parse_apply_unary_chk:NwNw, \@@_parse_apply_unary_chk:nNNNw}
+% \begin{macro}[EXP]{\@@_parse_apply_unary_type:NNN, \@@_parse_apply_unary_error:NNw}
 %   In contrast to \cs{@@_parse_apply_function:NNNwN}, this checks that
 %   the operand |#4| is a single argument (namely there is a single
 %   |;|).  We use the fact that any floating point starts with a
-%   \enquote{safe} token like \cs{s__fp}.  If there is no argument
+%   \enquote{safe} token like \cs{s_@@}.  If there is no argument
 %   produce the |fp-no-arg| error; if there are at least two produce
 %   |fp-multi-arg|.  For the error message extract the mathematical
 %   function name (such as |sin|) from the \pkg{expl3} function that
 %   computes it, such as \cs{@@_sin_o:w}.
+%
+%   In addition, since there is a single argument we can dispatch on
+%   type and check that the resulting function exists.  This catches
+%   things like |sin((1,2))| where it does not make sense to take the
+%   sine of a tuple.
 %    \begin{macrocode}
 \cs_new:Npn \@@_parse_apply_unary:NNNwN #1#2#3#4@#5
   {
-    \@@_parse_apply_unary_aux:NwNw #4 @ ; . \q_stop
+    \@@_parse_apply_unary_chk:NwNw #4 @ ; . \q_stop
+    \@@_parse_apply_unary_type:NNN
     #3 #2 #4 @
     \exp:w \exp_end_continue_f:w #5 #1
   }
-\cs_new:Npn \@@_parse_apply_unary_aux:NwNw #1#2 ; #3#4 \q_stop
+\cs_new:Npn \@@_parse_apply_unary_chk:NwNw #1#2 ; #3#4 \q_stop
   {
     \if_meaning:w @ #3 \else:
       \token_if_eq_meaning:NNTF . #3
-        { \@@_parse_apply_unary_aux:nNNNw { no } }
-        { \@@_parse_apply_unary_aux:nNNNw { multi } }
+        { \@@_parse_apply_unary_chk:nNNNNw { no } }
+        { \@@_parse_apply_unary_chk:nNNNNw { multi } }
     \fi:
   }
-\cs_new:Npn \@@_parse_apply_unary_aux:nNNNw #1#2#3#4#5 @
+\cs_new:Npn \@@_parse_apply_unary_chk:nNNNNw #1#2#3#4#5#6 @
   {
     #2
-    \@@_error:nffn { fp-#1-arg } { \@@_func_to_name:N #3 } { } { }
-    \exp_after:wN #3 \exp_after:wN #4 \c_nan_fp @
+    \@@_error:nffn { fp-#1-arg } { \@@_func_to_name:N #4 } { } { }
+    \exp_after:wN #4 \exp_after:wN #5 \c_nan_fp @
+  }
+\cs_new:Npn \@@_parse_apply_unary_type:NNN #1#2#3
+  {
+    \@@_change_func_type:NNN #3 #1 \@@_parse_apply_unary_error:NNw
+    #2 #3
   }
+\cs_new:Npn \@@_parse_apply_unary_error:NNw #1#2
+  { \@@_invalid_operation_o:fw { \@@_func_to_name:N #1 } }
 %    \end{macrocode}
 % \end{macro}
+% \end{macro}
+% \end{macro}
 %
 % \begin{macro}[EXP]{\@@_parse_prefix_-:Nw, \@@_parse_prefix_!:Nw}
 %   The unary~|-| and boolean not are harder: we parse the operand using
@@ -2309,7 +2326,7 @@
     \exp_after:wN #1
     \exp:w \exp_end_continue_f:w
     \@@_exp_after_tuple_f:nw { }
-      \s_@@_tuple \@@_chk_tuple:w { #2 #4 } ;
+      \s_@@_tuple \@@_tuple_chk:w { #2 #4 } ;
     #5 #1
   }
 %    \end{macrocode}
@@ -2587,6 +2604,87 @@
 % \end{macro}
 % \end{macro}
 %
+% \subsection{Tools for functions}
+%
+% \begin{macro}[EXP]{\@@_parse_function_all_fp_o:fnw}
+%   Followed by \Arg{function name} \Arg{code} \meta{float array} |@|
+%   this checks all floats are floating point numbers (no tuples).
+%    \begin{macrocode}
+\cs_new:Npn \@@_parse_function_all_fp_o:fnw #1#2#3 @
+  {
+    \@@_array_if_all_fp:nTF {#3}
+      { #2 #3 @ }
+      {
+        \@@_error:nffn { fp-bad-args }
+          {#1}
+          { \fp_to_tl:n { \s_@@_tuple \@@_tuple_chk:w {#3} ; } }
+          { }
+        \exp_after:wN \c_nan_fp
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_function_one_two:nnw}
+% \begin{macro}[EXP]
+%   {
+%     \@@_parse_function_one_two_error_o:w,
+%     \@@_parse_function_one_two_aux:nnw,
+%     \@@_parse_function_one_two_auxii:nnw
+%   }
+%   This is followed by \Arg{function name} \Arg{code} \meta{float
+%   array} |@|.  It checks that the \meta{float array} consists of one
+%   or two floating point numbers (not tuples), then leaves the
+%   \meta{code} (if there is one float) or its tail (if there are two
+%   floats) followed by the \meta{float array}.  The \meta{code} should
+%   start with a single token such as \cs{@@_atan_default:w} that deals
+%   with the single-float case.
+%
+%   The first \cs{@@_if_type_fp:NTwFw} test catches the case of no
+%   argument and the case of a tuple argument.  The next one
+%   distinguishes the case of a single argument (no error, just add
+%   \cs{c_one_fp}) from a tuple second argument.  Finally check there is
+%   no further argument.
+%    \begin{macrocode}
+\cs_new:Npn \@@_parse_function_one_two:nnw #1#2#3
+  {
+    \@@_if_type_fp:NTwFw
+      #3 { } \s_@@ \@@_parse_function_one_two_error_o:w \q_stop
+    \@@_parse_function_one_two_aux:nnw {#1} {#2} #3
+  }
+\cs_new:Npn \@@_parse_function_one_two_error_o:w #1#2#3#4 @
+  {
+    \@@_error:nffn { fp-bad-args }
+      {#2}
+      { \fp_to_tl:n { \s_@@_tuple \@@_tuple_chk:w {#4} ; } }
+      { }
+    \exp_after:wN \c_nan_fp
+  }
+\cs_new:Npn \@@_parse_function_one_two_aux:nnw #1#2 #3; #4
+  {
+    \@@_if_type_fp:NTwFw
+      #4 { }
+      \s_@@
+      {
+        \if_meaning:w @ #4
+          \exp_after:wN \use_iv:nnnn
+        \fi:
+        \@@_parse_function_one_two_error_o:w
+      }
+      \q_stop
+    \@@_parse_function_one_two_auxii:nnw {#1} {#2} #3; #4
+  }
+\cs_new:Npn \@@_parse_function_one_two_auxii:nnw #1#2#3; #4; #5
+  {
+    \if_meaning:w @ #5 \else:
+      \exp_after:wN \@@_parse_function_one_two_error_o:w
+    \fi:
+    \use_ii:nn {#1} { \use_none:n #2 } #3; #4; #5
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
 % \subsection{Candidate: defining new \pkg{l3fp} functions}
 %
 % \begin{macro}[EXP]{\fp_function:Nw}
@@ -2723,6 +2821,8 @@
   { #1~got~more~than~one~argument;~used~nan. }
 \__kernel_msg_new:nnn { kernel } { fp-num-args }
   { #1~expects~between~#2~and~#3~arguments. }
+\__kernel_msg_new:nnn { kernel } { fp-bad-args }
+  { Arguments~in~#1#2~are~invalid. }
 %<*package>
 \cs_if_exist:cT { @unexpandable at protect }
   {
diff --git a/l3kernel/l3fp-random.dtx b/l3kernel/l3fp-random.dtx
index e7989a2..6f01b05 100644
--- a/l3kernel/l3fp-random.dtx
+++ b/l3kernel/l3fp-random.dtx
@@ -1,6 +1,6 @@
 % \iffalse meta-comment
 %
-%% File: l3fp-random.dtx Copyright (C) 2016,2017 The LaTeX3 Project
+%% 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
@@ -213,8 +213,9 @@
 % \begin{macro}[EXP]{\@@_randint_o:Nw}
 % \begin{macro}[EXP]
 %   {
+%     \@@_randint_default:w,
 %     \@@_randint_badarg:w,
-%     \@@_randint_e:w,
+%     \@@_randint_o:w,
 %     \@@_randint_e:wnn,
 %     \@@_randint_e:wwNnn,
 %     \@@_randint_e:wwwNnn,
@@ -243,19 +244,13 @@
 %     bound.  The result is compared to the upper bound and the process
 %     repeats if needed.
 %    \begin{macrocode}
-\cs_new:Npn \@@_randint_o:Nw ? #1 @
+\cs_new:Npn \@@_randint_o:Nw ?
   {
-    \if_case:w
-      \__int_eval:w \@@_array_count:n {#1} - 1 \__int_eval_end:
-         \exp_after:wN \@@_randint_e:w \c_one_fp #1
-    \or: \@@_randint_e:w #1
-    \else:
-      \__kernel_msg_expandable_error:nnnnn
-        { kernel } { fp-num-args } { randint() } { 1 } { 2 }
-      \exp_after:wN \c_nan_fp \exp:w
-    \fi:
-    \exp_after:wN \exp_end:
+    \@@_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;
@@ -269,7 +264,7 @@
       }
       { 1 \exp_stop_f: }
   }
-\cs_new:Npn \@@_randint_e:w #1; #2;
+\cs_new:Npn \@@_randint_o:w #1; #2; @
   {
     \if_case:w
         \@@_randint_badarg:w #1;
@@ -282,6 +277,7 @@
         { randint } { \@@_array_to_clist:n { #1; #2; } }
       \exp:w
     \fi:
+    \exp_after:wN \exp_end:
   }
 \cs_new:Npn \@@_randint_e:wnn #1;
   {
diff --git a/l3kernel/l3fp-round.dtx b/l3kernel/l3fp-round.dtx
index df4a3a4..9b2885e 100644
--- a/l3kernel/l3fp-round.dtx
+++ b/l3kernel/l3fp-round.dtx
@@ -422,14 +422,21 @@
 %
 % ^^A todo: This macro is intermingled with l3fp-parse.
 % ^^A todo: Add explanations.
-% \begin{macro}[EXP]{\@@_round_o:Nw}
+% \begin{macro}[EXP]{\@@_round_o:Nw, \@@_round_aux_o:Nw}
+%   First check that all arguments are floating point numbers.
 %   The |trunc|, |ceil| and |floor| functions expect one or two
 %   arguments (the second is $0$ by default), and the |round| function
 %   also accepts a third argument (\texttt{nan} by default), which
 %   changes |#1| from \cs{@@_round_to_nearest:NNN} to one of its
 %   analogues.
 %    \begin{macrocode}
-\cs_new:Npn \@@_round_o:Nw #1#2 @
+\cs_new:Npn \@@_round_o:Nw #1
+  {
+    \@@_parse_function_all_fp_o:fnw
+      { \@@_round_name_from_cs:N #1 }
+      { \@@_round_aux_o:Nw #1 }
+  }
+\cs_new:Npn \@@_round_aux_o:Nw #1#2 @
   {
     \if_case:w
       \__int_eval:w \@@_array_count:n {#2} \__int_eval_end:
diff --git a/l3kernel/l3fp-trig.dtx b/l3kernel/l3fp-trig.dtx
index 132c598..5351d5c 100644
--- a/l3kernel/l3fp-trig.dtx
+++ b/l3kernel/l3fp-trig.dtx
@@ -1,6 +1,6 @@
 % \iffalse meta-comment
 %
-%% File: l3fp-trig.dtx Copyright (C) 2011-2017 The LaTeX3 Project
+%% File: l3fp-trig.dtx Copyright (C) 2011-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
@@ -1087,44 +1087,34 @@
 %
 % \subsubsection{Arctangent and arccotangent}
 %
-% \begin{macro}[EXP]{\@@_atan_o:Nw, \@@_acot_o:Nw}
-% \begin{macro}[EXP]{\@@_atan_dispatch_o:NNnNw}
+% \begin{macro}[EXP]{\@@_atan_o:Nw, \@@_acot_o:Nw, \@@_atan_default:w}
 %   The parsing step manipulates \texttt{atan} and \texttt{acot} like
 %   \texttt{min} and \texttt{max}, reading in an array of operands, but
 %   also leaves \cs{use_i:nn} or \cs{use_ii:nn} depending on whether the
-%   result should be given in radians or in degrees.  Here, we dispatch
-%   according to the number of arguments.  The one-argument versions of
-%   arctangent and arccotangent are special cases of the two-argument
-%   ones: $\operatorname{atan}(y) = \operatorname{atan}(y, 1) = \operatorname{acot}(1, y)$ and
-%   $\operatorname{acot}(x) = \operatorname{atan}(1, x) = \operatorname{acot}(x, 1)$.
+%   result should be given in radians or in degrees.  The helper
+%   \cs{@@_parse_function_one_two:nnw} checks that the operand is one or
+%   two floating point numbers (not tuples) and leaves its second
+%   argument or its tail accordingly (its first argument is used for
+%   error messages).  More precisely if we are given a single floating
+%   point number \cs{@@_atan_default:w} places \cs{c_one_fp} (expanded)
+%   after it; otherwise \cs{@@_atan_default:w} is omitted by
+%   \cs{@@_parse_function_one_two:nnw}.
 %    \begin{macrocode}
-\cs_new:Npn \@@_atan_o:Nw
+\cs_new:Npn \@@_atan_o:Nw #1
   {
-    \@@_atan_dispatch_o:NNnNw
-      \@@_acotii_o:Nww \@@_atanii_o:Nww { atan }
+    \@@_parse_function_one_two:nnw
+      { #1 { atan } { atand } }
+      { \@@_atan_default:w \@@_atanii_o:Nww #1 }
   }
-\cs_new:Npn \@@_acot_o:Nw
+\cs_new:Npn \@@_acot_o:Nw #1
   {
-    \@@_atan_dispatch_o:NNnNw
-      \@@_atanii_o:Nww \@@_acotii_o:Nww { acot }
-  }
-\cs_new:Npn \@@_atan_dispatch_o:NNnNw #1#2#3#4#5@
-  {
-    \if_case:w
-      \__int_eval:w \@@_array_count:n {#5} - 1 \__int_eval_end:
-         \exp_after:wN #1 \exp_after:wN #4 \c_one_fp #5
-         \exp:w
-    \or: #2 #4 #5 \exp:w
-    \else:
-      \__kernel_msg_expandable_error:nnnnn
-        { kernel } { fp-num-args } { #3() } { 1 } { 2 }
-      \exp_after:wN \c_nan_fp \exp:w
-    \fi:
-    \exp_after:wN \exp_end:
+    \@@_parse_function_one_two:nnw
+      { #1 { acot } { acotd } }
+      { \@@_atan_default:w \@@_acotii_o:Nww #1 }
   }
+\cs_new:Npx \@@_atan_default:w #1#2#3 @ { #1 #2 #3 \c_one_fp @ }
 %    \end{macrocode}
 % \end{macro}
-% \end{macro}
 %
 % \begin{macro}[EXP]{\@@_atanii_o:Nww, \@@_acotii_o:Nww}
 %   If either operand is \texttt{nan}, we return it.  If both are
@@ -1139,7 +1129,7 @@
 %   \cs{@@_acotii_o:ww} simply reverses its two arguments.
 %    \begin{macrocode}
 \cs_new:Npn \@@_atanii_o:Nww
-    #1 \s_@@ \@@_chk:w #2#3#4; \s_@@ \@@_chk:w #5
+    #1 \s_@@ \@@_chk:w #2#3#4; \s_@@ \@@_chk:w #5 #6 @
   {
     \if_meaning:w 3 #2 \@@_case_return_i_o:ww \fi:
     \if_meaning:w 3 #5 \@@_case_return_ii_o:ww \fi:
@@ -1156,7 +1146,7 @@
     \fi:
     \@@_atan_normal_o:NNnwNnw #1
     \s_@@ \@@_chk:w #2#3#4;
-    \s_@@ \@@_chk:w #5
+    \s_@@ \@@_chk:w #5 #6
   }
 \cs_new:Npn \@@_acotii_o:Nww #1#2; #3;
   { \@@_atanii_o:Nww #1#3; #2; }
diff --git a/l3kernel/testfiles/m3rand001.tlg b/l3kernel/testfiles/m3rand001.tlg
index ab39ffa..c3a3af6 100644
--- a/l3kernel/testfiles/m3rand001.tlg
+++ b/l3kernel/testfiles/m3rand001.tlg
@@ -80,7 +80,7 @@ spelling (e.g., `I\hbox'). Otherwise just continue,
 and I'll forget about whatever was undefined.
 ! Undefined control sequence.
 <argument> \LaTeX3 error: 
-                           randint() expects between 1 and 2 arguments.
+                           Arguments in randint() are invalid.
 l. ...  }
 The control sequence at the end of the top line
 of your error message was never \def'ed. If you have
@@ -125,7 +125,7 @@ spelling (e.g., `I\hbox'). Otherwise just continue,
 and I'll forget about whatever was undefined.
 ! Undefined control sequence.
 <argument> \LaTeX3 error: 
-                           randint() expects between 1 and 2 arguments.
+                           Arguments in randint(1, 2, 3) are invalid.
 l. ...  }
 The control sequence at the end of the top line
 of your error message was never \def'ed. If you have





More information about the latex3-commits mailing list