% apnum.tex -- Arbitrary Precision Numbers
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% The documentation, Petr Olsak, 2014, 2015, 2016, 2018
% You can create the pdf version of this documentation by the command
% pdfcsplain apnum.d
% run this command four times (for the consistence of all cross references)
% If you need to generate the documentation then the packages csplain and docbytex
% have to be installed in your TeX distribution.
\input utf8off \clearmubyte % use pdfcsplain
\def\projectversion{1.7 Apr 2018}
\def\headtitle{Arbitrary Precision Numbers}
\widowpenalty=10000
\clubpenalty=10000
\emergencystretch=2em
\hbadness=2200
\showboxbreadth=1500 \showboxdepth=2
\input docby.tex
\setlinecomment{\percent} \noactive{\nb\percent} \noactive{\percent\cbrace}
\noactive{\nb fontdimen}
\noactive{\nb apSIGNa} \noactive{\nb apSIGNb}
\noactive{\nb apEa} \noactive{\nb apEb}
\noactive{\nb documentclass}
\def\tittoc{Table Of Contents}
\def\titindex{Index}
\def\titversion{version }
\def\db{\dg\nb}
\def\du#1{\api{\nb#1}}%\def\quotehook{\langleactive\let\Blue=\Red \gdef\quotehook{\langleactive}}}
\def\quotehook{\langleactive\obeyspaces}
\bgroup
\catcode`\[=1 \catcode`]=2 \catcode`\{=12 \catcode`\}=12
\gdef\obrace[{] \gdef\cbrace[}]
\egroup
\def\indexhook{%
The bold number is the number of the page where the item is documented.
Other numbers are pagenumbers of the occurrences of such item.
The items marked by $\succ$ are mentioned in user documentation.
\medskip}
\def\nn#1 {\noactive{\nb#1}}
\def\inumref[#1]{\ilink[#1]{\numref[#1]}}
\def\M{M\sb{\rm op}}
\def\cnvbookmark#1{\lowercase{\lowercase{#1}}}
{\obeyspaces\global\let =\ }
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\centerline{\tt apnum.tex}
\title Arbitrary Precision Numbers
\author Petr Ol\v s\'ak
\centerline{\ulink[ftp://math.feld.cvut.cz/olsak/makra/]%
{ftp://math.feld.cvut.cz/olsak/makra/}}
\dotoc \bookmarks
\sec User's Documentation
This macro file "apnum.tex" implements addition, subtraction,
multiplication, division, power to an integer and other calculation
($\sqrt x$, $e^x$, $\ln x$, $\sin x$, $\arctan x$, \dots) with
``large numbers'' with arbitrary number of decimal digits. The numbers
are in the form:
\begtt
.
\endtt
%
where optional "" is the sequence of "+" and/or "-". The nonzero number is
treated as negative if and only if there is odd number of "-" signs.
The first part or second part of decimal "" (but no both) can be empty.
The decimal point is optional if second part of "" is empty.
There can be unlimited number of digits in the operands. Only \TeX{} main
memory or your patience during calculation with very large numbers are your
limits. Note, that the "apnum.tex" implementation includes a lot of
optimization and it is above 100 times faster (on large numbers) than the
implementation of the similar task in the package "fltpoint.sty". And the
"fp.sty" doesn't implements arbitrary number of digits. The extensive
technical documentation can serve as an inspiration how to do \TeX{} macro
programming.
\subsec [eval] Evaluation of Expressions
After "\input apnum" in your document you can use the macro
\du{evaldef}"\evaldef{}".
It gives the possibility for comfortable calculation. The "" can
include numbers (in the form described above) combined by "+", "-", "*", "/"
and "^" operators and by possible brackets "()" in an usual way. The result
is stored to the "" as a ``literal macro''. Examples:
\begtt
\evaldef\A {2+4*(3+7)}
% ... the macro \A includes 42
\evaldef\B {\the\pageno * \A}
% ... the macro \B includes 84
\evaldef\C {123456789000123456789 * -123456789123456789123456789}
% ... \C includes -15241578765447341344197531849955953099750190521
\evaldef\D {1.23456789 + 12345678.9 - \A}
% ... the macro \D includes 12345596.13456789
\evaldef\X {1/3}
% ... the macro \X includes .3333333333333333333
\endtt
%
The limit of the number of digits of the division result can be set by
\du{apTOT}"\apTOT" and \du{apFRAC}"\apFRAC" registers.
First one declares maximum calculated digits in total
and second one declares maximum of digits after decimal point. The
result is limited by both those registers. If the "\apTOT" is negative,
then its absolute value is treated as a ``soft limit'': all digits before
decimal point are calculated even if this limit is exceeded. The digits
after decimal point are not calculated when this limit is reached.
The special value "\apTOT=0" means that the calculation is limited
only by "\apFRAC". Default values are "\apTOT=0" and "\apFRAC=20".
The operator "^" means the powering, i.~e.\ "2^8" is "256". The exponent have
to be an integer (no decimal point is allowed) and a relatively small
integer is assumed.
The scanner of the "\evaldef" macro reads (roughly speaking) the ""
in the form ``operand binary-operator operand binary-operator etc.''
without expansion.
The spaces are not significant in the "". The operands are:
\begitems
\item * numbers (in the format ".") or
\item * numbers in scientific notation (see the section \inumref[sci]) or
\item * sequences "\the" or "\number" or
\item * any other single "" optionally preceded by "" and
optionally followed by a sequence of parameters enclosed in braces, for
example "\A" or "\B{}" or "-\C{}{}". This case has
two meanings:
\begitems
\item * numeric constant defined in a ``literal macro''
("\def\A{42}", "\evaldef\A{13/15}") or
\item * ``function-like'' macro which returns a value after processing.
\enditems
\enditems
The "apnum.tex" macro file provides the following ``function-like'' macros
allowed to use them as an operand in the "":
\begitems
\item * \du{ABS}"\ABS {}" for absolute value,
\item * \du{SGN}"\SGN {}" returns sign of the "",
\item * \du{iDIV}"\iDIV {}{}" for integer division,
\item * \du{iMOD}"\iMOD {}{}" for integer remainder,
\item * \du{iFLOOR}"\iFLOOR {}" for rounding the number to the integer,
\item * \du{iFRAC}"\iFRAC {}" for fraction part of the "\iFLOOR",
\item * \du{FAC}"\FAC {}" for factorial,
\item * \du{BINOM}"\BINOM {}{}" for binomial coefficient,
\item * \du{SQRT}"\SQRT {}" for square root of the "",
\item * \du{EXP}"\EXP {}" applies exponential function to "",
\item * \du{LN}"\LN {}" for natural logarithm of the "",
\item * \du{SIN}\du{COS}\du{TAN}"\SIN {}", "\COS {}", "\TAN {}"
for $\sin x$, $\cos x$ and $\tan x$ functions,
\item * \du{ASIN}\du{ACOS}\du{ATAN}"\ASIN {}", "\ACOS {}", "\ATAN {}"
for $\arcsin x$, $\arccos x$ and $\arctan x$ functions,
\item * \du{PI}\du{PIhalf}"\PI", "\PIhalf" for constants $\pi$ and $\pi/2$.
\enditems
The arguments of all these functions can be a
nested "" with the syntax like in the "\evaldef" macro.
Example:
\begtt
\def\A{20}
\evaldef\B{ 30*\SQRT{ 100 + 1.12*\the\widowpenalty } / (4-\A) }
\endtt
%
Note that the arguments of the ``function-like'' macros are enclosed by normal
\TeX{} braces "{}" but the round brackets "()" are used for re-arranging of the
common priority of the "+", "-", "*", "/" and "^" operators.
The macros "\SQRT", "\EXP", "\LN", "\SIN", "\COS", "\TAN", "\ASIN", "\ACOS",
"\ATAN" use "\apTOT" and "\apFRAC" registers similar like during division.
The "\PI" and "\PIhalf" are ``function-like'' macros without parameters.
They returns the constant with "\apFRAC" digits after decimal point.
Users can define their own ``function-like'' macros, see the section
\inumref[pgm].
\medskip
The output of "\evaldef\foo{}" processing is stored, of course,
to the ``literal macro'' "\foo". But there are another outputs like side
effect of the processing:
\begitems
\item * The \du{OUT}"\OUT" macro includes exactly the same result as "\foo".
\item * The \du{apSIGN}"\apSIGN" register includes the value "1" or "0"
or "-1" respectively dependent on the fact that the output is positive, zero
or negative.
\item * The \du{apE}"\apE" register is equal to the decimal exponent when
scientific number format is used (see the next section~\inumref[sci]).
\enditems
For example, you can
compare long numbers using "\apSIGN" register (where direct usage of "\ifnum"
primitive may cause arithmetic overflow):
\begtt
\TEST {123456789123456789} > {123456789123456788} \iftrue OK \else KO \fi
\endtt
The "\TEST" macro is defined like:
\begtt
\def\TEST#1#2#3#4{\evaldef\tmp{#1-(#3)}\ifnum\apSIGN #2 0 }
\endtt
The "apnum.tex" macros do not provide the evaluation of the ""
at the expansion level only. There are two reasons. First, the macros can be
used in classical \TeX{} only with Knuth's plain \TeX{} macro. No e\TeX{} is
needed. And the expansion-only evaluation of any expression isn't possible
in classical \TeX. Second reason is the speed optimization
(see the section~\inumref[tests]). Anyway, users
needn't expansion-only evaluation. They can write "\evaldef\a{}"
"\edef\foo{...\a...}" instead of "\edef\foo{......}". There is
only one case when this ``pre-processing'' trick cannot be used: while
expansion of the parameters of asynchronous "\write" commands. But you can
save the "" unexpanded into the file and you can read the file
again in the second step and do "\evaldef" during reading the file.
\subsec [sci] Scientific Notation of Numbers
The macro "\evaldef" is able
to operate with the numbers written in the notation:
\begtt
.E
\endtt
%
For example "1.234E9" means $1.234\cdot 10^9$, i.~e.\ "1234000000" or
the text "1.234E-3" means ".001234". The decimal exponent (after the "E"
letter) have to be in the range $\pm\,2\,147\,483\,647$ because
we store this value in normal \TeX{} register.
The "\evaldef{}" operates with mantissa and exponent
separately if there are operands with scientific notation. It outputs the
result in the scientific notation if the result have non-zero exponent.
The \du{evalmdef}"\evalmdef{}" does the same as "\evaldef"
but only mantissa is saved in the output "" and in the "\OUT" macro.
The exponent is stored in the "\apE" register in such case.
You can define the macro which shows the complete result after "\evalmdef"
calculation, for example:
\begtt
\def\showE#1{\message{#1\ifnum\apE=0 \else*10^\the\apE\fi}}
\endtt
Suppose "\evalmdef\foo{}" is processed and the complete result is
$R={}$"\foo*10^\apE". There are two possibilities how to save such complete
result $R$ to the "\foo" macro: use "\apEadd\foo" or "\apEnum\foo". Both
macros do nothing if "\apE=0". Else the \du{apEadd}"\apEadd" macro adds
"E" to the "" macro and \du{apEnum}"\apEnum" moves
the decimal point to the new right position in the "" macro or
appends zeros. The "\apE" register is set to zero after the macro "\apEadd" or
"\apEnum" is finished. Example:
\begtt
\evalmdef\foo{ 3 * 4E9 } % \foo is 12, \apE=9
\apEadd\foo % \foo is 12E+9
\evalmdef\foo{ 7E9 + 5E9 } % \foo is 12, \apE=9
\apEnum\foo % \foo is 12000000000
\endtt
There are another usable macros for operations with scientific numbers.
\begitems
\item * \du{apROLL}"\apROLL {}" \dots the "" is assumed to
be a macro with the number. The decimal point of this number is
shifted right by "" parameter, i.~e.\ the result is multiplied by
"10^". The "" is redefined by this result.
For example the "\apEnum\A" does "\apROLL\A{\apE}".
\item * \du{apNORM}"\apNORM {}"
\dots the "" is supposed to be a macro with "" and it
will be redefined. The number "*10^\apE" (with current value of
the "\apE" register) is assumed.
The new mantissa saved in the "" is the ``normalized mantissa'' of
the same number. The "\apE" register is corrected so the ``normalized
mantissa''"*10^\apE" gives the same number.
The "" parameter is the number of non-zero digits before the decimal
point in the outputted mantissa. If the parameter ""
starts by dot following by integer (for example "{.2}"), then the
outputted mantissa has "" digits after decimal point.
For example "\def\A{1.234}\apE=0" "\apNORM\A{.0}" defines "\A" as "1234"
and "\apE=-3".
\item * The \du{apROUND}"\apROUND {}"
rounds the number, which is included in
the macro "" and redefines "" as rounded number.
The digits after decimal point at the position greater than "" are ignored
in the rounded number. The decimal point is removed, if it is the right
most character in the "\OUT". The ignored part is saved to the "\XOUT" macro
without trailing right zeros.
\enditems
Examples of "\apROUND" usage:
\begtt
\def\A{12.3456}\apROUND\A{1} % \A is "12.3", \XOUT is "456"
\def\A{12.3456}\apROUND\A{9} % \A is "12.3456", \XOUT is empty
\def\A{12.3456}\apROUND\A{0} % \A is "12", \XOUT is "3456"
\def\A{12.0000}\apROUND\A{0} % \A is "12", \XOUT is empty
\def\A{12.0001}\apROUND\A{2} % \A is "12", \XOUT is "01"
\def\A{.000010}\apROUND\A{2} % \A is "0", \XOUT is "001"
\def\A{-12.3456}\apROUND\A{2} % \A is "-12.34", \XOUT is "56"
\def\A{12.3456}\apROUND\A{-1} % \A is "10", \XOUT is "23456"
\def\A{12.3456}\apROUND\A{-4} % \A is "0", \XOUT is "00123456"
\endtt
The following example saves the result of the "\evalmdef" in scientific
notation with the mantissa with maximal three digits after decimal point and one
digit before.
\begtt
\evalmdef\X{...}\apNORM\X{1}\apROUND\X{3}\apEadd\X
\endtt
The macros "\apEadd", "\apEnum", "\apROLL", "\apNORM" and "\apROUND" redefine the macro
"" given as their first argument. They are not ``function-like''
macros and they cannot be used in an "".
The macro "" must be the number in the format
"." where "" is one minus or none
and the rest of number has the format described in the first paragraph of
this documentation. The scientific notation isn't allowed here. This format
of numbers is in accordance with the output of the "\evalmdef" macro.
The build in function-like macros "\SGN", "\iDIV", \dots "\SIN", "\COS", "\ATAN" etc.\
don't generate the result in scientific form regardless of its argument is in
scientific form or not. But there are exceptions: "\ABS" and "\SQRT" returns
scientific form if the argument is in this form. And "\EXP" returns scientific
form if the result is greater than $10^{K+1}$ or less than $10^{-K-1}$ where
$K={}$\du{apEX}"\apEX". The default value of this register is "\apEX=10".
\subsec [pgm] Notes for macro programmers
If you plan to create a ``function-like'' macro which can be used as an
operand in the "" then observe that first token in the
macro body must be "\relax". This tells to the "" scanner that
the calculation follows. The result of this calculation must be saved into
the "\OUT" macro and into the "\apSIGN" register.
Example. The "\ABS" macro for the absolute value is defined by:
\ilabel [abs:eval] {evalmdef}
\ifirst{apnum.tex} {ABS}{^^B\cbrace}{+-}
\begtt
Usage: \evaldef\A{ 2 - \ABS{3-10} }% \A includes -5.
\endtt
Note, that "\apSIGN" register is corrected by final routine of the expression scanner
according the "\OUT" value. But setting "\apSIGN" in your macro is
recommended because user can use your macro directly outside of "\evaldef".
If the result of the function-like macro needs to be expressed by scientific
notation then you have two possibilities: use ``{\tt E}'' notation in the
"\OUT" macro and keep "\apE" register zero. Or save the matissa only to the
"\OUT" macro and set the value of the exponent into the "\apE" register. The
second possibility is preferred and used by build in function-like macros.
Note the "\ABS" definition above: the "\evalmdef" in the line~\cite[abs:eval]
keeps only mantissa in
the "\OUT" macro and the "\apE" register is set by "\evalmdef" itself.
The "\evaldef\foo{}" is processed in two steps. The
"" scanner converts the input to the macro call of the
\du{apPLUS}"\apPLUS", \du{apMINUS}"\apMINUS", \du{apMUL}"\apMUL",
\du{apDIV}"\apDIV" or \du{apPOW}"\apPOW" macros with two
parameters. They do addition, subtraction, multiplication, division and
power to the integer. These macros are processed in the second step.
For example:
\begtt
\evaldef\A{ 2 - 3*8 } converts the input to:
\apMINUS{2}{\apMUL{3}{8}} and this is processed in the second step.
\endtt
The macros "\apPLUS", "\apMINUS", "\apMUL", "\apDIV" and "\apPOW" behave
like normal ``function-like'' macros with one important exception: they
don't accept general "" in their parameters, only single operand
(see section~\inumref[eval]) is allowed.
If your calculation is processed in the loop very intensively
than it is better to save time of such calculation and to avoid the
"" scanner processing (first step of the "\evaldef").
So, it is recommended to use directly the
Polish notation of the expression as shown in the second line in the example
above. See section~\inumref[fce] for more inspirations.
The output of the "\apPLUS", "\apMINUS", "\apMUL", "\apDIV" and "\apPOW" macros is
stored in "\OUT" macro and the registers "\apSIGN" and "\apE" are set
accordingly.
The number of digits calculated by "\apDIV" macro is limited by the
"\apTOT" and "\apFRAC" registers as described in the section~\inumref[eval].
There is another result of "\apDIV" calculation stored in the \du{XOUT}"\XOUT" macro.
It is the remainder of the division. Example:
\begtt
\apTOT=0 \apFRAC=0 \apDIV{1234567892345}{2}\ifnum\XOUT=0 even \else odd\fi
\endtt
%
You cannot apply "\ifodd" primitive on ``large numbers'' directly
because the numbers may be too big.
If you set something locally inside your ``function-like'' macro, then such
data are accessible only when your macro is called outside of "\evaldef". Each
parameter and the whole "\evaldef" is processed inside a \TeX{} group, so
your locally set data are inaccessible when your macro is used inside another
``function-like'' parameter or inside "\evaldef". The "\XOUT" output is set
locally by "\apDIV" macro, so it serves as a good example of this feature:
\begtt
{\apDIV{1}{3} ... \XOUT is .00000000000000000001 }
... \XOUT is undefined
\evaldef{1/3} ... \XOUT is undefined
\apPLUS{1}{\apDIV{1}{3}} ... \XOUT is undefined
\endtt
The macro "\apPOW{}{}" calculates the power to the integer
exponent. A slight optimization is implemented here so the usage of "\apPOW"
is faster than repeated multiplication. The decimal non-integer exponents are not
allowed. Use "\EXP" and "\LN" macros if you need to calculate non-integer
exponent:
\begtt
\def\POWER#1#2{\relax \EXP{(#2)*\LN{#1}}}
\endtt
%
Note that both parameters are excepted as an "". Thus the "#2" is
surrounded in the rounded brackets.
Examples of another common ``function-like'' macros:
\begtt
\evaldef\degcoef{PI/180}
\def\SINdeg#1{\relax \SIN{\degcoef*(#1)}}
\def\COSdeg#1{\relax \COS{\degcoef*(#1)}}
\def\SINH#1{\relax \evaldef\myE{\EXP{#1}}\evaldef\OUT{(\myE-1/\myE)/2}}
\def\ASINH#1{\relax \LN{#1+\SQRT{(#1)^2+1}}}
\def\LOG#1{\relax \apLNtenexec \apDIV{\LN{#1}}{\apLNten}}
\endtt
In another example, we implement the field "\F{}" as an
``function-like'' macro. User can set values by "\set\F{}={}"
and then these values can be used in an "".
\begtt
\def\set#1#2#3#4{\evaldef\index{#2}\evaldef\value{#4}%
\expandafter\edef\csname \string#1[\index]\endcsname{\value}}
\def\F#1{\relax % function-like macro
\evaldef\index{#1}%
\expandafter\ifx\csname \string\F[\index]\endcsname\relax
\def\OUT{0}% undefined value
\else
\edef\OUT{\csname \string\F[\index]\endcsname}%
\fi
}
\set \F{12/2} = {28+13}
\set \F{2*4} = {144^2}
\evaldef\test { 1 + \F{6} } \message{result=\test}
\endtt
%
As an exercise, you can implement linear interpolation of known values.
The final example shows, how to implement
the macro "\usedimen{}{}". It is ``function-like'' macro,
it can be used in the "" and it returns the
"" with the property "=".
\begtt
\def\usedimen #1#2{\relax % function-like macro
\def\OUT{0}% % default value, if the unit isn't known
\csname dimenX#2\endcsname{#1}}
\def\dimenXpt #1{\apDIV{\number#1}{65536}}
\def\dimenXcm #1{\apDIV{\number#1}{1864682.7}}
\def\dimenXmm #1{\apDIV{\number#1}{186468.27}}
%... etc.
\evaldef\a{\usedimen{\hsize}{cm}} % \a includes 15.91997501773358008845
\endtt
%
Note that user cannot write "\usedimen\hsize{cm}" without braces because
this isn't the syntactically correct operand (see section~\inumref[eval]) and the
"" scanner is unable to read it.
\subsec Printing expressions
\TeX{} was designed for printing. The "apnum.tex" provides common syntax of
"" (given in section \inumref[eval]) which can be used for
both: for {\it evaluating\/} or for {\it printing}. Printing can be done
using \du{eprint}"\eprint{}{}" macro. The
"" part declares locally what to do with ``variables'' or with
your ``function-like'' macros. You can insert your local "\def"'s or
"\let"'s here because the "" is executed in the group before
the "" is printed. The "\eprint" macro must be used in math mode
only. Example:
\begtt
\def\printresult#1{$$\displaylines{
\eprint{#1}\vars = \cr = \eprint{#1}\nums = \cr
= \apFRAC=8 \evaldef\OUT{#1}\OUT, \cr
\nums x = \X, \quad y = \Y.
}$$}
\def\X{-.25} \def\Y{18.11}
\def\vars{\def\X{x}\def\Y{y}\let\apMULop=\relax}
\def\nums{\corrnum\X \corrnum\Y}
\printresult
{-(\X-\SQRT{\Y^2+1}) + -((\Y*\X+1)/2) + \SIN{\X+\PIhalf} + 2*\COS{\Y}}
\endtt
generates the result:
$$
\displaylines{
- \left(x-\sqrt{y^2+1}\right) + \left(- {yx+1\over 2}\right)
+ \sin\!\left( x+{\pi\over2}\right) + 2 \cos y = \cr
= - \left(-0.25-\sqrt{18.11^2+1}\right) +
\left(- {18.11\cdot(-0.25)+1\over 2}\right)
+ \sin\!\left( -0.25+{\pi\over2}\right) + 2\cdot \cos 18.11 = \cr
= 22.5977863, \cr
x = -0.25, \quad y = 18.11
}
$$
This example prints the given "" in two forms: with
``variables as variables'' first and with ``variables as constants'' second.
The "" is prepared in the "\vars" macro for the first
form and in the "\nums" macro for the second.
Note that "\eprint" macro re-calculates the occurrences of round brackets but
keeps the meaning of the "".
For example "(a+b)/c" is printed as "{a+b\over c}" (without brackets) and
"6*-(a+b)" is printed as "6\cdot(-(a+b))" (new brackets pair is added).
Or "\SIN{x}" is printed as "\sin x" (without brackets) but "\SIN{x+1}"
is printed as "\sin(x+1)" (with brackets). And "\SIN{x}^2" is printed as
"\sin^2 x".
You can do \du{apMULop}"\let\apMULop=\," or "\let\apMULop=\relax" in the
"" part if you need not to print any operator for multiplying.
The default setting is "\let\apMULop=\cdot". Another possibility is to set
"\let\apMULop=\times".
The macro \du{corrnum}"\corrnum" corrects the number saved in
the "" macro if it is in the form
"[]." (i.~e.\ without digits before decimal point).
Then zero is added before decimal point. Else nothing is changed.
Warning. The first parameter of "\eprint" (i.~e.\ the ""), must
be directly expression without any expansion steps. For example, you cannot
define "\def\foo{}" and do "\eprint{\foo}{}" but you can do
"\expandafter\eprint\expandafter{\foo}{}".
The macro "\eprint" has its own intelligence about putting brackets. If you
need to put or remove brackets somewhere where the intelligence of "\eprint"
is different from your opinion, you can create your function-like macros
"\BK{}" and "\noBK{}". They evaluate the
"" when using "\evaldef".
The "\BK" prints the "" with brackets and
"\noBK" prints it without brackets when using "\eprint".
\begtt
\def\BK#1{\relax \evaldef\OUT{#1}} \let\noBK=\BK
\def\apEPj{\def\BK##1{\left(\eprint{##1}{}\right)}%
\def\noBK##1{\eprint{##1}{}}}
Now $\eprint{3+\BK{\SIN{1}}^2}{}$ prints $3+(\sin 1)^2$.
\endtt
Note that "\apEPj" macro is an initial hook of "\eprint"
(it is run inside group before processing of the second parameter of "\eprint").
\subsec [tests] Experiments
The following table shows the time needed for calculation of randomly
selected examples. The comparison with the package "fltpoint.sty" is shown.
The symbol $\infty$ means that it is out of my patience.
\bigskip
\noindent\hfil\vbox{\baselineskip=13pt
\halign{&\ \hfil#\hfil\ \cr
input & \# of digits in the result & time spent by {\tt apnum.tex} &
time spent by {\tt fltpoint.sty}\cr
\noalign{\smallskip\hrule\smallskip}
200! & 375 & 0.33 sec & 173 sec \cr
1000! & 2568 & 29 sec & $\infty$ \cr
$5^{17^2}$ & 203 & 0.1 sec & 81 sec \cr
$5^{17^3}$ & 3435 & 2.1 sec & $\infty$ \cr
$1/17$ & 1000 & 0.13 sec & 113 sec \cr
$1/17$ & 100000 & 142 sec & $\infty$ \cr
}}
\sec The Implementation
\subsec Name Convention, Version, Counters
The internal control sequence names typically used in "apnum.tex" have the form
"\apNAMEsuffix", but there are exceptions. The control sequences mentioned
in the section~\inumref[eval] (user's documentation) have typically more natural names.
And the internal counter registers have names "\apnumA", "\apnumB",
"\apnumC" etc.
The code starts by the greeting. The \db apVERSION includes the version of this software.
\ifirst{apnum.tex} {apVERSION}{\empty}{+-}
We declare auxiliary counters and one Boolean variable.
\inext{newcount}{\empty}{+-}
The counters \db apSIGN , \db apE , \db apTOT, \db apFRAC and \db apEX
are declared here:
\inext{newcount}{\empty}{+-}
Somebody sometimes sets the "@" character to the special catcode. But we
need to be sure that there is normal catcode of the "@" character.
\inext{catcode}{}{++}
\subsec Evaluation of the Expression
Suppose the following expression "\A+\B*(\C+\D)+\E" as an example.
The main task of the "\evaldef\x{\A+\B*(\C+\D)+\E}" is to prepare the macro
"\tmpb" with the content (in this example)
"\apPLUS{\apPLUS{\A}{\apMUL{\B}{\apPLUS{\C}{\D}}}}{\E}" and to execute the "\tmpb"
macro.
The expression scanner adds the "\limits" at the end of the expression and
reads from left to right the couples ``operand, operator''. For our example:
"\A+", "\B*", "\C+", "\D+" and "\E\limits". The "\limits" operator has the
priority 0, plus, minus have priority 1, "*"~and~"/" have priority 2 and "^"
has priority 3. The brackets are ignored, but each occurrence of the opening
bracket "(" increases priority by 4 and each occurrence of closing bracket
")" decreases priority by~4. The scanner puts each couple including its
current priority to the stack and does a test at the top of the stack. The
top of the stack is executed if the priority of the top operator is less or
equal the previous operator priority. For our example the stack is only
pushed without execution until "\D+" occurs. Our example in the stack looks
like:
\begtt
\D + 1 1<=5 exec:
\C + 5 {\C+\D} + 1 1<=2 exec:
\B * 2 \B * 2 {\B*{\C+\D}} + 1 1<=1 exec:
\A + 1 \A + 1 \A + 1 {\A+{\B*{\C+\D}}} + 1
bottom 0 bottom 0 bottom 0 bottom 0
\endtt
%
Now, the priority on the top is greater, then scanner pushes next couple and
does the test on the top of the stack again.
\begtt
\E \limits 0 0<=1 exec:
{\A+{\B*{\C+\D}}} + 1 {{\A+{\B*{\C+\D}}}+\E} \end 0 0<=0 exec:
bottom 0 bottom 0 RESULT
\endtt
Let $p_t$, $p_p$ are the priority on the top and the previous
priority in the stack. Let $v_t$, $v_p$ are operands on the top and in the
previous line in the stack, and the same notation is used for operators
$o_t$ and $o_p$. If $p_t\le p_p$ then: pop the stack twice, create composed
operand $v_n=v_p \, o_p \, v_t$ and push $v_n$, $o_t$, $p_t$. Else
push new couple ``operand, operator'' from the expression scanner.
In both cases try to execute the top of the stack again.
If the bottom of the stack is reached then the last operand is the result.
The \db evaldef and \db evalmdef macros are protected by
"\relax". It means that it can be used
inside an "" as a ``function-like'' macro, but I don't imagine
any usual application of this. The "\apEVALa" is executed.
\inext{evaldef}{evalmdef}{++}
The macro \db apEVALa "{}{}" runs the evaluation of the
expression in the group. The base priority is initialized by "\apnumA=0",
then "\apEVALb\limits" scans the expression and saves the
result in the form "\apPLUS{\A}{\apMUL{\B}{\C}}" (etc.) into the "\tmpb" macro. This
macro is executed. The group is finished by "\apEND" macro, which keeps the
"\OUT", "\apSIGN" and "\apE" values unchanged. Then "" is executed and
finally, the defined "" is set equivalent to the \db OUT macro.
\inext{apEVALa}{}{++}
The scanner is in one of the two states: reading operand or reading operator.
The first state is initialized by \db apEVALb which follows to the
"\apEVALc". The \db apEVALc reads one token and switches by its value.
If the value is a "+" or "-" sign, it is assumed to be the part of the
operand prefix. Plus sign is ignored (and "\apEVALc" is run again),
minus signs are accumulated into "\tmpa".
The auxiliary macro \db apEVALd runs the following tokens to the "\fi", but
first it closes the conditional and skips the rest of the macro "\apEVALc".
\ilabel [eval:the] {ifx\nb the}
\ilabel [eval:number] {ifx\nb number}
\ilabel [eval:num] {apTESTdigit}
\ilabel [eval:nonum] {apEVALg}
\inext{apEVALb}{def\nb apEVALd}{++}
If the next token is opening bracket, then the global priority is increased
by 4 using the macro \db apEVALe. Moreover, if the sign before bracket
generates the negative result, then the new multiplication (by $-1$)
is added using "\apEVALp" to the operand stack.
\inext{apEVALe}{^^B\cbrace}{++}
If the next token is "\the" or "\number" primitives (see lines \cite[eval:the] and
\cite[eval:number]), then one following token is
assumed as \TeX{} register and these two tokens are interpreted as an operand.
This is done by \db apEVALf. The operand is packed to the "\tmpb" macro.
\inext{apEVALf}{}{++}
If the next token is not a number (the "\apTESTdigit#1\iftrue" results like
"\iffalse" at line~\cite[eval:num]) then we save
the sign plus this token to the "\tmpb"
at line~\cite[eval:nonum]
and we do check of the following token by "\futurelet". The \db
apEVALg is processed after that. The test is performed here if the following
token is open brace (a macro with parameter). If this is true then this
parameter is appended to "\tmpb" by \db apEVALh and the test about the
existence of second parameter in braces is repeated by next "\futurelet".
The result of this loop is stored into "\tmpb" macro which includes
"" followed by "" followed by all
parameters in braces. This is considered as an operand.
\inext{apEVALg}{def\nb apEVALh}{++}
If the next token after the sign is a digit or a dot (tested in "\apEVALc"
by "\apTESTdigit" at line~\cite[eval:num]), then there are two cases. The
number includes the "E" symbol as a first symbol (this is allowed in
scientific notation, mantissa is assumed to equal to one). The "\apEVALk"
is executed in such case. Else the "\apEVALn" starts the reading the
number.
The first case with "E" letter in the number is solved by macros \db apEVALk
and \db apEVALm. The number after "E" is read by "\apE=" and this number is
appended to the "\tmpb" and the expression scanner skips to "\apEVALo".
\inext{apEVALk}{def\nb apEVALm}{++}
The second case (there is normal number) is processed by the macro \db
apEVALn. This macro reads digits (token per token) and saves them to the
"\tmpb". If the next token isn't digit nor dot then the second state of the
scanner (reading an operator) is invoked by running "\apEVALo".
If the "E" is found then the exponent is read to "\apE" and it is processed by
"\apEVALm".
\inext{apEVALn}{^^B\cbrace}{++}
The reading an operator is done by the \db apEVALo macro. This is more
simple because the operator is only one token. Depending on this token the
macro \db apEVALp "" pushes to the stack (by
the macro "\apEVALpush") the value from "\tmpb", the " and the priority
increased by "\apnumA" (level of brackets).
If there is a problem (level of brackets less than zero, level of brackets not
equal to zero at the end of the expression, unknown operator) we print an
error using "\apEVALerror" macro.
The "\apNext" is set to "\apEVALb", i.~e.\ scanner returns back to the state of
reading the operand. But exceptions exist: if the ")" is found then
priority is decreased and the macro "\apEVALo" is executed again.
If the end of the "" is found then the loop is ended by
"\let\apNext=\relax".
\inext{apEVALo}{\count=2 ^^B\cbrace}{++}
The \db apEVALstack macro includes the stack, three items
"{}{}{}" per level. Left part of the macro
contents is the top of the stack. The stack is initialized with empty
operand and operator and with priority zero. The dot here is only the ``total
bottom'' of the stack.
\inext{apEVALstack}{}{++}
The macro \db apEVALpush "{}{}{}" pushes its
parameters to the stack and runs "\apEVALdo@" to do the
desired work on the top of the stack.
\inext{apEVALpush}{^^B\cbrace}{++}
Finally, the macro
\db apEVALdo "{}{}{}{}{}{}@"
performs the execution described at the beginning of this section. The new
operand "" is created as "{vp}{vt}", this means
"\apPLUS{}{}" for example. The operand is not executed now, only the
result is composed by the normal \TeX{} notation. If the bottom of the stack
is reached then the result is saved to the "\tmpb" macro. This macro is
executed after group by the "\apEVALa" macro.
\inext{apEVALdo}{^^B\cbrace}{++}
The macro \db apEVALerror "" prints an error message. We decide to
be better to print only "\message", no "\errmessage". The "\tmpb" is
prepared to create "\OUT" as "??" and the "\apNext" macro is set in order to skip
the rest of the scanned "".
\inext{apEVALerror}{^^B\cbrace}{++}
The auxiliary macro \db apTESTdigit "\iftrue" tests, if the given token is
digit, dot or "E" letter.
\inext{apTESTdigit}{^^B\cbrace}{++}
\subsec Preparation of the Parameter
All operands of "\apPLUS", "\apMINUS", "\apMUL", "\apDIV" and "\apPOW" macros are
preprocessed by "\apPPa" macro. This macro solves (roughly speaking) the
following tasks:
\begitems
\item * It partially expands (by "\expandafter") the parameter while "" is read.
\item * The "" is removed from parameter and the appropriate "\apSIGN"
value is set.
\item * If the next token after "" is "\relax" then the rest of the
parameter is executed in the group and the results "\OUT", "\apSIGN" and
"\apE" are used.
\item * Else the number is read and saved to the parameter.
\item * If the read number has the scientific notation "E"
then only "" is saved to the parameter and "\apE" is set as
"". Else "\apE" is zero.
\enditems
The macro \db apPPa "" calls
\db apPPb "@" and starts reading the
"". The result will be stored to the "".
Each token from "" is processed by three "\expandafter"s (because
there could be "\csname...\endcsname"). It means that the
parameter is partially expanded when "" is read. The "\apPPb" macro
sets the initial value of "\tmpc" and "\apSIGN" and executes the macro
\db apPPc "@".
\inext{apPPa}{def\nb apPPd}{++}
The "\apPPc" reads one token from "" and it is called recursively
while there are "+" or "-" signs. If the read token is "+" or "-" then
the \db apPPd closes conditionals and executes "\apPPc" again.
If "\relax" is found then the rest of parameter is executed by the
\db apPPe. The macro ends by \db apPPf "@" and this macro
reverses the sign if the result is negative and removes the minus sign
from the front of the parameter.
\inext{apPPe}{def\nb apPPf}{++}
The \db apPPg "@" macro is called when the "" was processed
and removed from the input stream. The main reason of this macro is to
remove trailing zeros from the left and to check, if there is the zero value
written for example in the form "0000.000". When this macro is started then
"\tmpc" is empty. This is a flag for removing trailing zeros. They are simply
ignored before decimal point. The "\apPPg" is called again by \db apPPh
macro which removes the rest of "\apPPg" macro and closes the conditional.
If the decimal point is found then next zeros are accumulated to the "\tmpc".
If the end of the parameter "@" is found and we are in the ``removing zeros
state'' then the whole value is assumed to be zero and this is processed by
"\apPPi @". If another digit is found (say 2) then there are two
situations: if the "\tmpc" is non-empty, then the digit is appended to the
"\tmpc" and the "\apPPi" is processed (say "\apPPi .002")
followed by the rest of the parameter. Else the digit itself is stored to
the "\tmpc" and it is returned back to the input stream (say "\apPPi"~"2")
followed by the rest of the parameter.
\inext{apPPg}{def\nb apPPh}{++}
The macro \db apPPi "@"
switches to two cases: if the execution of the parameter was processed then
the "\OUT" doesn't include "E" notation and we can simply define
"" as the "" by the \db apPPj macro. This saves the
copying of the (possible) long result to the input stream again.
If the executing of the parameter was not performed, then we need to test
the existence of the "E" notation of the number by the \db apPPk macro. We
need to put the "" to the input stream and to use \db apPPl to
test these cases. We need to remove unwanted "E" letter by the \db apPPm macro.
\inext{apPPi}{def\nb apPPm}{++}
The \db apPPn "" macro does the same as "\apPPa\OUT{}",
but the minus sign is returned back to the "\OUT" macro if the result is
negative.
\inext{apPPn}{}{++}
The \db apPPab "{}{}" is used for parameters of
all macros "\apPLUS", "\apMUL" etc. It
prepares the "" to "\tmpa", "" to "\tmpb", the sign and
"" of "" to the "\apSIGNa" and "\apEa", the same
of "" to the "\apSIGNa" and "\apEa". Finally, it executes the
"".
\inext{apPPab}{^^B\cbrace}{++}
The \db apPPs "{}" prepares parameters for "\apROLL",
"\apROUND" and "\apNORM" macros. It saves the "" to the "\tmpc" macro,
expands the "" and runs the macro
\db apPPt \unskip~".@". The macro "\apPPt"
reads first token from the "" to "#2". If "#2" is minus
sign, then "\apnumG=-1". Else
"\apnumG=1". Finally the ".@"
is executed (but without the minus sign in the input stream).
If "#2" is zero then \db apPPu ".@" is executed. If
the "" is empty, (i.~e.\ the parameter is simply zero) then ""
isn't executed because there in nothing to do with zero number as a parameter of
"\apROLL", "\apROUND" or "\apNORM" macros.
\inext{apPPs}{\count=2 ^^B\cbrace}{++}
\subsec Addition and Subtraction
The significant part of the optimization in "\apPLUS", "\apMUL", "\apDIV" and
"\apPOW" macros
is the fact, that we don't treat with single decimal digits but with their
quartets. This means that we are using the numeral system with the base
10000 and we calculate four decimal digits in one elementary operation. The
base was chosen $10^4$ because the multiplication of such numbers gives
results less than $10^8$ and the maximal number in the \TeX{} register
is about $2\cdot10^9$. We'll use the word ``Digit'' (with capitalized D) in
this documentation if this means the digit in the numeral system with base
10000, i.~e.\ one Digit is four digits.
Note that for addition we can use the numeral system with the base $10^8$
but we don't do it, because the auxiliary macros "\apIV*" for numeral system of the
base $10^4$ are already prepared.
Suppose the following example (the spaces between Digits are here only for
more clarity).
\begtt
123 4567 8901 9999 \apnumA=12 \apnumE=3 \apnumD=16
+ 22.423 \apnumB=0 \apnumF=2 \apnumC=12
--------------------------
sum in reversed order and without transmissions:
{4230}{10021}{8901}{4567}{123} \apnumD=-4
sum in normal order including transmissions:
123 4567 8902 0021.423
\endtt
In the first pass, we put the number with more or equal Digits before decimal
point above the second number. There are three Digits more in the example.
The "\apnumC" register saves this information (multiplied by 4). The first
pass creates the sum in reversed order without transmissions between Digits.
It simply copies the "\apnumC/4" Digits from the first number to the result in reversed
order. Then it does the sums of Digits without transmissions. The "\apnumD"
is a relative position of the decimal point to the edge of the calculated
number.
The second pass reads the result of the first pass, calculates transmissions and
saves the result in normal order.
The first Digit of the operands cannot include four digits. The number of
digits in the first Digit is saved in "\apnumE" (for first operand) and in
"\apnumF" (for second one). The rule is to have the decimal point between
Digits in all circumstances.
The \db apPLUS and \db apMINUS macros prepare parameters using "\apPPab" and execute "\apPLUSa":
\inext{apPLUS}{apMINUS}{++}
The macro \db apPLUSa does the following work:
\ilabel[plus:apE] {apEa}
\ilabel[plus:DIGa] {apDIG\nb tmpa}
\ilabel[plus:DIGb] {apDIG\nb tmpb}
\ilabel[plus:moda] {-\nb apnumE}
\ilabel[plus:modb] {-\nb apnumF}
\ilabel[plus:apnC] {apnumC=}
\ilabel[plus:xA] {apSIGNa}
\ilabel[plus:xB] {apSIGNb}
\ilabel[plus:sg] {apSIGN=}
\ilabel[plus:xAm] {PLUSxA-}
\ilabel[plus:ba] {apPLUSg}
\ilabel[plus:bb] {apnumC=-}
\ilabel[plus:G] {apnumG=0}
\ilabel[plus:next] {apNext=}
\ilabel[plus:X] {apnumX=0}
\ilabel[plus:fa] {00123}
\ilabel[plus:fb] {apPLUSy}
\begitems
\item * It gets the operands in "\tmpa" and "\tmpb" macros using the "\apPPab".
\item * If the scientific notation is used and the decimal
exponents "\apEa" and "\apEb" are not equal then the decimal point of one
operand have to be shifted (by the macro "\apPLUSxE" at line~\cite[plus:apE]).
\item * The digits before decimal point are calculated for both operands by
the "\apDIG" macro. The first result is saved to "\apnumA" and the second
result is saved to "\apnumB". The "\apDIG" macro removes decimal point (if
exists) from the parameters (lines~\cite[plus:DIGa] and~\cite[plus:DIGb]).
\item * The number of digits in the first Digit is calculated by "\apIVmod"
for both operands. This number is saved to "\apnumE" and "\apnumF". This
number is subtracted from "\apnumA" and "\apnumB", so these
registers now includes multiply of four
(lines~\cite[plus:moda] and~\cite[plus:modb]).
\item * The "\apnumC" includes the difference of Digits before the decimal
point (multiplied by four) of given operands
(line~\cite[plus:apnC]).
\item * If the first operand is negative then the minus sign is inserted to
the \db apPLUSxA macro else this macro is empty. The same for the second
operand and for the macro \db apPLUSxB is done
(lines~\cite[plus:xA] and~\cite[plus:xB]).
\item * If both operands are positive, then the sign of the result "\apSIGN"
is set to one. If both operands are negative, then the sign is set to $-1$.
But in both cases mentioned above we will do (internally) addition, so the
macros "\apPLUSxA" and "\apPLUSxB" are set to empty.
If one operand is negative and second positive then we will do
subtraction. The "\apSIGN" register is set to zero and
it will set to the right value later
(lines~\cite[plus:sg] to~\cite[plus:xAm]).
\item * The macro "\apPLUSb"
does the calculation of the first pass. The "" has to have more
or equal Digits before decimal point than "". This is reason why
this macro is called in two variants dependent on the value "\apnumC".
The macros "\apPLUSxA" and "\apPLUSxB" (with the sign of the operands) are
exchanged (by the "\apPLUSg") if the operands are exchanged
(lines~\cite[plus:ba] to~\cite[plus:bb]).
\item * The "\apnumG" is set by the macro "\apPLUSb" to the sign of the
first nonzero Digit. It is equal to zero if there are only zero Digits after
first pass. The result is zero in such case and we do nothing more
(line~\cite[plus:G]).
\item * The transmission calculation is different for addition and
subtraction. If the subtraction is processed then the sign of the result
is set (using the value "\apnumG") and the "\apPLUSm" for transmissions is
prepared. Else the "\apPLUSp" for transmissions is prepared as the "\apNext" macro
(line~\cite[plus:next])
\item * The result of the first pass is expanded in the input stream and the
"\apNext" (i.~e.\ transmissions calculation) is activated at line~\cite[plus:X].
\item * if the result is in the form ".000123", then the decimal point and
the trailing zeros have to be inserted. Else the trailing zeros from the
left side of the result have to be removed by "\apPLUSy". This macro adds
the sign of the result too
(lines~\cite[plus:fa] to~\cite[plus:fb])
\enditems
\inext{apPLUSa}{^^B\cbrace}{++}
The macro \db apPLUSb ""
starts the first pass. The "" is the first operand (which have
more or equal Digits before decimal point). The "" is the number
of digits in the first Digit in the first operand. The "" is the
second operand and the "" is the number of digits in the first
Digit of the second operand. The "" is the number of Digits
before decimal point of the first operand, but without the first Digit and
multiplied by~4.
The macro"\apPLUSb" saves the second operand to "\tmpd" and appends the
$4-{}$"" empty parameters before this operand in order to
read desired number of digits to the first Digit of this oparand.
The macro "\apPLUSb" saves the first operand to the input queue after
"\apPLUSc" macro. It inserts the appropriate number of empty parameters (in
"\tmpc") before this operand in order to read the right number of digits in
the first attempt. It appends the "\apNL" marks to the end in order to
recognize the end of the input stream. These macros expands simply to zero
but we can test the end of input stream by "\ifx".
The macro "\apPLUSb" calculates the number of digits before decimal point
(rounded up to multiply by 4) in "\apnumD" by advancing "" by~4.
It initializes "\apnumZ" to zero. If the first nonzero Digit will be found
then "\apnumZ" will be set to this Digit in the "\apPLUSc" macro.
\inext{apPLUSb}{^^B\cbrace}{++}
The macro \db apPLUSc is called repeatedly. It reads one Digit from input
stream and saves it to the "\apnumY". Then it calls the \db apPLUSe, which
reads (if it is allowed, i.~e.\ if "\apnumC"{\tt\char`<}"=0") one digit from
second operand "\tmpd" by the "\apIVread" macro.
Then it does the addition of these digits and saves the result
into the "\OUT" macro in reverse order.
Note, that the sign "\apPLUSxA" is used when "\apnumY" is read and the sign
"\apPLUSxB" is used when advancing is performed. This means that we are
doing addition or subtraction here.
If the first nonzero Digit is reached, then the macro \db apPLUSh sets the
sign of the result to the "\apnumG" and (maybe) exchanges the "\apPLUSxA"
and "\apPLUSxB" macros (by the \db apPLUSg macro)
in order to the internal result of the subtraction will be always non-negative.
If the end of input stream is reached, then "\apNext" (used at line~\cite[plus:nn])
is reset from its original value "\apPLUSc" to the \db apPLUSd where the
"\apnumY" is simply set to zero. The reading from input stream is finished.
This occurs when there are more Digits after decimal point in the second
operand than in the first one. If the end of input stream is reached and the
"\tmpd" macro is empty (all data from second operand was read too) then the
\db apPLUSf macro removes the rest of input stream and the first pass of the
calculation is done.
\ilabel[plus:nn] {apNext^^E}
\inext{apPLUSc}{def\nb apPLUSh}{++}
Why there is a complication about reading one parameter from input stream
but second one from the macro "\tmpd"? This is more faster than to save both
parameters to the macros and using "\apIVread" for both because the
"\apIVread" must redefine its parameter. You can examine that this
parameter is very long.
The \db apPLUSm "@" macro does transmissions calculation when
subtracting. The "" from first pass is expanded in the input stream.
The "\apPLUSm" macro reads repeatedly one Digit from the "" until the
stop mark is reached. The Digits are in the range $-9999$ to $9999$. If the
Digit is negative then we need to add $10000$ and set the transmission value
"\apnumX" to one, else "\apnumX" is zero. When the next Digit is processed then
the calculated transmission value is subtracted. The macro "\apPLUSw" writes
the result for each Digit "\apnumA" in the normal (human readable) order.
\inext{apPLUSm}{^^B\cbrace}{++}
The \db apPLUSp "@" macro does transmissions calculation when
addition is processed. It is very similar to"\apPLUSm", but Digits are in
the range $0$ to $19998$. If the Digit value is greater then $9999$ then we
need to subtract $10000$ and set the transmission value "\apnumX" to one,
else "\apnumX" is zero.
\inext{apPLUSp}{^^B\cbrace}{++}
The \db apPLUSw writes the result with one Digit (saved in "\apnumA") to the
"\OUT" macro. The "\OUT" is initialized as empty. If it is empty (it means
we are after decimal point), then we need to write all four digits by
"\apIVwrite" macro (including left zeros) but we need to remove right zeros
by "\apREMzerosR". If the decimal point is reached, then it is saved to the
"\OUT". But if the previous "\OUT" is empty (it means there are no digits
after decimal point or all such digits are zero) then "\def\OUT{\empty}"
ensures that the "\OUT" is non-empty and the ignoring of right zeros are
disabled from now.
\inext{apPLUSw}{^^B\cbrace}{++}
The macro \db apPLUSy "@" removes left trailing zeros from the
"\OUT" macro and saves the possible minus sign by the \db apPLUSz macro.
\inext{apPLUSy}{def\nb apPLUSz}{++}
The macro \db apPLUSxE uses the "\apROLLa" in order to shift the decimal
point of the operand. We need to set the same decimal exponent in scientific
notation before the addition or subtraction is processed.
\inext{apPLUSxE}{^^B\cbrace}{++}
\subsec Multiplication
Suppose the following multiplication example: "1234*567=699678".
\def\begtthook{\lccode`~=`\ \lowercase{\def~{\ }}}
\begtt
Normal format: | Mirrored format:
1 2 3 4 * | 4 3 2 1 *
5 6 7 | 7 6 5
---------------- | -----------------
*7: 7 14 21 28 | *7: 28 21 14 7
*6: 6 12 18 24 | *6: 24 18 12 6
*5: 5 10 15 20 | *5: 20 15 10 5
---------------- | -----------------
6 9 9 6 7 8 | 8 7 6 9 9 6
\endtt
This example is in numeral system of base 10 only for simplification, the
macros work really with base 10000.
Because we have to do the transmissions between Digit positions
from right to left in the normal format and because it is more natural for
\TeX{} to put the data into the input stream and
read it sequentially from left to right, we use the mirrored format in our
macros.
The macro \db apMUL prepares parameters using "\apPPab" and executes "\apMULa"
\inext{apMUL}{}{++}
The macro \db apMULa does the following:
\ilabel[mul:apE] {apE}
\ilabel[mul:sgn] {apSIGN}
\ilabel[mul:sgn0] {apSIGN=0}
\ilabel[mul:diga] {apDIG\nb tmpa}
\ilabel[mul:digb] {apnumD=}
\ilabel[mul:ba] {apIVmod}
\ilabel[mul:bb] {tmpc}
\ilabel[mul:b] {*.}
\ilabel[mul:ca] {tmpb^^E}
\ilabel[mul:cb] {apMULc}
\ilabel[mul:d] {apMULd}
\ilabel[mul:g] {apMULg}
\ilabel[mul:z] {0-}
\ilabel[mul:zz] {tmpa\nb OUT}
\begitems
\item * It gets the parameters in "\tmpa" and "\tmpb" preprocessed using
the "\apPPab" macro.
\item * It evaluates the exponent of ten "\apE" which is usable when
the scientific notation of numbers is used
(line~\cite[mul:apE]).
\item * It calculates "\apSIGN" of the result
(line~\cite[mul:sgn]).
\item * If "\apSIGN=0" then the result is zero and we will do nothing more
(line~\cite[mul:sgn0]).
\item * The decimal point is removed from the parameters by
"\apDIG". The "\apnumD" includes the number of digits
before decimal point (after the "\apDIG" is used) and the
"" includes the number of digits in the rest. The "\apnumA"
or "\apnumB" includes total number of digits in the parameters "\tmpa" or
"\tmpb" respectively. The "\apnumD" is re-calculated: it saves the number
of digits after decimal point in the result
(lines~\cite[mul:diga] to~\cite[mul:digb]).
\item *
Let $A$ is the number of total digits in the "" and let
$F=A \mathrel{\rm mod} 4$, but if $F=0$ then reassign it to $F=4$. Then $F$
means the number of digits in the first Digit. This calculation
is done by "\apIVmod" macro. All another Digits will have four digits.
The "\apMULb@@@@" is able to read four digits, next four digits
etc. We need to insert appropriate number of empty parameters before the "".
For example "\apMULb{}{}{}@@@@" reads first only one digit from "",
next four digits etc. The appropriate number of empty parameters are prepared in
the "\tmpc" macro
(lines~\cite[mul:ba] to~\cite[mul:bb]).
\item * The "\apMULb" reads the "" (all Digits) and
prepares the "\OUT" macro in the special interleaved format
(described below). The format is finished by "*." in the line~\cite[mul:b].
\item * Analogical work is done with the second parameter "". But this
parameter is processed by "\apMULc", which reads Digits of the parameter
and inserts them to the "\tmpa" in the reversed order
(lines~\cite[mul:ca] to~\cite[mul:cb]).
\item * The main calculation is done by "\apMULd@", which reads Digits
from "" (in reversed order) and does multiplication of the
"