diff options
Diffstat (limited to 'Master/texmf-dist/tex/generic/polexpr/polexprsturm.tex')
-rw-r--r-- | Master/texmf-dist/tex/generic/polexpr/polexprsturm.tex | 1775 |
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 |