summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/generic/polexpr/polexprsturm.tex
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2021-04-10 21:13:16 +0000
committerKarl Berry <karl@freefriends.org>2021-04-10 21:13:16 +0000
commitf83ae24524b685073a4bd225d9a814e1b5850daf (patch)
treeb75236a60f69657356a312a2cb911cce756819ee /Master/texmf-dist/tex/generic/polexpr/polexprsturm.tex
parent89f4f0573f09dfd63100d90c8d80d1da160e3550 (diff)
polexpr (now generic)
git-svn-id: svn://tug.org/texlive/trunk@58821 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/generic/polexpr/polexprsturm.tex')
-rw-r--r--Master/texmf-dist/tex/generic/polexpr/polexprsturm.tex1775
1 files changed, 1775 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/generic/polexpr/polexprsturm.tex b/Master/texmf-dist/tex/generic/polexpr/polexprsturm.tex
new file mode 100644
index 00000000000..3fa1861558d
--- /dev/null
+++ b/Master/texmf-dist/tex/generic/polexpr/polexprsturm.tex
@@ -0,0 +1,1775 @@
+%% This file polexprsturm.tex is part of the polexpr package (0.8, 2021/03/29)
+%% Sturm Algorithm (polexpr 0.4)
+%% 0.5 uses primitive polynomials for faster evaluations afterwards
+%% 0.6 corrects misuse of \@ifstar! (mumble). \PolToSturm* was broken.
+%% 0.6's \PolToSturm* defines both normalized and unnormalized, the
+%% unnormalized using two underscores, so both are available
+%% Sole difference is that \PolToSturm* also declares them as
+%% user polynomials, whereas the non-starred only keeps the macros
+%% holding the coefficients in memory
+%% 0.6 fixes the case of a constant polynomial P which caused division
+%% by zero error from P'.
+%% 0.8 - fixes 0.7.5 failure to have updated to xint 1.4 format the defined
+%% \xintexpr variables holding the localization intervals extremities
+%% - also, it uses the prem() in computing the Sturm chain, for a 3X
+%% speed gain in the case of the "perturbed" first Wilkinson example
+%%
+\newcount\POL@count
+\newif\ifPOL@tosturm@makefirstprimitive\POL@tosturm@makefirstprimitivetrue
+\newif\ifPOL@isolz@nextwillneedrefine
+%%
+\def\PolToSturm{\POL@ifstar{\PolToSturm@@}{\PolToSturm@}}%
+\def\POL@aux@toint#1{\xintREZ{\xintNum{#1}}}% for polynomials with int. coeffs!
+%% Attention that some macros rely upon this one setting \POL@sturmname
+%% and \POL@sturm@N as it does
+\def\PolToSturm@#1#2{%
+ \edef\POL@sturmname{#2}%
+ % 0.6 uses 2 underscores (one before index, one after) to keep in memory
+ % the unnormalized chain
+ % This supposes #1 to be a genuine polynomial, not only a name with
+ % a \POLuserpol@#1 macro
+ \POL@let{\POL@sturmname _0_}{#1}%
+ \ifnum\PolDegree{#1}=\z@
+ \def\POL@sturm@N{0}%
+ \POL@count\z@
+ % if I applied the same as for positive degree, I should make it -1
+ % if constant is negative. I also don't worry if polynomial is zero.
+ \XINT_global\@namedef{POLuserpol@\POL@sturmname _0}{0.\empty{1/1[0]}}%
+ \else
+ \ifPOL@tosturm@makefirstprimitive\POL@makeprimitive{\POL@sturmname _0_}\fi
+ \POL@tosturm@dosturm
+ \fi
+ \expandafter
+ \let\csname PolSturmChainLength_\POL@sturmname\endcsname\POL@sturm@N
+ % declare the normalized ones as full-fledged polynomials
+ % \POL@count\z@
+ \xintloop
+ \POL@newpol{\POL@sturmname _\the\POL@count}%
+ \unless\ifnum\POL@sturm@N=\POL@count
+ \advance\POL@count\@ne
+ \repeat
+}%
+\def\PolToSturm@@#1#2{\PolToSturm@{#1}{#2}\POL@tosturm@declareunnormalized}%
+\def\POL@tosturm@declareunnormalized{%
+ % optionally declare also the unnormalized ones
+ \POL@count\z@
+ \xintloop
+ \POL@newpol{\POL@sturmname _\the\POL@count _}%
+ \unless\ifnum\POL@sturm@N=\POL@count
+ \advance\POL@count\@ne
+ \repeat
+}%
+\def\POL@tosturm@dosturm{%
+ \POL@Diff@@one{\POL@sturmname _0_}{\POL@sturmname _1_}%
+ % re-utiliser \POL@varcoeffs directement?
+ \POL@makeprimitive{\POL@sturmname _1_}% does not do \POL@newpol
+ \POL@count\@ne
+ \xintloop
+ % prior to 0.8, code was using here \POL@divide
+ \POL@getprem{\POL@sturmname _\the\numexpr\POL@count-\@ne\relax _}%
+ {\POL@sturmname _\the\POL@count _}%
+ \expandafter\POL@split\POL@R;\POL@degR\POL@polR
+ \unless\ifnum\POL@degR=\m@ne
+ \advance\POL@count\@ne
+ \XINT_global\expandafter\let
+ \csname POLuserpol@\POL@sturmname _\the\POL@count _\endcsname\POL@R
+ \edef\POL@makeprim@icontent{-\POL@icontent\POL@polR}%
+ % this avoids the \POL@newpol from \PolMapCoeffs
+ \POL@mapcoeffs\POL@makeprim@macro{\POL@sturmname _\the\POL@count _}%
+ \repeat
+ \edef\POL@sturm@N{\the\POL@count}%
+ % normalize (now always done even by starred variant)
+ \ifnum\PolDegree{\POL@sturmname _\POL@sturm@N _}>\z@
+ % \POL@count\POL@sturm@N\relax
+ \xintloop
+ \advance\POL@count\m@ne
+ \POL@divide{\POL@sturmname _\the\POL@count _}%
+ {\POL@sturmname _\POL@sturm@N _}%
+ \XINT_global\expandafter
+ \let\csname POLuserpol@\POL@sturmname _\the\POL@count\endcsname\POL@Q
+ % quotient actually belongs to Z[X] and is primitive
+ \POL@mapcoeffs\POL@aux@toint{\POL@sturmname _\the\POL@count}%
+ \ifnum\POL@count>\z@
+ \repeat
+ \XINT_global\@namedef{POLuserpol@\POL@sturmname _\POL@sturm@N}{0.\empty{1/1[0]}}%
+ \else % they are already normalized
+ \advance\POL@count\@ne % attention to include last one also
+ \xintloop
+ \advance\POL@count\m@ne
+ \XINT_global\expandafter\let
+ \csname POLuserpol@\POL@sturmname _\the\POL@count\expandafter\endcsname
+ \csname POLuserpol@\POL@sturmname _\the\POL@count _\endcsname
+ \ifnum\POL@count>\z@
+ \repeat
+ \fi
+ % Back to \PolToSturm@, \POL@count holds 0
+}%
+\def\PolSturmChainLength#1{%
+ \romannumeral`&&@\csname PolSturmChainLength_#1\endcsname
+}%
+\def\PolSetToSturmChainSignChangesAt{%
+ \POL@chkopt\POL@oPolSetToSturmChainSignChangesAt[\global]%
+}%
+\def\POL@oPolSetToSturmChainSignChangesAt[#1]#2#3#4{%
+ \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{\POL@eval{\POL@sturmname _0}{#1}}}%
+ \let\POL@isolz@lastsign\POL@sturmchain@sign
+ \POL@count \z@
+ \ifnum\POL@isolz@lastsign=\z@
+ \edef\POL@isolz@lastsign
+ {\xintiiSgn{\POL@eval{\POL@sturmname _1}{#1}}}%
+ \POL@count \@ne
+ \fi
+ \xintloop
+ \unless\ifnum\POL@sturmlength=\POL@count
+ \advance\POL@count \@ne
+ \edef\POL@isolz@newsign
+ {\xintiiSgn{\POL@eval{\POL@sturmname _\the\POL@count}{#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
+}%
+\def\PolSetToNbOfZerosWithin{%
+ \POL@chkopt\POL@oPolSetToNbOfZerosWithin[\global]%
+}%
+\def\POL@oPolSetToNbOfZerosWithin[#1]#2#3#4#5{%
+ \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
+}%
+% 0.6 added starred variant to count multiplicities
+% 0.7 added double starred variant to locate all rational roots
+\def\PolSturmIsolateZeros{\POL@ifstar
+ {\PolSturmIsolateZerosAndGetMultiplicities}%
+ {\PolSturmIsolateZeros@}%
+}%
+\def\PolSturmIsolateZerosAndGetMultiplicities{\POL@ifstar
+ {\PolSturmIsolateZerosGetMultiplicitiesAndRationalRoots}%
+ {\PolSturmIsolateZerosAndGetMultiplicities@}%
+}%
+% on aurait besoin de ça dans xint, mais il aurait un \xintRaw{#1} alors
+\def\POL@xintfrac@getNDE #1%
+ {\expandafter\POL@xintfrac@getNDE@i\romannumeral`&&@#1}%
+\def\POL@xintfrac@getNDE@i #1/#2[#3]#4#5#6{\def#4{#1}\def#5{#2}\def#6{#3}}%
+%
+\def\PolSturmIsolateZerosGetMultiplicitiesAndRationalRoots{%
+ \POL@chkopt\POL@oPolSturmIsolateZerosGetMultiplicitiesAndRationalRoots[\empty]%
+}%
+\def\POL@oPolSturmIsolateZerosGetMultiplicitiesAndRationalRoots[#1]#2{%
+ \PolSturmIsolateZerosAndFindRationalRoots[#1]{#2}%
+ \ifnum\POL@isolz@NbOfRoots>\z@
+ % get multiplicities of irrational (real) roots, if any
+ \ifnum\POL@findrat@nbofirrroots>\z@
+ \POL@findrat@getirrmult
+ \fi
+ \POL@isolzmult@defvar@M
+ \fi
+}%
+% added at 0.7
+\def\PolSturmIsolateZerosAndFindRationalRoots{%
+ \POL@chkopt\POL@oPolSturmIsolateZerosAndFindRationalRoots[\empty]%
+}%
+\def\POL@oPolSturmIsolateZerosAndFindRationalRoots[#1]#2{%
+ % #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)
+ \edef\POL@sturmname{#2}%
+ \edef\POL@sturm@N{\@nameuse{PolSturmChainLength_\POL@sturmname}}%
+ % isolate the roots (detects case of constant polynomial)
+ \PolSturmIsolateZeros@{\POL@sturmname}%
+ \ifnum\POL@isolz@NbOfRoots=\z@
+ % no real roots, define empty arrays nevertheless
+ \begingroup\globaldefs\@ne
+ \expandafter\xintAssignArray\expandafter\to\csname POL_ZM\POL@sturmname*\endcsname
+ \expandafter\xintAssignArray\expandafter\to\csname POL_RI\POL@sturmname*\endcsname
+ \endgroup
+ \else
+ % all we currently know is that multiplicities are at least one
+ \begingroup\globaldefs\@ne
+ \expandafter\POL@initarray\csname POL_ZM\POL@sturmname*\endcsname{1}%
+ \endgroup
+ % on ne va pas utiliser de Horner, mais des divisions par X - x, et ces
+ % choses vont évoluer, ainsi que le coefficient dominant entier
+ % (pour \POL@divide entre autres if faut des noms de user pol)
+ \XINT_global
+ \expandafter\let
+ \csname POLuserpol@\POL@sturmname\POL@sqfnorr\expandafter\endcsname
+ \csname POLuserpol@\POL@sturmname _0\endcsname
+ \XINT_global
+ \expandafter\let
+ \csname POLuserpol@\POL@sturmname\POL@norr\expandafter\endcsname
+ \csname POLuserpol@\POL@sturmname _0_\endcsname
+ % attention formé avec\xintREZ d'où le \xintAbs pas \xintiiAbs
+ % D and its exponent E will get updated along the way
+ \edef\POL@findrat@D{\xintAbs{\PolLeadingCoeff{\POL@sturmname _0}}}%
+ \POL@xintfrac@getNDE\POL@findrat@D\POL@findrat@Dint\POL@_\POL@findrat@Dexp
+ \xintiiifOne{\POL@findrat@Dint}
+ {\let\POL@findrat@E\POL@findrat@Dexp} % aussi ok pour 1[0]
+ {\edef\POL@findrat@E{\the\numexpr\xintLen{\POL@findrat@Dint}%
+ +\POL@findrat@Dexp}}%
+% ATTENTION QUE LA CONVENTION DE SIGNE POUR \POL@findrat@E EST OPPOSÉE À CELLE
+% POUR LE CODE PLUS ANCIEN FAISANT "REFINE"
+ \POL@initarray\POL@IfMultIsKnown\xint_secondoftwo
+ \let\POL@findrat@nbofirrroots\POL@isolz@NbOfRoots
+ % find all rational roots, and their multiplicities,
+ % factor them out in passing from original (Sturm root) polynomial
+ \ifnum\POL@findrat@E<7
+ \def\POL@findrat@index{1}%
+ \POL@findrat@loop@secondpass@direct
+ \else
+ % we do a first pass scanning for "small" roots p/q (i.e. q < 1000)
+ \def\POL@findrat@index{1}%
+ \POL@findrat@loop@firstpass
+ % and now we do the final pass finding them all
+ \def\POL@findrat@index{1}%
+ \POL@findrat@loop@secondpass
+ \fi
+ % declare the new polynomials
+ \POL@newpol{\POL@sturmname\POL@sqfnorr}% without multiplicities
+ \POL@newpol{\POL@sturmname\POL@norr}% with multiplicities
+ % declare the array holding the interval indices for the rational roots
+ \expandafter\POL@findrat@doRRarray\csname POL_RI\POL@sturmname*\endcsname
+ \fi
+}%
+\def\POL@findrat@doRRarray#1{%
+ % il faudrait un \xintAssignArray* qui fasse même expansion que \xintFor*
+ \edef\POL@temp{%
+ \xintiloop[1+1]
+ \romannumeral0\csname POL_ZK\POL@sturmname*\xintiloopindex\endcsname
+ \xintbracediloopindex % I should have named it \xintiloopbracedindex...
+ {}%
+ \ifnum\xintiloopindex<\POL@isolz@NbOfRoots\space
+ \repeat }%
+ \begingroup\globaldefs\@ne
+ % attention de ne surtout pas faire un \expandafter ici, car en cas d'un
+ % seul item, \xintAssignArray l'unbraces...
+ \xintAssignArray\POL@temp\to#1%
+ \endgroup
+}%
+\def\POL@findrat@loop@firstpass{%
+ \PolSturmIfZeroExactlyKnown{\POL@sturmname}{\POL@findrat@index}%
+ \POL@findrat@loop@decimal% get its multiplicity
+ \POL@findrat@loop@aa % refine interval and check
+ \edef\POL@findrat@index{\the\numexpr\POL@findrat@index+\@ne}%
+ \ifnum\POL@findrat@index>\POL@isolz@NbOfRoots
+ \else
+ \expandafter\POL@findrat@loop@firstpass
+ \fi
+}%
+\def\POL@findrat@loop@aa{%
+ % we do a first pass to identify roots with denominators < 1000
+ \PolEnsureIntervalLength{\POL@sturmname}{\POL@findrat@index}{-6}%
+ % attention that perhaps now the root is known!
+ \PolSturmIfZeroExactlyKnown{\POL@sturmname}{\POL@findrat@index}%
+ \POL@findrat@loop@decimal
+ \POL@findrat@loop@a
+}%
+\def\POL@findrat@loop@decimal{% we have an already found decimal root
+ % we do not go via @storeit, as it is already stored
+ % j'ai beaucoup hésité néanmoins, car je pourrais faire \xintIrr ici,
+ % mais attention aussi à l'interaction avec le \PolDecToString. Les racines
+ % trouvées directement (qui peuvent être des nombres décimaux) sont elles
+ % stockées comme fraction irréductibles (modulo action additionnelle de
+ % \PolDecToString).
+ \POL@xintfrac@getNDE
+ {\xintIrr{\POL@xintexprGetVar{\POL@sturmname L_\POL@findrat@index}}[0]}%
+ \POL@findrat@xN\POL@findrat@xD\POl@_
+ % we can't move this to updatequotients because other branch will
+ % need to do the division first anyhow
+ \edef\POLuserpol@_findrat@oneterm{1.\noexpand\empty
+ {\xintiiOpp\POL@findrat@xN/1[0]}{\POL@findrat@xD/1[0]}}%
+ \POL@divide{\POL@sturmname\POL@sqfnorr}{_findrat@oneterm}% the one without mult.
+ %\expandafter\POL@split\POL@R;\POL@degR\POL@polR
+ \POL@findrat@loop@updatequotients
+ \POL@findrat@loop@getmultiplicity
+}%
+% lacking from xint 1.3c, but \xintSgn has overhead, so we define ii version
+\def\xintiiifNeg{\romannumeral0\xintiiifneg }%
+\def\xintiiifneg #1%
+{%
+ \ifcase \xintiiSgn{#1}
+ \expandafter\xint_stop_atsecondoftwo
+ \or\expandafter\xint_stop_atsecondoftwo
+ \else\expandafter\xint_stop_atfirstoftwo
+ \fi
+}%
+\def\POL@findrat@getE #1/1[#2]{#2}% /1 as it should be there.
+% so an error will arise if not but cf \POL@refine@getE where I did not put it
+\def\POL@findrat@loop@a{%
+ % attention that the width may have been already smaller than 10^{-6}
+ \POL@get@IsoLeft@rawin
+ \POL@get@IsoRight@rawin
+ \edef\POL@findrat@localW
+ {\the\numexpr-\expandafter\POL@findrat@getE
+ % do I really need the \xintREZ?
+ \romannumeral0\xintrez
+ {\xintSub{\POL@IsoRight@rawin}{\POL@IsoLeft@rawin}}%
+ }% at least 6, maybe larger
+ \expandafter\POL@get@Int@aux
+ \POL@IsoLeft@rawin\POL@IsoLeft@Int{-\POL@findrat@localW}%
+ \expandafter\POL@get@Int@aux
+ \POL@IsoRight@rawin\POL@IsoRight@Int{-\POL@findrat@localW}%
+ % in case of odd, some waste here
+ \edef\POL@findrat@halflocalW{\the\numexpr(\POL@findrat@localW+1)/2-1}%
+ % Legendre Theorem will be used now but we separate a branch where
+ % everything can be done with \numexpr
+ \ifnum\POL@findrat@localW>9
+ % not implemented yet by lazyness!
+ % this root will be handled in second pass only
+ \else
+ \POL@findrat@gcdloop
+ \fi
+}%
+\def\POL@findrat@gcdloop{%
+ % we must be careful with sign
+ % but we are certain no extremity is a root
+ \let\POL@findrat@ifnegative\xint_secondoftwo
+ \xintiiifSgn\POL@IsoLeft@Int
+ \POL@findrat@gcdloop@n
+ \POL@error@thisisimpossible
+ \POL@findrat@gcdloop@p
+}%
+\def\POL@findrat@gcdloop@n{%
+ \let\POL@findrat@ifnegative\xint_firstoftwo
+ \let\POL@temp\POL@IsoRight@Int
+ \edef\POL@IsoRight@Int{\xintiiOpp{\POL@IsoLeft@Int}}%
+ \edef\POL@IsoLeft@Int{\xintiiOpp{\POL@temp}}%
+ \POL@findrat@gcdloop@p
+}%
+\def\POL@findrat@gcdloop@p{%
+ \edef\POL@findrat@gcdloop@Ap{\xintDec{\xintDouble\POL@IsoRight@Int}}%
+ \edef\POL@findrat@gcdloop@A
+ % at most 2e9: this is acceptable to \numexpr
+ {2\romannumeral\xintreplicate\POL@findrat@localW{0}}%
+ \xintAssign
+ \xintiiDivision\POL@findrat@gcdloop@Ap\POL@findrat@gcdloop@A
+ \to\POL@findrat@gcdloop@B\POL@findrat@gcdloop@An
+ % on fait de la tambouille pour n'utiliser que \numexpr par la suite
+ % le reste @An est < 2.10^9 au pire donc ok pour \numexpr
+ % we will drop integral part in our updating P
+ \let\POL@findrat@gcdloop@Binitial\POL@findrat@gcdloop@B
+ \def\POL@findrat@gcdloop@B{0}% do as if B1 = 0
+ \def\POL@findrat@gcdloop@Pp{1}% P0
+ \def\POL@findrat@gcdloop@P{0}% P1
+ \def\POL@findrat@gcdloop@Qp{0}% Q0
+ \def\POL@findrat@gcdloop@Q{1}% Q1
+ % A2=An can not be zero, as Ap (=A0) is odd and A (=A1=200...000) is even
+ % first Binitial + P1/Q1 ( = Binitial) can not be root
+ \let\POL@findrat@gcdloop@Ap\POL@findrat@gcdloop@A % A1
+ \let\POL@findrat@gcdloop@A\POL@findrat@gcdloop@An % A2
+ \def\next{\POL@findrat@gcdloop@update}%
+ \def\POL@findrat@gcdloop@done{0}%
+ \POL@findrat@gcdloop@body
+}%
+\def\POL@findrat@gcdloop@body{%
+ % annoying that \numexpr has no divmod... use counts? but groups annoying
+ \edef\POL@findrat@gcdloop@B
+ {\the\numexpr(\POL@findrat@gcdloop@Ap+\POL@findrat@gcdloop@A/2)/%
+ \POL@findrat@gcdloop@A - \@ne}%
+ \edef\POL@findrat@gcdloop@An
+ {\the\numexpr\POL@findrat@gcdloop@Ap-%
+ \POL@findrat@gcdloop@B*\POL@findrat@gcdloop@A}%
+ \edef\POL@findrat@gcdloop@Pn
+ {\the\numexpr\POL@findrat@gcdloop@Pp+%
+ \POL@findrat@gcdloop@B*\POL@findrat@gcdloop@P}%
+ \edef\POL@findrat@gcdloop@Qn
+ {\the\numexpr\POL@findrat@gcdloop@Qp+%
+ \POL@findrat@gcdloop@B*\POL@findrat@gcdloop@Q}%
+ \ifnum\expandafter\xintLength\expandafter{\POL@findrat@gcdloop@Qn}%
+ >\POL@findrat@halflocalW\space
+ \let\next\empty % no solution was found
+ \else
+ % with these conditions on denom, only candidates are by Legendre
+ % theorem among the convergents as computed here
+ \ifnum\POL@findrat@gcdloop@Qn>\POL@findrat@gcdloop@An\space
+ % means that P/Q is in interval and is thus a candidate
+ % it is automatically irreducible
+ \edef\POL@findrat@x{\xintiiAdd
+ {\xintiiMul{\POL@findrat@gcdloop@Qn}{\POL@findrat@gcdloop@Binitial}}%
+ {\POL@findrat@gcdloop@Pn}/\POL@findrat@gcdloop@Qn[0]}%
+ \POL@findrat@gcdloop@testit
+ \if1\POL@findrat@gcdloop@done
+ \let\next\empty % a solution was found
+ \fi
+ \fi
+ \fi
+ \next
+}%
+\def\POL@findrat@gcdloop@update{%
+ \ifnum\POL@findrat@gcdloop@An>\z@
+ \let\POL@findrat@gcdloop@Ap\POL@findrat@gcdloop@A
+ \let\POL@findrat@gcdloop@A\POL@findrat@gcdloop@An
+ \let\POL@findrat@gcdloop@Pp\POL@findrat@gcdloop@P
+ \let\POL@findrat@gcdloop@P\POL@findrat@gcdloop@Pn
+ \let\POL@findrat@gcdloop@Qp\POL@findrat@gcdloop@Q
+ \let\POL@findrat@gcdloop@Q\POL@findrat@gcdloop@Qn
+ \expandafter\POL@findrat@gcdloop@body
+ \fi
+}%
+\def\POL@findrat@gcdloop@testit{%
+ % zero should never occur here
+ \POL@findrat@ifnegative{\edef\POL@findrat@x{-\POL@findrat@x}}{}%
+ \POL@xintfrac@getNDE\POL@findrat@x\POL@findrat@xN\POL@findrat@xD\POL@_
+ \edef\POLuserpol@_findrat@oneterm{1.\noexpand\empty
+ {\xintiiOpp{\POL@findrat@xN}/1[0]}{\POL@findrat@xD/1[0]}}%
+ \POL@divide{\POL@sturmname\POL@sqfnorr}{_findrat@oneterm}% the one without mult.
+ \expandafter\POL@split\POL@R;\POL@degR\POL@polR
+ \ifnum\POL@degR=\m@ne % found a root
+ \POL@findrat@loop@storeit
+ \POL@findrat@loop@updatequotients
+ \POL@findrat@loop@getmultiplicity % will continue updating the mult. one
+ \def\POL@findrat@gcdloop@done{1}%
+ \fi
+}%
+% This is second phase
+\def\POL@findrat@loop@secondpass{%
+ \PolSturmIfZeroExactlyKnown{\POL@sturmname}{\POL@findrat@index}%
+ {}% nothing more to be done, already stored
+ \POL@findrat@loop@bb % refine interval and check
+ \edef\POL@findrat@index{\the\numexpr\POL@findrat@index+\@ne}%
+ \ifnum\POL@findrat@index>\POL@isolz@NbOfRoots
+ \else
+ \expandafter\POL@findrat@loop@secondpass
+ \fi
+}%
+\def\POL@findrat@loop@secondpass@direct{%
+ \PolSturmIfZeroExactlyKnown{\POL@sturmname}{\POL@findrat@index}%
+ \POL@findrat@loop@decimal
+ \POL@findrat@loop@bb
+ \edef\POL@findrat@index{\the\numexpr\POL@findrat@index+\@ne}%
+ \ifnum\POL@findrat@index>\POL@isolz@NbOfRoots
+ \else
+ \expandafter\POL@findrat@loop@secondpass@direct
+ \fi
+}%
+\def\POL@findrat@loop@bb{%
+ \PolEnsureIntervalLength{\POL@sturmname}{\POL@findrat@index}{-\POL@findrat@E}%
+ % ATTENTION THAT PERHAPS NOW THE ROOT IS KNOWN!
+ \PolSturmIfZeroExactlyKnown{\POL@sturmname}{\POL@findrat@index}%
+ \POL@findrat@loop@decimal
+ \POL@findrat@loop@b
+}%
+\def\POL@findrat@loop@b{%
+ \edef\POL@findrat@Lscaled{\xintMul{\POL@findrat@D}%
+ {\POL@xintexprGetVar{\POL@sturmname L_\POL@findrat@index}}}%
+ \edef\POL@findrat@Rscaled{\xintMul{\POL@findrat@D}%
+ {\POL@xintexprGetVar{\POL@sturmname R_\POL@findrat@index}}}%
+ \xintiiifNeg{\POL@findrat@Lscaled}% using ii version is an abuse
+ {% negative interval (right bound possibly zero!)
+ % truncate towards zero (i.e. to the right) the left bound
+ \edef\POL@findrat@Num{\xintNum{\POL@findrat@Lscaled}/1[0]}%
+ % interval boundaries are not root hence in case that was exact
+ % this will not be found as a root; check if in interval
+ \xintifLt\POL@findrat@Num\POL@findrat@Rscaled
+ \POL@findrat@loop@c
+ {}% iterate
+ }%
+ {% positive interval (left bound possibly zero!)
+ % truncate towards zero (i.e. to the left) the right bound
+ \edef\POL@findrat@Num{\xintNum{\POL@findrat@Rscaled}/1[0]}%
+ % check if in interval
+ \xintifGt\POL@findrat@Num\POL@findrat@Lscaled
+ \POL@findrat@loop@c
+ {}% iterate
+ }%
+}%
+\def\POL@findrat@loop@c{%
+ % safer to do the edef as \POL@findrat@x used later in storeit
+ \edef\POL@findrat@x{\xintIrr{\xintDiv\POL@findrat@Num\POL@findrat@D}[0]}%
+ \POL@xintfrac@getNDE\POL@findrat@x\POL@findrat@xN\POL@findrat@xD\POL@_
+ \edef\POLuserpol@_findrat@oneterm{1.\noexpand\empty
+ {\xintiiOpp{\POL@findrat@xN}/1[0]}{\POL@findrat@xD/1[0]}}%
+ \POL@divide{\POL@sturmname\POL@sqfnorr}{_findrat@oneterm}% the one without mult.
+ \expandafter\POL@split\POL@R;\POL@degR\POL@polR
+ \ifnum\POL@degR=\m@ne % found a root
+ \POL@findrat@loop@storeit
+ \POL@findrat@loop@updatequotients
+ \POL@findrat@loop@getmultiplicity % will continue updating the mult. one
+ \fi
+ % iterate
+}%
+\def\POL@findrat@loop@storeit{%
+ % update storage, I can not use storeleftandright here (due to rawout etc...)
+ \expandafter
+ \xdef\csname POL_ZL\POL@sturmname*\POL@findrat@index\endcsname
+ {\PolDecToString{\POL@findrat@x}}%
+ \global\expandafter
+ \let\csname POL_ZR\POL@sturmname*\POL@findrat@index\expandafter\endcsname
+ \csname POL_ZL\POL@sturmname*\POL@findrat@index\endcsname
+ \global\expandafter
+ \let\csname POL_ZK\POL@sturmname*\POL@findrat@index\endcsname
+ \xint_stop_atfirstoftwo
+ \begingroup\xintglobaldefstrue
+ % skip some overhead of \xintdefvar...
+ % BUT attention to changes in xint 1.4 internal format !
+ \XINT_expr_defvar_one{\POL@sturmname L_\POL@findrat@index}%
+ {{\POL@findrat@x}}%
+ \XINT_expr_defvar_one{\POL@sturmname R_\POL@findrat@index}%
+ {{\POL@findrat@x}}%
+ \XINT_expr_defvar_one{\POL@sturmname Z_\POL@findrat@index _isknown}%
+ {{1}}%
+ \endgroup
+}%
+\def\POL@findrat@loop@updatequotients{%
+ % attention last division must have been one testing vanishing of\POL@sqfnorr
+ \XINT_global\expandafter\let\csname POLuserpol@\POL@sturmname\POL@sqfnorr\endcsname\POL@Q
+ % quotient belongs to Z[X] and is primitive
+ \POL@mapcoeffs\POL@aux@toint{\POL@sturmname\POL@sqfnorr}%
+ % update the one with multiplicities
+ \POL@divide{\POL@sturmname\POL@norr}{_findrat@oneterm}%
+ \XINT_global\expandafter\let\csname POLuserpol@\POL@sturmname\POL@norr\endcsname\POL@Q
+ \POL@mapcoeffs\POL@aux@toint{\POL@sturmname\POL@norr}
+ % updating of \POL@findrat@D at end of execution of getmultiplicity
+}%
+\def\POL@findrat@loop@getmultiplicity{%
+ % the one without multiplicity must not be divided again!
+ % check if we have remaining multiplicity
+ \POL@divide{\POL@sturmname\POL@norr}{_findrat@oneterm}%
+ \expandafter\POL@split\POL@R;\POL@degR\POL@polR
+ \ifnum\POL@degR=\m@ne % yes
+ \XINT_global\expandafter\let\csname POLuserpol@\POL@sturmname\POL@norr\endcsname\POL@Q
+ \POL@mapcoeffs\POL@aux@toint{\POL@sturmname\POL@norr}%
+ \expandafter
+ \xdef
+ \csname POL_ZM\POL@sturmname*\POL@findrat@index\endcsname
+ {\the\numexpr
+ \csname POL_ZM\POL@sturmname*\POL@findrat@index\endcsname+\@ne}%
+ \expandafter\POL@findrat@loop@getmultiplicity
+ \else
+ % done with multiplicity for this rational root, update stuff
+ \edef\POL@findrat@nbofirrroots
+ {\the\numexpr\POL@findrat@nbofirrroots-\@ne}%
+ \@namedef{POL@IfMultIsKnown\POL@findrat@index}{\xint_firstoftwo}%
+ \edef\POL@findrat@D{\xintAbs{\PolLeadingCoeff{\POL@sturmname\POL@sqfnorr}}}%
+ \POL@xintfrac@getNDE\POL@findrat@D\POL@findrat@Dint\POL@_\POL@findrat@Dexp
+ \xintiiifOne{\POL@findrat@Dint}
+ {\let\POL@findrat@E\POL@findrat@Dexp} % aussi ok pour 1[0]
+ {\edef\POL@findrat@E{\the\numexpr\xintLen{\POL@findrat@Dint}%
+ +\POL@findrat@Dexp}}%
+ \fi
+}%
+\def\POL@findrat@getirrmult{%
+ % first get the GCD of remaining pol with its derivative
+ \POL@divide{\POL@sturmname\POL@norr}{\POL@sturmname\POL@sqfnorr}%
+ \expandafter\let
+ % attention au _ (cf. grosse astuce pour \POL@isolzmult@loop)
+ \csname POLuserpol@@_1\POL@sturmname _\endcsname\POL@Q
+ \ifnum\PolDegree{@_1\POL@sturmname _}>\z@
+ % il reste des multiplicités (mais peut-être pour des racines complexes)
+ % (ou pour des racines en-dehors de l'intervalle optionnel)
+ % attention recyclage ici de \POL@isolzmult@loop qui dépend de
+ % la grosse astuce avec \@gobble
+ \POL@makeprimitive{@_1\POL@sturmname _}%
+ \let\POL@originalsturmname\POL@sturmname
+ % trick to get isolzmult@loop to define @@lastGCD to @_1sturmname_
+ % because it will do \POL@sturmname _\POL@sturm@N _
+ \edef\POL@sturmname{@_1\POL@sturmname}%
+ \let\POL@sturm@N\@gobble% !
+ \let\POL@isolz@NbOfRoots@with_unknown_mult\POL@findrat@nbofirrroots
+ \POL@tosturm@makefirstprimitivefalse
+\expanded{\unexpanded{%
+ \unless\ifxintveryverbose\xintverbosefalse\polnewpolverbosefalse\fi
+ \POL@isolzmult@loop
+}\ifxintverbose\noexpand\xintverbosetrue\fi
+ \ifpolnewpolverbose\noexpand\polnewpolverbosetrue\fi}%
+ \POL@tosturm@makefirstprimitivetrue
+ \let\POL@sturmname\POL@originalsturmname
+ \fi
+}%
+\def\PolSturmIsolateZerosAndGetMultiplicities@{%
+ \POL@chkopt\POL@oPolSturmIsolateZerosAndGetMultiplicities@[\empty]%
+}%
+\def\POL@oPolSturmIsolateZerosAndGetMultiplicities@[#1]#2{%
+ % #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)
+ \edef\POL@sturmname{#2}%
+ \edef\POL@sturm@N{\@nameuse{PolSturmChainLength_\POL@sturmname}}%
+ % isolate the roots (detects case of constant polynomial)
+ \PolSturmIsolateZeros@{\POL@sturmname}%
+ \ifnum\POL@isolz@NbOfRoots=\z@
+ % no roots, define empty array nevertheless
+ \begingroup\globaldefs\@ne
+ \expandafter\xintAssignArray\expandafter\to\csname POL_ZM\POL@sturmname*\endcsname
+ \endgroup
+ \else
+ % all we currently know is that multiplicities are at least one
+ \begingroup\globaldefs\@ne
+ \expandafter\POL@initarray\csname POL_ZM\POL@sturmname*\endcsname{1}%
+ \endgroup
+ % check if GCD had positive degree (hence some roots, maybe complex, have
+ % multiplicity)
+ \ifnum\PolDegree{\POL@sturmname _\POL@sturm@N _}>\z@
+ % scratch array of flags to signal known multiplicities
+ \POL@initarray\POL@IfMultIsKnown\xint_secondoftwo
+ % this count has utility for the case there are other roots
+ % either complex or outside interval (in case of optional argument)
+ \let\POL@isolz@NbOfRoots@with_unknown_mult\POL@isolz@NbOfRoots
+ % store Sturm chain name, it is needed and altered in isolzmult@loop
+ \let\POL@originalsturmname\POL@sturmname
+ \POL@tosturm@makefirstprimitivefalse
+\expanded{\unexpanded{%
+ \unless\ifxintveryverbose\xintverbosefalse\polnewpolverbosefalse\fi
+ \POL@isolzmult@loop
+}\ifxintverbose\noexpand\xintverbosetrue\fi
+ \ifpolnewpolverbose\noexpand\polnewpolverbosetrue\fi}%
+ \POL@tosturm@makefirstprimitivetrue
+ \let\POL@sturmname\POL@originalsturmname
+ \fi
+ \POL@isolzmult@defvar@M
+ \fi
+}%
+\def\POL@isolzmult@defvar@M{%
+ % Attention that is used not only in ...GetMultiplicities@ but also
+ % in FindRationalRoots
+ \begingroup\xintglobaldefstrue
+ % added at 0.7
+ \let\x\POL@isolz@NbOfRoots
+ \xintloop
+ % skip some overhead of \xintdefvar...
+ % ATTENTION to xint 1.4 internal changes !
+ \XINT_expr_defvar_one{\POL@sturmname M_\x}%
+ {{\csname POL_ZM\POL@sturmname*\x\endcsname}}%
+ \edef\x{\the\numexpr\x-\@ne}%
+ \ifnum\x>\z@
+ \repeat
+ \endgroup
+}%
+\def\POL@isolzmult@loop{%
+ % we are here only if last iteration gave a new GCD still of degree > 0
+ % \POL@sturm@N is the one from last iteration
+ % Attention to not use \POL@sturmname directly in first arg. of \PolToSturm
+ % Attention that we need for the case of known roots also to have the last
+ % GCD (with its multiplicities) known as a genuine polynomial
+ % - because of usage of \POL@eval in @isknown branch
+ % - because \PolToSturm@ does a \POL@let which would be anomalous
+ % if the extended structure is not existing
+ \edef\POL@isolzmult@lastGCD{\POL@sturmname _\POL@sturm@N _}%
+ \edef\POL@isolzmult@newsturmname{@_1\POL@sturmname}%
+ \POL@newpol{\POL@isolzmult@lastGCD}%
+ \PolToSturm@{\POL@isolzmult@lastGCD}{\POL@isolzmult@newsturmname}%
+ % now both \POL@sturmname and \POL@sturm@N have changed
+ \edef\POL@isolzmult@newGCDdegree{\PolDegree{\POL@sturmname _\POL@sturm@N _}}%
+ \let\POL@isolzmult@index\POL@isolz@NbOfRoots
+ \xintloop
+ % ATTENTION that this executes macros which also modifies \POL@sturmname!
+ % (but not \POL@sturm@N)
+ \POL@isolzmult@doone
+ \edef\POL@isolzmult@index{\the\numexpr\POL@isolzmult@index-\@ne}%
+ \if1\ifnum\POL@isolz@NbOfRoots@with_unknown_mult=\z@ 0\fi
+ \ifnum\POL@isolzmult@index=\z@ 0\fi 1%
+ \repeat
+ \let\POL@sturmname\POL@isolzmult@newsturmname
+ \if1\ifnum\POL@isolz@NbOfRoots@with_unknown_mult=\z@ 0\fi
+ % (if new GCD is constant, time to abort)
+ \ifnum\POL@isolzmult@newGCDdegree=\z@ 0\fi 1%
+ \expandafter\POL@isolzmult@loop
+ \fi
+}%
+\def\POL@isolzmult@doone{%
+ \csname POL@IfMultIsKnown\POL@isolzmult@index\endcsname
+ {}% nothing to do
+ {\POL@SturmIfZeroExactlyKnown{\POL@originalsturmname}%
+ {\POL@isolzmult@index}%
+ \POL@isolzmult@loop@isknown
+ \POL@isolzmult@loop@isnotknown
+ \POL@isolzmult@loop@sharedbody
+ }%
+}%
+\def\POL@isolzmult@loop@isknown{%
+ \xintifZero
+ % attention that \POL@eval requires a declared polynomial
+ {\POL@eval{\POL@isolzmult@lastGCD}%
+ {\POL@xintexprGetVar{\POL@originalsturmname L_\POL@isolzmult@index}}}%
+ {\let\POL@isolzmult@haszero\@ne}%
+ {\let\POL@isolzmult@haszero\z@}%
+}%
+\def\POL@isolzmult@loop@isnotknown{%
+ \edef\POL@isolzmult@loop@A
+ {\POL@xintexprGetVar{\POL@originalsturmname L_\POL@isolzmult@index}}
+ \edef\POL@isolzmult@loop@B
+ {\POL@xintexprGetVar{\POL@originalsturmname
+ R_\POL@isolzmult@index}}
+ % attention that \PolSetToNbOfZerosWithin sets \POL@sturmname to 2nd argument
+ \PolSetToNbOfZerosWithin
+ \POL@isolzmult@haszero % nb of zeros A < x <= B, here 0 or 1
+ \POL@isolzmult@newsturmname
+ \POL@isolzmult@loop@A
+ \POL@isolzmult@loop@B
+}%
+\def\POL@isolzmult@loop@sharedbody{%
+ \ifnum\POL@isolzmult@haszero>\z@
+ \expandafter
+ \xdef
+ \csname POL_ZM\POL@originalsturmname*\POL@isolzmult@index\endcsname
+ {\the\numexpr
+ \csname POL_ZM\POL@originalsturmname
+ *\POL@isolzmult@index\endcsname+\@ne}%
+ \else
+ % multiplicity now known, no need to check this index in future
+ \@namedef{POL@IfMultIsKnown\POL@isolzmult@index}{\xint_firstoftwo}%
+ \edef\POL@isolz@NbOfRoots@with_unknown_mult
+ {\the\numexpr\POL@isolz@NbOfRoots@with_unknown_mult-\@ne}%
+ \fi
+}%
+\def\PolSturmIsolateZeros@{%
+ \POL@chkopt\POL@oPolSturmIsolateZeros@[\empty]%
+}%
+\def\POL@oPolSturmIsolateZeros@[#1]#2{%
+ % #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)
+ % For reasons I have forgotten (no time now) this code **must** be used
+ % with a *normalized* Sturm chain.
+ \edef\POL@sturmname{#2}%
+ \edef\POL@sturmlength{\PolSturmChainLength{#2}}%
+ % attention to constant polynomial, we must redefine the arrays then
+ \ifnum\POL@sturmlength>\z@
+ \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}%
+ \else
+ % constant polynomial
+ \def\POL@isolz@NbOfRoots{0}%
+ \fi
+ \ifnum\POL@isolz@NbOfRoots=\z@
+ \begingroup\globaldefs\@ne
+ \expandafter\xintAssignArray\expandafter\to\csname POL_ZL#2*\endcsname
+ \expandafter\xintAssignArray\expandafter\to\csname POL_ZR#2*\endcsname
+ \expandafter\xintAssignArray\expandafter\to\csname POL_ZK#2*\endcsname
+ \endgroup
+ \else
+ \begingroup\globaldefs\@ne
+ \expandafter\POL@initarray\csname POL_ZL#2*\endcsname{0}%
+ \expandafter\POL@initarray\csname POL_ZR#2*\endcsname{0}%
+ \expandafter\POL@initarray\csname POL_ZK#2*\endcsname
+ \xint_stop_atsecondoftwo
+ \endgroup
+ \ifx\empty#1\relax\expandafter\POL@isolz@getaprioribound\fi
+ \expandafter\POL@isolz@main
+ \fi
+}%
+\def\POL@initarray#1#2{%
+% ATTENTION, if only one item, \xintAssignArray UNBRACES IT
+% so we use an \empty trick to avoid that. Maybe considered a bug of xinttools?
+ \expandafter\xintAssignArray\expandafter\empty
+ \romannumeral\xintreplicate{\POL@isolz@NbOfRoots}{{#2}}\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, \POL@IsoRightSign is zero
+ \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{\POL@eval{\POL@sturmname _1}{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
+ % \POL@IsoRight@SV was modified if zero is a root
+ \edef\POL@isolz@NbOfNegRoots{\the\numexpr\POL@IsoLeftSV-\POL@IsoRightSV}%
+ \gdef\POL@isolz@IntervalIndex{0}%
+ \let\POL@isolz@@E\POL@isolz@E
+ \ifnum\POL@isolz@NbOfNegRoots>\z@
+% refactored at 0.7 to fix cases leading to an intervals with zero as end-point
+ \POL@isolz@findroots@neg
+ \fi
+ \let\POL@isolz@E\POL@isolz@@E
+ \def\POL@IsoLeft@Int{0}%
+ \let\POL@IsoLeftSV \POL@IsoAtZeroSV % véritable SV en zéro
+ \let\POL@IsoLeftSign\POL@IsoAtZeroSign% véritable signe en zéro
+ \ifnum\POL@IsoLeftSign=\z@
+ \xdef\POL@isolz@IntervalIndex{\the\numexpr\POL@isolz@IntervalIndex+\@ne}%
+ \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@
+ % always do that to avoid zero as end-point whether it is a root or not
+ \global\POL@isolz@nextwillneedrefinetrue
+ \POL@isolz@findroots@pos
+ \fi
+}%
+\def\POL@isolz@findroots@neg{%
+ \def\POL@IsoRight@Int{-1}%
+ \POL@isolz@findnextzeroboundeddecade@neg
+ \def\POL@IsoLeft@Int{-10}%
+ \let\POL@@IsoRightSign\POL@IsoRightSign % a zero there is possible
+ \let\POL@@IsoRightSV \POL@IsoRightSV
+ % this will do possibly recursive \POL@isolz@check's
+ \POL@isolz@explorenexteightsubdecades@neg
+ \ifnum\POL@isolz@IntervalIndex<\POL@isolz@NbOfNegRoots\space
+ % above did not explore -2, -1 for this optimization (SV known at Right)
+ \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
+ % I don't like being inside TeX conditionals
+ \expandafter\expandafter\expandafter\POL@isolz@findroots@neg
+ \fi
+ \fi
+}%
+\def\POL@isolz@findnextzeroboundeddecade@neg{%
+ \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, iterate
+ \repeat
+}%
+\def\POL@isolz@explorenexteightsubdecades@neg{%
+ \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 % may recurse if multiple roots are to be found
+ \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
+}%
+\def\POL@isolz@findroots@pos{%
+ % remark (2018/12/08), this needs some refactoring, I hardly understand
+ % the logic and it hides most into the recursion done by \POL@isolz@check
+ % It would probably make more sense to proceed like done for the negative
+ % but here finding the largest roots first.
+ \def\POL@IsoRight@Int{1}%
+ \POL@isolz@findnextzeroboundeddecade@pos
+ \unless\ifnum\POL@IsoRightSV=\POL@IsoLeftSV\space
+ % this actually explores the whole of some interval (0, 10^{e-1}]
+ % in a context where some roots are known to be in (10^{e-1}, 10^{e}]
+ % and none are larger
+ \POL@isolz@check % will recurse inside groups if needed with modified E
+ \fi
+ % we know get the roots in the last 9 decades from 10^{e-1} to 10^{e}
+ % we should arguably do a more efficient dichotomy here
+ \def\POL@IsoLeft@Int{1}%
+ \let\POL@IsoLeftSV\POL@IsoRightSV
+ \let\POL@IsoLeftSign\POL@IsoRightSign
+ \xintloop
+ \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 % recurses in needed
+ \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
+}%
+\def\POL@isolz@findnextzeroboundeddecade@pos{%
+ \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
+}%
+\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@IsoRightSign is zero iff root now exactly known
+ \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{\POL@eval{\POL@sturmname _0}{\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{\POL@eval{\POL@sturmname _0}{\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{\POL@eval{\POL@sturmname _0}{\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{\POL@eval{\POL@sturmname _0}{\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{\POL@eval{\POL@sturmname _0}{\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{\POL@eval{\POL@sturmname _0}{\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{\POL@eval{\POL@sturmname _0}{\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{\POL@eval{\POL@sturmname _0}{\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{\POL@eval{\POL@sturmname _0}{\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_ZL\POL@sturmname*\POL@isolz@IntervalIndex\endcsname
+ {\PolDecToString{\POL@IsoLeft@rawout}}%
+ \expandafter
+ \xdef\csname POL_ZR\POL@sturmname*\POL@isolz@IntervalIndex\endcsname
+ {\PolDecToString{\POL@IsoRight@rawout}}%
+ % added at 0.6
+ \ifnum\POL@IsoRightSign=\z@
+ \global
+ \expandafter
+ \let\csname POL_ZK\POL@sturmname*\POL@isolz@IntervalIndex\endcsname
+ \xint_stop_atfirstoftwo
+ \fi
+ \begingroup\xintglobaldefstrue
+ % skip some overhead of \xintdefvar...
+ % Let me repeat: ATTENTION to change of internal format at xint 1.4
+ \XINT_expr_defvar_one{\POL@sturmname L_\POL@isolz@IntervalIndex}%
+ {{\POL@IsoLeft@rawout}}%
+ \XINT_expr_defvar_one{\POL@sturmname R_\POL@isolz@IntervalIndex}%
+ {{\POL@IsoRight@rawout}}%
+ % added at 0.7
+ \XINT_expr_defvar_one{\POL@sturmname Z_\POL@isolz@IntervalIndex _isknown}%
+ {{\ifnum\POL@IsoRightSign=\z@ 1\else 0\fi}}%
+ \endgroup
+}%
+%% \PolRefineInterval
+%% ATTENTION TO xint 1.4 INTERNAL CHANGES
+\def\POL@xintexprGetVar#1{\expandafter\expandafter\expandafter\xint_firstofone
+ \csname XINT_expr_varvalue_#1\endcsname}%
+% attention, also used by \POL@findrat@loop@a
+\def\POL@get@IsoLeft@rawin{%
+ \edef\POL@IsoLeft@rawin
+ {\POL@xintexprGetVar{\POL@sturmname L_\POL@isolz@IntervalIndex}}%
+}%
+% attention, also used by \POL@findrat@loop@a
+\def\POL@get@IsoRight@rawin{%
+ \edef\POL@IsoRight@rawin
+ {\POL@xintexprGetVar{\POL@sturmname R_\POL@isolz@IntervalIndex}}%
+}%
+% attention, also used by \POL@findrat@loop@a
+\def\POL@get@Int@aux #1/1[#2]#3#4{\edef#3{\xintDSH{#4-#2}{#1}}}%
+\def\POL@get@IsoLeft@Int{%
+ \expandafter\POL@get@Int@aux\POL@IsoLeft@rawin\POL@IsoLeft@Int\POL@isolz@E
+}%
+\def\PolRefineInterval{\POL@ifstar\POL@srefine@start\POL@refine@start}%
+\def\POL@refine@start{%
+ \POL@chkopt\POL@oPOL@refine@start[1]%
+}%
+\def\POL@oPOL@refine@start[#1]#2#3{%
+ \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@get@IsoLeft@rawin
+ \edef\POL@IsoLeftSign
+ {\xintiiSgn{\POL@eval{\POL@sturmname _0}{\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@get@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}}}%
+ \POL@get@IsoLeft@Int
+ \edef\POL@IsoRight@Int{\xintInc{\POL@IsoLeft@Int}}%
+ #1%
+ \POL@refine@storeleftandright % \POL@IsoRightSign not zero
+ \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{\POL@eval{\POL@sturmname _0}{\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{\POL@eval{\POL@sturmname _0}{\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
+%
+%
+\def\PolIntervalWidth#1#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_ZR#1*}{#2}}%
+ {\@nameuse{POL_ZL#1*}{#2}}}
+}%
+\def\PolEnsureIntervalLengths#1#2{% #1 = Sturm chain name,
+ % localize roots in intervals of length at most 10^{#2}
+ \edef\POL@sturmname{#1}%
+ \edef\POL@ensure@targetE{\the\numexpr#2}%
+ \edef\POL@nbofroots{\csname POL_ZL\POL@sturmname*0\endcsname}%
+ \ifnum\POL@nbofroots>\z@
+ \expandafter\POL@ensureintervallengths
+ \fi
+}%
+\def\POL@ensureintervallengths{%
+ \POL@count\z@
+ % \POL@count used by \POL@sturmchain@getSV@at but latter not used
+ \xintloop
+ \advance\POL@count\@ne
+ \edef\POL@isolz@IntervalIndex{\the\POL@count}%
+ \POL@ensure@one
+ \ifnum\POL@nbofroots>\POL@count
+ \repeat
+}%
+\def\PolEnsureIntervalLength#1#2#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}%
+% peut-être autoriser -1, -2, ... ?
+ \ifnum\POL@isolz@IntervalIndex>\z@
+% 0.7, add this safeguard but attention means this structure must be in place
+ \ifnum\csname POL_ZL\POL@sturmname*0\endcsname>\z@
+% je ne fais pas les \expandafter mais je préfèrerais ne pas être à l'intérieur
+ \POL@ensure@one
+ \fi
+ \fi
+}%
+\def\POL@ensure@one{%
+ \POL@get@IsoLeft@rawin
+ \POL@get@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}%
+ \POL@get@IsoLeft@Int
+ \edef\POL@IsoRight@Int{\xintInc{\POL@IsoLeft@Int}}%
+ \ifnum\POL@isolz@E>\POL@ensure@targetE\space
+ \edef\POL@IsoLeftSign
+ {\xintiiSgn{\POL@eval{\POL@sturmname _0}{\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, it 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{\POL@eval{\POL@sturmname _0}{\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
+}%
+%
+%% \PolPrintIntervals
+\catcode`_ 8
+\catcode`& 4
+\def\PolPrintIntervals{\POL@ifstar{\PolPrintIntervals@@}{\PolPrintIntervals@}}%
+% As explained in the docs, this is an example of customization so is not
+% itself customizable, apart from redefining it entirely!
+\def\PolPrintIntervals@@{%
+ \begingroup
+ \def\POL@AfterPrintIntervals{\endgroup}%
+ \let\PolPrintIntervalsPrintExactZero\POL@@PrintIntervalsPrintExactZero
+ \let\PolPrintIntervalsUnknownRoot\POL@@PrintIntervalsUnknownRoot
+ \let\PolPrintIntervalsKnownRoot\POL@@PrintIntervalsKnownRoot
+\ifdefined\array
+ \def\arraystretch{2}%
+ \def\PolPrintIntervalsBeginEnv{\[\begin{array}{cl}}%\]
+ \def\PolPrintIntervalsEndEnv{\end{array}\]}%
+\else
+ \def\PolPrintIntervalsBeginEnv{$$\tabskip0pt plus 1000pt minus 1000pt
+ \halign to\displaywidth\bgroup
+ \hfil\vrule height 2\ht\strutbox
+ depth 2\dp\strutbox
+ width \z@
+ $####$\tabskip6pt&$####$\hfil
+ \tabskip0pt plus 1000pt minus 1000pt\cr}%$$
+ \def\PolPrintIntervalsEndEnv{\crcr\egroup$$}%$$
+\fi
+ \PolPrintIntervals@
+}%
+\def\PolPrintIntervals@{%
+ \POL@chkopt\POL@oPolPrintIntervals@[Z]%
+}%
+\def\POL@oPolPrintIntervals@[#1]#2{%
+ \def\PolPrintIntervalsTheVar{#1}%
+ \def\PolPrintIntervalsTheSturmName{#2}%
+ \ifnum\@nameuse{POL_ZL#2*}{0}=\z@
+ \PolPrintIntervalsNoRealRoots
+ \else
+ \gdef\PolPrintIntervalsTheIndex{1}%
+ \POL@PrintIntervals@DoDefs
+ \begingroup\edef\POL@tmp{\endgroup
+ \unexpanded\expandafter{\PolPrintIntervalsBeginEnv}%
+ \unexpanded\expandafter{\POL@PrintIntervals@Loop}%
+ \unexpanded\expandafter{\PolPrintIntervalsEndEnv}%
+ }\POL@tmp
+ \fi
+ \POL@AfterPrintIntervals
+ \def\PolPrintIntervalsTheVar{#1}%
+ \def\PolPrintIntervalsTheSturmName{#2}%
+}%
+\let\POL@AfterPrintIntervals\empty
+\let\PolPrintIntervalsNoRealRoots\empty
+\def\PolPrintIntervalsArrayStretch{1}%
+\ifdefined\array
+ \def\PolPrintIntervalsBeginEnv{\[\begin{array}{rcccl}}%
+ \def\PolPrintIntervalsEndEnv{\end{array}\]}%
+\else
+ \def\PolPrintIntervalsBeginEnv
+ {$$\tabskip 0pt plus 1000pt minus 1000pt
+ \halign to\displaywidth\bgroup
+ \hfil\vrule height\PolPrintIntervalsArrayStretch\ht\strutbox
+ depth \PolPrintIntervalsArrayStretch\dp\strutbox
+ width \z@
+ $##$\tabskip 6pt &\hfil $##$\hfil &\hfil $##$\hfil &\hfil $##$\hfil &$##$\hfil
+ \tabskip 0pt plus 1000pt minus 1000pt \cr
+ }%$$
+ \def\PolPrintIntervalsEndEnv{\crcr\egroup$$}%$$
+\fi
+\def\PolPrintIntervalsKnownRoot{%
+ &&\PolPrintIntervalsTheVar_{\PolPrintIntervalsTheIndex}%
+ &=&\PolPrintIntervalsPrintExactZero
+}%
+\def\PolPrintIntervalsUnknownRoot{%
+ \PolPrintIntervalsPrintLeftEndPoint&<&%
+ \PolPrintIntervalsTheVar_{\PolPrintIntervalsTheIndex}&<&%
+ \PolPrintIntervalsPrintRightEndPoint
+}%
+\def\PolPrintIntervalsPrintExactZero {\PolPrintIntervalsTheLeftEndPoint}%
+\def\PolPrintIntervalsPrintLeftEndPoint {\PolPrintIntervalsTheLeftEndPoint}%
+\def\PolPrintIntervalsPrintRightEndPoint{\PolPrintIntervalsTheRightEndPoint}%
+%
+\ifdefined\mbox
+\def\PolPrintIntervalsPrintMultiplicity{(\mbox{mult. }\PolPrintIntervalsTheMultiplicity)}%
+\else
+\def\PolPrintIntervalsPrintMultiplicity{(\hbox{mult. }\PolPrintIntervalsTheMultiplicity)}%
+\fi
+%
+\def\POL@@PrintIntervalsKnownRoot{%
+ \PolPrintIntervalsPrintMultiplicity&%
+ \PolPrintIntervalsTheVar_{\PolPrintIntervalsTheIndex}=%
+ \PolPrintIntervalsPrintExactZero
+}%
+\ifdefined\frac
+\def\POL@@PrintIntervalsPrintExactZero{%
+ \displaystyle
+ \xintSignedFrac{\PolPrintIntervalsTheLeftEndPoint}%
+}%
+\else
+\def\POL@@PrintIntervalsPrintExactZero{%
+ \displaystyle
+ \xintSignedFwOver{\PolPrintIntervalsTheLeftEndPoint}%
+}%
+\fi
+\def\POL@@PrintIntervalsUnknownRoot{%
+ \PolPrintIntervalsPrintMultiplicity&%
+ \xintifSgn{\PolPrintIntervalsTheLeftEndPoint}%
+ {\xintifSgn{\PolPrintIntervalsTheRightEndPoint}
+ {\PolPrintIntervalsTheVar_{\PolPrintIntervalsTheIndex}=%
+ \PolPrintIntervalsPrintRightEndPoint\dots}%
+ {0>\PolPrintIntervalsTheVar_{\PolPrintIntervalsTheIndex}>%
+ \PolPrintIntervalsPrintLeftEndPoint}%
+ {\PolErrorThisShouldNotHappenPleaseReportToAuthorA}}%
+ {\xintifSgn{\PolPrintIntervalsTheRightEndPoint}
+ {\PolErrorThisShouldNotHappenPleaseReportToAuthorB}%
+ {\PolErrorThisShouldNotHappenPleaseReportToAuthorC}%
+ {0<\PolPrintIntervalsTheVar_{\PolPrintIntervalsTheIndex}<%
+ \PolPrintIntervalsPrintRightEndPoint}}%
+ {\xintifSgn{\PolPrintIntervalsTheRightEndPoint}
+ {\PolErrorThisShouldNotHappenPleaseReportToAuthorD}%
+ {\PolErrorThisShouldNotHappenPleaseReportToAuthorE}%
+ {\PolPrintIntervalsTheVar_{\PolPrintIntervalsTheIndex}=%
+ \PolPrintIntervalsPrintLeftEndPoint\dots}}%
+}%
+\catcode`& 7
+\catcode`_ 11
+\def\POL@PrintIntervals@Loop#1{%
+\def\POL@PrintIntervals@Loop{%
+ \POL@SturmIfZeroExactlyKnown\PolPrintIntervalsTheSturmName
+ \PolPrintIntervalsTheIndex
+ \PolPrintIntervalsKnownRoot
+ \PolPrintIntervalsUnknownRoot
+ \xdef\PolPrintIntervalsTheIndex{\the\numexpr\PolPrintIntervalsTheIndex+\@ne}%
+ \unless\ifnum\PolPrintIntervalsTheIndex>
+ \@nameuse{POL_ZL\PolPrintIntervalsTheSturmName*0}
+ \POL@PrintIntervals@DoDefs
+ \xint_afterfi{#1\POL@PrintIntervals@Loop}%
+ \fi
+}}%
+\ifdefined\array\POL@PrintIntervals@Loop{\\}\else\POL@PrintIntervals@Loop{\cr}\fi
+\def\POL@PrintIntervals@DoDefs{%
+ \xdef\PolPrintIntervalsTheLeftEndPoint{%
+ \csname POL_ZL\PolPrintIntervalsTheSturmName*\PolPrintIntervalsTheIndex
+ \endcsname
+ }%
+ \xdef\PolPrintIntervalsTheRightEndPoint{%
+ \csname POL_ZR\PolPrintIntervalsTheSturmName*\PolPrintIntervalsTheIndex
+ \endcsname
+ }%
+ \xdef\PolPrintIntervalsTheMultiplicity{%
+ \ifcsname POL_ZM\PolPrintIntervalsTheSturmName*\PolPrintIntervalsTheIndex
+ \endcsname
+ \csname POL_ZM\PolPrintIntervalsTheSturmName*\PolPrintIntervalsTheIndex
+ \endcsname
+ \else
+ ?% or use 0 ?
+ \fi
+ }%
+}%
+%
+%% Expandable interface
+%
+\def\PolSturmIfZeroExactlyKnown#1#2{% #1 = sturmname, #2=index
+ \romannumeral0\csname POL_ZK#1*\endcsname{#2}%
+}%
+\def\POL@SturmIfZeroExactlyKnown#1#2{% #1 = sturmname, #2=index
+ \romannumeral0\csname POL_ZK#1*\the\numexpr#2\endcsname
+}%
+\def\PolSturmIsolatedZeroMultiplicity#1#2{%
+ \romannumeral`&&@\csname POL_ZM#1*\endcsname{#2}%
+}%
+\def\PolSturmIsolatedZeroLeft#1#2{%
+ \romannumeral`&&@\csname POL_ZL#1*\endcsname{#2}%
+}%
+\def\PolSturmIsolatedZeroRight#1#2{%
+ \romannumeral`&&@\csname POL_ZR#1*\endcsname{#2}%
+}%
+\def\PolSturmNbOfIsolatedZeros#1{%
+ \romannumeral`&&@\csname POL_ZL#1*0\endcsname
+}%
+\def\PolSturmRationalRoot#1#2{%
+ \romannumeral`&&@\csname POL_ZL#1*%
+ \csname POL_RI#1*\endcsname{#2}\endcsname
+}%
+\def\PolSturmRationalRootIndex#1#2{%
+ \romannumeral`&&@\csname POL_RI#1*\endcsname{#2}%
+}%
+\def\PolSturmRationalRootMultiplicity#1#2{%
+ \romannumeral`&&@\csname POL_ZM#1%
+ *\csname POL_RI#1*\endcsname{#2}\endcsname
+}%
+\def\PolSturmNbOfRationalRoots#1{%
+ \romannumeral`&&@\csname POL_RI#1*0\endcsname
+}%
+\def\PolSturmNbOfRationalRootsWithMultiplicities#1{%
+% means the \POL@norr must not have been changed in-between...
+ \the\numexpr\PolDegree{#1}-\PolDegree{#1\POL@norr}\relax
+}%
+\def\PolSturmIntervalIndex#1#2#3{\the\numexpr\POL@eval@fork
+ #2\PolSturmIntervalIndexAt
+ \At\PolSturmIntervalIndexAtExpr\krof {#1}{#3}%
+}%
+\def\PolSturmIntervalIndexAtExpr#1#2{%
+ \PolSturmIntervalIndexAt{#1}{\xinttheexpr#2\relax}%
+}%
+% ! is of catcode 11 in all of polexpr
+\def\PolSturmIntervalIndexAt#1#2{%
+ \expandafter\POL@sturm@index@at\romannumeral`&&@#2!{#1}\xint_bye\relax
+}%
+\def\POL@sturm@index@at#1!#2%
+{%
+ \expandafter\POL@sturm@index@at@iloop
+ \romannumeral`&&@\PolSturmNbOfIsolatedZeros{#2}!{#2}{#1}%
+}%
+% implementation is sub-optimal as it should use some kind of binary tree
+% search rather than comparing to the intervals from right to left as here
+\def\POL@sturm@index@at@iloop #1!%
+{%
+ \ifnum #1=\z@ 0\expandafter\xint_bye\fi
+ \POL@sturm@index@at@iloop@a #1!%
+}%
+\def\POL@sturm@index@at@iloop@a #1!#2#3%
+{% #1 = index, #2 = sturmname, #3 value
+ \PolSturmIfZeroExactlyKnown{#2}{#1}
+ {\xintifCmp{#3}{\POL@xintexprGetVar{#2L_#1}}%
+ {}%
+ {#1\xint_bye}%
+ {0\xint_bye}%
+ }%
+ {\xintifGt{#3}{\POL@xintexprGetVar{#2L_#1}}%
+ {\xintifLt{#3}{\POL@xintexprGetVar{#2R_#1}}%
+ {#1\xint_bye}%
+ {0\xint_bye}%
+ }%
+ {}%
+ }%
+ % attention that catcode of ! is 11 in polexpr.sty
+ \expandafter\POL@sturm@index@at@iloop\the\numexpr#1-\@ne !{#2}{#3}%
+}%
+%
+\def\POL@leq@fork#1\LessThanOrEqualTo#2#3\krof{#2}%
+\def\PolSturmNbOfRootsOf#1#2#3{\romannumeral`&&@\POL@leq@fork
+ #2\PolNbOfRootsLessThanOrEqualTo
+ \LessThanOrEqualTo\PolNbOfRootsLessThanOrEqualToExpr\krof {#1}{#3}%
+}%
+\def\PolNbOfRootsLessThanOrEqualToExpr#1#2
+ {\PolNbOfRootsLessThanOrEqualTo{#1}{\xinttheexpr#2\relax}}%
+\def\PolNbOfRootsLessThanOrEqualTo#1{%
+ \ifnum\PolSturmNbOfIsolatedZeros{#1}=\z@
+ \expandafter\xint_firstofthree\expandafter0%
+ \else
+ \expandafter\PolNbOfRootsLessThanOrEqualTo@%
+ \fi {#1}%
+}%
+\def\PolNbOfRootsLessThanOrEqualTo@ #1#2%
+{%
+ \expandafter\POL@nbofrootsleq@prep\romannumeral`&&@#2!{#1}%
+}%
+\def\POL@nbofrootsleq@prep#1!#2%
+{%
+ \expandafter\POL@nbofrootsleq@iloop\expandafter 1\expandafter !%
+ \romannumeral0\xintsgn{\POL@eval{#2_0}{#1}}!%
+ #1!{#2}%
+}%
+\def\POL@nbofrootsleq@iloop#1!#2!#3!#4%
+{% #1 = index, #2 = sign of evaluation at value, #3 = value, #4 = sturmname
+ \xintifCmp{#3}{\POL@xintexprGetVar{#4L_#1}}%
+ {\POL@nbofrootsleq@return #1-\@ne !}%
+ {\POL@nbofrootsleq@return
+ \PolSturmIfZeroExactlyKnown{#4}{#1}{#1}{#1-\@ne}!%
+ }%
+ % in third branch we are sure that if root is exactly known
+ % the test \xintifLt will be negative
+ {\xintifLt{#3}{\POL@xintexprGetVar{#4R_#1}}%
+ {\POL@nbofrootsleq@return
+ #1\ifnum#2=\xintSgn{\POL@eval{#4_0}{\POL@xintexprGetVar{#4L_#1}}}
+ -\@ne\fi !%
+ }%
+ {\ifnum#1=\PolSturmNbOfIsolatedZeros{#4}
+ \expandafter\POL@nbofrootsleq@rightmost
+ \fi \expandafter\POL@nbofrootsleq@iloop \the\numexpr\@ne+%
+ }%
+ }%
+ #1!#2!#3!{#4}%
+}%
+\def\POL@nbofrootsleq@return #1!#2!#3!#4!#5{\the\numexpr #1\relax}%
+\def\POL@nbofrootsleq@rightmost\expandafter\POL@nbofrootsleq@iloop
+ \the\numexpr\@ne+#1!#2!#3!#4{#1}%
+%
+\def\PolSturmNbWithMultOfRootsOf#1#2#3{%
+ \the\numexpr0\POL@leq@fork
+ #2\PolNbWithMultOfRootsLessThanOrEqualTo
+ \LessThanOrEqualTo\PolNbWithMultOfRootsLessThanOrEqualToExpr\krof {#1}{#3}%
+}%
+\def\PolNbWithMultOfRootsLessThanOrEqualToExpr#1#2{%
+ \PolNbWithMultOfRootsLessThanOrEqualTo{#1}{\xinttheexpr#2\relax}%
+}%
+\def\PolNbWithMultOfRootsLessThanOrEqualTo#1{%
+ \ifnum\PolSturmNbOfIsolatedZeros{#1}=\z@
+ \expandafter\POL@nbwmofroots@noroots
+ \else
+ \expandafter\PolNbWithMultOfRootsLessThanOrEqualTo@%
+ \fi {#1}%
+}%
+\def\POL@nbwmofroots@noroots#1#2{\relax}%
+\def\PolNbWithMultOfRootsLessThanOrEqualTo@ #1#2%
+{%
+ \expandafter\POL@nbwmofrootsleq@prep\romannumeral`&&@#2!{#1}%
+}%
+\def\POL@nbwmofrootsleq@prep#1!#2%
+{%
+ \expandafter\POL@nbwmofrootsleq@iloop\expandafter 1\expandafter !%
+ \romannumeral0\xintsgn{\POL@eval{#2_0}{#1}}!%
+ #1!{#2}%
+}%
+\def\POL@nbwmofrootsleq@iloop#1!#2!#3!#4%
+{% #1 = index, #2 = sign of evaluation at value, #3 = value, #4 = sturmname
+ \xintifCmp{#3}{\POL@xintexprGetVar{#4L_#1}}%
+ {\POL@nbwmofrootsleq@return !}%
+ {\POL@nbwmofrootsleq@return
+ \PolSturmIfZeroExactlyKnown{#4}{#1}%
+ {+\PolSturmIsolatedZeroMultiplicity{#4}{#1}}{}!%
+ }%
+ % in third branch we are sure that if root is exactly known
+ % the test \xintifLt will be negative
+ {\xintifLt{#3}{\POL@xintexprGetVar{#4R_#1}}%
+ {\POL@nbwmofrootsleq@return
+ \unless
+ \ifnum#2=\xintSgn{\POL@eval{#4_0}{\POL@xintexprGetVar{#4L_#1}}}
+ +\PolSturmIsolatedZeroMultiplicity{#4}{#1}\fi !%
+ }%
+ {+\PolSturmIsolatedZeroMultiplicity{#4}{#1}%
+ \ifnum#1=\PolSturmNbOfIsolatedZeros{#4}
+ \expandafter\POL@nbwmofrootsleq@return\expandafter !%
+ \fi
+ \expandafter\POL@nbwmofrootsleq@iloop \the\numexpr\@ne+%
+ }%
+ }%
+ #1!#2!#3!{#4}%
+}%
+\def\POL@nbwmofrootsleq@return #1!#2!#3!#4!#5{#1\relax}%
+\endinput