diff options
author | Norbert Preining <norbert@preining.info> | 2021-03-30 03:00:56 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2021-03-30 03:00:56 +0000 |
commit | 93809c868bf15852ddc7b50542713131a5d8c05f (patch) | |
tree | 3c741be9d43fdee2f3ecdb17d463deb188d881a8 /macros/generic/polexpr/polexpr.sty | |
parent | 3c5dd0d2f091e5e866af1de0327171b253ec9178 (diff) |
CTAN sync 202103300300
Diffstat (limited to 'macros/generic/polexpr/polexpr.sty')
-rw-r--r-- | macros/generic/polexpr/polexpr.sty | 1057 |
1 files changed, 1057 insertions, 0 deletions
diff --git a/macros/generic/polexpr/polexpr.sty b/macros/generic/polexpr/polexpr.sty new file mode 100644 index 0000000000..c94a4e8d61 --- /dev/null +++ b/macros/generic/polexpr/polexpr.sty @@ -0,0 +1,1057 @@ +% author: Jean-François Burnol +% License: LPPL 1.3c (author-maintained) +% Usage: \input polexpr.sty (Plain or other macro formats) +% or \usepackage{polexpr} (LaTeX macro format) +% polexpr.sty (this file) inputs: +% polexprcore.tex +% polexprexpr.tex +% polexprsturm.tex +\begingroup\catcode61\catcode48\catcode32=10\relax% + \catcode13=5 % ^^M + \endlinechar=13 % + \catcode123=1 % { + \catcode125=2 % } + \catcode64=11 % @ + \catcode35=6 % # + \catcode44=12 % , + \catcode45=12 % - + \catcode46=12 % . + \catcode58=12 % : + \def\z {\endgroup}% + \expandafter\let\expandafter\x\csname ver@polexpr.sty\endcsname + \expandafter\let\expandafter\w\csname ver@xintexpr.sty\endcsname + \expandafter + \ifx\csname PackageInfo\endcsname\relax + \def\y#1#2{\immediate\write-1{Package #1 Info: #2.}}% + \else + \def\y#1#2{\PackageInfo{#1}{#2}}% + \fi + \expandafter + % I don't think engine exists providing \expanded but not \numexpr + \ifx\csname expanded\endcsname\relax + \y{polexpr}{\expanded not available, aborting input}% + \aftergroup\endinput + \else + \ifx\x\relax % plain-TeX, first loading of polexpr.sty + \ifx\w\relax % but xintexpr.sty not yet loaded. + \expandafter\def\expandafter\z\expandafter + {\z\input xintexpr.sty\relax}% + \fi + \else + \def\empty {}% + \ifx\x\empty % LaTeX, first loading, + % variable is initialized, but \ProvidesPackage not yet seen + \ifx\w\relax % xintexpr.sty not yet loaded. + \expandafter\def\expandafter\z\expandafter + {\z\RequirePackage{xintexpr}[2021/02/20]}% + \fi + \else + \aftergroup\endinput % polexpr already loaded. + \fi + \fi + \fi +\z% +\XINTsetupcatcodes% +\XINT_providespackage +\ProvidesPackage{polexpr}% + [2021/03/29 v0.8 Polynomial expressions with rational coefficients (JFB)]% +\begingroup + \def\x#1/#2/#3 #4\xint:{#1#2#3}% + \ifnum\expandafter\x\expanded{\csname ver@xintexpr.sty\endcsname}\xint: + <20210220 % actually 20200131 (xint 1.4) is presumably ok + \immediate\write128{! Package polexpr error: xintexpr too old, aborting input}% + \else\expandafter\xint_gobble_i + \fi +\endinput\endgroup +\let\PolDecToString\xintDecToString +\long\def\POL@ifstar#1#2% +{% + \begingroup\def\@tempa{#1}\def\@tempb{#2}% + \futurelet\@let@token\POL@@ifstar +}% +\def\POL@@ifstar +{% + \xint_firstofone{\ifx} \@let@token\def\next{\POL@@again\POL@@ifstar}\else + \ifx*\@let@token\def\next##1{\expandafter\endgroup\@tempa}\else + \def\next{\expandafter\endgroup\@tempb}\fi\fi\next +}% +\xint_firstofone{\def\POL@@again#1} {\futurelet\@let@token#1}% +\long\def\POL@chkopt#1[#2]% +{% + \begingroup\def\@tempa{#1}\def\@tempb{#1[#2]}% + \futurelet\@let@token\POL@@ifopt +}% +\def\POL@@ifopt +{% + \xint_firstofone{\ifx} \@let@token\def\next{\POL@@again\POL@@ifopt}\else + \ifx[\@let@token\def\next{\expandafter\endgroup\@tempa}\else %] + \def\next{\expandafter\endgroup\@tempb}\fi\fi\next +}% +% \polexprsetup added at 0.7 +\catcode`! 3 +\def\polexprsetup#1{\POL@setup_parsekeys #1,=!,\xint_bye}% +\def\POL@setup_parsekeys #1=#2#3,{% + \ifx!#2\expandafter\xint_bye\fi + \csname POL@setup_setkey_\xint_zapspaces #1 \xint_gobble_i\endcsname + \xint_firstoftwo + {\PackageWarning{polexpr}{The \detokenize{#1} key is unknown! ignoring}}% + {\xintZapLastSpaces{#2#3}}% + \POL@setup_parsekeys +}% +\def\POL@setup_setkey_norr #1#2{\edef\POL@norr}% +\def\POL@setup_setkey_sqfnorr #1#2{\edef\POL@sqfnorr}% +\polexprsetup{norr=_norr, sqfnorr=_sqf_norr} +\catcode`! 11 % special catcode for ! as used in xintexpr.sty +% +\newif\ifxintveryverbose +\newif\ifpolnewpolverbose +\newif\ifpoltypesetall +\newif\ifpoltoexprall +%% +%% Main data format for non-expandable manipulations +%% +%% The main exchange structure is: +%% N.\empty{coeff0}{coeff1}....{coeffN} +%% It is stored in macros \POLuserpol@<name of polynomial> +%% The \empty is basically there to avoid brace-stripping +%% in some grabbing contexts (maybe I should revisit this) +%% +%% The zero polynomial is stored as -1.\empty{0/1[0]} +%% Degree zero polynomials are 0.\empty{numeric value} +%% +%% Depending on input path the numeric values coeff0, coeff1, ...., coeffN +%% may have been or not already converted into A/B[n] format. +%% As a rule, computations are not followed with reducing the fractions +%% to smallest terms; the innocent may be unaware that computing +%% with fractions quickly give gigantic numbers. There is \PolReduceCoeffs +%% to do that. +%% +%% This base structure is maintained at 0.8 for legacy reasons but perhaps I +%% need to revisit this. A characteristic of the package so far is that it +%% thus stores and manipulate polynomials basically as the complete sequence +%% of coefficients, (using the xintfrac "zero" for missing coefficients) which +%% means that it will handle poorly polynomials of high degrees such as X^500. +%% +%% Test if zero +\def\POL@ifZero#1{\expandafter\POL@ifZero@aux#1;}% +\def\POL@ifZero@aux #1#2;{\if-#1\expandafter\xint_firstoftwo + \else\expandafter\xint_secondoftwo + \fi}% +%% Split into degree and coefficients +% The \expandafter chain removes the \empty token +\def\POL@split#1.#2;#3#4% + {\def#3{#1}\expandafter\def\expandafter#4\expandafter{#2}}% +%% Define from values stored in a "macros-array" +\def\POL@resultfromarray #1{% + \edef\POL@result{\ifnum\count@>\z@ + \the\numexpr\count@-\@ne.\noexpand\empty + \xintiloop [1+1]% + \expandafter\POL@braceit\csname POL@array#1\xintiloopindex\endcsname + \ifnum\xintiloopindex<\count@ + \repeat + \else-1.\noexpand\empty{0/1[0]}\fi}% +}% +\def\POL@braceit#1{{#1}}% needed as \xintiloopindex can not "see" through braces +%% +%% Conversion between legacy data storage and the one used for the +%% the novel polexpr 0.8 notion of \xintexpr polynomial variables +%% +%% The 0.8 expandable implementation of core algebra is also manipulating +%% the complete list of coefficients. The internal data structure is +%% (this is the numeric leaf in xintexpr ople terminology) currently: +%% PN.{coeff0}{coeff1}....{coeffN} +%% where the P letter identifies the polynomial type. +%% Here the degree N is *always* at least 1: if some evaluation ends +%% up in a constant polynomial it will always be output as a genuine +%% scalar numeric variable, as a rule in in A/B[n] format +%% +%% This is not definitive and I need to think about it more (in particular +%% in the distant perspective of supporting multi-variable polynomials). +%% However modifying this will be costly labor at this stage. +%% +\input polexprcore.tex\relax % load expandable algebra +\def\POL@vartolegacy #1% \romannumeral\POL@vartolegacy ... \xint: +{% + \if 0#1\xint_dothis\POL@vartolegacy@zero\fi + \if P#1\xint_dothis\POL@vartolegacy@pol\fi + \xint_orthat\POL@vartolegacy@scalar #1% +}% +\def\POL@vartolegacy@zero #1\xint:{\xint_c_ -1.\empty{0/1[0]}}% +\def\POL@vartolegacy@scalar #1\xint:{\xint_c_ 0.\empty{#1}}% +\def\POL@vartolegacy@pol P#1.#2\xint:{\xint_c_ #1.\empty#2}% +% +\def\POL@tovar#1{\romannumeral\expandafter\expandafter\expandafter + \POL@legacytovar\csname POLuserpol@#1\endcsname}% +\def\POL@legacytovar #1.% \romannumeral\POL@legacytovar N.\empty{c0}... +{% + \ifnum #1<\xint_c_i\xint_dothis\POL@legacytovar@scalar\fi + \xint_orthat\POL@legacytovar@pol #1.% +}% +\def\POL@legacytovar@scalar #1.\empty#2{\xint_c_ #2}% +\def\POL@legacytovar@pol #1.\empty{\xint_c_ P#1.}% +%% +%% Extend \xintexpr (\xintdefvar, \xintdeffunc) to recognize the new +%% polynomial type +%% +%% **** It does NOT apply to \xintfloatexpr context +%% +\input polexprexpr.tex\relax +%% +%% \poldef +%% +\def\PolDef{\POL@chkopt\POL@oPolDef[x]}% +\def\POL@oPolDef[#1]#2#3{\poldef #2(#1):=#3;}% +\def\poldef{\edef\POL@restoresemicolon{\catcode59=\the\catcode59\relax}% + \catcode59 12 \POL@defpol}% +\def\POL@defpol #1(#2)#3=#4;{% + \POL@restoresemicolon + \edef\POL@polname{\xint_zapspaces #1 \xint_gobble_i}% +\begingroup + \unless\ifxintveryverbose\xintverbosefalse\fi + %% RADICAL CHANGE AT 0.8: + %% we define a **variable** not a **function** + %% ever since polexpr initial version, a function was defined and + %% the associated macros was then deconstructed in further analysis + %% via non-expandable approach. At 0.8 the polynomial algebra has + %% been implemented expandably allowing direct plug-in into \xintexpr + \xintdefvar __pol = subs(#4,#2=qraw({{P1.{0/1[0]}{1/1[0]}}}));% + \expandafter +\endgroup + \expandafter\def\expandafter\POL@result\expandafter + {\romannumeral0\expandafter\xint_stop_atfirstofone + \romannumeral0\csname XINT_expr_varvalue___pol\endcsname}% + \XINT_global\expandafter\def\csname POLuserpol@\POL@polname\expandafter\endcsname + \expandafter{\romannumeral\expandafter\POL@vartolegacy\POL@result\xint:}% + \expandafter\POL@newpol\expandafter{\POL@polname}% +}% +\def\POL@newpol#1{% + % 0.7.5 had some complicated special handling of constant + % polynomials, but these are complications of the past + % First a variable usable in \poldef but not in \xintexpr for arithmetic + % only for special dedicated functions such as coeff(), deg() + % (when they will be implemented). In \poldef, composition of polynomials + % in P(Q) syntax will be more efficient than P(Q(x)). + % This will use \XINT_global and obey \xintverbose... setting + \XINT_expr_defvar_one{#1}{{\POL@tovar{#1}}}% + % Second a function usable not only in \poldef but also in \xintexpr + % Will use \XINT_global + \POL@newpolhorner{#1}% + \POL@defpolfunc{#1}{expr}% + \XINT_global\expandafter\let\csname XINT_flexpr_func_#1\endcsname\@undefined + \ifpolnewpolverbose\POL@info{#1}\fi +}% +\def\POL@newfloatpol#1{% + \POL@newfloatpolhorner{#1}% + \POL@defpolfunc{#1}{flexpr}% + \ifpolnewpolverbose\POL@floatinfo{#1}% + \else + \ifxintverbose\POL@floatinfo{#1}\fi + \fi +}% +\def\POL@info #1{% + \xintMessage {polexpr}{Info}% + {Function #1 for the \string\xintexpr\space parser is + \ifxintglobaldefs(globally) \fi + associated to \string\XINT_expr_polfunc_#1\space + with meaning: + \expandafter\meaning + \csname XINT_expr_polfunc_#1\endcsname}% +}% +\def\POL@floatinfo #1{% + \xintMessage {polexpr}{Info}% + {Function #1 for the \string\xintfloatexpr\space parser is + \ifxintglobaldefs(globally) \fi + associated to \string\XINT_flexpr_polfunc_#1\space + with meaning: + \expandafter\meaning + \csname XINT_flexpr_polfunc_#1\endcsname}% +}% +% +\def\POL@newpolhorner#1{% + \expandafter\expandafter\expandafter\POL@split + \csname POLuserpol@#1\endcsname;\POL@var@deg\POL@var@coeffs + \edef\POL@var@coeffs{\xintRevWithBraces{\POL@var@coeffs}}% + \begingroup + \expandafter\POL@newpol@horner\POL@var@coeffs\relax + \expandafter + \endgroup + \expandafter\XINT_global + \expandafter\def\csname XINT_expr_polfunc_#1\expandafter\endcsname + \expandafter##\expandafter1\expandafter{\POL@tmp{##1}}% +}% +\def\POL@newfloatpolhorner#1{% + %% redefine function to expand by Horner scheme. Is this useful? + %% perhaps bad idea for numerical evaluation of thing such as (1+x)^10? +% note: I added {0/1[0]} item to zero polynomial also to facilitate this + \expandafter\expandafter\expandafter\POL@split + \csname POLuserpol@#1\endcsname;\POL@var@deg\POL@var@coeffs + \edef\POL@var@coeffs{\xintRevWithBraces{\POL@var@coeffs}}% + \begingroup + \expandafter\POL@newpol@floathorner\POL@var@coeffs\relax + \expandafter + \endgroup + \expandafter\def\csname XINT_flexpr_polfunc_#1\expandafter\endcsname + \expandafter##\expandafter1\expandafter{\POL@tmp{##1}}% +}% +\def\POL@newpol@horner#1{\let\xintPolAdd\relax\let\xintPolMul\relax + \def\POL@tmp##1{#1}\POL@newpol@horner@loop.}% +\def\POL@newpol@horner@loop.#1{% + \if\relax#1\expandafter\xint_gob_til_dot\fi + \edef\POL@tmp##1{\xintiiifZero{#1} + {\xint_firstofone}{\xintPolAdd{#1}}% + {\xintPolMul{##1}{\POL@tmp{##1}}}}% + \POL@newpol@horner@loop.% +}% +\def\POL@newpol@floathorner#1{\let\XINTinFloatAdd\relax\let\XINTinFloatMul\relax + \edef\POL@tmp##1{\XINTinFloatdigits{#1}}% + \POL@newpol@floathorner@loop.}% +\def\POL@newpol@floathorner@loop.#1{% + \if\relax#1\expandafter\xint_gob_til_dot\fi + \edef\POL@tmp##1{\xintiiifZero{#1} + {\xint_firstofone}{\XINTinFloatAdd{\XINTinFloatdigits{#1}}}% + {\XINTinFloatMul{##1}{\POL@tmp{##1}}}}% + \POL@newpol@floathorner@loop.% +}% +%% +%% Non-expandable polynomial manipulations +%% +\def\PolGenFloatVariant#1{\POL@newfloatpol{#1}}% +% +\def\PolLet#1#2{\if=\noexpand#2\expandafter\xint_firstoftwo + \else\expandafter\xint_secondoftwo\fi + \POL@@let\POL@let{#1}{#2}}% +\def\POL@@let#1#2#3{\POL@let{#1}{#3}}% +\def\POL@let#1#2{% + \XINT_global + \expandafter\let\csname POLuserpol@#1\expandafter\endcsname + \csname POLuserpol@#2\endcsname + \XINT_expr_defvar_one{#1}{{\POL@tovar{#1}}}% + \XINT_global + \expandafter\let\csname XINT_expr_polfunc_#1\expandafter\endcsname + \csname XINT_expr_polfunc_#2\endcsname + \POL@defpolfunc{#1}{expr}% + \ifpolnewpolverbose\POL@info{#1}\fi +}% +\def\PolGlobalLet#1#2{\begingroup\xintglobaldefstrue\PolLet{#1}{#2}\endgroup} +% +\def\PolAssign#1{\def\POL@polname{#1}\POL@assign}% zap spaces in #1? +\def\POL@assign#1\toarray#2{% + \expandafter\expandafter\expandafter\POL@split + \csname POLuserpol@\POL@polname\endcsname;\POL@var@deg\POL@var@coeffs + \xintAssignArray\POL@var@coeffs\to#2% + % modify \#200 macro to return 0/1[0] for out of range indices + \@namedef{\xint_arrayname00}##1##2##3{% + \@namedef{\xint_arrayname00}####1{% + \ifnum####1>##1 \xint_dothis{ 0/1[0]}\fi + \ifnum####1>\m@ne \xint_dothis + {\expandafter\expandafter\expandafter##3% + \csname##2####1\endcsname}\fi + \unless\ifnum-####1>##1 \xint_dothis + {\expandafter\expandafter\expandafter##3% + \csname##2\the\numexpr##1+####1+\@ne\endcsname}\fi + \xint_orthat{ 0/1[0]}}% space stops a \romannumeral0 + }% + \csname\xint_arrayname00\expandafter\expandafter\expandafter\endcsname + \expandafter\expandafter\expandafter + {\csname\xint_arrayname0\expandafter\endcsname\expandafter}\expandafter + {\xint_arrayname}{ }% +}% +\def\PolGet{}% +\def\PolGet#1#2\fromarray#3{% + \begingroup % closed in \POL@getfromarray + \POL@getfromarray{#1}{#3}% + \POL@newpol{#1}% +}% +\def\POL@getfromarray#1#2{% + \count@=#2{0} %<- intentional space + \ifnum\count@=\z@ + \def\POL@result{-1.\empty{0/1[0]}}% 0.5 fix for empty array + \else + \xintloop + \edef\POL@tmp{#2{\count@}}% + \edef\POL@tmp{\xintRaw{\POL@tmp}}% +% sadly xinttools (current 1.3a) arrays have no setters for individual items... + \expandafter\let\csname POL@tmparray\the\count@\endcsname\POL@tmp + \if0\xintiiSgn{\POL@tmp}% + \advance\count@\m@ne + \repeat + \count\tw@\count@ + \xintloop + \ifnum\count@>\@ne + \advance\count@\m@ne + \edef\POL@tmp{#2{\count@}}% + \edef\POL@tmp{\xintRaw{\POL@tmp}}% + \expandafter\let\csname POL@tmparray\the\count@\endcsname\POL@tmp + \repeat + \count@\count\tw@ + \def\POL@tmp##1.{{\csname POL@tmparray##1\endcsname}}% + \edef\POL@result{\the\numexpr\count@-\@ne.\noexpand\empty + \xintiloop[1+1]% + \expandafter\POL@tmp\xintiloopindex.% + \ifnum\xintiloopindex<\count@ + \repeat}% + \fi + \expandafter + \endgroup + \expandafter + \XINT_global + \expandafter + \def\csname POLuserpol@#1\expandafter\endcsname + \expandafter{\POL@result}% +}% +% +\def\PolFromCSV#1#2{% + \begingroup % closed in \POL@getfromarray + \xintAssignArray\xintCSVtoList{#2}\to\POL@arrayA + \POL@getfromarray{#1}\POL@arrayA + \POL@newpol{#1}% +}% +% +\def\PolMapCoeffs#1#2{% #1 = macro, #2 = name + \POL@mapcoeffs{#1}{#2}% + \POL@newpol{#2}% +}% +\def\POL@mapcoeffs#1#2{% + \begingroup + \def\POL@mapcoeffs@macro{#1}% + \expandafter\expandafter\expandafter\POL@split + \csname POLuserpol@#2\endcsname;\POL@mapcoeffs@deg\POL@mapcoeffs@coeffs +% ATTENTION à ne pas faire un \expandafter ici, car brace removal si 1 item + \xintAssignArray\POL@mapcoeffs@coeffs\to\POL@arrayA + \def\index{0}% + \count@\z@ + \expandafter\POL@map@loop\expandafter.\POL@mapcoeffs@coeffs\relax + \xintloop +% this abuses that \POL@arrayA0 is never 0. + \xintiiifZero{\csname POL@arrayA\the\count@\endcsname}% + {\iftrue}% + {\iffalse}% + \advance\count@\m@ne + \repeat +% donc en sortie \count@ est 0 ssi pol nul. + \POL@resultfromarray A% + \expandafter + \endgroup + \expandafter + \XINT_global + \expandafter + \def\csname POLuserpol@#2\expandafter\endcsname\expandafter{\POL@result}% +}% +\def\POL@map@loop.#1{\if\relax#1\expandafter\xint_gob_til_dot\fi + \advance\count@\@ne + \edef\POL@map@coeff{\POL@mapcoeffs@macro{#1}}% + \expandafter + \let\csname POL@arrayA\the\count@\endcsname\POL@map@coeff + \edef\index{\the\numexpr\index+\@ne}% + \POL@map@loop.}% +% +\def\POL@xintIrr#1{\xintIrr{#1}[0]}% +\def\PolReduceCoeffs{\POL@ifstar\POL@sreducecoeffs\POL@reducecoeffs}% +\def\POL@reducecoeffs#1{\PolMapCoeffs{\POL@xintIrr}{#1}}% +\def\POL@sreducecoeffs#1{\PolMapCoeffs{\xintPIrr}{#1}}% +% +\def\PolMakeMonic#1{% + \edef\POL@leadingcoeff{\PolLeadingCoeff{#1}}% + \edef\POL@leadingcoeff@inverse{\xintDiv{1/1[0]}{\POL@leadingcoeff}}% + \PolMapCoeffs{\xintMul{\POL@leadingcoeff@inverse}}{#1}% +}% +% +%% \PolMakePrimitive (0.5) +% This uses expandable \PolIContent +% Note: the integer coefficients stored in A/1[n] form with +% A not having trailing zeroes, due to usage of \xintREZ here. +\def\POL@makeprim@macro#1% + {\xintREZ{\xintNum{\xintDiv{#1}{\POL@makeprim@icontent}}}}% +\def\PolMakePrimitive#1{% + % This does not need a full user declared polynomial on input, only + % a \POLuserpol@name macro, but on output it is fully declared + \edef\POL@makeprim@icontent{\PolIContent{#1}}% + \PolMapCoeffs\POL@makeprim@macro{#1}% +}% +\def\POL@makeprimitive#1{% + % Avoids declaring the polynomial, internal usage in \PolToSturm + \edef\POL@makeprim@icontent{\PolIContent{#1}}% + \POL@mapcoeffs\POL@makeprim@macro{#1}% +}% +% +%% Euclidean division +% now based on the expandable routine from polexprcore.tex +% +\def\PolDivide#1#2#3#4{% #3=quotient, #4=remainder of #1 by #2 + \POL@divide{#1}{#2}% + \XINT_global\expandafter\let\csname POLuserpol@#3\endcsname\POL@Q + \POL@newpol{#3}% + \XINT_global\expandafter\let\csname POLuserpol@#4\endcsname\POL@R + \POL@newpol{#4}% +}% +\def\PolQuo#1#2#3{% #3=quotient of #1 by #2 + \POL@divide{#1}{#2}% + \XINT_global\expandafter\let\csname POLuserpol@#3\endcsname\POL@Q + \POL@newpol{#3}% +}% +\def\PolRem#1#2#3{% #3=remainder of #1 by #2 + \POL@divide{#1}{#2}% + \XINT_global\expandafter\let\csname POLuserpol@#3\endcsname\POL@R + \POL@newpol{#3}% +}% +\def\POL@divide#1#2{% + % much simpler at 0.8 thanks to our expandable macros + \xintAssign\xintPolQuoRem{\POL@tovar{#1}}{\POL@tovar{#2}}\to\POL@Q\POL@R + \odef\POL@Q{\romannumeral\expandafter\POL@vartolegacy\POL@Q\xint:}% + \odef\POL@R{\romannumeral\expandafter\POL@vartolegacy\POL@R\xint:}% +}% +%% Euclidean special pseudo-remainder +\def\POL@getprem#1#2{% + \let\POL@Q\undefined % trap errors in Sturm code update to use \POL@prem + % this was simpler before I converted \xintPolPRem into returning a tuple... + \odef\POL@R{\romannumeral\expandafter\POL@vartolegacy + \romannumeral0\expandafter\xint_stop_atsecondoftwo + \romannumeral`&&@\xintPolPRem{\POL@tovar{#1}}{\POL@tovar{#2}}% + \xint:}% +}% +% +%%%%%%%%%%%% +%% +%% Things are currenly implemented twice : here the legacy macros +%% such as GCD or Diff, and in polexprcore.tex the expandable +%% support macros for the \xinteval interface. +%% +%% Soon, I will probably remove all legacy code (like I did already +%% for division) and make the user macros simple wrappers to the +%% expandable code. +%% +%% But for 0.8 release, I preferred not to yet, as I did not have +%% really the time to compare speed. Usage of the "special +%% pseudo euclidean remainder" (expandable) code in Sturm chain +%% construction proved very beneficial as it divided by 3 the +%% \PolToSturm execution time on the Wilkinson perturbed type 1 +%% example in the documentation. +%% +%%%%%%%%%%%% +% +%% GCD +% +% It seems I didn't even use here the (now deleted) macros implementing +% division, and I redid here what was needed: this code, which I leave +% standing as I have other priorities, does not use the \POL@divide ! +% +\def\PolGCD#1#2#3{% sets #3 to the (unitary) G.C.D. of #1 and #2 + \POL@GCD{#1}{#2}{#3}% + \POL@newpol{#3}% +}% +\def\POL@GCD #1#2#3{% + \begingroup + \expandafter\let\expandafter\POL@A\csname POLuserpol@#1\endcsname + \expandafter\let\expandafter\POL@B\csname POLuserpol@#2\endcsname + \expandafter\POL@split\POL@A;\POL@degA\POL@polA + \expandafter\POL@split\POL@B;\POL@degB\POL@polB + \ifnum\POL@degA<\z@ + \expandafter\xint_firstoftwo\else\expandafter\xint_secondoftwo + \fi + {\ifnum\POL@degB<\z@ + \expandafter\xint_firstoftwo\else\expandafter\xint_secondoftwo + \fi + {\def\POL@result{-1.\empty{0/1[0]}}}% + {\xintAssignArray\POL@polB\to\POL@arrayB + \POL@normalize{B}% + \POL@gcd@exit BA}}% + {\ifnum\POL@degB<\z@ + \expandafter\xint_firstoftwo\else\expandafter\xint_secondoftwo + \fi + {\xintAssignArray\POL@polA\to\POL@arrayA + \POL@normalize{A}% + \POL@gcd@exit AB}% + {\ifnum\POL@degA<\POL@degB\space + \let\POL@tmp\POL@B\let\POL@B\POL@A\let\POL@A\POL@tmp + \let\POL@tmp\POL@degB\let\POL@degB\POL@degA\let\POL@degA\POL@tmp + \let\POL@tmp\POL@polB\let\POL@polB\POL@polA\let\POL@polA\POL@tmp + \fi + \xintAssignArray\POL@polA\to\POL@arrayA + \xintAssignArray\POL@polB\to\POL@arrayB + \POL@gcd AB% + }}% + \expandafter + \endgroup + \expandafter + \XINT_global + \expandafter\def\csname POLuserpol@#3\expandafter\endcsname + \expandafter{\POL@result}% +}% +\def\POL@normalize#1{% + \expandafter\def\expandafter\POL@tmp\expandafter + {\csname POL@array#1\csname POL@array#10\endcsname\endcsname}% + \edef\POL@normalize@leading{\POL@tmp}% + \expandafter\def\POL@tmp{1/1[0]}% + \count@\csname POL@deg#1\endcsname\space + \xintloop + \ifnum\count@>\z@ + \expandafter\edef\csname POL@array#1\the\count@\endcsname + {\xintIrr{\xintDiv + {\csname POL@array#1\the\count@\endcsname}% + {\POL@normalize@leading}}[0]}% + \advance\count@\m@ne + \repeat +}% +\def\POL@gcd#1#2{% + \POL@normalize{#2}% + \edef\POL@degQ{\the\numexpr\csname POL@deg#1\endcsname + -\csname POL@deg#2\endcsname}% + \count@\numexpr\csname POL@deg#1\endcsname+\@ne\relax + \count\tw@\numexpr\POL@degQ+\@ne\relax + \xintloop + \POL@gcd@getremainder@loopbody#1#2% + \ifnum\count\tw@>\z@ + \repeat + \expandafter\def\csname POL@array#10\endcsname{1}% + \xintloop + \xintiiifZero{\csname POL@array#1\the\count@\endcsname}% + {\iftrue}% + {\iffalse}% + \advance\count@\m@ne + \repeat + \expandafter\edef\csname POL@deg#1\endcsname{\the\numexpr\count@-\@ne}% + \ifnum\count@<\@ne + \expandafter\POL@gcd@exit + \else + \expandafter\edef\csname POL@array#10\endcsname{\the\count@}% + \expandafter\POL@gcd + \fi{#2}{#1}% +}% +\def\POL@gcd@getremainder@loopbody#1#2{% + \edef\POL@gcd@ratio{\csname POL@array#1\the\count@\endcsname}% + \advance\count@\m@ne + \advance\count\tw@\m@ne + \count4 \count@ + \count6 \csname POL@deg#2\endcsname\space + \xintloop + \ifnum\count6>\z@ + \expandafter\edef\csname POL@array#1\the\count4\endcsname + {\xintSub + {\csname POL@array#1\the\count4\endcsname}% + {\xintMul + {\POL@gcd@ratio}% + {\csname POL@array#2\the\count6\endcsname}}}% + \advance\count4 \m@ne + \advance\count6 \m@ne + \repeat +}% +\def\POL@gcd@exit#1#2{% + \count@\numexpr\csname POL@deg#1\endcsname+\@ne\relax + \POL@resultfromarray #1% +}% +% +%% DIFFERENTIATION +% +\def\POL@diff@loop@one #1/#2[#3]#4% + {\xintIrr{\xintiiMul{#4}{#1}/#2[0]}[#3]}% +\def\POL@diff#1{\POL@diff@loop1.}% +\def\POL@diff@loop#1.#2{% + \if\relax#2\expandafter\xint_gob_til_dot\fi + {\expandafter\POL@diff@loop@one\romannumeral0\xintraw{#2}{#1}}% + \expandafter\POL@diff@loop\the\numexpr#1+\@ne.% +}% +\def\PolDiff{\POL@chkopt\POL@oPolDiff[1]}% +\def\POL@oPolDiff[#1]{% + % optional parameter is how many times to derivate + % first mandatory arg is name of polynomial function to derivate, + % same name as in \NewPolExpr + % second mandatory arg name of derivative + \edef\POL@iterindex{\the\numexpr#1\relax}% + \ifnum\POL@iterindex<\z@ + \expandafter\xint_firstoftwo + \else + \expandafter\xint_secondoftwo + \fi + {\PolAntiDiff[-\POL@iterindex]}{\POL@Diff}% +}% +\def\POL@Diff{% + \ifcase\POL@iterindex\space + \expandafter\POL@Diff@no + \or\expandafter\POL@Diff@one + \else\xint_afterfi{\POL@Iterate\POL@Diff@one}% + \fi +}% +\def\POL@Diff@no #1#2{\POL@let{#2}{#1}}% +\def\POL@Diff@one #1#2{\POL@Diff@@one {#1}{#2}\POL@newpol{#2}}% +\def\POL@Diff@@one#1#2{% + \expandafter\expandafter\expandafter\POL@split + \csname POLuserpol@#1\endcsname;\POL@var@deg\POL@var@coeffs + \ifnum\POL@var@deg<\@ne + \XINT_global\@namedef{POLuserpol@#2}{-1.\empty{0/1[0]}}% + \else + \edef\POL@var@coeffs{\expandafter\POL@diff\POL@var@coeffs\relax}% + \XINT_global\expandafter\edef\csname POLuserpol@#2\endcsname + {\the\numexpr\POL@var@deg-\@ne.\noexpand\empty\POL@var@coeffs}% + \fi +}% +% lazy way but allows to share with AntiDiff +\def\POL@Iterate#1#2#3{% + \begingroup + \xintverbosefalse + #1{#2}{#3}% + \xintloop + \ifnum\POL@iterindex>\tw@ + #1{#3}{#3}% + \edef\POL@iterindex{\the\numexpr\POL@iterindex-\@ne}% + \repeat + \expandafter + \endgroup\expandafter + \XINT_global + \expandafter + \def\csname POLuserpol@#3\expandafter\endcsname + \expandafter{\romannumeral`&&@\csname POLuserpol@#3\endcsname}% + #1{#3}{#3}% +}% +% +%% ANTI-DIFFERENTIATION +% +\def\POL@antidiff@loop@one #1/#2[#3]#4% + {\xintIrr{#1/\xintiiMul{#4}{#2}[0]}[#3]}% +\def\POL@antidiff{\POL@antidiff@loop1.}% +\def\POL@antidiff@loop#1.#2{% + \if\relax#2\expandafter\xint_gob_til_dot\fi + {\expandafter\POL@antidiff@loop@one\romannumeral0\xintraw{#2}{#1}}% + \expandafter\POL@antidiff@loop\the\numexpr#1+\@ne.% +}% +\def\PolAntiDiff{\POL@chkopt\POL@oPolAntiDiff[1]}% +\def\POL@oPolAntiDiff[#1]{% + % optional parameter is how many times to derivate + % first mandatory arg is name of polynomial function to derivate, + % same name as in \NewPolExpr + % second mandatory arg name of derivative + \edef\POL@iterindex{\the\numexpr#1\relax}% + \ifnum\POL@iterindex<\z@ + \expandafter\xint_firstoftwo + \else + \expandafter\xint_secondoftwo + \fi + {\PolDiff[-\POL@iterindex]}{\POL@AntiDiff}% +}% +\def\POL@AntiDiff{% + \ifcase\POL@iterindex\space + \expandafter\POL@AntiDiff@no + \or\expandafter\POL@AntiDiff@one + \else\xint_afterfi{\POL@Iterate\POL@AntiDiff@one}% + \fi +}% +\let\POL@AntiDiff@no\POL@Diff@no +\def\POL@AntiDiff@one #1#2{\POL@AntiDiff@@one{#1}{#2}\POL@newpol{#2}}% +\def\POL@AntiDiff@@one#1#2{% + \expandafter\expandafter\expandafter\POL@split + \csname POLuserpol@#1\endcsname;\POL@var@deg\POL@var@coeffs + \ifnum\POL@var@deg<\z@ + \XINT_global\@namedef{POLuserpol@#2}{-1.\empty{0/1[0]}}% + \else + \edef\POL@var@coeffs{\expandafter\POL@antidiff\POL@var@coeffs\relax}% + \XINT_global\expandafter\edef\csname POLuserpol@#2\endcsname + {\the\numexpr\POL@var@deg+\@ne.\noexpand\empty{0/1[0]}\POL@var@coeffs}% + \fi +}% +% +%% +%% Localization of roots +%% +% this is big. It provides also output macros, of both expandable and +% non-expandable type +\input polexprsturm.tex\relax +% +% +%% Non-expandable output macros +% +\def\PolTypesetCmdPrefix#1{\xintiiifSgn{#1}{}{+}{+}}% +\def\PolTypesetCmd#1{\xintifOne{\xintiiAbs{#1}}% + {\ifnum\PolIndex=\z@\xintiiSgn{#1}\else + \xintiiifSgn{#1}{-}{}{}\fi + \let\PolIfCoeffIsPlusOrMinusOne\xint_firstoftwo}% + {\PolTypesetOne{#1}% + \let\PolIfCoeffIsPlusOrMinusOne\xint_secondoftwo}% + }% +\ifdefined\frac +\def\PolTypesetOne{\xintSignedFrac}% +\else +\def\PolTypesetOne{\xintSignedFwOver}% +\fi +\catcode`^ 7 +\def\PolTypesetMonomialCmd{% + \ifcase\PolIndex\space + % + \or\PolVar + \else\PolVar^{\PolIndex}% + \fi +}% +\catcode`^ 11 % normal xint catcode +\def\PolTypeset{\POL@ifstar + {\def\POL@ts@ascending{1}\POL@Typeset}% + {\def\POL@ts@ascending{0}\POL@Typeset}% +}% +%% +%% \PolTypeset +%% +%% extended at 0.8 to handle arbitrary expressions on input +%% +\def\POL@Typeset{\POL@chkopt\POL@oPOL@Typeset[x]}% +\def\POL@oPOL@Typeset[#1]#2{% + \ifmmode\let\POL@endtypeset\empty\else$\def\POL@endtypeset{$}\fi + \ifcsname POLuserpol@#2\endcsname + \expandafter\expandafter\expandafter\POL@split + \csname POLuserpol@#2\endcsname;\POL@var@deg\POL@var@coeffs + \else + \xintAssign\expandafter\xint_firstofone\romannumeral0\xintbareeval + subs((deg(x),coeffs(x)),x=subs(#2,\PolToExprInVar=pol([0,1])))\relax + \to\POL@var@deg\POL@var@coeffs + \fi + \if\POL@ts@ascending1% + \def\PolIndex{0}% + \let\POL@ts@reverse\xint_firstofone + \let\POL@@ne@or@m@ne\@ne + \else + \let\PolIndex\POL@var@deg + \ifnum\PolIndex<\z@\def\PolIndex{0}\fi + \let\POL@ts@reverse\xintRevWithBraces + \let\POL@@ne@or@m@ne\m@ne + \fi + \def\PolVar{#1}% + \ifnum\POL@var@deg<\z@ + \PolTypesetCmd{0/1[0]}\PolTypesetMonomialCmd + \else + \ifnum\POL@var@deg=\z@ + \expandafter\PolTypesetCmd\POL@var@coeffs\PolTypesetMonomialCmd + \else + \def\POL@ts@prefix##1{\let\POL@ts@prefix\PolTypesetCmdPrefix}% + \expandafter\POL@ts@loop + \romannumeral-`0\POL@ts@reverse{\POL@var@coeffs}\relax + \fi + \fi + \POL@endtypeset +}% +\def\POL@ts@loop{\ifpoltypesetall\expandafter\xint_firstoftwo + \else\expandafter\xint_secondoftwo\fi + {\POL@ts@nocheck}{\POL@ts@check}.% +}% +\def\POL@ts@check.#1{% + \if\relax#1\expandafter\xint_gob_til_dot\fi + \xintiiifZero{#1}% + {}% + {\POL@ts@prefix{#1}\PolTypesetCmd{#1}\PolTypesetMonomialCmd}% + \edef\PolIndex{\the\numexpr\PolIndex+\POL@@ne@or@m@ne}\POL@ts@check.% +}% +\def\POL@ts@nocheck.#1{% + \if\relax#1\expandafter\xint_gob_til_dot\fi + \POL@ts@prefix{#1}\PolTypesetCmd{#1}\PolTypesetMonomialCmd + \edef\PolIndex{\the\numexpr\PolIndex+\POL@@ne@or@m@ne}\POL@ts@nocheck.% +}% +% +%% +%% Expandable output macros (legacy) +%% +\def\POL@eval@fork#1\At#2#3\krof{#2}% +\def\PolEval#1#2#3{\romannumeral`&&@\POL@eval@fork + #2\PolEvalAt + \At\PolEvalAtExpr\krof {#1}{#3}% +}% +\def\PolEvalAt#1#2{% + \xintpraw{\csname XINT_expr_polfunc_#1\endcsname{#2}}% +}% +\def\POL@eval#1#2{% + \csname XINT_expr_polfunc_#1\endcsname{#2}% +}% +\def\PolEvalAtExpr#1#2{\xinttheexpr #1(#2)\relax}% +% +\def\PolEvalReduced#1#2#3{\romannumeral`&&@\POL@eval@fork + #2\PolEvalReducedAt + \At\PolEvalReducedAtExpr\krof {#1}{#3}% +}% +\def\PolEvalReducedAt#1#2{% + \xintpraw % in order not to print denominator if the latter equals 1 + {\xintIrr{\csname XINT_expr_polfunc_#1\endcsname{#2}}[0]}% +}% +\def\PolEvalReducedAtExpr#1#2{% + \xintpraw + {\expandafter\xintIrr\romannumeral`&&@\xintthebareeval#1(#2)\relax[0]}% +}% +% +\def\PolFloatEval#1#2#3{\romannumeral`&&@\POL@eval@fork + #2\PolFloatEvalAt + \At\PolFloatEvalAtExpr\krof {#1}{#3}% +}% +\def\PolFloatEvalAt#1#2{% + \xintpfloat{\csname XINT_flexpr_polfunc_#1\endcsname{#2}}% +}% +\def\PolFloatEvalAtExpr#1#2{\xintthefloatexpr #1(#2)\relax}% +\def\PolLeadingCoeff#1{% + \romannumeral`&&@\expandafter\expandafter\expandafter\xintlastitem + \expandafter\expandafter\expandafter + {\csname POLuserpol@#1\endcsname}% +}% +% +\def\PolNthCoeff#1#2{\romannumeral`&&@% + \expandafter\POL@nthcoeff + \romannumeral0\xintnthelt{\ifnum\numexpr#2<\z@#2\else(#2)+1\fi}% + {\expandafter\expandafter\expandafter + \xint_gob_til_dot\csname POLuserpol@#1\endcsname}@% +}% +\def\POL@nthcoeff#1@{\if @#1@\expandafter\xint_firstoftwo + \else\expandafter\xint_secondoftwo\fi + {0/1[0]}{#1}}% +% +% returns -1 for zero polynomial for context of numerical expression +% should it return -\infty? +\def\PolDegree#1{\romannumeral`&&@\expandafter\expandafter\expandafter + \POL@degree\csname POLuserpol@#1\endcsname;}% +\def\POL@degree #1.#2;{#1}% +% +\def\PolToList#1{\romannumeral`&&@\expandafter\expandafter\expandafter + \xint_gob_til_dot\csname POLuserpol@#1\endcsname}% +% +\def\PolToCSV#1{\romannumeral0\xintlistwithsep{, }{\PolToList{#1}}}% +% +% \PolIContent (0.5) +% Why did I call this IContent and not Content? Ah, I see Maple terminology +% But I realize now I misread the Maple doc, its icontent() is the gcd of +% all coeffs of a multivariate polynomial. Whereas content(,) second argument +% specifies which variable to consider expression as being univariate in it +% +\def\POL@icontent#1{\romannumeral0\expandafter\XINT_fgcd_out + \romannumeral0\expandafter\XINT_fgcdof\romannumeral`&&@#1^}% +% Since xintexpr 1.4d, \xintGCDof always outputs an irreducible fraction A/B. +% (with B=1 if A/B integer). +\def\PolIContent#1{\xintGCDof{\PolToList{#1}}}% +% +\def\PolToExprCmd#1{\xintPRaw{\xintRawWithZeros{#1}}}% +\def\PolToFloatExprCmd#1{\xintFloat{#1}}% +% \def\PolTypesetCmdPrefix#1{\xintiiifSgn{#1}{}{+}{+}}% +\let\PolToExprTermPrefix\PolTypesetCmdPrefix +\def\PolToExprOneTermStyleA#1#2{% + \ifnum#2=\z@ + \PolToExprCmd{#1}% + \else + \xintifOne{\xintiiAbs{#1}} + {\xintiiifSgn{#1}{-}{}{}}% + from \PolToExprTermPrefix + {\PolToExprCmd{#1}\PolToExprTimes}% + \fi + \ifcase\xintiiAbs{#2} %<-- space here mandatory + \or\PolToExprVar + \else\PolToExprVar\PolToExprCaret\xintiiAbs{#2}% + \fi +}% +\let\PolToExprOneTerm\PolToExprOneTermStyleA +\def\PolToExprOneTermStyleB#1#2{% + \ifnum#2=\z@ + \xintNumerator{#1}% + \else + \xintifOne{\xintiiAbs{\xintNumerator{#1}}} + {\xintiiifSgn{#1}{-}{}{}}% + from \PolToExprTermPrefix + {\xintNumerator{#1}\PolToExprTimes}% + \fi + \ifcase\xintiiAbs{#2} %<-- space here mandatory + \or\PolToExprVar + \else\PolToExprVar\PolToExprCaret\xintiiAbs{#2}% + \fi + \xintiiifOne{\xintDenominator{#1}}{}{/\xintDenominator{#1}}% +}% +\def\PolToFloatExprOneTerm#1#2{% + \ifnum#2=\z@ + \PolToFloatExprCmd{#1}% + \else + \PolToFloatExprCmd{#1}\PolToExprTimes + \fi + \ifcase\xintiiAbs{#2} %<-- space here mandatory + \or\PolToExprVar + \else\PolToExprVar\PolToExprCaret\xintiiAbs{#2}% + \fi +}% +\def\PolToExprTimes{*}% +\def\PolToExprVar{x}% +\def\PolToExprInVar{x}% +\edef\PolToExprCaret{\string ^}% +%% +%% \PolToExpr +%% +%% extended at 0.8 to handle arbitrary expressions on input +%% +\def\PolToExpr#1{% + \if*\noexpand#1\expandafter\xint_firstoftwo\else + \expandafter\xint_secondoftwo\fi + \PolToExprAscending\PolToExprDescending{#1}}% +\def\PolToFloatExpr#1{% + \if*\noexpand#1\expandafter\xint_firstoftwo\else + \expandafter\xint_secondoftwo\fi + \PolToFloatExprAscending\PolToFloatExprDescending{#1}}% +\def\PolToExpr@getit#1% +{% + \ifcsname XINT_expr_varvalue_#1\endcsname + \csname XINT_expr_varvalue_#1\expandafter\endcsname + \else + \expandafter\xint_firstofone\romannumeral0% + \xintbareeval subs(#1,\PolToExprInVar=pol([0,1]))\expandafter\relax + \fi +}% +\def\PolToExprAscending#1#2{% + \expandafter\POL@toexpr\romannumeral0\PolToExpr@getit{#2}% + \PolToExprOneTerm\POL@toexprA +}% +\def\PolToFloatExprAscending#1#2{% + \expandafter\POL@toexpr\romannumeral0\PolToExpr@getit{#2}% + \PolToFloatExprOneTerm\POL@toexprA +}% +\def\PolToExprDescending#1{% + \expandafter\POL@toexpr\romannumeral0\PolToExpr@getit{#1}% + \PolToExprOneTerm\POL@toexprD +}% +\def\PolToFloatExprDescending#1{% + \expandafter\POL@toexpr\romannumeral0\PolToExpr@getit{#1}% + \PolToFloatExprOneTerm\POL@toexprD +}% +\def\POL@toexpr#1#2#3{\POL@toexpr@fork#3#2#1\relax}% +\def\POL@toexpr@fork #1#2#3{% + \POL_Pfork + #3\POL@toexpr@pol + P\POL@toexpr@cst + \krof #1#2#3% +}% +\def\POL@toexpr@cst#1#2#3\relax{#2{#3}{0}}% +\def\POL@toexpr@pol#1#2P#3.{#1{#3}#2\empty}% +% now back to legacy pre 0.8 code +\def\POL@toexprA #1#2\empty#3{% + \ifpoltoexprall\expandafter\POL@toexprall@b + \else\expandafter\POL@toexpr@b + \fi {#3}#2{0}1.% +}% +\def\POL@toexprD #1#2#3\relax{% #3 has \empty to prevent brace removal + \expandafter\POL@toexprD@a\expandafter#2% + \the\numexpr #1\expandafter.\romannumeral0\xintrevwithbraces{#3}\relax +}% +\def\POL@toexprD@a #1#2.#3{% + \ifpoltoexprall\expandafter\POL@toexprall@b + \else\expandafter\POL@toexpr@b + \fi{#3}#1{-#2}\the\numexpr\@ne+-#2.% +}% +\def\POL@toexpr@b #1#2#3{% + \xintiiifZero{#1}% + {\expandafter\POL@toexpr@loop\expandafter\POL@toexpr@b}% + {#2{#1}{#3}% + \expandafter\POL@toexpr@loop\expandafter\POL@toexpr@c}% + \expandafter#2% +}% +\def\POL@toexpr@c #1#2#3{% + \xintiiifZero{#1}% + {}% + {\PolToExprTermPrefix{#1}#2{#1}{#3}}% + \expandafter\POL@toexpr@loop\expandafter\POL@toexpr@c + \expandafter#2% +}% +\def\POL@toexprall@b #1#2#3{% + #2{#1}{#3}% + \expandafter\POL@toexpr@loop\expandafter\POL@toexprall@c + \expandafter#2% +}% +\def\POL@toexprall@c #1#2#3{% + \PolToExprTermPrefix{#1}#2{#1}{#3}% + \expandafter\POL@toexpr@loop\expandafter\POL@toexprall@c + \expandafter#2% +}% +\def\POL@toexpr@loop#1#2#3.#4{% + \if\relax#4\expandafter\xint_gob_til_dot\fi + #1{#4}#2{#3}\the\numexpr\@ne+#3.% +}% +\XINT_restorecatcodes_endinput% |