From 93809c868bf15852ddc7b50542713131a5d8c05f Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Tue, 30 Mar 2021 03:00:56 +0000 Subject: CTAN sync 202103300300 --- macros/latex/contrib/polexpr/README.md | 102 - macros/latex/contrib/polexpr/polexpr.html | 2911 -------------------------- macros/latex/contrib/polexpr/polexpr.sty | 3164 ----------------------------- macros/latex/contrib/polexpr/polexpr.txt | 2598 ----------------------- 4 files changed, 8775 deletions(-) delete mode 100644 macros/latex/contrib/polexpr/README.md delete mode 100644 macros/latex/contrib/polexpr/polexpr.html delete mode 100644 macros/latex/contrib/polexpr/polexpr.sty delete mode 100644 macros/latex/contrib/polexpr/polexpr.txt (limited to 'macros/latex/contrib/polexpr') diff --git a/macros/latex/contrib/polexpr/README.md b/macros/latex/contrib/polexpr/README.md deleted file mode 100644 index a901b56adc..0000000000 --- a/macros/latex/contrib/polexpr/README.md +++ /dev/null @@ -1,102 +0,0 @@ -Package polexpr README -====================== - -License -------- - -Copyright (C) 2018-2020 Jean-François Burnol - -See documentation of package [xint](http://www.ctan.org/pkg/xint) for -contact information. - -This Work may be distributed and/or modified under the conditions of the -LaTeX Project Public License version 1.3c. This version of this license -is in - -> - -and version 1.3 or later is part of all distributions of LaTeX version -2005/12/01 or later. - -This Work has the LPPL maintenance status author-maintained. - -The Author of this Work is Jean-François Burnol. - -This Work consists of the package file polexpr.sty, this README.md and -the documentation file polexpr.txt. - -Abstract --------- - -The package provides `\poldef`. This a parser of polynomial expressions -based upon the `\xintdeffunc` mechanism of xintexpr. - -The parsed expressions use the operations of algebra (inclusive of -composition of functions) with standard operators, fractional numbers -(possibly in scientific notation) and previously defined polynomial -functions or other constructs as recognized by the `\xintexpr` numerical -parser. - -The polynomials are then not only genuine `\xintexpr` (and -`\xintfloatexpr`) numerical functions but additionally are known to the -package via their coefficients. This allows dedicated macros to -implement polynomial algorithmics. - -Releases --------- - -- 0.1 (2018/01/11) - Initial release (files README, polexpr.sty). -- 0.2 (2018/01/14) - Documentation moved to polexpr.{txt,html}. -- 0.3 (2018/01/17) - Make polynomials known to `\xintfloatexpr` and improve - documentation. -- 0.3.1 (2018/01/18) - Fix two typos in documentation. -- 0.4 (2018/02/16) - - Revert 0.3 automatic generation of floating point variants. - - Move CHANGE LOG from README.md to HTML documentation. - - A few bug fixes and breaking changes. Please refer to - `polexpr.html`. - - Main new feature: root localization via [Sturm - Theorem](https://en.wikipedia.org/wiki/Sturm%27s_theorem). -- 0.4.1 (2018/03/01) - Synced with xint 1.3. -- 0.4.2 (2018/03/03) - Documentation fix. -- 0.5 (2018/04/08) - - new macros `\PolMakePrimitive` and `\PolIContent`. - - main (breaking) change: `\PolToSturm` creates a chain of primitive - integer coefficients polynomials. -- 0.5.1 (2018/04/22) - The `'` character can be used in polynomial names. -- 0.6 (2018/11/20) - New feature: multiplicity of roots. -- 0.7 (2018/12/08), 0.7.1 (bugfix), 0.7.2 (bugfix) (2018/12/09) - New feature: finding all rational roots. -- 0.7.3 (2019/02/04) - Bugfix: polynomial names ending in digits caused errors. Thanks to - Thomas Söll for report. -- 0.7.4 (2019/02/12) - Bugfix: 20000000000 is too big for \numexpr, shouldn't I know that? - Thanks to Jürgen Gilg for report. -- 0.7.5 (2020/01/31) - Synced with xint 1.4. Requires it. - -Files of 0.7.5 release: - -- README.md, -- polexpr.sty (package file), -- polexpr.txt (documentation), -- polexpr.html (conversion via - [DocUtils](http://docutils.sourceforge.net/docs/index.html) - rst2html.py) - -Acknowledgments ---------------- - -Thanks to Jürgen Gilg whose question about -[xint](http://www.ctan.org/pkg/xint) usage for differentiating -polynomials was the initial trigger leading to this package, and to -Jürgen Gilg and Thomas Söll for testing it on some concrete problems. diff --git a/macros/latex/contrib/polexpr/polexpr.html b/macros/latex/contrib/polexpr/polexpr.html deleted file mode 100644 index 7496332976..0000000000 --- a/macros/latex/contrib/polexpr/polexpr.html +++ /dev/null @@ -1,2911 +0,0 @@ - - - - - - -Package polexpr documentation - - - -
-

Package polexpr documentation

-

0.7.5 (2020/01/31)

- - -
-

Contents

- -
-
-

Basic syntax

-

The syntax is:

-
-\poldef polname(x):= expression in variable x;
-
-

where:

-
    -
  • in place of x an arbitrary dummy variable is authorized, -i.e. per default any of [a-z|A-Z] (more letters can be declared -under Unicode engines.)
  • -
  • polname consists of letters, digits, and the _ and -' characters. It must start with a letter.
  • -
-
-

Attention!

-

The ' is authorized since 0.5.1. As a result some constructs -recognized by the \xintexpr parser, such as var1 'and' var2 -will get misinterpreted and cause errors. However these constructs -are unlikely to be frequently needed in polynomial expressions, and -the \xintexpr syntax offers alternatives, so it was deemed a -small evil. Of course the \xintexpr parser is modified only -temporarily during execution of \poldef.

-
-

One can also issue:

-
-\PolDef{polname}{expression in variable x}
-
-

which admits an optional first argument to modify the variable letter -from its default x.

-
-
\poldef f(x):= 1-x+x^2;
-
defines polynomial f. Polynomial names must start with a -letter and may contain letters, digits, underscores and the right -tick character. The -variable must be a single letter. The colon character is optional. -The semi-colon at end of expression is mandatory.
-
\PolDef{f}{1-x+x^2}
-
does the same as \poldef f(x):= 1-x+x^2; To use another letter -than x in the expression, one must pass it as an extra optional -argument to \PolDef. Useful if the semi-colon has been assigned -some non-standard catcode by some package.
-
\PolLet{g}={f}
-
saves a copy of f under name g. Also usable without =.
-
\poldef f(z):= f(z)^2;
-
redefines f in terms of itself.
-
\poldef f(T):= f(f(T));
-
again redefines f in terms of its (new) self.
-
\poldef k(z):= f(z)-g(g(z)^2)^2;
-
should now define the zero polynomial... Let's check: -\[ k(z) = \PolTypeset[z]{k} \]
-
\PolDiff{f}{f'}
-
sets f' to the derivative of f. The name doesn't have to be -f' (in fact the ' is licit only since 0.5.1).
-
-
-

Important

-

This is not done automatically. If some new definition needs to use -the derivative of some available polynomial, that derivative -polynomial must have been defined via \PolDiff: something like -T'(x)^2 will not work without a prior \PolDiff{T}{T'}.

-
-
-
\PolDiff{f'}{f''}
-
obtains second derivative.
-
\PolDiff[3]{f}{f'''}
-
computes the third derivative.
-
-
-$f(z)   = \PolTypeset[z]{f}    $\newline
-$f'(z)  = \PolTypeset[z]{f'}   $\newline
-$f''(z) = \PolTypeset[z]{f''}  $\newline
-$f'''(z)= \PolTypeset[z]{f'''} $\par
-
-
-

Important

-

The package does not currently know rational functions: / in -a parsed polynomial expression does the Euclidean quotient:

-
-(1-x^2)/(1-x)
-
-

does give 1+x but

-
-(1/(1-x))*(1-x^2)
-
-

evaluates to zero. This will work as expected:

-
-\poldef k(x):= (x-1)(x-2)(x-3)(x-4)/(x^2-5x+4);
-
-
-
-

Attention!

-

1/2 x^2 skips the space and is treated like 1/(2*x^2) because -of the tacit multiplication rules of xintexpr. But this means it -gives zero! Thus one must use (1/2)x^2 or 1/2*x^2 or -(1/2)*x^2 for disambiguation: x - 1/2*x^2 + 1/3*x^3.... It is -even simpler to move the denominator to the right: x - x^2/2 + -x^3/3 - ....

-

It is worth noting that 1/2(x-1)(x-2) suffers the same issue: -xint tacit multiplication always "ties more", hence this gets -interpreted as 1/(2*(x-1)*(x-2)) which gives zero by polynomial -division. Thus, use one of (1/2)(x-1)(x-2), 1/2*(x-1)(x-2) or -(x-1)(x-2)/2.

-
-

After:

-
-\poldef f_1(x):= 25(x-1)(x^2-2)(x-3)(x-4)(x-5);%
-\poldef f_2(x):= 37(x-1)(x^2-2)(x-6)(x-7)(x-8);%
-
-

the macro call \PolGCD{f_1}{f_2}{k} sets k to the (unitary) GCD of -f_1 and f_2 (hence to the expansion of (x-1)(x^2-2).)

-
-
\PolToExpr{k}
-
will (expandably) give in this case x^3-x^2-2*x+2. This is -useful for console or file output (the syntax is Maple- and -PSTricks-compatible; the letter used in output can be -(non-expandably) changed via a redefinition of \PolToExprVar.)
-
\PolToExpr*{k}
-
gives ascending powers: 2-2*x-x^2+x^3.
-
-
-
-

Examples of localization of roots

-
    -
  • To make printed decimal numbers more enjoyable than via -\xintSignedFrac:

    -
    -\renewcommand\PolTypesetOne[1]{\PolDecToString{\xintREZ{#1}}}%
    -
    -

    \PolDecToString will use decimal notation to incorporate the power -of ten part; and the \xintREZ will have the effect to suppress -trailing zeros if present in raw numerator (if those digits end up -after decimal mark.) Notice that the above are expandable macros and -that one can also do:

    -
    -\renewcommand\PolToExprCmd[1]{\PolDecToString{\xintREZ{#1}}}%
    -
    -

    to modify output of \PolToExpr{polname}.

    -
  • -
  • For extra info in log file use \xintverbosetrue.

    -
  • -
  • Only for some of these examples is the output included here.

    -
  • -
-
-

A typical example

-

In this example the polynomial is square-free.

-
-\poldef f(x) := x^7 - x^6 - 2x + 1;
-
-\PolToSturm{f}{f}
-\PolSturmIsolateZeros{f}
-The \PolTypeset{f} polynomial has \PolSturmNbOfIsolatedZeros{f} distinct real
-roots which are located in the following intervals:
-\PolPrintIntervals{f}
-Here is the second root with ten more decimal digits:
-\PolRefineInterval[10]{f}{2}
-\[\PolSturmIsolatedZeroLeft{f}{2}<Z_2<\PolSturmIsolatedZeroRight{f}{2}\]
-And here is the first root with twenty digits after decimal mark:
-\PolEnsureIntervalLength{f}{1}{-20}
-\[\PolSturmIsolatedZeroLeft{f}{1}<Z_1<\PolSturmIsolatedZeroRight{f}{1}\]
-The first element of the Sturm chain has degree $\PolDegree{f_0}$. As
-this is the original degreee $\PolDegree{f}$ we know that $f$ is square free.
-Its derivative is up to a constant \PolTypeset{f_1} (in this example
-it is identical with it).
-\PolToSturm{f_1}{f_1}\PolSturmIsolateZeros{f_1}%
-The derivative has \PolSturmNbOfIsolatedZeros{f_1} distinct real
-roots:
-\PolPrintIntervals[W]{f_1}
-\PolEnsureIntervalLengths{f_1}{-10}%
-Here they are with ten digits after decimal mark:
-\PolPrintIntervals[W]{f_1}
-\PolDiff{f_1}{f''}
-\PolToSturm{f''}{f''}
-\PolSturmIsolateZeros{f''}
-The second derivative is \PolTypeset{f''}.
-It has \PolSturmNbOfIsolatedZeros{f''} distinct real
-roots:
-\PolPrintIntervals[X]{f''}
-Here is the positive one with 20 digits after decimal mark:
-\PolEnsureIntervalLength{f''}{2}{-20}%
-\[X_2 = \PolSturmIsolatedZeroLeft{f''}{2}\dots\]
-The more mathematically advanced among our dear readers will be able
-to give the exact value for $X_2$!
-
-
-
-

A degree four polynomial with nearby roots

-

Notice that this example is a bit outdated as 0.7 release has -added \PolSturmIsolateZeros**{sturmname} which would find exactly -the roots. The steps here retain their interest when one is interested -in finding isolating intervals for example to prepare some demonstration -of dichotomy method.

-
-\PolDef{Q}{(x-1.050001)(x-1.105001)(x-1.110501)(x-1.111051)}
-\PolTypeset{Q}
-\PolToSturm{Q}{Q} % it is allowed to use same prefix for Sturm chain
-\PolSturmIsolateZeros{Q}
-\PolPrintIntervals{Q}
-% reports 1.0 < Z_1 < 1.1, 1.10 < Z_2 < 1.11, 1.110 < Z_3 < 1.111, and 1.111 < Z_4 < 1.112
-% but the above bounds do not allow minimizing separation between roots
-% so we refine:
-\PolRefineInterval*{Q}{1}
-\PolRefineInterval*{Q}{2}
-\PolRefineInterval*{Q}{3}
-\PolRefineInterval*{Q}{4}
-\PolPrintIntervals{Q}
-% reports 1.05 < Z_1 < 1.06, 1.105 < Z_2 < 1.106, 1.1105 < Z_3 < 1.1106,
-% and 1.11105 < Z_4 < 1.11106.
-\PolEnsureIntervalLengths{Q}{-6}
-\PolPrintIntervals{Q}
-% of course finds here all roots exactly
-
-
-
-

The degree nine polynomial with 0.99, 0.999, 0.9999 as triple roots

-
-% define a user command (xinttools is loaded automatically by polexpr)
-\newcommand\showmultiplicities[1]{% #1 = "sturmname"
-\xintFor* ##1 in {\xintSeq{1}{\PolSturmNbOfIsolatedZeros{#1}}}\do{%
-    The multiplicity is \PolSturmIsolatedZeroMultiplicity{#1}{##1}
-    \PolSturmIfZeroExactlyKnown{#1}{##1}%
-    {at the root $x=\PolSturmIsolatedZeroLeft{#1}{##1}$}
-    {for the root such that
-    $\PolSturmIsolatedZeroLeft{#1}{##1}<x<\PolSturmIsolatedZeroRight{#1}{##1}$}
-    \par
-}}%
-\PolDef{f}{(x-0.99)^3(x-0.999)^3(x-0.9999)^3}
-\renewcommand\PolTypesetOne[1]{\PolDecToString{\xintREZ{#1}}}
-\PolTypeset{f}\par
-\PolToSturm{f}{f}% it is allowed to use "polname" as "sturmname" too
-\PolSturmIsolateZerosAndGetMultiplicities{f}% use the "sturmname" here
-% or \PolSturmIsolateZeros*{f} which is exactly the same, but shorter..
-
-\showmultiplicities{f}
-
-

In this example, the output will look like this (but using math mode):

-
-x^9 - 8.9667x^8 + 35.73400293x^7 - 83.070418400109x^6 + 124.143648875193123x^5
-- 123.683070924326075877x^4 + 82.149260397553075617891x^3
-- 35.07602992699900159127007x^2 + 8.7364078733314648368671733x
-- 0.967100824643585986488103299
-
-The multiplicity is 3 at the root x = 0.99
-The multiplicity is 3 at the root x = 0.999
-The multiplicity is 3 at the root x = 0.9999
-
-

On first pass, these rational roots were found (due to their relative -magnitudes, using \PolSturmIsolateZeros** was not needed here). But -multiplicity computation works also with (decimal) roots not yet -identified or with non-decimal or irrational roots.

-

It is fun to modify only a tiny bit the polynomial and see if polexpr -survives:

-
-\PolDef{g}{f(x)+1e-27}
-\PolTypeset{g}\par
-\PolToSturm{g}{g}
-\PolSturmIsolateZeros*{g}
-
-\showmultiplicities{g}
-
-

This produces:

-
-x^9 - 8.9667x^8 + 35.73400293x^7 - 83.070418400109x^6 + 124.143648875193123x^5
-- 123.683070924326075877x^4 + 82.149260397553075617891x^3
-- 35.07602992699900159127007x^2 + 8.7364078733314648368671733x
-- 0.967100824643585986488103298
-
-The multiplicity is 1 for the root such that 0.98 < x < 0.99
-The multiplicity is 1 for the root such that 0.9991 < x < 0.9992
-The multiplicity is 1 for the root such that 0.9997 < x < 0.9998
-
-

Which means that the multiplicity-3 roots each became a real and a pair of -complex ones. Let's see them better:

-
-\PolEnsureIntervalLengths{g}{-10}
-
-\showmultiplicities{g}
-
-

which produces:

-
-The multiplicity is 1 for the root such that 0.9899888032 < x < 0.9899888033
-The multiplicity is 1 for the root such that 0.9991447980 < x < 0.9991447981
-The multiplicity is 1 for the root such that 0.9997663986 < x < 0.9997663987
-
-
-
-

A degree five polynomial with three rational roots

-
-\poldef Q(x) :=  1581755751184441 x^5
-               -14907697165025339 x^4
-               +48415668972339336 x^3
-               -63952057791306264 x^2
-               +46833913221154895 x
-               -49044360626280925;
-
-\PolToSturm{Q}{Q}
-%\begin{flushleft}
-  \renewcommand\PolTypesetCmdPrefix[1]{\allowbreak\xintiiifSgn{#1}{}{+}{+}}%
-  $Q_0(x) = \PolTypeset{Q_0}$
-%\end{flushleft}
-\PolSturmIsolateZeros**{Q}
-\PolPrintIntervals{Q}
-
-$Q_{norr}(x) = \PolTypeset{Q_norr}$
-
-

Here, all real roots are rational:

-
-Z_1 = 833719/265381
-Z_2 = 165707065/52746197
-Z_3 = 355/113
-
-Q_norr(x) = x^2 + 1
-
-

And let's get their decimal expansion too:

-
-% print decimal expansion of the found roots
-\renewcommand\PolPrintIntervalsPrintExactZero
-            {\xintTrunc{20}{\PolPrintIntervalsTheLeftEndPoint}\dots}
-\PolPrintIntervals{Q}
-
-Z_1 = 3.14159265358107777120...
-Z_2 = 3.14159265358979340254...
-Z_3 = 3.14159292035398230088...
-
-
-
-

A Mignotte type polynomial

-
-\PolDef{P}{x^10 - (10x-1)^2}%
-\PolTypeset{P}              % prints it in expanded form
-\PolToSturm{P}{P}           % we can use same prefix for Sturm chain
-\PolSturmIsolateZeros{P}    % finds 4 real roots
-This polynomial has \PolSturmNbOfIsolatedZeros{P} distinct real roots:
-\PolPrintIntervals{P}%
-% reports  -2 < Z_1 < -1, 0.09 < Z_2 < 0.10, 0.1 < Z_3 < 0.2, 1 < Z_4 < 2
-Let us refine the second and third intervals to separate the corresponding
-roots:
-\PolRefineInterval*{P}{2}% will refine to 0.0999990 < Z_2 < 0.0999991
-\PolRefineInterval*{P}{3}% will refine to 0.100001 < Z_3 < 0.100002
-\PolPrintIntervals{P}%
-Let us now get to know all roots with 10 digits after decimal mark:
-\PolEnsureIntervalLengths{P}{-10}%
-\PolPrintIntervals{P}% now all roots are known 10 decimal digits after mark
-Finally, we display 20 digits of the second root:
-\PolEnsureIntervalLength{P}{2}{-20}% makes Z_2 known with 20 digits after mark
-\[\PolSturmIsolatedZeroLeft{P}{2}<Z_2<\PolSturmIsolatedZeroRight{P}{2}\]
-
-

The last line produces:

-
-0.09999900004999650028 < Z_2 < 0.09999900004999650029
-
-
-
-

The Wilkinson polynomial

-

See Wilkinson polynomial.

-
-\documentclass{article}
-\usepackage{polexpr}
-\begin{document}
-%\xintverbosetrue % for the curious...
-
-\poldef f(x) := mul((x - i), i = 1..20);
-
-\renewcommand\PolTypesetCmdPrefix[1]{\allowbreak\xintiiifSgn{#1}{}{+}{+}}%
-\renewcommand\PolTypesetOne[1]{\xintDecToString{#1}}%
-
-\noindent\PolTypeset{f}
-
-\PolToSturm{f}{f}
-\PolSturmIsolateZeros{f}
-\PolPrintIntervals{f}
-
-\clearpage
-
-\poldef g(x) := f(x) - 2**{-23} x**19;
-
-% be patient!
-\PolToSturm{g}{g}
-\noindent\PolTypeset{g_0}% integer coefficient primitive polynomial
-
-\PolSturmIsolateZeros{g}
-\PolEnsureIntervalLengths{g}{-10}
-
-\renewcommand\PolPrintIntervalsPrintMultiplicity{}
-\PolPrintIntervals*{g}
-
-\end{document}
-
-

The first polynomial:

-
-f(x) = x**20
-- 210 x**19
-+ 20615 x**18
-- 1256850 x**17
-+ 53327946 x**16
-- 1672280820 x**15
-+ 40171771630 x**14
-- 756111184500 x**13
-+ 11310276995381 x**12
-- 135585182899530 x**11
-+ 1307535010540395 x**10
-- 10142299865511450 x**9
-+ 63030812099294896 x**8
-- 311333643161390640 x**7
-+ 1206647803780373360 x**6
-- 3599979517947607200 x**5
-+ 8037811822645051776 x**4
-- 12870931245150988800 x**3
-+ 13803759753640704000 x**2
-- 8752948036761600000 x
-+ 2432902008176640000
-
-

is handled fast enough (a few seconds), but the modified one f(x) - -2**-23 x**19 takes about 20x longer (the Sturm chain polynomials -have integer coefficients with up to 321 digits, whereas (surprisingly -perhaps) those of the Sturm chain polynomials derived from f never -have more than 21 digits ...).

-

Once the Sturm chain is computed and the zeros isolated, obtaining their -decimal digits is relatively faster. Here is for the ten real roots of -f(x) - 2**-23 x**19 as computed by the code above:

-
-Z_1 = 0.9999999999...
-Z_2 = 2.0000000000...
-Z_3 = 2.9999999999...
-Z_4 = 4.0000000002...
-Z_5 = 4.9999999275...
-Z_6 = 6.0000069439...
-Z_7 = 6.9996972339...
-Z_8 = 8.0072676034...
-Z_9 = 8.9172502485...
-Z_10 = 20.8469081014...
-
-
-
-

The second Wilkinson polynomial

-
-\documentclass{article}
-\usepackage{polexpr}
-\begin{document}
-\poldef f(x) := mul(x - 2^-i, i = 1..20);
-
-%\PolTypeset{f}
-
-\PolToSturm{f}{f}
-\PolSturmIsolateZeros**{f}
-\PolPrintIntervals{f}
-\end{document}
-
-

This takes more time than the polynomial with 1, 2, .., 20 as roots but -less than the latter modified by the 2**-23 change in one -coefficient.

-

Here is the output (with release 0.7.2):

-
-Z_1  = 0.00000095367431640625
-Z_2  = 0.0000019073486328125
-Z_3  = 0.000003814697265625
-Z_4  = 0.00000762939453125
-Z_5  = 0.0000152587890625
-Z_6  = 0.000030517578125
-Z_7  = 0.00006103515625
-Z_8  = 0.0001220703125
-Z_9  = 1/4096
-Z_10 = 1/2048
-Z_11 = 1/1024
-Z_12 = 1/512
-Z_13 = 1/256
-Z_14 = 1/128
-Z_15 = 0.015625
-Z_16 = 0.03125
-Z_17 = 0.0625
-Z_18 = 0.125
-Z_19 = 0.25
-Z_20 = 0.5
-
-

There is some incoherence in output format which has its source in the -fact that some roots are found in branches which can only find decimal -roots, whereas some are found in branches which could find general -fractions and they use \xintIrr before storage of the found root. -This may evolve in future.

-
-
-

The degree 41 polynomial with -2, -1.9, -1.8, ..., 0, 0.1, ..., 1.9, 2 as roots

-
-\PolDef{P}{mul((x-i*1e-1), i=-20..20)}% i/10 is same but less efficient
-
-

In the defining expression we could have used i/10 but this gives -less efficient internal form for the coefficients (the 10's end up -in denominators). Using \PolToExpr{P} after having done

-
-\renewcommand\PolToExprCmd[1]{\PolDecToString{\xintREZ{#1}}}
-
-

we get this expanded form:

-
-x^41
--28.7*x^39
-+375.7117*x^37
--2975.11006*x^35
-+15935.28150578*x^33
--61167.527674162*x^31
-+173944.259366417394*x^29
--373686.963560544648*x^27
-+613012.0665016658846445*x^25
--771182.31133138163125495*x^23
-+743263.86672885754888959569*x^21
--545609.076599482896371978698*x^19
-+301748.325708943677229642930528*x^17
--123655.8987669450434698869844544*x^15
-+36666.1782054884005855608205864192*x^13
--7607.85821367459445649518380016128*x^11
-+1053.15135918687298508885950223794176*x^9
--90.6380005918141132650786081964032*x^7
-+4.33701563847327366842552218288128*x^5
--0.0944770968420804735498178265088*x^3
-+0.00059190121813899276854174416896*x
-
-

which shows coefficients with up to 36 significant digits...

-

Stress test: not a hard challenge to xint + polexpr, but be a bit patient!

-
-\PolDef{P}{mul((x-i*1e-1), i=-20..20)}%
-\PolToSturm{P}{S}           % dutifully computes S_0, ..., S_{41}
-% the [1] optional argument limits the search to interval (-10,10)
-\PolSturmIsolateZeros[1]{S} % finds *exactly* (but a bit slowly) all 41 roots!
-\PolPrintIntervals{S}       % nice, isn't it?
-
-
-

Note

-

Release 0.5 has experimental addition of optional argument -E to \PolSturmIsolateZeros. It instructs to search roots only -in interval (-10^E, 10^E). Important: the extremities are -assumed to not be roots. In this example, the [1] in -\PolSturmIsolateZeros[1]{S} gives some speed gain; without it, it -turns out in this case that polexpr would have started with -(-10^6, 10^6) interval.

-

Please note that this will probably get replaced in future by the -specification of a general interval. Do not rely on meaning of this -optional argument keeping the same.

-
-
-
-

Roots of Chebyshev polynomials

-
-\newcount\mycount
-\poldef T_0(x) := 1;
-\poldef T_1(x) := x;
-\mycount 2
-\xintloop
-  \poldef T_\the\mycount(x) :=
-          2x*T_\the\numexpr\mycount-1(x)
-           - T_\the\numexpr\mycount-2(x);
-\ifnum\mycount<15
-\advance\mycount 1
-\repeat
-
-\[T_{15} = \PolTypeset[X]{T_15}\]
-\PolToSturm{T_15}{T_15}
-\PolSturmIsolateZeros{T_15}
-\PolEnsureIntervalLengths{T_15}{-10}
-\PolPrintIntervals{T_15}
-
-
-
-
-

Non-expandable macros

-
-

\poldef polname(letter):= expression in letter;

-
-

This evaluates the polynomial expression and stores the coefficients -in a private structure accessible later via other package macros, -under the user-chosen polname. Of course the expression can -use other previously defined polynomials. Names must start with a -letter and are constituted of letters, digits, underscores and -(since 0.5.1) the right tick '. -The whole xintexpr syntax is authorized:

-
-\poldef sin(z) := add((-1)^i z^(2i+1)/(2i+1)!, i = 0..10);
-
-

With fractional coefficients, beware the tacit multiplication issue.

-

As a side effect the function polname() is recognized as a -genuine \xintexpr...\relax function for (exact) numerical -evaluation (or within an \xintdefvar assignment.) It computes -values not according to the original expression but via the Horner -scheme corresponding to the polynomial coefficients.

-
-

Attention!

-

Release 0.3 also did the necessary set-up to let the -polynomial be known to the \xintfloatexpr (or -\xintdeffloatvar) parser.

-

Since 0.4 this isn't done automatically. Even more, a -previously existing floating point variant of the same name will -be let undefined again, to avoid hard to debug mismatches between -exact and floating point polynomials. This also applies when the -polynomial is produced not via \poldef or \PolDef but as -a product of the other package macros.

-

See \PolGenFloatVariant{polname}.

-
-

The original expression is lost after parsing, and in particular -the package provides no way to typeset it. This has to be done -manually, if needed.

-
-
-
-

\PolDef[letter]{polname}{expression in letter}

-
-Does the same as \poldef in an undelimited macro -format (thus avoiding potential problems with the catcode of the -semi-colon in presence of some packages.) In absence of the -[letter] optional argument, the variable is assumed to be x.
-
-
-

\PolGenFloatVariant{polname}

-
-

Makes the polynomial also usable in the \xintfloatexpr parser. -It will therein evaluates via an Horner scheme with coefficients -already pre-rounded to the float precision.

-

See also \PolToFloatExpr{polname}.

-
-

Attention!

-

Release 0.3 did this automatically on \PolDef and -\poldef but this was removed at 0.4 for optimization.

-

Any operation, for example generating the derivative polynomial, -or dividing two polynomials or using the \PolLet, must be -followed by explicit usage of \PolGenFloatVariant{polname} if -the new polynomial is to be used in \xintfloatexpr or alike -context.

-
-
-
-
-

\PolLet{polname_2}={polname_1}

-
-Makes a copy of the already defined polynomial polname_1 to a -new one polname_2. Same effect as -\PolDef{polname_2}{polname_1(x)} but with less overhead. The -= is optional.
-
- -
-

\PolAssign{polname}\toarray\macro

-
-

Defines a one-argument expandable macro \macro{#1} which expands -to the (raw) #1th polynomial coefficient.

-
    -
  • Attention, coefficients here are indexed starting at 1.
  • -
  • With #1=-1, -2, ..., \macro{#1} returns leading coefficients.
  • -
  • With #1=0, returns the number of coefficients, i.e. 1 + deg f -for non-zero polynomials.
  • -
  • Out-of-range #1's return 0/1[0].
  • -
-

See also \PolNthCoeff{polname}{number}. The main difference is that -with \PolAssign, \macro is made a prefix to 1 + deg f -already defined (hidden to user) macros holding individually the -coefficients but \PolNthCoeff{polname}{number} does each time the job -to expandably recover the Nth coefficient, and due to -expandability can not store it in a macro for future usage (of course, -it can be an argument in an \edef.) The other difference -is the shift by one in indexing, mentioned above (negative -indices act the same in both.)

-
-
-
-

\PolGet{polname}\fromarray\macro

-
-

Does the converse operation to -\PolAssign{polname}\toarray\macro. Each individual -\macro{number} gets expanded in an \edef and then normalized -via xintfrac's macro \xintRaw.

-

The leading zeros are removed from the polynomial.

-

(contrived) Example:

-
-\xintAssignArray{1}{-2}{5}{-3}\to\foo
-\PolGet{f}\fromarray\foo
-
-

This will define f as would have \poldef f(x):=1-2x+5x^2-3x^3;.

-
-

Note

-

Prior to 0.5, coefficients were not normalized via -\xintRaw for internal storage.

-
-
-
-
-

\PolFromCSV{polname}{<csv>}

-
-

Defines a polynomial directly from the comma separated list of values -(or a macro expanding to such a list) of its coefficients, the first -item gives the constant term, the last item gives the leading -coefficient, except if zero, then it is dropped (iteratively). List -items are each expanded in an \edef and then put into normalized -form via xintfrac's macro \xintRaw.

-

As leading zero coefficients are removed:

-
-\PolFromCSV{f}{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
-
-

defines the zero polynomial, which holds only one coefficient.

-

See also expandable macro \PolToCSV.

-
-

Note

-

Prior to 0.5, coefficients were not normalized via -\xintRaw for internal storage.

-
-
-
-
-

\PolTypeset{polname}

-
-

Typesets in descending powers in math mode. It uses letter x but -this can be changed via an optional argument:

-
-\PolTypeset[z]{polname}
-
-

By default zero coefficients are skipped (issue \poltypesetalltrue -to get all of them in output).

-

These commands (whose meanings will be found in the package code) -can be re-defined for customization. Their default definitions are -expandable, but this is not a requirement.

-
-
-

\PolTypesetCmd{raw_coeff}

-
-

Checks if the coefficient is 1 or -1 and then skips printing -the 1, except for the constant term. Also it sets conditional -\PolIfCoeffIsPlusOrMinusOne{A}{B}.

-

The actual printing of the coefficients, when not equal to plus or -minus one is handled by \PolTypesetOne{raw_coeff}.

-
-
-
-

\PolTypesetOne{raw_coeff}

-
-

The default is \xintSignedFrac but this macro is annoying as it -insists to use a power of ten, and not decimal notation.

-

One can do things such as for example: [1]

-
-\renewcommand\PolTypesetOne[1]{\num{\xintPFloat[5]{#1}}}
-\renewcommand\PolTypesetOne[1]{\num{\xintRound{4}{#1}}}
-
-

where e.g. we used the \num macro of siunitx as it -understands floating point notation.

- - - - - -
[1]the difference in the syntaxes of \xintPFloat and -\xintRound is explained from the fact that -\xintPFloat by default uses the prevailing precision -hence the extra argument like here 5 is an optional one.
-

One can also give a try to using \PolDecToString{decimal number} -which uses decimal notation (at least for the numerator part).

-
-
-
-

\PolTypesetMonomialCmd

-
-This decides how a monomial (in variable \PolVar and with -exponent \PolIndex) is to be printed. The default does nothing -for the constant term, \PolVar for the first degree and -\PolVar^{\PolIndex} for higher degrees monomials. Beware that -\PolIndex expands to digit tokens and needs termination in -\ifnum tests.
-
-
-

\PolTypesetCmdPrefix{raw_coeff}

-
-Expands to a + if the raw_coeff is zero or positive, and to -nothing if raw_coeff is negative, as in latter case the -\xintSignedFrac used by \PolTypesetCmd{raw_coeff} will put -the - sign in front of the fraction (if it is a fraction) and -this will thus serve as separator in the typeset formula. Not used -for the first term.
-
-
-
-

\PolTypeset*{polname}

-
-Typesets in ascending powers. Use e.g. [h] optional argument -(after the *) to use letter h rather than x.
-
-
-

\PolDiff{polname_1}{polname_2}

-
-

This sets polname_2 to the first derivative of polname_1. It -is allowed to issue \PolDiff{f}{f}, effectively replacing f -by f'.

-

Coefficients of the result polname_2 are irreducible fractions -(see Technicalities for the whole story.)

-
-
-
-

\PolDiff[N]{polname_1}{polname_2}

-
-This sets polname_2 to the N-th derivative of polname_1. -Identical arguments is allowed. With N=0, same effect as -\PolLet{polname_2}={polname_1}. With negative N, switches to -using \PolAntiDiff.
-
-
-

\PolAntiDiff{polname_1}{polname_2}

-
-

This sets polname_2 to the primitive of polname_1 vanishing -at zero.

-

Coefficients of the result polname_2 are irreducible fractions -(see Technicalities for the whole story.)

-
-
-
-

\PolAntiDiff[N]{polname_1}{polname_2}

-
-This sets polname_2 to the result of N successive integrations on -polname_1. With negative N, it switches to using \PolDiff.
-
-
-

\PolDivide{polname_1}{polname_2}{polname_Q}{polname_R}

-
-This sets polname_Q and polname_R to be the quotient and -remainder in the Euclidean division of polname_1 by -polname_2.
-
-
-

\PolQuo{polname_1}{polname_2}{polname_Q}

-
-This sets polname_Q to be the quotient in the Euclidean division -of polname_1 by polname_2.
-
-
-

\PolRem{polname_1}{polname_2}{polname_R}

-
-This sets polname_R to be the remainder in the Euclidean division -of polname_1 by polname_2.
-
-
-

\PolGCD{polname_1}{polname_2}{polname_GCD}

-
-This sets polname_GCD to be the (monic) GCD of the two first -polynomials. It is a unitary polynomial except if both polname_1 -and polname_2 vanish, then polname_GCD is the zero -polynomial.
- -
-
-

\PolToSturm{polname}{sturmname}

-
-

With polname being for example P, the macro starts by -computing polynomials P and P', then computes the (opposite -of the) remainder in euclidean division, iteratively.

-

The last non-zero remainder P_N_ (where N is obtainable as -\PolSturmChainLength{sturmname}) is up to a factor -the GCD of P and P' hence it is a constant if and only if -P is square-free.

-
-

Note

-
    -
  • Since 0.5 all these polynomials are divided by their rational -content, so they have integer coefficients with no common factor, -and the last one if a constant is either 1 or -1.
  • -
  • After this normalization to primitive polynomials, they are -stored internally as sturmname_k_, k=0,1, ....
  • -
  • These polynomials are used internally only. To keep them as -genuine declared polynomials also after the macro call, use the -starred variant PolToSturm*.
  • -
-
-
-

Note

-

It is perfectly allowed to use the polynomial name as Sturm chain name: -\PolToSturm{f}(f}.

-
-

The macro then declares sturmname_0, sturmname_1, ..., which are -the (non-declared) sturmname_k_ divided by the last one. Division is -not done if this last one is the constant 1 or -1, i.e. if the -original polynomial was square-free. These polynomials are primitive -polynomials too, i.e. with integer coefficients having no common factor.

-

Thus sturmname_0 has exactly the same real and complex roots as -polynomial polname, but with each root now of multiplicity one: -i.e. it is the "square-free part" of original polynomial polname.

-

Notice that sturmname_1 isn't necessarily the derivative of -sturmname_0 due to the various normalizations.

-

The polynomials sturmname_k main utility is for the execution of -\PolSturmIsolateZeros{sturmname}. Be careful not to use these -names sturmname_0, sturmname_1, etc... for defining other -polynomials after having done \PolToSturm{polname}{sturmname} and -before executing \PolSturmIsolateZeros{sturmname} else the -latter will behave erroneously.

-

\PolSturmChainLength{sturmname} gives the index of the last -element of the Sturm chain.

-
-
-
-

\PolToSturm*{polname}{sturmname}

-
-

Does the same as un-starred version and additionally it -keeps for user usage the memory of the un-normalized Sturm chain -polynomials sturmname_k_, k=0,1, ..., N, with -N being \PolSturmChainLength{sturmname}.

-
-

Note

-

This behaviour was modified at 0.6, anyhow the macro was -broken at 0.5.

-
-
-

Hint

-

The square-free part of polname is sturmname_0, and their -quotient is the polynomial with name -sturname_\PolSturmChainLength{sturmname}_. It thus easy to -set-up a loop iteratively computing the latter until the last one -is a constant, thus obtaining the decomposition of an f as -a product c f_1 f_2 f_3 ... of a constant and square-free (primitive) -polynomials, where each f_i divides its predecessor.

-
-
-
-
-

\PolSetToSturmChainSignChangesAt{\macro}{sturmname}{fraction}

-
-

Sets macro \macro to the number of sign changes in the Sturm -chain with name prefix sturmname, at location fraction -(which must be in format as acceptable by the xintfrac macros.)

-
-

Note

-

The author was lazy and did not provide rather an expandable -variant, where one would do \edef\macro{\PolNbOf...}.

-

This will presumably get added in a future release.

-

After some hesitation it was decided the macro would by default -act globally. To make the scope of its macro definition local, -use [\empty] as extra optional argument.

-
-
-
-
-

\PolSetToNbOfZerosWithin{\macro}{sturmname}{value_a}{value_b}

-
-

Applies the Sturm Theorem to set \macro to the exact number -of distinct roots of sturmname_0 in the interval (value_a, -value_b] (the macro first re-orders the value for value_a <= -value_b to hold).

-
-

Note

-

The author was lazy and did not provide rather an expandable -variant, where one would do \edef\macro{\PolNbOf...}.

-

This will presumably get added in future.

-

After some hesitation it was decided the macro would by default -act globally. To make the scope of its macro definition local, -use [\empty] as extra optional argument.

-
-

See also the expandable -\PolSturmNbOfRootsOf{sturmname}\LessThanOrEqualTo{value}, from -which it is immediate (with \numexpr) to create an expandable -variant of this macro. However the difference is that this macro -requires only \PolToSturm to have been executed, -whereas the expandable variant requires prior execution of -\PolSturmIsolateZeros.

-

See also the expandable -\PolSturmNbWithMultOfRootsOf{sturmname}\LessThanOrEqualTo{value} -which requires prior execution of -\PolSturmIsolateZeros*.

-
-
-
-

\PolSturmIsolateZeros{sturmname}

-
-

The macros locates, using Sturm theorem, as many disjoint -intervals as there are (real) roots.

-
-

Important

-

The Sturm chain must have been produced by an earlier -\PolToSturm{polname}{sturmname}.

-

Why does this macro ask for argument the name of Sturm chain, -rather than the name of a polynomial? well this is mainly for -legacy reason, and because it is accompanied by other macros for -which it is simpler to assume the argument will be the name of an -already computed Sturm chain.

-

Notice that \PolToSturm{f}{f} is perfectly legal (the -sturmname can be same as the polname): it defines -polynomials f_0, f_1, ... having f has name prefix.

-

Such a prior call -to \PolToSturm must have been made at any rate for -\PolSturmIsolateZeros to be usable.

-
-

After its execution they are two types of such intervals (stored in -memory and accessible via macros or xintexpr variables, see below):

-
    -
  • singleton {a}: then a is a root, (necessarily a decimal -number, but not all such decimal numbers are exactly identified yet).
  • -
  • open intervals (a,b): then there is exactly one root z -such that a < z < b, and the end points are guaranteed to not -be roots.
  • -
-

The interval boundaries are decimal numbers, originating -in iterated decimal subdivision from initial intervals -(-10^E, 0) and (0, 10^E) with E chosen initially large -enough so that all roots are enclosed; if zero is a root it is always -identified as such. The non-singleton intervals are of the -type (a/10^f, (a+1)/10^f) with a an integer, which is -neither 0 nor -1. Hence either a and a+1 are both positive -or they are both negative.

-

One does not a priori know what will be the lengths of these -intervals (except that they are always powers of ten), they -vary depending on how many digits two successive roots have in -common in their respective decimal expansions.

-
-

Important

-

If some two consecutive intervals share an end-point, no -information is yet gained about the separation between the two -roots which could at this stage be arbitrarily small.

-

See \PolRefineInterval*{sturmname}{index} which addresses -this issue.

-
- -

The interval boundaries (and exactly found roots) are made available -for future computations in \xintexpr-essions or polynomial -definitions as variables <sturmname>L_1, -<sturmname>L_2, etc..., for the left end-points and -<sturmname>R_1, <sturmname>R_2, ..., for the right -end-points.

-

Thus for example, if sturmname is f, one can use the -xintexpr variables fL_1, fL_2, ... to refer in expressions -to the left end-points (or to the exact root, if left and right end -points coincide). Additionally, xintexpr variable fZ_1_isknown -will have value 1 if the root in the first interval is known, -and 0 otherwise. And similarly for the other intervals.

-

Also, macros \PolSturmIsolatedZeroLeft{sturmname}{index} and -\PolSturmIsolatedZeroRight{sturmname}{index} are provided which -expand to these same values, written in decimal notation (i.e. -pre-processed by \PolDecToString.) And there -is also \PolSturmIfZeroExactlyKnown{sturmname}{index}{A}{B}.

-
-

Important

-

Trailing zeroes in the stored decimal numbers accessible via the -macros are significant: they are also present in the decimal -expansion of the exact root.

-
-

These variables and macros are automatically updated when one next -uses macros such as \PolRefineInterval*{sturmname}{index}.

-

The start of decimal expansion of a positive k-th root is given -by \PolSturmIsolatedZeroLeft{sturmname}{k}, and for a negative root it is given -by PolSturmIsolatedZeroRight{sturmname}{k}. These two decimal -numbers are either both zero or both of the same sign.

-

The number of distinct roots is obtainable expandably as -\PolSturmNbOfIsolatedZeros{sturmname}.

-

Furthermore -\PolSturmNbOfRootsOf{sturmname}\LessThanOrEqualTo{value} and -\PolSturmNbOfRootsOf{sturmname}\LessThanOrEqualToExpr{expression}. -will expandably compute respectively the number of real roots at -most equal to value or expression, and the same but with -multiplicities.

-
-

Note

-

In the current implementation the xintexpr variables -and xinttools arrays are globally defined. On the -other hand the Sturm sequence polynomials obey the current scope.

-
-
-

Note

-

As all computations are done exactly there can be no errors... -apart those due to bad coding by author. The results are exact -bounds for the mathematically exact real roots.

-

Future releases will perhaps also provide macros based on Newton -or Regula Falsi methods. Exact computations with such methods -lead however quickly to very big fractions, and this forces usage -of some rounding scheme for the abscissas if computation times -are to remain reasonable. This raises issues of its own, which -are studied in numerical mathematics.

-
-
-
-
-

\PolSturmIsolateZeros*{sturmname}

-
-

The macro does the same as \PolSturmIsolateZeros{sturmname} and -then in addition it does the extra work to determine all -multiplicities (of the real roots): -after executing this macro, -\PolSturmIsolatedZeroMultiplicity{sturmname}{index} will expand -to the multiplicity of the root located in the index-th -interval (intervals are enumerated from left to right, with index -starting at 1).

-

Furthermore, if for example the sturmname is f, xintexpr -variables fM_1, fM_2... hold the multiplicities thus -computed.

-
-

Note

-

It is not necessary to have executed the PolToSturm* starred -variant, as the non-starred variant keeps internally the memory of the -original GCD (and even of the full non-normalized original Sturm -chain), even though it does not make the declarations as user-level -genuine polynomials.

-
-

See The degree nine polynomial with 0.99, 0.999, 0.9999 as triple -roots for an example.

-
-
-
-

\PolSturmIsolateZeros**{sturmname}

-
-

The macro does the same as \PolSturmIsolateZeros*{sturmname} and -in addition it does the extra work to determine all the rational -roots.

-
-

Note

-

After execution of this macro, a root is "known" if and only if -it is rational.

-
-

Furthermore, primitive polynomial sturmname_sqf_norr is created -to match the (square-free) sturmname_0 from which all rational -roots have been removed (see \polexprsetup for customizing this -name). The number of distinct rational roots is thus the difference -between the degrees of these two polynomials (see also -\PolSturmNbOfRationalRoots{sturmname}).

-

And sturmname_norr is sturmname_0_ from which all rational -roots have been removed (see \polexprsetup), i.e. it contains -the irrational roots of the original polynomial, with the same -multiplicities.

-

See A degree five polynomial with three rational -roots for an example.

-
-
- - -
-

\PolSturmIsolateZerosAndFindRationalRoots{sturmname}

-
-

This works exactly like \PolSturmIsolateZeros**{sturmname} -(inclusive of declaring the polynomials sturmname_sqf_norr and -sturmname_norr with no rational roots) except that it does not -compute the multiplicities of the non-rational roots.

-
-

Note

-

There is no macro to find the rational roots but not compute -their multiplicities at the same time.

-
-
-

Attention!

-

This macro does not define xintexpr variables -sturmnameM_1, sturmnameM_2, ... holding the -multiplicities and it leaves the multiplicity array (whose accessor -is \PolSturmIsolatedZeroMultiplicity{sturmname}{index}) into -a broken state, as all non-rational roots will supposedly have -multiplicity one. This means that the output of -\PolPrintIntervals* for example will be -erroneous for the intervals with irrational roots.

-

I decided to document it because finding multiplicities of the -non rational roots is somewhat costly, and one may be interested -only into finding the rational roots (of course random -polynomials with integer coefficients will not have any -rational root anyhow).

-
-
-
-
-

\PolRefineInterval*{sturmname}{index}

-
-The index-th interval (starting indexing at one) is further -subdivided as many times as is necessary in order for the newer -interval to have both its end-points distinct from the end-points of -the original interval. This means that the kth root is then -strictly separated from the other roots.
-
-
-

\PolRefineInterval[N]{sturmname}{index}

-
-The index-th interval (starting count at one) is further -subdivided once, reducing its length by a factor of 10. This is done -N times if the optional argument [N] is present.
-
-
-

\PolEnsureIntervalLength{sturmname}{index}{E}

-
-The index-th interval is subdivided until its length becomes at -most 10^E. This means (for E<0) that the first -E digits -after decimal mark of the kth root will then be known exactly.
-
-
-

\PolEnsureIntervalLengths{sturmname}{E}

-
-

The intervals as obtained from \PolSturmIsolateZeros are (if -necessary) subdivided further by (base 10) dichotomy in order for -each of them to have length at most 10^E (length will be shorter -than 10^E in output only if it did not change or became zero.)

-

This means that decimal expansions of all roots will be known with --E digits (for E<0) after decimal mark.

-
-
-
-

\PolPrintIntervals[varname]{sturmname}

-
-

This is a convenience macro which prints the bounds for the roots -Z_1, Z_2, ... (the optional argument varname allows to -specify a replacement for the default Z). This will be done (by -default) in a -math mode array, one interval per row, and pattern rcccl, -where the second and fourth column hold the < sign, except when -the interval reduces to a singleton, which means the root is known -exactly.

-
-

Attention!

-

This macro was refactored at 0.7, its default output remained -identical but the ways to customize it got completely -modified.

-
-

See next macros which govern its output.

-
-
-

\PolPrintIntervalsNoRealRoots

-
-

Executed in place of an array environment, when there are no -real roots. Default definition:

-
-\newcommand\PolPrintIntervalsNoRealRoots{}
-
-
-
-
-

\PolPrintIntervalsBeginEnv

-
-

Default definition:

-
-\newcommand\PolPrintIntervalsBeginEnv{\[\begin{array}{rcccl}}
-
-
-
-
-

\PolPrintIntervalsEndEnv

-
-

Default definition:

-
-\newcommand\PolPrintIntervalsEndEnv{\end{array}\]}
-
-
-
-
-

\PolPrintIntervalsKnownRoot

-
-

Default definition:

-
-\newcommand\PolPrintIntervalsKnownRoot{%
-  &&\PolPrintIntervalsTheVar_{\PolPrintIntervalsTheIndex}%
-  &=&\PolPrintIntervalsPrintExactZero
-}
-
-
-
-
-

\PolPrintIntervalsUnknownRoot

-
-

Default definition:

-
-\newcommand\PolPrintIntervalsUnknownRoot{%
-  \PolPrintIntervalsPrintLeftEndPoint&<&%
-  \PolPrintIntervalsTheVar_{\PolPrintIntervalsTheIndex}&<&%
-  \PolPrintIntervalsPrintRightEndPoint
-}
-
-
-
-
-

\PolPrintIntervalsPrintExactZero

-
-

Default definition:

-
-\newcommand\PolPrintIntervalsPrintExactZero{\PolPrintIntervalsTheLeftEndPoint}
-
-
-
-
-

\PolPrintIntervalsPrintLeftEndPoint

-
-

Default definition:

-
-\newcommand\PolPrintIntervalsPrintLeftEndPoint{\PolPrintIntervalsTheLeftEndPoint}
-
-
-
-
-

\PolPrintIntervalsPrintRightEndPoint

-
-

Default definition is:

-
-\newcommand\PolPrintIntervalsPrintRightEndPoint{\PolPrintIntervalsTheRightEndPoint}
-
-
-
-
-
-

\PolPrintIntervals*[varname]{sturmname}

-
-

This starred variant produces an alternative output (which -displays the root multiplicity), and is provided as an -example of customization.

-

As replacement for \PolPrintIntervalsKnownRoot, -\PolPrintIntervalsPrintExactZero, -\PolPrintIntervalsUnknownRoot it uses its own -\POL@@PrintIntervals... macros. We only reproduce here one -definition:

-
-\newcommand\POL@@PrintIntervalsPrintExactZero{%
-   \displaystyle
-   \xintSignedFrac{\PolPrintIntervalsTheLeftEndPoint}%
-}%
-
-

Multiplicities are printed using this auxiliary macro:

-
-
-

\PolPrintIntervalsPrintMultiplicity

-
-

whose default definition is:

-
-\newcommand\PolPrintIntervalsPrintMultiplicity{(\mbox{mult. }\PolPrintIntervalsTheMultiplicity)}
-
-
-
-
-
-

\PolMapCoeffs{\macro}{polname}

-
-

It modifies ('in-place': original coefficients get lost) each -coefficient of the defined polynomial via the expandable macro -\macro. The degree is adjusted as necessary if some leading -coefficients vanish after the operation. In replacement text of -\macro, \index expands to the coefficient index (which is -defined to be zero for the constant term).

-

Notice that \macro will have to handle inputs of the shape -A/B[N] (xintfrac internal notation). This means that it probably -will have to be expressed in terms of macros from xintfrac package.

-

Example:

-
-\def\foo#1{\xintMul{#1}{\the\numexpr\index*\index\relax}}
-
-

(or with \xintSqr{\index}) to replace n-th coefficient -f_n by f_n*n^2.

-
-
-
-

\PolReduceCoeffs{polname}

-
-About the same as \PolMapCoeffs{\xintIrr}{polname} (but -maintaining a [0] postfix for speedier xintfrac parsing when -polynomial function is used for computations.) This is a -one-argument macro, working 'in-place'.
-
-
-

\PolReduceCoeffs*{polname}

-
-

This starred variant leaves un-touched the decimal exponent in the -internal representation of the fractional coefficients, i.e. if a -coefficient is internally A/B[N], then A/B is reduced to -smallest terms, but the 10^N part is kept as is. Note: if the -polynomial is freshly defined directly via \PolFromCSV its coefficients might still be internally in some -format like 1.5e7; the macro will anyhow always first do the -needed conversion to strict format A/B[N].

-

Evaluations with polynomials treated by this can be much faster than -with those handled by the non-starred variant -\PolReduceCoeffs{polname}: as the numerators and denominators -remain smaller, this proves very beneficial in favorable cases -(especially when the coefficients are decimal numbers) to the -expansion speed of the xintfrac macros used internally by -\PolEval.

-
-
-
-

\PolMakeMonic{polname}

-
-Divides by the leading coefficient. It is recommended to execute -\PolReduceCoeffs*{polname} immediately afterwards. This is not -done automatically, due to the case the original polynomial had integer -coefficients and we want to keep the leading one as common -denominator.
-
-
-

\PolMakePrimitive{polname}

-
-Divides by the integer content see (\PolIContent). This thus produces a polynomial with integer -coefficients having no common factor. The sign of the leading -coefficient is not modified.
-
-
-
-

Expandable macros

-

All these macros expand completely in two steps except \PolToExpr -and \PolToFloatExpr (and their auxiliaries) which need a -\write, \edef or a \csname...\endcsname context.

-
-

\PolEval{polname}\AtExpr{numerical expression}

-
-It boils down to -\xinttheexpr polname(numerical expression)\relax.
-
-
-

\PolEval{polname}\At{fraction}

-
-Evaluates the polynomial at value fraction which must be in (or -expand to) a format acceptable to the xintfrac macros.
-
-
-

\PolEvalReduced{polname}\AtExpr{numerical expression}

-
-Boils down to \xinttheexpr reduce(polname(numerical expression))\relax.
-
-
-

\PolEvalReduced{polname}\At{fraction}

-
-Evaluates the polynomial at value fraction which must be in (or -expand to) a format acceptable to the xintfrac macros, and produce -an irreducible fraction.
-
-
-

\PolFloatEval{polname}\AtExpr{numerical expression}

-
-

Boils down to \xintthefloatexpr polname(numerical expression)\relax.

-

This is done via a Horner Scheme (see \poldef and -\PolGenFloatVariant{polname}), with already rounded -coefficients. [2] To use the exact coefficients with exactly -executed additions and multiplications, just insert it in the float -expression as in this example: [3]

-
-\xintthefloatexpr 3.27*\xintexpr f(2.53)\relax^2\relax
-
-

The f(2.53) is exactly computed then rounded at the time of -getting raised to the power 2. Moving the ^2 inside, that -operation would also be treated exactly.

- - - - - -
[2]Anyway each floating point operation starts by rounding its -operands to the floating point precision.
- - - - - -
[3]The \xintexpr here could be \xinttheexpr but that -would be less efficient. Cf. xintexpr documentation about -nested expressions.
-
-
-
-

\PolFloatEval{polname}\At{fraction}

-
-Evaluates the polynomial at value fraction which must be in (or -expand to) a format acceptable to the xintfrac macros, and produces -a floating point number.
-
-
-

\PolIfCoeffIsPlusOrMinusOne{A}{B}

-
-

This macro is a priori undefined.

-

It is defined via the default \PolTypesetCmd{raw_coeff} to be -used if needed in the execution of \PolTypesetMonomialCmd, -e.g. to insert a \cdot in front of \PolVar^{\PolIndex} if -the coefficient is not plus or minus one.

-

The macro will execute A if the coefficient has been found to be -plus or minus one, and B if not.

-
-
-
-

\PolLeadingCoeff{polname}

-
-Expands to the leading coefficient.
-
-
-

\PolNthCoeff{polname}{number}

-
-It expands to the raw N-th coefficient (0/1[0] if the index -number is out of range). With N=-1, -2, ... expands to the -leading coefficients.
-
-
-

\PolDegree{polname}

-
-It expands to the degree. This is -1 if zero polynomial but this -may change in future. Should it then expand to -\infty ?
-
-
-

\PolIContent{polname}

-
-

It expands to the contents of the polynomial, i.e. to the positive -fraction such that dividing by this fraction produces a polynomial -with integer coefficients having no common prime divisor.

-

See \PolMakePrimitive.

-
-
-
-

\PolToExpr{polname}

-
-

Expands [4] to coeff_N*x^N+... (descending powers.)

- - - - - -
[4]in a \write, \edef, or \csname...\endcsname, but -not under \romannumeral-`0.
-

By default zero coefficients are skipped (issue \poltoexpralltrue to -get all of them in output).

-

By default, no + sign before negative coefficients, for -compliance with Maple input format (but see -\PolToExprTermPrefix{raw_coeff}.) Also, like the default -behaviour of \PolTypeset{polname}, does not print (for the non -constant terms) coefficients equal to plus or minus one. The degree -one monomial is output as x, not x^1. Complete customization is -possible, see next macros.

-

Of course \PolToExpr{f} can be inserted in a \poldef, as the -latter expands token by token, hence will force complete expansion -of \PolToExpr{f}, but a simple f(x) is more efficient for -the identical result.

-
-
-

\PolToExprOneTerm{raw_coeff}{number}

-
-

This two argument expandable command takes care of the monomial and -its coefficient. The default definition is done in order for -coefficients of absolute value 1 not be printed explicitely -(except of course for the constant term). Also by default, the -monomial of degree one is x not x^1, and x^0 is skipped.

-

For compatibility with Maple input requirements, by default a * -always precedes the x^number, except if the coefficient is a one -or a minus one. See \PolToExprTimes.

-
-
- -
-

\PolToExprOneTermStyleB{raw_coeff}{number}

-
-

For output in this style:

-
-2*x^11/3+3*x^8/7-x^5-x^4/4-x^3-x^2/2-2*x+1
-
-

issue \let\PolToExprOneTerm\PolToExprOneTermStyleB before usage of -\PolToExpr. Note that then \PolToExprCmd isn't used at all. -To revert to package default, issue -\let\PolToExprOneTerm\PolToExprOneTermStyleA.

-

To suppress the *'s, cf. \PolToExprTimes.

-
-
-
-

\PolToExprCmd{raw_coeff}

-
-It is the one-argument macro used by the package definition of -\PolToExprOneTerm for the coefficients themselves (when not -equal to plus or minus one), and it defaults to -\xintPRaw{\xintRawWithZeros{#1}}. One will have to redefine it -to \xintIrr{#1} or to \xintPRaw{\xintIrr{#1}} to obtain in the -output forcefully reduced coefficients.
-
-
-

\PolToExprTermPrefix{raw_coeff}

-
-Defined identically as \PolTypesetCmdPrefix{raw_coeff}. It -prefixes with a plus sign for non-negative coefficients, because -they don't carry one by themselves.
-
-
-

\PolToExprVar

-
-This expands to the variable to use in output (it does not have to -be a single letter, may be an expandable macro.) Initial definition -is x.
-
-
-

\PolToExprTimes

-
-This expands to the symbol used for multiplication of an -x^{number} by the corresponding coefficient. The default is -*. Redefine the macro to expand to nothing to get rid of it (but -this will give output incompatible with some professional computer -algebra software).
-
-
-
-

\PolToExpr*{polname}

-
-Expands to coeff_0+coeff_1*x+coeff_2*x^2+... (ascending powers). -Customizable like \PolToExpr{polname} via the same macros.
-
-
-

\PolToFloatExpr{polname}

-
-

Similar to \PolToExpr{polname} but uses \PolToFloatExprCmd -which by default rounds and converts the coefficients to floating -point format.

-
-

Note

-

It is not necessary to have issued -\PolGenFloatVariant{polname}. The rounded coefficients are -not easily recoverable from the \xintfloatexpr polynomial -function hence \PolToFloatExprCmd operates from the exact -coefficients anew.

-

Attention that both macros obey the prevailing float precision. -If it is changed between those macro calls, then a mismatch -exists between the coefficients as used in \xintfloatexpr and -those output by \PolToFloatExpr{polname}.

-
-
-
-

\PolToFloatExprOneTerm{raw_coeff}{number}

-
-Similar to \PolToExprOneTerm. But does not treat -especially coefficients equal to plus or minus one.
-
-
-

\PolToFloatExprCmd{raw_coeff}

-
-

It is the one-argument macro used by \PolToFloatExprOneTerm. -Its package definition is \xintFloat{#1}.

-
-

Caution!

-

Currently (xint 1.3c) \xintFloat{0} outputs 0.e0 -which is perfectly acceptable input for Python, but not for -Maple. Thus, one should better leave the \poltoexprallfalse -toggle to its default \iffalse state, if one intends to use -the output in a Maple worksheet.

-

But even then the zero polynomial will cause a problem. Workaround:

-
-\renewcommand\PolToFloatExprCmd[1]{\xintiiifZero{#1}{0.0}{\xintFloat{#1}}}
-
-

Usage of \xintiiifZero and not \xintifZero is only for -optimization (I can't help it) because #1 is known to be -in xintfrac raw format.

-
-
-
-
-
-

\PolToFloatExpr*{polname}

-
-Typesets in ascending powers.
-
-
-

\PolToList{polname}

-
-Expands to {coeff_0}{coeff_1}...{coeff_N} with N = degree, and -coeff_N the leading coefficient -(the zero polynomial does give {0/1[0]} and not an -empty output.)
-
-
-

\PolToCSV{polname}

-
-Expands to coeff_0, coeff_1, coeff_2, ....., coeff_N, starting -with constant term and ending with leading coefficient. Converse -to \PolFromCSV.
-
-
-

\PolSturmChainLength{sturmname}

-
-

Returns the integer N such that sturmname_N is the last one -in the Sturm chain sturmname_0, sturmname_1, ...

-

See \PolToSturm{polname}{sturmname}.

-
-
-
-

\PolSturmIfZeroExactlyKnown{sturmname}{index}{A}{B}

-
-

Executes A if the index-th interval reduces to a singleton, -i.e. the root is known exactly, else B.

-
-

Note

-

index is allowed to be something like 1+2*3 as it is fed -to \the\numexpr...\relax.

-
-
-
-
-

\PolSturmIsolatedZeroLeft{sturmname}{index}

-
-

Expands to the left end-point for the index-th interval, as -computed by some earlier \PolSturmIsolateZeros{sturmname}.

-
-

Note

-

Of course, this is kept updated by macros such as -\PolRefineInterval{sturmname}{index}.

-
-

The value is pre-formatted using \PolDecTostring.

-
-
-
-

\PolSturmIsolatedZeroRight{sturmname}{index}

-
-

Expands to the right end-point for the index-th interval as -computed by some earlier \PolSturmIsolateZeros{sturmname} and -possibly refined afterwards.

-

The value is pre-formatted using \PolDecTostring.

-
-
-
-

\PolSturmIsolatedZeroMultiplicity{sturmname}{index}

-
-

Expands to the multiplicity of the unique root contained in the -index-th interval.

-
-

Attention!

-

A prior execution of \PolSturmIsolateZeros*{sturmname} is mandatory.

-
-

See The degree nine polynomial with 0.99, 0.999, 0.9999 as triple -roots for an example of use.

-
-
-
-

\PolSturmNbOfIsolatedZeros{sturmname}

-
-Expands to the number of real roots of the polynomial -<sturmname>_0, i.e. the number of distinct real roots of the -polynomial originally used to create the Sturm chain via -\PolToSturm{polname}{sturmname}.
-
-

Warning

-

The next few macros counting roots, with or without multiplicities, -less than or equal to some value, are under evaluation and may be -removed from the package if their utility is judged to be not high -enough. They can be re-coded at user level on the basis of the other -documented package macros anyway.

-
-
-

\PolSturmNbOfRootsOf{sturmname}\LessThanOrEqualTo{value}

-
-

Expands to the number of distinct roots (of the polynomial used to -create the Sturm chain) less than or equal to the value (i.e. a -number of fraction recognizable by the xintfrac macros).

-
-

Attention!

-

\PolSturmIsolateZeros{sturmname} must have been executed -beforehand.

-

And the argument is a sturmname, not a polname (this is -why the macro contains Sturm in its name), simply to be reminded -of the above constraint.

-
-
-
-
-

\PolSturmNbOfRootsOf{sturmname}\LessThanOrEqualToExpr{expression}

-
-

Expands to the number of distinct roots (of the polynomial -used to create the Sturm chain) which are less than or equal to the -given expression.

-
-

Attention!

-

\PolSturmIsolateZeros{sturmname} must have been executed -beforehand.

-
-
-
-
-

\PolSturmNbWithMultOfRootsOf{sturmname}\LessThanOrEqualTo{value}

-
-

Expands to the number counted with multiplicities of the roots (of -the polynomial used to create the Sturm chain) which are less than -or equal to the given value.

-
-

Attention!

-

\PolSturmIsolateZeros*{sturmname} (or the double starred -variant) must have been executed beforehand.

-
-
-
-
-

\PolSturmNbWithMultOfRootsOf{sturmname}\LessThanOrEqualToExpr{expression}

-
-

Expands to the total number of roots (counted with multiplicities) -which are less than or equal to the given expression.

-
-

Attention!

-

\PolSturmIsolateZeros*{sturmname} (or the double starred -variant) must have been executed beforehand.

-
-
-
-
-
-

\PolSturmNbOfRationalRoots{sturmname}

-
-

Expands to the number of rational roots (without multiplicities).

-
-

Attention!

-

\PolSturmIsolateZeros**{sturmname} must have been executed -beforehand.

-
-
-
-
-

\PolSturmNbOfRationalRootsWithMultiplicities{sturmname}

-
-

Expands to the number of rational roots (counted with multiplicities).

-
-

Attention!

-

\PolSturmIsolateZeros**{sturmname} must have been executed -beforehand.

-
-
-
-
-

\PolSturmRationalRoot{sturmname}{k}

-
-

Expands to the kth rational root (they are ordered and indexed -starting at 1 for the most negative).

-
-

Attention!

-

\PolSturmIsolateZeros**{sturmname} must have been executed -beforehand.

-
-
-
-
-

\PolSturmRationalRootIndex{sturmname}{k}

-
-

Expands to index of the kth rational root as part of the -ordered real roots (without multiplicities). I.e., above macro -\PolSturmRationalRoot{sturmname}{k} is equivalent to this -nested call:

-
-\PolSturmIsolatedZeroLeft{sturmname}{\PolSturmRationalRootIndex{sturmname}{k}}
-
-
-

Attention!

-

\PolSturmIsolateZeros**{sturmname} must have been executed -beforehand.

-
-
-
-
-

\PolSturmRationalRootMultiplicity{sturmname}{k}

-
-

Expands to the multiplicity of the kth rational root.

-
-

Attention!

-

\PolSturmIsolateZeros**{sturmname} must have been executed -beforehand.

-
-
-
-
-

\PolIntervalWidth{sturmname}{index}

-
-The 10^E width of the current index-th root localization -interval. Output is in xintfrac raw 1/1[E] format (if not zero).
-
-
-

Expandable macros for use within execution of \PolPrintIntervals

-

These macros are for usage within custom user redefinitions of -\PolPrintIntervalsKnownRoot, \PolPrintIntervalsUnknownRoot, or -in redefinitions of PolPrintIntervalsPrintExactZero (used in the -default for the former) and of \PolPrintIntervalsPrintLeftEndPoint, -\PolPrintIntervalsPrintRightEndPoint (used in the default for the -latter).

-
-

Attention!

-

Some macros formerly mentioned here got removed at 0.7: -\PolPrintIntervalsTheEndPoint, -\PolIfEndPointIsPositive{A}{B}, -\PolIfEndPointIsNegative{A}{B}, -\PolIfEndPointIsZero{A}{B}.

-
-
-

\PolPrintIntervalsTheVar

-
-Expands to the name (default Z) used for representing the roots, -which was passed as optional argument varname to -\PolPrintIntervals[varname]{sturmname}.
-
-
-

\PolPrintIntervalsTheIndex

-
-Expands to the index of the considered interval (indexing starting -at 1 for the leftmost interval).
-
-
-

\PolPrintIntervalsTheSturmName

-
-Expands to the argument which was passed as sturmname to -\PolPrintIntervals[varname]{sturmname}.
-
-
-

\PolPrintIntervalsTheLeftEndPoint

-
-The left end point of the interval, as would be produced by -\PolSturmIsolatedZeroLeft if it was -used with arguments the Sturm chain name and interval index returned -by \PolPrintIntervalsTheSturmName and -\PolPrintIntervalsTheIndex.
-
-
-

\PolPrintIntervalsTheRightEndPoint

-
-The right end point of the interval, as would be produced by -\PolSturmIsolatedZeroRight for -this Sturm chain name and index.
-
-
-

\PolPrintIntervalsTheMultiplicity

-
-The multiplicity of the unique root within the interval of index -\PolPrintIntervalsTheIndex. Makes sense only if the starred (or -double-starred) variant of \PolSturmIsolateZeros was used earlier.
-
-
-
-

\PolDecToString{decimal number}

-
-

This is a utility macro to print decimal numbers. It has been -backported to xintfrac (release 1.3 of 2018/03/01) under -the name \xintDecToString, and the polexpr macro is simply -now an alias to it.

-

For example -\PolDecToString{123.456e-8} will expand to 0.00000123456 -and \PolDecToString{123.450e-8} to 0.00000123450 which -illustrates that trailing zeros are not trimmed. To trim trailing -zeroes, one can use \PolDecToString{\xintREZ{#1}}.

-

The precise behaviour of this macro may evolve in future releases of -xint.

-
-
-
-
-

Booleans (with default setting as indicated)

-
-

\xintverbosefalse

-
-

This is actually an xintexpr configuration. Setting it to -true triggers the writing of information to the log when new -polynomials are defined.

-
-

Caution!

-

The macro meanings as written to the log are to be considered -unstable and undocumented internal structures.

-
-
-
-
-

\poltypesetallfalse

-
-If true, \PolTypeset{polname} will also typeset the vanishing -coefficients.
-
-
-

\poltoexprallfalse

-
-If true, \PolToExpr{polname} and \PolToFloatExpr{polname} will -also include the vanishing coefficients in their outputs.
-
-
-
-

\polexprsetup

-
-

Serves to customize the package. Currently only two keys are -recognized:

-
    -
  • norr: the postfix that \PolSturmIsolateZeros**{sturmname} -should append to sturmname to declare the primitive polynomial -obtained from original one after removal of all rational roots. -The default value is _norr (standing for “no rational roots”).
  • -
  • sqfnorr: the postfix that \PolSturmIsolateZeros**{sturmname} -should append to sturmname to declare the primitive polynomial -obtained from original one after removal of all rational roots and -suppression of all multiplicities. -The default value is _sqf_norr (standing for “square-free with -no rational roots”).
  • -
-

The package executes \polexprsetup{norr=_norr, -sqfnorr=_sqf_norr} as default.

-
-
-
-

Technicalities

-
    -
  • The catcode of the semi-colon is reset temporarily by \poldef macro in case some other package (for example the French -babel module) may have made it active. This will fail though if the -whole thing was already part of a macro argument, in such cases one -can use \PolDef{f}{P(x)} -rather. The colon in := may be active with no consequences.

    -
  • -
  • As a consequence of xintfrac addition and subtraction always using -least common multiples for the denominators [5], user-chosen common -denominators survive additions and multiplications. For example, this:

    -
    -\poldef P(x):= 1/2 + 2/2*x + 3/2*x^3 + 4/2*x^4;
    -\poldef Q(x):= 1/3 + (2/3)x + (3/3)x^3 + (4/3)x^4;
    -\poldef PQ(x):= P(x)Q(x);
    -
    -

    gives internally the polynomial:

    -
    -1/6+4/6*x^1+4/6*x^2+6/6*x^3+20/6*x^4+16/6*x^5+9/6*x^6+24/6*x^7+16/6*x^8
    -
    -

    where all coefficients have the same denominator 6. Notice though that -\PolToExpr{PQ} outputs the 6/6*x^3 as x^3 because (by -default) it recognizes and filters out coefficients equal to one or -minus one (since release 0.3). One can use for example -\PolToCSV{PQ} to see the internally stored coefficients.

    - - - - - -
    [5]

    prior to 0.4.1, polexpr used to temporarily patch -during the parsing of polynomials the xintfrac macros. This -patch was backported to xint at release 1.3.

    -
    -
  • -
  • \PolDiff{polname_1}{polname_2} always applies \xintIrr to the -resulting coefficients, except that the power of ten part [N] -(for example an input in scientific notation such as 1.23e5 gives -123/1[3] internally in xintfrac) is not taken into account in the -reduction of the fraction. This is tentative and may change.

    -

    Same remark for \PolAntiDiff{polname_1}{polname_2}.

    -
  • -
  • Currently, the package stores all coefficients from index 0 to -index equal to the polynomial degree inside a single macro, as a list. -This data structure is obviously very inefficient for polynomials of -high degree and few coefficients (as an example with \poldef -f(x):=x^1000 + x^500; the subsequent definition \poldef g(x):= -f(x)^2; will do of the order of 1,000,000 multiplications and -additions involvings only zeroes... which does take time). This -may change in the future.

    -
  • -
  • As is to be expected internal structures of the package are barely -documented and unstable. Don't use them.

    -
  • -
-
-
-

CHANGE LOG

- -
-
-

Acknowledgments

-

Thanks to Jürgen Gilg whose question about xint usage for -differentiating polynomials was the initial trigger leading to this -package, and to Jürgen Gilg and Thomas Söll for testing it on some -concrete problems.

-

Renewed thanks to them on occasion of the 0.6 and 0.7 releases for their -continued interest.

-

See README.md for the License.

-
-
- - diff --git a/macros/latex/contrib/polexpr/polexpr.sty b/macros/latex/contrib/polexpr/polexpr.sty deleted file mode 100644 index 30fef28914..0000000000 --- a/macros/latex/contrib/polexpr/polexpr.sty +++ /dev/null @@ -1,3164 +0,0 @@ -% author: Jean-François Burnol -% License: LPPL 1.3c (author-maintained) -\ProvidesPackage{polexpr}% - [2020/01/31 v0.7.5 Polynomial expressions with rational coefficients (JFB)]% -\RequirePackage{xintexpr}[2020/01/31]% xint 1.4 -\edef\POL@restorecatcodes % TODO: think better about what is reasonable here - {\catcode`\noexpand\_ \the\catcode`\_ % - \catcode`\noexpand\! \the\catcode`\! % - \catcode`\noexpand\* \the\catcode`\* % - \catcode`\noexpand\~ \the\catcode`\~ % - \catcode`\noexpand\: \the\catcode`\: % - \catcode0 \the\catcode0\relax}% -\catcode`\_ 11 \catcode0 12 \catcode`\* 12 -\long\def\xint_stop_atfirstoftwo #1#2{ #1}% not yet in xint 1.3c -\long\def\xint_stop_atsecondoftwo #1#2{ #2}% - -%% 0.7.5 VERY SERIOUS TROUBLES TO GET polexpr TO WORK WITH xintexpr 1.4 - -%% I hesitated about incorporating it directly into xint 1.4 -%% Don't do this at home, only xint gurus are allowed. -\let\POL@originalXINT_expr_redefinemacros\XINT_expr_redefinemacros -\def\XINT_expr_redefinemacros -{% - \POL@originalXINT_expr_redefinemacros - \POL@activateNEhook -}% -%% Using \def's and not \let's to get better readable trace -%% in case I need to debug but this never happens -\def\POL@activateNEhook@xint % done in a group -{% - \def\POL@NEhook@polfunc{\POL@NE@polfunc}% -}% -\def\POL@activateNEhook@pol -{% - \def\POL@NEhook@polfunc{\POL@NP@polfunc}% -}% -\def\POL@activateNEhook{\POL@activateNEhook@xint}% -% -% -\catcode`~ 12 -\catcode`! 11 -\catcode`: 11 -% We drop consideration of \XINT_global matters -% because we have other more urgent and arduous problems -\def\POL@defpolfunc #1#2% -{% - \expandafter\POL@defpolfunc_a - \csname XINT_#2_func_#1\expandafter\endcsname - \csname XINT_#2_polfunc_#1\endcsname {#1}{#2}% -}% -\def\POL@defpolfunc_a #1#2#3#4% -{% - \protected % xintexpr 1.4 does things such as \expandafter\xintAdd\expanded - \expandafter\def\expandafter#2\expandafter##\expandafter1\expandafter - {% - #2{##1}% - }% - \def#1##1##2##3% - {% - % put it directly at the correct level of bracing - % don't worry for now about minimizing how many times ##3 is grabbed - \expandafter##1\expandafter##2\expandafter{\expandafter - {\romannumeral`^^@\POL@NEhook@polfunc{XINT_#4_polfunc_#3}#2{##3}}}% - }% -}% -% -\def\POL@polfunc@go #1#2#3{#2#3}% brace stripping intentional -\def\POL@NEhook@polfunc{\POL@polfunc@go}% default for pure numerics -% -% Hook for expansion in \poldef -\def\POL@NP@polfunc #1{% -\def\POL@NP@polfunc ##1##2##3% -{% - \if0\expandafter\XINT:NE:hastilde\detokenize{##3}~!\relax - \expandafter\XINT:NE:hashash\detokenize{##3}#1!\relax 0% - \expandafter\POL@polfunc@go - \else - \expandafter\POL:NP:polfunc:p - \fi {##1}{##2}{##3}% -}}\expandafter\POL@NP@polfunc\string#% -\def\POL:NP:polfunc:p #1#2#3% -{% - ~romannumeral~POL:usepolfunc:pol{#1}{#3}% -}% -\def\POL:usepolfunc:pol #1%#2% -{% -% Here we are in the core of \poldef and we really -% need to get rid of some \expanded tokens so -% we accept being exposed to \expanded but arrange to -% remain invariant. Then we will try to speed up -% polynomial composition (at this time the \#1 -% is a nested Horner type macro) by «pre-expanding» -% the argument, but this means using the \POL@get -% methods inside an \hbox -% -% \POL@applypolfunc will be defined \protected -% - \expandafter\xint_c_\expandafter\POL@applypolfunc -% This will be \protected - \csname#1\endcsname -% #2% brace stripping is deliberate -}% -% -% Hook for expansion in \xintexpr -\def\POL@NE@polfunc #1{% -\def\POL@NE@polfunc ##1##2##3% -{% - \if0\expandafter\XINT:NE:hastilde\detokenize{##3}~!\relax - \expandafter\XINT:NE:hashash\detokenize{##3}#1!\relax 0% - \expandafter\POL@polfunc@go - \else - \expandafter\POL:NE:polfunc:p - \fi {##1}{##2}{##3}% -}}\expandafter\POL@NE@polfunc\string#% -\def\POL:NE:polfunc:p #1#2#3% -{% - ~romannumeral~POL:usepolfunc:xint{#1}{#3}% -}% -\def\POL:usepolfunc:xint #1% -{% -% This is done to overcome \protected and is useful -% in case the polynomial function ends up nested -% in some non-polynomial user declared function -% as the latter (and other things) tries to pre-expand -% its arguments (as they may be used multiple time) -% using \expanded, but \#1 is protected. -% And this works recursively. We are inside braces. -% However we have a very big problem with constant -% polynomial functions. We have to handle them -% in a special way. - -`0\csname#1\expandafter\endcsname\expanded -}% -\catcode`~ 13 -\catcode`: 12 - - -%% Start defining some \protected ones here -\protected\def\POL@empty{}% -\newif\ifPOL@pol -\protected\def\POL@polglobaltrue {\global\let\ifPOL@pol\iftrue}% -\protected\def\POL@polglobalfalse{\global\let\ifPOL@pol\iffalse}% - - -%% Patch xintexpr to authorize ' in names (0.5.1) -%% Adapted 0.7.5 to follow-up on xintexpr 1.4 internal changes -%% (much simpler than previous stuff...) -%% This allows ' as a character in a polynomial name (not initial one) -\def\POL@XINT_expr_scanfunc_b #1% -{% - \ifcat \relax#1\xint_dothis{\iffalse{\fi}(_#1}\fi - \if (#1\xint_dothis{\iffalse{\fi}(`}\fi - \if 1\ifcat a#10\fi - \ifnum\xint_c_ix<1\string#1 0\fi - \if @#10\fi - \if _#10\fi - \if '#10\fi - 1% - \xint_dothis{\iffalse{\fi}(_#1}\fi - \xint_orthat {#1\XINT_expr_scanfunc_a}% -}% - - -%% Activate polexp's modified xintexpr (only during definitions -%% of polynomials) -\def\POL@hackxintexpr {% - \let\POL@originalXINT_expr_scanfunc_b\XINT_expr_scanfunc_b - \let\XINT_expr_scanfunc_b\POL@XINT_expr_scanfunc_b - \def\POL@activateNEhook{\POL@activateNEhook@pol}% -}% -\def\POL@restorexintexpr {% - \let\XINT_expr_scanfunc_b\POL@originalXINT_expr_scanfunc_b - \def\POL@activateNEhook{\POL@activateNEhook@xint}% -}% - - -%% AUXILIARIES -\catcode`! 3 -%% added at 0.7 -\newcommand\polexprsetup[1]{\POL@setup_parsekeys #1,=!,\xint_bye}% -\def\POL@setup_parsekeys #1=#2#3,{% - \ifx!#2\expandafter\xint_bye\fi - \csname POL@setup_setkey_\xint_zapspaces #1 \xint_gobble_i\endcsname - \xint_firstoftwo - {\PackageWarning{polexpr}{The \detokenize{#1} key is unknown! ignoring}}% - {\xintZapLastSpaces{#2#3}}% - \POL@setup_parsekeys -}% -\catcode`! 11 -\def\POL@setup_setkey_norr #1#2{\edef\POL@norr}% -\def\POL@setup_setkey_sqfnorr #1#2{\edef\POL@sqfnorr}% -\polexprsetup{norr=_norr, sqfnorr=_sqf_norr} - -\newcount\POL@count -\newif\ifxintveryverbose -\newif\ifpoltypesetall -\newif\ifPOL@tosturm@makefirstprimitive -\POL@tosturm@makefirstprimitivetrue -\newif\ifPOL@isolz@nextwillneedrefine -\newif\ifpoltoexprall -%% the main exchange structure (stored in macros \POLuserpol@) -%% is: degree.\POL@empty{coeff0}{coeff1}....{coeffN} -%% (degree=N except zero polynomial recognized from degree set to -1 -%% but it has always the {0/1[0]} coeff0.) -\def\POL@ifZero#1{\expandafter\POL@ifZero@aux#1;}% -\def\POL@ifZero@aux #1#2;{\if-#1\expandafter\xint_firstoftwo - \else\expandafter\xint_secondoftwo - \fi}% -\def\POL@split#1.#2;#3#4% separates degree and list of coefficients -% The \expandafter chain removes the \empty token - {\def#3{#1}\expandafter\def\expandafter#4\expandafter{#2}}% -% -\def\POL@resultfromarray #1{% ATTENTION, **MUST** be executed with -% \count@ set to 1 + degree (\count@ = 0 for zero polynomial) -% Attention to the \protected here at 0.7.5 -% They are many all over the place - \protected\edef\POL@result{\ifnum\count@>\z@ - \the\numexpr\count@-\@ne.\POL@empty - \xintiloop [1+1]% - \expandafter\POL@braceit\csname POL@array#1\xintiloopindex\endcsname - \ifnum\xintiloopindex<\count@ - \repeat -% Attention to this \protected\POL@empty -% They are many all over the place - \else-1.\POL@empty{0/1[0]}\fi}% -}% -\def\POL@braceit#1{{#1}}% needed as \xintiloopindex can not "see" through braces - - -\newcommand\PolDef[3][x]{\poldef #2(#1):=#3;}% -\def\poldef{\edef\POL@restoresemicolon{\catcode59=\the\catcode59\relax}% - \catcode59 12 \POL@defpol}% -\def\POL@defpol #1(#2)#3=#4;{% - \POL@restoresemicolon - \edef\POL@tmp{\ifxintverbose1\else0\fi}% - \unless\ifxintveryverbose\xintverbosefalse\fi - \POL@hackxintexpr - \xintdeffunc __pol(#2):=0+(#4);% force conversion to raw if a constant - \POL@restorexintexpr - \if1\POL@tmp\xintverbosetrue\fi - \edef\POL@polname{\xint_zapspaces #1 \xint_gobble_i}% - \begingroup - \setbox0\hbox{% - \let\xintScalarAdd\xintAdd - \let\xintScalarSub\xintSub - \let\xintScalarMul\xintMul - \let\xintScalarDiv\xintDiv - \let\xintScalarPow\xintPow - \let\xintScalarOpp\xintOpp - \let\xintAdd\POL@add - \let\xintMul\POL@mul - \let\xintDiv\POL@div - \let\xintPow\POL@pow - \let\xintOpp\POL@opp - \def\xintSub ##1##2{\xintAdd{##1}{\xintOpp{##2}}}% - % \xintAdd{0} to get \POL@result defined even if numerical only expression - % I could also test \ifPOL@pol, but this is anyhow small overhead -% Attention that xintexpr 1.4 has braces all over the place - \expandafter\xintAdd\expandafter{\expandafter0\expandafter}% - \romannumeral0\csname XINT_expr_userfunc___pol\endcsname - {\POL@polglobaltrue\protected\def\POL@result{1.\POL@empty{0/1[0]}{1/1[0]}}}% - \expandafter}\expandafter - \endgroup\expandafter - \def\csname POLuserpol@\POL@polname\expandafter\endcsname - \expandafter{\POL@result}% - \expandafter\POL@newpol\expandafter{\POL@polname}% -}% -% - - -\def\POL@newpol#1{% -%% We must handle specially constant polynomials because they must -%% be made to work expandably in \poldef of other polynomials due -%% to complicated matters having to do with the \POL@ifpol conditional - \ifnum\PolDegree{#1}<\@ne - % non-zero constant - % I am defining this one only for the Info message, no time now - \expandafter\edef\csname XINT_expr_polfunc_#1\endcsname - ##1{\PolNthCoeff{#1}{0}}% - % No hooks here! - \expandafter\edef\csname XINT_expr_func_#1\endcsname ##1##2##3% - {##1##2{{\PolNthCoeff{#1}{0}}}}% - \else - % polynomial of degree at least 1. This means that mechanism - % to get \POL@result will get activated and we must be very careful - % to never \edef when the Horner macro will be converted to - % a polynomial - \POL@newpolhorner{#1}% - \POL@defpolfunc{#1}{expr}% - \fi - \expandafter\let\csname XINT_flexpr_func_#1\endcsname\@undefined - \ifxintverbose\POL@info{#1}\fi -}% -\def\POL@newfloatpol#1{% -%% We must handle specially constant polynomials because they must -%% be made to work expandably in \poldef of other polynomials due -%% to complicated matters having to do with the \POL@ifpol conditional - \ifnum\PolDegree{#1}<\@ne - % non-zero constant - % I am defining this one only for the Info message, no time now - \expandafter\edef\csname XINT_flexpr_polfunc_#1\endcsname - ##1{\PolNthCoeff{#1}{0}}% - % No hooks here! - \expandafter\edef\csname XINT_flexpr_func_#1\endcsname ##1##2##3% - {##1##2{{\PolNthCoeff{#1}{0}}}}% - \else - % polynomial of degree at least 1. This means that mechanism - % to get \POL@result will get activated and we must be very careful - % to never \edef when the Horner macro will be converted to - % a polynomial - \POL@newfloatpolhorner{#1}% - \POL@defpolfunc{#1}{flexpr}% - \fi - \ifxintverbose\POL@floatinfo{#1}\fi -}% -\def\POL@info #1{% - \xintMessage {polexpr}{Info}% - {Function #1 for the \string\xintexpr\space parser is - associated to \string\XINT_expr_polfunc_#1\space - whose meaning uses Horner scheme: - \expandafter\meaning - \csname XINT_expr_polfunc_#1\endcsname}% -}% -\def\POL@floatinfo #1{% - \xintMessage {polexpr}{Info}% - {Function #1 for the \string\xintfloatexpr\space parser is - associated to \string\XINT_flexpr_polfunc_#1\space - whose meaning uses Horner scheme: - \expandafter\meaning - \csname XINT_flexpr_polfunc_#1\endcsname}% -}% -% -\def\POL@newpolhorner#1{% - %% redefine function to expand by Horner scheme. Is this useful? - %% perhaps bad idea for numerical evaluation of thing such as (1+x)^10? -% note: I added {0/1[0]} item to zero polynomial also to facilitate this - \expandafter\expandafter\expandafter\POL@split - \csname POLuserpol@#1\endcsname;\POL@var@deg\POL@var@coeffs - \edef\POL@var@coeffs{\xintRevWithBraces{\POL@var@coeffs}}% - \begingroup - \expandafter\POL@newpol@horner\POL@var@coeffs\relax - \expandafter - \endgroup - \expandafter\def\csname XINT_expr_polfunc_#1\expandafter\endcsname - \expandafter##\expandafter1\expandafter{\POL@tmp{##1}}% -}% -\def\POL@newfloatpolhorner#1{% - %% redefine function to expand by Horner scheme. Is this useful? - %% perhaps bad idea for numerical evaluation of thing such as (1+x)^10? -% note: I added {0/1[0]} item to zero polynomial also to facilitate this - \expandafter\expandafter\expandafter\POL@split - \csname POLuserpol@#1\endcsname;\POL@var@deg\POL@var@coeffs - \edef\POL@var@coeffs{\xintRevWithBraces{\POL@var@coeffs}}% - \begingroup - \expandafter\POL@newpol@floathorner\POL@var@coeffs\relax - \expandafter - \endgroup - \expandafter\def\csname XINT_flexpr_polfunc_#1\expandafter\endcsname - \expandafter##\expandafter1\expandafter{\POL@tmp{##1}}% -}% -\def\POL@newpol@horner#1{\let\xintAdd\relax\let\xintMul\relax - \def\POL@tmp##1{#1}\POL@newpol@horner@loop.}% -\def\POL@newpol@horner@loop.#1{% - \if\relax#1\expandafter\xint_gob_til_dot\fi - \edef\POL@tmp##1{\xintiiifZero{#1} - {\@firstofone}{\xintAdd{#1}}% - {\xintMul{##1}{\POL@tmp{##1}}}}% - \POL@newpol@horner@loop.% -}% -\def\POL@newpol@floathorner#1{\let\XINTinFloatAdd\relax\let\XINTinFloatMul\relax - \def\xintAdd{\XINTinFloatAdd}\def\xintMul{\XINTinFloatMul}% - \edef\POL@tmp##1{\XINTinFloatdigits{#1}}% - \POL@newpol@floathorner@loop.}% -\def\POL@newpol@floathorner@loop.#1{% - \if\relax#1\expandafter\xint_gob_til_dot\fi - \edef\POL@tmp##1{\xintiiifZero{#1} - {\@firstofone}{\xintAdd{\XINTinFloatdigits{#1}}}% - {\xintMul{##1}{\POL@tmp{##1}}}}% - \POL@newpol@floathorner@loop.% -}% - - -\newcommand\PolGenFloatVariant[1]{\POL@newfloatpol{#1}}% - - -\newcommand\PolLet[2]{\if=\noexpand#2\expandafter\xint_firstoftwo - \else\expandafter\xint_secondoftwo\fi - \POL@@let\POL@let{#1}{#2}}% -\def\POL@@let#1#2#3{\POL@let{#1}{#3}}% -\def\POL@let#1#2{% - \expandafter\let\csname POLuserpol@#1\expandafter\endcsname - \csname POLuserpol@#2\endcsname - \expandafter\let\csname XINT_expr_polfunc_#1\expandafter\endcsname - \csname XINT_expr_polfunc_#2\endcsname - \POL@defpolfunc{#1}{expr}% - \ifxintverbose\POL@info{#1}\fi -}% -\newcommand\PolGlobalLet[2]{\begingroup - \globaldefs\@ne - \if=\noexpand#2\expandafter\xint_firstoftwo - \else\expandafter\xint_secondoftwo\fi -% do I need to check something here relative to \xintNewExpr? - \POL@@globallet\POL@globallet {#1}{#2}}% -\def\POL@@globallet#1#2#3{\POL@globallet{#1}{#3}}% -\def\POL@globallet#1#2{\POL@let{#1}{#2}\endgroup}% - -\newcommand\PolAssign[1]{\def\POL@polname{#1}\POL@assign}% zap spaces in #1? -\def\POL@assign#1\toarray#2{% - \expandafter\expandafter\expandafter\POL@split - \csname POLuserpol@\POL@polname\endcsname;\POL@var@deg\POL@var@coeffs - \xintAssignArray\POL@var@coeffs\to#2% - % modify \#200 macro to return 0/1[0] for out of range indices - \@namedef{\xint_arrayname00}##1##2##3{% - \@namedef{\xint_arrayname00}####1{% - \ifnum####1>##1 \xint_dothis{ 0/1[0]}\fi - \ifnum####1>\m@ne \xint_dothis - {\expandafter\expandafter\expandafter##3% - \csname##2####1\endcsname}\fi - \unless\ifnum-####1>##1 \xint_dothis - {\expandafter\expandafter\expandafter##3% - \csname##2\the\numexpr##1+####1+\@ne\endcsname}\fi - \xint_orthat{ 0/1[0]}}% space stops a \romannumeral0 - }% - \csname\xint_arrayname00\expandafter\expandafter\expandafter\endcsname - \expandafter\expandafter\expandafter - {\csname\xint_arrayname0\expandafter\endcsname\expandafter}\expandafter - {\xint_arrayname}{ }% -}% - - -\newcommand\PolGet{}% -\def\PolGet#1#2\fromarray#3{% - \begingroup % closed in \POL@getfromarray - \POL@getfromarray{#1}{#3}% - \POL@newpol{#1}% -}% -\def\POL@getfromarray#1#2{% - \count@=#2{0} %<- intentional space - \ifnum\count@=\z@ - \protected\def\POL@result{-1.\POL@empty{0/1[0]}}% 0.5 fix for empty array - \else - \xintloop - \edef\POL@tmp{#2{\count@}}% - \edef\POL@tmp{\xintRaw{\POL@tmp}}% -% sadly xinttools (current 1.3a) arrays have no setters for individual items... - \expandafter\let\csname POL@tmparray\the\count@\endcsname\POL@tmp - \if0\xintiiSgn{\POL@tmp}% - \advance\count@\m@ne - \repeat -% dans le cas particulier d'un array avec que des éléments nuls, \count@ est -% ici devenu 0 et la boucle s'est arrêtée car #2{0} était au moins 1. De plus -% \POL@tmparray1 est bien 0/1[0] donc ok pour polynôme nul dans \POL@result - \count\tw@\count@ - \xintloop -% on mouline tous les coeffs via \xintRaw - \ifnum\count@>\@ne - \advance\count@\m@ne - \edef\POL@tmp{#2{\count@}}% - \edef\POL@tmp{\xintRaw{\POL@tmp}}% - \expandafter\let\csname POL@tmparray\the\count@\endcsname\POL@tmp - \repeat - \count@\count\tw@ - \def\POL@tmp##1.{{\csname POL@tmparray##1\endcsname}}% - \protected\edef\POL@result{\the\numexpr\count@-\@ne.\POL@empty - \xintiloop[1+1]% - \expandafter\POL@tmp\xintiloopindex.% - \ifnum\xintiloopindex<\count@ - \repeat}% - \fi - \expandafter - \endgroup - \expandafter - \def\csname POLuserpol@#1\expandafter\endcsname - \expandafter{\POL@result}% -}% - - -\newcommand\PolFromCSV[2]{% - \begingroup % closed in \POL@getfromarray - \xintAssignArray\xintCSVtoList{#2}\to\POL@arrayA - \POL@getfromarray{#1}\POL@arrayA - \POL@newpol{#1}% -% semble un peu indirect et sous-optimal -% mais je veux élaguer les coefficients nuls. Peut-être à revoir. -}% - - -\newcommand\PolTypesetCmdPrefix[1]{\xintiiifSgn{#1}{}{+}{+}}% -\newcommand\PolTypesetCmd[1]{\xintifOne{\xintiiAbs{#1}}% - {\ifnum\PolIndex=\z@\xintiiSgn{#1}\else - \xintiiifSgn{#1}{-}{}{}\fi - \let\PolIfCoeffIsPlusOrMinusOne\@firstoftwo}% - {\PolTypesetOne{#1}% - \let\PolIfCoeffIsPlusOrMinusOne\@secondoftwo}% - }% -\newcommand\PolTypesetOne{\xintSignedFrac}% -\newcommand\PolTypesetMonomialCmd{% - \ifcase\PolIndex\space - % - \or\PolVar - \else\PolVar^{\PolIndex}% - \fi -}% -\newcommand\PolTypeset{\@ifstar - {\def\POL@ts@ascending{1}\POL@Typeset}% - {\def\POL@ts@ascending{0}\POL@Typeset}% -}% -\newcommand\POL@Typeset[2][x]{% LaTeX \newcommand forces optional argument first - \ensuremath{% - \expandafter\expandafter\expandafter\POL@split - \csname POLuserpol@#2\endcsname;\POL@var@deg\POL@var@coeffs - \if\POL@ts@ascending1% - \def\PolIndex{0}% - \let\POL@ts@reverse\@firstofone - \let\POL@@ne@or@m@ne\@ne - \else - \let\PolIndex\POL@var@deg - \ifnum\PolIndex<\z@\def\PolIndex{0}\fi - \let\POL@ts@reverse\xintRevWithBraces - \let\POL@@ne@or@m@ne\m@ne - \fi - \def\PolVar{#1}% - \ifnum\POL@var@deg<\z@ - \PolTypesetCmd{0/1[0]}\PolTypesetMonomialCmd - \else - \ifnum\POL@var@deg=\z@ - \expandafter\PolTypesetCmd\POL@var@coeffs\PolTypesetMonomialCmd - \else - \def\POL@ts@prefix##1{\let\POL@ts@prefix\PolTypesetCmdPrefix}% - \expandafter\POL@ts@loop - \romannumeral-`0\POL@ts@reverse{\POL@var@coeffs}\relax - \fi - \fi - }% -}% -\def\POL@ts@loop{\ifpoltypesetall\expandafter\xint_firstoftwo - \else\expandafter\xint_secondoftwo\fi - {\POL@ts@nocheck}{\POL@ts@check}.% -}% -\def\POL@ts@check.#1{% - \if\relax#1\expandafter\xint_gob_til_dot\fi - \xintiiifZero{#1}% - {}% - {\POL@ts@prefix{#1}\PolTypesetCmd{#1}\PolTypesetMonomialCmd}% - \edef\PolIndex{\the\numexpr\PolIndex+\POL@@ne@or@m@ne}\POL@ts@check.% -}% -\def\POL@ts@nocheck.#1{% - \if\relax#1\expandafter\xint_gob_til_dot\fi - \POL@ts@prefix{#1}\PolTypesetCmd{#1}\PolTypesetMonomialCmd - \edef\PolIndex{\the\numexpr\PolIndex+\POL@@ne@or@m@ne}\POL@ts@nocheck.% -}% - - -\newcommand\PolMapCoeffs[2]{% #1 = macro, #2 = name - \POL@mapcoeffs{#1}{#2}% - \POL@newpol{#2}% -}% -\def\POL@mapcoeffs#1#2{% - \begingroup - \def\POL@mapcoeffs@macro{#1}% - \expandafter\expandafter\expandafter\POL@split - \csname POLuserpol@#2\endcsname;\POL@mapcoeffs@deg\POL@mapcoeffs@coeffs -% ATTENTION à ne pas faire un \expandafter ici, car brace removal si 1 item - \xintAssignArray\POL@mapcoeffs@coeffs\to\POL@arrayA - \def\index{0}% - \count@\z@ - \expandafter\POL@map@loop\expandafter.\POL@mapcoeffs@coeffs\relax - \xintloop -% this abuses that \POL@arrayA0 is never 0. - \xintiiifZero{\csname POL@arrayA\the\count@\endcsname}% - {\iftrue}% - {\iffalse}% - \advance\count@\m@ne - \repeat -% donc en sortie \count@ est 0 ssi pol nul. - \POL@resultfromarray A% - \expandafter - \endgroup - \expandafter - \def\csname POLuserpol@#2\expandafter\endcsname\expandafter{\POL@result}% -}% -\def\POL@map@loop.#1{\if\relax#1\expandafter\xint_gob_til_dot\fi - \advance\count@\@ne - \edef\POL@map@coeff{\POL@mapcoeffs@macro{#1}}% - \expandafter - \let\csname POL@arrayA\the\count@\endcsname\POL@map@coeff - \edef\index{\the\numexpr\index+\@ne}% - \POL@map@loop.}% -\def\POL@xintIrr#1{\xintIrr{#1}[0]}% -\newcommand\PolReduceCoeffs{\@ifstar\POL@sreducecoeffs\POL@reducecoeffs}% -\def\POL@reducecoeffs#1{\PolMapCoeffs{\POL@xintIrr}{#1}}% -\def\POL@sreducecoeffs#1{\PolMapCoeffs{\xintPIrr}{#1}}% - - -%% EUCLIDEAN DIVISION -\newcommand\PolDivide[4]{% #3=quotient, #4=remainder of #1 by #2 - \POL@divide{#1}{#2}% - \expandafter\let\csname POLuserpol@#3\endcsname\POL@Q - \POL@newpol{#3}% - \expandafter\let\csname POLuserpol@#4\endcsname\POL@R - \POL@newpol{#4}% -}% -\newcommand\PolQuo[3]{% #3=quotient of #1 by #2 - \POL@divide{#1}{#2}% - \expandafter\let\csname POLuserpol@#3\endcsname\POL@Q - \POL@newpol{#3}% -}% -\newcommand\PolRem[3]{% #3=remainder of #1 by #2 - \POL@divide{#1}{#2}% - \expandafter\let\csname POLuserpol@#3\endcsname\POL@R - \POL@newpol{#3}% -}% -\newcommand\POL@divide[2]{% - \begingroup - \let\xintScalarSub\xintSub - \let\xintScalarAdd\xintAdd - \let\xintScalarMul\xintMul - \let\xintScalarDiv\xintDiv - \expandafter\let\expandafter\POL@A\csname POLuserpol@#1\endcsname - \expandafter\let\expandafter\POL@B\csname POLuserpol@#2\endcsname - \POL@div@c - \let\POL@Q\POL@result - \ifnum\POL@degQ<\z@ - \let\POL@R\POL@A - \else - \count@\numexpr\POL@degR+\@ne\relax - \POL@resultfromarray R% - \let\POL@R\POL@result - \fi - \expandafter - \endgroup - \expandafter - \def\csname POL@Q\expandafter\expandafter\expandafter\endcsname - \expandafter\expandafter\expandafter{\expandafter\POL@Q\expandafter}% - \expandafter - \def\csname POL@R\expandafter\endcsname\expandafter{\POL@R}% -}% - - -%% GCD -\newcommand\PolGCD[3]{% sets #3 to the (unitary) G.C.D. of #1 and #2 - \POL@GCD{#1}{#2}{#3}% - \POL@newpol{#3}% -}% -\def\POL@GCD #1#2#3{% - \begingroup - \let\xintScalarSub\xintSub - \let\xintScalarAdd\xintAdd - \let\xintScalarMul\xintMul - \let\xintScalarDiv\xintDiv - \expandafter\let\expandafter\POL@A\csname POLuserpol@#1\endcsname - \expandafter\let\expandafter\POL@B\csname POLuserpol@#2\endcsname - \expandafter\POL@split\POL@A;\POL@degA\POL@polA - \expandafter\POL@split\POL@B;\POL@degB\POL@polB - \ifnum\POL@degA<\z@ - \expandafter\xint_firstoftwo\else\expandafter\xint_secondoftwo - \fi - {\ifnum\POL@degB<\z@ - \expandafter\xint_firstoftwo\else\expandafter\xint_secondoftwo - \fi - {\protected\def\POL@result{-1.\POL@empty{0/1[0]}}}% - {\xintAssignArray\POL@polB\to\POL@arrayB - \POL@normalize{B}% - \POL@gcd@exit BA}}% - {\ifnum\POL@degB<\z@ - \expandafter\xint_firstoftwo\else\expandafter\xint_secondoftwo - \fi - {\xintAssignArray\POL@polA\to\POL@arrayA - \POL@normalize{A}% - \POL@gcd@exit AB}% - {\ifnum\POL@degA<\POL@degB\space - \let\POL@tmp\POL@B\let\POL@B\POL@A\let\POL@A\POL@tmp - \let\POL@tmp\POL@degB\let\POL@degB\POL@degA\let\POL@degA\POL@tmp - \let\POL@tmp\POL@polB\let\POL@polB\POL@polA\let\POL@polA\POL@tmp - \fi - \xintAssignArray\POL@polA\to\POL@arrayA - \xintAssignArray\POL@polB\to\POL@arrayB - \POL@gcd AB% - }}% - \expandafter - \endgroup - \expandafter\def\csname POLuserpol@#3\expandafter\endcsname - \expandafter{\POL@result}% -}% -\def\POL@normalize#1{% - \expandafter\def\expandafter\POL@tmp\expandafter - {\csname POL@array#1\csname POL@array#10\endcsname\endcsname}% - \edef\POL@normalize@leading{\POL@tmp}% - \expandafter\def\POL@tmp{1/1[0]}% - \count@\csname POL@deg#1\endcsname\space - \xintloop - \ifnum\count@>\z@ - \expandafter\edef\csname POL@array#1\the\count@\endcsname - {\xintIrr{\xintScalarDiv - {\csname POL@array#1\the\count@\endcsname}% - {\POL@normalize@leading}}[0]}% - \advance\count@\m@ne - \repeat -}% -\def\POL@gcd#1#2{% - \POL@normalize{#2}% - \edef\POL@degQ{\the\numexpr\csname POL@deg#1\endcsname - -\csname POL@deg#2\endcsname}% - \count@\numexpr\csname POL@deg#1\endcsname+\@ne\relax - \count\tw@\numexpr\POL@degQ+\@ne\relax - \xintloop - \POL@gcd@getremainder@loopbody#1#2% - \ifnum\count\tw@>\z@ - \repeat - \expandafter\def\csname POL@array#10\endcsname{1}% - \xintloop - \xintiiifZero{\csname POL@array#1\the\count@\endcsname}% - {\iftrue}% - {\iffalse}% - \advance\count@\m@ne - \repeat - \expandafter\edef\csname POL@deg#1\endcsname{\the\numexpr\count@-\@ne}% - \ifnum\count@<\@ne - \expandafter\POL@gcd@exit - \else - \expandafter\edef\csname POL@array#10\endcsname{\the\count@}% - \expandafter\POL@gcd - \fi{#2}{#1}% -}% -\def\POL@gcd@getremainder@loopbody#1#2{% - \edef\POL@gcd@ratio{\csname POL@array#1\the\count@\endcsname}% - \advance\count@\m@ne - \advance\count\tw@\m@ne - \count4 \count@ - \count6 \csname POL@deg#2\endcsname\space - \xintloop - \ifnum\count6>\z@ - \expandafter\edef\csname POL@array#1\the\count4\endcsname - {\xintScalarSub - {\csname POL@array#1\the\count4\endcsname}% - {\xintScalarMul - {\POL@gcd@ratio}% - {\csname POL@array#2\the\count6\endcsname}}}% - \advance\count4 \m@ne - \advance\count6 \m@ne - \repeat -}% -\def\POL@gcd@exit#1#2{% - \count@\numexpr\csname POL@deg#1\endcsname+\@ne\relax - \POL@resultfromarray #1% -}% - - -%% TODO: BEZOUT - - -%% DIFFERENTIATION -\def\POL@diff@loop@one #1/#2[#3]#4% - {\xintIrr{\xintiiMul{#4}{#1}/#2[0]}[#3]}% -\def\POL@diff#1{\POL@diff@loop1.}% -\def\POL@diff@loop#1.#2{% - \if\relax#2\expandafter\xint_gob_til_dot\fi - {\expandafter\POL@diff@loop@one\romannumeral0\xintraw{#2}{#1}}% - \expandafter\POL@diff@loop\the\numexpr#1+\@ne.% -}% -\newcommand\PolDiff[1][1]{% - % optional parameter is how many times to derivate - % first mandatory arg is name of polynomial function to derivate, - % same name as in \NewPolExpr - % second mandatory arg name of derivative - \edef\POL@iterindex{\the\numexpr#1\relax}% - \ifnum\POL@iterindex<\z@ - \expandafter\@firstoftwo - \else - \expandafter\@secondoftwo - \fi - {\PolAntiDiff[-\POL@iterindex]}{\POL@Diff}% -}% -\def\POL@Diff{% - \ifcase\POL@iterindex\space - \expandafter\POL@Diff@no - \or\expandafter\POL@Diff@one - \else\xint_afterfi{\POL@Iterate\POL@Diff@one}% - \fi -}% -\def\POL@Diff@no #1#2{\POL@let{#2}{#1}}% -\def\POL@Diff@one #1#2{\POL@Diff@@one {#1}{#2}\POL@newpol{#2}}% -\def\POL@Diff@@one#1#2{% - \expandafter\expandafter\expandafter\POL@split - \csname POLuserpol@#1\endcsname;\POL@var@deg\POL@var@coeffs - \ifnum\POL@var@deg<\@ne - \@namedef{POLuserpol@#2}{-1.\POL@empty{0/1[0]}}% - \else - \edef\POL@var@coeffs{\expandafter\POL@diff\POL@var@coeffs\relax}% - \expandafter\edef\csname POLuserpol@#2\endcsname - {\the\numexpr\POL@var@deg-\@ne.\POL@empty\POL@var@coeffs}% - \fi -}% -% lazy way but allows to share with AntiDiff -\def\POL@Iterate#1#2#3{% - \begingroup - \xintverbosefalse - #1{#2}{#3}% - \xintloop - \ifnum\POL@iterindex>\tw@ - #1{#3}{#3}% - \edef\POL@iterindex{\the\numexpr\POL@iterindex-\@ne}% - \repeat - \expandafter - \endgroup\expandafter - \def\csname POLuserpol@#3\expandafter\endcsname - \expandafter{\romannumeral`^^@\csname POLuserpol@#3\endcsname}% - #1{#3}{#3}% -}% - - -%% ANTI-DIFFERENTIATION -\def\POL@antidiff@loop@one #1/#2[#3]#4% - {\xintIrr{#1/\xintiiMul{#4}{#2}[0]}[#3]}% -\def\POL@antidiff{\POL@antidiff@loop1.}% -\def\POL@antidiff@loop#1.#2{% - \if\relax#2\expandafter\xint_gob_til_dot\fi - {\expandafter\POL@antidiff@loop@one\romannumeral0\xintraw{#2}{#1}}% - \expandafter\POL@antidiff@loop\the\numexpr#1+\@ne.% -}% -\newcommand\PolAntiDiff[1][1]{% - % optional parameter is how many times to derivate - % first mandatory arg is name of polynomial function to derivate, - % same name as in \NewPolExpr - % second mandatory arg name of derivative - \edef\POL@iterindex{\the\numexpr#1\relax}% - \ifnum\POL@iterindex<\z@ - \expandafter\@firstoftwo - \else - \expandafter\@secondoftwo - \fi - {\PolDiff[-\POL@iterindex]}{\POL@AntiDiff}% -}% -\def\POL@AntiDiff{% - \ifcase\POL@iterindex\space - \expandafter\POL@AntiDiff@no - \or\expandafter\POL@AntiDiff@one - \else\xint_afterfi{\POL@Iterate\POL@AntiDiff@one}% - \fi -}% -\let\POL@AntiDiff@no\POL@Diff@no -\def\POL@AntiDiff@one #1#2{\POL@AntiDiff@@one{#1}{#2}\POL@newpol{#2}}% -\def\POL@AntiDiff@@one#1#2{% - \expandafter\expandafter\expandafter\POL@split - \csname POLuserpol@#1\endcsname;\POL@var@deg\POL@var@coeffs - \ifnum\POL@var@deg<\z@ - \@namedef{POLuserpol@#2}{-1.\POL@empty{0/1[0]}}% - \else - \edef\POL@var@coeffs{\expandafter\POL@antidiff\POL@var@coeffs\relax}% - \expandafter\edef\csname POLuserpol@#2\endcsname - {\the\numexpr\POL@var@deg+\@ne.\POL@empty{0/1[0]}\POL@var@coeffs}% - \fi -}% - -%% IContent and \PolMakePrimitive (0.5) -\def\POL@aux@mgcd@loop#1#2{% - \if\relax#2\expandafter\POL@aux@mgcd@exit\fi - \expandafter - \POL@aux@mgcd@loop\romannumeral0\POL@aux@gcd#1.#2.% -}% -\def\POL@aux@mgcd@exit - \expandafter - \POL@aux@mgcd@loop\romannumeral0\POL@aux@gcd#1.\relax.{\xintiiabs{#1}}% -\def\POL@aux@gcd#1.#2.{% - \if0\xintiiSgn{#1}\expandafter\POL@aux@gcd@exit\fi - \expandafter\POL@aux@gcd\romannumeral0\xintmod {#2}{#1}.#1.}% -\def\POL@aux@gcd@exit - \expandafter\POL@aux@gcd\romannumeral0\xintmod #1#2.#3.{{#1}}% - -\def\POL@icontent #1{\romannumeral0\expandafter - \POL@aux@mgcd@loop\romannumeral`^^@#1\relax}% - -\newcommand\PolIContent[1]{\romannumeral0\expandafter - \POL@aux@mgcd@loop\romannumeral`^^@\PolToList{#1}\relax}% - - -\def\POL@makeprim@macro#1% - {\xintREZ{\xintNum{\xintDiv{#1}{\POL@makeprim@icontent}}}}% -\newcommand\PolMakePrimitive[1]{% - % This does not need a full user declared polynomial on input, only - % a \POLuserpol@name macro, but on output it is fully declared - \edef\POL@makeprim@icontent{\PolIContent{#1}}% - \PolMapCoeffs\POL@makeprim@macro{#1}% -}% -\def\POL@makeprimitive#1{% - % Avoids declaring the polynomial, internal usage in \PolToSturm - \edef\POL@makeprim@icontent{\PolIContent{#1}}% - \POL@mapcoeffs\POL@makeprim@macro{#1}% -}% - - -%% 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'. -\newcommand\PolToSturm{\@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. - \@namedef{POLuserpol@\POL@sturmname _0}{0.\POL@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 - \POL@divide{\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 - \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 _}% - \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 - \@namedef{POLuserpol@\POL@sturmname _\POL@sturm@N}{0.\POL@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 - \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 -}% -\newcommand\PolSturmChainLength[1] - {\romannumeral`^^@\csname PolSturmChainLength_#1\endcsname}% - -\newcommand\PolSetToSturmChainSignChangesAt[4][\global]{% - \edef\POL@sturmchain@X{\xintREZ{#4}}% - \edef\POL@sturmname{#3}% - \edef\POL@sturmlength{\PolSturmChainLength{\POL@sturmname}}% - \POL@sturmchain@getSV@at\POL@sturmchain@X - #1\let#2\POL@sturmchain@SV -}% -\def\POL@sturmchain@getSV@at#1{% ATTENTION USES \POL@count - \def\POL@sturmchain@SV{0}% - \edef\POL@sturmchain@sign{\xintiiSgn{\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 -}% -\newcommand\PolSetToNbOfZerosWithin[5][\global]{% - \edef\POL@tmpA{\xintREZ{#4}}% - \edef\POL@tmpB{\xintREZ{#5}}% - \edef\POL@sturmname{#3}% - \edef\POL@sturmlength{\PolSturmChainLength{\POL@sturmname}}% - \POL@sturmchain@getSV@at\POL@tmpA - \let\POL@SVA\POL@sturmchain@SV - \POL@sturmchain@getSV@at\POL@tmpB - \let\POL@SVB\POL@sturmchain@SV - \ifnum\POL@SVA<\POL@SVB\space - #1\edef#2{\the\numexpr\POL@SVB-\POL@SVA}% - \else - #1\edef#2{\the\numexpr\POL@SVA-\POL@SVB}% - \fi -}% - - -% 0.6 added starred variant to count multiplicities -% 0.7 added double starred variant to locate all rational roots -\newcommand\PolSturmIsolateZeros{\@ifstar - {\PolSturmIsolateZerosAndGetMultiplicities}% - {\PolSturmIsolateZeros@}% -}% -\newcommand\PolSturmIsolateZerosAndGetMultiplicities{\@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}}% -\newcommand\PolSturmIsolateZerosGetMultiplicitiesAndRationalRoots[2][\empty]{% - \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 -\newcommand\PolSturmIsolateZerosAndFindRationalRoots[2][\empty]{% - % #1 optional E such that roots are searched in -10^E < x < 10^E - % both -10^E and +10^E must not be roots! - % #2 name of Sturm chain (already pre-computed) - \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) - \expandafter\let - \csname POLuserpol@\POL@sturmname\POL@sqfnorr\expandafter\endcsname - \csname POLuserpol@\POL@sturmname _0\endcsname - \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.\POL@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.\POL@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.\POL@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... - \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 - \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}% - \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 - \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 - \POL@isolzmult@loop - \POL@tosturm@makefirstprimitivetrue - \let\POL@sturmname\POL@originalsturmname - \fi -}% - - -\newcommand\PolSturmIsolateZerosAndGetMultiplicities@[2][\empty]{% - % #1 optional E such that roots are searched in -10^E < x < 10^E - % both -10^E and +10^E must not be roots! - % #2 name of Sturm chain (already pre-computed) - \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 - \POL@isolzmult@loop - \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... - \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 -}% - - -\newcommand\PolSturmIsolateZeros@[2][\empty]{% - % #1 optional E such that roots are searched in -10^E < x < 10^E - % both -10^E and +10^E must not be roots! - % #2 name of Sturm chain (already pre-computed from a given polynomial) - % 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... - \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{\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 -}% -\newcommand\PolRefineInterval{\@ifstar\POL@srefine@start\POL@refine@start}% -\newcommand\POL@refine@start[3][1]{% - \edef\POL@isolz@IntervalIndex{\the\numexpr#3}% - \edef\POL@sturmname{#2}% - \expandafter\POL@refine@sharedbody\expandafter - {\expandafter\POL@refine@loop\expandafter{\the\numexpr#1}}% -}% -\def\POL@srefine@start#1#2{% - \edef\POL@isolz@IntervalIndex{\the\numexpr#2}% - \edef\POL@sturmname{#1}% - \POL@refine@sharedbody - {\let\POL@refine@left@next\POL@refine@main % we want to recurse if needed - \let\POL@refine@right@next\POL@refine@main % we want to recurse if needed - \POL@refine@main}% -}% -\def\POL@refine@sharedbody#1{% - \POL@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 - - -\newcommand\PolIntervalWidth[2]{% -% le \xintRez est à cause des E positifs, car trailing zéros explicites -% si je travaillais à partir des variables xintexpr directement ne devrait -% pas être nécessaire, mais trop fragile par rapport à chgt internes possibles - \romannumeral0\xintrez{\xintSub{\@nameuse{POL_ZR#1*}{#2}}% - {\@nameuse{POL_ZL#1*}{#2}}} -}% - - -\newcommand\PolEnsureIntervalLengths[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 -}% -\newcommand\PolEnsureIntervalLength[3]{% #1 = Sturm chain name, - % #2 = index of interval - % localize roots in intervals of length at most 10^{#3} - \edef\POL@sturmname{#1}% - \edef\POL@ensure@targetE{\the\numexpr#3}% - \edef\POL@isolz@IntervalIndex{\the\numexpr#2}% -% 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èrerai 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 -}% - - -\catcode`_ 8 -\newcommand\PolPrintIntervals - {\@ifstar{\PolPrintIntervals@@}{\PolPrintIntervals@}}% -\newcommand\PolPrintIntervals@@{% - \begingroup - \def\POL@AfterPrintIntervals{\endgroup}% - \def\arraystretch{2}% - \let\PolPrintIntervalsPrintExactZero\POL@@PrintIntervalsPrintExactZero - \let\PolPrintIntervalsUnknownRoot\POL@@PrintIntervalsUnknownRoot - \let\PolPrintIntervalsKnownRoot\POL@@PrintIntervalsKnownRoot - \def\PolPrintIntervalsBeginEnv{\[\begin{array}{cl}}%\] - \def\PolPrintIntervalsEndEnv{\end{array}\]}% - \PolPrintIntervals@ -}% -\newcommand\PolPrintIntervals@[2][Z]{\POL@PrintIntervals{#1}{#2}}% -\newcommand\POL@PrintIntervals[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 -\newcommand\PolPrintIntervalsNoRealRoots{}% -\newcommand\PolPrintIntervalsBeginEnv{\[\begin{array}{rcccl}}% -\newcommand\PolPrintIntervalsEndEnv{\end{array}\]}% -\newcommand\PolPrintIntervalsKnownRoot{% - &&\PolPrintIntervalsTheVar_{\PolPrintIntervalsTheIndex}% - &=&\PolPrintIntervalsPrintExactZero -}% -\newcommand\PolPrintIntervalsUnknownRoot{% - \PolPrintIntervalsPrintLeftEndPoint&<&% - \PolPrintIntervalsTheVar_{\PolPrintIntervalsTheIndex}&<&% - \PolPrintIntervalsPrintRightEndPoint -}% -\newcommand\PolPrintIntervalsPrintExactZero {\PolPrintIntervalsTheLeftEndPoint}% -\newcommand\PolPrintIntervalsPrintLeftEndPoint {\PolPrintIntervalsTheLeftEndPoint}% -\newcommand\PolPrintIntervalsPrintRightEndPoint{\PolPrintIntervalsTheRightEndPoint}% -\newcommand\PolPrintIntervalsPrintMultiplicity{(\mbox{mult. }\PolPrintIntervalsTheMultiplicity)}% -% -\newcommand\POL@@PrintIntervalsKnownRoot{% - \PolPrintIntervalsPrintMultiplicity&% - \PolPrintIntervalsTheVar_{\PolPrintIntervalsTheIndex}=% - \PolPrintIntervalsPrintExactZero -}% -\newcommand\POL@@PrintIntervalsPrintExactZero{% - \displaystyle - \xintSignedFrac{\PolPrintIntervalsTheLeftEndPoint}% -}% -\newcommand\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`_ 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{\\\POL@PrintIntervals@Loop}% - \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 - }% -}% - - -\newcommand\PolSturmIfZeroExactlyKnown[2]{% #1 = sturmname, #2=index - \romannumeral0\csname POL_ZK#1*\endcsname{#2}% -}% -\newcommand\POL@SturmIfZeroExactlyKnown[2]{% #1 = sturmname, #2=index - \romannumeral0\csname POL_ZK#1*\the\numexpr#2\endcsname -}% -\newcommand\PolSturmIsolatedZeroMultiplicity[2]{% - \romannumeral`^^@\csname POL_ZM#1*\endcsname{#2}% -}% -\newcommand\PolSturmIsolatedZeroLeft[2]{% - \romannumeral`^^@\csname POL_ZL#1*\endcsname{#2}% -}% -\newcommand\PolSturmIsolatedZeroRight[2]{% - \romannumeral`^^@\csname POL_ZR#1*\endcsname{#2}% -}% -\newcommand\PolSturmNbOfIsolatedZeros[1]{% - \romannumeral`^^@\csname POL_ZL#1*0\endcsname -}% -\newcommand\PolSturmRationalRoot[2]{% - \romannumeral`^^@\csname POL_ZL#1*% - \csname POL_RI#1*\endcsname{#2}\endcsname -}% -\newcommand\PolSturmRationalRootIndex[2]{% - \romannumeral`^^@\csname POL_RI#1*\endcsname{#2}% -}% -\newcommand\PolSturmRationalRootMultiplicity[2]{% - \romannumeral`^^@\csname POL_ZM#1% - *\csname POL_RI#1*\endcsname{#2}\endcsname -}% -\newcommand\PolSturmNbOfRationalRoots[1]{% - \romannumeral`^^@\csname POL_RI#1*0\endcsname -}% -\newcommand\PolSturmNbOfRationalRootsWithMultiplicities[1]{% -% means the \POL@norr must not have been changed in-between... - \the\numexpr\PolDegree{#1}-\PolDegree{#1\POL@norr}\relax -}% - - -\let\PolDecToString\xintDecToString - - -\newcommand\PolMakeMonic[1]{% - \edef\POL@leadingcoeff{\PolLeadingCoeff{#1}}% - \edef\POL@leadingcoeff@inverse{\xintDiv{1/1[0]}{\POL@leadingcoeff}}% - \PolMapCoeffs{\xintMul{\POL@leadingcoeff@inverse}}{#1}% -}% - - -%% CORE ALGEBRA MACROS -%% We do this non-expandably, but in a nestable way... this is the whole -%% point because \xintdeffunc as used by \poldef creates a big nested macro. -%% The idea is to execute it with another meaning given to \xintAdd etc.., -%% so that it operates on "polynomials". This is a mixture of expandable -%% and non-expandable techniques. -%% -%% And it was complicated to let it work with xintexpr 1.4 -%% -\def\POL@get#1#2#3{% - \relax %!! part de la tambouille pour fonctionner en xint 1.4 - \POL@polglobalfalse - \begingroup - \protected\def\POL@result{#3}% - #3% - \expandafter - \endgroup - \expandafter\def\expandafter#1\expandafter{\POL@result}% - \unless\ifPOL@pol - % avoid expanding more than twice #3 - % #3 must be purely numerical or at least compatible with \edef - % this is why at 0.7.5 I had to handle especially constant - % polynomial functions to remove any protection from them - % (because the protection triggers the COMPOSITION when - % the polynomial is found as argument of another one and - % this is not expandable) - \edef#1{#3}% - \xintiiifZero{#1}% - {\def#1{-1.\POL@empty{0/1[0]}}}% - {\edef#1{0.\POL@empty{#1}}}% - \fi - #2% -}% - -%% COMPOSITION -%% This did not exist before 0.7.5 and is part of its adaptation to xint 1.4 -%% We thus took up this opportunity to speed up substantially composition. -%% Very serious difficulties with constant polynomials. Had to handle them -%% especially. -%% OK, that was really tough, but advantage now is that composition -%% at 0.7.5 should be more efficient than before. However when polynomials -%% become big via composition, coefficients also are big and the time -%% taken by arithmetic dominates. No time to test really, though, relieved -%% I can release xint 1.4 at last. My basic polexpr test suite passes, -%% but it goes back already to old releases. -\protected\def\POL@applypolfunc#1#2% -{% -% This #2 may be also invoing \POL@applypolfunc... - \POL@get\POL@A\POL@applypolfunc@b#2#1% -}% -\def\POL@applypolfunc@b #1% -{% -% and now the have our Horner scheme nested macro -% which hopefully will do its job with \POL@add, \POL@mul etc... - \POL@polglobalfalse - \expandafter#1\expanded - {{\POL@polglobaltrue\protected\def\noexpand\POL@result{\POL@A}}}% - \unless\ifPOL@pol - \odef\POL@result{#1{0}}% - \xintiiifZero{\POL@result}% - {\protected\def\POL@result{-1.\POL@empty{0/1[0]}}}% - {\protected\edef\POL@result{0.\POL@empty{\POL@result}}}% - \fi -}% - -%% ADDITION -\def\POL@add {\POL@get\POL@A\POL@add@b}% -\def\POL@add@b{\POL@get\POL@B\POL@add@c}% -\def\POL@add@c{% - \POL@polglobaltrue - \POL@ifZero\POL@A - {\let\POL@result\POL@B}% - {\POL@ifZero\POL@B - {\let\POL@result\POL@A}% - {\POL@@add}}% -}% -\def\POL@@add{% - \expandafter\POL@split\POL@A;\POL@degA\POL@polA - \expandafter\POL@split\POL@B;\POL@degB\POL@polB - \ifnum\POL@degA>\POL@degB\relax - \xintAssignArray\POL@polA\to\POL@arrayA - \xintAssignArray\POL@polB\to\POL@arrayB - \else - \xintAssignArray\POL@polB\to\POL@arrayA - \xintAssignArray\POL@polA\to\POL@arrayB - \let\POL@tmp\POL@degB\let\POL@degB\POL@degA\let\POL@degA\POL@tmp - \fi - \count@\z@ - \xintloop - \advance\count@\@ne - \expandafter\edef\csname POL@arrayA\the\count@\endcsname - {\xintScalarAdd{\@nameuse{POL@arrayA\the\count@}}% - {\@nameuse{POL@arrayB\the\count@}}}% - \unless\ifnum\POL@degB<\count@ - \repeat - \count@\@nameuse{POL@arrayA0} % 1+\POL@degA - % trim zero leading coefficients (we could check for equal degrees, - % but would not bring much as anyhow loop exists immediately if not) - \xintloop - % this abuses that \POL@arrayA0 is never zero - \xintiiifZero{\@nameuse{POL@arrayA\the\count@}}% - {\iftrue}% - {\iffalse}% - \advance\count@\m@ne - \repeat - \POL@resultfromarray A% attention that \POL@arrayA0 not updated -}% - -%% MULTIPLICATION -\def\POL@mul {\POL@get\POL@A\POL@mul@b}% -\def\POL@mul@b{\POL@get\POL@B\POL@mul@c}% -\def\POL@mul@c{% - \POL@polglobaltrue - \POL@ifZero\POL@A - {\protected\def\POL@result{-1.\POL@empty{0/1[0]}}}% - {\POL@ifZero\POL@B - {\protected\def\POL@result{-1.\POL@empty{0/1[0]}}}% - {\POL@@mul}}% -}% -\def\POL@@mul{% - \expandafter\POL@split\POL@A;\POL@degA\POL@polA - \expandafter\POL@split\POL@B;\POL@degB\POL@polB - \ifnum\POL@degA>\POL@degB\relax - \xintAssignArray\POL@polA\to\POL@arrayA - \xintAssignArray\POL@polB\to\POL@arrayB - \else - \xintAssignArray\POL@polB\to\POL@arrayA - \xintAssignArray\POL@polA\to\POL@arrayB - \let\POL@tmp\POL@degB - \let\POL@degB\POL@degA - \let\POL@degA\POL@tmp - \fi - \count@\z@ - \xintloop - \POL@@mul@phaseIloopbody - \unless\ifnum\POL@degB<\count@ - \repeat - \xintloop - \unless\ifnum\POL@degA<\count@ % car attention au cas de mêmes degrés - \POL@@mul@phaseIIloopbody - \repeat - \edef\POL@degC{\the\numexpr\POL@degA+\POL@degB}% - \xintloop - \unless\ifnum\POL@degC<\count@ - \POL@@mul@phaseIIIloopbody - \repeat - %\count@\the\numexpr\POL@degC+\@ne\relax % never zero polynomial here - \POL@resultfromarray C% -}% -\def\POL@@mul@phaseIloopbody{% - \advance\count@\@ne - \def\POL@tmp{0[0]}% - \count\tw@\z@ - \xintloop - \advance\count\tw@\@ne - \edef\POL@tmp{% - \xintScalarAdd - {\POL@tmp}% - {\xintScalarMul - {\@nameuse{POL@arrayA\the\count\tw@}}% - {\@nameuse{POL@arrayB\the\numexpr\count@+\@ne-\count\tw@}}% - }% - }% - \ifnum\count\tw@<\count@ - \repeat - \expandafter\let\csname POL@arrayC\the\count@\endcsname\POL@tmp -}% -\def\POL@@mul@phaseIIloopbody{% - \advance\count@\@ne - \def\POL@tmp{0[0]}% - \count\tw@\count@ - \advance\count\tw@-\@nameuse{POL@arrayB0} % - \xintloop - \ifnum\count\tw@<\count@ - \advance\count\tw@\@ne - \edef\POL@tmp{% - \xintScalarAdd - {\POL@tmp}% - {\xintScalarMul - {\@nameuse{POL@arrayA\the\count\tw@}}% - {\@nameuse{POL@arrayB\the\numexpr\count@+\@ne-\count\tw@}}% - }% - }% - \repeat - \expandafter\let\csname POL@arrayC\the\count@\endcsname\POL@tmp -}% -\def\POL@@mul@phaseIIIloopbody{% - \advance\count@\@ne - \def\POL@tmp{0[0]}% - \count\tw@\count@ - \advance\count\tw@-\@nameuse{POL@arrayB0} % - \xintloop - \advance\count\tw@\@ne - \edef\POL@tmp{% - \xintScalarAdd{\POL@tmp}% - {\xintScalarMul - {\@nameuse{POL@arrayA\the\count\tw@}}% - {\@nameuse{POL@arrayB\the\numexpr\count@+\@ne-\count\tw@}}% - }% - }% - \ifnum\@nameuse{POL@arrayA0}>\count\tw@ - \repeat - \expandafter\let\csname POL@arrayC\the\count@\endcsname\POL@tmp -}% - -%% POWERS (SCALAR EXPONENT...) -\def\POL@pow #1#2{% - \POL@polglobalfalse - \begingroup - \protected\def\POL@result{#1}% - #1% - \expandafter - \endgroup - \expandafter\def\expandafter\POL@A\expandafter{\POL@result}% - \unless\ifPOL@pol - \edef\POL@A{\xintScalarPow{#1}{#2}}% no error check - \xintiiifZero{\POL@A}% - {\protected\def\POL@result{-1.\POL@empty{0/1[0]}}}% - {\protected\edef\POL@result{0.\POL@empty{\POL@A}}}% - \else - \edef\POL@B{\numexpr\xintNum{#2}\relax}% no check on exponent >= 0 - \ifcase\POL@B - \protected\def\POL@result{0.\POL@empty{1/1[0]}}% - \or - \let\POL@result\POL@A - \else - \POL@@pow@check - \fi - \fi - \POL@polglobaltrue -}% -\def\POL@@pow@check {% -% no problem here with leftover tokens! -% should I have used that I-don't-care technique more elsewhere? - \ifnum\@ne>\POL@A - % polynomial is a constant, must get rid of dot and \empty (\POL@empty) - \edef\POL@A{\expandafter\xintScalarPow\romannumeral`^^@% - \expandafter\xint_gob_til_dot\POL@A{\POL@B}}% - \xintiiifZero{\POL@A}% - {\protected\def\POL@result{-1.\POL@empty{0/1[0]}}}% - {\protected\edef\POL@result{0.\POL@empty{\POL@A}}}% - \else - \ifnum\@ne=\POL@A - % perhaps a constant times X, check constant term - \xintiiifZero - {\expandafter\xint_firstoftwo\romannumeral`^^@% - \expandafter\xint_gob_til_dot\POL@A} - {\protected\edef\POL@result - {\the\POL@B.% here at least 2. - \POL@empty - \romannumeral\xintreplicate{\POL@B}{{0/1[0]}}% - {\xintScalarPow - {\expandafter\xint_secondoftwo\romannumeral`^^@% - \expandafter\xint_gob_til_dot\POL@A}% - {\POL@B}}}}% - {\POL@@pow}% not constant times X, use general recursion - \else - \POL@@pow% general recursion - \fi\fi -}% -\def\POL@@pow@recurse#1#2{% - \begingroup - #1% - \expandafter - \endgroup - \expandafter\def\expandafter\POL@A\expandafter{\POL@result}% - \edef\POL@B{\numexpr\xintNum{#2}\relax}% - \ifcase\POL@B - \POL@thisshouldneverhappen - \or - \let\POL@result\POL@A - \else - \expandafter\POL@@pow - \fi -}% -\def\POL@@pow {% - \let\POL@pow@exp\POL@B - \let\POL@B\POL@A - \POL@@mul - \let\POL@sqA\POL@result - \ifodd\POL@pow@exp\space - \expandafter\POL@@pow@odd - \the\numexpr(\POL@pow@exp+\@ne)/\tw@-\@ne\expandafter.% - \else - \expandafter\POL@@pow@even - \the\numexpr(\POL@pow@exp+\@ne)/\tw@-\@ne\expandafter.% - \fi -}% -\def\POL@@pow@even#1.{% - \expandafter\POL@@pow@recurse\expandafter - {\expandafter\def\expandafter\POL@result\expandafter{\POL@sqA}}% - {#1}% -}% -\def\POL@@pow@odd#1.{% - \expandafter\POL@@pow@odd@i\expandafter{\POL@A}{#1}% -}% -\def\POL@@pow@odd@i #1#2{% - \expandafter\POL@@pow@recurse\expandafter - {\expandafter\def\expandafter\POL@result\expandafter{\POL@sqA}}% - {#2}% - \expandafter\POL@mul\expandafter - {\expandafter\def\expandafter\POL@result\expandafter - {\POL@result}\POL@polglobaltrue}% - {\protected\def\POL@result{#1}\POL@polglobaltrue}% -}% - -%% DIVISION -%% no check on divisor being non-zero -\def\POL@div {\POL@get\POL@A\POL@div@b}% -\def\POL@div@b{\POL@get\POL@B\POL@div@c}% -\def\POL@div@c{% - \POL@polglobaltrue - \expandafter\POL@split\POL@A;\POL@degA\POL@polA - \expandafter\POL@split\POL@B;\POL@degB\POL@polB - \ifnum\POL@degA<\POL@degB\space - \@namedef{POL@arrayQ1}{0/1[0]}% - \def\POL@degQ{-1}% - \else - \xintAssignArray\POL@polA\to\POL@arrayR - \xintAssignArray\POL@polB\to\POL@arrayB - \POL@@div - \fi - \count@\numexpr\POL@degQ+\@ne\relax - \POL@resultfromarray Q% -}% -\def\POL@@div{% - \xintAssignArray\POL@polA\to\POL@arrayR - \xintAssignArray\POL@polB\to\POL@arrayB - \edef\POL@B@leading{\csname POL@arrayB\the\numexpr\POL@degB+\@ne\endcsname}% - \edef\POL@degQ{\the\numexpr\POL@degA-\POL@degB}% - \count@\numexpr\POL@degA+\@ne\relax - \count\tw@\numexpr\POL@degQ+\@ne\relax - \xintloop - \POL@@div@loopbody - \ifnum\count\tw@>\z@ - \repeat - %%\expandafter\def\csname POL@arrayR0\endcsname{1}% - \xintloop - \xintiiifZero{\csname POL@arrayR\the\count@\endcsname}% - {\iftrue}% - {\iffalse}% - \advance\count@\m@ne - \repeat - \edef\POL@degR{\the\numexpr\count@-\@ne}% -}% -\def\POL@@div@loopbody{% - \edef\POL@@div@ratio{% - \xintScalarDiv{\csname POL@arrayR\the\count@\endcsname}% - {\POL@B@leading}}% - \expandafter\let\csname POL@arrayQ\the\count\tw@\endcsname - \POL@@div@ratio - \advance\count@\m@ne - \advance\count\tw@\m@ne - \count4 \count@ - \count6 \POL@degB\space - \xintloop - \ifnum\count6>\z@ - \expandafter\edef\csname POL@arrayR\the\count4\endcsname - {\xintScalarSub - {\csname POL@arrayR\the\count4\endcsname}% - {\xintScalarMul - {\POL@@div@ratio}% - {\csname POL@arrayB\the\count6\endcsname}}}% - \advance\count4 \m@ne - \advance\count6 \m@ne - \repeat -}% - -%% MINUS SIGN AS UNARY OPERATOR -\def\POL@opp #1{% - \POL@polglobalfalse - \begingroup - \protected\def\POL@result{#1}% - #1% - \expandafter - \endgroup - \expandafter\def\expandafter\POL@A\expandafter{\POL@result}% - \unless\ifPOL@pol - \edef\POL@A{\xintScalarOpp{#1}}% - \xintiiifZero{\POL@A}% - {\protected\def\POL@result{-1.\POL@empty{0/1[0]}}}% - {\protected\edef\POL@result{0.\POL@empty{\POL@A}}}% - \else - \edef\POL@B{0.\POL@empty{-1/1[0]}}% - \POL@@mul - \fi - \POL@polglobaltrue -}% - - -%% EXPANDABLE MACROS -\def\POL@eval@fork#1\At#2#3\krof{#2}% -\newcommand\PolEval[3]{\romannumeral`^^@\POL@eval@fork - #2\PolEvalAt - \At\PolEvalAtExpr\krof {#1}{#3}% -}% -\newcommand\PolEvalAt[2] - {\xintpraw{\csname XINT_expr_polfunc_#1\endcsname{#2}}}% -\newcommand\POL@eval[2] - {\csname XINT_expr_polfunc_#1\endcsname{#2}}% -\newcommand\PolEvalAtExpr[2]{\xinttheexpr #1(#2)\relax}% -% -\newcommand\PolEvalReduced[3]{\romannumeral`^^@\POL@eval@fork - #2\PolEvalReducedAt - \At\PolEvalReducedAtExpr\krof {#1}{#3}% -}% -\newcommand\PolEvalReducedAt[2]{% - \xintpraw % in order not to print denominator if the latter equals 1 - {\xintIrr{\csname XINT_expr_polfunc_#1\endcsname{#2}}[0]}% -}% -\newcommand\PolEvalReducedAtExpr[2]{% - \xintpraw - {\expandafter\xintIrr\romannumeral`^^@\xintthebareeval#1(#2)\relax[0]}% -}% -% -\newcommand\PolFloatEval[3]{\romannumeral`^^@\POL@eval@fork - #2\PolFloatEvalAt - \At\PolFloatEvalAtExpr\krof {#1}{#3}% -}% -\newcommand\PolFloatEvalAt[2] - {\xintpfloat{\csname XINT_flexpr_polfunc_#1\endcsname{#2}}}% -\newcommand\PolFloatEvalAtExpr[2]{\xintthefloatexpr #1(#2)\relax}% - - -\newcommand\PolSturmIntervalIndex[3]{\the\numexpr\POL@eval@fork - #2\PolSturmIntervalIndexAt - \At\PolSturmIntervalIndexAtExpr\krof {#1}{#3}% -}% -\newcommand\PolSturmIntervalIndexAtExpr[2] - {\PolSturmIntervalIndexAt{#1}{\xinttheexpr#2\relax}}% -\newcommand\PolSturmIntervalIndexAt[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}% - }% - {}% - }% - % 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}% -\newcommand\PolSturmNbOfRootsOf[3]{\romannumeral`^^@\POL@leq@fork - #2\PolNbOfRootsLessThanOrEqualTo - \LessThanOrEqualTo\PolNbOfRootsLessThanOrEqualToExpr\krof {#1}{#3}% -}% -\newcommand\PolNbOfRootsLessThanOrEqualToExpr[2] - {\PolNbOfRootsLessThanOrEqualTo{#1}{\xinttheexpr#2\relax}}% -\newcommand\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}% - - -\newcommand\PolSturmNbWithMultOfRootsOf[3] -{\the\numexpr0\POL@leq@fork - #2\PolNbWithMultOfRootsLessThanOrEqualTo - \LessThanOrEqualTo\PolNbWithMultOfRootsLessThanOrEqualToExpr\krof {#1}{#3}% -}% -\newcommand\PolNbWithMultOfRootsLessThanOrEqualToExpr[2] - {\PolNbWithMultOfRootsLessThanOrEqualTo{#1}{\xinttheexpr#2\relax}}% -\newcommand\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}% - - -\newcommand\PolLeadingCoeff[1]{% - \romannumeral`^^@\expandafter\expandafter\expandafter\xintlastitem - \expandafter\expandafter\expandafter - {\csname POLuserpol@#1\endcsname}% -}% -% -\newcommand\PolNthCoeff[2]{\romannumeral`^^@% - \expandafter\POL@nthcoeff - \romannumeral0\xintnthelt{\ifnum\numexpr#2<\z@#2\else(#2)+1\fi}% - {\expandafter\expandafter\expandafter - \xint_gob_til_dot\csname POLuserpol@#1\endcsname}@% -}% -\def\POL@nthcoeff#1@{\if @#1@\expandafter\xint_firstoftwo - \else\expandafter\xint_secondoftwo\fi - {0/1[0]}{#1}}% -% -% returns -1 for zero polynomial for context of numerical expression -% should it return -\infty? -\newcommand\PolDegree[1]{\romannumeral`^^@\expandafter\expandafter\expandafter - \POL@degree\csname POLuserpol@#1\endcsname;}% -\def\POL@degree #1.#2;{#1}% -% -\newcommand\PolToList[1]{\romannumeral`^^@\expandafter\expandafter\expandafter - \xint_gob_til_dot\csname POLuserpol@#1\endcsname}% -% -\newcommand\PolToCSV[1]{\romannumeral0\xintlistwithsep{, }{\PolToList{#1}}}% - - -\newcommand\PolToExprCmd[1]{\xintPRaw{\xintRawWithZeros{#1}}}% -\newcommand\PolToFloatExprCmd[1]{\xintFloat{#1}}% -\let\PolToExprTermPrefix\PolTypesetCmdPrefix -\newcommand\PolToExprOneTermStyleA[2]{% - \ifnum#2=\z@ - \PolToExprCmd{#1}% - \else - \xintifOne{\xintiiAbs{#1}} - {\xintiiifSgn{#1}{-}{}{}}% + from \PolToExprTermPrefix - {\PolToExprCmd{#1}\PolToExprTimes}% - \fi - \ifcase\xintiiAbs{#2} %<-- space here mandatory - \or\PolToExprVar - \else\PolToExprVar^\xintiiAbs{#2}% - \fi -}% -\let\PolToExprOneTerm\PolToExprOneTermStyleA -\newcommand\PolToExprOneTermStyleB[2]{% - \ifnum#2=\z@ - \xintNumerator{#1}% - \else - \xintifOne{\xintiiAbs{\xintNumerator{#1}}} - {\xintiiifSgn{#1}{-}{}{}}% + from \PolToExprTermPrefix - {\xintNumerator{#1}\PolToExprTimes}% - \fi - \ifcase\xintiiAbs{#2} %<-- space here mandatory - \or\PolToExprVar - \else\PolToExprVar^\xintiiAbs{#2}% - \fi - \xintiiifOne{\xintDenominator{#1}}{}{/\xintDenominator{#1}}% -}% -\newcommand\PolToFloatExprOneTerm[2]{% - \ifnum#2=\z@ - \PolToFloatExprCmd{#1}% - \else - \PolToFloatExprCmd{#1}\PolToExprTimes - \fi - \ifcase\xintiiAbs{#2} %<-- space here mandatory - \or\PolToExprVar - \else\PolToExprVar^\xintiiAbs{#2}% - \fi -}% -\newcommand\PolToExprTimes{*}% -\newcommand\PolToExprVar{x}% -\newcommand\PolToExpr[1]{% - \if*\noexpand#1\expandafter\xint_firstoftwo\else - \expandafter\xint_secondoftwo\fi - \PolToExprAscending\PolToExprDescending{#1}}% -\newcommand\PolToFloatExpr[1]{% - \if*\noexpand#1\expandafter\xint_firstoftwo\else - \expandafter\xint_secondoftwo\fi - \PolToFloatExprAscending\PolToFloatExprDescending{#1}}% -\newcommand\PolToExprAscending[2]{% - \expandafter\POL@toexpr\csname POLuserpol@#2\endcsname - \PolToExprOneTerm\POL@toexprA}% -\newcommand\PolToFloatExprAscending[2]{% - \expandafter\POL@toexpr\csname POLuserpol@#2\endcsname - \PolToFloatExprOneTerm\POL@toexprA}% -\newcommand\PolToExprDescending[1]{% - \expandafter\POL@toexpr\csname POLuserpol@#1\endcsname - \PolToExprOneTerm\POL@toexprD}% -\newcommand\PolToFloatExprDescending[1]{% - \expandafter\POL@toexpr\csname POLuserpol@#1\endcsname - \PolToFloatExprOneTerm\POL@toexprD}% -% -\def\POL@toexpr#1#2#3{\expandafter\POL@toexpr@ - \expandafter#3\expandafter#2#1\relax}% -\def\POL@toexpr@#1#2#3.{% - \ifnum#3<\z@ - #2{0/1[0]}{0}\expandafter\xint_gobble_v - \else - \expandafter#1% - \fi {#3}#2}% -% -\def\POL@toexprA #1#2\POL@empty#3{% - \ifpoltoexprall\expandafter\POL@toexprall@b - \else\expandafter\POL@toexpr@b - \fi {#3}#2{0}1.% -}% -\def\POL@toexprD #1#2#3\relax{% #3 has \empty (\POL@empty) to prevent brace removal - \expandafter\POL@toexprD@a\expandafter#2% - \the\numexpr #1\expandafter.\romannumeral0\xintrevwithbraces{#3}\relax -}% -\def\POL@toexprD@a #1#2.#3{% - \ifpoltoexprall\expandafter\POL@toexprall@b - \else\expandafter\POL@toexpr@b - \fi{#3}#1{-#2}\the\numexpr\@ne+-#2.% -}% -\def\POL@toexpr@b #1#2#3{% - \xintiiifZero{#1}% - {\expandafter\POL@toexpr@loop\expandafter\POL@toexpr@b}% - {#2{#1}{#3}% - \expandafter\POL@toexpr@loop\expandafter\POL@toexpr@c}% - \expandafter#2% -}% -\def\POL@toexpr@c #1#2#3{% - \xintiiifZero{#1}% - {}% - {\PolToExprTermPrefix{#1}#2{#1}{#3}}% - \expandafter\POL@toexpr@loop\expandafter\POL@toexpr@c - \expandafter#2% -}% -\def\POL@toexprall@b #1#2#3{% - #2{#1}{#3}% - \expandafter\POL@toexpr@loop\expandafter\POL@toexprall@c - \expandafter#2% -}% -\def\POL@toexprall@c #1#2#3{% - \PolToExprTermPrefix{#1}#2{#1}{#3}% - \expandafter\POL@toexpr@loop\expandafter\POL@toexprall@c - \expandafter#2% -}% -\def\POL@toexpr@loop#1#2#3.#4{% - \if\relax#4\expandafter\xint_gob_til_dot\fi - #1{#4}#2{#3}\the\numexpr\@ne+#3.% -}% - - -\POL@restorecatcodes -\endinput diff --git a/macros/latex/contrib/polexpr/polexpr.txt b/macros/latex/contrib/polexpr/polexpr.txt deleted file mode 100644 index 898375926b..0000000000 --- a/macros/latex/contrib/polexpr/polexpr.txt +++ /dev/null @@ -1,2598 +0,0 @@ -.. comment: -*- fill-column: 72; mode: rst; -*- - -=============================== - Package polexpr documentation -=============================== - -0.7.5 (2020/01/31) -================== - -.. contents:: - -Basic syntax ------------- - -The syntax is:: - - \poldef polname(x):= expression in variable x; - -where: - -- in place of ``x`` an arbitrary *dummy variable* is authorized, - i.e. per default any of ``[a-z|A-Z]`` (more letters can be declared - under Unicode engines.) - -- ``polname`` consists of letters, digits, and the ``_`` and - ``'`` characters. It must start with a letter. - -.. attention:: - - The ``'`` is authorized since ``0.5.1``. As a result some constructs - recognized by the ``\xintexpr`` parser, such as ``var1 'and' var2`` - will get misinterpreted and cause errors. However these constructs - are unlikely to be frequently needed in polynomial expressions, and - the ``\xintexpr`` syntax offers alternatives, so it was deemed a - small evil. Of course the ``\xintexpr`` parser is modified only - temporarily during execution of ``\poldef``. - -One can also issue:: - - \PolDef{polname}{expression in variable x} - -which admits an optional first argument to modify the variable letter -from its default ``x``. - -``\poldef f(x):= 1-x+x^2;`` - defines polynomial ``f``. Polynomial names must start with a - letter and may contain letters, digits, underscores and the right - tick character. The - variable must be a single letter. The colon character is optional. - The semi-colon at end of expression is mandatory. - -``\PolDef{f}{1-x+x^2}`` - does the same as ``\poldef f(x):= 1-x+x^2;`` To use another letter - than ``x`` in the expression, one must pass it as an extra optional - argument to ``\PolDef``. Useful if the semi-colon has been assigned - some non-standard catcode by some package. - -``\PolLet{g}={f}`` - saves a copy of ``f`` under name ``g``. Also usable without ``=``. - -``\poldef f(z):= f(z)^2;`` - redefines ``f`` in terms of itself. - -``\poldef f(T):= f(f(T));`` - again redefines ``f`` in terms of its (new) self. - -``\poldef k(z):= f(z)-g(g(z)^2)^2;`` - should now define the zero polynomial... Let's check: - ``\[ k(z) = \PolTypeset[z]{k} \]`` - -``\PolDiff{f}{f'}`` - sets ``f'`` to the derivative of ``f``. The name doesn't have to be - ``f'`` (in fact the ``'`` is licit only since ``0.5.1``). - -.. important:: - - This is not done automatically. If some new definition needs to use - the derivative of some available polynomial, that derivative - polynomial must have been defined via ``\PolDiff``: something like - ``T'(x)^2`` will not work without a prior ``\PolDiff{T}{T'}``. - -``\PolDiff{f'}{f''}`` - obtains second derivative. - -``\PolDiff[3]{f}{f'''}`` - computes the third derivative. - -:: - - $f(z) = \PolTypeset[z]{f} $\newline - $f'(z) = \PolTypeset[z]{f'} $\newline - $f''(z) = \PolTypeset[z]{f''} $\newline - $f'''(z)= \PolTypeset[z]{f'''} $\par - -.. important:: - - The package does not currently know rational functions: ``/`` in - a parsed polynomial expression does the Euclidean quotient:: - - (1-x^2)/(1-x) - - does give ``1+x`` but :: - - (1/(1-x))*(1-x^2) - - evaluates to zero. This will work as expected:: - - \poldef k(x):= (x-1)(x-2)(x-3)(x-4)/(x^2-5x+4); - -.. _warningtacit: - -.. attention:: - - ``1/2 x^2`` skips the space and is treated like ``1/(2*x^2)`` because - of the tacit multiplication rules of \xintexpr. But this means it - gives zero! Thus one must use ``(1/2)x^2`` or ``1/2*x^2`` or - ``(1/2)*x^2`` for disambiguation: ``x - 1/2*x^2 + 1/3*x^3...``. It is - even simpler to move the denominator to the right: ``x - x^2/2 + - x^3/3 - ...``. - - It is worth noting that ``1/2(x-1)(x-2)`` suffers the same issue: - xint_ tacit multiplication always "ties more", hence this gets - interpreted as ``1/(2*(x-1)*(x-2))`` which gives zero by polynomial - division. Thus, use one of ``(1/2)(x-1)(x-2)``, ``1/2*(x-1)(x-2)`` or - ``(x-1)(x-2)/2``. - -After:: - - \poldef f_1(x):= 25(x-1)(x^2-2)(x-3)(x-4)(x-5);% - \poldef f_2(x):= 37(x-1)(x^2-2)(x-6)(x-7)(x-8);% - -the macro call ``\PolGCD{f_1}{f_2}{k}`` sets ``k`` to the (unitary) GCD of -``f_1`` and ``f_2`` (hence to the expansion of ``(x-1)(x^2-2)``.) - -``\PolToExpr{k}`` - will (expandably) give in this case ``x^3-x^2-2*x+2``. This is - useful for console or file output (the syntax is Maple- and - PSTricks-compatible; the letter used in output can be - (non-expandably) changed via a redefinition of `\\PolToExprVar`_.) - -``\PolToExpr*{k}`` - gives ascending powers: ``2-2*x-x^2+x^3``. - -Examples of localization of roots ---------------------------------- - -- To make printed decimal numbers more enjoyable than via - ``\xintSignedFrac``:: - - \renewcommand\PolTypesetOne[1]{\PolDecToString{\xintREZ{#1}}}% - - ``\PolDecToString`` will use decimal notation to incorporate the power - of ten part; and the ``\xintREZ`` will have the effect to suppress - trailing zeros if present in raw numerator (if those digits end up - after decimal mark.) Notice that the above are expandable macros and - that one can also do:: - - \renewcommand\PolToExprCmd[1]{\PolDecToString{\xintREZ{#1}}}% - - to modify output of `\\PolToExpr{polname}`_. - -- For extra info in log file use ``\xintverbosetrue``. - -- Only for some of these examples is the output included here. - - -A typical example -~~~~~~~~~~~~~~~~~ - -In this example the polynomial is square-free. - -:: - - \poldef f(x) := x^7 - x^6 - 2x + 1; - - \PolToSturm{f}{f} - \PolSturmIsolateZeros{f} - The \PolTypeset{f} polynomial has \PolSturmNbOfIsolatedZeros{f} distinct real - roots which are located in the following intervals: - \PolPrintIntervals{f} - Here is the second root with ten more decimal digits: - \PolRefineInterval[10]{f}{2} - \[\PolSturmIsolatedZeroLeft{f}{2}`_. - - As a side effect the function ``polname()`` is recognized as a - genuine ``\xintexpr...\relax`` function for (exact) numerical - evaluation (or within an ``\xintdefvar`` assignment.) It computes - values not according to the original expression but via the Horner - scheme corresponding to the polynomial coefficients. - - .. attention:: - - Release ``0.3`` also did the necessary set-up to let the - polynomial be known to the ``\xintfloatexpr`` (or - ``\xintdeffloatvar``) parser. - - Since ``0.4`` this isn't done automatically. Even more, a - previously existing floating point variant of the same name will - be let undefined again, to avoid hard to debug mismatches between - exact and floating point polynomials. This also applies when the - polynomial is produced not via ``\poldef`` or ``\PolDef`` but as - a product of the other package macros. - - See `\\PolGenFloatVariant{polname}`_. - - The original expression is lost after parsing, and in particular - the package provides no way to typeset it. This has to be done - manually, if needed. - -.. _PolDef: - -``\PolDef[letter]{polname}{expression in letter}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Does the same as `\\poldef `_ in an undelimited macro - format (thus avoiding potential problems with the catcode of the - semi-colon in presence of some packages.) In absence of the - ``[letter]`` optional argument, the variable is assumed to be ``x``. - -.. _PolGenFloatVariant: - -``\PolGenFloatVariant{polname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Makes the polynomial also usable in the ``\xintfloatexpr`` parser. - It will therein evaluates via an Horner scheme with coefficients - already pre-rounded to the float precision. - - See also `\\PolToFloatExpr{polname}`_. - - .. attention:: - - Release ``0.3`` did this automatically on ``\PolDef`` and - ``\poldef`` but this was removed at ``0.4`` for optimization. - - Any operation, for example generating the derivative polynomial, - or dividing two polynomials or using the ``\PolLet``, **must** be - followed by explicit usage of ``\PolGenFloatVariant{polname}`` if - the new polynomial is to be used in ``\xintfloatexpr`` or alike - context. - -.. _PolLet: - -``\PolLet{polname_2}={polname_1}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Makes a copy of the already defined polynomial ``polname_1`` to a - new one ``polname_2``. Same effect as - ``\PolDef{polname_2}{polname_1(x)}`` but with less overhead. The - ``=`` is optional. - -.. _PolGlobalLet: - -``\PolGlobalLet{polname_2}={polname_1}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Acts globally. - -.. _PolAssign: - -``\PolAssign{polname}\toarray\macro`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Defines a one-argument expandable macro ``\macro{#1}`` which expands - to the (raw) #1th polynomial coefficient. - - - Attention, coefficients here are indexed starting at 1. - - - With #1=-1, -2, ..., ``\macro{#1}`` returns leading coefficients. - - - With #1=0, returns the number of coefficients, i.e. ``1 + deg f`` - for non-zero polynomials. - - - Out-of-range #1's return ``0/1[0]``. - - See also `\\PolNthCoeff{polname}{number}`_. The main difference is that - with ``\PolAssign``, ``\macro`` is made a prefix to ``1 + deg f`` - already defined (hidden to user) macros holding individually the - coefficients but `\\PolNthCoeff{polname}{number}`_ does each time the job - to expandably recover the ``Nth`` coefficient, and due to - expandability can not store it in a macro for future usage (of course, - it can be an argument in an ``\edef``.) The other difference - is the shift by one in indexing, mentioned above (negative - indices act the same in both.) - -.. _PolGet: - -``\PolGet{polname}\fromarray\macro`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Does the converse operation to - ``\PolAssign{polname}\toarray\macro``. Each individual - ``\macro{number}`` gets expanded in an ``\edef`` and then normalized - via xintfrac_\ 's macro ``\xintRaw``. - - The leading zeros are removed from the polynomial. - - (contrived) Example:: - - \xintAssignArray{1}{-2}{5}{-3}\to\foo - \PolGet{f}\fromarray\foo - - This will define ``f`` as would have ``\poldef f(x):=1-2x+5x^2-3x^3;``. - - .. note:: - - Prior to ``0.5``, coefficients were not normalized via - ``\xintRaw`` for internal storage. - -.. _PolFromCSV: - -``\PolFromCSV{polname}{}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Defines a polynomial directly from the comma separated list of values - (or a macro expanding to such a list) of its coefficients, the *first - item* gives the constant term, the *last item* gives the leading - coefficient, except if zero, then it is dropped (iteratively). List - items are each expanded in an ``\edef`` and then put into normalized - form via xintfrac_\ 's macro ``\xintRaw``. - - As leading zero coefficients are removed:: - - \PolFromCSV{f}{0, 0, 0, 0, 0, 0, 0, 0, 0, 0} - - defines the zero polynomial, which holds only one coefficient. - - See also expandable macro `\\PolToCSV <\\PolToCSV{polname}_>`_. - - .. note:: - - Prior to ``0.5``, coefficients were not normalized via - ``\xintRaw`` for internal storage. - -.. _PolTypeset: - -``\PolTypeset{polname}`` -~~~~~~~~~~~~~~~~~~~~~~~~ - - Typesets in descending powers in math mode. It uses letter ``x`` but - this can be changed via an optional argument:: - - \PolTypeset[z]{polname} - - By default zero coefficients are skipped (issue ``\poltypesetalltrue`` - to get all of them in output). - - These commands (whose meanings will be found in the package code) - can be re-defined for customization. Their default definitions are - expandable, but this is not a requirement. - -.. _PolTypesetCmd: - -``\PolTypesetCmd{raw_coeff}`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Checks if the coefficient is ``1`` or ``-1`` and then skips printing - the ``1``, except for the constant term. Also it sets conditional - `\\PolIfCoeffIsPlusOrMinusOne{A}{B}`_. - - The actual printing of the coefficients, when not equal to plus or - minus one is handled by `\\PolTypesetOne{raw_coeff}`_. - -.. _PolTypesetOne: - -``\PolTypesetOne{raw_coeff}`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - The default is ``\xintSignedFrac`` but this macro is annoying as it - insists to use a power of ten, and not decimal notation. - - One can do things such as for example: [#]_ - - :: - - \renewcommand\PolTypesetOne[1]{\num{\xintPFloat[5]{#1}}} - \renewcommand\PolTypesetOne[1]{\num{\xintRound{4}{#1}}} - - where e.g. we used the ``\num`` macro of ``siunitx`` as it - understands floating point notation. - - .. [#] the difference in the syntaxes of ``\xintPFloat`` and - ``\xintRound`` is explained from the fact that - ``\xintPFloat`` by default uses the prevailing precision - hence the extra argument like here ``5`` is an optional one. - - One can also give a try to using `\\PolDecToString{decimal number}`_ - which uses decimal notation (at least for the numerator part). - -.. _PolTypesetMonomialCmd: - -``\PolTypesetMonomialCmd`` -^^^^^^^^^^^^^^^^^^^^^^^^^^ - - This decides how a monomial (in variable ``\PolVar`` and with - exponent ``\PolIndex``) is to be printed. The default does nothing - for the constant term, ``\PolVar`` for the first degree and - ``\PolVar^{\PolIndex}`` for higher degrees monomials. Beware that - ``\PolIndex`` expands to digit tokens and needs termination in - ``\ifnum`` tests. - -.. _PolTypesetCmdPrefix: - -``\PolTypesetCmdPrefix{raw_coeff}`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Expands to a ``+`` if the ``raw_coeff`` is zero or positive, and to - nothing if ``raw_coeff`` is negative, as in latter case the - ``\xintSignedFrac`` used by `\\PolTypesetCmd{raw_coeff}`_ will put - the ``-`` sign in front of the fraction (if it is a fraction) and - this will thus serve as separator in the typeset formula. Not used - for the first term. - -.. _PolTypeset*: - -``\PolTypeset*{polname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~ - - Typesets in ascending powers. Use e.g. ``[h]`` optional argument - (after the ``*``) to use letter ``h`` rather than ``x``. - -.. _PolDiff: - -``\PolDiff{polname_1}{polname_2}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - This sets ``polname_2`` to the first derivative of ``polname_1``. It - is allowed to issue ``\PolDiff{f}{f}``, effectively replacing ``f`` - by ``f'``. - - Coefficients of the result ``polname_2`` are irreducible fractions - (see `Technicalities`_ for the whole story.) - -.. _PolDiff[N]: - -``\PolDiff[N]{polname_1}{polname_2}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - This sets ``polname_2`` to the ``N``-th derivative of ``polname_1``. - Identical arguments is allowed. With ``N=0``, same effect as - ``\PolLet{polname_2}={polname_1}``. With negative ``N``, switches to - using ``\PolAntiDiff``. - -.. _PolAntiDiff: - -``\PolAntiDiff{polname_1}{polname_2}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - This sets ``polname_2`` to the primitive of ``polname_1`` vanishing - at zero. - - Coefficients of the result ``polname_2`` are irreducible fractions - (see `Technicalities`_ for the whole story.) - -.. _PolAntiDiff[N]: - -``\PolAntiDiff[N]{polname_1}{polname_2}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - This sets ``polname_2`` to the result of ``N`` successive integrations on - ``polname_1``. With negative ``N``, it switches to using ``\PolDiff``. - -.. _PolDivide: - -``\PolDivide{polname_1}{polname_2}{polname_Q}{polname_R}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - This sets ``polname_Q`` and ``polname_R`` to be the quotient and - remainder in the Euclidean division of ``polname_1`` by - ``polname_2``. - -.. _PolQuo: - -``\PolQuo{polname_1}{polname_2}{polname_Q}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - This sets ``polname_Q`` to be the quotient in the Euclidean division - of ``polname_1`` by ``polname_2``. - -.. _PolRem: - -``\PolRem{polname_1}{polname_2}{polname_R}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - This sets ``polname_R`` to be the remainder in the Euclidean division - of ``polname_1`` by ``polname_2``. - -.. _PolGCD: - -``\PolGCD{polname_1}{polname_2}{polname_GCD}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - This sets ``polname_GCD`` to be the (monic) GCD of the two first - polynomials. It is a unitary polynomial except if both ``polname_1`` - and ``polname_2`` vanish, then ``polname_GCD`` is the zero - polynomial. - -.. ``\PolIGCD{polname_1}{polname_2}{polname_iGCD}`` - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - **NOT YET** - - This **assumes** that the two polynomials have integer coefficients. - It then computes the greatest common divisor in the integer - polynomial ring, normalized to have a positive leading coefficient - (if the inputs are not both zero). - - ``\PolIContent{polname}`` - ~~~~~~~~~~~~~~~~~~~~~~~~~ - - **NOT YET** - - This computes a positive rational number such that dividing the - polynomial with it returns an integer coefficients polynomial with - no common factor among the coefficients. - -.. _PolToSturm: - -``\PolToSturm{polname}{sturmname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - With ``polname`` being for example ``P``, the macro starts by - computing polynomials ``P`` and ``P'``, then computes the (opposite - of the) remainder in euclidean division, iteratively. - - The last non-zero remainder ``P_N_`` (where ``N`` is obtainable as - `\\PolSturmChainLength{sturmname}`_) is up to a factor - the GCD of ``P`` and ``P'`` hence it is a constant if and only if - ``P`` is square-free. - - .. note:: - - - Since ``0.5`` all these polynomials are divided by their rational - content, so they have integer coefficients with no common factor, - and the last one if a constant is either ``1`` or ``-1``. - - - After this normalization to primitive polynomials, they are - stored internally as ``sturmname_k_``, ``k=0,1, ...``. - - - These polynomials are used internally only. To keep them as - genuine declared polynomials also after the macro call, use the - starred variant `PolToSturm*`_. - - .. note:: - - It is perfectly allowed to use the polynomial name as Sturm chain name: - ``\PolToSturm{f}(f}``. - - The macro then declares ``sturmname_0``, ``sturmname_1``, ..., which are - the (non-declared) ``sturmname_k_`` divided by the last one. Division is - not done if this last one is the constant ``1`` or ``-1``, i.e. if the - original polynomial was square-free. These polynomials are primitive - polynomials too, i.e. with integer coefficients having no common factor. - - Thus ``sturmname_0`` has exactly the same real and complex roots as - polynomial ``polname``, but with each root now of multiplicity one: - i.e. it is the "square-free part" of original polynomial ``polname``. - - Notice that ``sturmname_1`` isn't necessarily the derivative of - ``sturmname_0`` due to the various normalizations. - - The polynomials ``sturmname_k`` main utility is for the execution of - `\\PolSturmIsolateZeros{sturmname}`_. Be careful not to use these - names ``sturmname_0``, ``sturmname_1``, etc... for defining other - polynomials after having done ``\PolToSturm{polname}{sturmname}`` and - before executing ``\PolSturmIsolateZeros{sturmname}`` else the - latter will behave erroneously. - - `\\PolSturmChainLength{sturmname}`_ gives the index of the last - element of the Sturm chain. - -.. _PolToSturm*: - -``\PolToSturm*{polname}{sturmname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Does the same as `un-starred version `_ and additionally it - keeps for user usage the memory of the *un-normalized* Sturm chain - polynomials ``sturmname_k_``, ``k=0,1, ..., N``, with - ``N`` being `\\PolSturmChainLength{sturmname}`_. - - .. note:: - - This behaviour was modified at ``0.6``, anyhow the macro was - broken at ``0.5``. - - .. hint:: - - The square-free part of ``polname`` is ``sturmname_0``, and their - quotient is the polynomial with name - ``sturname_\PolSturmChainLength{sturmname}_``. It thus easy to - set-up a loop iteratively computing the latter until the last one - is a constant, thus obtaining the decomposition of an ``f`` as - a product ``c f_1 f_2 f_3 ...`` of a constant and square-free (primitive) - polynomials, where each ``f_i`` divides its predecessor. - -.. _PolSetToSturmChainSignChangesAt: - -``\PolSetToSturmChainSignChangesAt{\macro}{sturmname}{fraction}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Sets macro ``\macro`` to the number of sign changes in the Sturm - chain with name prefix ``sturmname``, at location ``fraction`` - (which must be in format as acceptable by the xintfrac_ macros.) - - .. note:: - - The author was lazy and did not provide rather an expandable - variant, where one would do ``\edef\macro{\PolNbOf...}``. - - This will presumably get added in a future release. - - After some hesitation it was decided the macro would by default - act globally. To make the scope of its macro definition local, - use ``[\empty]`` as extra optional argument. - -.. _PolSetToNbOfZerosWithin: - -``\PolSetToNbOfZerosWithin{\macro}{sturmname}{value_a}{value_b}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Applies the `Sturm Theorem`_ to set ``\macro`` to the exact number - of **distinct** roots of ``sturmname_0`` in the interval ``(value_a, - value_b]`` (the macro first re-orders the value for ``value_a <= - value_b`` to hold). - - .. note:: - - The author was lazy and did not provide rather an expandable - variant, where one would do ``\edef\macro{\PolNbOf...}``. - - This will presumably get added in future. - - After some hesitation it was decided the macro would by default - act globally. To make the scope of its macro definition local, - use ``[\empty]`` as extra optional argument. - - See also the expandable - `\\PolSturmNbOfRootsOf{sturmname}\\LessThanOrEqualTo{value}`_, from - which it is immediate (with ``\numexpr``) to create an expandable - variant of this macro. However the difference is that this macro - requires only `\\PolToSturm `_ to have been executed, - whereas the expandable variant requires prior execution of - `\\PolSturmIsolateZeros `_. - - See also the expandable - `\\PolSturmNbWithMultOfRootsOf{sturmname}\\LessThanOrEqualTo{value}`_ - which requires prior execution of - `\\PolSturmIsolateZeros* `_. - - -.. _PolSturmIsolateZeros: - -``\PolSturmIsolateZeros{sturmname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - The macros locates, using `Sturm theorem`_, as many disjoint - intervals as there are (real) roots. - - .. important:: - - The Sturm chain must have been produced by an earlier - `\\PolToSturm{polname}{sturmname}`_. - - Why does this macro ask for argument the name of Sturm chain, - rather than the name of a polynomial? well this is mainly for - legacy reason, and because it is accompanied by other macros for - which it is simpler to assume the argument will be the name of an - already computed Sturm chain. - - Notice that ``\PolToSturm{f}{f}`` is perfectly legal (the - ``sturmname`` can be same as the ``polname``): it defines - polynomials ``f_0``, ``f_1``, ... having ``f`` has name prefix. - - Such a prior call - to ``\PolToSturm`` must have been made at any rate for - ``\PolSturmIsolateZeros`` to be usable. - - After its execution they are two types of such intervals (stored in - memory and accessible via macros or xintexpr_ variables, see below): - - - singleton ``{a}``: then ``a`` is a root, (necessarily a decimal - number, but not all such decimal numbers are exactly identified yet). - - - open intervals ``(a,b)``: then there is exactly one root ``z`` - such that ``a < z < b``, and the end points are guaranteed to not - be roots. - - The interval boundaries are decimal numbers, originating - in iterated decimal subdivision from initial intervals - ``(-10^E, 0)`` and ``(0, 10^E)`` with ``E`` chosen initially large - enough so that all roots are enclosed; if zero is a root it is always - identified as such. The non-singleton intervals are of the - type ``(a/10^f, (a+1)/10^f)`` with ``a`` an integer, which is - neither ``0`` nor ``-1``. Hence either ``a`` and ``a+1`` are both positive - or they are both negative. - - One does not *a priori* know what will be the lengths of these - intervals (except that they are always powers of ten), they - vary depending on how many digits two successive roots have in - common in their respective decimal expansions. - - .. important:: - - If some two consecutive intervals share an end-point, no - information is yet gained about the separation between the two - roots which could at this stage be arbitrarily small. - - See `\\PolRefineInterval*{sturmname}{index}`_ which addresses - this issue. - - .. This procedure is covariant - with the independent variable ``x`` becoming ``-x``. - Hmm, pas sûr et trop fatigué - - The interval boundaries (and exactly found roots) are made available - for future computations in ``\xintexpr``-essions or polynomial - definitions as variables ``L_1``, - ``L_2``, etc..., for the left end-points and - ``R_1``, ``R_2``, ..., for the right - end-points. - - Thus for example, if ``sturmname`` is ``f``, one can use the - xintexpr_ variables ``fL_1``, ``fL_2``, ... to refer in expressions - to the left end-points (or to the exact root, if left and right end - points coincide). Additionally, xintexpr_ variable ``fZ_1_isknown`` - will have value ``1`` if the root in the first interval is known, - and ``0`` otherwise. And similarly for the other intervals. - - Also, macros `\\PolSturmIsolatedZeroLeft{sturmname}{index}`_ and - `\\PolSturmIsolatedZeroRight{sturmname}{index}`_ are provided which - expand to these same values, written in decimal notation (i.e. - pre-processed by `\\PolDecToString `_.) And there - is also `\\PolSturmIfZeroExactlyKnown{sturmname}{index}{A}{B}`_. - - .. important:: - - Trailing zeroes in the stored decimal numbers accessible via the - macros are significant: they are also present in the decimal - expansion of the exact root. - - These variables and macros are automatically updated when one next - uses macros such as `\\PolRefineInterval*{sturmname}{index}`_. - - The start of decimal expansion of a positive ``k``-th root is given - by `\\PolSturmIsolatedZeroLeft{sturmname}{k} - `_, and for a negative root it is given - by `\PolSturmIsolatedZeroRight{sturmname}{k} - `_. These two decimal - numbers are either both zero or both of the same sign. - - The number of distinct roots is obtainable expandably as - `\\PolSturmNbOfIsolatedZeros{sturmname}`_. - - Furthermore - `\\PolSturmNbOfRootsOf{sturmname}\\LessThanOrEqualTo{value}`_ and - `\\PolSturmNbOfRootsOf{sturmname}\\LessThanOrEqualToExpr{expression}`_. - will expandably compute respectively the number of real roots at - most equal to ``value`` or ``expression``, and the same but with - multiplicities. - - .. note:: - - In the current implementation the xintexpr_ variables - and xinttools_ arrays are globally defined. On the - other hand the Sturm sequence polynomials obey the current scope. - - .. note:: - - As all computations are done *exactly* there can be no errors... - apart those due to bad coding by author. The results are exact - bounds for the mathematically exact real roots. - - Future releases will perhaps also provide macros based on Newton - or Regula Falsi methods. Exact computations with such methods - lead however quickly to very big fractions, and this forces usage - of some rounding scheme for the abscissas if computation times - are to remain reasonable. This raises issues of its own, which - are studied in numerical mathematics. - -.. _PolSturmIsolateZeros*: - -``\PolSturmIsolateZeros*{sturmname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - The macro does the same as `\\PolSturmIsolateZeros{sturmname}`_ and - then in addition it does the extra work to determine all - multiplicities (of the real roots): - after executing this macro, - `\\PolSturmIsolatedZeroMultiplicity{sturmname}{index}`_ will expand - to the multiplicity of the root located in the ``index``\ -th - interval (intervals are enumerated from left to right, with index - starting at ``1``). - - Furthermore, if for example the ``sturmname`` is ``f``, xintexpr_ - variables ``fM_1``, ``fM_2``... hold the multiplicities thus - computed. - - .. note:: - - It is **not** necessary to have executed the `PolToSturm*`_ starred - variant, as the non-starred variant keeps internally the memory of the - original GCD (and even of the full non-normalized original Sturm - chain), even though it does not make the declarations as *user-level* - genuine polynomials. - - See `The degree nine polynomial with 0.99, 0.999, 0.9999 as triple - roots`_ for an example. - -.. _PolSturmIsolateZeros**: - -``\PolSturmIsolateZeros**{sturmname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - The macro does the same as `\\PolSturmIsolateZeros*{sturmname}`_ and - in addition it does the extra work to determine all the *rational* - roots. - - .. note:: - - After execution of this macro, a root is "known" if and only if - it is rational. - - Furthermore, primitive polynomial ``sturmname_sqf_norr`` is created - to match the (square-free) ``sturmname_0`` from which all rational - roots have been removed (see `\\polexprsetup`_ for customizing this - name). The number of distinct rational roots is thus the difference - between the degrees of these two polynomials (see also - `\\PolSturmNbOfRationalRoots{sturmname}`_). - - And ``sturmname_norr`` is ``sturmname_0_`` from which all rational - roots have been removed (see `\\polexprsetup`_), i.e. it contains - the irrational roots of the original polynomial, with the same - multiplicities. - - See `A degree five polynomial with three rational - roots`_ for an example. - -.. _PolSturmIsolateZerosAndGetMultiplicities: - -``\PolSturmIsolateZerosAndGetMultiplicities{sturmname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - This is another name for `\\PolSturmIsolateZeros*{sturmname}`_. - -.. _PolSturmIsolateZerosGetMultiplicitiesAndRationalRoots: - -``\PolSturmIsolateZerosGetMultiplicitiesAndRationalRoots{sturmname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - This is another name for `\\PolSturmIsolateZeros**{sturmname}`_. - - -``\PolSturmIsolateZerosAndFindRationalRoots{sturmname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - This works exactly like `\\PolSturmIsolateZeros**{sturmname}`_ - (inclusive of declaring the polynomials ``sturmname_sqf_norr`` and - ``sturmname_norr`` with no rational roots) except that it does *not* - compute the multiplicities of the *non-rational* roots. - - .. note:: - - There is no macro to find the rational roots but not compute - their multiplicities at the same time. - - .. attention:: - - This macro does *not* define xintexpr_ variables - ``sturmnameM_1``, ``sturmnameM_2``, ... holding the - multiplicities and it leaves the multiplicity array (whose accessor - is `\\PolSturmIsolatedZeroMultiplicity{sturmname}{index}`_) into - a broken state, as all non-rational roots will supposedly have - multiplicity one. This means that the output of - `\\PolPrintIntervals* `_ for example will be - erroneous for the intervals with irrational roots. - - I decided to document it because finding multiplicities of the - non rational roots is somewhat costly, and one may be interested - only into finding the rational roots (of course random - polynomials with integer coefficients will not have *any* - rational root anyhow). - - -.. _PolRefineInterval*: - -``\PolRefineInterval*{sturmname}{index}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - The ``index``\ -th interval (starting indexing at one) is further - subdivided as many times as is necessary in order for the newer - interval to have both its end-points distinct from the end-points of - the original interval. This means that the ``k``\ th root is then - strictly separated from the other roots. - -.. _PolRefineInterval[N]: - -``\PolRefineInterval[N]{sturmname}{index}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - The ``index``\ -th interval (starting count at one) is further - subdivided once, reducing its length by a factor of 10. This is done - ``N`` times if the optional argument ``[N]`` is present. - -.. _PolEnsureIntervalLength: - -``\PolEnsureIntervalLength{sturmname}{index}{E}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - The ``index``\ -th interval is subdivided until its length becomes at - most ``10^E``. This means (for ``E<0``) that the first ``-E`` digits - after decimal mark of the ``k``\ th root will then be known exactly. - -.. _PolEnsureIntervalLengths: - -``\PolEnsureIntervalLengths{sturmname}{E}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - The intervals as obtained from ``\PolSturmIsolateZeros`` are (if - necessary) subdivided further by (base 10) dichotomy in order for - each of them to have length at most ``10^E`` (length will be shorter - than ``10^E`` in output only if it did not change or became zero.) - - This means that decimal expansions of all roots will be known with - ``-E`` digits (for ``E<0``) after decimal mark. - -.. _PolPrintIntervals: - -``\PolPrintIntervals[varname]{sturmname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - This is a convenience macro which prints the bounds for the roots - ``Z_1``, ``Z_2``, ... (the optional argument ``varname`` allows to - specify a replacement for the default ``Z``). This will be done (by - default) in a - math mode ``array``, one interval per row, and pattern ``rcccl``, - where the second and fourth column hold the ``<`` sign, except when - the interval reduces to a singleton, which means the root is known - exactly. - - .. attention:: - - This macro was refactored at 0.7, its default output remained - identical but the ways to customize it got completely - modified. - - See next macros which govern its output. - -``\PolPrintIntervalsNoRealRoots`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Executed in place of an ``array`` environment, when there are no - real roots. Default definition:: - - \newcommand\PolPrintIntervalsNoRealRoots{} - -``\PolPrintIntervalsBeginEnv`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Default definition:: - - \newcommand\PolPrintIntervalsBeginEnv{\[\begin{array}{rcccl}} - -``\PolPrintIntervalsEndEnv`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Default definition:: - - \newcommand\PolPrintIntervalsEndEnv{\end{array}\]} - -``\PolPrintIntervalsKnownRoot`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Default definition:: - - \newcommand\PolPrintIntervalsKnownRoot{% - &&\PolPrintIntervalsTheVar_{\PolPrintIntervalsTheIndex}% - &=&\PolPrintIntervalsPrintExactZero - } - -``\PolPrintIntervalsUnknownRoot`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Default definition:: - - \newcommand\PolPrintIntervalsUnknownRoot{% - \PolPrintIntervalsPrintLeftEndPoint&<&% - \PolPrintIntervalsTheVar_{\PolPrintIntervalsTheIndex}&<&% - \PolPrintIntervalsPrintRightEndPoint - } - - -.. _PolPrintIntervalsPrintExactZero: - -``\PolPrintIntervalsPrintExactZero`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Default definition:: - - \newcommand\PolPrintIntervalsPrintExactZero{\PolPrintIntervalsTheLeftEndPoint} - - -.. _PolPrintIntervalsPrintLeftEndPoint: - -``\PolPrintIntervalsPrintLeftEndPoint`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Default definition:: - - \newcommand\PolPrintIntervalsPrintLeftEndPoint{\PolPrintIntervalsTheLeftEndPoint} - -.. _PolPrintIntervalsPrintRightEndPoint: - -``\PolPrintIntervalsPrintRightEndPoint`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Default definition is:: - - \newcommand\PolPrintIntervalsPrintRightEndPoint{\PolPrintIntervalsTheRightEndPoint} - -.. _PolPrintIntervals*: - -``\PolPrintIntervals*[varname]{sturmname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - This starred variant produces an alternative output (which - displays the root multiplicity), and is provided as an - example of customization. - - As replacement for `\\PolPrintIntervalsKnownRoot`_, - `\\PolPrintIntervalsPrintExactZero`_, - `\\PolPrintIntervalsUnknownRoot`_ it uses its own - ``\POL@@PrintIntervals...`` macros. We only reproduce here one - definition:: - - \newcommand\POL@@PrintIntervalsPrintExactZero{% - \displaystyle - \xintSignedFrac{\PolPrintIntervalsTheLeftEndPoint}% - }% - - Multiplicities are printed using this auxiliary macro: - -``\PolPrintIntervalsPrintMultiplicity`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - whose default definition is:: - - \newcommand\PolPrintIntervalsPrintMultiplicity{(\mbox{mult. }\PolPrintIntervalsTheMultiplicity)} - - -.. _PolMapCoeffs: - -``\PolMapCoeffs{\macro}{polname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - It modifies ('in-place': original coefficients get lost) each - coefficient of the defined polynomial via the *expandable* macro - ``\macro``. The degree is adjusted as necessary if some leading - coefficients vanish after the operation. In replacement text of - ``\macro``, ``\index`` expands to the coefficient index (which is - defined to be zero for the constant term). - - Notice that ``\macro`` will have to handle inputs of the shape - ``A/B[N]`` (xintfrac_ internal notation). This means that it probably - will have to be expressed in terms of macros from xintfrac_ package. - - Example:: - - \def\foo#1{\xintMul{#1}{\the\numexpr\index*\index\relax}} - - (or with ``\xintSqr{\index}``) to replace ``n``-th coefficient - ``f_n`` by ``f_n*n^2``. - -.. _PolReduceCoeffs: - -``\PolReduceCoeffs{polname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - About the same as ``\PolMapCoeffs{\xintIrr}{polname}`` (but - maintaining a ``[0]`` postfix for speedier xintfrac_ parsing when - polynomial function is used for computations.) This is a - one-argument macro, working 'in-place'. - -.. _PolReduceCoeffs*: - -``\PolReduceCoeffs*{polname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - This starred variant leaves un-touched the decimal exponent in the - internal representation of the fractional coefficients, i.e. if a - coefficient is internally ``A/B[N]``, then ``A/B`` is reduced to - smallest terms, but the ``10^N`` part is kept as is. Note: if the - polynomial is freshly defined directly via `\\PolFromCSV - `_ its coefficients might still be internally in some - format like ``1.5e7``; the macro will anyhow always first do the - needed conversion to strict format ``A/B[N]``. - - Evaluations with polynomials treated by this can be much faster than - with those handled by the non-starred variant - `\\PolReduceCoeffs{polname}`_: as the numerators and denominators - remain smaller, this proves very beneficial in favorable cases - (especially when the coefficients are decimal numbers) to the - expansion speed of the xintfrac_ macros used internally by - `\\PolEval `_. - -.. _PolMakeMonic: - -``\PolMakeMonic{polname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Divides by the leading coefficient. It is recommended to execute - `\\PolReduceCoeffs*{polname}`_ immediately afterwards. This is not - done automatically, due to the case the original polynomial had integer - coefficients and we want to keep the leading one as common - denominator. - -.. _PolMakePrimitive: - -``\PolMakePrimitive{polname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Divides by the integer content see (`\\PolIContent - `_). This thus produces a polynomial with integer - coefficients having no common factor. The sign of the leading - coefficient is not modified. - -Expandable macros ------------------ - -All these macros expand completely in two steps except ``\PolToExpr`` -and ``\PolToFloatExpr`` (and their auxiliaries) which need a -``\write``, ``\edef`` or a ``\csname...\endcsname`` context. - -.. _PolEvalAtExpr: - -``\PolEval{polname}\AtExpr{numerical expression}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - It boils down to - ``\xinttheexpr polname(numerical expression)\relax``. - -.. _PolEvalAt: - -``\PolEval{polname}\At{fraction}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Evaluates the polynomial at value ``fraction`` which must be in (or - expand to) a format acceptable to the xintfrac_ macros. - -.. _PolEvalReducedAtExpr: - -``\PolEvalReduced{polname}\AtExpr{numerical expression}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Boils down to ``\xinttheexpr reduce(polname(numerical expression))\relax``. - -.. _PolEvalReducedAt: - -``\PolEvalReduced{polname}\At{fraction}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Evaluates the polynomial at value ``fraction`` which must be in (or - expand to) a format acceptable to the xintfrac_ macros, and produce - an irreducible fraction. - -.. _PolFloatEvalAtExpr: - -``\PolFloatEval{polname}\AtExpr{numerical expression}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Boils down to ``\xintthefloatexpr polname(numerical expression)\relax``. - - This is done via a Horner Scheme (see `\\poldef `_ and - `\\PolGenFloatVariant{polname}`_), with already rounded - coefficients. [#]_ To use the *exact coefficients* with *exactly - executed* additions and multiplications, just insert it in the float - expression as in this example: [#]_ - - :: - - \xintthefloatexpr 3.27*\xintexpr f(2.53)\relax^2\relax - - The ``f(2.53)`` is exactly computed then rounded at the time of - getting raised to the power ``2``. Moving the ``^2`` inside, that - operation would also be treated exactly. - - - .. [#] Anyway each floating point operation starts by rounding its - operands to the floating point precision. - - .. [#] The ``\xintexpr`` here could be ``\xinttheexpr`` but that - would be less efficient. Cf. xintexpr_ documentation about - nested expressions. - -.. _PolFloatEvalAt: - -``\PolFloatEval{polname}\At{fraction}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Evaluates the polynomial at value ``fraction`` which must be in (or - expand to) a format acceptable to the xintfrac_ macros, and produces - a floating point number. - -.. _PolIfCoeffIsPlusOrMinusOne: - -``\PolIfCoeffIsPlusOrMinusOne{A}{B}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - This macro is a priori undefined. - - It is defined via the default `\\PolTypesetCmd{raw_coeff}`_ to be - used if needed in the execution of `\\PolTypesetMonomialCmd`_, - e.g. to insert a ``\cdot`` in front of ``\PolVar^{\PolIndex}`` if - the coefficient is not plus or minus one. - - The macro will execute ``A`` if the coefficient has been found to be - plus or minus one, and ``B`` if not. - -.. _PolLeadingCoeff: - -``\PolLeadingCoeff{polname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Expands to the leading coefficient. - -.. _PolNthCoeff: - -``\PolNthCoeff{polname}{number}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - It expands to the raw ``N``-th coefficient (``0/1[0]`` if the index - number is out of range). With ``N=-1``, ``-2``, ... expands to the - leading coefficients. - -.. _PolDegree: - -``\PolDegree{polname}`` -~~~~~~~~~~~~~~~~~~~~~~~ - - It expands to the degree. This is ``-1`` if zero polynomial but this - may change in future. Should it then expand to ``-\infty`` ? - -.. _PolIContent: - -``\PolIContent{polname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~ - - It expands to the contents of the polynomial, i.e. to the positive - fraction such that dividing by this fraction produces a polynomial - with integer coefficients having no common prime divisor. - - See `\\PolMakePrimitive `_. - -.. _PolToExpr: - -``\PolToExpr{polname}`` -~~~~~~~~~~~~~~~~~~~~~~~ - - Expands [#]_ to ``coeff_N*x^N+...`` (descending powers.) - - .. [#] in a ``\write``, ``\edef``, or ``\csname...\endcsname``, but - not under ``\romannumeral-`0``. - - By default zero coefficients are skipped (issue ``\poltoexpralltrue`` to - get all of them in output). - - By default, no ``+`` sign before negative coefficients, for - compliance with Maple input format (but see - `\\PolToExprTermPrefix{raw_coeff}`_.) Also, like the default - behaviour of `\\PolTypeset{polname}`_, does not print (for the non - constant terms) coefficients equal to plus or minus one. The degree - one monomial is output as ``x``, not ``x^1``. Complete customization is - possible, see next macros. - - Of course ``\PolToExpr{f}`` can be inserted in a ``\poldef``, as the - latter expands token by token, hence will force complete expansion - of ``\PolToExpr{f}``, but a simple ``f(x)`` is more efficient for - the identical result. - -.. _PolToExprOneTerm: - -``\PolToExprOneTerm{raw_coeff}{number}`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - This two argument expandable command takes care of the monomial and - its coefficient. The default definition is done in order for - coefficients of absolute value ``1`` not be printed explicitely - (except of course for the constant term). Also by default, the - monomial of degree one is ``x`` not ``x^1``, and ``x^0`` is skipped. - - For compatibility with Maple input requirements, by default a ``*`` - always precedes the ``x^number``, except if the coefficient is a one - or a minus one. See `\\PolToExprTimes`_. - -.. _PolToExprOneTermStyleA: - -``\PolToExprOneTermStyleA{raw_coeff}{number}`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Holds the default package meaning of - `\\PolToExprOneTerm{raw_coeff}{number}`_. - -.. _PolToExprOneTermStyleB: - -``\PolToExprOneTermStyleB{raw_coeff}{number}`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - For output in this style:: - - 2*x^11/3+3*x^8/7-x^5-x^4/4-x^3-x^2/2-2*x+1 - - issue ``\let\PolToExprOneTerm\PolToExprOneTermStyleB`` before usage of - ``\PolToExpr``. Note that then ``\PolToExprCmd`` isn't used at all. - To revert to package default, issue - ``\let\PolToExprOneTerm\PolToExprOneTermStyleA``. - - To suppress the ``*``'s, cf. `\\PolToExprTimes`_. - -.. _PolToExprCmd: - -``\PolToExprCmd{raw_coeff}`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - It is the one-argument macro used by the package definition of - ``\PolToExprOneTerm`` for the coefficients themselves (when not - equal to plus or minus one), and it defaults to - ``\xintPRaw{\xintRawWithZeros{#1}}``. One will have to redefine it - to ``\xintIrr{#1}`` or to ``\xintPRaw{\xintIrr{#1}}`` to obtain in the - output forcefully reduced coefficients. - -.. _PolToExprTermPrefix: - -``\PolToExprTermPrefix{raw_coeff}`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Defined identically as `\\PolTypesetCmdPrefix{raw_coeff}`_. It - prefixes with a plus sign for non-negative coefficients, because - they don't carry one by themselves. - -.. _PolToExprVar: - -``\PolToExprVar`` -^^^^^^^^^^^^^^^^^ - - This expands to the variable to use in output (it does not have to - be a single letter, may be an expandable macro.) Initial definition - is ``x``. - -.. _PolToExprTimes: - -``\PolToExprTimes`` -^^^^^^^^^^^^^^^^^^^ - - This expands to the symbol used for multiplication of an - ``x^{number}`` by the corresponding coefficient. The default is - ``*``. Redefine the macro to expand to nothing to get rid of it (but - this will give output incompatible with some professional computer - algebra software). - -.. _PolToExpr*: - -``\PolToExpr*{polname}`` -~~~~~~~~~~~~~~~~~~~~~~~~ - - Expands to ``coeff_0+coeff_1*x+coeff_2*x^2+...`` (ascending powers). - Customizable like `\\PolToExpr{polname}`_ via the same macros. - -.. _PolToFloatExpr: - -``\PolToFloatExpr{polname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Similar to `\\PolToExpr{polname}`_ but uses `\\PolToFloatExprCmd - <\\PolToFloatExprCmd{raw_coeff}>`_ - which by default rounds and converts the coefficients to floating - point format. - - .. note:: - - It is not necessary to have issued - `\\PolGenFloatVariant{polname}`_. The rounded coefficients are - not easily recoverable from the ``\xintfloatexpr`` polynomial - function hence ``\PolToFloatExprCmd`` operates from the *exact* - coefficients anew. - - Attention that both macros obey the prevailing float precision. - If it is changed between those macro calls, then a mismatch - exists between the coefficients as used in ``\xintfloatexpr`` and - those output by ``\PolToFloatExpr{polname}``. - -.. _PolToFloatExprOneTerm: - -``\PolToFloatExprOneTerm{raw_coeff}{number}`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Similar to `\\PolToExprOneTerm - <\\PolToExprOneTerm{raw_coeff}{number}>`_. But does not treat - especially coefficients equal to plus or minus one. - -.. _PolToFloatExprCmd: - -``\PolToFloatExprCmd{raw_coeff}`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - It is the one-argument macro used by ``\PolToFloatExprOneTerm``. - Its package definition is ``\xintFloat{#1}``. - - .. caution:: - - Currently (xint_ ``1.3c``) ``\xintFloat{0}`` outputs ``0.e0`` - which is perfectly acceptable input for Python, but not for - Maple. Thus, one should better leave the `\\poltoexprallfalse`_ - toggle to its default ``\iffalse`` state, if one intends to use - the output in a Maple worksheet. - - But even then the zero polynomial will cause a problem. Workaround:: - - \renewcommand\PolToFloatExprCmd[1]{\xintiiifZero{#1}{0.0}{\xintFloat{#1}}} - - Usage of ``\xintiiifZero`` and not ``\xintifZero`` is only for - optimization (I can't help it) because ``#1`` is known to be - in ``xintfrac`` raw format. - -.. _PolToFloatExpr*: - -``\PolToFloatExpr*{polname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Typesets in ascending powers. - -.. _PolToList: - -``\PolToList{polname}`` -~~~~~~~~~~~~~~~~~~~~~~~ - - Expands to ``{coeff_0}{coeff_1}...{coeff_N}`` with ``N`` = degree, and - ``coeff_N`` the leading coefficient - (the zero polynomial does give ``{0/1[0]}`` and not an - empty output.) - -.. _PolToCSV: - -``\PolToCSV{polname}`` -~~~~~~~~~~~~~~~~~~~~~~ - - Expands to ``coeff_0, coeff_1, coeff_2, ....., coeff_N``, starting - with constant term and ending with leading coefficient. Converse - to `\\PolFromCSV <\\PolFromCSV{polname}{\}_>`_. - -.. _PolSturmChainLength: - -``\PolSturmChainLength{sturmname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Returns the integer ``N`` such that ``sturmname_N`` is the last one - in the Sturm chain ``sturmname_0``, ``sturmname_1``, ... - - See `\\PolToSturm{polname}{sturmname}`_. - -.. _PolSturmIfZeroExactlyKnown: - -``\PolSturmIfZeroExactlyKnown{sturmname}{index}{A}{B}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Executes ``A`` if the ``index``\ -th interval reduces to a singleton, - i.e. the root is known exactly, else ``B``. - - .. note:: - - ``index`` is allowed to be something like ``1+2*3`` as it is fed - to ``\the\numexpr...\relax``. - -.. _PolSturmIsolatedZeroLeft: - -``\PolSturmIsolatedZeroLeft{sturmname}{index}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Expands to the left end-point for the ``index``\ -th interval, as - computed by some earlier `\\PolSturmIsolateZeros{sturmname}`_. - - .. note:: - - Of course, this is kept updated by macros such as - `\\PolRefineInterval{sturmname}{index} `_. - - The value is pre-formatted using `\\PolDecTostring - `_. - -.. _PolSturmIsolatedZeroRight: - -``\PolSturmIsolatedZeroRight{sturmname}{index}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Expands to the right end-point for the ``index``\ -th interval as - computed by some earlier `\\PolSturmIsolateZeros{sturmname}`_ and - possibly refined afterwards. - - The value is pre-formatted using `\\PolDecTostring - `_. - -.. _PolSturmIsolatedZeroMultiplicity: - -``\PolSturmIsolatedZeroMultiplicity{sturmname}{index}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Expands to the multiplicity of the unique root contained in the - ``index``\ -th interval. - - .. attention:: - - A prior execution of `\\PolSturmIsolateZeros*{sturmname}`_ is mandatory. - - See `The degree nine polynomial with 0.99, 0.999, 0.9999 as triple - roots`_ for an example of use. - -.. _PolSturmNbOfIsolatedZeros: - -``\PolSturmNbOfIsolatedZeros{sturmname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Expands to the number of real roots of the polynomial - ``_0``, i.e. the number of distinct real roots of the - polynomial originally used to create the Sturm chain via - `\\PolToSturm{polname}{sturmname}`_. - -.. warning:: - - The next few macros counting roots, with or without multiplicities, - less than or equal to some value, are under evaluation and may be - removed from the package if their utility is judged to be not high - enough. They can be re-coded at user level on the basis of the other - documented package macros anyway. - -``\PolSturmNbOfRootsOf{sturmname}\LessThanOrEqualTo{value}`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Expands to the number of distinct roots (of the polynomial used to - create the Sturm chain) less than or equal to the ``value`` (i.e. a - number of fraction recognizable by the xintfrac_ macros). - - .. attention:: - - `\\PolSturmIsolateZeros{sturmname}`_ must have been executed - beforehand. - - And the argument is a ``sturmname``, not a ``polname`` (this is - why the macro contains Sturm in its name), simply to be reminded - of the above constraint. - -``\PolSturmNbOfRootsOf{sturmname}\LessThanOrEqualToExpr{expression}`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Expands to the number of distinct roots (of the polynomial - used to create the Sturm chain) which are less than or equal to the - given ``expression``. - - .. attention:: - - `\\PolSturmIsolateZeros{sturmname}`_ must have been executed - beforehand. - -``\PolSturmNbWithMultOfRootsOf{sturmname}\LessThanOrEqualTo{value}`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Expands to the number counted with multiplicities of the roots (of - the polynomial used to create the Sturm chain) which are less than - or equal to the given ``value``. - - .. attention:: - - `\\PolSturmIsolateZeros*{sturmname}`_ (or the double starred - variant) must have been executed beforehand. - -``\PolSturmNbWithMultOfRootsOf{sturmname}\LessThanOrEqualToExpr{expression}`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Expands to the total number of roots (counted with multiplicities) - which are less than or equal to the given ``expression``. - - .. attention:: - - `\\PolSturmIsolateZeros*{sturmname}`_ (or the double starred - variant) must have been executed beforehand. - -``\PolSturmNbOfRationalRoots{sturmname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Expands to the number of rational roots (without multiplicities). - - .. attention:: - - `\\PolSturmIsolateZeros**{sturmname}`_ must have been executed - beforehand. - -``\PolSturmNbOfRationalRootsWithMultiplicities{sturmname}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Expands to the number of rational roots (counted with multiplicities). - - .. attention:: - - `\\PolSturmIsolateZeros**{sturmname}`_ must have been executed - beforehand. - -``\PolSturmRationalRoot{sturmname}{k}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Expands to the ``k``\ th rational root (they are ordered and indexed - starting at 1 for the most negative). - - .. attention:: - - `\\PolSturmIsolateZeros**{sturmname}`_ must have been executed - beforehand. - -``\PolSturmRationalRootIndex{sturmname}{k}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Expands to ``index`` of the ``k``\ th rational root as part of the - ordered real roots (without multiplicities). I.e., above macro - `\\PolSturmRationalRoot{sturmname}{k}`_ is equivalent to this - nested call:: - - \PolSturmIsolatedZeroLeft{sturmname}{\PolSturmRationalRootIndex{sturmname}{k}} - - .. attention:: - - `\\PolSturmIsolateZeros**{sturmname}`_ must have been executed - beforehand. - -``\PolSturmRationalRootMultiplicity{sturmname}{k}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Expands to the multiplicity of the ``k``\ th rational root. - - .. attention:: - - `\\PolSturmIsolateZeros**{sturmname}`_ must have been executed - beforehand. - -.. _PolIntervalWidth: - -``\PolIntervalWidth{sturmname}{index}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - The ``10^E`` width of the current ``index``\ -th root localization - interval. Output is in xintfrac_ raw ``1/1[E]`` format (if not zero). - -Expandable macros for use within execution of ``\PolPrintIntervals`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -These macros are for usage within custom user redefinitions of -`\\PolPrintIntervalsKnownRoot`_, `\\PolPrintIntervalsUnknownRoot`_, or -in redefinitions of `\PolPrintIntervalsPrintExactZero`_ (used in the -default for the former) and of `\\PolPrintIntervalsPrintLeftEndPoint`_, -`\\PolPrintIntervalsPrintRightEndPoint`_ (used in the default for the -latter). - -.. attention:: - - Some macros formerly mentioned here got removed at 0.7: - ``\PolPrintIntervalsTheEndPoint``, - ``\PolIfEndPointIsPositive{A}{B}``, - ``\PolIfEndPointIsNegative{A}{B}``, - ``\PolIfEndPointIsZero{A}{B}``. - -``\PolPrintIntervalsTheVar`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Expands to the name (default ``Z``) used for representing the roots, - which was passed as optional argument ``varname`` to - `\\PolPrintIntervals[varname]{sturmname}`_. - -``\PolPrintIntervalsTheIndex`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Expands to the index of the considered interval (indexing starting - at 1 for the leftmost interval). - -``\PolPrintIntervalsTheSturmName`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Expands to the argument which was passed as ``sturmname`` to - `\\PolPrintIntervals[varname]{sturmname}`_. - -``\PolPrintIntervalsTheLeftEndPoint`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - The left end point of the interval, as would be produced by - `\\PolSturmIsolatedZeroLeft `_ if it was - used with arguments the Sturm chain name and interval index returned - by `\\PolPrintIntervalsTheSturmName`_ and - `\\PolPrintIntervalsTheIndex`_. - -``\PolPrintIntervalsTheRightEndPoint`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - The right end point of the interval, as would be produced by - `\\\PolSturmIsolatedZeroRight `_ for - this Sturm chain name and index. - -``\PolPrintIntervalsTheMultiplicity`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - The multiplicity of the unique root within the interval of index - `\\PolPrintIntervalsTheIndex`_. Makes sense only if the starred (or - double-starred) variant of `\\PolSturmIsolateZeros - `_ was used earlier. - -.. _PolDecToString: - -``\PolDecToString{decimal number}`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - This is a utility macro to print decimal numbers. It has been - backported to xintfrac_ (release ``1.3`` of ``2018/03/01``) under - the name ``\xintDecToString``, and the ``polexpr`` macro is simply - now an alias to it. - - For example - ``\PolDecToString{123.456e-8}`` will expand to ``0.00000123456`` - and ``\PolDecToString{123.450e-8}`` to ``0.00000123450`` which - illustrates that trailing zeros are not trimmed. To trim trailing - zeroes, one can use ``\PolDecToString{\xintREZ{#1}}``. - - The precise behaviour of this macro may evolve in future releases of - xint_. - -Booleans (with default setting as indicated) --------------------------------------------- - -``\xintverbosefalse`` -~~~~~~~~~~~~~~~~~~~~~ - - This is actually an xintexpr_ configuration. Setting it to - ``true`` triggers the writing of information to the log when new - polynomials are defined. - - .. caution:: - - The macro meanings as written to the log are to be considered - unstable and undocumented internal structures. - -``\poltypesetallfalse`` -~~~~~~~~~~~~~~~~~~~~~~~ - - If ``true``, `\\PolTypeset{polname}`_ will also typeset the vanishing - coefficients. - - -``\poltoexprallfalse`` -~~~~~~~~~~~~~~~~~~~~~~ - - If ``true``, `\\PolToExpr{polname}`_ and `\\PolToFloatExpr{polname}`_ will - also include the vanishing coefficients in their outputs. - -``\polexprsetup`` ------------------ - - Serves to customize the package. Currently only two keys are - recognized: - - - ``norr``: the postfix that `\\PolSturmIsolateZeros**{sturmname}`_ - should append to ``sturmname`` to declare the primitive polynomial - obtained from original one after removal of all rational roots. - The default value is ``_norr`` (standing for “no rational roots”). - - - ``sqfnorr``: the postfix that `\\PolSturmIsolateZeros**{sturmname}`_ - should append to ``sturmname`` to declare the primitive polynomial - obtained from original one after removal of all rational roots and - suppression of all multiplicities. - The default value is ``_sqf_norr`` (standing for “square-free with - no rational roots”). - - The package executes ``\polexprsetup{norr=_norr, - sqfnorr=_sqf_norr}`` as default. - -Technicalities --------------- - -- The catcode of the semi-colon is reset temporarily by `\\poldef - `_ macro in case some other package (for example the French - babel module) may have made it active. This will fail though if the - whole thing was already part of a macro argument, in such cases one - can use `\\PolDef{f}{P(x)} `_ - rather. The colon in ``:=`` may be active with no consequences. - -- As a consequence of xintfrac_ addition and subtraction always using - least common multiples for the denominators [#]_, user-chosen common - denominators survive additions and multiplications. For example, this:: - - \poldef P(x):= 1/2 + 2/2*x + 3/2*x^3 + 4/2*x^4; - \poldef Q(x):= 1/3 + (2/3)x + (3/3)x^3 + (4/3)x^4; - \poldef PQ(x):= P(x)Q(x); - - gives internally the polynomial:: - - 1/6+4/6*x^1+4/6*x^2+6/6*x^3+20/6*x^4+16/6*x^5+9/6*x^6+24/6*x^7+16/6*x^8 - - where all coefficients have the same denominator 6. Notice though that - ``\PolToExpr{PQ}`` outputs the ``6/6*x^3`` as ``x^3`` because (by - default) it recognizes and filters out coefficients equal to one or - minus one (since release ``0.3``). One can use for example - ``\PolToCSV{PQ}`` to see the internally stored coefficients. - - .. [#] prior to ``0.4.1``, ``polexpr`` used to temporarily patch - during the parsing of polynomials the xintfrac_ macros. This - patch was backported to xint_ at release ``1.3``. - -- `\\PolDiff{polname_1}{polname_2}`_ always applies ``\xintIrr`` to the - resulting coefficients, except that the *power of ten* part ``[N]`` - (for example an input in scientific notation such as ``1.23e5`` gives - ``123/1[3]`` internally in xintfrac) is not taken into account in the - reduction of the fraction. This is tentative and may change. - - Same remark for `\\PolAntiDiff{polname_1}{polname_2}`_. - -- Currently, the package stores all coefficients from index ``0`` to - index equal to the polynomial degree inside a single macro, as a list. - This data structure is obviously very inefficient for polynomials of - high degree and few coefficients (as an example with ``\poldef - f(x):=x^1000 + x^500;`` the subsequent definition ``\poldef g(x):= - f(x)^2;`` will do of the order of 1,000,000 multiplications and - additions involvings only zeroes... which does take time). This - may change in the future. - -- As is to be expected internal structures of the package are barely - documented and unstable. Don't use them. - - -CHANGE LOG ----------- - -- v0.1 (2018/01/11): initial release. Features: - - * The `\\poldef `_ parser itself, - * Differentiation and anti-differentiation, - * Euclidean division and GCDs, - * Various utilities such as `\\PolFromCSV `_, - `\\PolMapCoeffs `_, - `\\PolToCSV `_, `\\PolToExpr `_, ... - - Only one-variable polynomials so far. - -- v0.2 (2018/01/14) - - * Fix: ``"README thinks \numexpr recognizes ^ operator"``. - * Convert README to reStructuredText markup. - * Move main documentation from README to separate ``polexpr.txt`` file. - * Provide ``polexpr.html`` as obtained via DocUtils_ ``rst2html.py``. - * Convert README to (CTAN compatible) Markdown markup. - - Due to lack of available time the test suite might not be extensive - enough. Bug reports are very welcome! - -- v0.3 (2018/01/17) - - * bug fixes: - - - the ``0.1`` `\\PolEval `_ accepted expressions for its second - argument, but this was removed by mistake at ``0.2``. Restored. - - **Attention**: at ``0.4`` this has been reverted again, and - `\\PolEval{P}\\AtExpr{foo} `_ syntax is needed for - using expressions in the second argument. - * incompatible or breaking changes: - - - `\\PolToExpr `_ now by default uses *descending* - powers (it also treats differently coefficients equal to 1 or -1.) - Use `\\PolToExpr* `_ for *ascending* powers. - - `\\PolEval `_ reduced the output to smallest terms, - but as this is costly with big fractions and not needed if e.g. - wrapped in an ``\xintRound`` or ``\xintFloat``, this step has been - removed; the former meaning is available as `\\PolEvalReduced - `_. - * new (or newly documented) macros: - - - `\\PolTypesetCmd `_ - - `\\PolTypesetCmdPrefix `_ - - `\\PolTypesetMonomialCmd `_ - - `\\PolEvalReducedAt `_ - - `\\PolToFloatExpr `_ - - `\\PolToExprOneTerm `_ - - `\\PolToFloatExprOneTerm `_ - - `\\PolToExprCmd `_ - - `\\PolToFloatExprCmd `_ - - `\\PolToExprTermPrefix `_ - - `\\PolToExprVar `_ - - `\\PolToExprTimes `_ - * improvements: - - - documentation has a table of contents, internal hyperlinks, - standardized signature notations and added explanations. - - one can do ``\PolLet{g}={f}`` or ``\PolLet{g}{f}``. - - ``\PolToExpr{f}`` is highly customizable. - - `\\poldef `_ and other defining macros prepare the polynomial - functions for usage within ``\xintthefloatexpr`` (or - ``\xintdeffloatvar``). Coefficients are pre-rounded to the - floating point precision. Indispensible for numerical algorithms, - as exact fractions, even reduced, quickly become very big. See the - documentation about how to use the exact polynomials also in - floating point context. - - **Attention**: this has been reverted at ``0.4``. The macro - `\\PolGenFloatVariant `_ must be used for - generation floating point polynomial functions. - -- v0.3.1 (2018/01/18) - - Fixes two typos in example code included in the documentation. - -- v0.4 (2018/02/16) - - * bug fixes: - - - when Euclidean division gave a zero remainder, the internal - representation of this zero polynomial could be faulty; this - could cause mysterious bugs in conjunction with other package - macros such as `\\PolMapCoeffs `_. - - `\\PolGCD `_ was buggy in case of first polynomial being - of lesser degree than the second one. - * breaking changes: - - - formerly `\\PolEval{P}\\At{foo} `_ allowed ``foo`` to - be an expression, which was transparently handled via - ``\xinttheexpr``. Now, ``foo`` must be a fraction (or a macro - expanding to such) in the format acceptable by ``xintfrac.sty`` - macros. Use `\\PolEval{P}\\AtExpr{foo} `_ for more - general arguments using expression syntax. E.g., if ``foo`` is the - name of a variable known to ``\xintexpr``. - - The same holds for `\\PolEvalReduced `_ - and `\\PolFloatEval `_. - - the ``3.0`` automatic generation of floating point variants has - been reverted. Not only do *not* the package macros automatically - generate floating point variants of newly created polynomials, - they actually make pre-existing such variant undefined. - - See `\\PolGenFloatVariant `_. - * new non-expandable macros: - - - `\\PolGenFloatVariant `_ - - `\\PolGlobalLet `_ - - `\\PolTypesetOne `_ - - `\\PolQuo `_ - - `\\PolRem `_ - - `\\PolToSturm `_ - - `\\PolToSturm\* `_ - - `\\PolSetToSturmChainSignChangesAt `_ - - `\\PolSetToNbOfZerosWithin `_ - - `\\PolSturmIsolateZeros `_ - - `\\PolRefineInterval* `_ - - `\\PolRefineInterval[N] `_ - - `\\PolEnsureIntervalLength `_ - - `\\PolEnsureIntervalLengths `_ - - `\\PolPrintIntervals `_ - - `\\PolPrintIntervalsPrintExactZero `_ - - `\\PolPrintIntervalsPrintLeftEndPoint `_ - - `\\PolPrintIntervalsPrintRightEndPoint `_ - - `\\PolReduceCoeffs* `_ - - `\\PolMakeMonic `_ - * new expandable macros: - - - `\\PolToExprOneTermStyleA `_ - - `\\PolIfCoeffIsPlusOrMinusOne `_ - - `\\PolLeadingCoeff `_ - - `\\PolSturmChainLength `_ - - `\\PolSturmNbOfIsolatedZeros `_ - - `\\PolSturmIfZeroExactlyKnown `_ - - `\\PolSturmIsolatedZeroLeft `_ - - `\\PolSturmIsolatedZeroRight `_ - - ``\PolPrintIntervalsTheEndPoint`` (removed at 0.7) - - `\\PolPrintIntervalsTheIndex`_ - - ``\PolIfEndPointIsPositive`` (removed at 0.7) - - ``\PolIfEndPointIsNegative`` (removed at 0.7) - - ``\PolIfEndPointIsZero`` (removed at 0.7) - - `\\PolIntervalWidth `_ - - `\\PolDecToString `_ - * improvements: - - The main new feature is implementation of the `Sturm algorithm`_ - for localization of the real roots of polynomials. - -- v0.4.1 (2018/03/01) - - Synced with xint 1.3. - -- v0.4.2 (2018/03/03) - - Documentation fix. - -- v0.5 (2018/04/08) - - * bug fixes: - - - `\\PolGet{polname}\\fromarray\\macro`_ crashed when ``\macro`` was - an xinttools_ array macro with no items. It now produces the zero - polynomial. - * breaking changes: - - - `\\PolToSturm`_ creates primitive integer coefficients polynomials. - This speeds up localization of roots via - `\\PolSturmIsolateZeros`_. In case of user protests the author - will make available again the code producing the bona fide Sturm - polynomials as used formerly. - - polynomials created from `\\PolFromCSV`_ or `\\PolGet `_ - get their coefficients normalized via xintfrac_\ 's ``\xintRaw``. - * experimental change: - - - optional argument to `\\PolSturmIsolateZeros`_ (see `The - degree 41 polynomial with -2, -1.9, -1.8, ..., 0, 0.1, ..., 1.9, 2 - as roots`_ for usage). It will presumably be replaced in future by - an interval specification. - * new non-expandable macro: - - - `\\PolMakePrimitive`_ - * new expandable macro: - - - `\\PolIContent`_ - -- v0.5.1 (2018/04/22) - - * new feature: - - - the character ``'`` can be used in polynomial names. - -- v0.6 (2018/11/20) - - * bugfix: - - - the starred variant `\\PolToSturm*{polname}{sturmname}`_ was - broken. On the occasion of the fix, its meaning has been modified, - see its documentation. - - - using `\\PolToSturm `_ with a constant polynomial - caused a division by zero error. - - * new macro: - - - `\\PolSturmIsolateZeros* `_ - acts like the `non-starred variant - `_ then computes all the multiplicities. - - * new expandable macros: - - - `\\PolSturmIsolatedZeroMultiplicity{sturmname}{index}`_ - - `\\PolSturmNbOfRootsOf{sturmname}\\LessThanOrEqualTo{value}`_ - - `\\PolSturmNbOfRootsOf{sturmname}\\LessThanOrEqualToExpr{expression}`_ - - `\\PolSturmNbWithMultOfRootsOf{sturmname}\\LessThanOrEqualTo{value}`_ - - `\\PolSturmNbWithMultOfRootsOf{sturmname}\\LessThanOrEqualToExpr{expression}`_ - -- v0.7 (2018/12/08), v0.7.1 (bugfix), v0.7.2 (2nd bugfix) (2018/12/09) - - * breaking changes: - - - although `\\PolPrintIntervals[varname]{sturmname}`_ default output - remains the same, some auxiliary macros for user-customization - have been removed: ``\PolPrintIntervalsTheEndPoint``, - ``\PolIfEndPointIsPositive{A}{B}``, - ``\PolIfEndPointIsNegative{A}{B}``, and - ``\PolIfEndPointIsZero{A}{B}``. - - * bugfix: - - - it could happen that, contrarily to documentation, an interval - computed by `\\PolSturmIsolateZeros{sturmname}`_ had zero as an - endpoint, - - `\\PolEnsureIntervalLength{sturmname}{index}{E}`_ could under - certain circumstances erroneously replace a non-zero root by - zero, - - `\\PolEnsureIntervalLengths{sturmname}{E}`_ crashed when used with - a polynomial with no real roots, hence for which no isolation intervals - existed (thanks to Thomas Söll for report). - - * new macros: - - - `\\PolSturmIsolateZeros**{sturmname}`_ - - `\\PolSturmIsolateZerosGetMultiplicitiesAndRationalRoots{sturmname}`_ - - `\\PolSturmIsolateZerosAndFindRationalRoots{sturmname}`_ - - `\\polexprsetup`_ - - `\\PolPrintIntervals* `_ - - `\\PolPrintIntervalsNoRealRoots`_ - - `\\PolPrintIntervalsBeginEnv`_ - - `\\PolPrintIntervalsEndEnv`_ - - `\\PolPrintIntervalsKnownRoot`_ - - `\\PolPrintIntervalsUnknownRoot`_ - - `\\PolPrintIntervalsPrintMultiplicity`_ - - * new expandable macros: - - - `\\PolSturmNbOfRationalRoots{sturmname}`_ - - `\\PolSturmNbOfRationalRootsWithMultiplicities{sturmname}`_ - - `\\PolSturmRationalRoot{sturmname}{k}`_ - - `\\PolSturmRationalRootIndex{sturmname}{k}`_ - - `\\PolSturmRationalRootMultiplicity{sturmname}{k}`_ - - `\\PolPrintIntervalsTheVar`_ - - `\\PolPrintIntervalsTheSturmName`_ - - `\\PolPrintIntervalsTheMultiplicity`_ - -- v0.7.3 (2019/02/04) - - * bugfix: - - - Debugging information not destined to user showed in log if root - finding was done under ``\xintverbosetrue`` regime. - - `\\PolPrintIntervalsTheVar`_ remained defined after - `\\PolPrintIntervals`_ but was left undefined after - `\\PolPrintIntervals*`_ (reported by Jürgen Gilg). Now remains - defined in both cases, and `\\PolPrintIntervalsTheSturmName`_ - also. - - Polynomial names ending in digits caused errors (reported by Thomas - Söll). - -- v0.7.4 (2019/02/12) - - * bugfix: - - - 20000000000 is too big for ``\numexpr``, shouldn't I know that? - Thanks to Jürgen Gilg for report. - -- v0.7.5 (2020/01/31) - - Synced with xint 1.4. Requires it. - - -Acknowledgments ---------------- - -Thanks to Jürgen Gilg whose question about xint_ usage for -differentiating polynomials was the initial trigger leading to this -package, and to Jürgen Gilg and Thomas Söll for testing it on some -concrete problems. - -Renewed thanks to them on occasion of the ``0.6`` and ``0.7`` releases for their -continued interest. - -See README.md for the License. - -.. _xinttools: -.. _xintfrac: -.. _xintexpr: -.. _xint: http://www.ctan.org/pkg/xint - -.. _Wilkinson polynomial: https://en.wikipedia.org/wiki/Wilkinson%27s_polynomial - -.. _Sturm algorithm: -.. _Sturm Theorem: https://en.wikipedia.org/wiki/Sturm%27s_theorem - -.. _DocUtils: http://docutils.sourceforge.net/docs/index.html -- cgit v1.2.3