%% filename: polexprsturm.tex %% Part of the polexpr package (0.8.7a, 2022/05/19) %% %% Implements the Sturm localization Algorithm %% Added at 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 compatibility bug with xint 1.4 internal format %% regarding the defined \xintexpr variables holding the localization %% intervals extremities %% - also, it uses the prem() in computing the Sturm chain, with a 3X %% speed gain in the case of the "perturbed" first Wilkinson example %% 0.8.6 has better a priori bounds for positive and negative roots \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 defining \POL@sturmname %% and \POL@sturm@N as it currently 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_}% \POL@makeprimitive{\POL@sturmname _1_}% does not do \POL@newpol \POL@count\@ne \xintloop \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 }% % attention that this modifies current \POL@count value \def\POL@sturmchain@getSV@at#1{% \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@}% }% \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}% \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 \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 % 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}}% \POL@initarray\POL@IfMultIsKnown\xint_secondoftwo \let\POL@findrat@nbofirrroots\POL@isolz@NbOfRoots % find all rational roots, and their multiplicities, % factor them out 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 array holding the interval indices for the rational roots \expandafter\POL@findrat@doRRarray\csname POL_RI\POL@sturmname*\endcsname \fi % declare the new polynomials (also if no real roots exist) \POL@newpol{\POL@sturmname\POL@sqfnorr}% without multiplicities \POL@newpol{\POL@sturmname\POL@norr}% with multiplicities }% \def\POL@findrat@doRRarray#1{% \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 \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 \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}% \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 \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 % 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{% \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}}}% % using ii version is an abuse \xintiiifNeg{\POL@findrat@Lscaled}% {% 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 \csname POLuserpol@@_1\POL@sturmname _\endcsname\POL@Q \ifnum\PolDegree{@_1\POL@sturmname _}>\z@ \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 this 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 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@pos{\the\numexpr\xint_zapspaces #1 \xint_gobble_i\relax}% \let\POL@isolz@E@neg\POL@isolz@E@pos \POL@sturmchain@getSV@at{1[\POL@isolz@E@pos]}% \let\POL@isolz@plusinf@SV \POL@sturmchain@SV \let\POL@isolz@plusinf@sign\POL@sturmchain@sign \POL@sturmchain@getSV@at{-1[\POL@isolz@E@neg]}% \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@pos}% {Try again with a larger exponent. (X to abort).}% \fi \ifnum\POL@isolz@minusinf@sign=\z@ \PackageError{polexpr}% {The polynomial #2 vanishes at set lower bound -10^\POL@isolz@E@neg}% {Try again with a larger exponent. (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 % (is this to be considered as a bug of xinttools?) % We use an \empty trick to avoid that. \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 }% % This utility macro bounds positive roots (strictly) by a 10^Epos % and negative roots strictly by some -10^Eneg. % (prior to 0.8.6, an E was found with -10^E < all roots < 10^E) % To obtain Epos, the Cauchy bound "1 + max_j {-a_j/lc(P)|}" % is used, where non-negative a_j/lc(P)'s are ignored. % In case the a_j's all have same sign as lc(P) or vanish, there are % no positive roots. And the macro in this case outputs an E=0 exponent. % But if at least one non-zero a_j has opposite sign to the leading coeff, % the produced E will be at least 1. % Thus if E=0 on exit, it is proof that there are no (positive) roots. \def\POL@isolz@updateE #1;% {\unless\ifnum#1<\POL@isolz@E\space\edef\POL@isolz@E{\the\numexpr#1+\@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 % We want an E such that 0 < positive roots < +10^E \def\POL@isolz@E{0}% \advance\POL@count\m@ne \xintloop \ifnum\POL@count>\z@ % only those coefficients with opposite sign to the leading coefficient % trigger an E update \xintiiifSgn{\POL@arrayA\POL@count}% {\expandafter\POL@isolz@updateE \the\numexpr\xintilogten{\xintAdd{1/1[0]}{\xintiiOpp{\POL@arrayA\POL@count}}};% }{}{}% \advance\POL@count\m@ne \repeat \let\POL@isolz@E@pos\POL@isolz@E % We want an E such that 0 > negative roots > -10^E \def\POL@isolz@E{0}% \POL@count\POL@arrayA{0}\relax \advance\POL@count\m@ne \xintloop \ifnum\POL@count>\@ne \xintiiifSgn{\xintiiOpp{\POL@arrayA\POL@count}}% {\expandafter\POL@isolz@updateE \the\numexpr\xintilogten{\xintAdd{1/1[0]}{\POL@arrayA\POL@count}};% }{}{}% \advance\POL@count\m@ne \xintiiifSgn{\POL@arrayA\POL@count}% {\expandafter\POL@isolz@updateE \the\numexpr\xintilogten{\xintAdd{1/1[0]}{\xintiiOpp{\POL@arrayA\POL@count}}};% }{}{}% \advance\POL@count\m@ne \repeat \ifnum\POL@count=\@ne \xintiiifSgn{\xintiiOpp{\POL@arrayA\POL@count}}% {\expandafter\POL@isolz@updateE \the\numexpr\xintilogten{\xintAdd{1/1[0]}{\POL@arrayA\POL@count}};% }{}{}% \fi \let\POL@isolz@E@neg\POL@isolz@E \ifxintverbose \xintMessage{polexpr}{Info}{Epos=\POL@isolz@E@pos, Eneg=\POL@isolz@E@neg.}% \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 {% \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}% % 0.8.6 has separate initial E's for positive and negative roots \let\POL@isolz@E\POL@isolz@E@neg \ifnum\POL@isolz@NbOfNegRoots>\z@ % refactored at 0.7 to fix cases leading to intervals having zero as end-point \POL@isolz@findroots@neg \fi \let\POL@isolz@E\POL@isolz@E@pos \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{% \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 now 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 \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 has been 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... \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 \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{% \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@ % attention that \POL@count would be modified by \POL@sturmchain@getSV@at % but this latter macro not invoked by \POL@ensure@one \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}% \ifnum\POL@isolz@IntervalIndex>\z@ \ifnum\csname POL_ZL\POL@sturmname*0\endcsname>\z@ \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, the starred version is an example of customization % It is itself basically not easily customizable, except for this: \def\PolPrintIntervals@@arraystretch{2}% (the 2 was hardcoded prior to 0.8.6) \def\PolPrintIntervals@@{% \begingroup \def\POL@AfterPrintIntervals{\endgroup}% \let\PolPrintIntervalsPrintExactZero\POL@@PrintIntervalsPrintExactZero \let\PolPrintIntervalsUnknownRoot\POL@@PrintIntervalsUnknownRoot \let\PolPrintIntervalsKnownRoot\POL@@PrintIntervalsKnownRoot \ifdefined\array \let\arraystretch\PolPrintIntervals@@arraystretch \def\PolPrintIntervalsBeginEnv{\[\begin{array}{cl}}%\] \def\PolPrintIntervalsEndEnv{\end{array}\]}% \else \def\PolPrintIntervalsBeginEnv{$$\tabskip0pt plus 1000pt minus 1000pt \halign to\displaywidth\bgroup \hfil\vrule height \PolPrintIntervals@@arraystretch\ht\strutbox depth \PolPrintIntervals@@arraystretch\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}% % This is added at 0.8.6 to allow usage of amsmath environment as they typeset % twice: we must prepare for a second execution. Adds slight general overhead. \gdef\noexpand\PolPrintIntervalsTheIndex{1}% \noexpand\POL@PrintIntervals@DoDefs \unexpanded\expandafter{\PolPrintIntervalsEndEnv}% }\POL@tmp \fi \POL@AfterPrintIntervals \def\PolPrintIntervalsTheVar{#1}% \def\PolPrintIntervalsTheSturmName{#2}% }% \let\POL@AfterPrintIntervals\empty \let\PolPrintIntervalsNoRealRoots\empty \def\PolPrintIntervalsArrayStretch{1}% used by non-starred version \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 \xintTeXsignedFrac{\PolPrintIntervalsTheLeftEndPoint}% }% \else \def\POL@@PrintIntervalsPrintExactZero{% \displaystyle \xintTeXsignedOver{\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{% \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{\PolPrintIntervalsRowSeparator\POL@PrintIntervals@Loop}% \fi }% % added at 0.8.6: \ifdefined\array \def\PolPrintIntervalsRowSeparator{\\}% \else \def\PolPrintIntervalsRowSeparator{\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