summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/bnumexpr
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2021-05-18 03:01:12 +0000
committerNorbert Preining <norbert@preining.info>2021-05-18 03:01:12 +0000
commit97eca13a92a0b8ad423b606c8970c9ce64d9b2bf (patch)
tree7275ec8351efb8f8108d65ec87d4cc81512a295a /macros/latex/contrib/bnumexpr
parentd999da47ff375df843c0c9bbfd5911236dd9d40a (diff)
CTAN sync 202105180301
Diffstat (limited to 'macros/latex/contrib/bnumexpr')
-rw-r--r--macros/latex/contrib/bnumexpr/README.md37
-rw-r--r--macros/latex/contrib/bnumexpr/bnumexpr.dtx1231
-rw-r--r--macros/latex/contrib/bnumexpr/bnumexpr.pdfbin96634 -> 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
index 79f259f3f2..912ef2e948 100644
--- a/macros/latex/contrib/bnumexpr/bnumexpr.pdf
+++ b/macros/latex/contrib/bnumexpr/bnumexpr.pdf
Binary files differ