% \iffalse meta-comment % %% File: l3fparray.dtx % % Copyright (C) 2018-2024 The LaTeX 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{^^A % The \pkg{l3fparray} module\\ Fast global floating point arrays^^A % } % % \author{^^A % The \LaTeX{} Project\thanks % {^^A % E-mail: % \href{mailto:latex-team@latex-project.org} % {latex-team@latex-project.org}^^A % }^^A % } % % \date{Released 2024-03-14} % % \maketitle % % \begin{documentation} % % \section{\pkg{l3fparray} documentation} % % For applications requiring heavy use of floating points, this module % provides arrays which can be accessed in constant time (contrast % \pkg{l3seq}, where access time is linear). The interface is very % close to that of \pkg{l3intarray}. The size of the array is fixed and % must be given at point of initialisation % % \begin{function}[added = 2018-05-05]{\fparray_new:Nn, \fparray_new:cn} % \begin{syntax} % \cs{fparray_new:Nn} \meta{fparray~var} \Arg{size} % \end{syntax} % Evaluates the integer expression \meta{size} and allocates an % \meta{floating point array variable} with that number of (zero) % entries. The variable name should start with |\g_| because % assignments are always global. % \end{function} % % \begin{function}[EXP, added = 2018-05-05]{\fparray_count:N, \fparray_count:c} % \begin{syntax} % \cs{fparray_count:N} \meta{fparray~var} % \end{syntax} % Expands to the number of entries in the \meta{floating point array % variable}. This is performed in constant time. % \end{function} % % \begin{function}[added = 2018-05-05]{\fparray_gset:Nnn, \fparray_gset:cnn} % \begin{syntax} % \cs{fparray_gset:Nnn} \meta{fparray~var} \Arg{position} \Arg{value} % \end{syntax} % Stores the result of evaluating the floating point expression % \meta{value} into the \meta{floating point array variable} at the % (integer expression) \meta{position}. If the \meta{position} is not % between $1$ and the \cs{fparray_count:N}, an error occurs. % Assignments are always global. % \end{function} % % \begin{function}[added = 2018-05-05]{\fparray_gzero:N, \fparray_gzero:c} % \begin{syntax} % \cs{fparray_gzero:N} \meta{fparray~var} % \end{syntax} % Sets all entries of the \meta{floating point array variable} to % $+0$. Assignments are always global. % \end{function} % % \begin{function}[EXP, added = 2018-05-05] % { % \fparray_item:Nn, \fparray_item:cn % \fparray_item_to_tl:Nn, \fparray_item_to_tl:cn % } % \begin{syntax} % \cs{fparray_item:Nn} \meta{fparray~var} \Arg{position} % \end{syntax} % Applies \cs{fp_use:N} or \cs{fp_to_tl:N} (respectively) to the % floating point entry stored at the (integer expression) % \meta{position} in the \meta{floating point array variable}. If the % \meta{position} is not between $1$ and the \cs{fparray_count:N}, an % error occurs. % \end{function} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{l3fparray} implementation} % % \begin{macrocode} %<*package> % \end{macrocode} % % \begin{macrocode} %<@@=fp> % \end{macrocode} % % In analogy to \pkg{l3intarray} it would make sense to have % |<@@=fparray>|, but we need direct access to \cs{@@_parse:n} from % \pkg{l3fp-parse}, and a few other (less crucial) internals of the % \pkg{l3fp} family. % % \subsection{Allocating arrays} % % There are somewhat more than $(2^{31}-1)^2$ floating point numbers so % we store each floating point number as three entries in integer % arrays. To avoid having to multiply indices by three or to add 1 etc, % a floating point array is just a token list consisting of three % tokens: integer arrays of the same size. % % \begin{variable}{\g_@@_array_int} % Used to generate unique names for the three integer arrays. % \begin{macrocode} \int_new:N \g_@@_array_int % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_array_loop_int} % Used to loop in \cs{@@_array_gzero:N}. % \begin{macrocode} \int_new:N \l_@@_array_loop_int % \end{macrocode} % \end{variable} % % \begin{macro}{\fparray_new:Nn, \fparray_new:cn} % \begin{macro}{\@@_array_new:nNNN} % Build a three-token token list, then define all three tokens to be % integer arrays of the same size. No need to initialize the data: % the integer arrays start with zeros, and three zeros denote % precisely \cs{c_zero_fp}, as we want. % \begin{macrocode} \cs_new_protected:Npn \fparray_new:Nn #1#2 { \tl_new:N #1 \prg_replicate:nn { 3 } { \int_gincr:N \g_@@_array_int \exp_args:NNc \tl_gput_right:Nn #1 { g_@@_array_ \@@_int_to_roman:w \g_@@_array_int _intarray } } \exp_last_unbraced:Nfo \@@_array_new:nNNNN { \int_eval:n {#2} } #1 #1 } \cs_generate_variant:Nn \fparray_new:Nn { c } \cs_new_protected:Npn \@@_array_new:nNNNN #1#2#3#4#5 { \int_compare:nNnTF {#1} < 0 { \msg_error:nnn { kernel } { negative-array-size } {#1} \cs_undefine:N #1 \int_gsub:Nn \g_@@_array_int { 3 } } { \intarray_new:Nn #2 {#1} \intarray_new:Nn #3 {#1} \intarray_new:Nn #4 {#1} } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[EXP]{\fparray_count:N, \fparray_count:c} % Size of any of the intarrays, here we pick the third. % \begin{macrocode} \cs_new:Npn \fparray_count:N #1 { \exp_after:wN \use_i:nnn \exp_after:wN \intarray_count:N #1 } \cs_generate_variant:Nn \fparray_count:N { c } % \end{macrocode} % \end{macro} % % \subsection{Array items} % % \begin{macro}[EXP]{\@@_array_bounds:NNnTF, \@@_array_bounds_error:NNn} % See the \pkg{l3intarray} analogue: only names change. % The functions \cs{fparray_gset:Nnn} and \cs{fparray_item:Nn} share % bounds checking. The |T| branch is used if |#3| is within bounds of % the array |#2|. % \begin{macrocode} \cs_new:Npn \@@_array_bounds:NNnTF #1#2#3#4#5 { \if_int_compare:w 1 > #3 \exp_stop_f: \@@_array_bounds_error:NNn #1 #2 {#3} #5 \else: \if_int_compare:w #3 > \fparray_count:N #2 \exp_stop_f: \@@_array_bounds_error:NNn #1 #2 {#3} #5 \else: #4 \fi: \fi: } \cs_new:Npn \@@_array_bounds_error:NNn #1#2#3 { #1 { kernel } { out-of-bounds } { \token_to_str:N #2 } {#3} { \fparray_count:N #2 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\fparray_gset:Nnn, \fparray_gset:cnn} % \begin{macro} % { % \@@_array_gset:NNNNww, \@@_array_gset:w, % \@@_array_gset_recover:Nw, \@@_array_gset_special:nnNNN, % \@@_array_gset_normal:w % } % Evaluate, then store exponent in one intarray, sign and $8$ digits % of mantissa in the next, and $8$ trailing digits in the last. % \begin{macrocode} \cs_new_protected:Npn \fparray_gset:Nnn #1#2#3 { \exp_after:wN \exp_after:wN \exp_after:wN \@@_array_gset:NNNNww \exp_after:wN #1 \exp_after:wN #1 \int_value:w \int_eval:n {#2} \exp_after:wN ; \exp:w \exp_end_continue_f:w \@@_parse:n {#3} } \cs_generate_variant:Nn \fparray_gset:Nnn { c } \cs_new_protected:Npn \@@_array_gset:NNNNww #1#2#3#4#5 ; #6 ; { \@@_array_bounds:NNnTF \msg_error:nneee #4 {#5} { \exp_after:wN \@@_change_func_type:NNN \@@_use_i_until_s:nw #6 ; \@@_array_gset:w \@@_array_gset_recover:Nw #6 ; {#5} #1 #2 #3 } { } } \cs_new_protected:Npn \@@_array_gset_recover:Nw #1#2 ; { \@@_error:nffn { unknown-type } { \tl_to_str:n { #2 ; } } { } { } \exp_after:wN #1 \c_nan_fp } \cs_new_protected:Npn \@@_array_gset:w \s_@@ \@@_chk:w #1#2 { \if_case:w #1 \exp_stop_f: \@@_case_return:nw { \@@_array_gset_special:nnNNN {#2} } \or: \exp_after:wN \@@_array_gset_normal:w \or: \@@_case_return:nw { \@@_array_gset_special:nnNNN { #2 3 } } \or: \@@_case_return:nw { \@@_array_gset_special:nnNNN { 1 } } \fi: \s_@@ \@@_chk:w #1 #2 } \cs_new_protected:Npn \@@_array_gset_normal:w \s_@@ \@@_chk:w 1 #1 #2 #3#4#5 ; #6#7#8#9 { \__kernel_intarray_gset:Nnn #7 {#6} {#2} \__kernel_intarray_gset:Nnn #8 {#6} { \if_meaning:w 2 #1 3 \else: 1 \fi: #3#4 } \__kernel_intarray_gset:Nnn #9 {#6} { 1 \use:nn #5 } } \cs_new_protected:Npn \@@_array_gset_special:nnNNN #1#2#3#4#5 { \__kernel_intarray_gset:Nnn #3 {#2} {#1} \__kernel_intarray_gset:Nnn #4 {#2} {0} \__kernel_intarray_gset:Nnn #5 {#2} {0} } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\fparray_gzero:N, \fparray_gzero:c} % \begin{macrocode} \cs_new_protected:Npn \fparray_gzero:N #1 { \int_zero:N \l_@@_array_loop_int \prg_replicate:nn { \fparray_count:N #1 } { \int_incr:N \l_@@_array_loop_int \exp_after:wN \@@_array_gset_special:nnNNN \exp_after:wN 0 \exp_after:wN \l_@@_array_loop_int #1 } } \cs_generate_variant:Nn \fparray_gzero:N { c } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP] % { % \fparray_item:Nn, % \fparray_item:cn, % \fparray_item_to_tl:Nn, % \fparray_item_to_tl:cn, % } % \begin{macro}[EXP] % { % \@@_array_item:NwN, % \@@_array_item:NNNnN, % \@@_array_item:N, % \@@_array_item:w, % \@@_array_item_special:w, % \@@_array_item_normal:w % } % \begin{macrocode} \cs_new:Npn \fparray_item:Nn #1#2 { \exp_after:wN \@@_array_item:NwN \exp_after:wN #1 \int_value:w \int_eval:n {#2} ; \@@_to_decimal:w } \cs_generate_variant:Nn \fparray_item:Nn { c } \cs_new:Npn \fparray_item_to_tl:Nn #1#2 { \exp_after:wN \@@_array_item:NwN \exp_after:wN #1 \int_value:w \int_eval:n {#2} ; \@@_to_tl:w } \cs_generate_variant:Nn \fparray_item_to_tl:Nn { c } \cs_new:Npn \@@_array_item:NwN #1#2 ; #3 { \@@_array_bounds:NNnTF \msg_expandable_error:nnfff #1 {#2} { \exp_after:wN \@@_array_item:NNNnN #1 {#2} #3 } { \exp_after:wN #3 \c_nan_fp } } \cs_new:Npn \@@_array_item:NNNnN #1#2#3#4 { \exp_after:wN \@@_array_item:N \int_value:w \__kernel_intarray_item:Nn #2 {#4} \exp_after:wN ; \int_value:w \__kernel_intarray_item:Nn #3 {#4} \exp_after:wN ; \int_value:w \__kernel_intarray_item:Nn #1 {#4} ; } \cs_new:Npn \@@_array_item:N #1 { \if_meaning:w 0 #1 \exp_after:wN \@@_array_item_special:w \fi: \@@_array_item:w #1 } \cs_new:Npn \@@_array_item:w #1 #2#3#4#5 #6 ; 1 #7 ; { \exp_after:wN \@@_array_item_normal:w \int_value:w \if_meaning:w #1 1 0 \else: 2 \fi: \exp_stop_f: #7 ; {#2#3#4#5} {#6} ; } \cs_new:Npn \@@_array_item_special:w #1 ; #2 ; #3 ; #4 { \exp_after:wN #4 \exp:w \exp_end_continue_f:w \if_case:w #3 \exp_stop_f: \exp_after:wN \c_zero_fp \or: \exp_after:wN \c_nan_fp \or: \exp_after:wN \c_minus_zero_fp \or: \exp_after:wN \c_inf_fp \else: \exp_after:wN \c_minus_inf_fp \fi: } \cs_new:Npn \@@_array_item_normal:w #1 #2#3#4#5 #6 ; #7 ; #8 ; #9 { #9 \s_@@ \@@_chk:w 1 #1 {#8} #7 {#2#3#4#5} {#6} ; } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex