% Copyright 2012-2022, Alexander Shibakov % This file is part of SPLinT % % SPLinT is free software: you can redistribute it and/or modify % it under the terms of the GNU General Public License as published by % the Free Software Foundation, either version 3 of the License, or % (at your option) any later version. % % SPLinT is distributed in the hope that it will be useful, % but WITHOUT ANY WARRANTY; without even the implied warranty of % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the % GNU General Public License for more details. % % You should have received a copy of the GNU General Public License % along with SPLinT. If not, see . % various macros that implement `expandable arithmetic' that can be % used with the tree evaluator macros; % these are here as an example only; a much more complete version of % big integer arithmetic (including division and exponentiation) is % implemented in the `bigintcalc' package by Heiko Oberdiek; I was % unaware of the existence of `bigintcalc' when these macros were written; % note that the tree evaluator macros perform recursive expansion of % their arguments; `bigintcalc' uses \number for the same purpose % (since \number will keep expanding tokens until a non digit is % encountered) % macros that expand into a sequence of given length % #1 is the 1-radix of the number read so far % #2 is the `digit' % #3 is the next 10-radix digit to be processed or `S' \def\unroll#1#2#3{\csname unroll#3\endcsname{#1}{#2}} \expandafter\def\csname unroll0\endcsname#1#2{\unroll{#1#1#1#1#1#1#1#1#1#1}{#2}} \expandafter\def\csname unroll1\endcsname#1#2{\unroll{#2#1#1#1#1#1#1#1#1#1#1}{#2}} \expandafter\def\csname unroll2\endcsname#1#2{\unroll{#2#2#1#1#1#1#1#1#1#1#1#1}{#2}} \expandafter\def\csname unroll3\endcsname#1#2{\unroll{#2#2#2#1#1#1#1#1#1#1#1#1#1}{#2}} \expandafter\def\csname unroll4\endcsname#1#2{\unroll{#2#2#2#2#1#1#1#1#1#1#1#1#1#1}{#2}} \expandafter\def\csname unroll5\endcsname#1#2{\unroll{#2#2#2#2#2#1#1#1#1#1#1#1#1#1#1}{#2}} \expandafter\def\csname unroll6\endcsname#1#2{\unroll{#2#2#2#2#2#2#1#1#1#1#1#1#1#1#1#1}{#2}} \expandafter\def\csname unroll7\endcsname#1#2{\unroll{#2#2#2#2#2#2#2#1#1#1#1#1#1#1#1#1#1}{#2}} \expandafter\def\csname unroll8\endcsname#1#2{\unroll{#2#2#2#2#2#2#2#2#1#1#1#1#1#1#1#1#1#1}{#2}} \expandafter\def\csname unroll9\endcsname#1#2{\unroll{#2#2#2#2#2#2#2#2#2#1#1#1#1#1#1#1#1#1#1}{#2}} \def\unrollS#1#2#3{% \ifx#3F% \xskiptofi{#1S}% \else \xskiptofi{G{#1}}% \fi } \def\sequence#1#2{\expandafter\s@quence\expandafter{\number#1}{#2}} \def\s@quence#1#2{\unrollbegin{}{#2}{}#1} \def\unrollbegin#1#2#3#4#5{% \ifx#5S% \ifx#40% \yybreak@{S}% \else \yybreak@{\unroll{#1}{#2}{#3}#4#5}% \fi \else \yybreak{\unroll{#1}{#2}{#3}#4#5}% \yycontinue } % macros that count the number of non-S in a sequence \def\startconversion#1{% \ifx#1S% \xskiptofi{0}% \else \xskiptofi{\convertone{}{}}% \fi } \def\convertzer@#1#2#3{% \ifx#3S% \xskiptofi{#1}% \else \xskiptofi{\convertone{0#1}{#2}}% \fi } \def\convertzero#1#2#3{% \ifx#3S% \xskiptofi{\convertzer@{#1}{}#2S}% \else \xskiptofi{\convertone{#1}{#2}}% \fi } \def\convertone#1#2#3{% \ifx#3S% \xskiptofi{\convertzero{1#1}{}#2S}% \else \xskiptofi{\converttwo{#1}{#2}}% \fi } \def\converttwo#1#2#3{% \ifx#3S% \xskiptofi{\convertzero{2#1}{}#2S}% \else \xskiptofi{\convertthree{#1}{#2}}% \fi } \def\convertthree#1#2#3{% \ifx#3S% \xskiptofi{\convertzero{3#1}{}#2S}% \else \xskiptofi{\convertfour{#1}{#2}}% \fi } \def\convertfour#1#2#3{% \ifx#3S% \xskiptofi{\convertzero{4#1}{}#2S}% \else \xskiptofi{\convertfive{#1}{#2}}% \fi } \def\convertfive#1#2#3{% \ifx#3S% \xskiptofi{\convertzero{5#1}{}#2S}% \else \xskiptofi{\convertsix{#1}{#2}}% \fi } \def\convertsix#1#2#3{% \ifx#3S% \xskiptofi{\convertzero{6#1}{}#2S}% \else \xskiptofi{\convertseven{#1}{#2}}% \fi } \def\convertseven#1#2#3{% \ifx#3S% \xskiptofi{\convertzero{7#1}{}#2S}% \else \xskiptofi{\converteight{#1}{#2}}% \fi } \def\converteight#1#2#3{% \ifx#3S% \xskiptofi{\convertzero{8#1}{}#2S}% \else \xskiptofi{\convertnine{#1}{#2}}% \fi } \def\convertnine#1#2#3{% \ifx#3S% \xskiptofi{\convertzero{9#1}{}#2S}% \else \xskiptofi{\convertzero{#1}{#2#3}}% \fi } % list evaluator \def\expander#1#2{% \ifcat\noexpand#2\relax \xskiptofi{\@xp@nder#1}% \else \ifx#2G% \grabexpanderpostfix{#1}% \else \xskiptofifi{#1}% \fi \fi #2% } \def\@xpander#1#2{% \ifcat\noexpand#2\relax \xskiptofi{\expandafter\expander\expandafter{\expandafter#1\expandafter}#2}% \else \xskiptofi{\@xpander{#1\expandafter#2}}% \fi } \def\@xp@nder#1{% \ifcat\noexpand#1\relax \xskiptofi{\expandafter\expander\expandafter{\expandafter}}% \else \xskiptofi \@xpander% \fi #1% } \def\grabexpanderpostfix#1\else#2\fi\fi G#3{% \fi\fi\expander{#1#3}% } % the next two macros set up the multiplication and addition tables in the form of % \xdigit a b c which expands to the two digits of a\times b + c % and % \sdigit a b c which expands to the two digits of a + b + c % where c \in \{0, 1\}. % altogether there are 1200 sequences in use. this number can be % reduced to under 500 at the expense of more sophisticated % conditionals: % o in the case of \xdigit, a == 1 or b == 1 reduces \xdigit a b c to % \sdigit a c 0 (for b == 1), whereas a == 0 or b == 0 makes % \xdigit a b c {c}{0}, finally for a == 2 or b == 2, \xdigit a b c is % the same as \sdigit a a c (if b == 2); in addition, \xdigit a b 0 % and \xdigit a b 1 are the same for all pairs (a,b) other than (3,3) % and (7,7); thus, the number of \xdigit sequences can be reduced to % 261 (by also requiring that a < b) % o similar techniques can be used to reduce the number of \sdigit % sequences necessary to under 60 \def\setxdigitmachine{% \tempca=0 \loop \ifnum10>\tempca \expandafter\setxdcs\expandafter0\expandafter0\the\tempca \else \ifnum100>\tempca \expandafter\setxdcs\expandafter0\the\tempca \else \expandafter\setxdcs\the\tempca \fi \fi \advance\tempca\@ne \ifnum1000>\tempca \repeat } \def\setsdigitmachine{% \tempca=0 \loop \ifnum10>\tempca \expandafter\setsdcs\expandafter0\expandafter0\the\tempca \else \ifnum100>\tempca \expandafter\setsdcs\expandafter0\the\tempca \else \expandafter\setsdcs\the\tempca \fi \fi \advance\tempca\@ne \ifnum200>\tempca \repeat } % #1: carry % #2: the first summand % #3: the second summand % the result: % {sum digit}{new carry} \def\setsdcs#1#2#3{% \tempcb=#2 \advance\tempcb#3 \advance\tempcb#1 \ifnum10>\tempcb \expandafter\edef\csname sdigit#2#3#1\endcsname{\expandafter\parenthesize\expandafter0\the\tempcb}% \else \expandafter\edef\csname sdigit#2#3#1\endcsname{\expandafter\parenthesize\the\tempcb}% \fi } % #1: the first multiplier % #2: the second multiplier % #3: carry % the result: % {product digit}{new carry} \def\setxdcs#1#2#3{% \tempcb=#1 \multiply\tempcb#2 \advance\tempcb#3 \ifnum10>\tempcb \expandafter\edef\csname xdigit#1#2#3\endcsname{\expandafter\parenthesize\expandafter0\the\tempcb}% \else \expandafter\edef\csname xdigit#1#2#3\endcsname{\expandafter\parenthesize\the\tempcb}% \fi } \def\parenthesize#1#2{{#2}{#1}} \setxdigitmachine \setsdigitmachine % #1: carryover % #2: shift % #3: multiplier % #4: carry % #5: digits so far % #6: next digit \def\smallmultiply#1#2#3#4#5#6{% \ifx#6F% \ifx#40% \yybreak@{\attachnxtnumber#1{#2#5}}% \else \yybreak@{\attachnxtnumber#1{#2#5#4}}% \fi \else \yybreak{\expandafter\expandafter\expandafter \sm@llmultiply\csname xdigit#3#6#4\endcsname{#1}{#2}{#3}{#5}}% \yycontinue } \def\sm@llmultiply#1#2#3#4#5#6{% \smallmultiply{#3}{#4}{#5}{#2}{#6#1}% } % carryover: % {number1}{number2}{shift}{product so far} % #1: number1 % #2: number2 % #3: shift % #4: product so far % #5; new number \def\attachnxtnumber#1#2#3#4#5{% \startnxtproduct{#1}{#2}{#30}{#4#5F}#1% } % #1: number1 % #2: number2 % #3: shift % #4: product so far % #5; next digit \def\startnxtproduct#1#2#3#4#5{% \ifx#5F% \xskiptofi{\addmultiples#4G}% \else \xskiptofi{\expandafter\sm@llm@ltiply\expandafter{\expandafter{\eatone#1}{#2}{#3}{#4}}{#3}{#5}{0}{#2}}% \fi } % #1: carryover % #2: shift % #3: multiplier % #4: carry % #5: number2 % #6: remainder of the number \def\sm@llm@ltiply#1#2#3#4#5#6F{% \smallmultiply{#1}{#2}{#3}{#4}{}#5% } \def\addmultiples#1F#2{% \ifx#2G% \xskiptofi{\postprocesssum{#1}}% \else \xskiptofi{\startnxtsum{#1}#2}% \fi } \def\postprocesssum#1#2{% #2{#1}% } % the summation macro below can be used for subtraction, as well, % using a well known 9-complement + 1 technique (compare two numbers, % take the complement of the smaller, add 1, add the results, remove % the first nonzero digit of the sum; the result of the comparison % determines the sign of the sum; \def\startnxtsum#1#2F{% \startnxts@m{}{0}{#1}{#2}% } % #1: sum so far % #2: carry % #3: first number % #4: second number \def\startnxts@m#1#2#3#4{% \ifx F#3F% the first number is exhausted \ifx F#4F% both numbers are exhausted: finished \yybreak@{\addmultiples#1#2F}% \else % the second number is still non empty \yybreak@{\makesimples@m{#1}{#2}0F#4F}% \fi \else \ifx F#4F% the second number is exhausted \yybreak@{\makesimples@m{#1}{#2}#3F0F}% \else % both numbers are non empty \yybreak@{\makesimples@m{#1}{#2}#3F#4F}% \fi \yycontinue } % #1: sum so far % #2: carry % #3: first digit % #4: rest of first number % #5: second digit % #6: rest of second number \def\makesimples@m#1#2#3#4F#5#6F{% \expandafter\expandafter\expandafter\makesimpl@s@m\csname sdigit#3#5#2\endcsname{#1}{#4}{#6}% } % #1: digit % #2: carry % #3: sum so far % #4: first number % #5: second mumber \def\makesimpl@s@m#1#2#3#4#5{% \startnxts@m{#3#1}{#2}{#4}{#5}% } % #1: prefix % #2: postfix % #3: digits \def\reversepp#1#2#3{% \r@versepp{#1}{#2}{}#3R% } \def\r@versepp#1#2#3#4{% \ifx#4R% \xskiptofi{#1#3#2}% \else \xskiptofi{\r@versepp{#1}{#2}{#4#3}}% \fi } \def\xmul#1#2{% \reversepp{\xm@l}{F}{#2F#1}% } \def\xm@l#1F#2F{% \startnxtproduct{#1F}{#2F}{}{}#1F{\reversepp{\eatzeros}{F}}% } \def\xsum#1#2{% \reversepp{\addmultiples}{FG{\reversepp{\eatzeros}{F}}}{#2F#1}% } \def\eatzeros#1{% to remove zero carry digits \ifx#10% \yybreak\eatzeros \else \ifx#1F% \yybreak@0% \else \yybreak@{\removepostfix#1}% \fi \yycontinue } \def\removepostfix#1F{#1}