diff options
author | Norbert Preining <norbert@preining.info> | 2021-05-18 03:01:12 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2021-05-18 03:01:12 +0000 |
commit | 97eca13a92a0b8ad423b606c8970c9ce64d9b2bf (patch) | |
tree | 7275ec8351efb8f8108d65ec87d4cc81512a295a /macros/latex/contrib/bnumexpr | |
parent | d999da47ff375df843c0c9bbfd5911236dd9d40a (diff) |
CTAN sync 202105180301
Diffstat (limited to 'macros/latex/contrib/bnumexpr')
-rw-r--r-- | macros/latex/contrib/bnumexpr/README.md | 37 | ||||
-rw-r--r-- | macros/latex/contrib/bnumexpr/bnumexpr.dtx | 1231 | ||||
-rw-r--r-- | macros/latex/contrib/bnumexpr/bnumexpr.pdf | bin | 96634 -> 131719 bytes |
3 files changed, 928 insertions, 340 deletions
diff --git a/macros/latex/contrib/bnumexpr/README.md b/macros/latex/contrib/bnumexpr/README.md index a7db8bf12c..cd161f7cb4 100644 --- a/macros/latex/contrib/bnumexpr/README.md +++ b/macros/latex/contrib/bnumexpr/README.md @@ -1,5 +1,5 @@ | Source: bnumexpr.dtx -| Version: v1.4a, 2021/05/13 (doc: 2021/05/13) +| Version: v1.5, 2021/05/17 (doc: 2021/05/17) | Author: Jean-Francois Burnol | Info: Expressions with big integers | License: LPPL 1.3c @@ -20,7 +20,8 @@ these extensions: - comma separated expressions, - the space character as well as the underscore may serve to separate groups of digits, -- optional conversion of output to hexadecimal. +- optional conversion of output to hexadecimal, +- customizability and extendibility of the syntax. The expression parser is a scaled-down variant from the `\xintiiexpr...\relax` @@ -55,21 +56,26 @@ Run `"etex bnumexpr.dtx"` to extract these files: : change history. `bnumexpr.tex` - : can be used to generate the documentation: + : can be used to generate the documentation - : - with latex+dvipdfmx: `"latex bnumexpr.tex"` (thrice) then - `"dvipdfmx bnumexpr.dvi"`. +To generate the documentation: - : - with pdflatex: `"pdflatex bnumexpr.tex"` (thrice). + - with latex+dvipdfmx: `"latex bnumexpr.tex"` (thrice) then + `"dvipdfmx bnumexpr.dvi"`. - : In both cases files `README.md` and `bnumexprchanges.tex` must - be located in the same repertory as `bnumexpr.tex` and `bnumexpr.dtx`. + - with pdflatex: `"pdflatex bnumexpr.tex"` (thrice). -without `bnumexpr.tex`: - : `"pdflatex bnumexpr.dtx"` (thrice) extracts all files and +In both cases files `README.md` and `bnumexprchanges.tex` must +be located in the same repertory as `bnumexpr.tex` and `bnumexpr.dtx`. + +Without `bnumexpr.tex`: + + - `"pdflatex bnumexpr.dtx"` (thrice) extracts all files and simultaneously generates the pdf documentation. -Finishing the installation: +Final steps: + +- move files to appropriate destination: bnumexpr.sty --> TDS:tex/latex/bnumexpr/ @@ -78,6 +84,8 @@ Finishing the installation: bnumexpr.pdf --> TDS:doc/latex/bnumexpr/ README.me --> TDS:doc/latex/bnumexpr/ +- discard auxiliary files generated during compilation. + License ======= @@ -96,9 +104,8 @@ This Work has the LPPL maintenance status "author-maintained". The Author and Maintainer of this Work is Jean-Francois Burnol. -This Work consists of the main source file `bnumexpr.dtx` -and the derived files +This Work consists of the main source file and its derived files - bnumexpr.sty, bnumexpr.pdf, bnumexpr.tex, bnumexprchanges.tex, - and README.md + bnumexpr.dtx, bnumexpr.sty, bnumexpr.pdf, bnumexpr.tex, + bnumexprchanges.tex, README.md diff --git a/macros/latex/contrib/bnumexpr/bnumexpr.dtx b/macros/latex/contrib/bnumexpr/bnumexpr.dtx index 23ead1e2e1..7c37bb2449 100644 --- a/macros/latex/contrib/bnumexpr/bnumexpr.dtx +++ b/macros/latex/contrib/bnumexpr/bnumexpr.dtx @@ -1,17 +1,17 @@ % -*- coding: utf-8-unix; time-stamp-format: "%02d-%02m-%:y %02H:%02M:%02S %Z"; sentence-end-double-space: t; -*- %<*dtx> -\def\bnedtxtimestamp {Time-stamp: <13-05-2021 12:37:08 CEST>} +\def\bnedtxtimestamp {Time-stamp: <17-05-2021 14:16:23 CEST>} \iffalse %</dtx> %<*drv> %% --------------------------------------------------------------- -\def\bnedocdate {2021/05/13}% package bnumexpr documentation date -\def\bnepackdate{2021/05/13}% package bnumexpr date -\def\bneversion {1.4a} % package bnumexpr version +\def\bnedocdate {2021/05/17}% package bnumexpr documentation date +\def\bnepackdate{2021/05/17}% package bnumexpr date +\def\bneversion {1.5} % package bnumexpr version %</drv> %<*readme>-------------------------------------------------------- | Source: bnumexpr.dtx -| Version: v1.4a, 2021/05/13 (doc: 2021/05/13) +| Version: v1.5, 2021/05/17 (doc: 2021/05/17) | Author: Jean-Francois Burnol | Info: Expressions with big integers | License: LPPL 1.3c @@ -32,7 +32,8 @@ these extensions: - comma separated expressions, - the space character as well as the underscore may serve to separate groups of digits, -- optional conversion of output to hexadecimal. +- optional conversion of output to hexadecimal, +- customizability and extendibility of the syntax. The expression parser is a scaled-down variant from the `\xintiiexpr...\relax` @@ -67,21 +68,26 @@ Run `"etex bnumexpr.dtx"` to extract these files: : change history. `bnumexpr.tex` - : can be used to generate the documentation: + : can be used to generate the documentation - : - with latex+dvipdfmx: `"latex bnumexpr.tex"` (thrice) then - `"dvipdfmx bnumexpr.dvi"`. +To generate the documentation: - : - with pdflatex: `"pdflatex bnumexpr.tex"` (thrice). + - with latex+dvipdfmx: `"latex bnumexpr.tex"` (thrice) then + `"dvipdfmx bnumexpr.dvi"`. - : In both cases files `README.md` and `bnumexprchanges.tex` must - be located in the same repertory as `bnumexpr.tex` and `bnumexpr.dtx`. + - with pdflatex: `"pdflatex bnumexpr.tex"` (thrice). -without `bnumexpr.tex`: - : `"pdflatex bnumexpr.dtx"` (thrice) extracts all files and +In both cases files `README.md` and `bnumexprchanges.tex` must +be located in the same repertory as `bnumexpr.tex` and `bnumexpr.dtx`. + +Without `bnumexpr.tex`: + + - `"pdflatex bnumexpr.dtx"` (thrice) extracts all files and simultaneously generates the pdf documentation. -Finishing the installation: +Final steps: + +- move files to appropriate destination: bnumexpr.sty --> TDS:tex/latex/bnumexpr/ @@ -90,6 +96,8 @@ Finishing the installation: bnumexpr.pdf --> TDS:doc/latex/bnumexpr/ README.me --> TDS:doc/latex/bnumexpr/ +- discard auxiliary files generated during compilation. + License ======= @@ -108,11 +116,10 @@ This Work has the LPPL maintenance status "author-maintained". The Author and Maintainer of this Work is Jean-Francois Burnol. -This Work consists of the main source file `bnumexpr.dtx` -and the derived files +This Work consists of the main source file and its derived files - bnumexpr.sty, bnumexpr.pdf, bnumexpr.tex, bnumexprchanges.tex, - and README.md + bnumexpr.dtx, bnumexpr.sty, bnumexpr.pdf, bnumexpr.tex, + bnumexprchanges.tex, README.md %</readme>-------------------------------------------------------- %<*!readme> @@ -122,12 +129,37 @@ and the derived files %% %</!readme> %<*changes>------------------------------------------------------- +\item[1.5 (2021/05/17)] + \begin{itemize} + \item \textbf{breaking change:} the power operators act now in a right + associative way; this has been announced at \xintexprname as a probable + future evolution, and is implemented in anticipation here now. + \item \textbf{fix two bugs} (imported from upstream \xintexprname) regarding + hexadecimal input: impossibility to use |"\foo| syntax (one had to do + |\expandafter"\foo| which is unexpected constraint; a very longstanding + \xintexprname bug) and issues with leading zeros (since \xintexprname |1.2m|). + \item renamed |\bnumexprsetup| into |\bnumsetup|; the former remains + available but is deprecated. + \item the customizability and extendibility is now total: + \begin{enumerate} + \item |\bnumprintone|, |\bnumprintonetohex|, |\bnumprintonesep|, |\bnumhextodec|, + \item |\bnumdefinfix| which allows to add extra infix operators, + \item |\bnumdefpostfix| which allows to add extra postfix operators. + \end{enumerate} + \item |\bnumsetup|, |\bnumdefinfix|, |\bnumdefpostfix| obey the + |\xintglobaldefstrue| and |\xintverbosetrue| settings. + \item documentation is extended, providing details regarding the precedence + model of the parser, as inherited from upstream \xintexprname; also an + example of usage of |\bnumsetup| is included on how to transform + |\bnumeval| into a calculator with fractions. + \end{itemize} + \item[1.4a (2021/05/13)] \begin{itemize} \item fix undefined control sequences errors encountered by the parser in case of either extra or missing closing parenthesis (due to a problem in technology transfer at |1.4| from upstream \xintexprname). - \item fix |\BNE_Op_opp| must now be f-expandable (also caused as a + \item fix |\BNE_Op_opp| must now be \emph{f}-expandable (also caused as a collateral to the technology transfer). \item fix user documentation regarding the constraints applying to the user replacement macros for the core algebra, as they have changed at |1.4|. @@ -366,6 +398,16 @@ pdfstartview=FitH,% pdfpagemode=UseOutlines} \usepackage{bookmark} +% importé de xint 2021/05/14 +\newcommand\RaisedLabel[2][6]{% +\vspace*{-#1\baselineskip}% +\begingroup + \let\leavevmode\relax\phantomsection + \label{#2}% +\endgroup +\vspace*{#1\baselineskip}% +} + %---- \verb, and verbatim like `environments'. \MicroFont et \MacroFont \makeatletter \def\MicroFont {\ttfamily\cdbx@SetSoftWrapBox\color{smallverbcolor}} @@ -397,43 +439,27 @@ pdfpagemode=UseOutlines} \def\makestarlowast {\let*\lowast\catcode`\*\active}% \catcode`* 12 -%------------obsolete------------ -%--- straight quotes, added (finally...) Nov 4, 2014 -% \begingroup\makeatletter -% \catcode`\'\active -% \catcode`\`\active -% \@firstofone {\endgroup -% \def\makequotesstraight {% textcomp package has been loaded by newtxtt -% \let`\textasciigrave -% \let'\textquotesingle -% \catcode39\active -% \catcode96\active }% -% } -%------------ - -% Tentative, mardi 09 septembre 2014 à 22:41:28 - -% Modifié mardi 13 octobre 2015 à 15:24:00 pour utilisable en -% argument, et en particulier dans un \footnote{}. Mais utilise -% \scantokens et macro délimitée maintenant. - -%\def\jf@makeletter #1{\catcode`#111\relax } - \def\verb #1% {% \relax\leavevmode\null \begingroup \MicroFont - \catcode`_ 11 \catcode`! 11 \catcode`: 11 - \catcode`< 11 \catcode`> 11 - % \scantokens will have a result of inserting a space after cs's. - % hence the need to have the catcodes of things like _ right. - % I also need < for > for one occasion in code comments - % naturally won't work in footnotes though. - % this code is truly not satisfying, but enough for my needs here. - \def\@jfverb ##1#1{\let\do\@makeother \dospecials + \def\@makeletter##1{\catcode`##1=11\relax}% + % \scantokens will have a result of inserting a space after cs's. + % hence the need to have the catcodes of things like _ right. + % I also need < for > for code comments + % \dospecials at begin document + % \do\ \do\\\do\{\do\}\do\$\do\&\do\#\do\^\do\_\do\%\do\~\do\| + \odef\dospecials{\dospecials\do\:\do\<\do\>\do\-\do\+}% + % naturally won't work in footnotes though. + % this code is truly not satisfying, but enough for my needs here. + \catcode`\\ 11 \catcode`## 11 + % I don't do \catcode`\% 11 to avoid surprises in code comments + % if my |...| has a linebreak + \def\@jfverb ##1#1{\let\do\@makeletter \dospecials + % lowering a bit the * \makestarlowast - %\let\do\do@noligs \verbatim@nolig@list % not needed here + %\let\do\do@noligs \verbatim@nolig@list % not needed here \@vobeyspaces\everyeof{\noexpand}% \expandafter\@@jfverb\scantokens{##1}\relax}% \@jfverb @@ -455,17 +481,49 @@ pdfpagemode=UseOutlines} \makeatother +% Lundi 17 mai 2021 à 13:58:27 +% J'ajoute un export automatique des exemples qui servira de poor man test +% suite +\newwrite\tests +\immediate\openout\tests=\jobname-out.txt + % mardi 13 octobre 2015 à 15:32:51 % le | n'est pas encore actif ici! \catcode`| \active +% first one uses \bnumexpr deliberately with no \bnethe to test 1.4 \def\bneshow #1% - {|\thebnumexpr#1\relax|$$\color{digitscolor}\bnumexpr#1\relax$$} + {|\thebnumexpr#1\relax|% + \edef\x{\bnumexpr#1\relax}% + \immediate\write\tests{\detokenize{\thebnumexpr#1\relax}^^J\x}% + $$\color{digitscolor}\x$$\ignorespaces} + \def\bneshowthe #1% + {|\bnethe\bnumexpr#1\relax|% + \edef\x{\bnethe\bnumexpr#1\relax}% + \immediate\write\tests{\detokenize{\bnethe\bnumexpr#1\relax}^^J\x}% + $$\color{digitscolor}\x$$\ignorespaces} \def\bneshoweval #1% - {|\bnumeval{#1}|$$\color{digitscolor}\bnumeval{#1}$$} + {|\bnumeval{#1}|% + \edef\x{\bnumeval{#1}}% + \immediate\write\tests{\detokenize{\bnumeval{#1}}^^J\x}% + $$\color{digitscolor}\x$$\ignorespaces} \def\bneshowevaltohex #1% - {|\evaltohex{#1}|$$\color{digitscolor}\evaltohex{#1}$$} + {|\evaltohex{#1}|% + \edef\x{\evaltohex{#1}}% + \immediate\write\tests{\detokenize{\evaltohex{#1}}^^J\x}% + $$\color{digitscolor}\x$$\ignorespaces} \catcode`| 12 +% toujours pénible de devoir se battre avec les espaces verticaux de TeX +\AtBeginDocument{% +% \message{ICI below: \the\belowdisplayskip^^J% +% belowshort: \the\belowdisplayshortskip^^J% +% above: \the\abovedisplayskip^^J% +% aboveshor: \the\abovedisplayshortskip^^J% +% }% + \belowdisplayskip\belowdisplayshortskip + \abovedisplayskip\abovedisplayshortskip +} + \DeclareRobustCommand\csa [1]{{\char92\detokenize{#1}}} \newcommand\csh[1]{\texorpdfstring{\csa{#1}}{\textbackslash\detokenize{#1}}} @@ -480,53 +538,30 @@ pdfpagemode=UseOutlines} {bnumexpr}% \xspace }% +\def\bnumnameuserdoc + {\texorpdfstring + {\hyperref[titlepage]{{\color{PineGreen}\ttzfamily bnumexpr}}} + {bnumexpr}% + \xspace }% + \def\xintkernelname {\href{http://www.ctan.org/pkg/xint}{xintkernel}\xspace }% -\def\xintname - {\href{http://www.ctan.org/pkg/xint}{xint}\xspace }% \def\xintcorename {\href{http://www.ctan.org/pkg/xint}{xintcore}\xspace }% +\def\xintname + {\href{http://www.ctan.org/pkg/xint}{xint}\xspace }% +\def\xintfracname + {\href{http://www.ctan.org/pkg/xint}{xintfrac}\xspace }% \def\xintexprname {\href{http://www.ctan.org/pkg/xintexpr}{xintexpr}\xspace }% \def\xintbinhexname {\href{http://www.ctan.org/pkg/xint}{xintbinhex}\xspace }% - -% \centeredline: OUR OWN LITTLE MACRO FOR CENTERING LINES -% ======================================================= - -% 7 mars 2013 -% ----------- -% -% This macro allows to conveniently center a line inside a paragraph and still -% allow use therein of \verb or other macros changing catcodes. -% A proposito, the \LaTeX \centerline uses \hsize and not \linewidth ! -% (which in my humble opinion is bad) - -% Actually my \centeredline works nicely in list environments. - -% \ignorespaces ajouté le 9 juin 2013. - -% Note: \centeredline creates a group - -\makeatletter -\newcommand*\centeredline {% - \ifhmode \\\relax - \def\centeredline@{\hss\egroup\hskip\z@skip\ignorespaces }% - \else - \def\centeredline@{\hss\egroup }% - \fi - \afterassignment\@centeredline - \let\next=} -\def\@centeredline - {\hbox to \linewidth \bgroup \hss \bgroup \aftergroup\centeredline@ } - - \frenchspacing \usepackage{bnumexpr} - +\usepackage{xintfrac} \usepackage{etoc} - +\usepackage{centeredline} \usepackage{framed} \makeatletter\let\check@percent\relax\makeatother \begin{document} @@ -540,6 +575,8 @@ pdfpagemode=UseOutlines} The \bnumname package\par } +\RaisedLabel{titlepage} + {\centering \textsc{Jean-François Burnol}\par \footnotesize @@ -563,12 +600,20 @@ it is analogous to \item it computes powers (with either |**| or |^| as infix operator), \item it computes factorials (with |!| as postfix operator), \item it has an operator |//| for floored division and |/:| for the - associated modulo, -\item the space character is ignored and can thus be used to separate in the + associated modulo (like {\catcode`\% 12 |%|} in Python which we can't use for obvious reasons), +\item the space character is ignored% +% +\footnote{It is not completely ignored, |\count37<space>| will automatically + be prefixed by |\number| and the space token delimits the integer indexing + the count register. Also, devious inputs using nested braces around spaces + may create unexpected internal situations and even break the parser.} +% +and can thus be used to separate in the source blocks of digits for better readability of long numbers, \item also the underscore |_| may be used as visual digit separator, -\item comma separated expressions are allowed. +\item comma separated expressions are allowed, +\item syntax is customizable and extendible. \end{itemize} There is also a more core-level |\bnumexpr...\relax| construct% \footnote{Since |1.4|, one can use |\bnumexpr...\relax| directly in @@ -596,22 +641,165 @@ installations for \verb?latex|pdflatex? and also for \verb?xelatex|lualatex?. Further, at |1.4| (|2021/05/12|) the |\expanded| primitive is required. It is available in all engines since \TeX Live 2019. -By default the arithmetic operations are executed via the \xintcorename -macros, but via option |custom| and usage of |\bnumexprsetup| it is -possible to replace them by expandable macros of a custom origin. - \section{Examples} +With certain languages, Babel with PDF\LaTeX\ may make some characters active, for example the +|!| with the French language. It must then be input as |\string!|. \noindent\bneshow {---1 208 637 867 * (2 187 917 891 - 3 109 197 072)}% -\bneshoweval {(13_8089_1090-300_1890_2902)*(1083_1908_3901-109_8290_3890)}% +\bneshowthe {(13_8089_1090-300_1890_2902)*(1083_1908_3901-109_8290_3890)}% \bneshoweval {(92_874_927_979**5-31_9792_7979**6)/30!}% \bneshoweval {30!/20!/21/22/23/24/25/(26*27*28*29)}% \bneshoweval {13^50//12^50, 13^50/:12^50}% \bneshoweval {13^50/12^50, 12^50}% \bneshoweval {(1^10+2^10+3^10+4^10+5^10+6^10+7^10+8^10+9^10)^3}% \bneshoweval {100!/36^100}% -\bneshoweval {"10*"100*"1000*"A0000, 16^(1+2+3+4)*10}% -\bneshowevaltohex{"7FFFFFFF+1, "400^3, "ABCDEF*"FEDCBA}% +\bneshoweval {"0010*"0100*"1000*"A0000, 16^(1+2+3+4)*10}% +\bneshowevaltohex{"7FFFFFFF+1, "0400^3, "ABCDEF*"0000FEDCBA, 1234}% +\bneshoweval {"\evaltohex{12345678}FFFF, 000012345679*16**4-1} + +\section{The \texttt{custom} package option and \csh{bnumsetup}} + +Package \bnumname needs that some \emph{big integer engine} provides the macros doing +the actual computations. By default, it loads package \xintcorename (a subset +of \href{http://www.ctan.org/pkg/xint}{xint}) and uses \csa{bnumsetup} in +the following way: +\begin{verbatim} + \usepackage{xintcore} + \bnumsetup{add=\xintiiAdd, sub=\xintiiSub, mul=\xintiiMul, + divround=\xintiiDivRound, div=\xintiiDivFloor, + mod=\xintiiMod, pow=\xintiiPow, fac=\xintiiFac, + opp=\xintiiOpp} +\end{verbatim} + +If +using \csa{bnumsetup}, it is not necessary to specify all keys, +for example one can do |\bnumsetup{mul=\MySlowerMul}|, and only +multiplication will be changed. + +Naturally it is up to the user to load the appropriate package for +the alternative macros. + +The macros serving as custom user replacements% +% +\footnote{% + The replacement macros will by default receive arguments composed of + explicit digit tokens, with no leading zeros, with at most one leading minus + sign and no plus sign. + + The format of these arguments will depend on what the other customized + macros do. For example if |opp=\foo| is used and the custom |\foo| inserts a + |+| when taking the opposite of a negative number, then the other custom + macros for arithmetic (and the |\foo| macro itself) must be able to handle + arguments starting optionally with such a |+|.% +} +must be \emph{f}-expandable, +except for the computation of factorials, which only has to be +\emph{x}-expandable.% +% +\footnote{Prior to |1.4|, only \emph{x}-expandability was required. It is easy however to use an |\expanded| based wrapper to convert \emph{x}-expandable macros into \emph{f}-expandable ones.} +% + +Macro |\bnumsetup| can be used multiple times in the same document, thus +allowing to switch math engines or to remap operators to some other arithmetic +macros of the same math engine. Its effect obeys the local scope.% +% +\footnote{The effect is global if under |\xintglobaldefstrue| setting.} + +The hexadecimal input via the |"| prefix is converted internally to decimal +notation using |\xintHexToDec| from package \xintbinhexname, and customization +is possible via redefinition of |\bnumhextodec|, whose default is to be an +alias to |\xintHexToDec|. + +The final conversion back to hexadecimal done by |\evaltohex| is handled by +|\bnumprintonetohex| which defaults to |\xintDecToHex|. + +These two steps can thus be customized as will. But the loading of package +\xintbinhexname can not be canceled. + +\section{\csh{bnumprintone}, \csh{bnumprintonetohex}, \csh{bnumprintonesep}} + +The computed values are printed one by one, separated by a comma and a space +(this is customizable as |\bnumprintonesep|), and each value being handed over +to |\bnumprintone|. By default this does nothing else than producing its +argument as is, it can be redefined at will (perhaps using macros such as in +\autoref{sec:printing} to handle the case of very long numbers). + +There is also |\bnumprintonetohex| which is used by |\evaltohex| (this is its +sole difference from |\bnumeval|). Its default definition makes it an alias +to |\xintDecToHex| from package \xintbinhexname. + +\section{Example of customization: let the syntax handle fractions!} + +I will show how to transform |\bnumeval| into a calculator with fractions! We +will use the \xintfracname macros, but coerce them into always producing +fractions in lowest terms (except for powers). For optimization we use +the |[0]| post-fix which speeds-up the input parsing by the \xintfracname +macros. We remove it on output via a custom |\bnumprintone|. + +Note that the |/| operator is associated to |divround| key but of course here +the used macro will simply do an exact division of fractions, not a rounded-to-% +an integer division. This is the whole point of using a macro of our own +choosing! +\begin{verbatim} +\usepackage{xintfrac} +\newcommand\myIrrAdd[2]{\xintIrr{\xintAdd{#1}{#2}}[0]} +\newcommand\myIrrSub[2]{\xintIrr{\xintSub{#1}{#2}}[0]} +\newcommand\myIrrMul[2]{\xintIrr{\xintMul{#1}{#2}}[0]} +\newcommand\myDiv[2]{\xintIrr{\xintDiv{#1}{#2}}[0]} +\newcommand\myDivFloor[2]{\xintDivFloor{#1}{#2}[0]} +\newcommand\myMod[2]{\xintIrr{\xintMod{#1}{#2}}[0]} +\newcommand\myPow[2]{\xintPow{#1}{#2}}% will have trailing [0] +\newcommand\myFac[1]{\xintFac{#1}}% produces trailing [0] +\bnumsetup{add=\myIrrAdd, sub=\myIrrSub, mul=\myIrrMul, + divround=\myDiv, div=\myDivFloor, + mod=\myMod, pow=\myPow, fac=\myFac}% +% % if any operation happened, the result is already irreducible +% % (except power of non-irreducible) so this is overhead: +% \let\bnumprintone\xintIrr +% % but it is safe way to get rid of the trailing [0] +% % else we have to take care of case with no [0] because of no operations +% % well let's do it: +\makeatletter +\def\myPrintOne#1{\@myPrintOne#1[0]\relax} +\def\@myPrintOne#1[0]#2\relax{#1} +\let\bnumprintone\myPrintOne +\makeatother +\end{verbatim} +\begingroup +\newcommand\myIrrAdd[2]{\xintIrr{\xintAdd{#1}{#2}}[0]} +\newcommand\myIrrSub[2]{\xintIrr{\xintSub{#1}{#2}}[0]} +\newcommand\myIrrMul[2]{\xintIrr{\xintMul{#1}{#2}}[0]} +\newcommand\myDiv[2]{\xintIrr{\xintDiv{#1}{#2}}[0]} +\newcommand\myDivFloor[2]{\xintDivFloor{#1}{#2}[0]} +\newcommand\myMod[2]{\xintIrr{\xintMod{#1}{#2}}[0]} +\newcommand\myPow[2]{\xintPow{#1}{#2}}% will have trailing [0] +\newcommand\myFac[1]{\xintFac{#1}}% produces trailing [0] +\bnumsetup{add=\myIrrAdd, sub=\myIrrSub, mul=\myIrrMul, divround=\myDiv, + div=\myDivFloor, mod=\myMod, pow=\myPow, fac=\myFac}% +\makeatletter +\def\myPrintOne#1{\@myPrintOne#1[0]\relax}% +\def\@myPrintOne#1[0]#2\relax{#1}% +\let\bnumprintone\myPrintOne +\makeatother +\bneshoweval{1000000*(1/100+1/2^7-20/5^4)/(1/3-5/7+9/11)^2} +\bneshoweval{(1-1/2)(1-1/3)(1-1/4)(1-1/5)(1-1/6)(1-1/7)} +\bneshoweval{(1-1/3+1/9-1/27-1/81+1/243-1/729+1/2187)^5} +\bneshoweval{(1+1/10)^10 /: (1-1/10)^10} +\bneshoweval{2^-3^4} +\endgroup + +This last example is computed differently than it would be with \xintexprname +|1.4f| because \bnumname |1.5| already applies right associativity to powers, +whereas \xintexprname |1.4f| still applies left associativity. + +Note also that the above changes break |\evaltohex| whose output routine uses +by default |\xintDecToHex| which will choke on fractional input. However it is +not difficult to write a macro applying separately to numerator and +denominator. + +Computations with fractions quickly give birth to big results, see +\autoref{sec:printing} on how to modify |\bnumprintone| to coerce \TeX\ into +wrapping numbers too long for the available width. \section{Differences from \csh{numexpr}} \label{sec:differences} @@ -692,12 +880,12 @@ syntax as in ... \fi \end{verbatim} -the end of line will insert a terminating space token. Again, here -|\bnumeval{...}| must produce an integer acceptable to \TeX, i.e. at most -{\number"7FFFFFFF} in absolute value. +the end of line will (under the normal \LaTeX\ configuration) insert a +terminating space token. Again, here |\bnumeval{...}| must produce an integer +acceptable to \TeX, i.e. at most {\number"7FFFFFFF} in absolute value. \section{Printing big numbers} - +\label{sec:printing} \LaTeX{} will not split long numbers at the end of lines. I personally often use helper macros (not in the package) of the following type: @@ -714,95 +902,254 @@ often use helper macros (not in the package) of the following type: \noindent|\thebnumexpr 1000!\relax=|\phantom{0}{\color{digitscolor}\printnumber{\thebnumexpr 1000!\relax}}\par -\section{Expression syntax} - -It is the expected one with infix operators and parentheses, the -recognized operators being |+|, |-|, |*|, |/| (rounded division), -|^| (power), |**| (power), |//| (by default floored division), -|/:| (the associated modulo) and |!| (factorial). One can input hexadecimal -numbers as in \TeX\ syntax for number assignments, i.e. using a |"| prefix and -only uppercase letters |ABCDEF|. +\section{Expression syntax and its customizability} + +The implemented syntax is the expected one with infix operators and +parentheses, the recognized operators being |+|, |-|, |*|, |/| (rounded +division), |^| (power), |**| (power), |//| (by default floored division), |/:| +(the associated modulo) and |!| (factorial). One can input hexadecimal numbers +as in \TeX\ syntax for number assignments, i.e. using a |"| prefix and only +uppercase letters |ABCDEF|. + +Different computations may be separated by commas. The whole expression is +handled token by token, any component (digit, operator, parentheses... even +the ending |\relax|) may arise on the spot from macro expansions. The +underscore |_| can be used to separate digits in long numbers, for readability +of the input. + +The precedence rules are as expected and detailed in the next section. +Operators on the same level of precedence (like |*|, |/|, |//|, |/:|) behave +in a left-associative way, and these examples behave as e.g.\@ with Python +analogous operators:\newline \bneshoweval{100//3*4, 100*4//3, 100/:3*4, + 100*4/:3, 100//3/:5} + +At |1.5| a change was made to the power operators which became +right-associative. Again, this matches the behaviour e.g.\@ of Python:% +% +\footnote{It had been announced at \xintexprname \texttt{1.4} that probably in + future power operator would become right-associative, so we experiment it + here in \bnumname in advance.}% +% +\newline +\bneshoweval{2^3^4, 2^(3^4)} -The modulo |/:| is by default associated with the floored division |//|, but -using |\bnumexprsetup{mod=...}| it can, like the other operators, be remapped -to any macro of one's choice. +It is possible to customize completely the behaviour of the parser, in two ways: +\begin{itemize} +\item via |\bnumsetup| which has a simple interface to replace the macros + associated to the operators |+|, |-|, |*|, |/|, |//|, |/:|, |**| and |^| by + custom macros, +\item or even more completely via |\bnumdefinfix| and |\bnumdefpostfix| which + allow to add new operators to the syntax! (or overwrite existing ones...) +\end{itemize} -Different computations may be separated by commas. The whole -expression is handled token by token, any component (digit, operator, -parenthesis... even the ending |\relax|) may arise on the spot from -macro expansions. +\section{Precedences} -The precedence rules are the expected ones. Tacit multiplication applies in -front of parentheses, and after them, and it has an elevated precedence -compared to multiplication explicitly induced by |*|. +The parser implements precedence rules based on concepts which are summarized +below. I am providing them for users who will use the customizing macros. +\begin{itemize} +\item an infix operator has two associated precedence levels, say |L| and + |R|, +\item the parser proceeds from left to right, pausing each time it has found a + new number and an operator following it, +\item the parser compares the left-precedence |L| of the new found operator to + the right-precedence |R_last| of the last delayed operation (which already + has one argument and would like to know if it can use the new found one): if + |L| is at most equal to it, the delayed operation is now executed, else the + new-found operation is kept around to be executed first, once it will have + gathered its arguments, of which only one is known at this stage. +\end{itemize} -There is currently no user interface to change precedence levels. The -three operators |/|, |//|, |/:| are at the same level of precedence as the -multiplication |*|. The factorial postfix |!| has highest precedence. The -minus signs inherit the precedence level of the previously encountered infix -operators. +Although there is thus internally all the needed room for sophistication, the +implemented table of precedences simply puts all of multiplication and +division related operations at the same level, which means that left +associativity will apply with these operators. I could see that Python behaves +the same way for its analogous operators. -In case of equal precedence the operations are left-associative,% -% -\footnote{But it has been announced at \xintexprname \texttt{1.4} that - probably in future power operator will become right-associative, and if this - is done, this will be transferred to \bnumname also.} +Here is the default table of precedences as implemented by the package: +\begin{center} +\DeleteShortVerb{\|}% + \begin{tabular}{|c|c|c|} + \multicolumn3{c}{Table of precedences}\\\hline +\MakeShortVerb{\|}% + operator& left &right\\\hline + |+|,|-|& 12&12\\ + |*|,|/|,|//|,|/:|& 14&14\\ + tacit |*|&16&14\\ + |**|, |^|& 18&17\\ + |!| &20&n/a\\\hline + \end{tabular} +\end{center} +Tacit multiplication applies in front of parentheses, and after them, also in +front of count variables or registers. As shown in the table it has an +elevated precedence compared to multiplication explicitly induced by |*|, so +|100/4(9)| is computed as |100/36| and not as |25*9|:\newline +\bneshoweval{100/4(9), (100/4)9, 1000 // (100/4) 9 (1+1) * 13} +More generally |A/B(C)(D)(E)*F| will compute |(A/(B*C*D*E))*F|.% % -hence:\newline \bneshoweval{2^3^4, (2^3)^4, 2^(3^4)} +\footnote{The |B(C)(D)(E)| product will be computed as |B*(C*(D*E))| because + the right-precedence of tacit multiplication is |14| but its left-precedence + is |16|, creating right associativity. As the underlying mathematical + operation is associative this is irrelevant to final result.} + +The unary |-|, as prefix, has a special behaviour: after an infix operator it +will acquire a right-precedence which is the minimum of |12| (i.e.\@ the +precedence of addition and subtraction) and of the right-precedence of the +infix operator. For example |2^-3^4| will be parsed as |2^(-(3^4))|, raising +an error because the parser is by default integer only, but see the section +about |\bnumsetup| which explains how to let |\bnumeval| computes fractions! -The underscore |_| can be used to separate digits in long numbers, for -readability of the input. -\section{Options} +\section{\csh{bnumdefinfix}} -The sole package option is |custom|: it tells \bnumname not to load -package \xintcorename. +It is possible to define infix binary operators of one's own choosing.% +% +\footnote{The effect of |\bnumdefinfix| is global if under |\xintglobaldefstrue| setting.} +% +The syntax is +\centeredline{|\bnumdefinfix|\marg{operator}\marg{\string\macro}\marg{L-prec}\marg{R-prec}} +\begin{description} +\item[\marg{operator}] The characters for the operator, they may be letters or + non-letters, and must not be active or among the special characters |\|, + \textcolor{smallverbcolor}{\{}, \textcolor{smallverbcolor}{\}}, |#| and + {\catcode`\% 12 |%|}. Also, + spaces will be removed.% +% +\footnote{The |_| can be used, but not as first character of the operator, as + it would be mis-construed on usage as part of the previous number, and + ignored as such.}% +% +$^{,}$% +% +{\catcode`# 12 \footnote{It is actually possible to use |#| as an operator + name or a character in such a name but the definition with |\bnumdefinfix| + must then be done either with \expandafter|\string\string#| or + |####|...}}% +$^{,}$% +% +\footnote{Active characters must be prefixed by |\string| both at the time of + the definition and at the time of use. It is probably better to use a toggle which + will turn off the activity, both at time of definition and at time of use.}% +\item[\marg{\string\macro}] The expandable macro (expecting two mandatory + arguments) which is to assign to the infix operator. This macro must be + \emph{f}-expandable. Also it must (if the default package configuration is + not modified for the core operators) produce integers in the ``strict'' + format which is expected by the \xintcorename macros for arithmetic: no + leading zeros, at most one minus sign, no plus sign, no spaces. +\item[\marg{L-prec}] An integer, minimal |4|, maximal |22|, which governs + the left-\hskip0pt precedence of the infix operator. +\item[\marg{R-prec}] An integer, minimal |4|, maximal |22|, which governs the + right-\hskip0pt precedence of the infix operator. +\end{description} +Generally, the two precedences are set to the same value. -\section{\csh{bnumexprsetup}} +Once a multi-character operator is defined, the first characters of its name +can be used if no ambiguity. In case of ambiguity, it is the earliest defined +shortcut which prevails, except for the full name. So for example if |$abc| +operator is defined, and |$ab| is defined next, then |$| and |$a| will still +serve as shortcuts to the original |$abc|, but |$ab| will refer to the newly +defined operator. -Package \bnumname needs that some big integer engine provides the macros doing -the actual computations. By default, it loads package \xintcorename (a subset -of \href{http://www.ctan.org/pkg/xint}{xint}) and uses \csa{bnumexprsetup} in -the following way: +Fully qualified names are never ambiguous, and a shortcut once defined will +change meaning under only these two possibilities: +\begin{itemize} +\item it is re-defined as the full name of a new operator, +\item the original operator to which the shortcut refers is defined again; then + the shortcut is automatically updated to point to the new meaning. +\end{itemize} \begin{verbatim} - \usepackage{xintcore} - \bnumexprsetup{add=\xintiiAdd, sub=\xintiiSub, mul=\xintiiMul, - divround=\xintiiDivRound, div=\xintiiDivFloor, - mod=\xintiiMod, pow=\xintiiPow, fac=\xintiiFac, - opp=\xintiiOpp}% +\def\equals#1#2{\ifnum\pdfstrcmp{#1}{#2}=0 \expandafter1\else + \expandafter0\fi} +% or: +\def\equals#1#2{\expanded{\ifnum\pdfstrcmp{#1}{#2}=0 1\else0\fi}} +\def\differ#1#2{\expanded{\ifnum\pdfstrcmp{#1}{#2}=0 0\else1\fi}} +\bnumdefinfix{==}{\equals}{10}{10} +\bnumdefinfix{!=}{\differ}{10}{10} +\bnumdefinfix{times}{\xintiiMul}{14}{14} +\bnumdefinfix{++}{\xintiiAdd}{19}{19} \end{verbatim} - -If -using \csa{bnumexprsetup}, it is not necessary to specify all keys, -for example one can do |\bnumexprsetup{mul=\MyFasterMul}|, and only -multiplication will be changed. - -Naturally it is up to the user to load the appropriate package for -the alternative macros. - -The macros serving as custom user replacements must be \emph{f}-expandable.% +%\def\equals#1#2{\ifnum\pdfstrcmp{#1}{#2}=0 \expandafter1\else\expandafter0\fi} +\def\equals#1#2{\expanded{\ifnum\pdfstrcmp{#1}{#2}=0 1\else0\fi}} +\def\differ#1#2{\expanded{\ifnum\pdfstrcmp{#1}{#2}=0 0\else1\fi}} +\bnumdefinfix{==}{\equals}{10}{10} +\bnumdefinfix{!=}{\differ}{10}{10} +\bnumdefinfix{times}{\xintiiMul}{14}{14} +\bnumdefinfix{++}{\xintiiAdd}{19}{19} % -\footnote{Prior to |1.4|, only \emph{x}-expandability was required. The author - could relax again the constraint, if asked to do so. Or perhaps simply add - an option for it.} +\bneshoweval{2 + 3! = 5, 2 + (3!) == 8} Notice in the |2+3! = 5| example that +the existence of |!=| prevails on applying the factorial, so this is test +whether |2+3| and |5| differ; it is not a matter of precedence here, but of +input parsing ignoring spaces. And |2+3! == 8| would create an error as after +having found the |!=| operator and now expecting a digit (as there is no |!==| +operator) the parser would find an unexpected |=| and report an error. Hence +the usage of parentheses in the input.% % -(except for the computation of factorials, which only has to be -\emph{x}-expandable). +\footnote{As |!=| is indeed defined out-of-the-box in the \xintexprname + syntax, the |3! == 8| issue applies with |\xinteval| and perhaps I should + add it to the user documentation, as warning.}\newline +\bneshoweval{2^5 == 4 times 8, 11 t 14} +\bneshoweval{100 ++ -10 ^ 3, (100 - 10)^3, 2 ** 5 ++ 3, 2^(5+3)} -They will by default receive arguments composed of explicit digit tokens, with -no leading zeros, with at most one leading minus sign and no plus sign. -The format of these arguments may depend on what the |opp=\foo| replacement -macro does. If the custom |\foo| inserts a |+| when taking the opposite of a -negative number, then the custom macros for arithmetic (and the |\foo| macro -itself) must be able to handle arguments starting optionally with such a |+|. +\section{\csh{bnumdefpostfix}} -Macro |\bnumexprsetup| can be used multiple times in the same document, thus -allowing to switch math engines or to remap operators to some other arithmetic -macros of the same math engine. Its effect obeys the local scope. +It is possible to define postfix unary operators of one's own choosing.% +% +\footnote{The effect of |\bnumdefpostfix| is global if under |\xintglobaldefstrue| setting.} +% +The syntax is +\centeredline{|\bnumdefpostfix|\marg{operator}\marg{\string\macro}\marg{L-prec}} +\begin{description} +\item[\marg{operator}] The characters for the operator name: same + conditions as for |\bnumdefinfix|. Postfix and infix operators share the + same name-space, regarding abbreviated names. +\item[\marg{\string\macro}] The one argument expandable macro to assign to + the postfix operator. This macro only needs to be \emph{x}-expandable. +\item[\marg{L-prec}] An integer, minimal |4|, maximal |22|, which governs the + left-\hskip0pt precedence of the infix operator. +\end{description} +Examples below which use the maximal precedence are typical of what is +expected of a ``function'' (and I even used |.len()| notation with parentheses +in one example, the parentheses are part of the postfix operator name). And +indeed such postfix operators are thus a way to implement functions in +disguise, circumventing the fact that the \bnumname parser will never be +extended to work with functional syntax (for this, see \xintexprname). With +the convention (followed in some examples) that such postfix operators start +with a full stop, but never contain another one, we can chain simply by using +concatenation (no need for parentheses), as there will be no +ambiguity. +\begin{verbatim} +\usepackage{xint}% for \xintiiSum, \xintiiSqrt +\def\myRev#1{\xintNum{\xintReverseOrder{#1}}}% reverse and trim leading zeros +\bnumdefpostfix{$}{\myRev}{22}% the $ will have top precedence +\bnumdefpostfix{:}{\myRev}{4}% the : will have lowest precedence +\bnumdefpostfix{::}{\xintiiSqr}{4}% the :: is a completely different operator +\bnumdefpostfix{.len()}{\xintLength}{22}% () for fun but a single . will be enough! +\bnumdefpostfix{.sumdigits}{\xintiiSum}{22}% .s will abbreviate +\bnumdefpostfix{.sqrt}{\xintiiSqrt}{22}% .sq will be unambiguous (but confusing) +\bnumdefpostfix{.rep}{\xintReplicate3}{22}% .r will be unambiguous +\end{verbatim} +\def\myRev#1{\xintNum{\xintReverseOrder{#1}}}% reverse digits and trim leading zeros +\bnumdefpostfix{$}{\myRev}{22}% $ +\bnumdefpostfix{:}{\myRev}{4}% +\bnumdefpostfix{::}{\xintiiSqr}{4}% +\bnumdefpostfix{.len()}{\xintLength}{22}% +\bnumdefpostfix{.sumdigits}{\xintiiSum}{22}% +\bnumdefpostfix{.sqrt}{\xintiiSqrt}{22}% +\bnumdefpostfix{.rep}{\xintReplicate3}{22}% +\bneshoweval{(2^31).len(), (2^31)., 2^31$, 2^31:, (2^31)$} +\bneshoweval{(2^31).sqrt, 100000000.sq.sq} +\bneshoweval{(2^31).sumdigits, 123456789.s, 123456789.s.s, 123456789.s.s.s} +\bneshoweval{10^10+10000+2000+300+40+5:} +\bneshoweval{1+2+3+4+5+6+7+8+9+10 :: +1 :: *2 :: :: :} +\bneshoweval{123456789.r}\par\vspace*{-\baselineskip}% Toujours LaTeX avec ses + % espaces verticaux TROP GRANDS DEPUIS 30 ANS +\begin{verbatim} +\bnumdefpostfix{.rep}{\xintReplicate5}{22}% .rep modified --> .r too +\end{verbatim} +\bnumdefpostfix{.rep}{\xintReplicate5}{22}% +\bneshoweval{123456789.r} -At |1.4| release there is no option to customize how the hexadecimal input and -output is handled, it goes necessarily via the \xintbinhexname macros. +\clearpage \section{Readme} \begingroup @@ -819,7 +1166,7 @@ output is handled, it goes necessarily via the \xintbinhexname macros. \noindent\kern\parindent\input README.md \endgroup }\x -%\clearpage +\clearpage \section{Changes} %\small @@ -841,61 +1188,53 @@ output is handled, it goes necessarily via the \xintbinhexname macros. %</dtx> %<*package> % -% \section{Package \bnumnameimp implementation} +% \section{\bnumname implementation} % \label{sec:bnumexprcode} % \etocdefaultlines % \localtableofcontents -% \etocmarkbothnouc{Package \bnumnameimp implementation} -% -% Comments are sparse. Actually at |1.4|, there are simply no comments. I -% transferred from \xintexprname its |\expanded| based infra-structure from -% its own |1.4| release of January 2020. +% \etocmarkbothnouc{\bnumnameuserdoc \hyperref[sec:bnumexprcode]{implementation}} % -% Error handling by the parser is kept to a minimum; if something goes wrong, -% the offensive token gets discarded, and it is not even always the case that -% some expandable error message is issued. +% I transferred mid-May 2021 from \xintexprname its |\expanded| based +% infra-structure from its own |1.4| release of January 2020 and bumped +% version to |1.4|. Also I added support for hexadecimal input and output, via +% unconditional loading of \xintbinhexname. % -% A few comments at |1.4a|: +% A few comments added here at |1.4a|: % \begin{itemize} % \item It looked a bit costly and probably would have been mostly useless to % end users to integrate in \bnumname support for nested structures via % square brackets [, ], which is in \xintexprname since its January 2020 -% |1.4| release. But some underlying related architecture remains here; we -% could make some micro-gains probably but diverging from upstream code +% |1.4| release. But some of the related architecture remains here; we +% could make some gains probably but diverging from upstream code % would make maintenance a nightmare. +% \item Formerly, the |\csname...\endcsname| encapsulation technique had the +% after-effect to allow the macros supporting the infix operators to be only +% \emph{x}-expandable. At |1.4|, I could have still allowed support macros +% being only \emph{x}-expandable, but, keeping in sync with upstream, I have +% used only a |\romannumeral| trigger and did not insert an |\expanded|, so +% now the support macros must be \emph{f}-expandable. The |1.4a| release +% fixes the related user documentation of |\bnumsetup| which was not +% updated at |1.4|. The support macro for the factorial however needs only +% be \emph{x}-expandable. +% \item Also, I simply do not understand why the legacy (|1.2e|) user documentation +% said that the support macros were supposed to \emph{f}-expand their arguments, as +% they are used only with arguments being explicit digit tokens (and +% optional minus sign). % \item The |\bnumexpr\relax| syntax creating an empty ople is by itself now % legal, and can be injected (comma separated) in an expression, keeping it % invariant, however |\bnumeval{}| ends in a |File ended while scanning use % of \BNE_print_c| error because |\BNEprint| makes the tacit requirement % that the 1D ople to output has at least one item. -% \item Formerly, the |\csname...\endcsname| encapsulation technique had the -% after-effect to allow the macros supporting the infix operators to be only -% x-expandable. At |1.4|, I could have still allowed only x-expandable -% macros, but, keeping in sync with upstream, I have used only a -% |\romannumeral| trigger and did not insert an |\expanded|, so now the -% support macros must be f-expandable. The |1.4a| release fixes the related -% user documentation of |\bnumexprsetup| which was not updated at |1.4|. -% The support macro for the factorial however needs only be x-expandable. -% \item Also, I simply do not understand why the legacy user documentation -% said that the support macros were supposed to f-expand their arguments, as -% they are used only with arguments being explicit digit tokens (and -% optional minus sign). -% \item I hesitated a bit about making the decimal to hexadecimal and -% hexadecimal to decimal support macros customizable, but dropped the -% idea. Loading \xintbinhexname unconditionally has also the advantage to -% not have to define some \xintkernelname provided helper macros; and it -% still does not load \xintcorename, so is keeping dependencies somewhat low -% and the |custom| option significant. -% \item I had fleetingly consider a parser |\hexexpr| where input is -% hexadecimal with no |"| prefix, but implementing this basically means -% duplicating with new names a large chunk of the parser code to have parser -% specific |"getnext"| macros for example. Not worth it. % \end{itemize} % -% \subsection{Package identification and catcode setup} +% At |1.5|, right-associativity is implemented for powers in anticipation of +% upstream, and the customizability and extendibility of the package is made +% total via added |\bnumdefinfix| and |\bnumdefpostfix|. +% +% \subsection{Package identification}\leavevmode % \begin{macrocode} \NeedsTeXFormat{LaTeX2e}% -\ProvidesPackage{bnumexpr}[2021/05/13 v1.4a Expressions with big integers (JFB)]% +\ProvidesPackage{bnumexpr}[2021/05/17 v1.5 Expressions with big integers (JFB)]% % \end{macrocode} % \subsection{Load unconditionally xintbinhex} % Newly done at |1.4|. Formerly, \bnumname had no dependency if loaded @@ -908,46 +1247,81 @@ output is handled, it goes necessarily via the \xintbinhexname macros. % \begin{macrocode} \RequirePackage{xintbinhex}[2021/05/10]% % \end{macrocode} -% \subsection{\csh{bnumexprsetup}} +% \subsection{Package options}\leavevmode +% \begin{macrocode} +\def\BNEtmpa {0}% +\DeclareOption {custom}{\def\BNEtmpa {1}}% +\ProcessOptions\relax +% \end{macrocode} +% \subsection{\csh{bnumsetup} and conditional loading of \xintcorename} +% The keys should have been |Add|, |Sub|, \dots, not |add|, |sub|, \dots, so +% internally macros |\BNE_Op_Add| etc\dots\ macro names would be used, but well, +% let's simply live with this. +% +% |\bnumsetup| replaces at |1.5| deprecated |\bnumexprsetup| which is kept as an alias. % \begin{macrocode} {\catcode`! 3 \catcode`_ 11 % - \gdef\bnumexprsetup #1{\BNE_parsekeys #1,=!,}% + \gdef\bnumsetup #1{\BNE_parsekeys #1,=!,}% \gdef\BNE_parsekeys #1=#2#3,% {% \ifx!#2\expandafter\BNE_parsedone\fi + \XINT_global \expandafter \let\csname BNE_Op_\xint_zapspaces #1 \xint_gobble_i\endcsname% - =#2\BNE_parsekeys + =#2% + \ifxintverbose + \PackageInfo{bnumexpr}{assigned + \ifxintglobaldefs globally \fi + \string#2 to \xint_zapspaces #1 \xint_gobble_i\MessageBreak +% \end{macrocode} +% Workaround the space inserted by |\on@line|. +% \begin{macrocode} + \expandafter\xint_firstofone}% + \fi + \BNE_parsekeys }% \gdef\BNE_parsedone #1\BNE_parsekeys {}% }% -% \end{macrocode} -% \subsection{Package options} -% The keys should have been |Add|, |Sub|, \dots, not |add|, |sub|, \dots, so -% internally macros |\BNE_Op_Add| etc\dots\ macro names would be used, but well, -% let's simply leave with this. -% \begin{macrocode} -\def\BNEtmpa {0}% -\DeclareOption {custom}{\def\BNEtmpa {1}}% -\ProcessOptions\relax -\edef\BNErestorecatcodes{\XINTrestorecatcodes}% -\XINTsetcatcodes% -\if0\BNEtmpa\expandafter\xint_secondoftwo\fi -\xint_gobble_i{% +\let\bnumexprsetup\bnumsetup +\if0\BNEtmpa\expandafter\@secondoftwo\fi +\@gobble{% \RequirePackage{xintcore}[2021/05/10]% - \bnumexprsetup{add=\xintiiAdd, sub=\xintiiSub, mul=\xintiiMul, + \bnumsetup{add=\xintiiAdd, sub=\xintiiSub, mul=\xintiiMul, divround=\xintiiDivRound, div=\xintiiDivFloor, mod=\xintiiMod, pow=\xintiiPow, fac=\xintiiFac, opp=\xintiiOpp}% }% % \end{macrocode} +% \subsection{Activate usual \xintname catcodes for code source}\leavevmode +% \begin{macrocode} +\edef\BNErestorecatcodes{\XINTrestorecatcodes}% +\XINTsetcatcodes% +% \end{macrocode} % Strangely those three are not defined in xintkernel.sty, but only in xint.sty % \begin{macrocode} \long\def\xint_firstofthree #1#2#3{#1}% \long\def\xint_secondofthree #1#2#3{#2}% \long\def\xint_thirdofthree #1#2#3{#3}% % \end{macrocode} -% \subsection{\csh{bnumexpr}, \csh{thebnumexpr}, \csh{bnethe}, \csh{bnumeval}} +% For the mechanism of |\bnumdefinfix| we need [1] precedence levels to be +% available as |\chardef|'s. \xintkernelname already provides 0-10, 12, 14, +% 16, 18, 20, 22. Admittedly they could be created only dynamically, and then +% I would not have to set a cap at 22, but well, that's already a large +% supported range for a functionality nobody will use, as nobody probably uses +% the package to start with. +% +% .. [1] left levels need to be represented by one token; right levels are +% hard-coded into |checkp_<op>| macros and could have been there explicit +% digit tokens but we will use the |\xint_c_...| |\char|-tokens. +% \begin{macrocode} +\chardef\xint_c_xi 11 +\chardef\xint_c_xiii 13 +\chardef\xint_c_xv 15 +\chardef\xint_c_xvii 17 +\chardef\xint_c_xix 19 +\chardef\xint_c_xxi 21 +% \end{macrocode} +% \subsection{\csh{bnumexpr}, \csh{thebnumexpr}, \csh{bnethe}, \csh{bnumeval}}\leavevmode % \begin{macrocode} \def\XINTfstop {\noexpand\XINTfstop}% \def\bnumexpr {\romannumeral0\bnumexpro}% @@ -962,26 +1336,32 @@ output is handled, it goes necessarily via the \xintbinhexname macros. \def\bnebareeval{\BNE_start}% \def\bnethe#1{\expanded\expandafter\xint_gobble_i\romannumeral`&&@#1}% \protected\def\BNEprint.#1{{\BNE_print#1.}}% -\def\BNE_print#1{#1\expandafter\BNE_print_a\string}% +\def\BNE_print#1{\bnumprintone{#1}\expandafter\BNE_print_a\string}% \def\BNE_print_a#1{\unless\if#1.\expandafter\BNE_print_b\fi}% \def\BNE_print_b {\expandafter\BNE_print_c\expandafter{\expandafter\xint_gobble_i\string}}% -\def\BNE_print_c#1{, #1\expandafter\BNE_print_a\string}% +\def\BNE_print_c#1{\bnumprintonesep\bnumprintone{#1}\expandafter\BNE_print_a\string}% \protected\def\BNEprinthex.#1{{\BNE_printhex#1.}}% -\def\BNE_printhex#1{\xintDecToHex{#1}\expandafter\BNE_printhex_a\string}% +\def\BNE_printhex#1{\bnumprintonetohex{#1}\expandafter\BNE_printhex_a\string}% \def\BNE_printhex_a#1{\unless\if#1.\expandafter\BNE_printhex_b\fi}% \def\BNE_printhex_b {\expandafter\BNE_printhex_c\expandafter{\expandafter\xint_gobble_i\string}}% -\def\BNE_printhex_c#1{, \xintDecToHex{#1}\expandafter\BNE_printhex_a\string}% +\def\BNE_printhex_c#1{\bnumprintonesep\bnumprintonetohex{#1}\expandafter\BNE_printhex_a\string}% +\let\bnumprintone\xint_firstofone +\let\bnumprintonetohex\xintDecToHex +\def\bnumprintonesep{, }% % \end{macrocode} % \subsection{\csh{BNE_getnext}} +% The upstream |\BNE_put_op_first| has a string of included |\expandafter|, +% which was imported here at |1.4| and |1.4a| but they serve nothing in our +% context. Removed this useless overhead at |1.5|. % \begin{macrocode} \def\BNE_getnext #1% {% \expandafter\BNE_put_op_first\romannumeral`&&@% \expandafter\BNE_getnext_a\romannumeral`&&@#1% }% -\def\BNE_put_op_first #1#2#3{\expandafter#2\expandafter#3\expandafter{#1}}% +\def\BNE_put_op_first #1#2#3{#2#3{#1}}% \def\BNE_getnext_a #1% {% \ifx\relax #1\xint_dothis\BNE_foundprematureend\fi @@ -1021,8 +1401,7 @@ output is handled, it goes necessarily via the \xintbinhexname macros. % In the case of hitting a |(|, previous release inserted directly a % |\BNE_oparen|. But the expansion architecture imported from upstream % |\xintiiexpr| has been refactored, and the |..._oparen| meaning and -% usage evolved. We stick with |{}\xint_c_ii^v (| from upstream, -% which works (I am 15 months away from \xintexprname |1.4|). +% usage evolved. We stick with |{}\xint_c_ii^v (| from upstream. % \begin{macrocode} \def\BNE_getnextfork #1{% \if#1+\xint_dothis \BNE_getnext_a \fi @@ -1031,7 +1410,7 @@ output is handled, it goes necessarily via the \xintbinhexname macros. \xint_orthat {\BNE_scan_number #1}% }% % \end{macrocode} -% \subsection{Parsing an integer in decimal or hexadecimal notation} +% \subsection{Parsing an integer in decimal or hexadecimal notation}\leavevmode % \begin{macrocode} \def\BNE_scan_number #1% {% @@ -1039,7 +1418,22 @@ output is handled, it goes necessarily via the \xintbinhexname macros. \ifnum \xint_c_ix<1\string#1 \xint_dothis \BNE_startint\fi \xint_orthat \BNE_notadigit #1% }% -\def\BNE_notadigit#1{\BNE_getnext }% +% \end{macrocode} +% If user employs |\bnumdefinfix| with |\string#|, and then tries |100##3|, +% the first |#| will be interpreted as operator (assuming no +% operator starting with |##| has actually been defined) and the error +% "message" (which is not using |\message| or a |\write|) +% will then be +% \centeredline{|Digit? (got `##')|} +% because the parser is actually looking for a digit but finds the second |#|, +% and TeX displays it doubled. This is doubly +% confusing, but well, let's not dwell on that. +% \begin{macrocode} +\def\BNE_notadigit#1% +{% + \expandafter\BNE_scan_number + \romannumeral`&&@\XINT_expandableerror{Digit? (got `#1'). Hit I<RET><digit>}% +}% \def\BNE_startint #1% {% \if #10\expandafter\BNE_gobz_a\else\expandafter\BNE_scanint_a\fi #1% @@ -1060,15 +1454,18 @@ output is handled, it goes necessarily via the \xintbinhexname macros. {% \expandafter\BNE_scanint_main\romannumeral`&&@#1% }% +% \end{macrocode} +% Upstream (at |1.4f|) has |_getop| here, but let's jump directly to |BNE_getop_a|. +% \begin{macrocode} \def\BNE_scanint_hit_cs \ifnum#1\fi#2\BNE_scanint_again {% - \iffalse{{{\fi}}\expandafter}\romannumeral`&&@\BNE_getop#2% + \iffalse{{{\fi}}\expandafter}\romannumeral`&&@\BNE_getop_a#2% }% \def\BNE_scanint_next #1\BNE_scanint_again {% \if _#1\xint_dothis\BNE_scanint_again\fi \xint_orthat - {\iffalse{{{\fi}}\expandafter}\romannumeral`&&@\BNE_getop#1}% + {\iffalse{{{\fi}}\expandafter}\romannumeral`&&@\BNE_getop_a#1}% }% \def\BNE_gobz_scanint_main #1% {% @@ -1080,29 +1477,72 @@ output is handled, it goes necessarily via the \xintbinhexname macros. {% \expandafter\BNE_gobz_scanint_main\romannumeral`&&@#1% }% +% \end{macrocode} +% Upstream (at |1.4f|) has |_getop| here, but let's jump directly to |BNE_getop_a|. +% \begin{macrocode} \def\BNE_gobz_scanint_hit_cs\ifnum#1\fi#2\BNE_scanint_again {% - 0\iffalse{{{\fi}}\expandafter}\romannumeral`&&@\BNE_getop#2% + 0\iffalse{{{\fi}}\expandafter}\romannumeral`&&@\BNE_getop_a#2% }% \def\BNE_gobz_scanint_next #1\BNE_scanint_again {% \if _#1\xint_dothis\BNE_gobz_scanint_again\fi \if 0#1\xint_dothis\BNE_gobz_scanint_again\fi \xint_orthat - {0\iffalse{{{\fi}}\expandafter}\romannumeral`&&@\BNE_getop#1}% + {0\iffalse{{{\fi}}\expandafter}\romannumeral`&&@\BNE_getop_a#1}% }% \def\BNE_hex_in #1.% {% - \expanded{{{\xintHexToDec{#1}}}\expandafter}\romannumeral`&&@\BNE_getop + \expanded{{{\bnumhextodec{#1}}}\expandafter}\romannumeral`&&@\BNE_getop +}% +\let\bnumhextodec\xintHexToDec +% \end{macrocode} +% Upstream (until |1.4f|) had a long-standing bug in its hexadecimal input, +% which was inherited here at |1.4|: the |\BNE_scanhex| triggered +% |\BNE_scanhex_a| which then grabbed an unexpanded token and used it as is +% in an |\ifcat|... this made syntax such as |"\foo| broken. Fixed here at +% |1.5|. +% +% And there was a further long-standing bug in upstream (from |1.2m| to +% |1.4f|) about leading hexadecimal zeros not being trimmed. This was +% inherited here at |1.4|. Fixed also at |1.5|. +% \begin{macrocode} +\def\BNE_scanhex #1#2% #1=" +{% + \expandafter\BNE_hex_in\expanded\bgroup + \expandafter\BNE_scanhexgobz_a\romannumeral`&&@#2% +}% +\def\BNE_scanhexgobz_a #1% +{% + \ifcat #1\relax0.\iffalse{\fi\expandafter}\expandafter\xint_gobble_i\fi + \BNE_scanhexgobz_aa #1% +}% +\def\BNE_scanhexgobz_aa #1% +{% + \if\ifnum`#1>`0 + \ifnum`#1>`9 + \ifnum`#1>`@ + \ifnum`#1>`F + 0\else1\fi\else0\fi\else1\fi\else0\fi 1% + \xint_dothis\BNE_scanhex_b + \fi + \if 0#1\xint_dothis\BNE_scanhexgobz_bgob\fi + \if _#1\xint_dothis\BNE_scanhexgobz_bgob\fi + \if .#1\xint_dothis\BNE_scanhexgobz_toII\fi + \xint_orthat + {\XINT_expandableerror + {HexDigit was expected but saw `#1'. Using 0, hit <RET>}% + 0.>;\iffalse{\fi}}% + #1% }% -\def\BNE_scanhex #1% #1=" +\def\BNE_scanhexgobz_bgob #1#2% {% - \expandafter\BNE_hex_in\expanded\bgroup\BNE_scanhex_a + \expandafter\BNE_scanhexgobz_a\romannumeral`&&@#2% }% \def\BNE_scanhex_a #1% {% - \ifcat #1\relax\xint_dothis{.\iffalse{\fi}#1}\fi - \xint_orthat {\BNE_scanhex_aa #1}% + \ifcat #1\relax.\iffalse{\fi\expandafter}\expandafter\xint_gobble_i\fi + \BNE_scanhex_aa #1% }% \def\BNE_scanhex_aa #1% {% @@ -1114,7 +1554,7 @@ output is handled, it goes necessarily via the \xintbinhexname macros. \expandafter\BNE_scanhex_b \else \if _#1\xint_dothis{\expandafter\BNE_scanhex_bgob}\fi - \xint_orthat {\xint_afterfi {.\iffalse{\fi}}}% + \xint_orthat {.\iffalse{\fi\expandafter}}% \fi #1% }% @@ -1128,6 +1568,14 @@ output is handled, it goes necessarily via the \xintbinhexname macros. }% % \end{macrocode} % \subsection{\csh{BNE_getop}} +% The upstream analog to |\BNE_getop_a| applies |\string| to |#1| in its +% thirdofthree branch before handing over to analog of |\BNE_scanop_a|, but I +% see no reason for doing it here (and I do have to check if upstream has +% any valid reason to do it). Removed. First branch was a |\BNE_foundend|, +% used only here, and expanding to |\xint_c_\relax|, let's move the +% |#1| (which will be |\relax|) last and simply insert |\xint_c_|. +% +% The |_scanop| macros have been refactored at upstream and here |1.5|. % \begin{macrocode} \def\BNE_getop #1% {% @@ -1141,50 +1589,59 @@ output is handled, it goes necessarily via the \xintbinhexname macros. \ifnum\xint_c_ix<1\string#1 \xint_dothis\xint_secondofthree\fi \if (#1\xint_dothis \xint_secondofthree\fi %) \xint_orthat \xint_thirdofthree - {\BNE_foundend}% - {\BNE_precedence_*** *#1}% - {\expandafter\BNE_scanop_a \string#1}% + \xint_c_ + {\BNE_prec_tacit *}% + \BNE_scanop_a + #1% }% \catcode`* 12 -\def\BNE_foundend {\xint_c_ \relax}% \def\BNE_scanop_a #1#2% {% \expandafter\BNE_scanop_b\expandafter#1\romannumeral`&&@#2% }% \def\BNE_scanop_b #1#2% {% - \ifcat#2\relax\xint_dothis{\BNE_foundop_a #1#2}\fi - \ifcsname BNE_itself_#1#2\endcsname - \xint_dothis - {\expandafter\BNE_scanop_c\csname BNE_itself_#1#2\endcsname}\fi - \xint_orthat {\BNE_foundop_a #1#2}% + \unless\ifcat#2\relax + \ifcsname BNE_itself_#1#2\endcsname + \BNE_scanop_c + \fi\fi + \BNE_foundop_a #1#2% }% -\def\BNE_scanop_c #1#2% +\def\BNE_scanop_c #1#2#3#4#5% #1#2=\fi\fi {% - \expandafter\BNE_scanop_d\expandafter#1\romannumeral`&&@#2% + #1#2% + \expandafter\BNE_scanop_d\csname BNE_itself_#4#5\expandafter\endcsname + \romannumeral`&&@% }% \def\BNE_scanop_d #1#2% {% - \ifcat#2\relax \xint_dothis{\BNE_foundop #1#2}\fi - \ifcsname BNE_itself_#1#2\endcsname - \xint_dothis - {\expandafter\BNE_scanop_c\csname BNE_itself_#1#2\endcsname }\fi - \xint_orthat {\csname BNE_precedence_#1\endcsname #1#2}% + \unless\ifcat#2\relax + \ifcsname BNE_itself_#1#2\endcsname + \BNE_scanop_c + \fi\fi + \BNE_foundop #1#2% }% +% \end{macrocode} +% If a postfix say |?s| is defined and |?r| is encountered the |?| will have +% been interpreted as a shortcut to |?s| and then the |r| will be found with +% the parser (after having executed the already found postfix) now looking for +% another operator so the error message will be |Operator? (got `r')| which is +% doubly confusing... well, let's not dwell on that. +% \begin{macrocode} \def\BNE_foundop_a #1% {% \ifcsname BNE_precedence_#1\endcsname \csname BNE_precedence_#1\expandafter\endcsname \expandafter #1% \else - \xint_afterfi{\BNE_getop\romannumeral0% - \XINT_expandableerror - {"#1" is unknown as operator. (I)nsert one:} }%<<deliberate space - \fi + \expandafter\BNE_getop_a\romannumeral`&&@% + \xint_afterfi{\XINT_expandableerror + {Operator? (got `#1'). Hit I<RET><operator>}}% + \fi }% \def\BNE_foundop #1{\csname BNE_precedence_#1\endcsname #1}% % \end{macrocode} -% \subsection{Expansion spanning; opening and closing parentheses} +% \subsection{Expansion spanning; opening and closing parentheses}\leavevmode % \begin{macrocode} \def\BNE_tmpa #1#2#3#4#5% {% @@ -1211,7 +1668,7 @@ output is handled, it goes necessarily via the \xintbinhexname macros. \def#5% {% \XINT_expandableerror - {An extra ) has been removed. Hit Return, fingers crossed.}% + {An extra ) has been removed. Hit <RET>, fingers crossed.}% \expandafter#2\romannumeral`&&@\expandafter\BNE_put_op_first \romannumeral`&&@\BNE_getop_legacy }% @@ -1267,13 +1724,13 @@ output is handled, it goes necessarily via the \xintbinhexname macros. \csname BNE_op_-xii\endcsname \let\BNE_precedence_)\xint_c_i \def\BNE_missing_) - {\XINT_expandableerror{Sorry to report a missing ) at the end of this journey.}% + {\XINT_expandableerror{Missing ). Hit <RET> to proceed}% \xint_c_ \BNE_done }% \catcode`) 12 % \end{macrocode} % \subsection{The comma as binary operator} % At |1.4|, it is simply a union operator for 1D oples. Inserting directly -% here a |<space><comma>| separator (as in earlier releases) in accumulated +% here a |<comma><space>| separator (as in earlier releases) in accumulated % result would avoid having to do it on output but to the cost of diverging % from \xintexprname upstream code, and to have to let the |\evaltohex| output % routine handle comma separated values rather than braced values. @@ -1312,36 +1769,53 @@ output is handled, it goes necessarily via the \xintbinhexname macros. \expandafter\let\csname BNE_precedence_,\endcsname\xint_c_iii % \end{macrocode} % \subsection{The minus as prefix operator of variable precedence level} -% This |\BNE_Op_opp| causes trouble as at |1.4| it must be f-expandable, +% This |\BNE_Op_opp| caused trouble at |1.4| as it must be \emph{f}-expandable, % whereas earlier it expanded inside |\csname...\endcsname| context, so I -% could define it as {\catcode`\#12 |\if-#1\else\if0#10\else-#1\fi\fi|} where -% {\catcode`\#12 |#1|} was the +% could define it as \centeredline{|\if-#1\else\if0#10\else-#1\fi\fi|} where +% |#1| was the % first token of unbraced argument but this meant at |1.4| an added % |\xint_firstofone| here. Well let's return to sanity at |1.4a| and not add % the |\xint_firstofone| and simply default |\BNE_Op_opp| to |\xintiiOpp|, % which it should have been all along! And on this occasion let's trim user -% documentation of irrelevant complications. +% documentation of complications. +% +% The package used to need to define unary minus operator with precedences 12, +% 14, and 18. It also defined it at level 16 but this was unneedeed actually, +% no operator possibly generating usage of an |op_-xvi|. +% +% At |1.5| the right precedence of powers was lowered to 17, so we now need +% here only 12, 14, and 17. +% +% Due to |\bnumdefinfix| it is needed to support also, perhaps, the other +% levels 13, 15, 16, 18, .... This will be done only if necessary and is the +% reason why the macros |\BNE_defminus_a| and |\BNE_defminus_b| are given +% permanent names. In fact it is now |\BNE_defbin_b| which will decide to invoke +% or not the |\BNE_defminus_a|, and we activate it here only for the base +% precedence 12. +% +% The |\XINT_global| are inexistent in upstream at |1.4f| as it does not +% incorporate yet some analog to |\bnumdefinfix/\bnumdefpostfix|. % \begin{macrocode} -\def\BNE_tmpb #1#2#3#4#5% +\def\BNE_defminus_b #1#2#3#4#5% {% - \def #1% \BNE_op_-<level> + \XINT_global\def #1% \BNE_op_-<level> {% \expandafter #2\romannumeral`&&@\expandafter#3% \romannumeral`&&@\BNE_getnext }% - \def #2##1##2##3% \BNE_exec_-<level> + \XINT_global\def #2##1##2##3% \BNE_exec_-<level> {% \expandafter ##1\expandafter ##2\expandafter {\expandafter{\romannumeral`&&@\BNE_Op_opp##3}}% }% - \def #3##1% \BNE_check-_-<level> + \XINT_global\def #3##1% \BNE_check-_-<level> {% \xint_UDsignfork ##1{\expandafter #4\romannumeral`&&@#1}% -{#4##1}% \krof }% - \def #4##1##2% \BNE_checkp_-<level> + \XINT_global\def #4##1##2% \BNE_checkp_-<level> {% \ifnum ##1>#5% \expandafter #4% @@ -1351,51 +1825,71 @@ output is handled, it goes necessarily via the \xintbinhexname macros. \fi }% }% -\def\BNE_tmpa #1% +\def\BNE_defminus_a #1% {% -\expandafter\BNE_tmpb + \expandafter\BNE_defminus_b \csname BNE_op_-#1\expandafter\endcsname \csname BNE_exec_-#1\expandafter\endcsname \csname BNE_check-_-#1\expandafter\endcsname \csname BNE_checkp_-#1\expandafter\endcsname \csname xint_c_#1\endcsname }% -\BNE_tmpa {xii}% -\BNE_tmpa {xiv}% -\BNE_tmpa {xvi}% -\BNE_tmpa {xviii}% +\BNE_defminus_a {xii}% % \end{macrocode} % \subsection{The infix operators.} % I could have at the |1.4| refactoring injected usage of |\expanded| here, -% but kept in sync with upstream \xintexprname code. +% but kept in sync with upstream \xintexprname code. Any \emph{x}-expandable +% macro can easily be converted into an \emph{f}-expandable one using +% |\expanded|, so this is no serious limitation. % % Macro names are somewhat bad and there is much risk of confusion in future % maintenance of |\BNE_Op_| prefix (used for |\BNE_Op_add| etc...; besides % this should have been |\BNE_Op_Add|) and |\BNE_op_| prefix (used for -% {\catcode`+ 11 |\BNE_op_+|} etc...). +% |\BNE_op_+| etc...). +% +% At |1.5| decision is made to anticipate the announced upstream change to let +% the power operators be right associative, matching Python behaviour. This +% change is simply implemented by hardcoding in |\BNE_checkp_<op>| the right +% precedence which so far, for such operators, had been identical with the +% left precedence (upstream has examples of direct coding without +% formalization). In fact the right precedence existed already as argument to +% |\BNE_defbin_b| as the precedence to assign to unary minus following |<op>|. +% +% Note1: although it is easy to change the left precedence at user level, the +% right precedence is now more inaccessible. But on the other hand \bnumname +% provides |\bnumdefinfix| so all is customizable at user level. +% +% Note2: Tacit multiplication is not really a separate operator, it is +% the |*| with an elevated left precedence, which costs nothing to create and +% this precedence is stored in chardef token |\BNE_prec_tacit|. +% +% Compared to upstream, we use here numbers as arguments to |\BNE_defbin_b|, +% and convert to roman numerals internally, also the operator macro is passed +% as a control sequence not as its name (and |#6| and |#7| are permuted in +% |\BNE_defbin_c|). % \begin{macrocode} \def\BNE_defbin_c #1#2#3#4#5#6#7% {% - \def #1##1% \BNE_op_<op> + \XINT_global\def #1##1% \BNE_op_<op> {% \expanded{\unexpanded{#2{##1}}\expandafter}% \romannumeral`&&@\expandafter#3\romannumeral`&&@\BNE_getnext }% - \def #2##1##2##3##4% \BNE_exec_<op> + \XINT_global\def #2##1##2##3##4% \BNE_exec_<op> {% \expandafter##2\expandafter##3\expandafter - {\expandafter{\romannumeral`&&@#6##1##4}}% + {\expandafter{\romannumeral`&&@#7##1##4}}% }% - \def #3##1% \BNE_check-_<op> + \XINT_global\def #3##1% \BNE_check-_<op> {% \xint_UDsignfork ##1{\expandafter#4\romannumeral`&&@#5}% -{#4##1}% \krof }% - \def #4##1##2% \BNE_checkp_<op> + \XINT_global\def #4##1##2% \BNE_checkp_<op> {% - \ifnum ##1>#7% + \ifnum ##1>#6% \expandafter#4% \romannumeral`&&@\csname BNE_op_##2\expandafter\endcsname \else @@ -1405,51 +1899,138 @@ output is handled, it goes necessarily via the \xintbinhexname macros. }% \def\BNE_defbin_b #1#2#3#4% {% - \expandafter\BNE_defbin_c - \csname BNE_op_#1\expandafter\endcsname - \csname BNE_exec_#1\expandafter\endcsname - \csname BNE_check-_#1\expandafter\endcsname - \csname BNE_checkp_#1\expandafter\endcsname - \csname BNE_op_-#3\expandafter\endcsname - \csname #4\expandafter\endcsname - \csname BNE_precedence_#1\endcsname - \expandafter - \let\csname BNE_precedence_#1\expandafter\endcsname - \csname xint_c_#2\endcsname + \expandafter\BNE_defbin_c + \csname BNE_op_#1\expandafter\endcsname + \csname BNE_exec_#1\expandafter\endcsname + \csname BNE_check-_#1\expandafter\endcsname + \csname BNE_checkp_#1\expandafter\endcsname + \csname BNE_op_-\romannumeral\ifnum#3>12 #3\else 12\fi + \expandafter\endcsname + \csname xint_c_\romannumeral#3\endcsname #4% + \XINT_global + \expandafter + \let\csname BNE_precedence_#1\expandafter\endcsname + \csname xint_c_\romannumeral#2\endcsname + \unless + \ifcsname BNE_exec_-\romannumeral\ifnum#3>12 #3\else 12\fi\endcsname +% \end{macrocode} +% This will execute only for |#3>12| as |\BNE_exec_-xii| exists. +% \begin{macrocode} + \expandafter\BNE_defminus_a\expandafter{\romannumeral#3}% + \fi }% -\BNE_defbin_b {//} {xiv}{xiv}{BNE_Op_div}% -\BNE_defbin_b {/:} {xiv}{xiv}{BNE_Op_mod}% -\BNE_defbin_b + {xii}{xii}{BNE_Op_add}% -\BNE_defbin_b - {xii}{xii}{BNE_Op_sub}% -\BNE_defbin_b * {xiv}{xiv}{BNE_Op_mul}% -\BNE_defbin_b / {xiv}{xiv}{BNE_Op_divround}% -\BNE_defbin_b ^ {xviii}{xviii}{BNE_Op_pow}% -\expandafter\def\csname BNE_itself_**\endcsname {^}% +\BNE_defbin_b + {12} {12} \BNE_Op_add +\BNE_defbin_b - {12} {12} \BNE_Op_sub +\BNE_defbin_b * {14} {14} \BNE_Op_mul +\BNE_defbin_b / {14} {14} \BNE_Op_divround +\BNE_defbin_b {//} {14} {14} \BNE_Op_div +\BNE_defbin_b {/:} {14} {14} \BNE_Op_mod +\BNE_defbin_b ^ {18} {17} \BNE_Op_pow +% \end{macrocode} +% At upstream, we can use shortcut +% \centeredline{|\expandafter\def\csname BNE_itself_**\endcsname {^}|} +% but it means then that any redefinition of |^| propagates to |**|, besides it +% creates a special case which would need consideration by +% |\BNE_dotheitselves|, or special restrictions to add to user documentation. +% Better to simply handle |**| as a full operator. +% \begin{macrocode} +\BNE_defbin_b {**} {18} {17} \BNE_Op_pow +\expandafter\def\csname BNE_itself_**\endcsname {**}% \expandafter\def\csname BNE_itself_//\endcsname {//}% \expandafter\def\csname BNE_itself_/:\endcsname {/:}% -\expandafter\let\csname BNE_precedence_***\endcsname \xint_c_xvi +\let\BNE_prec_tacit\xint_c_xvi +% \end{macrocode} +% \subsection{\csh{bnumdefinfix}: extending the syntax}\leavevmode +% |#1| gives the operator characters, |#2| the associated macro, |#3| its +% left-precedence and |#4| its right precedence (as integers). +% +% The "itself" definitions are done in such a way that unambiguous abbreviations +% work; but in case of ambiguity the first defined operator is used. +% +% However, if for example operator |$a| was defined after |$ab|, then although +% |$| will use |$ab| which was defined first, |$a| will use as expected the +% second defined operator. +% +% The mismatch |\BNE_defminus_a| vs |\BNE_defbin_b| is inherited from +% upstream, I keep it to simplify maintenance. +% \begin{macrocode} +\def\bnumdefinfix #1#2#3#4% +{% + \edef\BNE_tmpa{#1}% + \edef\BNE_tmpa{\xint_zapspaces_o\BNE_tmpa}% + \edef\BNE_tmpL{\the\numexpr#3\relax}% + \edef\BNE_tmpL{\ifnum\BNE_tmpL<4 4\else\ifnum\BNE_tmpL<23 \BNE_tmpL\else 22\fi\fi}% + \edef\BNE_tmpR{\the\numexpr#4\relax}% + \edef\BNE_tmpR{\ifnum\BNE_tmpR<4 4\else\ifnum\BNE_tmpR<23 \BNE_tmpR\else 22\fi\fi}% + \BNE_defbin_b \BNE_tmpa\BNE_tmpL\BNE_tmpR #2% + \expandafter\BNE_dotheitselves\BNE_tmpa\relax + \ifxintverbose + \PackageInfo{bnumexpr}{infix operator \BNE_tmpa\space + \ifxintglobaldefs globally \fi + does + \unexpanded{#2}\MessageBreak with precedences \BNE_tmpL, \BNE_tmpR;}% + \fi +}% +\def\BNE_dotheitselves#1#2% +{% + \if#2\relax\expandafter\xint_gobble_ii + \else + \XINT_global + \expandafter\edef\csname BNE_itself_#1#2\endcsname{#1#2}% + \unless\ifcsname BNE_precedence_#1\endcsname + \XINT_global + \expandafter\edef\csname BNE_precedence_#1\endcsname + {\csname BNE_precedence_\BNE_tmpa\endcsname}% + \XINT_global + \expandafter\odef\csname BNE_op_#1\endcsname + {\csname BNE_op_\BNE_tmpa\endcsname}% + \fi + \fi + \BNE_dotheitselves{#1#2}% +}% % \end{macrocode} -% \subsection{! as postfix factorial operator} -% This one uses |\expanded|. +% \subsection{\csh{bnumdefpostfix}} +% Support macros for postfix operators only need to be \emph{x}-expandable. % \begin{macrocode} -\catcode`! 11 -\let\BNE_precedence_! \xint_c_xx -\def\BNE_op_! #1% +\def\bnumdefpostfix #1#2#3% {% - \expandafter\BNE_put_op_first - \expanded{{{\BNE_Op_fac#1}}\expandafter}\romannumeral`&&@\BNE_getop + \edef\BNE_tmpa{#1}% + \edef\BNE_tmpa{\xint_zapspaces_o\BNE_tmpa}% + \edef\BNE_tmpL{\the\numexpr#3\relax}% + \edef\BNE_tmpL{\ifnum\BNE_tmpL<4 4\else\ifnum\BNE_tmpL<23 \BNE_tmpL\else 22\fi\fi}% + \XINT_global + \expandafter\let\csname BNE_precedence_\BNE_tmpa\expandafter\endcsname + \csname xint_c_\romannumeral\BNE_tmpL\endcsname + \XINT_global + \expandafter\def\csname BNE_op_\BNE_tmpa\endcsname ##1% + {% + \expandafter\BNE_put_op_first + \expanded{{{#2##1}}\expandafter}\romannumeral`&&@\BNE_getop + }% + \expandafter\BNE_dotheitselves\BNE_tmpa\relax + \ifxintverbose + \PackageInfo{bnumexpr}{postfix operator \BNE_tmpa\space + \ifxintglobaldefs globally \fi + does \unexpanded{#2}\MessageBreak + with precedence \BNE_tmpL;}% + \fi }% % \end{macrocode} -% \subsection{Cleanup} +% \subsection{! as postfix factorial operator}\leavevmode % \begin{macrocode} -\let\BNEtmpa\relax \let\BNE_tmpa\relax \let\BNE_tmpb\relax \let\BNE_tmpc\relax +\bnumdefpostfix{!}{\BNE_Op_fac}{20}% +% \end{macrocode} +% \subsection{Cleanup}\leavevmode +% \begin{macrocode} +\let\BNEtmpa\relax +\let\BNE_tmpa\relax \let\BNE_tmpb\relax \let\BNE_tmpc\relax +\let\BNE_tmpR\relax \let\BNE_tmpL\relax \BNErestorecatcodes% % \end{macrocode} % \MakePercentComment %</package> %<*dtx> -\DeleteShortVerb{\|}% -\CheckSum {947}% +\CheckSum {1205}% \makeatletter\check@checksum\makeatother% \Finale% %% End of file xint.dtx diff --git a/macros/latex/contrib/bnumexpr/bnumexpr.pdf b/macros/latex/contrib/bnumexpr/bnumexpr.pdf Binary files differindex 79f259f3f2..912ef2e948 100644 --- a/macros/latex/contrib/bnumexpr/bnumexpr.pdf +++ b/macros/latex/contrib/bnumexpr/bnumexpr.pdf |