% -*- coding: utf-8; time-stamp-format: "%02d-%02m-%:y at %02H:%02M:%02S %Z" ; time-stamp-active: nil -*- %<*dtx> \def\bnedtxtimestampaux #1<#2>{#2} \edef\bnedtxtimestamp{\bnedtxtimestampaux Time-stamp: <07-09-2025 at 12:03:52 CEST>} \catcode0 0 \catcode`\\ 12 ^^@iffalse % %<*drv> %% --------------------------------------------------------------- \def\bnepackdate{2025/09/07}% \def\bneversion {1.6a}% % %<*readme>-------------------------------------------------------- | Source: bnumexpr.dtx | Version: v1.6a, 2025/09/07 | Author: Jean-François Burnol | Info: Expressions with big integers | License: LPPL 1.3c Usage and aim ============= The package provides `\bnumeval`. To use with LaTeX, add `\usepackage{bnumexpr}` to the preamble. To use with Plain TeX, execute `\input miniltx.tex` then `\input bnumexpr.sty`. `\bnumeval` is an expandable expression parser extending LaTeX's `\inteval` (a wrapper of eTeX's `\numexpr`). Compared to `\inteval`, the added functionalities are: - support for arbitrarily big integers, - `//` for floored division, - `/:` for the associated remainder, - `^` and `**` for powers, - `!` for factorials, - comma separated calculations, - space character or underscore may serve to separate digits, - `0b`, `0o` and `0x` are recognized as prefixes for binary, octal, and hexadecimal inputs (in addition to the TeX prefixes `'` and `"`), - optional argument `[b]`, `[o]` or `[h]` for converting the output to binary, octal, or hexadecimal, - quasi complete customizability and extensibility of the syntax. The last item means that not only is it possible to map any given operation to a macro of one's own choosing, it is also possible to add new infix or postfix operators to the syntax. Installation ============ The package is provided by the major LaTeX distributions. For a manual installation obtain `bnumexpr.dtx`, from CTAN: > Run `"etex bnumexpr.dtx"` to extract in particular the style file `bnumexpr.sty` (and other files inclusive of this `README.md`). Put it where (La)TeX can find it and you are done. To build the documentation check the instructions in extracted file `bnumexpr.tex`. License ======= Copyright © 2014-2022, 2025 Jean-François Burnol | This Work may be distributed and/or modified under the | conditions of the LaTeX Project Public License 1.3c. | This version of this license is in > | and version 1.3 or later is part of all distributions of | LaTeX version 2005/12/01 or later. This Work has the LPPL maintenance status "author-maintained". The Author and Maintainer of this Work is Jean-François Burnol. This Work consists of the main source file and its derived files bnumexpr.dtx, bnumexpr.sty, bnumexpr.pdf, bnumexpr.tex, bnumexprchanges.tex, README.md %-------------------------------------------------------- %<*!readme> %% --------------------------------------------------------------- %% The bnumexpr package: Expressions with big integers %% Copyright (C) 2014-2022, 2025 Jean-Francois Burnol %% % %<*changes>------------------------------------------------------- \item[1.6a (2025/09/07)] bugfix: the |1.6| support for Babel-active characters worked with |\bnumeval| (which is recommended interface) but not with |\bnumexpr|. \item[1.6 (2025/09/05)]\mbox{}\newline \textbf{Breaking changes:} \begin{itemize}[noitemsep] \item Release |1.4n| or later of the \xintname bundle is required (for those components actually used, which by default are \xintkernelname, \xintcorename and \xintbinhexname). \item \cs{evaltohex} is deprecated and causes an auto-recovering error to signal it. It will be removed at next release. Use new |\bnumeval||[h]|. \item \cs{bnumexprsetup} was deprecated at |1.5| and renamed into |\bnumsetup|. It has now been removed. \item \cs{bnumprintonetohex} and \cs{bnumhextodec}, which were documented as customizable do not exist anymore. Check the documentation for |\bnumprintonehex| and |\bnumsetup|'s key |hextodec|. \item Under the |custom| option, not only \xintcorename but also \xintbinhexname are not loaded. Use |customcore| to avoid that. There is also |custombinhex|. \end{itemize} \textbf{Bug fixes:} \begin{itemize} \item An underscore |_| located in front of a number used to cause an error. It is now ignored. \end{itemize} \textbf{New features:} \begin{itemize} \item |0b|, |0o| and |0x| are recognized as prefixes for binary, octal, and hexadecimal inputs. And |'| is recognized as prefix for octal input, in addition to |"| for hexadecimal. \item |\bnumeval| accepts an optional argument |[b]| or |[o]| or |[h]| for automatic conversion of the calculated value (or comma separated values) to respectively binary, octal, or hexadecimal. \item Babel-active characters (such as |:| and |!| with French) do not need any preventive measures anymore such as using |\string!| in place of |!|. \item |\bnumsetup| can now be used also to customize which macros implement conversion from decimal to other bases. \end{itemize} The documentation was extensively revised and made more user-friendly. \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 \cs{bnumexprsetup} into |\bnumsetup|; the former remains available but is deprecated. [REMOVED AT |1.6|] \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 \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|. \end{itemize} \item[1.4 (2021/05/12)] \begin{itemize} \item technology transfer from \xintexprname |1.4| of |2020/01/31|. The |\expanded| primitive is now required (TeXLive 2019). \item addition to the syntax of the |"| prefix for hexadecimal input. \item addition of |\evaltohex| which is like |\bnumeval| with an extra conversion step to hexadecimal notation. \end{itemize} \item[1.2e (2019/01/08)] Fixes a documentation glitch (extra braces when mentioning |\the\numexpr| or |\thebnumexpr|). \item[1.2d (2019/01/07)] \begin{itemize} \item requires \xintcorename |1.3d| or later (if not using option |custom|). \item adds |\bnumeval|\marg{expression} user interface. \end{itemize} \item[1.2c (2017/12/05)] \textbf{Breaking changes:} \begin{itemize} \item requires \xintcorename |1.2p| or later (if not using option |custom|). \item |divtrunc| key of \cs{bnumexprsetup} is renamed to |div|. \item the |//| and |/:| operators are now by default associated to the \emph{floored} division. This is to keep in sync with the change of \xintcorename at |1.2p|. \item for backwards compatibility, one may add to existing document:\newline \string\bnumexprsetup\{div=\string\xintiiDivTrunc, mod=\string\xintiiModTrunc\} \end{itemize} \item[1.2b (2017/07/09)] \begin{itemize} \item the |_| may be used to separate visually blocks of digits in long numbers. \end{itemize} \item[1.2a (2015/10/14)] \begin{itemize} \item requires \xintcorename |1.2| or later (if not using option |custom|). \item additions to the syntax: factorial |!|, truncated division |//|, its associated modulo |/:| and |**| as alternative to |^|. \item all options removed except |custom|. \item new command \cs{bnumexprsetup} which replaces the commands such as |\bnumexprusesbigintcalc|. \item the parser is no more limited to numbers with at most 5000 digits. \end{itemize} \item[1.1b (2014/10/28)] \begin{itemize} \item README converted to |markdown/pandoc| syntax, \item the package now loads only |xintcore|, which belongs to |xint| bundle version |1.1| and extracts from the earlier |xint| package the core arithmetic operations as used by |bnumexpr|. \end{itemize} \item[1.1a (2014/09/22)] \begin{itemize} \item added |l3bigint| option to use experimental \LaTeX3 package of the same name, \item added Changes and Readme sections to the documentation, \item better |\BNE_protect| mechanism for use of |\bnumexpr...\relax| inside an |\edef| (without |\bnethe|). Previous one, inherited from |xintexpr.sty 1.09n|, assumed that the |\.=| dummy control sequence encapsulating the computation result had |\relax| meaning. But removing this assumption was only a matter of letting |\BNE_protect| protect two, not one, tokens. This will be backported to next version of \xintexprname, naturally (done with |xintexpr.sty 1.1|). \end{itemize} \item[1.1 (2014/09/21)] First release. This is down-scaled from the (development version of) \xintexprname. Motivation came the previous day from a chat with \textsc{Joseph Wright} over big int status in \LaTeX3. The |\bnumexpr...\relax| parser can be used on top of big int macros of one's choice. Functionalities limited to the basic operations. I leave the power operator |^| as an option. %------------------------------------------------------- %<*drv>----------------------------------------------------------- %% latex bnumexpr.tex (thrice) && dvipdfmx bnumexpr.dvi %% to produce bnumexpr.pdf %% %% or pdflatex bnumexpr.tex (no need to change \Withdvipdfmx toggle, %% pdf engine will be detected automatically) %% \NeedsTeXFormat{LaTeX2e}[2020/02/02] \ProvidesFile{bnumexpr.tex}% [\bnepackdate\space v\bneversion\space driver file for % bnumexpr documentation (JFB)]% \PassOptionsToClass{a4paper,fontsize=11pt}{scrartcl} \chardef\Withdvipdfmx 1 % \chardef\NoSourceCode 0 % replace 0 by 1 for not including source code \input bnumexpr.dtx %%% Local Variables: %%% mode: latex %%% End: %----------------------------------------------------------- %<*dtx> ^^@fi ^^@catcode`\ 0 \catcode0 12 \chardef\noetex 0 \ifx\numexpr\undefined \chardef\noetex 1 \fi \ifnum\noetex=1 \chardef\extractfiles 0 % extract files, then stop \else \ifx\ProvidesFile\undefined \chardef\extractfiles 0 % no LaTeX2e; etex, ... on bnumexpr.dtx \else % latex/pdflatex on bnumexpr.tex or on bnumexpr.dtx \ifx\Withdvipdfmx\undefined % latex run is on bnumexpr.dtx, we will extract all files \chardef\extractfiles 1 % 1 = extract and typeset, 2=only typeset \chardef\Withdvipdfmx 0 % 0 = pdflatex or latex+dvips, 1 = dvipdfmx \chardef\NoSourceCode 0 % 0 = include source code, 1 = do not \NeedsTeXFormat{LaTeX2e}[2020/02/02]% \PassOptionsToClass{a4paper,fontsize=11pt}{scrartcl}% \else % latex run is on bnumexpr.tex, \chardef\extractfiles 2 % no extractions \fi \ProvidesFile{bnumexpr.dtx}[bundle source (\bnepackdate)]% \fi \fi \ifnum\extractfiles<2 % extract files \def\MessageDeFin{\newlinechar10 \let\Msg\message \Msg{^^J}% \Msg{********************************************************************^^J}% \Msg{*^^J}% \Msg{* To finish the installation you have to move the following^^J}% \Msg{* files into a directory searched by TeX:^^J}% \Msg{*^^J}% \Msg{*\space\space\space\space bnumexpr.sty^^J}% \Msg{*^^J}% \Msg{* To produce the documentation run latex thrice on bnumexpr.tex^^J}% \Msg{* then dvipdfmx on bnumexpr.dvi. (ignore the dvipdfmx warnings)^^J}% \Msg{*^^J}% \Msg{* Happy TeXing!^^J}% \Msg{*^^J}% \Msg{********************************************************************^^J}% }% \begingroup \input docstrip.tex \askforoverwritefalse \generate{\nopreamble\nopostamble \file{README.md}{\from{bnumexpr.dtx}{readme}} \usepreamble\defaultpreamble \usepostamble\defaultpostamble \file{bnumexprchanges.tex}{\from{bnumexpr.dtx}{changes}} \file{bnumexpr.tex}{\from{bnumexpr.dtx}{drv}} \file{bnumexpr.sty}{\from{bnumexpr.dtx}{package}}} \endgroup \fi % end of file extraction \ifnum\extractfiles=0 % direct tex/etex/xetex on bnumexpr.dtx, files now extracted, stop \MessageDeFin\expandafter\end \fi % no use of docstrip to extract files if latex compilation was on bnumexpr.tex \ifdefined\MessageDeFin\AtEndDocument{\MessageDeFin}\fi %------------------------------------------------------------------------------- \documentclass {scrartcl} %%% START OF CUSTOM doc.sty LOADING (May 21, 2022) \usepackage{doc}[=v2] % As explained above I was formerly using scrdoc hence ltxdoc indirectly. % Let's emulate here the little I appear to need from ltxdoc.cls and % srcdoc.cls. % \AtBeginDocument{\MakeShortVerb{\|}} \DeclareFontShape{OT1}{cmtt}{bx}{n}{<-> ssub * cmtt/m/n}{} \DeclareFontFamily{OMS}{cmtt}{\skewchar\font 48} % '60 \DeclareFontShape{OMS}{cmtt}{m}{n}{<-> ssub * cmsy/m/n}{} \DeclareFontShape{OMS}{cmtt}{bx}{n}{<-> ssub * cmsy/b/n}{} \DeclareFontShape{OT1}{cmss}{m}{it}{<->ssub*cmss/m/sl}{} \ifnum\NoSourceCode=1 \OnlyDescription \fi \CodelineNumbered \DisableCrossrefs % \setcounter{StandardModuleDepth}{1} % we don't use this mechanism currently \def\cmd#1{\cs{\expandafter\cmd@to@cs\string#1}} \def\cmd@to@cs#1#2{\char\number`#2\relax} % This is hyperref compatible (contrarily to using \bslash) % I need the \detokenize due to usage in implementation part with names % containing the underscore for example \DeclareRobustCommand\cs[1]{\texttt{\textbackslash\detokenize{#1}}} \providecommand\marg[1]{% {\ttfamily\char`\{}\meta{#1}{\ttfamily\char`\}}} \providecommand\oarg[1]{% {\ttfamily[}\meta{#1}{\ttfamily]}} \providecommand\parg[1]{% {\ttfamily(}\meta{#1}{\ttfamily)}} % There is very little we seem to need from the scrdoc extras: page geometry % is set by geometry package and a4paper option from bnumexpr.tex file. So it % seems I only need the hologo loading: \usepackage{hologo} \DeclareRobustCommand*{\eTeX}{\hologo{eTeX}}% %\DeclareRobustCommand*{\LuaTeX}{\hologo{LuaTeX}}% % %%% end of ltxdoc+srcdoc emulation. \ifnum\NoSourceCode=1 \OnlyDescription\fi \usepackage{ifpdf} \ifpdf\chardef\Withdvipdfmx 0 \fi \makeatletter \ifnum\Withdvipdfmx=1 % I think those are mostly obsolete... \@for\@tempa:=hyperref,bookmark,graphicx,xcolor,pict2e\do {\PassOptionsToPackage{dvipdfmx}\@tempa} % \PassOptionsToPackage{dvipdfm}{geometry} \PassOptionsToPackage{bookmarks=true}{hyperref} \PassOptionsToPackage{dvipdfmx-outline-open}{hyperref} %\PassOptionsToPackage{dvipdfmx-outline-open}{bookmark} % \def\pgfsysdriver{pgfsys-dvipdfm.def} \else \PassOptionsToPackage{bookmarks=true}{hyperref} \fi \makeatother \pagestyle{headings} \usepackage[T1]{fontenc} \usepackage[hscale=0.66,vscale=0.75]{geometry} % requires newtxtt 1.05 or later \usepackage[zerostyle=d,scaled=0.95,straightquotes]{newtxtt} \renewcommand\familydefault\ttdefault \usepackage[noendash]{mathastext} \renewcommand\familydefault\sfdefault \usepackage{graphicx} \usepackage[dvipsnames]{xcolor} \definecolor{joli}{RGB}{225,95,0} \definecolor{JOLI}{RGB}{225,95,0} \definecolor{BLUE}{RGB}{0,0,255} %% \definecolor{niceone}{RGB}{38,128,192}% utilisé avant pour urlcolor %% \colorlet{jfverbcolor}{yellow!5} \colorlet{smallverbcolor}{magenta}% 1.6 release NavyBlue RoyalPurple \colorlet{softwrapcolor}{blue} \colorlet{digitscolor}{olive}% 1.6 {OrangeRed} % transféré de xint-manual.tex (maintenant dans xint.dtx) \DeclareFontFamily{U}{MdSymbolC}{} \DeclareFontShape {U}{MdSymbolC}{m}{n}{<-> MdSymbolC-Regular}{} \makeatletter \newbox\cdbx@SoftWrapIcon \def\cdbx@SetSoftWrapBox{% \setbox\cdbx@SoftWrapIcon\hb@xt@\z@ {\hb@xt@\fontdimen2\font {\hss{\color{softwrapcolor}\usefont{U}{MdSymbolC}{m}{n}\char"97}\hss}% \hss}% } \makeatother \usepackage[french, main=english]{babel} \usepackage{hyperref} \hypersetup{% %linktoc=all,% breaklinks=true,% colorlinks=true,% urlcolor=Cerulean,% 1.6 ProcessBlue,%SkyBlue linkcolor=PineGreen,% pdfauthor={Jean-François Burnol},% pdftitle={The bnumexpr package},% pdfsubject={Arithmetic with TeX},% pdfkeywords={Expansion, arithmetic, TeX},% 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}} %\def\MicroFont {\ttfamily \color[named]{OrangeRed}\cdbx@SetSoftWrapBox } % \MacroFont est utilisé par macrocode, mais sa définition est figée dans % \macro@font au \begin{document} \def\MacroFont {\ttfamily \baselineskip12pt\relax } %--- November 4, 2014 making quotes straight. (maintenu pour *) % % there is no hook in \macrocode after \dospecials etc. Thus I will need to % take the risk that some future evolution of doc.sty (or perhaps scrdoc) % invalidates the following. % % Actually, I should not at all rely on the doc class, I should do it all by % myself. \def\macrocode{\macro@code \frenchspacing \@vobeyspaces \makestarlowast % \makequotesstraight (obsolète) \xmacro@code } %--- lower * symbol in text \def\lowast{\raisebox{-.25\height}{*}} \catcode`* \active \def\makestarlowast {\let*\lowast\catcode`\*\active}% \catcode`* 12 \def\verb #1% {% \relax\leavevmode\null \begingroup \MicroFont \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 \@vobeyspaces\everyeof{\noexpand}% \expandafter\@@jfverb\scantokens{##1}\relax}% \@jfverb }% % Pour v1.2 je laisse tomber les colorbox. À la place je change la couleur % du texte. J'utilise le violet de xint. (mis dans \Microfont) % \def\@@@jfverb #1{\ifcat\noexpand#1$% $ % \endgroup\else % % \penalty\z@ % \discretionary{\copy\cdbx@SoftWrapIcon}{}{}% % % \colorbox{jfverbcolor}{\strut #1}% unfortunately, impacts pdf size % #1% % \expandafter\@@@jfverb\fi } \def\@@jfverb #1{\ifcat\noexpand#1\relax\endgroup\@\else \discretionary{\copy\cdbx@SoftWrapIcon}{}{}% #1\expandafter\@@jfverb\fi } \makeatother % Lundi 17 mai 2021 % 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 % le | n'est pas encore actif ici! \catcode`| \active % first one uses \bnumexpr deliberately with no \bnethe to test 1.4. \def\bneshow #1% {|\bnumexpr#1\relax|% \edef\TEMP{\bnumexpr#1\relax}% \immediate\write\tests{\detokenize{\bnumexpr#1\relax}^^J\TEMP}% \[\color{digitscolor}\TEMP\]\ignorespaces} \def\bneshowthe #1% {|\bnethe\bnumexpr#1\relax|% \edef\TEMP{\bnethe\bnumexpr#1\relax}% \immediate\write\tests{\detokenize{\bnethe\bnumexpr#1\relax}^^J\TEMP}% \[\color{digitscolor}\TEMP\]\ignorespaces} \def\bneshoweval #1% {|\bnumeval|\unskip|{{#1}}|% problème de grab de mon \verb d'où {{..}} \edef\TEMP{\bnumeval{#1}}% \immediate\write\tests{\detokenize{\bnumeval{#1}}^^J\TEMP}% \[\color{digitscolor}\TEMP\]\ignorespaces} \def\bneshowevaltohex #1% {|\bnumeval|\unskip|[h]{#1}|% \edef\TEMP{\bnumeval[h]{#1}}% \immediate\write\tests{\detokenize{\bnumeval[h]{#1}}^^J\TEMP}% \[\color{digitscolor}\TEMP\]\ignorespaces} \def\bneshowevaltooct #1% {|\bnumeval|\unskip|[o]{#1}|% \edef\TEMP{\bnumeval[o]{#1}}% \immediate\write\tests{\detokenize{\bnumeval[o]{#1}}^^J\TEMP}% \[\color{digitscolor}\TEMP\]\ignorespaces} \def\bneshowevaltobin #1% {|\bnumeval|\unskip|[b]{#1}|% \edef\TEMP{\bnumeval[b]{#1}}% \immediate\write\tests{\detokenize{\bnumeval[b]{#1}}^^J\TEMP}% \[\color{digitscolor}\TEMP\]\ignorespaces} \catcode`| 12 % Mardi 14 juin 2022 \def\bnedotest #1{\oodef\x{#1}\immediate\write\tests{\detokenize{#1}^^J\x}} % 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 } \usepackage{xspace} \def\bnumname {\href{http://www.ctan.org/pkg/bnumexpr}{bnumexpr}\xspace }% \def\bnumnameimp {\texorpdfstring {\hyperref[sec:bnumexprcode]{{\color{smallverbcolor}\ttzfamily bnumexpr}}} {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\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 }% \DeclareRobustCommand\ctanpackage[1]{\href{https://ctan.org/pkg/#1}{#1}} %\frenchspacing \usepackage{bnumexpr} \usepackage{xintfrac} \usepackage{etoc} \usepackage{centeredline} \usepackage{framed} \makeatletter\let\check@percent\relax\makeatother % 2022/06/15 -- comment updated 2025/09/07 % % latex builds at home during preparation for the release are done % using --translate-file=natural.tcx for the benefit of \ALERTE. % The shipped to CTAN dtx will not contain any \ALERTE execution % hence can be build using latex normally. % % AT LONG LAST LATEX HAS MADE THE UTF-8 MACROS PROTECTED % so many years waiting for that... {\catcode27=12 \gdef\babar{^^[}} \def\ALERTE{\edef\foo{\noexpand\AtEndDocument {\immediate\write128{\babar[1;31mATTENTION METTRE À JOUR COMMENTAIRE EN LIGNE \the\inputlineno\babar[0m}}}% \foo} % 2025/09/07 % https://tex.stackexchange.com/a/62032 % Et je mets [2020/02/02] dans les \NeedsTeXFormat de bnumexpr{.tex,.dtx}, % pour être sûr \ExplSyntaxOn \NewDocumentCommand{\getenv}{om} { \sys_get_shell:nnN { kpsewhich ~ --var-value ~ #2 } { } \l_tmpa_tl \tl_trim_spaces:N \l_tmpa_tl \IfNoValueTF { #1 } { \tl_use:N \l_tmpa_tl } { \tl_set_eq:NN #1 \l_tmpa_tl } } \ExplSyntaxOff % \getenv[\SHIPPINGNOW]{BNUMEXPR_RELEASING} % \SHIPPINGNOW->\par si la variable n'est pas définie. % L'expansion de \par débute par un \scan_stop: (au moins en 2025) \if1\SHIPPINGNOW\def\ALERTE{\ERREUR}\fi \usepackage[autolanguage,np]{numprint} \usepackage{enumitem} \newenvironment{TeXnote}{\par\footnotesize\medskip\noindent\textbf{\TeX-nical note: }}{\par\medskip} \begin{document} \thispagestyle{empty} \ttzfamily \pdfbookmark[1]{Title page}{TOP} {% \normalfont\Large\parindent0pt \parfillskip 0pt\relax \leftskip 2cm plus 1fil \rightskip 2cm plus 1fil The \bnumname package\par } \RaisedLabel{titlepage} {\centering \textsc{Jean-François Burnol}\par \footnotesize jfbu (at) free (dot) fr\par Package version: \bneversion\ (\bnepackdate)\par {From source file \texttt{bnumexpr.dtx} of \texttt{\bnedtxtimestamp}}\par } \etocsetnexttocdepth{subsection} \etocsettagdepth{code}{section} \etocsettocstyle{}{} \tableofcontents \section{\cs{bnumeval}} \label{sec:bnumexpr} \label{sec:thebnumexpr} \label{sec:bnumeval} This \LaTeX\ package \bnumname provides |\bnumeval|, which is an expandable parser of numerical expressions with big integers. Recent \LaTeX\ has |\inteval|, which is a slim wrapper for \eTeX{}'s \cs{numexpr} (embedded for twenty years in most \TeX-engines except original Knuth's |tex|). \begin{TeXnote} More precisely \cs{inteval}\marg{expression} is equivalent (up to how \TeX{} handles spaces located after in the source during tokenization, as tokenization of control sequences such as \cs{relax} causes \TeX{} to ignore space characters or end-of-line space after it) to: \centeredline{\cs{the}\cs{numexpr}\meta{expression}\cs{relax}} In an analogous way |\bnumeval|\marg{expression} has equivalent forms: \centeredline{|\bnethe\bnumexpr|\meta{expression}|\relax|} \centeredline{|\thebnumexpr|\meta{expression}\cs{relax}} For contexts where the alternative forms may be useful, refer to the \autoref{sec:advanced}. Everyday use needs only |\bnumeval|. \end{TeXnote} Here are the extra features from |\bnumeval| compared to \cs{inteval}: \begin{itemize}[noitemsep] \item It allows arbitrarily big integers, whereas \cs{inteval} is limited to a maximal input equal to $\the\numexpr"7FFFFFFF\relax$ ($2^{31}-1$, or hexadecimal |7FFFFFFF|). \item It recognizes |**| and |^| as infix operator for powers, \item It recognizes |!| as postfix operator for the factorials, \item The new operator |//| computes floored division with |/:| being the operator for the associated remainder (the operator |/| computes rounded division), \item In addition to the \TeX{} prefixes |'| and |"| for octal and hexadecimal, it recognizes |0b|, |0o| and |0x| for binary, octal, and hexadecimal, \item The space character is ignored% % \footnote{It is not completely ignored, |\count37| will automatically be prefixed by \cs{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 Braced material \string{...\string} encountered in the expression is automatically unbraced, \item Comma separated expressions are allowed, \item Some idiosyncrasies of \cs{numexpr} such as |\inteval{-(1)}| causing an error are avoided, \item Syntax is fully customizable and extensible. \end{itemize} Furthermore, |\bnumeval| recognizes an optional argument |[b]|, |[o]| or |[h]| which says to have the calculation result (or comma separated results) be converted to respectively binary, octal or hexadecimal digits. \section{Dependencies} \bnumname is a \LaTeX{} package but it can also be used with Plain \TeX, thanks to \ctanpackage{miniltx}. Use for this \cs{input} \ctanpackage{miniltx}|.tex| and then \cs{input} \bnumname \unskip|.sty|. Do not use \cs{input} but only \cs{usepackage} to load the package with \LaTeX. Addition, subtraction, multiplication, division(s), modulo operator, powers, and factorials are all by default executed by macros provided by the \xintcorename package. Conversions between decimal, binary, octal and hexadecimal bases are done using the macros from the \xintbinhexname package. |\bnumeval| is a scaled-down variant of |\xintiieval| from package \xintexprname, lacking support for nested structures, functions, variables, booleans, sequence generators, etc... . The \xintexprname package is NOT loaded, only as said previously \xintcorename and \xintbinhexname. \begin{TeXnote} Power users can use |\bnumsetup| to configure usage of alternative support macros of their own choosing. Options can disable the loading of \xintcorename and/or \xintbinhexname. But \xintkernelname is always loaded. See \autoref{sec:advanced}. Expert users can even add new operators to the syntax or change the built-in precedences. See \autoref{ssec:expert}. \end{TeXnote} \section{Examples} Some of these examples use the ancient syntax |\bnethe\bnumexpr...\relax| from the initial release (in 2014). The |\bnethe| prefix converts from some private format (using braces and other things). Some examples do not even have the |\bnethe| prefix to |\bnumexpr| because it is allowed in typesetting context to omit it (but in an \cs{edef} without it expansion gives the private format). For details refer to \autoref{sec:advanced} on advanced topics. Some further examples found in this documentation use the other ancient syntax |\thebnumexpr...\relax| where |\thebnumexpr| is equivalent to |\bnethe\bnumexpr|. The recommended interface is |\bnumeval|, as it has optional arguments to cause conversion to hexadecimal, octal or binary. They have no equivalent with |\bnethe\bnumexpr| or with |\thebnumexpr|. Some inputs are weird (such as the first one with three minus signs) because they served originally to check the syntax. % NOTE 2026/09/07. After the 1.6 blunder which shipped with \bnumexpr not % accepting Babel active contrarily to what announcement (but \bnumeval was % ok, and it is the recommened interface) and code comments said, I modified % the preamble to add "french" as language in order to test it here. This % means though that ! and : will be catcode active throughout the document % (even though French is used only locally) so this means my unit testing here % is not anymore one for the standard context with default catcodes but well. % % The \bneshow and other macros not only display the values but also save % input and output to an auxiliary file which is tested via git diff to check % for changes and detect regressions. % % Even with French active, one should beware that in vertical mode it % self-silences itself, and one can be fooled to think no error happened, if % the \bnumeval or \bnumexpr is first thing in the paragraph. But here the % \bneshow and other macros will quit vertical mode prior to computing the % expression, so that trap is avoided. \noindent\bneshow {--- 1 208 637 867 * (2 187 917 891 - 3 109 197 072)}% \bneshow { ( 13_8089_1090 - 300_1890_2902 ) * ( 1083_1908_3901 - 109_8290_3890 )}% \bneshoweval {92_874_927_979 ** 5 - 31_9792_7979 ** 6}% \foreignlanguage{french}{% \bneshowthe {10!, 20!, 30!}% broken at 1.6... fixed at 1.6a Testing tacit multiplication elevated precedence\string:\newline \bneshoweval {30!/(21*22*23*24*25)(26*27*28*29*30), 20!}% worked as expected at 1.6 \bneshoweval {13^50//12^50, 13^50/:12^50}% idem }% \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! /: 10^50}% % Let's check hexadecimal input:\newline \bneshoweval {"0010 * "0100 * 0x1000 * 0xA0000, 16^(1+2+3+4)*10}% And also hexadecimal output:\newline \bneshowevaltohex{"7F_FFF_FFF+1, 0x0400^3, "ABCDEF*"0000FEDCBA, 1234}% % Let's make a few checks of octal and binary:\newline \bneshowevaltooct {'75316420 * 0o44445555} \bneshowevaltobin {'75316420 * 0o44445555} \bneshowevaltobin {0xFFFF, 0o77, 0b1000001^3} % We end with some strange non-recommended things to check details of how the parser expands the input:\newline \bneshoweval {"0000\bnumeval[h]{00000012345678}FFFF, 000012345679*16**4-1} % \bneshowevaltooct{0b000\bnumeval[b]{'123456}, 0x\bnumeval[h]{0o00000123456}} \section{Customizing how output is ``printed out''} \subsection{Printing big numbers} \label{sec:printing} \TeX{} and \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: \begin{verbatim} \def\allowsplits #1{\ifx #1\relax \else #1\hskip 0pt plus 1pt\relax \expandafter\allowsplits\fi}% \def\printnumber #1{\expandafter\allowsplits \romannumeral-`0#1\relax }% \end{verbatim} \def\allowsplits #1{\ifx #1\relax \else #1\hskip 0pt plus 1pt\relax \expandafter\allowsplits\fi}% \def\printnumber #1{\expandafter\allowsplits \romannumeral-`0#1\relax }% Here is an example of use and its output: \begin{verbatim} \noindent|\bnumeval{1000!} =| \textcolor{digitscolor}{\printnumber{\bnumeval{1000!}}} \end{verbatim} \noindent|\bnumeval{1000!} =|\phantom{0}% \textcolor{digitscolor}{\printnumber{\bnumeval{1000!}}} \begin{TeXnote} |\bnumeval| is \emph{f}-expandable, so the |\romannumeral-`0| as used here in |\printnumber| causes its full expansion (even if for example the output contains multiple values, separated by commas). So then |\printnumber|'s auxiliary can simply loop over the tokens. \end{TeXnote} \begin{TeXnote} Note that inside math mode, the inserted \cs{hskip}'s have no effect. There should be some \cs{allowbreak}'s. By the way, we allow some stretch so that line endings match the actual linewidth. \end{TeXnote} \subsection{\cs{bnumprintone}, \cs{bnumprintonesep}} The output values are each fetched to |\bnumprintone| and separated by |\bnumprintonesep|. Here are the default definitions (or rather some quasi equivalents in \LaTeX's lingua): \begin{verbatim} \newcommand{\bnumprintone}[1]{#1} \newcommand{\bnumprintonesep}{, } \end{verbatim} In other terms |\bnumprintone| produces its argument ``as is'', and multiple values get separated by a comma and a space. Let's say you want the output to be boxed. Doing |\fbox{\bnumeval{...}}| will make one single frame even in case of multiple values. Redefining |\bnumprintone| is the way to go: \begingroup\RenewDocumentCommand{\bnumprintone}{m}{\fbox{#1}} \begin{verbatim} \RenewDocumentCommand{\bnumprintone}{m}{\fbox{#1}} \bnumeval{2^10, 3^10, 5^10, 7^10} \end{verbatim} \textcolor{digitscolor}{\bnumeval{2^10, 3^10, 5^10, 7^10}} \endgroup It is important to have used |\RenewDocumentCommand| and not \cs{renewcommand} here, because |\bnumprintone| and |\bnumprintonesep| have to be compatible with expansion only context. \begin{TeXnote} That means that |\bnumprintone| in an \cs{edef} should not give rise to any \cs{newcommand}, lower level \cs{def}, count or dimen assignments, etc.... This constraint is due to the fact that |\bnumeval| wraps the final print-out inside of \cs{expanded}, for \TeX nical reasons. The simplest way for |\bnumprintone| (considering that its argument will already have been fully expanded to digit tokens) and |\bnumprintonesep| to be ``safe'' is that they do not expand at all in \cs{edef}. This is the case if they are defined using \cs{RenewDocumentCommand}. % With an older \LaTeX{}, or Plain \eTeX{} (but having some \cs{fbox} at our disposal), we would have used here |\protected\def\bnumprintone#1{\fbox{#1}}|. \end{TeXnote} A more common use case will be to have the outputs be typeset according to the conventions of the document language. This is easily done redefining |\bnumprintone| in terms of (for example) the \cs{np} macro of package \ctanpackage{numprint}. \begingroup\RenewDocumentCommand{\bnumprintone}{m}{\np{#1}} \renewcommand{\bnumprintonesep}{ --- } \begin{verbatim} \RenewDocumentCommand{\bnumprintone}{m}{\np{#1}} \renewcommand{\bnumprintonesep}{ --- } \bnumeval{2^10, 3^10, 5^10, 7^10} \end{verbatim} \textcolor{digitscolor}{\bnumeval{2^10, 3^10, 5^10, 7^10}} \endgroup \begin{TeXnote} Usage of \cs{RenewDocumentCommand} for |\bnumprintonesep| was not needed here, obviously its expansion could cause no trouble. \end{TeXnote} Let's give another use case. Assume you are computing in one go multiple large values, too large to fit on a line. The simple-minded |\printnumber| of the previous section will (due to some \TeX nicality) swallow the spaces injected by |\bnumprintonesep|. To fix this, the simplest is to redefine |\bnumprintone| to execute |\printnumber|: \begingroup\let\bnumprintone\printnumber \begin{verbatim} \renewcommand{\bnumprintone}[1]{\printnumber{#1}} \bnumeval{2^100, 3^100, 5^100, 7^100} \end{verbatim} % je ne peux pas utiliser \bneshoweval car il passe ne mode mathématique \textcolor{digitscolor}{\bnumeval{2^100, 3^100, 5^100}} \endgroup \begin{TeXnote} Our |\printnumber| belongs to this family of macros causing no damage if expanding in an \cs{edef}. So, it was not needed to use \cs{RenewDocumentComand}. \end{TeXnote} \subsection{\cs{bnumprintonehex}, \cs{bnumprintoneoct}, \cs{bnumprintonebin}} When |\bnumeval| is exerted with |[h]|, |[o]| or |[b]| it does not use |\bnumprintone| but one of |\bnumprintonehex|, |\bnumprintoneoct| or |\bnumprintonebin|. The same |\bnumprintonesep| is used as with decimal numbers. The default definitions are as for |\bnumprintone| to ``print as is''. \begingroup \def\bnumprintonehex#1{0x#1} To give an example of a custom definition, one may want hexadecimal output to use the |0x| prefix. This is very easy: \begin{verbatim} \renewcommand{\bnumprintonehex}[1]{0x#1} \end{verbatim} \bneshowevaltohex{7^30, 13^20, 20!} \endgroup \begin{TeXnote} It was unneeded to use \cs{RenewDocumentCommand} here because prefixing with |0x| is obviously compatible with expansion-only context. \end{TeXnote} \section{Babel-active characters are not a problem!} Some languages use active characters with PDF\LaTeX. For example the |babel-french| module turns the colon |:| and the exclamation mark |!| into active characters (whose expansions would cause |\bnumeval| to crash). It used to be necessary to take preventive measures such as either turning the activation off altogether or use in the input |/\string:| and |\string!| as clumsy replacements of |/:| and |!|. Those troubled times are gone! With release |1.6| they will work fine as is in |\bnumeval|. The same applies to all other characters if babel-active. There are miracles sometimes! Warning: characters made active otherwise still need the \cs{string} or other workaround to be usable as operators in the syntax. \section{Fine print (not needed to read this for regular use)} \label{sec:advanced} \footnotesize \subsection{The \cs{bnumsetup} command} 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 \xintexprname) and package \xintbinhexname. \begin{verbatim} \usepackage{xintcore} \usepackage{xintbinhex} \end{verbatim} It then uses |\bnumsetup| in the following way (the final comma is optional, and spaces around equal signs also; there can also be spaces before the commas but the author dislikes such style a lot so they are not used here): \begin{verbatim} \bnumsetup{% add = \xintiiAdd, sub = \xintiiSub, opp = \xintiiOpp, mul = \xintiiMul, pow = \xintiiPow, fac = \xintiiFac, div=\xintiiDivFloor, mod=\xintiiMod, divround=\xintiiDivRound, hextodec=\xintHexToDec, octtodec=\xintOctToDec, bintodec=\xintBinToDec, dectohex=\xintDecToHex, dectooct=\xintDecToOct, dectobin=\xintDecToBin, }% \end{verbatim} One can use |\bnumsetup| to map one, some, or all keys to macros of one's own choosing. Of course it is then up to user to load the suitable packages. If one has alternatives for all of the above \xintcorename macros, so that this package is not needed at all, one can pass option |customcore| to \bnumname at loading time: \centeredline{|\usepackage[customcore]|\{\bnumname\}} This tells to not load \xintcorename. Similarly there is an option |custombinhex| to not load \xintbinhexname. Make sure then to provide suitable replacements to all base conversion macros! Option |custom| means doing both of |customcore| and |custombinhex|. Even under this option package \xintkernelname will always be loaded. Here are the conditions that the custom macros must obey: \begin{enumerate}[noitemsep] \item They all must be \emph{f}-expandable. More precisely: \begin{enumerate}[noitemsep] \item The macro for computing factorials only has to be \emph{x}-expandable. \item Note that any \emph{x}-expandable macro can be wrapped into an \emph{f}-expandable one, using \cs{expanded}. \item If |\bnumprintonehex| is redefined and becomes \cs{protected} then the macro for converting to hexadecimal (value of key |dectohex|) only has to be \emph{x}-expandable, and similarly for conversion to octal and binary. \end{enumerate} \item It is sufficient for them to be able to handle arguments in \emph{raw normalized form}, i.e., sequences of explicit decimal (or hexadecimal for the macro associated with key |hextodec|) digits, no leading zeros, with at most one minus sign and no plus sign. \item Their output format is limited only by the fact that it should be acceptable input to all the other operators, as well as to the user optional re-definition of |\bnumprintone|. If one cares about hexadecimal (et al.) output one must ensure the macros output format is suitable input for those macros actually doing the conversion from decimal to other bases. \item \emph{Important}: hence if only some macros among those associated to operators (i.e. those by default originating in \xintcorename), or to conversions into decimal, are custom, their output \textbf{must} be produced in \emph{raw normalized form}, as this is the format required by the \xintcorename macros and by the \xintbinhexname macros converting from decimal to other bases. However if one does not care about producing output in binary, octal or hexadecimal (as is the case in the next section), and if one has replaced all \xintcorename macros, the output format can be as one likes. \end{enumerate} \subsection{Example of customization: let's 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 already postfix [0] \newcommand\myFac[1]{\xintFac{#1}}% will have already postfix [0] \makeatletter \def\myRemovePostFix#1{\@myRemovePostFix#1[0]\relax}% \def\@myRemovePostFix#1[0]#2\relax{#1} \makeatother \let\bnumprintone\myRemovePostFix \bnumsetup{add=\myIrrAdd, sub=\myIrrSub, mul=\myIrrMul, divround=\myDiv, div=\myDivFloor, mod=\myMod, pow=\myPow, fac=\myFac}% \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 already postfix [0] \newcommand\myFac[1]{\xintFac{#1}}% will have already postfix [0] \makeatletter \def\myRemovePostFix#1{\@myRemovePostFix#1[0]\relax}% \def\@myRemovePostFix#1[0]#2\relax{#1} \makeatother \let\bnumprintone\myRemovePostFix \bnumsetup{add=\myIrrAdd, sub=\myIrrSub, mul=\myIrrMul, divround=\myDiv, div=\myDivFloor, mod=\myMod, pow=\myPow, fac=\myFac}% \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 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. \subsection{Significant differences between \cs{bnumexpr} and \cs{numexpr}} \label{sec:differences} Apart from the extension to big integers and the added operators, there are a number of important differences between |\bnumexpr| and |\numexpr|: \begin{enumerate}[noitemsep] \item Contrarily to \cs{numexpr}, the |\bnumexpr| parser stops only after having found (and swallowed) a mandatory ending \cs{relax} token (it can arise from expansion), \item In particular note that spaces between digits do not stop |\bnumexpr|, in contrast with \cs{numexpr}: |\the\numexpr 3 5+79\relax| expands (in one step) to \expandafter|\the\numexpr 3 5+79\relax| |\thebnumexpr 3 5+79\relax| expands (in two steps) to \expandafter\expandafter\expandafter|\thebnumexpr 3 5+79\relax| \item With |\edef\myvariable{\bnumexpr 1+2\relax}|, the computation is done at time of the \cs{edef}. It prepares |\myvariable| as a self-contained pre-computed unit which is recognized as such when inserted in a \bnumname expressions. It triggers tacit multiplication: |7\myvariable| is like |7*\myvariable|. This is different from what would happen if we had used |\edef\myvariable{\bnethe\bnumexpr...}| which would simply have |\myvariable| expand to digit tokens so |7\myvariable| then constructs a number with |7| as first digit. \begin{quote} Let's give an example. Note that \cs{edef} has the effect of pre-evaluating. With \cs{def} the outputs would be the same, but the computations would be delayed to |\bnumeval| execution. % attention mon \verb incapable de gérer % \noindent|\edef\x{\bnumexpr 3^10\relax}|% \% precomputes but keeps private format\newline \edef\x{\bnumexpr 3^10\relax}\bneshoweval{10000\x} \noindent|\edef\y{\bnethe\bnumexpr 3^10\relax}|% \% evaluates to explicit digits\newline \edef\y{\bnethe\bnumexpr 3^10\relax}\bneshoweval{10000\y} In the example with |\x|, tacit multiplication applied, whereas in the example with |\y| it is as if the digits had been input by hand in place of |\y|. % Note that the tacit multiplication behaves as expected relative to powers:\newline \bneshoweval{10^10\x} And we certainly do no want to try |10^10\y| which is like |10^10|\expandafter|\y|. \end{quote} There is no analog with \cs{numexpr}: \begin{enumerate}[noitemsep] \item |\edef\foo{\numexpr1+2\relax}| will define |\foo| as |\numexpr1+2\relax| where the calculation is not yet done. \item Inserting the |\foo| as is in the document text causes an error. \item Trying |\the\numexpr 7\foo\relax| with such a |\foo| causes an error. One must use the multiplication sign |*| explicitly. \end{enumerate} \item Expressions may be comma separated. On input, spaces are ignored, and on output the values are comma separated with a space after each comma, \item |\thebnumexpr -(1+1)\relax| is legal contrarily to |\the\numexpr -(1+1)\relax| which raises an error, \item |\thebnumexpr 2+-(1+1)\relax| is legal contrarily to |\the\numexpr 2+-(1+1)\relax| which raises an error, \item |\the\numexpr 2\cnta\relax| is illegal (with \cs{cnta} a \cs{count}-variable.) But |\thebnumexpr 2\cnta\relax| is perfectly legal and will do the tacit multiplication, \item More generally, tacit multiplication applies in front of parenthesized sub-expressions, or sub |\bnumexpr...\relax| (or |\numexpr...\relax|), or also after parentheses in front of numbers, \item The underscore |_| is accepted within the digits composing a number and is silently ignored by |\bnumexpr|. \end{enumerate} Regarding constructs such as |\edef\myvariable{\bnumexpr 1+2\relax}|, it was explained |\myvariable| behaves then in a special way in another |\bnumexpr| expression (or |\bnumeval|). It is also worth mentioning that it can be used directly in the typesetting stream. But if written to an external file it will expand to some internal format which is not documented as it may vary in future. One can NOT use a |\myvariable| as above in an \cs{ifnum} test, even if representing a single small integer. It will work with syntax such as |\ifnum\bnethe\myvariable=7 ...|. A point of note is that |\bnethe\myvariable| or |\bnethe\bnumexpr...\relax| expand to explicit digits so (assuming here there no other comma separated value computed), \begin{verbatim} \ifnum 3>\bnethe\bnumexpr...\relax ... \fi \end{verbatim} is dangerous, because the integer is not properly terminated. Here one could reverse the order, but the simplest way is simply to use |\bnumeval|: \begin{verbatim} \ifnum 3>\bnumeval{...} ... \fi \end{verbatim} Now, the end of line space injected by \TeX{} will terminate the integer and make the \cs{ifnum} test safe. \subsection{For the expert user: expression syntax and its customizability} \label{ssec:expert} \subsubsection{Expression syntax} 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|. Release |1.6| added support for the |0b|, |0o|, |0x| and |'| prefixes. Commas separating multiple expressions are allowed. The whole expression is handled token by token, any component (digit, operator, parentheses... even the ending \cs{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:\newline \bneshoweval{2^3^4, 2^(3^4)} 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 with |+|, |-|, |*|, |/|, |//|, |/:|, |**|, |^| 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} \subsubsection{Precedences} 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} 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. 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|.% % \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| compute fractions! \subsubsection{\cs{bnumdefinfix}} 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. Digits are not allowed to be first or last in \meta{operator}. The following characters are not allowed at all: |\|, \textcolor{smallverbcolor}{\{}, \textcolor{smallverbcolor}{\}}, |#| and {\catcode`\% 12 |%|}. 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 (except if they expand to innocent ones) must be prefixed by \cs{string} at the time of the definition of the operator whose names will use them. Same at time of use, except if they are Babel active then (new with |1.6|) they need no precaution 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. 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. Fully qualified names are never ambiguous, and a shortcut once defined will change meaning only under two circumstances: \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} \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} %\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} % \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.% % \footnote{With \xintexprname, whose \cs{xinteval} has a |!=| operator, |2+3!==8| is interpreted automatically as |2+(3!)==8|, thanks to internal work-around added at |1.4g|. This has not been backported to \bnumname as it does not per default support operators such as |!=| or |==| and only has generic support for adding multi-character operators. Regarding |2 + 3! = 5|, trying to let this be interpreted as |2+(3!)=5| makes sense only if a |=| operator has been defined. If no |!=| operator exists, the magic will be automatic. If however both |=| and |!=| exist, then it would need special overhead to the parser dealings when finding |!| to avoid the |!=| interpretation. One could imagine distinguishing |! =| from |!=| but the swallowing of spaces is deeply coded in the parser. As \bnumname by default supports no infix operator starting with |!|, it is not worth it to include in the package extra overhead to solve such issues when extending the syntax. At the level of \xintexprname, there is no issue because there is no |=| operator.}\newline % \bneshoweval{2^5 == 4 times 8, 11 t 14} \bneshoweval{100 ++ -10 ^ 3, (100 - 10)^3, 2 ^ 5 ++ 3, 2^(5+3)} \subsubsection{\cs{bnumdefpostfix}} 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} \normalsize \section{Changes} \begin{description} \input bnumexprchanges.tex \end{description} \section{License} \begingroup\makeatletter \baselineskip10pt \begin{verbatim} Copyright © 2014-2022, 2025 Jean-François Burnol | This Work may be distributed and/or modified under the | conditions of the LaTeX Project Public License 1.3c. | This version of this license is in > | and version 1.3 or later is part of all distributions of | LaTeX version 2005/12/01 or later. This Work has the LPPL maintenance status "author-maintained". The Author and Maintainer of this Work is Jean-François Burnol. This Work consists of the main source file and its derived files bnumexpr.dtx, bnumexpr.sty, bnumexpr.pdf, bnumexpr.tex, bnumexprchanges.tex, README.md \end{verbatim} \endgroup \normalsize \StopEventually{\end{document}\endinput} % workaround % https://github.com/latex3/latex2e/issues/563 \makeatletter \AddToHook{env/macrocode/begin}{\partopsep0pt\relax} \AddToHook{env/macrocode/after}{\@nobreakfalse} \makeatother \newgeometry{hscale=0.75,vscale=0.75}% ATTENTION \newgeometry fait % un reset de vscale si on ne le % précise pas ici !!! \MakePercentIgnore \AddToHook{env/macrocode/begin}{\bfseries} % % \catcode`\<=0 \catcode`\>=11 \catcode`\*=11 \catcode`\/=11 % \let\relax % \def<*package>{\catcode`\<=12 \catcode`\>=12 \catcode`\*=12 \catcode`\/=12 } % %<*package> % \etocdepthtag.toc{code} % \section{Commented source code} % \label{sec:bnumexprcode} % \etocdefaultlines % \etocignoredepthtags % \etocsetnexttocdepth{subsection} % \localtableofcontents % \etocmarkbothnouc{\bnumnameuserdoc \hyperref[sec:bnumexprcode]{implementation}} % % At |1.6|, |\bnumeval| requires the |1.4n| release of \xintcorename and % \xintbinhexname (or at least of \xintkernelname if option |custom| is used). % It adds |0b|, |0o|, |'|, and |0x| to the syntax, and % admits optional parameters |[b]|, |[o]|, and |[h]| to produce the % output converted to binary, octal, or hexadecimal. % % It is amusing that implementing the support for the optional argument % had the un-anticipated corollary that Babel active characters (such as % |!| with French) are auto-taming. See the code comments. % % A problem with |_| if upfront in numbers was fixed. % % There was some refactoring, relative to extending |\bnumsetup| with new keys % related to base conversion macros and this lead to the removal of % \cs{bnumprintonetohex} and \cs{bnumhextodec}. % % At |1.5|, right-associativity was enforced for powers in anticipation of % upstream \xintexprname |1.4g 2021/05/25|, and the customizability and % extendibility of the package is made total via added |\bnumdefinfix| and % |\bnumdefpostfix|. % % % \footnotesize % % Older comments at time of |1.4| and |1.4a| releases: % % I transferred mid-May 2021 from \xintexprname its \cs{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 % \xintbinhexname. % % 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 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 \cs{romannumeral} trigger and did not insert an \cs{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 % |Paragraph ended before \BNE_print_c was complete| % error because |\BNEprint| makes the tacit requirement % that the 1D ople to output has at least one item. % \end{itemize} % % \normalsize % \subsection{Package identification} % \begin{macrocode} \NeedsTeXFormat{LaTeX2e}% \ProvidesPackage{bnumexpr}[2025/09/07 v1.6a Expressions with big integers (JFB)]% % \end{macrocode} % \subsection{Load \xintkernelname} % At |1.6|, in order to make the base conversion macros also customizable, % hence not mandate loading of \xintbinhexname, we only load unconditionally % \xintkernelname. % % We then switch to the familiar catcode regime of the \xintexprname sources. % \begin{macrocode} \RequirePackage{xintkernel}[2025/09/05]% % \end{macrocode} % \subsection{Save catcode regime and switch to our own} % \begin{macrocode} \edef\BNErestorecatcodesendinput{\XINTrestorecatcodes\noexpand\endinput}% \XINTsetcatcodes% % \end{macrocode} % \subsection{Load optionally \xintcorename and \xintbinhexname} % |1.6| adds |customcore| as alias of legacy |custom|. It adds |custombinhex| % to add possibility of not loading \xintbinhexname either. % Option |custom| now means both of |customcore| and |custombinhex|. % % But who on Earth isn't going to use with delight both my \xintcorename and % \xintbinhexname? % \begin{macrocode} \def\BNE_tmpa{1}\def\BNE_tmpb{1}% \DeclareOption{custom}{\def\BNE_tmpa{0}\def\BNE_tmpb{0}}% \DeclareOption{customcore}{\def\BNE_tmpa{0}}% \DeclareOption{custombinhex}{\def\BNE_tmpb{0}}% \ProcessOptions\relax \if1\BNE_tmpa\RequirePackage{xintcore}[2025/09/05]\fi \if1\BNE_tmpb\RequirePackage{xintbinhex}[2025/09/05]\fi % \end{macrocode} % \subsection{\cs{bnumsetup}} % |\bnumsetup| is the new name at |1.5| of \cs{bnumexprsetup}. % The old name was kept as an alias at |1.5|, and deleted at |1.6|. % % Note that a final comma will cause no harm. % \begin{macrocode} \catcode`! 3 \def\bnumsetup #1{\BNE_parsekeys #1,=!,}% \def\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% \ifxintverbose \PackageInfo{bnumexpr}{assigned \ifxintglobaldefs globally \fi \string#2 to \xint_zapspaces #1 \xint_gobble_i\MessageBreak % \end{macrocode} % Workaround for the space inserted by \cs{on@line}. % \begin{macrocode} \expandafter\xint_firstofone}% \fi \BNE_parsekeys }% \def\BNE_parsedone #1\BNE_parsekeys {}% \catcode`! 12 % \end{macrocode} % Final comma and spaces are only to check if it does work. But I will % NOT insert spaces before commas, even though they are allowed! % % |1.6| also handles base conversion macros here. Prior to |1.6| this % |\bnumsetup| configuration was not executed if package received option % |custom| (now |customcore|). But as the user is then responsible for % redefining all keys, why bother. % \begin{macrocode} \bnumsetup{% add = \xintiiAdd, sub = \xintiiSub, opp = \xintiiOpp, mul = \xintiiMul, pow = \xintiiPow, fac = \xintiiFac, div = \xintiiDivFloor, mod = \xintiiMod, divround = \xintiiDivRound, hextodec=\xintHexToDec, octtodec=\xintOctToDec, bintodec=\xintBinToDec, dectohex=\xintDecToHex, dectooct=\xintDecToOct, dectobin=\xintDecToBin, }% % \end{macrocode} % By the way the keys should have been |Add|, |Sub|, \dots, not |add|, |sub|, % \dots, so internally |\BNE_Op_Add| etc\dots\ would have been the macros % defined by |\bnumsetup| and used in the code, not |\BNE_Op_add| (et al.) % whose casing does not match my naming conventions. % % \subsection{Some extra constants needed for user defined precedences} % % For the mechanism of |\bnumdefinfix| we need precedence levels to be % available as \cs{chardef}'s. \xintkernelname already provides 0-10, 12, 14, % 16, 17, 18, 20, 22. % % Left levels need to be represented by one token; right levels are % hard-coded into |checkp_| macros and could have been there explicit % digit tokens but we will use the |\xint_c_...| \cs{char}-tokens. % \begin{macrocode} \chardef\xint_c_xi 11 \chardef\xint_c_xiii 13 \chardef\xint_c_xv 15 \chardef\xint_c_xix 19 \chardef\xint_c_xxi 21 % \end{macrocode} % \subsection{\cs{bnumexpr}, \cs{bnethe}, \cs{bnumeval}} % % |\XINTfstop| has to be the same as defined in \xintexprname, in order for a % subexpression |\xintiiexpr...\relax| to get recognized in |\bnumeval| or % conversely for |\bnumexpr...\relax| to possibly serve inside an % |\xinteval|. But why use \bnumname then? Besides a sub \xintexprname-ession % will break |\bnumeval| if it is anything else than a 1D flat sequence. And % even then it can work only if internal storage format are kept in sync. % % |1.6| deprecates \cs{evaltohex} in favor of |\bnumeval||[h]|. % % The \cs{protected} |\BNEprint| will survive to |\bnumexpr| being expanded in % a \cs{write} or \cs{edef}. But its expansion will be forced by the % \cs{expanded} from |\bnethe|. % % I now really dislike |\thebnumexpr| macro name and at some point had % replaced it with |\bnumtheexpr| but this got reverted. % % |1.6a| uses the strange |\csname| in place of directly |\BNE_wrap| % in order to fix the |1.6| blunder which had done a similar thing, % but too late. This is to tame Babel active characters. The |\bnumeval| % was OK, though. Sadly the blunder was first done in \xintexprname, % after I had reverted perfectly valid implementation there, having % thought I could apply a shortcut, which was simply a brain fault. % And I backported it here... alas... % \begin{macrocode} \def\XINTfstop {\noexpand\XINTfstop}% \def\bnumexpr {\romannumeral0\bnumexpro}% \def\bnumexpro {\csname BNE_wrap\expandafter\endcsname \romannumeral0\BNE_bareeval}% % \end{macrocode} % While preparing |1.6| I wondered why the ``|.|'' after |\BNEprint| in % |\BNE_wrap| which is then gobbled by |\BNEprint|. It was clear it came from % \xintexprname, but why was it kept here? % % The reason is to support having a sub |\bnumexpr...\relax| inside % |\bnumeval| or |\xinteval|. Indeed such a sub-expression is identified via % the presence of the |\XINTfstop| after its expansion, and the code inside % \bnumname handling this is inherited from \xintexprname, so it expects the % structure |\XINTfstop| then a ``print'' macro, then possibly some stuff % delimited by a full stop (this is related to the implementation of the % optional arguments of |\xintfloateval| and |\xintieval|). % % As we keep this stuff handled the same way we must inject the seemingly % silly full stop here for |\bnumexpr...\relax| (or a macro defined from it % via an \cs{edef}) to be usable inside |\bnumval| or another % |\bnumexpr...\relax|. % % A consequence is that |\bnumexpr...\relax| can be used as sub-unit in % |\xinteval| and conversely |\xintiiexpr...\relax| in |\bnumeval|, as long as % it does not have nested structures via bracketed inputs, which are not % supported by \bnumname's syntax. But why would one do such things? Also % this can only work as long as internal storage of intermediate result by % |\bnumeval| is a sub-case of the way it is done for |\xinteval|. % \begin{macrocode} \def\BNE_wrap {\XINTfstop\BNEprint.}% % \end{macrocode} % It is important to keep in mind that |#1| has the structure % \{|{...}{...}...{...}|\} with an external brace pair, which here gets % removed. ^^A attention que mon |...| va retirer une paire d'accolades. % In the replacement the external |{{...}}| are for \cs{expanded}. % % See above about the strange ``|.|'' inserted by |\BNE_wrap| and gobbled % here. We also define a non \cs{protected} variant without the extra full % stop, it will serve for |\bnumeval| (and |\thebnumexpr|). % \begin{macrocode} \protected\def\BNEprint.#1{{\BNE_print#1.}}% \def\BNEprint_#1{{\BNE_print#1.}}% % \end{macrocode} % |\bnethe| removes the |\XINTfstop| and activates the printing via % |\BNEprint|. % % Attention that prior to |1.6| |\bnethe| grabbed a |#1|, hence would % work to print a braced |\bnumexpr...\relax|, but I don't see the reason % for doing that. Removed. % % |1.6a| modifies |\thebnumexpr| here for the Babel active thing. % \begin{macrocode} \def\bnethe{\expanded\expandafter\xint_gobble_i\romannumeral`&&@}% \def\thebnumexpr{\expanded\csname BNEprint_\expandafter\endcsname \romannumeral0\BNE_bareeval}% % \end{macrocode} % At |1.6| after implementing the |[h]| optional argument of |\bnumeval|, % there was the un-anticipated result that this tamed Babel active characters. % This is explained by the expansion happening while a \cs{csname} is not yet % closed. And by the fact that during its expansion |\bnumeval| does not % use delimited macros, for example to fetch up to a closing parenthesis. % % There used to be here a |\BNE_start| but it got replaced by its expansion. % % The |\BNE_check| is defined in the section \hyperref[ssec:span]{``Expansion % spanning''}. % % Prior to |1.6| |\BNE_bareeval| was named \cs{bnebareeval}, but this was % outside of the package namespace (it should have been \cs{bnumbareeval}, or % \cs{bnumexprbareeval}). Upstream has |\xintbareeval| without underscores % for legacy reasons. % \begin{macrocode} \def\BNE_bareeval{\expandafter\BNE_check\romannumeral`&&@\BNE_getnext}% % \end{macrocode} % These next are not \cs{protected} because they are only used with % |\bnumeval|, there is no analog of the private format which |\bnumexpr| % expands to. This also spares us having to define macros with names which % can be written to an external file and re-read using the standard catcodes. % Thus, no need for some |\BNEprinthex| et al.\@ here. % \begin{macrocode} \expandafter\def\csname BNEprint_[h]\endcsname#1{{\BNE_printhex#1.}}% \expandafter\def\csname BNEprint_[o]\endcsname#1{{\BNE_printoct#1.}}% \expandafter\def\csname BNEprint_[b]\endcsname#1{{\BNE_printbin#1.}}% \expandafter\let\csname BNEprint_[]\endcsname\BNEprint_ % \end{macrocode} % |[b]|, |[o]| and |[h]| added at |1.6|. % \begin{macrocode} \def\bnumeval #1#{\expanded\bnumeval_a{#1}}% \def\bnumeval_a#1#2{% \csname BNEprint_\xint_zapspaces #1 \xint_gobble_i\expandafter \endcsname\romannumeral0\BNE_bareeval#2\relax }% % \end{macrocode} % This is deprecated at |1.6| and raises an expandable error. % \begin{macrocode} \def\evaltohex {\expanded \XINT_expandableerror{\evaltohex is DEPRECATED, use \bnumeval with [h]}% \bnumeval_a{[h]}% }% % \end{macrocode} % This code is more compact at |1.6| than at |1.5|. % \begin{macrocode} \def\BNE_print#1{% \bnumprintone{#1}\expandafter\BNE_print_a\string }% \def\BNE_print_a#1{% \if#1.\BNE_print_z\fi\bnumprintonesep \expandafter\BNE_print\expandafter{\iffalse}\fi }% \def\BNE_print_z\fi#1\fi{\fi}% % \end{macrocode} % There is a breaking change at |1.6| as formerly there was a % \cs{bnumprintonetohex}. Now, the decimal to hexadecimal conversion is done % always, and the customizable wrapper was thus renamed to |\bnumprintonehex|. % \begin{macrocode} \def\BNE_printhex#1{% \expandafter\bnumprintonehex \expandafter{\romannumeral`&&@\BNE_Op_dectohex{#1}}% \expandafter\BNE_printhex_a\string }% \def\BNE_printhex_a#1{% \if#1.\BNE_print_z\fi\bnumprintonesep \expandafter\BNE_printhex\expandafter{\iffalse}\fi }% % \end{macrocode} % Octal and binary added at |1.6|. % \begin{macrocode} \def\BNE_printoct#1{% \expandafter\bnumprintoneoct \expandafter{\romannumeral`&&@\BNE_Op_dectooct{#1}}% \expandafter\BNE_printoct_a\string }% \def\BNE_printoct_a#1{% \if#1.\BNE_print_z\fi\bnumprintonesep \expandafter\BNE_printoct\expandafter{\iffalse}\fi }% \def\BNE_printbin#1{% \expandafter\bnumprintonebin \expandafter{\romannumeral`&&@\BNE_Op_dectobin{#1}}% \expandafter\BNE_printbin_a\string }% \def\BNE_printbin_a#1{% \if#1.\BNE_print_z\fi\bnumprintonesep \expandafter\BNE_printbin\expandafter{\iffalse}\fi }% \let\bnumprintone \xint_firstofone \let\bnumprintonehex\xint_firstofone \let\bnumprintoneoct\xint_firstofone \let\bnumprintonebin\xint_firstofone \def\bnumprintonesep{, }% % \end{macrocode} % \subsection{\cs{BNE_getnext}} % The upstream |\BNE_put_op_first| has a string of included \cs{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|. % % This |\BNE_getnext| token is injected via "start" macros associated to % operators or like syntax elements, as will be seen later on. % \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{#2#3{#1}}% \def\BNE_getnext_a #1% {% \ifx\relax #1\xint_dothis\BNE_foundprematureend\fi \ifx\XINTfstop#1\xint_dothis\BNE_subexpr\fi \ifcat\relax#1\xint_dothis\BNE_countetc\fi \xint_orthat{}\BNE_getnextfork #1% }% \def\BNE_foundprematureend\BNE_getnextfork #1{{}\xint_c_\relax}% \def\BNE_subexpr #1.#2% {% \expanded{\unexpanded{{#2}}\expandafter}\romannumeral`&&@\BNE_getop }% % \end{macrocode} % At |1.6| this also filters for \cs{catcode} (as per \xintname |1.4g| % |2021/05/25|). % \begin{macrocode} \def\BNE_countetc\BNE_getnextfork#1% {% \if0\ifx\count#11\fi \ifx\numexpr#11\fi \ifx\catcode#11\fi \ifx\dimen#11\fi \ifx\dimexpr#11\fi \ifx\skip#11\fi \ifx\glueexpr#11\fi \ifx\fontdimen#11\fi \ifx\ht#11\fi \ifx\dp#11\fi \ifx\wd#11\fi \ifx\fontcharht#11\fi \ifx\fontcharwd#11\fi \ifx\fontchardp#11\fi \ifx\fontcharic#11\fi 0\expandafter\BNE_fetch_as_number\fi \expandafter\BNE_getnext_a\number #1% }% \def\BNE_fetch_as_number \expandafter\BNE_getnext_a\number #1% {% \expanded{{{\number#1}}\expandafter}\romannumeral`&&@\BNE_getop }% % \end{macrocode} % 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. % % Also, at |1.6|, slight refactoring to handle digit tokens and opening % parenthesis a bit faster (but this is only first token...); and to ignore an % underscore as first character (rather than raise an error in this case). % % This merges former |\BNE_getnextfork| and |\BNE_scan_number|. % \begin{macrocode} \def\BNE_getnextfork #1{% \if#1-\xint_dothis {{}{}-}\fi \if#1(\xint_dothis {{}\xint_c_ii^v (}\fi \ifnum\xint_c_ix<1\string#1 \xint_dothis {\BNE_startint#1}\fi \xint_orthat {\BNE_getnextfork_a #1}% }% \def\BNE_getnextfork_a #1{% \if#1_\xint_dothis \BNE_getnext_a \fi \if#1+\xint_dothis \BNE_getnext_a \fi \if#1'\xint_dothis \BNE_startoct\fi \if#1"\xint_dothis \BNE_starthex\fi \xint_orthat {\BNE_unexpected #1}% }% % \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 \cs{message} or a \cs{write}) % will then be % \centeredline{|! xint error: Unexpected token `##'. Ignoring.|} % 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. % % |\BNE_unexpected| replaced here |\BNE_notadigit| at |1.6|. % \begin{macrocode} \def\BNE_unexpected#1% {% \XINT_expandableerror{Unexpected token `#1'. Ignoring.}\BNE_getnext_a }% % \end{macrocode} % \subsection{Parsing decimal, hexadecimal, octal, and binary} % Somewhat refactored at |1.6| compared to upstream |1.4m|. Fix the case of % an underscore |_| as first character in input. % \begin{macrocode} \def\BNE_startint #1% {% \if #10\expandafter\BNE_scanint_gobz_a\else\expandafter\BNE_scanint_a\fi #1% }% \def\BNE_wrapint_before{\expandafter{\romannumeral`&&@\iffalse}\fi}% \def\BNE_wrapint_after{\iffalse{{{\fi}}}}% \def\BNE_scanint_a #1#2% {\expandafter\BNE_wrapint_before \expanded\bgroup{\iffalse}\fi #1% \expandafter\BNE_scanint_main\romannumeral`&&@#2}% \def\BNE_scanint_gobz_a #1#2% {\expandafter\BNE_scanint_gobz_b\romannumeral`&&@#2}% % \end{macrocode} % It is important in case of |x|, |o|, or |b| to jump to % |\BNE_starthex| (et al.) and not for example to |\BNE_scanhex_a| % because the latter expects an \emph{f}-expansion to have been % applied already to what comes next. Besides, we do want to trim out % leading zeroes after the |0b|, |0o|, or |0x| prefix: although the % macros of \xintbinhexname do accept leading zeros on input, they may % then produce decimal output with leading zeros, and the ``|ii|'' % macros of \xintcorename consider that an input is vanishing as soon % as the first digit is |0|. % \begin{macrocode} \def\BNE_scanint_gobz_b #1% {% \ifx b#1\xint_dothis \BNE_startbin \fi \ifx o#1\xint_dothis \BNE_startoct \fi \ifx x#1\xint_dothis \BNE_starthex \fi \xint_orthat {\BNE_scanint_gobz_c #1}% }% \def\BNE_scanint_gobz_c #1% {% \expandafter\BNE_wrapint_before\expanded\bgroup{\iffalse}\fi \BNE_scanint_gobz_main#1% }% \def\BNE_scanint_main #1% {% \ifcat \relax #1\expandafter\BNE_scanint_hit_cs \fi \ifnum\xint_c_ix<1\string#1 \else\expandafter\BNE_scanint_checkagain\fi #1\BNE_scanint_again }% \def\BNE_scanint_again #1% {% \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 {% \expandafter\BNE_wrapint_after\romannumeral`&&@\BNE_getop_a#2% }% \def\BNE_scanint_checkagain #1\BNE_scanint_again {% \if _#1\BNE_scanint_checkagain_skip\fi \expandafter\BNE_wrapint_after\romannumeral`&&@\BNE_getop_a#1% }% % \end{macrocode} % |#1| is \cs{fi}. % \begin{macrocode} \def\BNE_scanint_checkagain_skip#1#2\BNE_getop_a#3{#1\BNE_scanint_again}% \def\BNE_scanint_gobz_main #1% {% \ifcat \relax #1\expandafter\BNE_scanint_gobz_hit_cs\fi \ifnum\xint_c_x<1\string#1 \else\expandafter\BNE_scanint_gobz_checkagain\fi #1\BNE_scanint_again }% \def\BNE_scanint_gobz_again #1% {% \expandafter\BNE_scanint_gobz_main\romannumeral`&&@#1% }% % \end{macrocode} % Upstream (at |1.4f|) has |_getop| here, but let's jump directly to % |BNE_getop_a|. The |#2| has been grabbed already and f-expanded. % Nevertheless this means one brace-stripping less. % \begin{macrocode} \def\BNE_scanint_gobz_hit_cs\ifnum#1\fi#2\BNE_scanint_again {% 0\expandafter\BNE_wrapint_after\romannumeral`&&@\BNE_getop_a#2% }% % \end{macrocode} % Fix at |1.6| for when an underscore is used as first character followed by % digits. No need to worry about being very efficient here. % \begin{macrocode} \def\BNE_scanint_gobz_checkagain #1\BNE_scanint_again {% \if _#1\xint_dothis\BNE_scanint_gobz_again\fi \if 0#1\xint_dothis\BNE_scanint_gobz_again\fi \xint_orthat {0\expandafter\BNE_wrapint_after\romannumeral`&&@\BNE_getop_a#1}% }% % \end{macrocode} % |1.5| backported from \xintexprname two bugfixes relative to parsing % hexadecimal input. One bug had |\BNE_scanhex_a| grab an unexpanded token and % used it as is in an \cs{ifcat}... this made syntax such as |"\foo| broken. The % other bug was about leading hexadecimal zeros not being trimmed. % % At |1.6| the code here is refactored to be written exactly as the |scanint| % one, rather than downscaling upstream \xintexprname which also has to handle % fractional input. This avoids gathering the hexadecimal digits then % grabbing then again as a whole via a delimited macro. % \begin{macrocode} \def\BNE_starthex #1% {% \expandafter\BNE_starthex_i\romannumeral`&&@#1% }% \def\BNE_starthex_i #1% {% \if #10\expandafter\BNE_scanhex_gobz_a\else\expandafter\BNE_scanhex_a\fi #1% }% \def\BNE_wraphex_before{\expandafter{\expandafter{% \romannumeral`&&@\iffalse}}\fi\BNE_Op_hextodec}% \def\BNE_wraphex_after{\iffalse{{{{\fi}}}}}% \def\BNE_scanhex_a #1#2% {\expandafter\BNE_wraphex_before \expanded\bgroup{\iffalse}\fi #1% \expandafter\BNE_scanhex_main\romannumeral`&&@#2}% \def\BNE_scanhex_gobz_a #1#2% {\expandafter\BNE_wraphex_before \expanded\bgroup{\iffalse}\fi \expandafter\BNE_scanhex_gobz_main\romannumeral`&&@#2}% % \end{macrocode} % At |1.6| we apply exact same scheme as for the |scanint| code. The sole % difference is the more complicated test for recognizing a digit. % \begin{macrocode} \def\BNE_scanhex_main #1% {% \ifcat \relax #1\expandafter\BNE_scanhex_hit_cs \fi \if\ifnum`#1>`/ \ifnum`#1>`9 \ifnum`#1>`@ \ifnum`#1>`F 0\else1\fi\else0\fi\else1\fi\else0\fi 1\else \expandafter\BNE_scanhex_checkagain\fi #1\BNE_scanhex_again }% \def\BNE_scanhex_again #1% {% \expandafter\BNE_scanhex_main\romannumeral`&&@#1% }% \def\BNE_scanhex_hit_cs #1\BNE_scanhex_checkagain\fi#2\BNE_scanhex_again {% \expandafter\BNE_wraphex_after\romannumeral`&&@\BNE_getop_a#2% }% \def\BNE_scanhex_checkagain #1\BNE_scanhex_again {% \if _#1\BNE_scanhex_checkagain_skip\fi \expandafter\BNE_wraphex_after\romannumeral`&&@\BNE_getop_a#1% }% % \end{macrocode} % |#1| is \cs{fi}, |#3| is underscore. % \begin{macrocode} \def\BNE_scanhex_checkagain_skip#1#2\BNE_getop_a#3{#1\BNE_scanhex_again}% \def\BNE_scanhex_gobz_main #1% {% \ifcat \relax #1\expandafter\BNE_scanhex_gobz_hit_cs\fi \if\ifnum`#1>`0 \ifnum`#1>`9 \ifnum`#1>`@ \ifnum`#1>`F 0\else1\fi\else0\fi\else1\fi\else0\fi 1\else \expandafter\BNE_scanhex_gobz_checkagain\fi #1\BNE_scanhex_again }% \def\BNE_scanhex_gobz_again #1% {% \expandafter\BNE_scanhex_gobz_main\romannumeral`&&@#1% }% \def\BNE_scanhex_gobz_hit_cs#1\BNE_scanhex_gobz_checkagain\fi#2\BNE_scanhex_again {% 0\expandafter\BNE_wraphex_after\romannumeral`&&@\BNE_getop_a#2% }% \def\BNE_scanhex_gobz_checkagain #1\BNE_scanhex_again {% \if _#1\xint_dothis\BNE_scanhex_gobz_again\fi \if 0#1\xint_dothis\BNE_scanhex_gobz_again\fi \xint_orthat {0\expandafter\BNE_wraphex_after\romannumeral`&&@\BNE_getop_a#1}% }% % \end{macrocode} % Added at |1.6|. Exact same code skeleton as for hexadecimal and decimal input. Leading % zeros are removed. % \begin{macrocode} \def\BNE_startoct #1% {% \expandafter\BNE_startoct_i\romannumeral`&&@#1% }% \def\BNE_startoct_i #1% {% \if #10\expandafter\BNE_scanoct_gobz_a\else\expandafter\BNE_scanoct_a\fi #1% }% \def\BNE_wrapoct_before{\expandafter{\expandafter{% \romannumeral`&&@\iffalse}}\fi\BNE_Op_octtodec}% \def\BNE_wrapoct_after{\iffalse{{{{\fi}}}}}% \def\BNE_scanoct_a #1#2% {\expandafter\BNE_wrapoct_before \expanded\bgroup{\iffalse}\fi #1% \expandafter\BNE_scanoct_main\romannumeral`&&@#2}% \def\BNE_scanoct_gobz_a #1#2% {\expandafter\BNE_wrapoct_before \expanded\bgroup{\iffalse}\fi \expandafter\BNE_scanoct_gobz_main\romannumeral`&&@#2}% \def\BNE_scanoct_main #1% {% \ifcat \relax #1\expandafter\BNE_scanoct_hit_cs \fi \if\ifnum`#1>`/ \ifnum`#1>`7 0\else1\fi\else0\fi 1\else \expandafter\BNE_scanoct_checkagain\fi #1\BNE_scanoct_again }% \def\BNE_scanoct_again #1% {% \expandafter\BNE_scanoct_main\romannumeral`&&@#1% }% \def\BNE_scanoct_hit_cs #1\BNE_scanoct_checkagain\fi#2\BNE_scanoct_again {% \expandafter\BNE_wrapoct_after\romannumeral`&&@\BNE_getop_a#2% }% \def\BNE_scanoct_checkagain #1\BNE_scanoct_again {% \if _#1\BNE_scanoct_checkagain_skip\fi \expandafter\BNE_wrapoct_after\romannumeral`&&@\BNE_getop_a#1% }% % \end{macrocode} % |#1| is \cs{fi}, |#3| is underscore. % \begin{macrocode} \def\BNE_scanoct_checkagain_skip#1#2\BNE_getop_a#3{#1\BNE_scanoct_again}% \def\BNE_scanoct_gobz_main #1% {% \ifcat \relax #1\expandafter\BNE_scanoct_gobz_hit_cs\fi \if\ifnum`#1>`0 \ifnum`#1>`7 0\else1\fi\else0\fi 1\else \expandafter\BNE_scanoct_gobz_checkagain\fi #1\BNE_scanoct_again }% \def\BNE_scanoct_gobz_again #1% {% \expandafter\BNE_scanoct_gobz_main\romannumeral`&&@#1% }% \def\BNE_scanoct_gobz_hit_cs#1\BNE_scanoct_gobz_checkagain\fi#2\BNE_scanoct_again {% 0\expandafter\BNE_wrapoct_after\romannumeral`&&@\BNE_getop_a#2% }% \def\BNE_scanoct_gobz_checkagain #1\BNE_scanoct_again {% \if _#1\xint_dothis\BNE_scanoct_gobz_again\fi \if 0#1\xint_dothis\BNE_scanoct_gobz_again\fi \xint_orthat {0\expandafter\BNE_wrapoct_after\romannumeral`&&@\BNE_getop_a#1}% }% % \end{macrocode} % Added at |1.6|. Exact same code skeleton as for octal and hexadecimal, % based upon the one for decimal input. % \begin{macrocode} \def\BNE_startbin #1% {% \expandafter\BNE_startbin_i\romannumeral`&&@#1% }% \def\BNE_startbin_i #1% {% \if #10\expandafter\BNE_scanbin_gobz_a\else\expandafter\BNE_scanbin_a\fi #1% }% \def\BNE_wrapbin_before{\expandafter{\expandafter{% \romannumeral`&&@\iffalse}}\fi\BNE_Op_bintodec}% \def\BNE_wrapbin_after{\iffalse{{{{\fi}}}}}% \def\BNE_scanbin_a #1#2% {\expandafter\BNE_wrapbin_before \expanded\bgroup{\iffalse}\fi #1% \expandafter\BNE_scanbin_main\romannumeral`&&@#2}% \def\BNE_scanbin_gobz_a #1#2% {\expandafter\BNE_wrapbin_before \expanded\bgroup{\iffalse}\fi \expandafter\BNE_scanbin_gobz_main\romannumeral`&&@#2}% \def\BNE_scanbin_main #1% {% \ifcat \relax #1\expandafter\BNE_scanbin_hit_cs \fi \if1\if0#11\else\if1#11\else0\fi\fi\else \expandafter\BNE_scanbin_checkagain\fi #1\BNE_scanbin_again }% \def\BNE_scanbin_again #1% {% \expandafter\BNE_scanbin_main\romannumeral`&&@#1% }% \def\BNE_scanbin_hit_cs #1\BNE_scanbin_checkagain\fi#2\BNE_scanbin_again {% \expandafter\BNE_wrapbin_after\romannumeral`&&@\BNE_getop_a#2% }% \def\BNE_scanbin_checkagain #1\BNE_scanbin_again {% \if _#1\BNE_scanbin_checkagain_skip\fi \expandafter\BNE_wrapbin_after\romannumeral`&&@\BNE_getop_a#1% }% % \end{macrocode} % |#1| is \cs{fi}, |#3| is underscore. % \begin{macrocode} \def\BNE_scanbin_checkagain_skip#1#2\BNE_getop_a#3{#1\BNE_scanbin_again}% \def\BNE_scanbin_gobz_main #1% {% \ifcat \relax #1\expandafter\BNE_scanbin_gobz_hit_cs\fi \if1#1\else\expandafter\BNE_scanbin_gobz_checkagain\fi #1\BNE_scanbin_again }% \def\BNE_scanbin_gobz_again #1% {% \expandafter\BNE_scanbin_gobz_main\romannumeral`&&@#1% }% \def\BNE_scanbin_gobz_hit_cs#1\BNE_scanbin_gobz_checkagain\fi#2\BNE_scanbin_again {% 0\expandafter\BNE_wrapbin_after\romannumeral`&&@\BNE_getop_a#2% }% \def\BNE_scanbin_gobz_checkagain #1\BNE_scanbin_again {% \if _#1\xint_dothis\BNE_scanbin_gobz_again\fi \if 0#1\xint_dothis\BNE_scanbin_gobz_again\fi \xint_orthat {0\expandafter\BNE_wrapbin_after\romannumeral`&&@\BNE_getop_a#1}% }% % \end{macrocode} % \subsection{\cs{BNE_getop}} % The upstream analog to |\BNE_getop_a| applies \cs{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 \cs{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% {% \expandafter\BNE_getop_a\romannumeral`&&@#1% }% \catcode`* 11 \def\BNE_getop_a #1% {% \ifx \relax #1\xint_dothis\xint_firstofthree\fi \ifcat \relax #1\xint_dothis\xint_secondofthree\fi \ifnum\xint_c_ix<1\string#1 \xint_dothis\xint_secondofthree\fi \if (#1\xint_dothis \xint_secondofthree\fi %) \xint_orthat \xint_thirdofthree \xint_c_ {\BNE_prec_tacit *}% \BNE_scanop_a #1% }% \catcode`* 12 \def\BNE_scanop_a #1#2% {% \expandafter\BNE_scanop_b\expandafter#1\romannumeral`&&@#2% }% \def\BNE_scanop_b #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#3#4#5% #1#2=\fi\fi {% #1#2% \expandafter\BNE_scanop_d\csname BNE_itself_#4#5\expandafter\endcsname \romannumeral`&&@% }% \def\BNE_scanop_d #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. % % Update 2021/05/22, I have changed the message, as part of a systematic % removal of |I| invites, in part because |xint 1.4g| changed its % expandable error method and now has a nice message saying \xintname will try % to recover by itself. And now I have about 55 characters available for the % message. % \begin{macrocode} \def\BNE_foundop_a #1% {% \ifcsname BNE_precedence_#1\endcsname \csname BNE_precedence_#1\expandafter\endcsname \expandafter #1% \else \expandafter\BNE_getop_a\romannumeral`&&@% \xint_afterfi{\XINT_expandableerror {Expected an operator but got `#1'. Ignoring.}}% \fi }% \def\BNE_foundop #1{\csname BNE_precedence_#1\endcsname #1}% % \end{macrocode} % \subsection{Expansion spanning; opening and closing parentheses} % \label{ssec:span} % There was refactoring of expandable error messages at \xintname |1.4g| and I % can now use up to 55 characters, but should not really invite user to % |I|nsert something as it does not fit well with generic message saying % \xintname will go ahead "hoping repair was complete". % % At |1.6|, we removed the |\BNE_start|, current |\BNE_bareeval| has its % meaning rather than expanding to it as formerly. % % Also here macros are defined one by one so that it is easier to understand % what is happening. Formerly |\BNE_tmpa| defined all of them in one go % (as is still the case in upstream \xintexprname). % \begin{macrocode} \def\BNE_tmpa#1{% \def\BNE_check##1% {% \xint_UDsignfork ##1{\expandafter\BNE_checkp\romannumeral`&&@#1}% -{\BNE_checkp##1}% \krof }% }\expandafter\BNE_tmpa\csname BNE_op_-xii\endcsname \def\BNE_tmpa#1{% \def\BNE_checkp##1##2% {% \ifcase ##1% \expandafter\BNE_done \or\expandafter#1% \else \expandafter\BNE_checkp \romannumeral`&&@\csname BNE_op_##2\expandafter\endcsname \fi }% }\expandafter\BNE_tmpa\csname BNE_extra_)\endcsname \expandafter\def\csname BNE_extra_)\endcsname{% \XINT_expandableerror {An extra ) was removed. Hit , fingers crossed.}% \expandafter\BNE_check\romannumeral`&&@\expandafter\BNE_put_op_first \romannumeral`&&@\BNE_getop_legacy }% \let\BNE_done\space \def\BNE_getop_legacy #1% {% \expanded{\unexpanded{{#1}}\expandafter}\romannumeral`&&@\BNE_getop }% % \end{macrocode} % Code style left untouched at |1.6|. % \begin{macrocode} \catcode`) 11 \def\BNE_tmpa #1#2#3#4#5#6% {% \def #1##1% op_( {% \expandafter #4\romannumeral`&&@\BNE_getnext }% \def #2##1% op_) {% \expanded{\unexpanded{\BNE_put_op_first{##1}}\expandafter}% \romannumeral`&&@\BNE_getop }% \def #3% oparen {% \expandafter #4\romannumeral`&&@\BNE_getnext }% \def #4##1% check- {% \xint_UDsignfork ##1{\expandafter#5\romannumeral`&&@#6}% -{#5##1}% \krof }% \def #5##1##2% checkp {% \ifcase ##1\expandafter\BNE_missing_) \or \csname BNE_op_##2\expandafter\endcsname \else \expandafter #5\romannumeral`&&@\csname BNE_op_##2\expandafter\endcsname \fi }% }% \expandafter\BNE_tmpa \csname BNE_op_(\expandafter\endcsname \csname BNE_op_)\expandafter\endcsname \csname BNE_oparen\expandafter\endcsname \csname BNE_check-_)\expandafter\endcsname \csname BNE_checkp_)\expandafter\endcsname \csname BNE_op_-xii\endcsname \let\BNE_precedence_)\xint_c_i \def\BNE_missing_) {\XINT_expandableerror{Missing ). Hit 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 || 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. % \begin{macrocode} \def\BNE_tmpa #1#2#3#4#5% {% \def #1##1% \BNE_op_, {% \expanded{\unexpanded{#2{##1}}\expandafter}% \romannumeral`&&@\expandafter#3\romannumeral`&&@\BNE_getnext }% \def #2##1##2##3##4{##2##3{##1##4}}% \BNE_exec_, \def #3##1% \BNE_check-_, {% \xint_UDsignfork ##1{\expandafter#4\romannumeral`&&@#5}% -{#4##1}% \krof }% \def #4##1##2% \BNE_checkp_, {% \ifnum ##1>\xint_c_iii \expandafter#4% \romannumeral`&&@\csname BNE_op_##2\expandafter\endcsname \else \expandafter##1\expandafter##2% \fi }% }% \expandafter\BNE_tmpa \csname BNE_op_,\expandafter\endcsname \csname BNE_exec_,\expandafter\endcsname \csname BNE_check-_,\expandafter\endcsname \csname BNE_checkp_,\expandafter\endcsname \csname BNE_op_-xii\endcsname \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| 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 \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 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|'s are absent from upstream \xintexprname as it does not % incorporate yet some analog to |\bnumdefinfix/\bnumdefpostfix|. % \begin{macrocode} \def\BNE_defminus_b #1#2#3#4#5% {% \XINT_global\def #1% \BNE_op_- {% \expandafter #2\romannumeral`&&@\expandafter#3% \romannumeral`&&@\BNE_getnext }% \XINT_global\def #2##1##2##3% \BNE_exec_- {% \expandafter ##1\expandafter ##2\expandafter {\expandafter{\romannumeral`&&@\BNE_Op_opp##3}}% }% \XINT_global\def #3##1% \BNE_check-_- {% \xint_UDsignfork ##1{\expandafter #4\romannumeral`&&@#1}% -{#4##1}% \krof }% \XINT_global\def #4##1##2% \BNE_checkp_- {% \ifnum ##1>#5% \expandafter #4% \romannumeral`&&@\csname BNE_op_##2\expandafter\endcsname \else \expandafter ##1\expandafter ##2% \fi }% }% \def\BNE_defminus_a #1% {% \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_defminus_a {xii}% % \end{macrocode} % \subsection{The infix operators.} % I could have at the |1.4| refactoring injected usage of \cs{expanded} here, % but kept in sync with upstream \xintexprname code. Any \emph{x}-expandable % macro can easily be converted into an \emph{f}-expandable one using % \cs{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 % |\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_| 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 ||. % % 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% {% \XINT_global\def #1##1% \BNE_op_ {% \expanded{\unexpanded{#2{##1}}\expandafter}% \romannumeral`&&@\expandafter#3\romannumeral`&&@\BNE_getnext }% \XINT_global\def #2##1##2##3##4% \BNE_exec_ {% \expandafter##2\expandafter##3\expandafter {\expandafter{\romannumeral`&&@#7##1##4}}% }% \XINT_global\def #3##1% \BNE_check-_ {% \xint_UDsignfork ##1{\expandafter#4\romannumeral`&&@#5}% -{#4##1}% \krof }% \XINT_global\def #4##1##2% \BNE_checkp_ {% \ifnum ##1>#6% \expandafter#4% \romannumeral`&&@\csname BNE_op_##2\expandafter\endcsname \else \expandafter ##1\expandafter ##2% \fi }% }% \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_-\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 + {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} % \xintexprname uses shortcut % \centeredline{|\expandafter\def\csname XINT_expr_itself_**\endcsname {^}|} % But doing it would mean that any redefinition of |^| propagates to |**|. % And 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 {/:}% \let\BNE_prec_tacit\xint_c_xvi % \end{macrocode} % \subsection{Extending the syntax: \cs{bnumdefinfix}, \cs{bnumdefpostfix}} % \subsubsection{\cs{bnumdefinfix}} % |#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} % \subsubsection{\cs{bnumdefpostfix}} % Support macros for postfix operators only need to be \emph{x}-expandable. % \begin{macrocode} \def\bnumdefpostfix #1#2#3% {% \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{! as postfix factorial operator} % \begin{macrocode} \bnumdefpostfix{!}{\BNE_Op_fac}{20}% % \end{macrocode} % \subsection{Cleanup} % \begin{macrocode} \let\BNE_tmpa\relax \let\BNE_tmpb\relax \let\BNE_tmpc\relax \let\BNE_tmpR\relax \let\BNE_tmpL\relax \BNErestorecatcodesendinput% % \end{macrocode} % \MakePercentComment % %<*dtx> \CheckSum {1627}% \makeatletter\check@checksum\makeatother% \Finale% %% End of file xint.dtx