summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/latex/polexpr
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2018-01-12 22:35:39 +0000
committerKarl Berry <karl@freefriends.org>2018-01-12 22:35:39 +0000
commitcb8ebf13b33bc2e0f8c2e4ffa30f0cd0ba783828 (patch)
tree3ff40c3ba44b07bf32d4ea2b1c9f40bfc6313ea4 /Master/texmf-dist/tex/latex/polexpr
parent39cc3fec95f3782316910590bb86993918c9dcdf (diff)
polexpr (12jan18)
git-svn-id: svn://tug.org/texlive/trunk@46291 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/latex/polexpr')
-rw-r--r--Master/texmf-dist/tex/latex/polexpr/polexpr.sty1009
1 files changed, 1009 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/latex/polexpr/polexpr.sty b/Master/texmf-dist/tex/latex/polexpr/polexpr.sty
new file mode 100644
index 00000000000..983016987ea
--- /dev/null
+++ b/Master/texmf-dist/tex/latex/polexpr/polexpr.sty
@@ -0,0 +1,1009 @@
+% author: Jean-François Burnol
+% License: LPPL 1.3c (author-maintained)
+\ProvidesPackage{polexpr}%
+ [2018/01/11 v0.1 Polynomial expressions with rational coefficients (JFB)]%
+\RequirePackage{xintexpr}[2016/03/19]% xint 1.2g (or 1.2c 2015/11/16 at least)
+\edef\POL@restorecatcodes
+ {\catcode`\noexpand\_ \the\catcode`\_ \catcode0 \the\catcode0\relax}%
+\catcode`\_ 11 \catcode0 12
+
+%% AUXILIARIES
+\newif\ifPOL@pol % (cf core algebra macros)
+%% the main exchange structure (stored in macros \POLuserpol@<name>)
+%% 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 \empty token is to avoid brace removal issues for degree 0 polynomials
+ {\def#3{#1}\expandafter\def\expandafter#4\expandafter{#2}}%
+%
+\def\POL@resultfromarray #1{% ATTENTION, **MUST** be executed with
+% \count@ set to 1 + degree (thus \count@ = 0 for zero polynomial)
+ \edef\POL@result{%
+ \the\numexpr\count@-\@ne.\noexpand\empty
+ \xintiloop [1+1]%
+% always done at least once with index 1, hence ok for zero polynomial
+ \expandafter\POL@braceit\csname POL@array#1\xintiloopindex\endcsname
+ \ifnum\xintiloopindex<\count@
+ \repeat}%
+}%
+\def\POL@braceit#1{{#1}}% needed as \xintiloopindex can not "see" through braces
+
+
+\newif\ifxintveryverbose
+\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
+ \let\POL@original_redefinemacros\XINT_expr_redefinemacros
+ \let\XINT_expr_redefinemacros\POL@redefinemacros
+ \edef\POL@tmp{\ifxintverbose1\else0\fi}%
+ \unless\ifxintveryverbose\xintverbosefalse\fi
+ \xintdeffunc __pol(#2):=#4;\if1\POL@tmp\xintverbosetrue\fi
+ \let\XINT_expr_redefinemacros\POL@original_redefinemacros
+ \edef\POL@polname{\xint_zapspaces #1 \xint_gobble_i}%
+ \begingroup
+ \setbox0\hbox{%
+ \let\xintScalarAdd\xintAdd
+ \let\XINT_fadd_C\POL_fadd_C % patch Add to use l.c.m.
+ \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 to get \POL@result defined even if numerical only expression
+ \xintAdd{0}%
+ {\csname XINT_expr_userfunc___pol\endcsname
+% comma delimited from xintexpr at 1.2p or earlier. Might change.
+ {\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}}%
+ \unless\ifcsname XINT_expr_userfuncNE:#1\endcsname\POL@addtoextras{#1}\fi
+ \expandafter\XINT_expr_defuserfunc
+ \csname XINT_expr_func_#1\expandafter\endcsname
+ \csname XINT_expr_userfunc_#1\endcsname
+ \ifxintverbose\POL@info{#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@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
+ \endgroup
+ \expandafter\let\csname XINT_expr_userfunc_#1\endcsname\POL@tmp
+}%
+\def\POL@newpol@horner#1{\let\xintAdd\relax\let\xintMul\relax
+ \gdef\POL@tmp##1,{#1}\POL@newpol@horner@loop.}%
+\def\POL@newpol@horner@loop.#1{%
+ \if\relax#1\expandafter\xint_gob_til_dot\fi
+ \xdef\POL@tmp##1,{\xintiiifZero{#1}{\@firstofone}{\xintAdd{#1}}%
+ {\xintMul{##1}{\POL@tmp##1,}}}%
+ \POL@newpol@horner@loop.%
+}%
+%% Customizes xintexpr.sty's \XINT_NewExpr (\POL@addtoextras{name})
+\begingroup
+\catcode`~ 12
+\catcode`$ 12 % $
+\catcode`! 11
+\gdef\POL@NEfork_one #1#2!#3#4{%
+ \if ###1\xint_dothis {\POL__settopol{#4}}\fi
+ \if ~#1\xint_dothis {\POL__userfunc{#4}}\fi
+ \if $#1\xint_dothis {~xintApply::csv{~POL_userfunc{#4}}}\fi %$
+ \xint_orthat {#3}#1#2\endcsname
+}%
+\gdef\POL@@redefineone #1#2#3{% #3 = name
+ % Used for immediate f(numerical)
+ \let#2#1%
+ % \XINT_expr_userfunc_name
+ \def#1##1\endcsname % key trick is to fetch up to \endcsname!
+ {\expandafter\POL@NEfork_one\romannumeral`^^@##1!#2{#3}}%
+}%
+ % Used when f(x) is encountered: great gain here!
+\gdef\POL__settopol#1#2,{~POL_settopol{#1}}%
+\gdef\POL_settopol#1{\global\POL@poltrue\expandafter\let\expandafter
+ \POL@result\csname POLuserpol@#1\endcsname}%
+ % Used when argument is neither numerical nor a macro parameter
+ % Quite some magic here! (braces couldn't be used this way in \xintexpr)
+\gdef\POL__userfunc#1#2,\endcsname{~POL_userfunc{#1}{#2},\endcsname}%
+\gdef\POL_userfunc#1{\csname XINT_expr_userfunc_#1\endcsname}%
+\endgroup
+\def\POL@addtoextras#1{%
+ \oodef\POL@redefineextras{\expandafter\POL@redefineextras
+ \expandafter\POL@@redefineone
+ \csname XINT_expr_userfunc_#1\expandafter\endcsname
+ \csname XINT_expr_userfuncNE:#1\endcsname{#1}}%
+}%
+%\let\POL@original@redefinemacros\XINT_expr_redefinemacros % do locally
+\def\POL@redefinemacros{\POL@original_redefinemacros\POL@redefineextras}%
+\let\POL@redefineextras\@empty
+%
+\newcommand\PolLet[2]{%
+ \expandafter\let\csname POLuserpol@#1\expandafter\endcsname
+ \csname POLuserpol@#2\endcsname
+ \unless\ifcsname XINT_expr_userfuncNE:#1\endcsname\POL@addtoextras{#1}\fi
+ \expandafter\let\csname XINT_expr_userfunc_#1\expandafter\endcsname
+ \csname XINT_expr_userfunc_#2\endcsname
+ \expandafter\XINT_expr_defuserfunc
+ \csname XINT_expr_func_#1\expandafter\endcsname
+ \csname XINT_expr_userfunc_#1\endcsname
+ \ifxintverbose\POL@info{#1}\fi
+}%
+
+
+\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[1]{\def\POL@polname{#1}% zap spaces in #1?
+ \begingroup % closed in \POL@getfrom
+ \POL@getfrom}%
+% attention au name clash proche avec \POL@get auxiliaire de \POL@add etc..
+\def\POL@getfrom#1\fromarray#2{%
+ \count@#2{0} % must be > 0, else could create infinite loop
+ % \ifnum\count@>\z@\else\InvalidArrayError_PolGet\fi
+ \xintloop
+ \edef\POL@tmp{#2{\count@}}%
+ \xintiiifZero{\POL@tmp}%
+ {\iftrue}%
+ {\iffalse}%
+ \advance\count@\m@ne
+ \repeat
+% should I use \xintRaw ? but if #2 expands only in an \edef, problem
+% (but it is not very probable the #2 macro does not already give completely
+% expanded contents), I would need to proceed in two steps. Or the \xintRaw
+% could get injected at level of \POL@newpol
+ \def\POL@tmp##1.{{#2{##1}}}%
+ \edef\POL@result{\the\numexpr\count@-\@ne.\noexpand\empty
+ \xintiloop[1+1]%
+ \expandafter\POL@tmp\xintiloopindex.%
+ \ifnum\xintiloopindex<\count@
+ \repeat}%
+ \expandafter
+ \endgroup
+ \expandafter
+ \def\csname POLuserpol@\POL@polname\expandafter\endcsname
+ \expandafter{\POL@result}%
+ \expandafter\POL@newpol\expandafter{\POL@polname}%
+}%
+
+
+\newcommand\PolFromCSV[2]{\def\POL@polname{#1}%
+ \begingroup % closed in \POL@getfrom
+ \xintAssignArray\xintCSVtoList{#2}\to\POL@arrayA
+ \POL@getfrom\fromarray\POL@arrayA
+% semble un peu indirect et sous-optimal
+% mais je veux élaguer les coefficients nuls. Peut-être à revoir.
+}%
+
+
+\newif\ifpoltypesetall
+\newcommand\PolTypesetPlus[1]{\xintiiifSgn{#1}{}{+}{+}}%
+\newcommand\PolTypesetCmd[1]{\xintifOne{\xintiiAbs{#1}}%
+ {\ifnum\PolIndex=\z@\xintiiSgn{#1}\else
+ \xintiiifSgn{#1}{-}{}{}\fi}%
+ {\xintSignedFrac{#1}}}%
+\newcommand\PolTypesetMonomial{%
+ \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]}%
+ \else
+ \ifnum\POL@var@deg=\z@
+ \expandafter\PolTypesetCmd\POL@var@coeffs
+ \else
+ \def\POL@ts@plus##1{\let\POL@ts@plus\PolTypesetPlus}%
+ \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@plus{#1}\PolTypesetCmd{#1}\PolTypesetMonomial}%
+ \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@plus{#1}\PolTypesetCmd{#1}\PolTypesetMonomial
+ \edef\PolIndex{\the\numexpr\PolIndex+\POL@@ne@or@m@ne}\POL@ts@nocheck.%
+}%
+
+
+\newcommand\PolMapCoeffs[2]{% #1 = macro, #2 = name
+ \begingroup
+ \def\POL@map@macro{#1}%
+ \expandafter\expandafter\expandafter\POL@split
+ \csname POLuserpol@#2\endcsname;\POL@var@deg\POL@var@coeffs
+% attention à ne pas faire un \expandafter ici, car brace removal si 1 item
+ \xintAssignArray\POL@var@coeffs\to\POL@arrayA
+ \def\index{0}%
+ \count@\z@
+ \expandafter\POL@map@loop\expandafter.\POL@var@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}%
+ \POL@newpol{#2}%
+}%
+\def\POL@map@loop.#1{\if\relax#1\expandafter\xint_gob_til_dot\fi
+ \advance\count@\@ne
+ \edef\POL@map@coeff{\POL@map@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[1]{\PolMapCoeffs{\POL@xintIrr}{#1}}%
+
+
+%% EUCLIDEAN DIVISION
+\newcommand\PolDivide[4]{% #3=quotient, #4=remainder of #1 by #2
+ \begingroup
+ \let\xintScalarSub\xintSub
+ \let\XINT_fadd_C\POL_fadd_C
+ \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 POLuserpol@#3\expandafter\expandafter\expandafter\endcsname
+ \expandafter\expandafter\expandafter{\expandafter\POL@Q\expandafter}%
+ \expandafter
+ \def\csname POLuserpol@#4\expandafter\endcsname\expandafter{\POL@R}%
+ \POL@newpol{#3}%
+ \POL@newpol{#4}%
+}%
+
+
+%% GCD
+\newcommand\PolGCD[3]{% sets #3 to the (unitary) G.C.D. of #1 and #2
+ \begingroup
+ \let\xintScalarSub\xintSub
+ \let\XINT_fadd_C\POL_fadd_C
+ \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@degB
+ \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}%
+ \POL@newpol{#3}%
+}%
+\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{\PolLet{#2}{#1}}%
+\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
+ \POL@newpol{#2}%
+}%
+% 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{%
+ \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
+ \POL@newpol{#2}%
+}%
+
+
+%% CORE ALGEBRA MACROS
+%% We do this non-expandably, but in a nestable way... this is the whole
+%% point because \xintdeffunc as used by \PolNewPol 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
+\newcommand\PolEval{}%
+\def\PolEval#1#2\At#3{\romannumeral`^^@\xinttheexpr reduce(#1(#3))\relax}%
+%
+\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}}}%
+
+
+\newif\ifpoltoexprall
+\newcommand\PolToExprCmd[1]{\xintPRaw{\xintRawWithZeros{#1}}}%
+\newcommand\PolToExprCmdWithSep[1]{\xintiiifSgn{#1}{}{+}{+}\PolToExprCmd{#1}}%
+\newcommand\PolToExpr[1]{\expandafter\expandafter\expandafter
+ \POL@toexpr\csname POLuserpol@#1\endcsname\relax}%
+%
+\def\POL@toexpr#1.\empty{%
+ \ifnum#1<\z@
+ \PolToExprCmd{0/1[0]}\expandafter\@gobbletwo
+ \else
+ \expandafter\POL@toexpr@a
+ \fi}%
+%
+\def\POL@toexpr@a #1{\ifpoltoexprall\expandafter\POL@toexprall@b
+ \else\expandafter\POL@toexpr@b
+ \fi{#1}{}\PolToExprCmd0.}%
+%
+\def\POL@toexpr@b #1#2#3{%
+ \xintiiifZero{#1}%
+ {\expandafter\POL@toexpr@loop\expandafter#3}%
+ {#3{#1}#2\expandafter\POL@toexpr@loop\expandafter\PolToExprCmdWithSep}%
+ \expandafter\POL@toexpr@b\the\numexpr\@ne+%
+}%
+\def\POL@toexprall@b #1#2#3{%
+ #3{#1}#2%
+ \expandafter\POL@toexpr@loop\expandafter\PolToExprCmdWithSep
+ \expandafter\POL@toexprall@b\the\numexpr\@ne+%
+}%
+\def\POL@toexpr@loop#1#2#3.#4{%
+ \if\relax#4\expandafter\xint_gob_til_dot\fi #2{#4}{*x^#3}#1#3.%
+}%
+
+
+%% Patch of xintfrac.sty's \xintAdd: for a/b + c/d, use lcm(b,d)
+\RequirePackage{xintgcd}
+\def\POL_fadd_C #1#2#3%
+{%
+ \expandafter\POL_fadd_D
+ \romannumeral0\xintiigcd{#2}{#3}.%
+ {#2}{#3}{#1}%
+}%
+\def\POL_fadd_D #1.#2#3%
+{%
+ \expandafter\POL_fadd_E
+ \romannumeral0\xintiiquo{#3}{#1}.%
+ {\romannumeral0\xintiiquo{#2}{#1}}{#2}{#3}%
+}%
+\def\POL_fadd_E #1.#2#3#4#5%
+{%
+ \expandafter\POL_fadd_F\romannumeral0\xintiimul{#1}{#3}.{#2}%
+ {\xintiiMul{#1}{#5}}%
+}%
+\def\POL_fadd_F #1.#2#3#4%
+{%
+ \expandafter\POL_fadd_G
+ \romannumeral0\xintiiadd{#3}{\xintiiMul{#2}{#4}}/#1%
+}%
+\def\POL_fadd_G #1{%
+\def\POL_fadd_G ##1{\if0##1\expandafter\XINT_fadd_iszero\fi#1##1}%
+}\POL_fadd_G{ }%
+
+\POL@restorecatcodes
+\endinput