% author: Jean-François Burnol % License: LPPL 1.3c (author-maintained) \ProvidesPackage{polexpr}% [2018/04/22 v0.5.1 Polynomial expressions with rational coefficients (JFB)]% \RequirePackage{xintexpr}[2018/03/01]% xint 1.3 \edef\POL@restorecatcodes {\catcode`\noexpand\_ \the\catcode`\_ % \catcode`\noexpand\! \the\catcode`\! % \catcode0 \the\catcode0\relax}% \catcode`\_ 11 \catcode0 12 %% PATCH xintexpr TO AUTHORIZE ' IN NAMES (0.5.1) \catcode`\! 11 \def\POL@XINT_expr_scanfunc_b #1% {% \ifx !#1\xint_dothis{(_}\fi \ifcat \relax#1\xint_dothis{(_}\fi \if (#1\xint_dothis{\xint_firstoftwo{(`}}\fi \if '#1\xint_dothis \XINT_expr_scanfunc_a \fi \if @#1\xint_dothis \XINT_expr_scanfunc_a \fi \if _#1\xint_dothis \XINT_expr_scanfunc_a \fi \ifnum \xint_c_ix<1\string#1 \xint_dothis \XINT_expr_scanfunc_a \fi \ifcat a#1\xint_dothis \XINT_expr_scanfunc_a \fi \xint_orthat {(_}% #1% }% %% AUXILIARIES \newcount\POL@count \newif\ifPOL@pol \newif\ifxintveryverbose \newif\ifpoltypesetall \newif\ifPOL@sturm@normalize \newif\ifPOL@isolz@nextwillneedrefine \newif\ifpoltoexprall %% the main exchange structure (stored in macros \POLuserpol@) %% is: degree.\empty{coeff0}{coeff1}....{coeffN} %% (degree=N except zero polynomial recognized from degree set to -1 %% but it has always the {0/1[0]} coeff0.) \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}% \def\POL@split#1.#2;#3#4% separates degree and list of coefficients % The \expandafter chain removes the \empty token {\def#3{#1}\expandafter\def\expandafter#4\expandafter{#2}}% % \def\POL@resultfromarray #1{% ATTENTION, **MUST** be executed with % \count@ set to 1 + degree (\count@ = 0 for zero polynomial) \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 \newcommand\PolDef[3][x]{\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@tmp{\ifxintverbose1\else0\fi}% \unless\ifxintveryverbose\xintverbosefalse\fi \let\POL@originalXINT_expr_scanfunc_b\XINT_expr_scanfunc_b \let\XINT_expr_scanfunc_b\POL@XINT_expr_scanfunc_b \xintdeffunc __pol(#2):=0+(#4);% force conversion to raw if a constant \if1\POL@tmp\xintverbosetrue\fi \edef\POL@polname{\xint_zapspaces #1 \xint_gobble_i}% \let\XINT_expr_scanfunc_b\POL@originalXINT_expr_scanfunc_b \begingroup \setbox0\hbox{% \let\xintScalarAdd\xintAdd \let\xintScalarSub\xintSub \let\xintScalarMul\xintMul \let\xintScalarDiv\xintDiv \let\xintScalarPow\xintPow \let\xintScalarOpp\xintOpp \let\xintAdd\POL@add \let\xintMul\POL@mul \let\xintDiv\POL@div \let\xintPow\POL@pow \let\xintOpp\POL@opp \def\xintSub ##1##2{\xintAdd{##1}{\xintOpp{##2}}}% % \xintAdd{0} to get \POL@result defined even if numerical only expression % I could also test \ifPOL@pol, but this is anyhow small overhead \xintAdd{0}% {\csname XINT_expr_userfunc___pol\endcsname {\global\POL@poltrue\def\POL@result{1.\empty{0/1[0]}{1/1[0]}}}}% \expandafter}\expandafter \endgroup\expandafter \def\csname POLuserpol@\POL@polname\expandafter\endcsname \expandafter{\POL@result}% \expandafter\POL@newpol\expandafter{\POL@polname}% }% %% \def\POL@newpol#1{% \expandafter\POL@ifZero\csname POLuserpol@#1\endcsname {\@namedef{XINT_expr_userfunc_#1}##1{0/1[0]}}% {\POL@newpolhorner{#1}}% \expandafter\XINT_expr_defuserfunc \csname XINT_expr_func_#1\endcsname{#1}{expr}% \expandafter\let\csname XINT_flexpr_func_#1\endcsname\@undefined \ifxintverbose\POL@info{#1}\fi }% \def\POL@newfloatpol#1{% \expandafter\POL@ifZero\csname POLuserpol@#1\endcsname {\@namedef{XINT_flexpr_userfunc_#1}##1{0[0]}}% {\POL@newfloatpolhorner{#1}}% \expandafter\XINT_expr_defuserfunc \csname XINT_flexpr_func_#1\endcsname{#1}{flexpr}% \ifxintverbose\POL@floatinfo{#1}\fi }% \def\POL@info #1{% \xintMessage {polexpr}{Info}% {Function #1 for the \string\xintexpr\space parser is associated to \string\XINT_expr_userfunc_#1\space whose meaning uses Horner scheme: \expandafter\meaning \csname XINT_expr_userfunc_#1\endcsname}% }% \def\POL@floatinfo #1{% \xintMessage {polexpr}{Info}% {Function #1 for the \string\xintfloatexpr\space parser is associated to \string\XINT_flexpr_userfunc_#1\space whose meaning uses Horner scheme: \expandafter\meaning \csname XINT_flexpr_userfunc_#1\endcsname}% }% % \def\POL@newpolhorner#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@horner\POL@var@coeffs\relax \expandafter \endgroup \expandafter\def\csname XINT_expr_userfunc_#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_userfunc_#1\expandafter\endcsname \expandafter##\expandafter1\expandafter{\POL@tmp{##1}}% }% \def\POL@newpol@horner#1{\let\xintAdd\relax\let\xintMul\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} {\@firstofone}{\xintAdd{#1}}% {\xintMul{##1}{\POL@tmp{##1}}}}% \POL@newpol@horner@loop.% }% \def\POL@newpol@floathorner#1{\let\XINTinFloatAdd\relax\let\XINTinFloatMul\relax \def\xintAdd{\XINTinFloatAdd}\def\xintMul{\XINTinFloatMul}% \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} {\@firstofone}{\xintAdd{\XINTinFloatdigits{#1}}}% {\xintMul{##1}{\POL@tmp{##1}}}}% \POL@newpol@floathorner@loop.% }% \newcommand\PolGenFloatVariant[1]{\POL@newfloatpol{#1}}% \newcommand\PolLet[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{% \expandafter\let\csname POLuserpol@#1\expandafter\endcsname \csname POLuserpol@#2\endcsname \expandafter\let\csname XINT_expr_userfunc_#1\expandafter\endcsname \csname XINT_expr_userfunc_#2\endcsname \expandafter\XINT_expr_defuserfunc \csname XINT_expr_func_#1\endcsname{#1}{expr}% \ifxintverbose\POL@info{#1}\fi }% \newcommand\PolGlobalLet[2]{\begingroup \globaldefs\@ne \if=\noexpand#2\expandafter\xint_firstoftwo \else\expandafter\xint_secondoftwo\fi % do I need to check something here relative to \xintNewExpr? \POL@@globallet\POL@globallet {#1}{#2}}% \def\POL@@globallet#1#2#3{\POL@globallet{#1}{#3}}% \def\POL@globallet#1#2{\POL@let{#1}{#2}\endgroup}% \newcommand\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}{ }% }% \newcommand\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 % dans le cas particulier d'un array avec que des éléments nuls, \count@ est % ici devenu 0 et la boucle s'est arrêtée car #2{0} était au moins 1. De plus % \POL@tmparray1 est bien 0/1[0] donc ok pour polynôme nul dans \POL@result \count\tw@\count@ \xintloop % on mouline tous les coeffs via \xintRaw \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 \def\csname POLuserpol@#1\expandafter\endcsname \expandafter{\POL@result}% }% \newcommand\PolFromCSV[2]{% \begingroup % closed in \POL@getfromarray \xintAssignArray\xintCSVtoList{#2}\to\POL@arrayA \POL@getfromarray{#1}\POL@arrayA \POL@newpol{#1}% % semble un peu indirect et sous-optimal % mais je veux élaguer les coefficients nuls. Peut-être à revoir. }% \newcommand\PolTypesetCmdPrefix[1]{\xintiiifSgn{#1}{}{+}{+}}% \newcommand\PolTypesetCmd[1]{\xintifOne{\xintiiAbs{#1}}% {\ifnum\PolIndex=\z@\xintiiSgn{#1}\else \xintiiifSgn{#1}{-}{}{}\fi \let\PolIfCoeffIsPlusOrMinusOne\@firstoftwo}% {\PolTypesetOne{#1}% \let\PolIfCoeffIsPlusOrMinusOne\@secondoftwo}% }% \newcommand\PolTypesetOne{\xintSignedFrac}% \newcommand\PolTypesetMonomialCmd{% \ifcase\PolIndex\space % \or\PolVar \else\PolVar^{\PolIndex}% \fi }% \newcommand\PolTypeset{\@ifstar {\def\POL@ts@ascending{1}\POL@Typeset}% {\def\POL@ts@ascending{0}\POL@Typeset}% }% \newcommand\POL@Typeset[2][x]{% LaTeX \newcommand forces optional argument first \ensuremath{% \expandafter\expandafter\expandafter\POL@split \csname POLuserpol@#2\endcsname;\POL@var@deg\POL@var@coeffs \if\POL@ts@ascending1% \def\PolIndex{0}% \let\POL@ts@reverse\@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 }% }% \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.% }% \newcommand\PolMapCoeffs[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 \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]}% \newcommand\PolReduceCoeffs{\@ifstar\POL@sreducecoeffs\POL@reducecoeffs}% \def\POL@reducecoeffs#1{\PolMapCoeffs{\POL@xintIrr}{#1}}% \def\POL@sreducecoeffs#1{\PolMapCoeffs{\xintPIrr}{#1}}% %% EUCLIDEAN DIVISION \newcommand\PolDivide[4]{% #3=quotient, #4=remainder of #1 by #2 \POL@divide{#1}{#2}% \expandafter\let\csname POLuserpol@#3\endcsname\POL@Q \POL@newpol{#3}% \expandafter\let\csname POLuserpol@#4\endcsname\POL@R \POL@newpol{#4}% }% \newcommand\PolQuo[3]{% #3=quotient of #1 by #2 \POL@divide{#1}{#2}% \expandafter\let\csname POLuserpol@#3\endcsname\POL@Q \POL@newpol{#3}% }% \newcommand\PolRem[3]{% #3=remainder of #1 by #2 \POL@divide{#1}{#2}% \expandafter\let\csname POLuserpol@#3\endcsname\POL@R \POL@newpol{#3}% }% \newcommand\POL@divide[2]{% \begingroup \let\xintScalarSub\xintSub \let\xintScalarAdd\xintAdd \let\xintScalarMul\xintMul \let\xintScalarDiv\xintDiv \expandafter\let\expandafter\POL@A\csname POLuserpol@#1\endcsname \expandafter\let\expandafter\POL@B\csname POLuserpol@#2\endcsname \POL@div@c \let\POL@Q\POL@result \ifnum\POL@degQ<\z@ \let\POL@R\POL@A \else \count@\numexpr\POL@degR+\@ne\relax \POL@resultfromarray R% \let\POL@R\POL@result \fi \expandafter \endgroup \expandafter \def\csname POL@Q\expandafter\expandafter\expandafter\endcsname \expandafter\expandafter\expandafter{\expandafter\POL@Q\expandafter}% \expandafter \def\csname POL@R\expandafter\endcsname\expandafter{\POL@R}% }% %% GCD \newcommand\PolGCD[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 \let\xintScalarSub\xintSub \let\xintScalarAdd\xintAdd \let\xintScalarMul\xintMul \let\xintScalarDiv\xintDiv \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\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{\xintScalarDiv {\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 {\xintScalarSub {\csname POL@array#1\the\count4\endcsname}% {\xintScalarMul {\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% }% %% TODO: BEZOUT %% 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.% }% \newcommand\PolDiff[1][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\@firstoftwo \else \expandafter\@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 \@namedef{POLuserpol@#2}{-1.\empty{0/1[0]}}% \else \edef\POL@var@coeffs{\expandafter\POL@diff\POL@var@coeffs\relax}% \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 \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.% }% \newcommand\PolAntiDiff[1][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\@firstoftwo \else \expandafter\@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@ \@namedef{POLuserpol@#2}{-1.\empty{0/1[0]}}% \else \edef\POL@var@coeffs{\expandafter\POL@antidiff\POL@var@coeffs\relax}% \expandafter\edef\csname POLuserpol@#2\endcsname {\the\numexpr\POL@var@deg+\@ne.\noexpand\empty{0/1[0]}\POL@var@coeffs}% \fi }% %% IContent and \PolMakePrimitive (0.5) \def\POL@aux@mgcd@loop#1#2{% \if\relax#2\expandafter\POL@aux@mgcd@exit\fi \expandafter \POL@aux@mgcd@loop\romannumeral0\POL@aux@gcd#1.#2.% }% \def\POL@aux@mgcd@exit \expandafter \POL@aux@mgcd@loop\romannumeral0\POL@aux@gcd#1.\relax.{\xintiiabs{#1}}% \def\POL@aux@gcd#1.#2.{% \if0\xintiiSgn{#1}\expandafter\POL@aux@gcd@exit\fi \expandafter\POL@aux@gcd\romannumeral0\xintmod {#2}{#1}.#1.}% \def\POL@aux@gcd@exit \expandafter\POL@aux@gcd\romannumeral0\xintmod #1#2.#3.{{#1}}% \def\POL@icontent #1{\romannumeral0\expandafter \POL@aux@mgcd@loop\romannumeral`^^@#1\relax}% \newcommand\PolIContent[1]{\romannumeral0\expandafter \POL@aux@mgcd@loop\romannumeral`^^@\PolToList{#1}\relax}% \def\POL@makeprim@macro#1% {\xintREZ{\xintNum{\xintDiv{#1}{\POL@makeprim@icontent}}}}% \newcommand\PolMakePrimitive[1]{% \edef\POL@makeprim@icontent{\PolIContent{#1}}% \PolMapCoeffs\POL@makeprim@macro{#1}% }% %% Sturm Algorithm (polexpr 0.4) %% 0.5 uses primitive polynomials for faster evaluations afterwards \newcommand\PolToSturm{% \@ifstar{\POL@sturm@normalizefalse}{\POL@sturm@normalizetrue}% \POL@ToSturm }% \def\POL@aux@toint#1{\xintREZ{\xintNum{#1}}}% \def\POL@ToSturm#1#2{% \edef\POL@sturmname{#2}% \POL@let{\POL@sturmname _0}{#1}% \PolMakePrimitive{\POL@sturmname _0}% \POL@Diff@@one{\POL@sturmname _0}{\POL@sturmname _1}% % re-utiliser \POL@varcoeffs directement? \PolMakePrimitive{\POL@sturmname _1}% \POL@count\@ne \xintloop \POL@divide{\POL@sturmname _\the\numexpr\POL@count-\@ne}% {\POL@sturmname _\the\POL@count}% \expandafter\POL@split\POL@R;\POL@degR\POL@polR \unless\ifnum\POL@degR=\m@ne \advance\POL@count\@ne \expandafter\let \csname POLuserpol@\POL@sturmname _\the\POL@count\endcsname\POL@R \edef\POL@makeprim@icontent{-\POL@icontent\POL@polR}% \POL@mapcoeffs\POL@makeprim@macro{\POL@sturmname _\the\POL@count}% \repeat \edef\POL@sturm@N{\the\POL@count}% \ifPOL@sturm@normalize \ifnum\PolDegree{\POL@sturmname _\POL@sturm@N}>\z@ \xintloop \advance\POL@count\m@ne \POL@divide{\POL@sturmname _\the\POL@count}% {\POL@sturmname _\POL@sturm@N}% \expandafter\let \csname POLuserpol@\POL@sturmname _\the\POL@count\endcsname\POL@Q % quotient actually belongs to Z[X] \POL@mapcoeffs{\POL@aux@toint}{\POL@sturmname _\the\POL@count}% \ifnum\POL@count>\z@ \repeat \@namedef{POLuserpol@\POL@sturmname _\POL@sturm@N}{0.\empty{1/1[0]}}% \fi \fi \POL@count\z@ \xintloop \POL@newpol{\POL@sturmname _\the\POL@count}% \unless\ifnum\POL@sturm@N=\POL@count \advance\POL@count\@ne \repeat \expandafter\let \csname PolSturmChainLength_\POL@sturmname \endcsname\POL@sturm@N }% \newcommand\PolSturmChainLength[1] {\romannumeral`^^@\csname PolSturmChainLength_#1\endcsname}% \newcommand\PolSetToSturmChainSignChangesAt[4][\global]{% \edef\POL@sturmchain@X{\xintREZ{#4}}% \edef\POL@sturmname{#3}% \edef\POL@sturmlength{\PolSturmChainLength{\POL@sturmname}}% \POL@sturmchain@getSV@at\POL@sturmchain@X #1\let#2\POL@sturmchain@SV }% \def\POL@sturmchain@getSV@at#1{% ATTENTION USES \POL@count \def\POL@sturmchain@SV{0}% \edef\POL@sturmchain@sign{\xintiiSgn{\PolEval{\POL@sturmname _0}\At{#1}}}% \let\POL@isolz@lastsign\POL@sturmchain@sign \POL@count \z@ \ifnum\POL@isolz@lastsign=\z@ \edef\POL@isolz@lastsign {\xintiiSgn{\PolEval{\POL@sturmname _1}\At{#1}}}% \POL@count \@ne \fi \xintloop \unless\ifnum\POL@sturmlength=\POL@count \advance\POL@count \@ne \edef\POL@isolz@newsign {\xintiiSgn{\PolEval{\POL@sturmname _\the\POL@count}\At{#1}}}% \ifnum\POL@isolz@newsign=\numexpr-\POL@isolz@lastsign\relax \edef\POL@sturmchain@SV{\the\numexpr\POL@sturmchain@SV+\@ne}% \let\POL@isolz@lastsign=\POL@isolz@newsign \fi \repeat }% \newcommand\PolSetToNbOfZerosWithin[5][\global]{% \edef\POL@tmpA{\xintREZ{#4}}% \edef\POL@tmpB{\xintREZ{#5}}% \edef\POL@sturmname{#3}% \edef\POL@sturmlength{\PolSturmChainLength{\POL@sturmname}}% \POL@sturmchain@getSV@at\POL@tmpA \let\POL@SVA\POL@sturmchain@SV \POL@sturmchain@getSV@at\POL@tmpB \let\POL@SVB\POL@sturmchain@SV \ifnum\POL@SVA<\POL@SVB\space #1\edef#2{\the\numexpr\POL@SVB-\POL@SVA}% \else #1\edef#2{\the\numexpr\POL@SVA-\POL@SVB}% \fi }% \newcommand\PolSturmIsolateZeros[2][\empty]{% % #1 optional E such that roots are searched in -10^E < x < 10^E % both -10^E and +10^E must not be roots! % #2 name of Sturm chain (already pre-computed from a given polynomial) \edef\POL@sturmname{#2}% \edef\POL@sturmlength{\PolSturmChainLength{#2}}% \ifx\empty#1\relax \POL@isolz@getsignchanges@plusinf \POL@isolz@getsignchanges@minusinf \else \edef\POL@isolz@E{\the\numexpr\xint_zapspaces #1 \xint_gobble_i\relax}% \POL@sturmchain@getSV@at{1[\POL@isolz@E]}% \let\POL@isolz@plusinf@SV \POL@sturmchain@SV \let\POL@isolz@plusinf@sign\POL@sturmchain@sign \POL@sturmchain@getSV@at{-1[\POL@isolz@E]}% \let\POL@isolz@minusinf@SV \POL@sturmchain@SV \let\POL@isolz@minusinf@sign\POL@sturmchain@sign \ifnum\POL@isolz@plusinf@sign=\z@ \PackageError{polexpr}% {The polynomial #2 vanishes at set upper bound 10^\POL@isolz@E}% {Compile again with a bigger exponent in source. (X to abort).}% \fi \ifnum\POL@isolz@minusinf@sign=\z@ \PackageError{polexpr}% {The polynomial #2 vanishes at set lower bound -10^\POL@isolz@E}% {Compile again with a bigger exponent in source. (X to abort).}% \fi \fi \edef\POL@isolz@NbOfRoots {\the\numexpr\POL@isolz@minusinf@SV-\POL@isolz@plusinf@SV}% \ifnum\POL@isolz@NbOfRoots=\z@ \begingroup\globaldefs\@ne \expandafter\xintAssignArray\expandafter\to\csname POL_ZeroInt#2L\endcsname \expandafter\xintAssignArray\expandafter\to\csname POL_ZeroInt#2R\endcsname \endgroup \else \begingroup\globaldefs\@ne \expandafter\POL@isolz@initarray\csname POL_ZeroInt#2L\endcsname \expandafter\POL@isolz@initarray\csname POL_ZeroInt#2R\endcsname \endgroup \ifx\empty#1\relax\expandafter\POL@isolz@getaprioribound\fi \expandafter\POL@isolz@main \fi }% \def\POL@isolz@initarray#1{% \expandafter\xintAssignArray \romannumeral\xintreplicate{\POL@isolz@NbOfRoots}{{0}}\to#1% }% \def\POL@isolz@getsignchanges@plusinf{% % Count number of sign changes at plus infinity in Sturm sequence \def\POL@isolz@plusinf@SV{0}% \edef\POL@isolz@lastsign{\xintiiSgn{\PolLeadingCoeff{\POL@sturmname _0}}}% \let\POL@isolz@plusinf@sign\POL@isolz@lastsign \POL@count\@ne \xintloop \edef\POL@isolz@newsign {\xintiiSgn{\PolLeadingCoeff{\POL@sturmname _\the\POL@count}}}% \unless\ifnum\POL@isolz@newsign=\POL@isolz@lastsign \edef\POL@isolz@plusinf@SV{\the\numexpr\POL@isolz@plusinf@SV+\@ne}% \fi \let\POL@isolz@lastsign=\POL@isolz@newsign \ifnum\POL@sturmlength>\POL@count \advance\POL@count\@ne \repeat }% \def\POL@isolz@getsignchanges@minusinf{% % Count number of sign changes at minus infinity in Sturm sequence \def\POL@isolz@minusinf@SV{0}% \edef\POL@isolz@lastsign{\xintiiSgn{\PolLeadingCoeff{\POL@sturmname _0}}}% \ifodd\PolDegree{\POL@sturmname _0} \edef\POL@isolz@lastsign{\xintiiOpp{\POL@isolz@lastsign}}% \fi \let\POL@isolz@minusinf@sign\POL@isolz@lastsign \POL@count\@ne \xintloop \edef\POL@isolz@newsign {\xintiiSgn{\PolLeadingCoeff{\POL@sturmname _\the\POL@count}}}% \ifodd\PolDegree{\POL@sturmname _\the\POL@count} \edef\POL@isolz@newsign{\xintiiOpp{\POL@isolz@newsign}}% \fi \unless\ifnum\POL@isolz@newsign=\POL@isolz@lastsign \edef\POL@isolz@minusinf@SV{\the\numexpr\POL@isolz@minusinf@SV+\@ne}% \fi \let\POL@isolz@lastsign=\POL@isolz@newsign \ifnum\POL@sturmlength>\POL@count \advance\POL@count\@ne \repeat }% % utility macro for a priori bound on root decimal exponent, via Float Rounding \def\POL@isolz@updateE #1e#2;% {\unless\ifnum#2<\POL@isolz@E\space\edef\POL@isolz@E{\the\numexpr#2+\@ne}\fi}% \def\POL@isolz@getaprioribound{% \PolAssign{\POL@sturmname _0}\toarray\POL@arrayA \edef\POL@isolz@leading{\POL@arrayA{\POL@arrayA{0}}}% \POL@count\z@ \xintloop \advance\POL@count\@ne \ifnum\POL@arrayA{0}>\POL@count \expandafter\edef\csname POL@arrayA\the\POL@count\endcsname {\xintDiv{\POL@arrayA\POL@count}\POL@isolz@leading}% \repeat \def\POL@isolz@E{1}% WE SEEK SMALLEST E SUCH HAT -10^E < roots < +10^E \advance\POL@count\m@ne \xintloop \ifnum\POL@count>\z@ \expandafter\POL@isolz@updateE % use floating point to get decimal exponent \romannumeral0\xintfloat[4]% should I use with [2] rather? (should work) {\xintAdd{1/1[0]}{\xintAbs{\POL@arrayA\POL@count}}};% \advance\POL@count\m@ne \repeat % \ifxintverbose\xintMessage{polexpr}{Info}% % {Roots a priori bounded in absolute value by 10 to the \POL@isolz@E.}% % \fi }% \def\POL@IsoRight@raw{\POL@IsoRight@Int/1[\POL@isolz@E]}% \def\POL@IsoLeft@raw {\POL@IsoLeft@Int/1[\POL@isolz@E]}% \def\POL@IsoRight@rawout{% \ifnum\POL@IsoRightSign=\z@\expandafter\xintREZ\fi\POL@IsoRight@raw}% \def\POL@IsoLeft@rawout{% \ifnum\POL@IsoRightSign=\z@ \expandafter\xint_firstoftwo\else\expandafter\xint_secondoftwo \fi{\xintREZ\POL@IsoRight@raw}% {\POL@IsoLeft@Int/1[\POL@isolz@E]}}% \def\POL@isolz@main {% % NOTE 2018/02/16. THIS WILL PRESUMABLY BE RE-ORGANIZED IN FUTURE TO DO % FIRST POSITIVE ROOTS THEN NEGATIVE ROOTS VIA CHANGE OF VARIABLE TO OPPOSITE. \global\POL@isolz@nextwillneedrefinefalse \def\POL@IsoRight@Int{0}% \POL@sturmchain@getSV@at\POL@IsoRight@raw \let\POL@IsoRightSV \POL@sturmchain@SV \let\POL@IsoRightSign\POL@sturmchain@sign \let\POL@IsoAtZeroSV \POL@IsoRightSV \let\POL@IsoAtZeroSign\POL@IsoRightSign \ifnum\POL@IsoAtZeroSign=\z@ \xdef\POL@isolz@IntervalIndex {\the\numexpr\POL@isolz@minusinf@SV-\POL@IsoRightSV}% \POL@refine@storeleftandright % store zero root \edef\POL@IsoRightSV{\the\numexpr\POL@IsoRightSV+\@ne}% % subtlety here if original polynomial had multiplicities, but ok. I checked! \edef\POL@IsoRightSign % evaluated twice, but that's not so bad {\xintiiOpp{\xintiiSgn{\PolEval{\POL@sturmname _1}\At{0/1[0]}}}}% \fi \def\POL@IsoLeft@Int{-1}% -10^E isn't a root! \let\POL@IsoLeftSV \POL@isolz@minusinf@SV \let\POL@IsoLeftSign\POL@isolz@minusinf@sign \edef\POL@isolz@NbOfNegRoots{\the\numexpr\POL@IsoLeftSV-\POL@IsoRightSV}% \gdef\POL@isolz@IntervalIndex{0}% \begingroup \let\POL@IsoAtZeroSV\POL@IsoRightSV % locally shifted if root at zero \let\POL@IsoAtZeroSign\POL@IsoRightSign \ifnum\POL@isolz@NbOfNegRoots>\z@ \def\POL@IsoRight@Int{-1}% \xintloop \edef\POL@isolz@E{\the\numexpr\POL@isolz@E-\@ne}% \POL@sturmchain@getSV@at\POL@IsoRight@raw \let\POL@IsoRightSV \POL@sturmchain@SV \let\POL@IsoRightSign\POL@sturmchain@sign % would an \ifx test be quicker? (to be checked) \ifnum\POL@IsoRightSV=\POL@IsoLeftSV\space % no roots in-between, sign and SV kept \repeat \def\POL@IsoLeft@Int{-10}% \let\POL@@IsoRightSign\POL@IsoRightSign % zero possible \let\POL@@IsoRightSV\POL@IsoRightSV \xintloop \edef\POL@IsoRight@Int{\the\numexpr\POL@IsoLeft@Int+\@ne}% % we could arguably do a more efficient dichotomy here \POL@sturmchain@getSV@at\POL@IsoRight@raw \let\POL@IsoRightSV \POL@sturmchain@SV \let\POL@IsoRightSign\POL@sturmchain@sign \POL@isolz@check \ifnum\POL@isolz@IntervalIndex=\POL@isolz@NbOfNegRoots\space \expandafter\xintbreakloop \fi \let\POL@IsoLeft@Int\POL@IsoRight@Int \let\POL@IsoLeftSign\POL@IsoRightSign \let\POL@IsoLeftSV\POL@IsoRightSV \ifnum\POL@IsoRight@Int < -\tw@ \repeat \ifnum\POL@isolz@IntervalIndex<\POL@isolz@NbOfNegRoots\space \def\POL@IsoRight@Int{-1}% \let\POL@IsoRightSign\POL@@IsoRightSign \let\POL@IsoRightSV\POL@@IsoRightSV \POL@isolz@check \ifnum\POL@isolz@IntervalIndex<\POL@isolz@NbOfNegRoots\space \def\POL@IsoLeft@Int{-1}% \let\POL@IsoLeftSign\POL@IsoRightSign \let\POL@IsoLeftSV\POL@IsoRightSV \def\POL@IsoRight@Int{0}% \let\POL@IsoRightSV\POL@IsoAtZeroSV % altered if 0 was a root \let\POL@IsoRightSign\POL@IsoAtZeroSign% id. % this will recurse to locate roots with smaller decimal exponents \POL@isolz@check % attention that this should not re-evaluate at 0 \fi \fi \fi \endgroup \def\POL@IsoLeft@Int{0}% \let\POL@IsoLeftSV \POL@IsoAtZeroSV \let\POL@IsoLeftSign\POL@IsoAtZeroSign \ifnum\POL@IsoLeftSign=\z@ \xdef\POL@isolz@IntervalIndex{\the\numexpr\POL@isolz@IntervalIndex+\@ne}% \global\POL@isolz@nextwillneedrefinetrue \else \global\POL@isolz@nextwillneedrefinefalse \fi \let\POL@@IsoRightSV \POL@isolz@plusinf@SV \let\POL@@IsoRightSign\POL@isolz@plusinf@sign % 10^E not a root! \edef\POL@isolz@NbOfPosRoots {\the\numexpr\POL@IsoLeftSV-\POL@@IsoRightSV}% attention @@ \ifnum\POL@isolz@NbOfPosRoots>\z@ \def\POL@IsoRight@Int{1}% \xintloop \edef\POL@isolz@E{\the\numexpr\POL@isolz@E-\@ne}% \POL@sturmchain@getSV@at\POL@IsoRight@raw \let\POL@IsoRightSV \POL@sturmchain@SV \let\POL@IsoRightSign\POL@sturmchain@sign \ifnum\POL@IsoRightSV=\POL@@IsoRightSV\space \let\POL@@IsoRightSign\POL@IsoRightSign % root here possible! \repeat \unless\ifnum\POL@IsoRightSV=\POL@IsoLeftSV\space \POL@isolz@check % will recurse inside groups if needed \fi \def\POL@IsoLeft@Int{1}% \let\POL@IsoLeftSV\POL@IsoRightSV \let\POL@IsoLeftSign\POL@IsoRightSign \xintloop % we could arguably do a more efficient dichotomy here \edef\POL@IsoRight@Int{\the\numexpr\POL@IsoLeft@Int+\@ne}% \POL@sturmchain@getSV@at\POL@IsoRight@raw \let\POL@IsoRightSV \POL@sturmchain@SV \let\POL@IsoRightSign\POL@sturmchain@sign \POL@isolz@check \let\POL@IsoLeft@Int\POL@IsoRight@Int \let\POL@IsoLeftSign\POL@IsoRightSign \let\POL@IsoLeftSV\POL@IsoRightSV \ifnum\POL@isolz@IntervalIndex=\POL@isolz@NbOfRoots\space \expandafter\xintbreakloop \fi \ifnum\POL@IsoLeft@Int < \xint_c_ix \repeat \ifnum\POL@isolz@IntervalIndex<\POL@isolz@NbOfRoots\space % get now the last, rightmost, root (or roots) \def\POL@IsoRight@Int{10}% \let\POL@IsoRightSign\POL@@IsoRightSign \let\POL@IsoRightSV\POL@@IsoRightSV \POL@isolz@check \fi \fi }% \def\POL@isolz@check{% \POL@IsoRightSign must be ready for use here % \ifxintverbose % \xintMessage{polexpr}{Info}% % {\the\numexpr\POL@IsoLeftSV-\POL@IsoRightSV\relax\space roots % in (\POL@IsoLeft@raw,\POL@IsoRight@raw] (E = \POL@isolz@E)}% % \fi \ifcase\numexpr\POL@IsoLeftSV-\POL@IsoRightSV\relax % no root in ]left, right] \global\POL@isolz@nextwillneedrefinefalse \or % exactly one root in ]left, right] \xdef\POL@isolz@IntervalIndex{\the\numexpr\POL@isolz@IntervalIndex+\@ne}% \ifnum\POL@IsoRightSign=\z@ % if right boundary is a root, ignore previous flag \global\POL@isolz@nextwillneedrefinefalse \fi % if left boundary is known to have been a root we refine interval \ifPOL@isolz@nextwillneedrefine \expandafter\expandafter\expandafter\POL@isolz@refine \else \POL@refine@storeleftandright \ifnum\POL@IsoRightSign=\z@ \global\POL@isolz@nextwillneedrefinetrue \fi \fi \else % more than one root, we need to recurse \expandafter\POL@isolz@recursedeeper \fi }% \def\POL@isolz@recursedeeper{% % NOTE 2018/02/16. I SHOULD DO A REAL BINARY DICHOTOMY HERE WHICH ON AVERAGE % SHOULD BRING SOME GAIN (LIKE WHAT IS ALREADY DONE FOR THE "refine" MACROS. % THUS IN FUTURE THIS MIGHT BE REFACTORED. \begingroup \edef\POL@isolz@E{\the\numexpr\POL@isolz@E-\@ne}% \edef\POL@@IsoRight@Int{\xintDSL{\POL@IsoRight@Int}}% \let\POL@@IsoRightSign \POL@IsoRightSign \let\POL@@IsoRightSV \POL@IsoRightSV \edef\POL@IsoLeft@Int {\xintDSL{\POL@IsoLeft@Int}}% \xintiloop[1+1] \edef\POL@IsoRight@Int{\xintInc{\POL@IsoLeft@Int}}% \POL@sturmchain@getSV@at\POL@IsoRight@raw \let\POL@IsoRightSV \POL@sturmchain@SV \let\POL@IsoRightSign\POL@sturmchain@sign \POL@isolz@check \let\POL@IsoLeft@Int\POL@IsoRight@Int \let\POL@IsoLeftSV\POL@IsoRightSV \let\POL@IsoLeftSign\POL@IsoRightSign% not used, actually \ifnum\POL@IsoLeftSV=\POL@@IsoRightSV\space \expandafter\xintbreakiloop \fi \ifnum\xintiloopindex < \xint_c_ix \repeat \let\POL@IsoRight@Int\POL@@IsoRight@Int \let\POL@IsoRightSign\POL@@IsoRightSign \let\POL@IsoRightSV \POL@@IsoRightSV % if we exited the loop via breakiloop this is superfluous % but it only costs one \ifnum \POL@isolz@check \endgroup }% \def\POL@isolz@refine{% % starting point is first root = left < unique second root < right % even if we hit exactly via refinement second root, we set flag false as % processing will continue with original right end-point, which isn't a root \global\POL@isolz@nextwillneedrefinefalse \begingroup \let\POL@@IsoRightSign\POL@IsoRightSign % already evaluated \xintloop \edef\POL@isolz@E{\the\numexpr\POL@isolz@E-\@ne}% \edef\POL@IsoLeft@Int {\xintDSL{\POL@IsoLeft@Int}}% \edef\POL@IsoRight@Int{\xintInc{\POL@IsoLeft@Int}}% \edef\POL@IsoRightSign {\xintiiSgn{\PolEval{\POL@sturmname _0}\At{\POL@IsoRight@raw}}}% \ifnum\POL@IsoRightSign=\POL@@IsoRightSign\space \repeat % now second root has been separated from the one at left end point % we update the storage of the root at left for it to have the same number % of digits in mantissa. No, I decided not to do that to avoid complications. % \begingroup % \let\POL@IsoRight@Int\POL@IsoLeft@Int % \def\POL@IsoRightSign{0}% % \edef\POL@isolz@IntervalIndex{\the\numexpr\POL@isolz@IntervalIndex-\@ne}% % \POL@refine@storeleftandright % \endgroup \edef\POL@@IsoRight@Int{\xintDSL{\xintInc{\xintDSR{\POL@IsoLeft@Int}}}}% \let\POL@IsoLeft@Int\POL@IsoRight@Int \let\POL@IsoLeftSign\POL@IsoRightSign \ifnum\POL@IsoRightSign=\z@ % check if new Left is actually a root \else \edef\POL@IsoRight@Int{\xintDec{\POL@@IsoRight@Int}}% \edef\POL@IsoRightSign {\xintiiSgn{\PolEval{\POL@sturmname _0}\At{\POL@IsoRight@raw}}}% \ifnum\POL@IsoRightSign=\POL@@IsoRightSign\space \POL@refine@doonce % we need to locate in interval (1, 9) in local scale \else \let\POL@IsoLeft@Int\POL@IsoRight@Int \ifnum\POL@IsoRightSign=\z@ \def\POL@IsoLeftSign{0}% \else \let\POL@IsoRight@Int\POL@@IsoRight@Int % the IsoRightSign is now wrong but here we don't care \fi\fi \fi % on exit, exact root found iff \POL@IsoRightSign is zero \POL@refine@storeleftandright \endgroup }% \def\POL@refine@doonce{% if exact root is found, always in IsoRight on exit % NOTE: FUTURE REFACTORING WILL GET RID OF \xintiiAdd WHICH ARE A BIT COSTLY % BUT BASICALLY NEEDED TO HANDLE BOTH NEGATIVE AND POSITIVE HERE. % I WILL RE-ORGANIZE THE WHOLE THING IN FUTURE TO GET ROOTS STARTING FROM % THE ORIGIN AND SIMPLY RE-LABEL THE NEGATIVE ONE AT THE END. 2018/02/16. \let\POL@@IsoRight@Int\POL@IsoRight@Int % 9 \let\POL@@IsoRightSign\POL@IsoRightSign \edef\POL@IsoRight@Int{\xintiiAdd{4}{\POL@IsoLeft@Int}}% 5 \edef\POL@IsoRightSign {\xintiiSgn{\PolEval{\POL@sturmname _0}\At{\POL@IsoRight@raw}}}% \ifnum\POL@IsoRightSign=\POL@IsoLeftSign\space \let\POL@IsoLeft@Int\POL@IsoRight@Int % 5 \edef\POL@IsoRight@Int{\xintiiAdd{2}{\POL@IsoLeft@Int}}% \edef\POL@IsoRightSign {\xintiiSgn{\PolEval{\POL@sturmname _0}\At{\POL@IsoRight@raw}}}% \ifnum\POL@IsoRightSign=\POL@IsoLeftSign\space \let\POL@IsoLeft@Int\POL@IsoRight@Int % 7 \edef\POL@IsoRight@Int{\xintInc{\POL@IsoLeft@Int}}% \edef\POL@IsoRightSign {\xintiiSgn{\PolEval{\POL@sturmname _0}\At{\POL@IsoRight@raw}}}% \ifnum\POL@IsoRightSign=\POL@IsoLeftSign\space \let\POL@IsoLeft@Int\POL@IsoRight@Int % 8 \let\POL@IsoRight@Int\POL@@IsoRight@Int % 9 \let\POL@IsoRightSign\POL@@IsoRightSign % opposite of one at left \fi % else 7, 8 with possible root at 8 \else \ifnum\POL@IsoRightSign=\z@ \let\POL@IsoLeft@Int\POL@IsoRight@Int % root at 7 \def\POL@IsoLeftSign{0}% \else \let\POL@@IsoRight@Int\POL@IsoRight@Int % 7 \edef\POL@IsoRight@Int{\xintInc{\POL@IsoLeft@Int}}% 6 \edef\POL@IsoRightSign {\xintiiSgn{\PolEval{\POL@sturmname _0}\At{\POL@IsoRight@raw}}}% \ifnum\POL@IsoRightSign=\POL@IsoLeftSign\space \let\POL@IsoLeft@Int\POL@IsoRight@Int % 6 \let\POL@IsoRight@Int\POL@@IsoRight@Int % 7 \let\POL@IsoRightSign\POL@@IsoRightSign \fi % else 5, 6 with possible root at 6 \fi\fi \else \ifnum\POL@IsoRightSign=\z@ \let\POL@IsoLeft@Int\POL@IsoRight@Int % root at 5 \def\POL@IsoLeftSign{0}% \else \let\POL@@IsoRight@Int\POL@IsoRight@Int % 5 \edef\POL@IsoRight@Int{\xintiiAdd{2}{\POL@IsoLeft@Int}}% \edef\POL@IsoRightSign {\xintiiSgn{\PolEval{\POL@sturmname _0}\At{\POL@IsoRight@raw}}}% \ifnum\POL@IsoRightSign=\POL@IsoLeftSign\space \let\POL@IsoLeft@Int\POL@IsoRight@Int % 3 \edef\POL@IsoRight@Int{\xintInc{\POL@IsoLeft@Int}}% 4 \edef\POL@IsoRightSign {\xintiiSgn{\PolEval{\POL@sturmname _0}\At{\POL@IsoRight@raw}}}% \ifnum\POL@IsoRightSign=\POL@IsoLeftSign\space \let\POL@IsoLeft@Int\POL@IsoRight@Int % 4 \let\POL@IsoRight@Int\POL@@IsoRight@Int % 5 \let\POL@IsoRightSign\POL@@IsoRightSign \fi % else 3, 4 with possible root at 4 \else \ifnum\POL@IsoRightSign=\z@ \let\POL@IsoLeft@Int\POL@IsoRight@Int % root at 3 \def\POL@IsoLeftSign{0}% \else \let\POL@@IsoRight@Int\POL@IsoRight@Int % 3 \edef\POL@IsoRight@Int{\xintInc{\POL@IsoLeft@Int}}% 2 \edef\POL@IsoRightSign {\xintiiSgn{\PolEval{\POL@sturmname _0}\At{\POL@IsoRight@raw}}}% \ifnum\POL@IsoRightSign=\POL@IsoLeftSign\space \let\POL@IsoLeft@Int\POL@IsoRight@Int % 2 \let\POL@IsoRight@Int\POL@@IsoRight@Int % 3 \let\POL@IsoRightSign\POL@@IsoRightSign \fi % else 1, 2 with possible root at 2 \fi\fi \fi\fi }% \def\POL@refine@storeleftandright{% \expandafter \xdef\csname POL_ZeroInt\POL@sturmname L\POL@isolz@IntervalIndex\endcsname {\PolDecToString{\POL@IsoLeft@rawout}}% \expandafter \xdef\csname POL_ZeroInt\POL@sturmname R\POL@isolz@IntervalIndex\endcsname {\PolDecToString{\POL@IsoRight@rawout}}% \begingroup\globaldefs\@ne \xintdefvar\POL@sturmname L_\POL@isolz@IntervalIndex:=qfrac(\POL@IsoLeft@rawout);% \xintdefvar\POL@sturmname R_\POL@isolz@IntervalIndex:=qfrac(\POL@IsoRight@rawout);% \endgroup }% %% \PolRefineInterval \def\POL@xintexprGetVar#1{\expandafter\expandafter\expandafter \XINT_expr_unlock\csname XINT_expr_var_#1\endcsname}% \def\POL@set@IsoLeft@rawin{% \edef\POL@IsoLeft@rawin {\POL@xintexprGetVar{\POL@sturmname L_\POL@isolz@IntervalIndex}}% }% \def\POL@set@IsoRight@rawin{% \edef\POL@IsoRight@rawin {\POL@xintexprGetVar{\POL@sturmname R_\POL@isolz@IntervalIndex}}% }% \def\POL@set@IsoLeft@Int #1/1[#2]{% \edef\POL@IsoLeft@Int{\xintDSH{\POL@isolz@E-#2}{#1}}% }% \newcommand\PolRefineInterval{\@ifstar\POL@srefine@start\POL@refine@start}% \newcommand\POL@refine@start[3][1]{% \edef\POL@isolz@IntervalIndex{\the\numexpr#3}% \edef\POL@sturmname{#2}% \expandafter\POL@refine@sharedbody\expandafter {\expandafter\POL@refine@loop\expandafter{\the\numexpr#1}}% }% \def\POL@srefine@start#1#2{% \edef\POL@isolz@IntervalIndex{\the\numexpr#2}% \edef\POL@sturmname{#1}% \POL@refine@sharedbody {\let\POL@refine@left@next\POL@refine@main % we want to recurse if needed \let\POL@refine@right@next\POL@refine@main % we want to recurse if needed \POL@refine@main}% }% \def\POL@refine@sharedbody#1{% \POL@set@IsoLeft@rawin \edef\POL@IsoLeftSign {\xintiiSgn{\PolEval{\POL@sturmname _0}\At{\POL@IsoLeft@rawin}}}% \ifnum\POL@IsoLeftSign=\z@ % do nothing if that interval was already a singleton \else % else both end-points are not roots and there is a single one in-between \POL@set@IsoRight@rawin \edef\POL@IsoRightSign{\the\numexpr-\POL@IsoLeftSign}% \edef\POL@isolz@E{\expandafter\POL@refine@getE % je pense que le xintrez ici est superflu \romannumeral0\xintrez{\xintSub{\POL@IsoRight@rawin}{\POL@IsoLeft@rawin}}}% \expandafter\POL@set@IsoLeft@Int\POL@IsoLeft@rawin \edef\POL@IsoRight@Int{\xintInc{\POL@IsoLeft@Int}}% #1% \POL@refine@storeleftandright \fi }% \def\POL@refine@loop#1{% \let\POL@refine@left@next \@empty % no recursion at end sub-intervals \let\POL@refine@right@next\@empty \xintiloop[1+1] \POL@refine@main \ifnum\POL@IsoRightSign=\z@ \expandafter\xintbreakiloop \fi \ifnum\xintiloopindex<#1 \repeat }% \def\POL@refine@main{% \edef\POL@isolz@E{\the\numexpr\POL@isolz@E-\@ne}% \edef\POL@IsoLeft@Int{\xintDSL{\POL@IsoLeft@Int}}% \edef\POL@IsoRight@Int{\xintDSL{\POL@IsoRight@Int}}% \let\POL@@IsoRight@Int\POL@IsoRight@Int \let\POL@@IsoRightSign\POL@IsoRightSign \edef\POL@IsoRight@Int{\xintInc{\POL@IsoLeft@Int}}% \edef\POL@IsoRightSign {\xintiiSgn{\PolEval{\POL@sturmname _0}\At{\POL@IsoRight@raw}}}% \ifnum\POL@IsoRightSign=\z@ \let\POL@IsoLeft@Int\POL@IsoRight@Int % root at 1 \def\POL@IsoLeftSign{0}% \let\POL@next\@empty \else \ifnum\POL@IsoRightSign=\POL@@IsoRightSign\space \let\POL@next\POL@refine@left@next % may be \@empty or \POL@refine@main for recursion \let\POL@refine@right@next\@empty \else \let\POL@IsoLeft@Int\POL@IsoRight@Int \edef\POL@IsoRight@Int{\xintDec{\POL@@IsoRight@Int}}% \edef\POL@IsoRightSign {\xintiiSgn{\PolEval{\POL@sturmname _0}\At{\POL@IsoRight@raw}}}% \ifnum\POL@IsoRightSign=\z@ \let\POL@IsoLeft@Int\POL@IsoRight@Int % root at 9 \def\POL@IsoLeftSign{0}% \let\POL@next\@empty \else \ifnum\POL@IsoRightSign=\POL@@IsoRightSign\space \let\POL@next\POL@refine@doonce \else \let\POL@IsoLeft@Int\POL@IsoRight@Int \let\POL@IsoRight@Int\POL@@IsoRight@Int \let\POL@IsoRightSign\POL@@IsoRightSign \let\POL@next\POL@refine@right@next \let\POL@refine@left@next\@empty \fi \fi \fi\fi \POL@next }% % lacking pre-defined xintfrac macro here (such as an \xintRawExponent) \def\POL@refine@getE#1[#2]{#2}% \xintREZ already applied, for safety \newcommand\PolIntervalWidth[2]{% % le \xintRez est à cause des E positifs, car trailing zéros explicites % si je travaillais à partir des variables xintexpr directement ne devrait % pas être nécessaire, mais trop fragile par rapport à chgt internes possibles \romannumeral0\xintrez{\xintSub{\@nameuse{POL_ZeroInt#1R}{#2}}% {\@nameuse{POL_ZeroInt#1L}{#2}}} }% \newcommand\PolEnsureIntervalLengths[2]{% #1 = Sturm chain name, % localize roots in intervals of length at most 10^{#2} \POL@count\z@ % \POL@count used by \POL@sturmchain@getSV@at but latter not used \edef\POL@sturmname{#1}% \edef\POL@ensure@targetE{\the\numexpr#2}% \edef\POL@nbofroots{\csname POL_ZeroInt\POL@sturmname L\endcsname 0}% \xintloop \advance\POL@count\@ne \edef\POL@isolz@IntervalIndex{\the\POL@count}% \POL@ensure@one \ifnum\POL@nbofroots>\POL@count \repeat }% \newcommand\PolEnsureIntervalLength[3]{% #1 = Sturm chain name, % #2 = index of interval % localize roots in intervals of length at most 10^{#3} \edef\POL@sturmname{#1}% \edef\POL@ensure@targetE{\the\numexpr#3}% \edef\POL@isolz@IntervalIndex{\the\numexpr#2}% \POL@ensure@one }% \def\POL@ensure@one{% \POL@set@IsoLeft@rawin \POL@set@IsoRight@rawin \edef\POL@ensure@delta{\xintREZ{\xintSub{\POL@IsoRight@rawin}{\POL@IsoLeft@rawin}}}% \xintiiifZero{\POL@ensure@delta} {} {\edef\POL@isolz@E{\expandafter\POL@refine@getE\POL@ensure@delta}% \expandafter\POL@set@IsoLeft@Int\POL@IsoLeft@rawin \edef\POL@IsoRight@Int{\xintInc{\POL@IsoLeft@Int}}% \ifnum\POL@isolz@E>\POL@ensure@targetE\space \edef\POL@IsoLeftSign {\xintiiSgn{\PolEval{\POL@sturmname _0}\At{\POL@IsoLeft@raw}}}% % at start left and right are not roots, and values of opposite signs % \edef\POL@IsoRightSign{\the\numexpr-\POL@IsoLeftSign}% \xintloop \POL@ensure@Eloopbody % decreases E by one at each iteration % if separation level is still too coarse we recurse at deeper level \ifnum\POL@isolz@E>\POL@ensure@targetE\space \repeat % will check if right is at a zero, needs \POL@IsoRightSign set up \POL@refine@storeleftandright \fi }% }% \def\POL@ensure@Eloopbody {% \edef\POL@isolz@E{\the\numexpr\POL@isolz@E-\@ne}% \edef\POL@IsoLeft@Int{\xintDSL{\POL@IsoLeft@Int}}% % this will loop at most ten times \xintloop \edef\POL@IsoRight@Int{\xintInc{\POL@IsoLeft@Int}}% \edef\POL@IsoRightSign {\xintiiSgn{\PolEval{\POL@sturmname _0}\At{\POL@IsoRight@raw}}}% % if we have found a zero at right boundary the \ifnum test will fail % and we exit the loop % else we exit the loop if sign at right boundary is opposite of % sign at left boundary (the latter is +1 or -1, never 0) % this is a bit wasteful if we go ten times to the right, because % we know that there the sign will be opposite, evaluation was superfluous \ifnum\POL@IsoLeftSign=\POL@IsoRightSign\space \let\POL@IsoLeft@Int\POL@IsoRight@Int \repeat % check for case when we exited the inner loop because we actually % found a zero, then we force exit from the main (E decreasing) loop \ifnum\POL@IsoRightSign=\z@ \expandafter\xintbreakloop \fi }% \catcode`_ 8 \newcommand\PolPrintIntervals[2][Z]{% \POL@count \@nameuse{POL_ZeroInt#2L}{0} \ifnum\POL@count=\z@ % No real roots.\par \else % There are \the\POL@count\space distinct real roots:\par \[\count@\POL@count \global\POL@count\@ne \begin{array}{rcccl} \xintloop \POL@SturmIfZeroExactlyKnown{#2}\POL@count {% exact root && #1_{\the\POL@count}&=& \POL@printintervals@prepare{#2R}% \PolPrintIntervalsPrintExactZero }% {% interval with root in its strict interior \POL@printintervals@prepare{#2L}% \PolPrintIntervalsPrintLeftEndPoint&<& #1_{\the\POL@count}&<& \POL@printintervals@prepare{#2R}% \PolPrintIntervalsPrintRightEndPoint }% \global\advance\POL@count\@ne \unless\ifnum\POL@count>\count@ \\% \repeat \end{array}\] \fi }% \catcode`_ 11 \newcommand\PolPrintIntervalsPrintExactZero {\PolPrintIntervalsTheEndPoint}% \newcommand\PolPrintIntervalsPrintLeftEndPoint {\PolPrintIntervalsTheEndPoint}% \newcommand\PolPrintIntervalsPrintRightEndPoint{\PolPrintIntervalsTheEndPoint}% \def\POL@printintervals@prepare#1{% \edef\PolPrintIntervalsTheIndex{\the\POL@count}% \edef\PolPrintIntervalsTheEndPoint{\@nameuse{POL_ZeroInt#1}\POL@count}% \xintiiifSgn{\POL@xintexprGetVar{#1_\PolPrintIntervalsTheIndex}} {\let\PolIfEndPointIsPositive\xint_secondoftwo \let\PolIfEndPointIsNegative\xint_firstoftwo \let\PolIfEndPointIsZero\xint_secondoftwo} {\let\PolIfEndPointIsPositive\xint_secondoftwo \let\PolIfEndPointIsNegative\xint_secondoftwo \let\PolIfEndPointIsZero\xint_firstoftwo} {\let\PolIfEndPointIsPositive\xint_firstoftwo \let\PolIfEndPointIsNegative\xint_secondoftwo \let\PolIfEndPointIsZero\xint_secondoftwo}% }% \newcommand\POL@SturmIfZeroExactlyKnown[2]{% faster than public one, % but does not check if #2 is in range \romannumeral0\xintifeq{\POL@xintexprGetVar{#1L_\the\numexpr#2\relax}}% {\POL@xintexprGetVar{#1R_\the\numexpr#2\relax}}% }% \newcommand\PolSturmIfZeroExactlyKnown[2]{% \romannumeral0\xintifeq{\PolSturmIsolatedZeroLeft{#1}{#2}}% {\PolSturmIsolatedZeroRight{#1}{#2}}% }% \newcommand\PolSturmIsolatedZeroLeft[2]{% \romannumeral`^^@\csname POL_ZeroInt#1L\endcsname{#2}}% \newcommand\PolSturmIsolatedZeroRight[2]{% \romannumeral`^^@\csname POL_ZeroInt#1R\endcsname{#2}}% \newcommand\PolSturmNbOfIsolatedZeros[1]{% \romannumeral`^^@\csname POL_ZeroInt#1L0\endcsname }% \let\PolDecToString\xintDecToString \newcommand\PolMakeMonic[1]{% \edef\POL@leadingcoeff{\PolLeadingCoeff{#1}}% \edef\POL@leadingcoeff@inverse{\xintDiv{1/1[0]}{\POL@leadingcoeff}}% \PolMapCoeffs{\xintMul{\POL@leadingcoeff@inverse}}{#1}% }% %% CORE ALGEBRA MACROS %% We do this non-expandably, but in a nestable way... this is the whole %% point because \xintdeffunc as used by \poldef creates a big nested macro. %% The idea is to execute it with another meaning given to \xintAdd etc.., %% so that it operates on "polynomials". This is a mixture of expandable %% and non-expandable techniques. \def\POL@get#1#2#3{% \global\POL@polfalse \begingroup \def\POL@result{#3}% #3% \expandafter \endgroup \expandafter\def\expandafter#1\expandafter{\POL@result}% \unless\ifPOL@pol % avoid expanding more than twice #3 \edef#1{#3}% \xintiiifZero{#1}% {\def#1{-1.\empty{0/1[0]}}}% {\edef#1{0.\noexpand\empty{#1}}}% \fi #2% }% %% ADDITION \def\POL@add {\POL@get\POL@A\POL@add@b}% \def\POL@add@b{\POL@get\POL@B\POL@add@c}% \def\POL@add@c{% \global\POL@poltrue \POL@ifZero\POL@A {\let\POL@result\POL@B}% {\POL@ifZero\POL@B {\let\POL@result\POL@A}% {\POL@@add}}% }% \def\POL@@add{% \expandafter\POL@split\POL@A;\POL@degA\POL@polA \expandafter\POL@split\POL@B;\POL@degB\POL@polB \ifnum\POL@degA>\POL@degB\relax \xintAssignArray\POL@polA\to\POL@arrayA \xintAssignArray\POL@polB\to\POL@arrayB \else \xintAssignArray\POL@polB\to\POL@arrayA \xintAssignArray\POL@polA\to\POL@arrayB \let\POL@tmp\POL@degB\let\POL@degB\POL@degA\let\POL@degA\POL@tmp \fi \count@\z@ \xintloop \advance\count@\@ne \expandafter\edef\csname POL@arrayA\the\count@\endcsname {\xintScalarAdd{\@nameuse{POL@arrayA\the\count@}}% {\@nameuse{POL@arrayB\the\count@}}}% \unless\ifnum\POL@degB<\count@ \repeat \count@\@nameuse{POL@arrayA0} % 1+\POL@degA % trim zero leading coefficients (we could check for equal degrees, % but would not bring much as anyhow loop exists immediately if not) \xintloop % this abuses that \POL@arrayA0 is never zero \xintiiifZero{\@nameuse{POL@arrayA\the\count@}}% {\iftrue}% {\iffalse}% \advance\count@\m@ne \repeat \POL@resultfromarray A% attention that \POL@arrayA0 not updated }% %% MULTIPLICATION \def\POL@mul {\POL@get\POL@A\POL@mul@b}% \def\POL@mul@b{\POL@get\POL@B\POL@mul@c}% \def\POL@mul@c{% \global\POL@poltrue \POL@ifZero\POL@A {\def\POL@result{-1.\empty{0/1[0]}}}% {\POL@ifZero\POL@B {\def\POL@result{-1.\empty{0/1[0]}}}% {\POL@@mul}}% }% \def\POL@@mul{% \expandafter\POL@split\POL@A;\POL@degA\POL@polA \expandafter\POL@split\POL@B;\POL@degB\POL@polB \ifnum\POL@degA>\POL@degB\relax \xintAssignArray\POL@polA\to\POL@arrayA \xintAssignArray\POL@polB\to\POL@arrayB \else \xintAssignArray\POL@polB\to\POL@arrayA \xintAssignArray\POL@polA\to\POL@arrayB \let\POL@tmp\POL@degB \let\POL@degB\POL@degA \let\POL@degA\POL@tmp \fi \count@\z@ \xintloop \POL@@mul@phaseIloopbody \unless\ifnum\POL@degB<\count@ \repeat \xintloop \unless\ifnum\POL@degA<\count@ % car attention au cas de mêmes degrés \POL@@mul@phaseIIloopbody \repeat \edef\POL@degC{\the\numexpr\POL@degA+\POL@degB}% \xintloop \unless\ifnum\POL@degC<\count@ \POL@@mul@phaseIIIloopbody \repeat %\count@\the\numexpr\POL@degC+\@ne\relax % never zero polynomial here \POL@resultfromarray C% }% \def\POL@@mul@phaseIloopbody{% \advance\count@\@ne \def\POL@tmp{0[0]}% \count\tw@\z@ \xintloop \advance\count\tw@\@ne \edef\POL@tmp{% \xintScalarAdd {\POL@tmp}% {\xintScalarMul {\@nameuse{POL@arrayA\the\count\tw@}}% {\@nameuse{POL@arrayB\the\numexpr\count@+\@ne-\count\tw@}}% }% }% \ifnum\count\tw@<\count@ \repeat \expandafter\let\csname POL@arrayC\the\count@\endcsname\POL@tmp }% \def\POL@@mul@phaseIIloopbody{% \advance\count@\@ne \def\POL@tmp{0[0]}% \count\tw@\count@ \advance\count\tw@-\@nameuse{POL@arrayB0} % \xintloop \ifnum\count\tw@<\count@ \advance\count\tw@\@ne \edef\POL@tmp{% \xintScalarAdd {\POL@tmp}% {\xintScalarMul {\@nameuse{POL@arrayA\the\count\tw@}}% {\@nameuse{POL@arrayB\the\numexpr\count@+\@ne-\count\tw@}}% }% }% \repeat \expandafter\let\csname POL@arrayC\the\count@\endcsname\POL@tmp }% \def\POL@@mul@phaseIIIloopbody{% \advance\count@\@ne \def\POL@tmp{0[0]}% \count\tw@\count@ \advance\count\tw@-\@nameuse{POL@arrayB0} % \xintloop \advance\count\tw@\@ne \edef\POL@tmp{% \xintScalarAdd{\POL@tmp}% {\xintScalarMul {\@nameuse{POL@arrayA\the\count\tw@}}% {\@nameuse{POL@arrayB\the\numexpr\count@+\@ne-\count\tw@}}% }% }% \ifnum\@nameuse{POL@arrayA0}>\count\tw@ \repeat \expandafter\let\csname POL@arrayC\the\count@\endcsname\POL@tmp }% %% POWERS (SCALAR EXPONENT...) \def\POL@pow #1#2{% \global\POL@polfalse \begingroup \def\POL@result{#1}% #1% \expandafter \endgroup \expandafter\def\expandafter\POL@A\expandafter{\POL@result}% \unless\ifPOL@pol \edef\POL@A{\xintScalarPow{#1}{#2}}% no error check \xintiiifZero{\POL@A}% {\def\POL@result{-1.\empty{0/1[0]}}}% {\edef\POL@result{0.\noexpand\empty{\POL@A}}}% \else \edef\POL@B{\numexpr\xintNum{#2}\relax}% no check on exponent >= 0 \ifcase\POL@B \def\POL@result{0.\empty{1/1[0]}}% \or \let\POL@result\POL@A \else \POL@@pow@check \fi \fi \global\POL@poltrue }% \def\POL@@pow@check {% % no problem here with leftover tokens! % should I have used that I-don't-care technique more elsewhere? \ifnum\@ne>\POL@A % polynomial is a constant, must get rid of dot and \empty \edef\POL@A{\expandafter\xintScalarPow\romannumeral`^^@% \expandafter\xint_gob_til_dot\POL@A{\POL@B}}% \xintiiifZero{\POL@A}% {\def\POL@result{-1.\empty{0/1[0]}}}% {\edef\POL@result{0.\noexpand\empty{\POL@A}}}% \else \ifnum\@ne=\POL@A % perhaps a constant times X, check constant term \xintiiifZero {\expandafter\xint_firstoftwo\romannumeral`^^@% \expandafter\xint_gob_til_dot\POL@A} {\edef\POL@result {\the\POL@B.% here at least 2. \noexpand\empty \romannumeral\xintreplicate{\POL@B}{{0/1[0]}}% {\xintScalarPow {\expandafter\xint_secondoftwo\romannumeral`^^@% \expandafter\xint_gob_til_dot\POL@A}% {\POL@B}}}}% {\POL@@pow}% not constant times X, use general recursion \else \POL@@pow% general recursion \fi\fi }% \def\POL@@pow@recurse#1#2{% \begingroup #1% \expandafter \endgroup \expandafter\def\expandafter\POL@A\expandafter{\POL@result}% \edef\POL@B{\numexpr\xintNum{#2}\relax}% \ifcase\POL@B \POL@thisshouldneverhappen \or \let\POL@result\POL@A \else \expandafter\POL@@pow \fi }% \def\POL@@pow {% \let\POL@pow@exp\POL@B \let\POL@B\POL@A \POL@@mul \let\POL@sqA\POL@result \ifodd\POL@pow@exp\space \expandafter\POL@@pow@odd \the\numexpr(\POL@pow@exp+\@ne)/\tw@-\@ne\expandafter.% \else \expandafter\POL@@pow@even \the\numexpr(\POL@pow@exp+\@ne)/\tw@-\@ne\expandafter.% \fi }% \def\POL@@pow@even#1.{% \expandafter\POL@@pow@recurse\expandafter {\expandafter\def\expandafter\POL@result\expandafter{\POL@sqA}}% {#1}% }% \def\POL@@pow@odd#1.{% \expandafter\POL@@pow@odd@i\expandafter{\POL@A}{#1}% }% \def\POL@@pow@odd@i #1#2{% \expandafter\POL@@pow@recurse\expandafter {\expandafter\def\expandafter\POL@result\expandafter{\POL@sqA}}% {#2}% \expandafter\POL@mul\expandafter {\expandafter\def\expandafter\POL@result\expandafter {\POL@result}\global\POL@poltrue}% {\def\POL@result{#1}\global\POL@poltrue}% }% %% DIVISION %% no check on divisor being non-zero \def\POL@div {\POL@get\POL@A\POL@div@b}% \def\POL@div@b{\POL@get\POL@B\POL@div@c}% \def\POL@div@c{% \global\POL@poltrue \expandafter\POL@split\POL@A;\POL@degA\POL@polA \expandafter\POL@split\POL@B;\POL@degB\POL@polB \ifnum\POL@degA<\POL@degB\space \@namedef{POL@arrayQ1}{0/1[0]}% \def\POL@degQ{-1}% \else \xintAssignArray\POL@polA\to\POL@arrayR \xintAssignArray\POL@polB\to\POL@arrayB \POL@@div \fi \count@\numexpr\POL@degQ+\@ne\relax \POL@resultfromarray Q% }% \def\POL@@div{% \xintAssignArray\POL@polA\to\POL@arrayR \xintAssignArray\POL@polB\to\POL@arrayB \edef\POL@B@leading{\csname POL@arrayB\the\numexpr\POL@degB+\@ne\endcsname}% \edef\POL@degQ{\the\numexpr\POL@degA-\POL@degB}% \count@\numexpr\POL@degA+\@ne\relax \count\tw@\numexpr\POL@degQ+\@ne\relax \xintloop \POL@@div@loopbody \ifnum\count\tw@>\z@ \repeat %%\expandafter\def\csname POL@arrayR0\endcsname{1}% \xintloop \xintiiifZero{\csname POL@arrayR\the\count@\endcsname}% {\iftrue}% {\iffalse}% \advance\count@\m@ne \repeat \edef\POL@degR{\the\numexpr\count@-\@ne}% }% \def\POL@@div@loopbody{% \edef\POL@@div@ratio{% \xintScalarDiv{\csname POL@arrayR\the\count@\endcsname}% {\POL@B@leading}}% \expandafter\let\csname POL@arrayQ\the\count\tw@\endcsname \POL@@div@ratio \advance\count@\m@ne \advance\count\tw@\m@ne \count4 \count@ \count6 \POL@degB\space \xintloop \ifnum\count6>\z@ \expandafter\edef\csname POL@arrayR\the\count4\endcsname {\xintScalarSub {\csname POL@arrayR\the\count4\endcsname}% {\xintScalarMul {\POL@@div@ratio}% {\csname POL@arrayB\the\count6\endcsname}}}% \advance\count4 \m@ne \advance\count6 \m@ne \repeat }% %% MINUS SIGN AS UNARY OPERATOR \def\POL@opp #1{% \global\POL@polfalse \begingroup \def\POL@result{#1}% #1% \expandafter \endgroup \expandafter\def\expandafter\POL@A\expandafter{\POL@result}% \unless\ifPOL@pol \edef\POL@A{\xintScalarOpp{#1}}% \xintiiifZero{\POL@A}% {\def\POL@result{-1.\empty{0/1[0]}}}% {\edef\POL@result{0.\noexpand\empty{\POL@A}}}% \else \edef\POL@B{0.\noexpand\empty{-1/1[0]}}% \POL@@mul \fi \global\POL@poltrue }% %% EXPANDABLE MACROS \def\Pol@Eval@fork#1\At#2#3\krof{#2}% \newcommand\PolEval[3]{\romannumeral`^^@\Pol@Eval@fork #2\PolEvalAt \At\PolEvalAtExpr\krof {#1}{#3}% }% \newcommand\PolEvalAt[2] {\xintpraw{\csname XINT_expr_userfunc_#1\endcsname{#2}}}% \newcommand\PolEvalAtExpr[2]{\xinttheexpr #1(#2)\relax}% % \newcommand\PolEvalReduced[3]{\romannumeral`^^@\Pol@Eval@fork #2\PolEvalReducedAt \At\PolEvalReducedAtExpr\krof {#1}{#3}% }% \newcommand\PolEvalReducedAt[2]{% \xintpraw % in order not to print denominator if the latter equals 1 {\xintIrr{\csname XINT_expr_userfunc_#1\endcsname{#2}}[0]}% }% \newcommand\PolEvalReducedAtExpr[2]{% \xintpraw {\xintIrr{\romannumeral`^^@\xintthebareeval#1(#2)\relax}[0]}% }% % \newcommand\PolFloatEval[3]{\romannumeral`^^@\Pol@Eval@fork #2\PolFloatEvalAt \At\PolFloatEvalAtExpr\krof {#1}{#3}% }% \newcommand\PolFloatEvalAt[2] {\xintpfloat{\csname XINT_flexpr_userfunc_#1\endcsname{#2}}}% \newcommand\PolFloatEvalAtExpr[2]{\xintthefloatexpr #1(#2)\relax}% % \newcommand\PolLeadingCoeff[1]{% \romannumeral`^^@\expandafter\expandafter\expandafter\xintlastitem \expandafter\expandafter\expandafter {\csname POLuserpol@#1\endcsname}% }% % \newcommand\PolNthCoeff[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? \newcommand\PolDegree[1]{\romannumeral`^^@\expandafter\expandafter\expandafter \POL@degree\csname POLuserpol@#1\endcsname;}% \def\POL@degree #1.#2;{#1}% % \newcommand\PolToList[1]{\romannumeral`^^@\expandafter\expandafter\expandafter \xint_gob_til_dot\csname POLuserpol@#1\endcsname}% % \newcommand\PolToCSV[1]{\romannumeral0\xintlistwithsep{, }{\PolToList{#1}}}% \newcommand\PolToExprCmd[1]{\xintPRaw{\xintRawWithZeros{#1}}}% \newcommand\PolToFloatExprCmd[1]{\xintFloat{#1}}% \let\PolToExprTermPrefix\PolTypesetCmdPrefix \newcommand\PolToExprOneTermStyleA[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^\xintiiAbs{#2}% \fi }% \let\PolToExprOneTerm\PolToExprOneTermStyleA \newcommand\PolToExprOneTermStyleB[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^\xintiiAbs{#2}% \fi \xintiiifOne{\xintDenominator{#1}}{}{/\xintDenominator{#1}}% }% \newcommand\PolToFloatExprOneTerm[2]{% \ifnum#2=\z@ \PolToFloatExprCmd{#1}% \else \PolToFloatExprCmd{#1}\PolToExprTimes \fi \ifcase\xintiiAbs{#2} %<-- space here mandatory \or\PolToExprVar \else\PolToExprVar^\xintiiAbs{#2}% \fi }% \newcommand\PolToExprTimes{*}% \newcommand\PolToExprVar{x}% \newcommand\PolToExpr[1]{% \if*\noexpand#1\expandafter\xint_firstoftwo\else \expandafter\xint_secondoftwo\fi \PolToExprAscending\PolToExprDescending{#1}}% \newcommand\PolToFloatExpr[1]{% \if*\noexpand#1\expandafter\xint_firstoftwo\else \expandafter\xint_secondoftwo\fi \PolToFloatExprAscending\PolToFloatExprDescending{#1}}% \newcommand\PolToExprAscending[2]{% \expandafter\POL@toexpr\csname POLuserpol@#2\endcsname \PolToExprOneTerm\POL@toexprA}% \newcommand\PolToFloatExprAscending[2]{% \expandafter\POL@toexpr\csname POLuserpol@#2\endcsname \PolToFloatExprOneTerm\POL@toexprA}% \newcommand\PolToExprDescending[1]{% \expandafter\POL@toexpr\csname POLuserpol@#1\endcsname \PolToExprOneTerm\POL@toexprD}% \newcommand\PolToFloatExprDescending[1]{% \expandafter\POL@toexpr\csname POLuserpol@#1\endcsname \PolToFloatExprOneTerm\POL@toexprD}% % \def\POL@toexpr#1#2#3{\expandafter\POL@toexpr@ \expandafter#3\expandafter#2#1\relax}% \def\POL@toexpr@#1#2#3.{% \ifnum#3<\z@ #2{0/1[0]}{0}\expandafter\xint_gobble_v \else \expandafter#1% \fi {#3}#2}% % \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.% }% \POL@restorecatcodes \endinput