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/generic/polexpr/README.md | 123 + macros/generic/polexpr/polexpr.html | 4086 ++++++++++++++++++++ macros/generic/polexpr/polexpr.sty | 1057 +++++ macros/generic/polexpr/polexprcore.tex | 1366 +++++++ macros/generic/polexpr/polexprexpr.tex | 179 + macros/generic/polexpr/polexprsturm.tex | 1775 +++++++++ macros/generic/xint/CHANGES.html | 177 +- macros/generic/xint/README.md | 8 +- macros/generic/xint/sourcexint.pdf | Bin 1026778 -> 1031328 bytes macros/generic/xint/xint.dtx | 852 ++-- macros/generic/xint/xint.pdf | Bin 1006708 -> 1012827 bytes macros/latex/contrib/pkuthss/doc/example.pdf | Bin 93675 -> 95861 bytes .../latex/contrib/pkuthss/doc/example/chap/abs.tex | 6 +- .../contrib/pkuthss/doc/example/chap/origin.tex | 10 +- .../latex/contrib/pkuthss/doc/example/thesis.tex | 37 +- macros/latex/contrib/pkuthss/doc/pkuthss.pdf | Bin 315791 -> 325590 bytes .../latex/contrib/pkuthss/doc/readme/ChangeLog.txt | 16 + .../pkuthss/doc/readme/chap/pkuthss-abs.tex | 6 +- .../pkuthss/doc/readme/chap/pkuthss-ack.tex | 5 +- .../pkuthss/doc/readme/chap/pkuthss-chap2.tex | 43 +- .../pkuthss/doc/readme/chap/pkuthss-chap3.tex | 21 +- .../pkuthss/doc/readme/chap/pkuthss-copy.tex | 4 +- .../pkuthss/doc/readme/pkuthss-english.patch | 68 +- .../latex/contrib/pkuthss/doc/readme/pkuthss.bib | 37 +- .../latex/contrib/pkuthss/doc/readme/pkuthss.tex | 30 +- macros/latex/contrib/pkuthss/tex/pkulogo.pdf | Bin 3530 -> 3530 bytes macros/latex/contrib/pkuthss/tex/pkuthss-gbk.def | 14 +- macros/latex/contrib/pkuthss/tex/pkuthss-utf8.def | 14 +- macros/latex/contrib/pkuthss/tex/pkuthss.cls | 97 +- macros/latex/contrib/pkuthss/tex/pkuword.pdf | Bin 6280 -> 6280 bytes 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 ------------- macros/latex/contrib/srdp-mathematik/README.txt | 56 +- .../contrib/srdp-mathematik/srdp-mathematik.pdf | Bin 161583 -> 176727 bytes .../contrib/srdp-mathematik/srdp-mathematik.sty | 54 +- .../contrib/srdp-mathematik/srdp-mathematik.tex | 238 +- macros/xetex/latex/xesoul/README | 5 + macros/xetex/latex/xesoul/xesoul.dtx | 103 + macros/xetex/latex/xesoul/xesoul.ins | 53 + macros/xetex/latex/xesoul/xesoul.pdf | Bin 0 -> 23237 bytes 42 files changed, 9832 insertions(+), 9483 deletions(-) create mode 100644 macros/generic/polexpr/README.md create mode 100644 macros/generic/polexpr/polexpr.html create mode 100644 macros/generic/polexpr/polexpr.sty create mode 100644 macros/generic/polexpr/polexprcore.tex create mode 100644 macros/generic/polexpr/polexprexpr.tex create mode 100644 macros/generic/polexpr/polexprsturm.tex 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 create mode 100644 macros/xetex/latex/xesoul/README create mode 100644 macros/xetex/latex/xesoul/xesoul.dtx create mode 100644 macros/xetex/latex/xesoul/xesoul.ins create mode 100644 macros/xetex/latex/xesoul/xesoul.pdf (limited to 'macros') diff --git a/macros/generic/polexpr/README.md b/macros/generic/polexpr/README.md new file mode 100644 index 0000000000..b32b8f056d --- /dev/null +++ b/macros/generic/polexpr/README.md @@ -0,0 +1,123 @@ +Package polexpr README +====================== + +Usage +----- + +The package can be used with TeX based formats incorporating the +e-TeX primitives. The `\expanded` primitive available generally +since TeXLive 2019 is required. + + \input polexpr.sty + +with Plain or other non-LaTeX macro formats, or: + + \usepackage{polexpr} + +with the LaTeX macro format. + +The package currently requires xintexpr.sty `1.4d` or later. + +Abstract +-------- + +The package provides a parser `\poldef` of algebraic polynomial +expressions. As it is based on +[xintexpr](http://www.ctan.org/pkg/xint) +the coefficients are allowed to be arbitrary rational numbers. + +Once defined, a polynomial is usable by its name either as a numerical +function in `\xintexpr/\xinteval`, or for additional polynomial +definitions, or as argument to the package macros. The localization of +real roots to arbitrary precision as well as the determination of all +rational roots is implemented via such macros. + +Since release `0.8`, polexpr extends the +[xintexpr](http://www.ctan.org/pkg/xint) syntax to recognize +polynomials as a new variable type (and not only as functions). +Functionality which previously was implemented via macros such as the +computation of a greatest common divisor is now available directly in +`\xintexpr`, `\xinteval` or `\poldef` via infix or functional syntax. + +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 xintexpr 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 xintexpr 1.4. Requires it. +- 0.8 (2021/03/29) + Complete refactoring of the package core for better integration with + and enhancement of xintexpr 1.4. + +Files of 0.8 release: + +- polexpr.sty, polexprcore.tex, polexprexpr.tex, polexprsturm.tex, +- README.md, +- polexpr.html (documentation) + +Acknowledgments +--------------- + +Thanks to Jürgen Gilg whose question about +[xintexpr](http://www.ctan.org/pkg/xintexpr) 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. + +License +------- + +Copyright (C) 2018-2021 Jean-François Burnol + +See documentation of package [xintexpr](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 files polexpr.sty, polexprcore.tex, +polexprexpr.tex, polexprsturm.tex, this README.md and polexpr.html. diff --git a/macros/generic/polexpr/polexpr.html b/macros/generic/polexpr/polexpr.html new file mode 100644 index 0000000000..81cd2b4b7c --- /dev/null +++ b/macros/generic/polexpr/polexpr.html @@ -0,0 +1,4086 @@ + + + + + +Package polexpr documentation + + + + +
+

Package polexpr documentation

+

0.8 (2021/03/29)

+ +
+

Contents

+ +
+
+

Usage

+

The package can be used with TeX based formats incorporating the e-TeX +primitives. The \expanded primitive available generally since +TeXLive 2019 is required.

+
\input polexpr.sty
+

with Plain or other non-LaTeX macro formats, or:

+
\usepackage{polexpr}
+

with the LaTeX macro format.

+

The package requires xintexpr 1.4d or later.

+
+

Note

+

Until 0.8 the package only had a LaTeX interface. As a result, +parts of this documentation may still give examples using LaTeX syntax such +as \newcommand. Please convert to the syntax appropriate to the +TeX macro format used if needed.

+
+
+
+

Abstract

+

The package provides a parser \poldef of algebraic polynomial +expressions. As it is based on xintexpr +the coefficients are allowed to be arbitrary rational numbers.

+

Once defined, a polynomial is usable by its name either as a numerical +function in \xintexpr/\xinteval, or for additional polynomial +definitions, or as argument to the package macros. The localization of +real roots to arbitrary precision as well as the determination of all +rational roots is implemented via such macros.

+

Since release 0.8, polexpr extends the xintexpr +syntax to recognize +polynomials as a new variable type (and not only as functions). +Functionality which previously was implemented via macros such as the +computation of a greatest common divisor is now available directly in +\xintexpr, \xinteval or \poldef via infix or functional +syntax.

+
+
+

Prerequisites

+
    +
  • The user must have some understanding of TeX as a macro-expansion +based programming interface, and in particular of how \edef +differs from \def: functionalities of the package as described in +the Expandable macros section are suitable for usage in \edef, +\write or \xinteval context. At 0.8 some of these +macros have an even more convenient functional interface inside +\xinteval, as is described in a dedicated section.

    +

    Despite its name \poldef is more to be seen as an \edef +although it does not define a TeX macro (at user level); and of course +\edef would do usually nothing on the typical input parsed by +\poldef which generally has no backslash in it: but if this input +does contain macros, they will then be expanded fully and are supposed to +produce recognizable syntax elements in this expansion only context.

    +

    Note that the def in \poldef reminds us that the macro does +some assignments hence is not usable in expandable only context. Its +whole point is rather to define entities which, them, can then be used +in the expandable only \xinteval (or \poldef) context.

    +
  • +
  • The user must have some familiarity with xintexpr and in +particular must know what \xintexpr, \xinttheexpr, +\xinteval and \xintfloatexpr, \xintthefloatexpr, +\xintfloateval mean and what are the good practices with them.

  • +
  • The user will become quickly aware that exact computations with +fractions easily lead to very big ones in very few steps; see +\PolReduceCoeffs{polname} in this context.

  • +
  • Finally, it is mandatory to read the entire documentation before +starting to use the package.

  • +
+
+
+

Quick syntax overview

+

The syntax is:

+
\poldef polname(x):= expression in variable x;
+
    +
  • In place of x an arbitrary dummy variable is authorized, +i.e. per default one 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: do not use the +underscore _ as first character of a polynomial name (even +if of catcode letter). No warning is emitted but dire consequences +will result.

    +
    +

    Hint

    +

    The @ is usable too, independently of whether it is of catcode +letter or other. This has always been the case, but was not +documented by polexpr prior to 0.8, as the author has never +found the time to provide some official guidelines on how to name +temporary variables and the @ is used already as such internally +to package; time has still not yet been found for 0.8 to review +the situation but it seems reasonable to recommend at any rate to +restrict usage of @ to scratch variables of defined macros and +to avoid using it to name document variable.

    +
    +
  • +
  • The colon before the equality sign is optional and its catcode does +not matter.

  • +
  • The semi-colon at the end of the expression is mandatory. Its catcode +does not matter if \poldef is not used inside the argument of +another macro.

  • +
+

There is an alternative syntax

+
\PolDef[optional letter]{polname}{expression in the letter}
+

Its optional first argument defaults to x.

+
+
\poldef f(x):= 1 - x + quo(x^5,1 - x + x^2);
+

defines polynomial f. The indeterminate x must be +only submitted to algebraic operations.

+

The quo() function (new at 0.8) computes the euclidean +division quotient.

+
+
+
+

Important

+

For backwards compatibility one can currently also use:

+
\poldef f(x):= 1 - x + x^5/(1 - x + x^2);
+

Due to precedence rules the first operand is x^5, not of course +1-x+x^5.

+

Note that (1-x^2)/(1-x) produces 1+x +but (1/(1-x))*(1-x^2) produces zero! One also has to be aware +of some precedence rules, for example:

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

does compute a degree 2 polynomial because the tacit multiplication +ties more than the division operator.

+

In short, it is safer to use the quo() function which avoids +surprises.

+
+
+

Attention!

+

Tacit multiplication means that +1/2 x^2 skips the space and is treated like 1/(2*x^2). +But then 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 +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: +xintexpr's tacit multiplication always "ties more", hence this +gets interpreted as 1/(2*(x-1)*(x-2)) which gives zero by +polynomial division. Thus, use in such cases one of +(1/2)(x-1)(x-2), 1/2*(x-1)(x-2) or (x-1)(x-2)/2.

+
+
+

Warning

+

The package does not currently know rational functions, but in order +to leave open this as a future possibility, the usage of / to stand +for the +euclidean quotient is deprecated.

+

Please start using rather the quo() function. It is possible +that in a future major relase A/B with B a non-scalar will +raise an error. Or, who knows, rational functions will be +implemented sometime during the next decades, and then A/B will +naturally be the rational function.

+
+
+

Important

+

\poldef P(x):=...; defines P both as a function, +to be used as:

+
P(..numeric or even polynomial expression..)
+

and as a variable which can used inside polynomial expressions or +as argument to some polynomial specific functions such as deg() +or polgcd() 1.

+
+
1
+

Functional syntax accepts expressions as arguments; but the +TeX macros described in the documentation, even the +expandable ones, work only (there are a few exceptions to the +general rule) with arguments being names of declared +polynomials.

+
+
+

One needs to have a clear understanding of the difference between +P used a function and P used as a variable: if P and +Q are both declared polynomials then:

+
(P+Q)(3)%  <--- attention!
+

is currently evaluated as (P+Q)*3, because P+Q is not known +as a function, but only as a variable of polynomial type. +Even worse:

+
(P)(3)%  <--- attention!
+

will compute P*3, because one can not in current xintexpr syntax +enclose a function name in parentheses: consequently it is the variable +which is used here. There is a meager possibility that in future +some internal changes to xintexpr would let (P)(3) actually +compute P(3) and (P+Q)(3) compute P(3) + Q(3), but note +that (P)(P) will then do P(P) and not P*P, +the latter, current interpretation, looking more +intuitive. Anyway, do not rely too extensively on tacit * and use +explicit (P+Q)*(1+2) if this is what is intended.

+

As an alternative to explicit P(3)+Q(3) there is evalp(P+Q,3).

+
+
+
\PolLet{g}={f}
+

saves a copy of f under name g. Also usable without =.

+

Has exactly the same effect as \poldef g(x):=f; or \poldef g(w):=f(w);.

+
+
\poldef f(z):= f^2;
+

redefines f in terms of itself. Prior to 0.8 one needed +the right hand side to be f(z)^2. Also, now sqr(f) is +possible (also sqr(f(x)) but not sqr(f)(x)).

+
+
+
+

Important

+

Note that f^2(z) or sqr(f)(z) will give a logical but +perhaps unexpected result: first f^2 is computed, then the +opening parenthesis is seen which inserts a tacit multiplication +*, so in the end it is as if the input had been f^2 * z. +Although f is both a variable and a function, f^2 is +computed as a polynomial variable and ceases being a function.

+
+
+
\poldef f(T):= f(f);
+

again modifies f. Here it is used both as variable and as +a function. Prior to 0.8 it needed to be f(f(T)).

+
+
\poldef k(z):= f-g(g^2)^2;
+

if everybody followed, this should now define the zero polynomial... +And f-sqr(g(sqr(g))) computes the same thing.

+

We can check this in a typeset document like this:

+
\poldef f(x):= 1 - x + quo(x^5,1 - x + x^2);%
+\PolLet{g}={f}%
+\poldef f(z):= f^2;%
+\poldef f(T):= f(f);%
+\poldef k(w):= f-sqr(g(sqr(g)));%
+$$f(x) = \vcenter{\hsize10cm \PolTypeset{f}} $$
+$$g(z) = \PolTypeset{g} $$
+$$k(z) = \PolTypeset{k} $$
+\immediate\write128{f(x)=\PolToExpr{f}}% ah, here we see it also
+
+
\poldef f'(x):= diff1(f);
+

(new at 0.8)

+
+
\PolDiff{f}{f'}
+

Both set f' (or any other chosen name) to the derivative +of f.

+
+
+
+

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 previously defined: something such as +f'(3)^2 will not work without a prior definition of f'.

+

But one can now use diff1(f) for on-the-spot construction with no +permanent declaration, so here evalp(diff1(f),3)^2. And +diff1(f)^2 is same as f'^2, assuming here f' was declared +to be the derived polynomial.

+

Notice that the name diff1() is experimental and may change. Use +\PolDiff{f}{f'} as the stable interface.

+
+
+
\PolTypeset{P}
+

Typesets (switching to math mode if in text mode):

+
\poldef f(x):=(3+x)^5;%
+\PolDiff{f}{f'}\PolDiff{f'}{f''}\PolDiff{f''}{f'''}%
+$$f(z)   = \PolTypeset[z]{f}    $$
+$$f'(z)  = \PolTypeset[z]{f'}   $$
+$$f''(z) = \PolTypeset[z]{f''}  $$
+$$f'''(z)= \PolTypeset[z]{f'''} $$
+

See the documentation for the configurability +via macros.

+

Since 0.8 \PolTypeset accepts directly an +expression, it does not have to be a pre-declared polynomial name:

+
\PolTypeset{mul(x-i,i=1..5)}
+
+
\PolToExpr{P}
+

Expandably (contrarily to \PolTypeset) +produces c_n*x^n + ... + c_0 starting from the leading +coefficient. The + signs are omitted if followed by negative +coefficients.

+

This is useful for console or file output. This syntax is Maple and +PSTricks \psplot[algebraic] compatible; and also it is +compatible with \poldef input syntax, of course. See +\PolToExprCaret for configuration of the ^, for example to +use rather ** for Python syntax compliance.

+

Changed at 0.8: the ^ in output is by default of catcode 12 +so in a draft document one can use \PolToExpr{P} inside the +typesetting flow (without requiring math mode, where the * would +be funny and ^12 would only put the 1 as exponent anyhow; +but arguably in text mode the + and - are not satisfactory +for math, except sometimes in monospace typeface, and anyhow TeX is +unable to break the expression across lines, barring special help).

+

See \PolToExpr{<pol. expr.>} and related macros for customization.

+

Extended at 0.8 to accept as argument not only the name of a +polynomial variable but more generally any polynomial expression.

+
+
+
+
+

The polexpr 0.8 extensions to the \xintexpr syntax

+

All the syntax elements described in this section can be used in the +\xintexpr/\xinteval context (where polynomials can be obtained from +the pol([]) constructor, once polexpr is loaded): their usage is +not limited to only \poldef context.

+
+

Note

+

If a variable myPol defined via \xintdefvar turns out +to be a polynomial, the difference with those declared via \poldef +will be:

+
    +
  1. myPol is not usable as function, but only as a variable. +Attention that f(x) if f is only a variable (even a +polynomial one) will actually compute f * x.

  2. +
  3. myPol is not known to the polexpr package, hence for example the +macros to achieve localization of its roots are unavailable.

    +

    In a parallel universe I perhaps have implemented this expandably +which means it could then be accessible with syntax such as +rightmostroot(pol([42,1,34,2,-8,1])) but...

    +
  4. +
+
+
+

Warning about unstability of the new syntax

+
+

Warning

+

Consider the entirety of this section as UNSTABLE and +EXPERIMENTAL (except perhaps regarding +, - and *).

+

And this applies even to items not explicitly flagged with one of +unstable, Unstable, or UNSTABLE which only reflect that +documentation was written over a period of time exceeding one minute, +enough for the author mood changes to kick in.

+

It is hard to find good names at the start of a life-long extension +program of functionalities, and perhaps in future it will be +preferred to rename everything or give to some functions other +meanings. Such quasi-complete renamings happened already a few times +during the week devoted to development.

+
+
+
+

Infix operators +, -, *, /, **, ^

+
+

As has been explained in the Quick syntax overview these infix +operators have been made polynomial aware, not only in the +\poldef context, but generally in any \xintexpr/\xinteval +context, inclusive of \xintdeffunc.

+

Conversely functions declared via \xintdeffunc and making use of +these operators will automatically be able to accept polynomials +declared from \poldef as variables.

+

Usage of / for euclidean division of polynomials is deprecated. +Only in case of a scalar denominator is it to be considered stable. +Please use rather quo().

+
+
+

Warning

+

The pow(x,a) function of xintexpr for x^a with fractional +a will not (with current xintexpr 1.4d) accept a polynomial +as first variable even if the second argument is an integer.

+

It is possible (via \poormanloghack) to instruct \xintexpr to +let x**a or x^a be as pow(x,a). If this is done ** +(resp. ^) will become unusable with polynomials (i.e. will create +a low-level TeX error).

+

And vice versa if polexpr gets loaded after the \poormanloghack +was used, ** and ^ in \xintexpr/\xinteval will again only +accept integer powers.

+

Thus employ \poormanloghack for at most one of ** or ^ +in order to keep one of them available for polynomials and integer +powers.

+
+
+
+

Experimental infix operators //, /:

+
+

Here is the tentative behaviour of A//B according to types:

+
    +
  • A non scalar and B non scalar: euclidean quotient,

  • +
  • A scalar and B scalar: floored division,

  • +
  • A scalar and B non scalar: produces zero,

  • +
  • A non scalar and B scalar: coefficient per +coefficient floored division.

  • +
+

This is an experimental overloading of the // and /: +from \xintexpr.

+

The behaviour in the last case, but not only, is to be considerd +unstable. The alternative would be for A//B with B +scalar to act as quo(A,B). But, we have currently chosen to let +//B for a scalar B act coefficient-wise on the numerator. +Beware that it thus means it can be employed with the idea of doing +euclidean division only by checking that B is non-scalar.

+

The /: operator provides the associated remainder so always +A is reconstructed from (A//B)*B + A/:B.

+

If : is active character use /\string: (it is safer to use +/\string : if it is not known if : has catcode other, letter, +or is active, but note that /: is fine and needs no precaution if +: has catcode letter, it is only an active : which is +problematic, like for all other characters possibly used in an +expression).

+
+

UNSTABLE

+

As explained above, there are (among other things) hesitations +about behaviour with pol2 a scalar.

+
+
+
+
+

Comparison operators <, >, <=, >=, ==, !=

+
+

NOT YET IMPLEMENTED

+

As the internal representation by xintfrac and xintexpr of +fractions does not currently require them to be in reduced terms, +such operations would be a bit costly as they could not benefit from +the \pdfstrcmp engine primitive. In fact xintexpr does not use +it yet anywhere, even for normalized pure integers, although it could +speed up signifcantly certain aspects of core arithmetic.

+

Equality of polynomials can currently be tested by computing the +difference, which is a bit costly. And of course the deg() +function allows comparing degrees. In this context note the +following syntax:

+
(deg(Q)) ?? { zero } { non-zero scalar } { non-scalar }
+

for branching.

+
+
+
+

pol(<nutple expression>)

+
+

This converts a nutple [c0,c1,...,cN] into the polynomial +variable having these coefficients. Attention that the square +brackets are mandatory, except of course if the argument is +actually an expression producing such a "nutple".

+
+

Currently, this process will not normalize the coefficients (such +as reducing to lowest terms), it only trims out the leading zero +coefficients.

+
+

Inside \xintexpr, this is the only (allowed) way to create ex +nihilo a polynomial variable; inside \poldef it is an alternative +input syntax which is more efficient than typing c0 + c1 * x + c2 * x^2 + ....

+
+
+

Important

+

Whenever an expression with polynomials collapses to a constant, it +becomes a scalar. There is currently no distinction during the +parsing of expressions by \poldef +or \xintexpr between constant polynomial variables and scalar +variables.

+

Naturally, \poldef can be used to declare a constant polynomial +P, then P can also be used as function having a value +independent of argument, but as a variable, it is non-distinguishable +from a scalar (of course functions such as deg() tacitly +consider scalars to be constant polynomials).

+

Notice that we tend to use the vocable "variable" to refer to +arbitrary expressions used as function arguments, without implying +that we are actually referring to pre-declared variables in the sense +of \xintdefvar.

+
+
+
+

\xinteval{<pol. expr.>}

+
+

This is documented here for lack of a better place: it evaluates the +polynomial expression then outputs the "string" pol([c0, c1, ..., cN]) +if the degree N is at least one (and the usual scalar output else).

+

The "pol" word uses letter catcodes, which is actually mandatory for +this output to be usable as input, but it does not make sense to use +this inside \poldef or \xintexpr at it means basically +executing pol(coeffs(..expression..)) which is but a convoluted +way to obtain the same result as (..expression..) (the +parentheses delimiting the polynomial expression).

+

For example, \xinteval{(1+pol([0,1]))^10} expands (in two steps) +to:

+
pol([1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1])
+

You do need loading polexpr for this, else of course pol([]) +remains unknown to \xinteval{} as well as the polynomial algebra ! +This example can also be done as +\xinteval{subs((1+x)^10,x=pol([0,1]))}.

+

I hesitated using as output the polynomial notation as produced by +\PolToExpr{}, but finally opted for this.

+
+
+
+

evalp(<pol. expr.>, <pol. expr>)

+
+

Evaluates the first argument as a polynomial function of the +second. Usually the second argument will be scalar, but this is not +required:

+
\poldef K(x):= evalp(-3x^3-5x+1,-27x^4+5x-2);
+

If the first argument is an already declared polynomial P, use +rather the functional form P() (which can accept a numerical as +well as polynomial argument) as it is more efficient.

+

One can also use subs() syntax 2 (see xintexpr documentation):

+
\poldef K(x):= subs(-3y^3-5y+1, y = -27x^4+5x-2);
+

but the evalp() will use a Horner evaluation scheme which is +usually more efficient.

+
+
2
+

by the way Maple uses the opposite, hence wrong, order +subs(x=..., P) but was written before computer science +reached the xintexpr heights. However it makes validating +Maple results by polexpr sometimes cumbersome, but perhaps +they will update it at some point.

+
+
+
+

name unstable

+

poleval? evalpol? peval? evalp? value? +eval? evalat? eval1at2? evalat2nd?

+

Life is so complicated when one asks questions. Not everybody does, +though, as is amply demonstrated these days.

+

syntax unstable

+

I am hesitating about permuting the order of the arguments.

+
+
+
+
+

deg(<pol. expr.>)

+
+

Computes the degree.

+
+
+

Important

+

As \xintexpr does not yet support infinities, the degree of +the zero polynomial is -1. Beware that this breaks additivity +of degrees, but deg(P)<0 correctly detects the zero polynomial, +and deg(P)<=0 detects scalars.

+
+
+
+

coeffs(<pol. expr.>)

+
+

Produces the nutple [c0,c1,...,cN] of coefficients. The highest +degree coefficient is always non zero (except for the zero +polynomial...).

+
+

name unstable

+

I am considering in particular using polcoeffs() to avoid +having to overload coeffs() in future when matrix type +will be added to xintexpr.

+
+
+
+
+

coeff(<pol. expr.>, <num. expr.>)

+
+

As expected. Produces zero if n is negative or higher than the +degree.

+
+

name and syntax unstable

+

I am hesitating with coeff(n,pol) syntax and also perhaps +using polcoeff() in order to avoid having to overload +coeff() when matrix type will be added to xintexpr.

+
+
+
+
+

lcoeff(<pol. expr.>)

+
+

The leading coefficient.

+
+
+
+

monicpart(<pol. expr.>)

+
+

Divides by the leading coefficient, except that monicpart(0)==0.

+
+

unstable

+

Currently the coefficients are reduced to lowest terms (contrarily +to legacy behaviour of \PolMakeMonic), and +additionally the xintfrac \xintREZ macro is applied which +extracts powers of ten from numerator or denominator and stores +them internally separately. This is generally beneficial to +efficiency of multiplication.

+
+
+
+
+

icontent(<pol. expr.>)

+
+

The gcd of the (possibly fractional) polynomial coefficients. It is +always produced as an irreducible (non-negative) fraction. According +to Gauss theorem the content of a product is the product of the +contents.

+
+

name unstable

+

Some hesitation with using content() rather.

+
+
+
+
+

primpart(<pol. expr.>)

+
+

The quotient (except for the zero polynomial) by +icontent(<pol. expr.>). This is thus a polynomial with +integer coefficients having 1 as greatest common divisor. The +sign of the leading coefficient is the same as in the original.

+

And primpart(0)==0.

+

The trailing zeros of the integer coefficients are extracted +into a power of ten exponent part, in the internal representation.

+
+
+
+

quorem(<pol. expr.>, <pol. expr.>)

+
+

Produces a nutple [Q,R] with Q the euclidean quotient and +R the remainder.

+
+

name unstable

+

poldiv()?

+
+
+
+
+

quo(<pol. expr.>, <pol. expr.>)

+
+

The euclidean quotient.

+

The deprecated pol1/pol2 syntax computes the same polynomial.

+
+
+
+

rem(<pol. expr.>, <pol. expr.>)

+
+

The euclidean remainder. If pol2 is a (non-zero) scalar, this is +zero.

+

There is no infix operator associated to this, for lack of evident +notation. Please advise.

+

/: can be used if one is certain that pol2 is of +degree at least one. But read the warning about it being unstable +even in that case.

+
+
+
+

prem(<pol. expr. 1>, <pol. expr. 2>)

+
+

Produces a nutple [m, spR] where spR is the (special) pseudo +Euclidean remainder. Its description is:

+
    +
  • the standard euclidean remainder R is spR/m

  • +
  • m = b^f with b equal to the absolute value of the +leading coefficient of pol2,

  • +
  • f is the number of non-zero coefficients in the euclidean +quotient, if deg(pol2)>0 (even if the remainder vanishes).

    +

    If pol2 is a scalar however, the function outputs [1,0].

    +
  • +
+

With these definitions one can show that if both pol1 and +pol2 have integer coefficients, then this is also the case of +spR, which makes its interest (and also m*Q has integer +coefficients, with Q the euclidean quotient, if deg(pol2)>0). +Also, prem() is computed faster than rem() for such integer +coefficients polynomials.

+
+

Hint

+

If you want the euclidean quotient R evaluated via spR/m +(which may be faster, even with non integer coefficients) use +subs(last(x)/first(x),x=prem(P,Q)) syntax as it avoids +computing prem(P,Q) twice. This does the trick both in +\poldef or in \xintdefvar.

+

However, as is explained in the xintexpr documentation, using +such syntax in an \xintdeffunc is (a.t.t.o.w) illusory, due to +technicalities of how subs() gets converted into nested +expandable macros. One needs an auxiliary function like this:

+
\xintdeffunc lastoverfirst(x):=last(x)/first(x);
+\xintdeffunc myR(x)=lastoverfirst(prem(x));
+

Then, myR(pol1,pol2) will evaluate prem(pol1,pol2) only +once and compute a polynomial identical to the euclidean +remainder (internal representations of coefficients may differ).

+
+

In this case of integer coefficients polynomials, the polexpr +internal representation of the integer coefficients in the pseudo +remainder will be with unit denominators only if that was already the +case for those of pol1 and pol2 (no automatic reduction to +lowest terms is made prior or after computation).

+

Pay attention here that b is the absolute value of the +leading coefficient of pol2. Thus the coefficients of the +pseudo-remainder have the same signs as those of the standard +remainder. This diverges from Maple's function with the same name.

+
+
+
+

divmod(<pol. expr. 1>, <pol. expr. 2>)

+
+

Overloads the scalar divmod() and associates it with the +experimental // and /: as extended to the polynomial type.

+

In particular when both pol1 and pol2 are scalars, this is +the usual divmod() (as in Python) and for pol1 and pol2 +non constant polynomials, this is the same as quorem().

+
+

Highly unstable overloading of \xinteval's divmod().

+
+
+
+
+

mod(<pol. expr. 1>, <pol. expr. 2>)

+
+

The R of the divmod() output. Same as R of quorem() +when the second argument pol2 is of degree at least one.

+
+

Highly unstable overloading of \xinteval's mod().

+
+
+
+
+

polgcd(<pol. expr. 1>, <pol. expr. 2>, ...)

+
+

Evaluates to the greatest common polynomial divisor of all the +polynomial inputs. The output is a primitive (in particular, +with integer coefficients) polynomial. It is zero if and only if all +inputs vanish.

+

Attention, there must be either at least two polynomial variables, or +alternatively, only one argument which then must be a bracketed list +or some expression or variable evaluating to such a "nutple" whose +items are polynomials (see the documentation of the scalar gcd() +in xintexpr).

+
+

The two variable case could (and was, during development) have been +defined at user level like this:

+
\xintdeffunc polgcd_(P,Q):=
+  (deg(Q))??{P}{1}{polgcd_(Q,primpart(last(prem(P,Q))))};
+\xintdeffunc polgcd(P,Q):=polgcd_(primpart(P),primpart(Q));%
+

This is basically what is done internally for two polynomials, up +to some internal optimizations.

+
+

UNSTABLE

+

I hesitate between returning a primitive or a monic polynomial. +Maple returns a primitive polynomial if all inputs 3 have integer +coefficients, else it returns a monic polynomial, but this is +complicated technically for us to add such a check and would add +serious overhead.

+

Internally, computations are done using primitive +integer-coefficients polynomials (as can be seen in the function +template above). So I decided finally to output a primitive +polynomial, as one can always apply monicpart() to it.

+

Attention that this is at odds with behaviour of the legacy +\PolGCD (non expandable) macro.

+
+
3
+

actually, only two polynomial arguments are allowed by Maple's +gcd() as far as I know.

+
+
+
+
+
+

resultant(<pol. expr. 1>, <pol. expr. 2>)

+
+

The resultant.

+
+

NOT YET IMPLEMENTED

+
+
+
+
+

disc(<pol. expr.>)

+
+

The discriminant.

+
+

NOT YET IMPLEMENTED

+
+
+
+
+

polpowmod(<pol. expr. 1>, <num. expr.>, <pol. expr. 2>)

+
+

Modular exponentiation: mod(pol1^N, pol2) in a more efficient +manner than first computing pol1^N then reducing modulo pol2.

+

Attention that this is using the mod() operation, whose current +experimental status is as follows:

+
    +
  • if deg(pol2)>0, the euclidean remainder operation,

  • +
  • if pol2 is a scalar, coefficient-wise reduction modulo pol2.

  • +
+

UNSTABLE

+
+

This is currently implemented at high level via \xintdeffunc and +recursive definitions, which were copied over from a scalar example +in the xintexpr manual:

+
\xintdeffunc polpowmod_(P, m, Q) :=
+       isone(m)?
+           % m=1: return P modulo Q
+           {   mod(P,Q)  }
+           % m > 1: test if odd or even and do recursive call
+           {   odd(m)? {  mod(P*sqr(polpowmod_(P, m//2, Q)), Q) }
+                       {  mod(  sqr(polpowmod_(P, m//2, Q)), Q) }
+            }
+         ;%
+\xintdeffunc polpowmod(P, m, Q) := (m)?{polpowmod_(P, m, Q)}{1};%
+

Negative exponents are not currently implemented.

+

For example:

+
\xinteval{subs(polpowmod(1+x,100,x^7),x=pol([0,1]))}
+\xinteval{subs(polpowmod(1+x,20,10), x=pol([0,1]))}
+

produce respectively:

+
pol([1, 100, 4950, 161700, 3921225, 75287520, 1192052400])
+pol([1, 0, 0, 0, 5, 4, 0, 0, 0, 0, 6, 0, 0, 0, 0, 4, 5, 0, 0, 0, 1])
+
+
+
+
+

rdcoeffs(<pol. expr.>)

+
+

This operates on the internal representation of the coefficients, +reducing them to lowest terms.

+
+

name HIGHLY undecided

+
+
+
+
+

rdzcoeffs(<pol. expr.>)

+
+

This operates on the internal representation of the coefficients, +reducing them to lowest terms then extracting from numerator +or denominator the maximal power of ten to store as a decimal +exponent.

+

This is sometimes favourable to more efficient polynomial algebra +computations.

+
+

name HIGHLY undecided

+
+
+
+
+

diff1(<pol. expr.>)

+
+

The first derivative.

+
+

name UNSTABLE

+

This name may be used in future to be the partial derivative with +respect to a first variable.

+
+
+
+
+

diff2(<pol. expr.>)

+
+

The second derivative.

+
+

name UNSTABLE

+

This name may be used in future to be the partial derivative with +respect to a second variable.

+
+
+
+
+

diffn(<pol. expr. P>, <num. expr. n>)

+
+

The nth derivative of P. For n<0 computes iterated primitives +vanishing at the origin.

+

The coefficients are not reduced to lowest terms.

+
+

name and syntax UNSTABLE

+

I am also considering reversing the order of the arguments.

+
+
+
+
+

antider(<pol. expr. P>)

+
+

The primitive of P with no constant term. Same as diffn(P,-1).

+
+
+
+

intfrom(<pol. expr. P>, <pol. expr. c>)

+
+

The primitive of P vanishing at c, i.e. \int_c^x P(t)dt.

+

Also c can be a polynomial... so if c is monomial x +this will give zero!

+
+

UNSTABLE

+

Allowing general polynomial variable for c adds a bit of +overhead to the case of a pure scalar. So I am hesitating +maintaining this feature whose interest appears dubious.

+
+
+
+
+

integral(<pol. expr. P>, [<pol. expr. a>, <pol. expr. b>])

+
+

\int_a^b P(t)dt.

+

The brackets here are not denoting an optional argument +but a mandatory nutple argument [a, b] with two items.

+

a and b are not restricted to be scalars, they can be +polynomials.

+
+

To compute \int_{x-1}^x P(t)dt it is more efficient to use +intfrom(x-1).

+

Similary to compute \int_x^{x+1} P(t)dt, use -intfrom(x+1).

+

UNSTABLE

+

Am I right to allow general polynomials a and b hence add +overhead to the pure scalar case ?

+
+
+
+
+
+

Examples of localization of roots

+
+

Note

+

As of 0.8, polexpr is usable with Plain TeX and not only with +LaTeX, the examples of this section have been converted to use a +syntax which (at least at time of writing, March 2021) works in both.

+

This is done in order for the examples to be easy to copy-paste to +documents using either macro format.

+
+
    +
  • To make printed decimal numbers more enjoyable than via +\xintSignedFrac (or \xintSignedFwOver with Plain):

    +
    \def\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:

    +
    \def\PolToExprCmd#1{\PolDecToString{\xintREZ{#1}}}%
    +

    to modify output of \PolToExpr{<pol. expr.>}.

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

  • +
+
+

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)
+\def\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}
+\def\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}
+  \def\PolTypesetCmdPrefix#1{\allowbreak\xintiiifSgn{#1}{}{+}{+}}%
+  $Q_0(x) = \PolTypeset{Q_0}$
+\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
+\def\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.

+
%\xintverbosetrue % for the curious...
+
+\poldef f(x) := mul((x - i), i = 1..20);
+
+\def\PolTypesetCmdPrefix#1{\allowbreak\xintiiifSgn{#1}{}{+}{+}}%
+\def\PolTypesetOne#1{\xintDecToString{#1}}%
+
+\noindent\PolTypeset{f}
+
+\PolToSturm{f}{f}
+\PolSturmIsolateZeros{f}
+\PolPrintIntervals{f}
+
+% \vfill\eject
+
+% This page is commented out because it takes about 30s on a 2GHz CPU
+% \poldef g(x) := f(x) - 2**{-23} x**19;
+
+% \PolToSturm{g}{g}
+% \noindent\PolTypeset{g_0}% integer coefficient primitive polynomial
+
+% \PolSturmIsolateZeros{g}
+% \PolEnsureIntervalLengths{g}{-10}
+
+% \let\PolPrintIntervalsPrintMultiplicity\empty
+% \PolPrintIntervals*{g}
+

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, 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

+
\poldef f(x) := mul(x - 2^-i, i = 1..20);
+
+%\PolTypeset{f}
+
+\PolToSturm{f}{f}
+\PolSturmIsolateZeros**{f}
+\PolPrintIntervals{f}
+

This takes more time than the polynomial with 1, 2, .., 20 as roots but +less than the latter modified by the 2**-23 tiny change to one of its +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

+
\def\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

+
+

Note

+

At 0.8 polexpr is usable with Plain TeX and not only with +LaTeX. Some examples given in this section may be using LaTeX syntax +such as \renewcommand. Convert to TeX primitives as appropriate +if testing with a non LaTeX macro format.

+
+
+

\poldef polname(letter):= expression using the letter as indeterminate;

+
+

This evaluates the polynomial expression and stores the +coefficients in a private structure accessible later via other +package macros, when used with argument the chosen polname. Of +course the expression can use other previously defined +polynomials.

+

Polynomial names must start with a letter and are constituted of +letters, digits, underscores and the right tick '.

+

The whole xintexpr syntax is authorized:

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

With fractional coefficients, beware the tacit multiplication issue.

+

Furthermore:

+
    +
  • a variable mypol is defined which can be used in \poldef +as well as in \xinteval for algebraic computations or as +argument to polynomial aware functions,

  • +
  • a function mypol() is defined which can be used in \poldef +as well as in \xinteval. It accepts there as argument scalars +and also other polynomials (via their names, thanks to previous +item).

  • +
+

Notice that any function defined via \xintdeffunc and using +only algebraic operations (and ople indexing or slicing operations) +should work fine in \xintexpr/\xinteval with such polynomial +names as argument.

+

In the case of a constant polynomial, the xintexpr variable (not the +internal data structure on which the package macros operate) +associated to it is indistinguishable from a scalar, it is actually +a scalar and has lost all traces from its origins as a polynomial +(so for example can be used as argument to the cos() function). +THIS MAY CHANGE

+

The function on the other hand remains a one-argument function, +which simply has a constant value.

+
+

Attention!

+

The function mypol() is defined only for +\xintexpr/\xinteval +context. It will be unknown to \xintfloateval.

+

Worse, a +previously existing floating point function 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 result of usage of the other package macros.

+

See \PolGenFloatVariant{polname} to generate a function +usable in \xintfloateval. Such a function can only be +used with scalar input, see next warning.

+
+
+

Attention!

+

Using the variable mypol inside \xintfloateval will +generate low-level errors because the infix operators there are +not polynomial-aware, and the polynomial specific functions such +as deg() are only defined for usage inside \xintexpr.

+

In short, currently polynomials defined via polexpr can +be used in floating point context only for numerical evaluations, +via functions obtained from \PolGenFloatVariant{polname} +usage.

+

Changes to the original polynomial via package macros are not +automatically mapped to the numerical floating point evaluator +which must be manually updated as necessary when the original +rational coefficient polynomial is modified.

+

THIS MAY CHANGE

+
+

The original expression is lost after parsing, and in particular the +package provides no way to typeset it (of course the package +provides macros to typeset the computed polynomial). Typesetting +the original expression has to be done manually, if needed.

+
+
+
+

\PolDef[letter]{polname}{expression using the letter as indeterminate}

+
+

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{<pol. expr.>}.

+
+

Attention!

+

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 \xintfloateval as a +function.

+
+
+
+
+

\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{polname_2}={polname_1}

+
+

Acts globally.

+
+
+
+

\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{<pol. expr.>}

+
+

Typesets in descending powers, switching to math mode if in text +mode, after evaluating the polynomial expression:

+
\PolTypeset{mul(x-i,i=1..5)}% possible since polexpr 0.8
+

The letter used in the input expression is by default x, +but can be modified by a redefinition of \PolToExprInVar.

+

It uses also by default the letter x on output but this one can +be changed via an optional argument:

+
\PolTypeset[z]{polname or polynomial expression}
+

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

+

The following macros (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}

+
+

Defaults to \xintSignedFrac (LaTeX) or \xintSignedFwOver +(else). But these xintfrac very old legacy macros are a bit +annoyin as they insist in exhibiting a power of ten rather than +using simpler decimal notation.

+

As alternative one can do things such as:

+
\def\PolTypesetOne#1{\xintDecToString{\xintREZ{#1}}}
+% or with LaTeX+siunitx for example
+\renewcommand\PolTypesetOne[1]{\num{\xintPFloat[5]{#1}}}
+% (as \num of siunitx understands floating point notation)
+\renewcommand\PolTypesetOne[1]{\num{\xintRound{4}{#1}}}
+
+
+
+

\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 (or \xintSignedFwOver) 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*{<pol. expr.>}

+
+

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

+

Extended at 0.8 to accept general expressions and not only +polynomial names. Redefine \PolToExprInVar to use in the +expression another letter than default 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.

+
+
+ +
+

\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

+
+

Note

+

At 0.8 polexpr is usable with Plain TeX and not only with +LaTeX. Some examples given in this section may be using LaTeX syntax +such as \renewcommand. Convert to TeX primitives as appropriate +if testing with a non LaTeX macro format.

+
+

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. 4 To use the exact coefficients with exactly +executed additions and multiplications, just insert it in the float +expression as in this example: 5

+
\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.

+
+
4
+

Anyway each floating point operation starts by rounding its +operands to the floating point precision.

+
+
5
+

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{<pol. expr.>}

+
+

Produces expandably 6 the string coeff_N*x^N+..., i.e. the +polynomial is using descending powers.

+
+
6
+

requires exhaustive expansion, for example as triggered by +\write or \edef.

+
+
+

Since 0.8 the input is not restricted to be a polynomial name but +is allowed to be an arbitrary expression (where by default the +letter x is recognized as the indeterminate; see +\PolToExprInVar).

+

The default output (which also by default uses the letter x and is +completely configurable, see in particular \PolToExprVar) is +compatible with both

+
    +
  • the Maple's input format,

  • +
  • and the PSTricks \psplot[algebraic] input format.

  • +
+

Attention that it is not compatible with Python, but see +\PolToExprCaret in this regard.

+

It has the following characteristics:

+
    +
  • vanishing coefficients are skipped (issue \poltoexpralltrue to +override this and produce output such as x^3+0*x^2+0*x^1+0),

  • +
  • negative coefficients are not prefixed by a + sign (else, +Maple would not be happy),

  • +
  • coefficients numerically equal to 1 (or -1) are present +only via their sign,

  • +
  • the letter x is used and the degree one monomial is output as +x, not as x^1.

  • +
  • (0.8) the caret ^ is of catcode 12. This means that one +can for convenience typeset in regular text mode, for example +using \texttt (in LaTeX). But TeX will not know how to break +the expression across end-of-lines anyhow. Formerly ^ was +suitable for math mode but as the exponent is not braced this +worked only for polynomials of degrees at most 9. Anyhow this +is not supposed to be a typesetting macro.

  • +
+

Complete customization is possible, see the next macros. Any user +redefinition must maintain the expandability property.

+
+
+

\PolToExprVar

+
+

Defaults to x. The letter used in input.

+
+
+
+

\PolToExprInVar

+
+

Defaults to x: the letter used as the polynomial indeterminate.

+

Recall that declared polynomials are more efficiently used in +algebraic expressions without the (x), i.e. P*Q is better +than P(x)*Q(x). Thus the input, even if an expression, does not +have to contain any x.

+

(new with 0.8)

+
+
+
+

\PolToExprTimes

+
+

Defaults to *.

+
+
+
+

\PolToExprCaret

+
+

Defaults to ^ of catcode 12. Set it to +expand to ** for Python compatible output.

+

(new with 0.8)

+
+
+
+

\PolToExprCmd{raw_coeff}

+
+

Defaults to \xintPRaw{\xintRawWithZeros{#1}}.

+

This means that the coefficient value is printed-out as a fraction +a/b, skipping the /b part if b turns out to be one.

+

Configure it to be \xintPRaw{\xintIrr{#1}} if the fractions +must be in irreducible terms.

+

An alternative is \xintDecToString{\xintREZ{#1}} which uses +integer or decimal fixed point format such as 23.0071 if the +internal representation of the number only has a power of ten as +denominator (the effect of \xintREZ here is to remove trailing +decimal zeros). The behaviour of \xintDecToString is not yet +stable for other cases, and for example at time of writing no +attempt is made to identify inputs having a finite decimal expansion +so for example 23.007/2 or 23.007/25 can appear in output +and not their finite decimal expansion with no denominator.

+
+
+
+

\PolToExprOneTerm{raw_coeff}{number}

+
+

This is the macro which from the coefficient and the exponent +produces the corresponding term in output, such as 2/3*x^7.

+

For its default definition, see the source code. It uses +\PolToExprCmd, \PolToExprTimes, \PolToExprVar and +\PolToExprCaret.

+
+
+
+

\PolToExprOneTermStyleA{raw_coeff}{number}

+
+

This holds the default package meaning of \PolToExprOneTerm.

+
+
+
+

\PolToExprOneTermStyleB{raw_coeff}{number}

+
+

This holds an alternative meaning, which puts the fractional part of +a coefficient after the monomial, i.e. like this:

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

\PolToExprCmd isn't used at all in this style. But +\PolToExprTimes, \PolToExprVar and \PolToExprCaret are obeyed.

+

To activate it use \let\PolToExprOneTerm\PolToExprOneTermStyleB. +To revert to the package default behaviour, issue +\let\PolToExprOneTerm\PolToExprOneTermStyleA.

+
+
+
+

\PolToExprTermPrefix{raw_coeff}

+
+

It receives as argument the coefficient. Its default behaviour is +to produce a + if the coefficient is positive, which will thus +serve to separate the monomials in the output. This is to match +the default for \PolToExprCmd{raw_coeff} which in case of a +positive coefficient does not output an explicit + prefix.

+
+
+
+
+

\PolToExpr*{<pol. expr.>}

+
+

Ascending powers: coeff_0+coeff_1*x+coeff_2*x^2+....

+

Extended at 0.8 to accept general expressions as input.

+

Customizable with the same macros as for +\PolToExpr{<pol. expr.>}.

+
+
+
+

\PolToFloatExpr{<pol. expr.>}

+
+

Similar to \PolToExpr{<pol. expr.>} but using \PolToFloatExprCmd which by default rounds and +converts the coefficients to floating point format.

+
+

Note

+

This is unrelated to \PolGenFloatVariant{polname}: +\PolToFloatExprCmd{raw_coeff} operates on the exact +coefficients anew (and may thus produce something else than +the coefficients of the polynomial function acting +in \xintfloateval if the floating point precision was changed +in between).

+
+

Extended at 0.8 to accept general expressions as input.

+
+
+

\PolToFloatExprOneTerm{raw_coeff}{number}

+
+

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

+
+
+
+

\PolToFloatExprCmd{raw_coeff}

+
+

The one-argument macro used by \PolToFloatExprOneTerm. +It defaults to \xintFloat{#1}.

+
+

Caution!

+

Currently \xintFloat{0} outputs 0.e0 +which is perfectly acceptable input for Python, but not for +Maple. Thus, one should better leave the \\ifpoltoexprall TeX +Boolean to its default \poltoexprallfalse, 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*{<pol. expr.>}

+
+

Ascending powers.

+

Extended at 0.8 to accept general expressions as input.

+
+
+
+

\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.

+
+
+ +
+

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.

+
+
+
+
+
+

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 +polynomial or scalar variables are defined.

+
+

Caution!

+

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

+
+
+
+
+

\polnewpolverbosefalse

+
+

When \poldef is used, both a variable and a function are +defined. The default \polnewpolverbosefalse setting suppresses +the print-out to the log and terminal of the function macro meaning, +as it only duplicates the information contained in the variable +which is already printed out to the log and terminal.

+

However \PolGenFloatVariant{polname} does still print out the +information relative to the polynomial function it defines for use in +\xintfloateval{} as there is no float polynomial variable, only the +function, and it is the only way to see its rounded coefficients +(\xintverbosefalse suppresses also that info).

+

If set to true, it overrides in both cases +\xintverbosefalse. The setting only affects polynomial +declarations. Scalar variables such as those holding information on +roots obey only the \xintverbose... setting.

+

(new with 0.8)

+
+
+
+

\poltypesetallfalse

+
+

If true, \PolTypeset will also typeset the vanishing +coefficients.

+
+
+
+

\poltoexprallfalse

+
+

If true, \PolToExpr{<pol. expr.>} and \PolToFloatExpr{<pol. expr.>} will +also include the vanishing coefficients in their outputs.

+
+
+
+
+

Utilies

+
+

\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 +xintexpr.

+
+
+
+

\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 7, 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*Q;
    +

    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.

    +
    +
    7
    +

    prior to 0.4.1, polexpr used to temporarily patch +during the parsing of polynomials the xintfrac macros. This +patch was backported to xintexpr 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 xintexpr 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, 0.7, and 0.8 +releases for their continued interest.

+

See README.md for the License.

+
+
+ + diff --git a/macros/generic/polexpr/polexpr.sty b/macros/generic/polexpr/polexpr.sty new file mode 100644 index 0000000000..c94a4e8d61 --- /dev/null +++ b/macros/generic/polexpr/polexpr.sty @@ -0,0 +1,1057 @@ +% author: Jean-François Burnol +% License: LPPL 1.3c (author-maintained) +% Usage: \input polexpr.sty (Plain or other macro formats) +% or \usepackage{polexpr} (LaTeX macro format) +% polexpr.sty (this file) inputs: +% polexprcore.tex +% polexprexpr.tex +% polexprsturm.tex +\begingroup\catcode61\catcode48\catcode32=10\relax% + \catcode13=5 % ^^M + \endlinechar=13 % + \catcode123=1 % { + \catcode125=2 % } + \catcode64=11 % @ + \catcode35=6 % # + \catcode44=12 % , + \catcode45=12 % - + \catcode46=12 % . + \catcode58=12 % : + \def\z {\endgroup}% + \expandafter\let\expandafter\x\csname ver@polexpr.sty\endcsname + \expandafter\let\expandafter\w\csname ver@xintexpr.sty\endcsname + \expandafter + \ifx\csname PackageInfo\endcsname\relax + \def\y#1#2{\immediate\write-1{Package #1 Info: #2.}}% + \else + \def\y#1#2{\PackageInfo{#1}{#2}}% + \fi + \expandafter + % I don't think engine exists providing \expanded but not \numexpr + \ifx\csname expanded\endcsname\relax + \y{polexpr}{\expanded not available, aborting input}% + \aftergroup\endinput + \else + \ifx\x\relax % plain-TeX, first loading of polexpr.sty + \ifx\w\relax % but xintexpr.sty not yet loaded. + \expandafter\def\expandafter\z\expandafter + {\z\input xintexpr.sty\relax}% + \fi + \else + \def\empty {}% + \ifx\x\empty % LaTeX, first loading, + % variable is initialized, but \ProvidesPackage not yet seen + \ifx\w\relax % xintexpr.sty not yet loaded. + \expandafter\def\expandafter\z\expandafter + {\z\RequirePackage{xintexpr}[2021/02/20]}% + \fi + \else + \aftergroup\endinput % polexpr already loaded. + \fi + \fi + \fi +\z% +\XINTsetupcatcodes% +\XINT_providespackage +\ProvidesPackage{polexpr}% + [2021/03/29 v0.8 Polynomial expressions with rational coefficients (JFB)]% +\begingroup + \def\x#1/#2/#3 #4\xint:{#1#2#3}% + \ifnum\expandafter\x\expanded{\csname ver@xintexpr.sty\endcsname}\xint: + <20210220 % actually 20200131 (xint 1.4) is presumably ok + \immediate\write128{! Package polexpr error: xintexpr too old, aborting input}% + \else\expandafter\xint_gobble_i + \fi +\endinput\endgroup +\let\PolDecToString\xintDecToString +\long\def\POL@ifstar#1#2% +{% + \begingroup\def\@tempa{#1}\def\@tempb{#2}% + \futurelet\@let@token\POL@@ifstar +}% +\def\POL@@ifstar +{% + \xint_firstofone{\ifx} \@let@token\def\next{\POL@@again\POL@@ifstar}\else + \ifx*\@let@token\def\next##1{\expandafter\endgroup\@tempa}\else + \def\next{\expandafter\endgroup\@tempb}\fi\fi\next +}% +\xint_firstofone{\def\POL@@again#1} {\futurelet\@let@token#1}% +\long\def\POL@chkopt#1[#2]% +{% + \begingroup\def\@tempa{#1}\def\@tempb{#1[#2]}% + \futurelet\@let@token\POL@@ifopt +}% +\def\POL@@ifopt +{% + \xint_firstofone{\ifx} \@let@token\def\next{\POL@@again\POL@@ifopt}\else + \ifx[\@let@token\def\next{\expandafter\endgroup\@tempa}\else %] + \def\next{\expandafter\endgroup\@tempb}\fi\fi\next +}% +% \polexprsetup added at 0.7 +\catcode`! 3 +\def\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 +}% +\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} +\catcode`! 11 % special catcode for ! as used in xintexpr.sty +% +\newif\ifxintveryverbose +\newif\ifpolnewpolverbose +\newif\ifpoltypesetall +\newif\ifpoltoexprall +%% +%% Main data format for non-expandable manipulations +%% +%% The main exchange structure is: +%% N.\empty{coeff0}{coeff1}....{coeffN} +%% It is stored in macros \POLuserpol@ +%% The \empty is basically there to avoid brace-stripping +%% in some grabbing contexts (maybe I should revisit this) +%% +%% The zero polynomial is stored as -1.\empty{0/1[0]} +%% Degree zero polynomials are 0.\empty{numeric value} +%% +%% Depending on input path the numeric values coeff0, coeff1, ...., coeffN +%% may have been or not already converted into A/B[n] format. +%% As a rule, computations are not followed with reducing the fractions +%% to smallest terms; the innocent may be unaware that computing +%% with fractions quickly give gigantic numbers. There is \PolReduceCoeffs +%% to do that. +%% +%% This base structure is maintained at 0.8 for legacy reasons but perhaps I +%% need to revisit this. A characteristic of the package so far is that it +%% thus stores and manipulate polynomials basically as the complete sequence +%% of coefficients, (using the xintfrac "zero" for missing coefficients) which +%% means that it will handle poorly polynomials of high degrees such as X^500. +%% +%% Test if zero +\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}% +%% Split into degree and coefficients +% The \expandafter chain removes the \empty token +\def\POL@split#1.#2;#3#4% + {\def#3{#1}\expandafter\def\expandafter#4\expandafter{#2}}% +%% Define from values stored in a "macros-array" +\def\POL@resultfromarray #1{% + \edef\POL@result{\ifnum\count@>\z@ + \the\numexpr\count@-\@ne.\noexpand\empty + \xintiloop [1+1]% + \expandafter\POL@braceit\csname POL@array#1\xintiloopindex\endcsname + \ifnum\xintiloopindex<\count@ + \repeat + \else-1.\noexpand\empty{0/1[0]}\fi}% +}% +\def\POL@braceit#1{{#1}}% needed as \xintiloopindex can not "see" through braces +%% +%% Conversion between legacy data storage and the one used for the +%% the novel polexpr 0.8 notion of \xintexpr polynomial variables +%% +%% The 0.8 expandable implementation of core algebra is also manipulating +%% the complete list of coefficients. The internal data structure is +%% (this is the numeric leaf in xintexpr ople terminology) currently: +%% PN.{coeff0}{coeff1}....{coeffN} +%% where the P letter identifies the polynomial type. +%% Here the degree N is *always* at least 1: if some evaluation ends +%% up in a constant polynomial it will always be output as a genuine +%% scalar numeric variable, as a rule in in A/B[n] format +%% +%% This is not definitive and I need to think about it more (in particular +%% in the distant perspective of supporting multi-variable polynomials). +%% However modifying this will be costly labor at this stage. +%% +\input polexprcore.tex\relax % load expandable algebra +\def\POL@vartolegacy #1% \romannumeral\POL@vartolegacy ... \xint: +{% + \if 0#1\xint_dothis\POL@vartolegacy@zero\fi + \if P#1\xint_dothis\POL@vartolegacy@pol\fi + \xint_orthat\POL@vartolegacy@scalar #1% +}% +\def\POL@vartolegacy@zero #1\xint:{\xint_c_ -1.\empty{0/1[0]}}% +\def\POL@vartolegacy@scalar #1\xint:{\xint_c_ 0.\empty{#1}}% +\def\POL@vartolegacy@pol P#1.#2\xint:{\xint_c_ #1.\empty#2}% +% +\def\POL@tovar#1{\romannumeral\expandafter\expandafter\expandafter + \POL@legacytovar\csname POLuserpol@#1\endcsname}% +\def\POL@legacytovar #1.% \romannumeral\POL@legacytovar N.\empty{c0}... +{% + \ifnum #1<\xint_c_i\xint_dothis\POL@legacytovar@scalar\fi + \xint_orthat\POL@legacytovar@pol #1.% +}% +\def\POL@legacytovar@scalar #1.\empty#2{\xint_c_ #2}% +\def\POL@legacytovar@pol #1.\empty{\xint_c_ P#1.}% +%% +%% Extend \xintexpr (\xintdefvar, \xintdeffunc) to recognize the new +%% polynomial type +%% +%% **** It does NOT apply to \xintfloatexpr context +%% +\input polexprexpr.tex\relax +%% +%% \poldef +%% +\def\PolDef{\POL@chkopt\POL@oPolDef[x]}% +\def\POL@oPolDef[#1]#2#3{\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@polname{\xint_zapspaces #1 \xint_gobble_i}% +\begingroup + \unless\ifxintveryverbose\xintverbosefalse\fi + %% RADICAL CHANGE AT 0.8: + %% we define a **variable** not a **function** + %% ever since polexpr initial version, a function was defined and + %% the associated macros was then deconstructed in further analysis + %% via non-expandable approach. At 0.8 the polynomial algebra has + %% been implemented expandably allowing direct plug-in into \xintexpr + \xintdefvar __pol = subs(#4,#2=qraw({{P1.{0/1[0]}{1/1[0]}}}));% + \expandafter +\endgroup + \expandafter\def\expandafter\POL@result\expandafter + {\romannumeral0\expandafter\xint_stop_atfirstofone + \romannumeral0\csname XINT_expr_varvalue___pol\endcsname}% + \XINT_global\expandafter\def\csname POLuserpol@\POL@polname\expandafter\endcsname + \expandafter{\romannumeral\expandafter\POL@vartolegacy\POL@result\xint:}% + \expandafter\POL@newpol\expandafter{\POL@polname}% +}% +\def\POL@newpol#1{% + % 0.7.5 had some complicated special handling of constant + % polynomials, but these are complications of the past + % First a variable usable in \poldef but not in \xintexpr for arithmetic + % only for special dedicated functions such as coeff(), deg() + % (when they will be implemented). In \poldef, composition of polynomials + % in P(Q) syntax will be more efficient than P(Q(x)). + % This will use \XINT_global and obey \xintverbose... setting + \XINT_expr_defvar_one{#1}{{\POL@tovar{#1}}}% + % Second a function usable not only in \poldef but also in \xintexpr + % Will use \XINT_global + \POL@newpolhorner{#1}% + \POL@defpolfunc{#1}{expr}% + \XINT_global\expandafter\let\csname XINT_flexpr_func_#1\endcsname\@undefined + \ifpolnewpolverbose\POL@info{#1}\fi +}% +\def\POL@newfloatpol#1{% + \POL@newfloatpolhorner{#1}% + \POL@defpolfunc{#1}{flexpr}% + \ifpolnewpolverbose\POL@floatinfo{#1}% + \else + \ifxintverbose\POL@floatinfo{#1}\fi + \fi +}% +\def\POL@info #1{% + \xintMessage {polexpr}{Info}% + {Function #1 for the \string\xintexpr\space parser is + \ifxintglobaldefs(globally) \fi + associated to \string\XINT_expr_polfunc_#1\space + with meaning: + \expandafter\meaning + \csname XINT_expr_polfunc_#1\endcsname}% +}% +\def\POL@floatinfo #1{% + \xintMessage {polexpr}{Info}% + {Function #1 for the \string\xintfloatexpr\space parser is + \ifxintglobaldefs(globally) \fi + associated to \string\XINT_flexpr_polfunc_#1\space + with meaning: + \expandafter\meaning + \csname XINT_flexpr_polfunc_#1\endcsname}% +}% +% +\def\POL@newpolhorner#1{% + \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\XINT_global + \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\xintPolAdd\relax\let\xintPolMul\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} + {\xint_firstofone}{\xintPolAdd{#1}}% + {\xintPolMul{##1}{\POL@tmp{##1}}}}% + \POL@newpol@horner@loop.% +}% +\def\POL@newpol@floathorner#1{\let\XINTinFloatAdd\relax\let\XINTinFloatMul\relax + \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} + {\xint_firstofone}{\XINTinFloatAdd{\XINTinFloatdigits{#1}}}% + {\XINTinFloatMul{##1}{\POL@tmp{##1}}}}% + \POL@newpol@floathorner@loop.% +}% +%% +%% Non-expandable polynomial manipulations +%% +\def\PolGenFloatVariant#1{\POL@newfloatpol{#1}}% +% +\def\PolLet#1#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{% + \XINT_global + \expandafter\let\csname POLuserpol@#1\expandafter\endcsname + \csname POLuserpol@#2\endcsname + \XINT_expr_defvar_one{#1}{{\POL@tovar{#1}}}% + \XINT_global + \expandafter\let\csname XINT_expr_polfunc_#1\expandafter\endcsname + \csname XINT_expr_polfunc_#2\endcsname + \POL@defpolfunc{#1}{expr}% + \ifpolnewpolverbose\POL@info{#1}\fi +}% +\def\PolGlobalLet#1#2{\begingroup\xintglobaldefstrue\PolLet{#1}{#2}\endgroup} +% +\def\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}{ }% +}% +\def\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@ + \def\POL@result{-1.\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 + \count\tw@\count@ + \xintloop + \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}}% + \edef\POL@result{\the\numexpr\count@-\@ne.\noexpand\empty + \xintiloop[1+1]% + \expandafter\POL@tmp\xintiloopindex.% + \ifnum\xintiloopindex<\count@ + \repeat}% + \fi + \expandafter + \endgroup + \expandafter + \XINT_global + \expandafter + \def\csname POLuserpol@#1\expandafter\endcsname + \expandafter{\POL@result}% +}% +% +\def\PolFromCSV#1#2{% + \begingroup % closed in \POL@getfromarray + \xintAssignArray\xintCSVtoList{#2}\to\POL@arrayA + \POL@getfromarray{#1}\POL@arrayA + \POL@newpol{#1}% +}% +% +\def\PolMapCoeffs#1#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 + \XINT_global + \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]}% +\def\PolReduceCoeffs{\POL@ifstar\POL@sreducecoeffs\POL@reducecoeffs}% +\def\POL@reducecoeffs#1{\PolMapCoeffs{\POL@xintIrr}{#1}}% +\def\POL@sreducecoeffs#1{\PolMapCoeffs{\xintPIrr}{#1}}% +% +\def\PolMakeMonic#1{% + \edef\POL@leadingcoeff{\PolLeadingCoeff{#1}}% + \edef\POL@leadingcoeff@inverse{\xintDiv{1/1[0]}{\POL@leadingcoeff}}% + \PolMapCoeffs{\xintMul{\POL@leadingcoeff@inverse}}{#1}% +}% +% +%% \PolMakePrimitive (0.5) +% This uses expandable \PolIContent +% Note: the integer coefficients stored in A/1[n] form with +% A not having trailing zeroes, due to usage of \xintREZ here. +\def\POL@makeprim@macro#1% + {\xintREZ{\xintNum{\xintDiv{#1}{\POL@makeprim@icontent}}}}% +\def\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}% +}% +% +%% Euclidean division +% now based on the expandable routine from polexprcore.tex +% +\def\PolDivide#1#2#3#4{% #3=quotient, #4=remainder of #1 by #2 + \POL@divide{#1}{#2}% + \XINT_global\expandafter\let\csname POLuserpol@#3\endcsname\POL@Q + \POL@newpol{#3}% + \XINT_global\expandafter\let\csname POLuserpol@#4\endcsname\POL@R + \POL@newpol{#4}% +}% +\def\PolQuo#1#2#3{% #3=quotient of #1 by #2 + \POL@divide{#1}{#2}% + \XINT_global\expandafter\let\csname POLuserpol@#3\endcsname\POL@Q + \POL@newpol{#3}% +}% +\def\PolRem#1#2#3{% #3=remainder of #1 by #2 + \POL@divide{#1}{#2}% + \XINT_global\expandafter\let\csname POLuserpol@#3\endcsname\POL@R + \POL@newpol{#3}% +}% +\def\POL@divide#1#2{% + % much simpler at 0.8 thanks to our expandable macros + \xintAssign\xintPolQuoRem{\POL@tovar{#1}}{\POL@tovar{#2}}\to\POL@Q\POL@R + \odef\POL@Q{\romannumeral\expandafter\POL@vartolegacy\POL@Q\xint:}% + \odef\POL@R{\romannumeral\expandafter\POL@vartolegacy\POL@R\xint:}% +}% +%% Euclidean special pseudo-remainder +\def\POL@getprem#1#2{% + \let\POL@Q\undefined % trap errors in Sturm code update to use \POL@prem + % this was simpler before I converted \xintPolPRem into returning a tuple... + \odef\POL@R{\romannumeral\expandafter\POL@vartolegacy + \romannumeral0\expandafter\xint_stop_atsecondoftwo + \romannumeral`&&@\xintPolPRem{\POL@tovar{#1}}{\POL@tovar{#2}}% + \xint:}% +}% +% +%%%%%%%%%%%% +%% +%% Things are currenly implemented twice : here the legacy macros +%% such as GCD or Diff, and in polexprcore.tex the expandable +%% support macros for the \xinteval interface. +%% +%% Soon, I will probably remove all legacy code (like I did already +%% for division) and make the user macros simple wrappers to the +%% expandable code. +%% +%% But for 0.8 release, I preferred not to yet, as I did not have +%% really the time to compare speed. Usage of the "special +%% pseudo euclidean remainder" (expandable) code in Sturm chain +%% construction proved very beneficial as it divided by 3 the +%% \PolToSturm execution time on the Wilkinson perturbed type 1 +%% example in the documentation. +%% +%%%%%%%%%%%% +% +%% GCD +% +% It seems I didn't even use here the (now deleted) macros implementing +% division, and I redid here what was needed: this code, which I leave +% standing as I have other priorities, does not use the \POL@divide ! +% +\def\PolGCD#1#2#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 + \expandafter\let\expandafter\POL@A\csname POLuserpol@#1\endcsname + \expandafter\let\expandafter\POL@B\csname POLuserpol@#2\endcsname + \expandafter\POL@split\POL@A;\POL@degA\POL@polA + \expandafter\POL@split\POL@B;\POL@degB\POL@polB + \ifnum\POL@degA<\z@ + \expandafter\xint_firstoftwo\else\expandafter\xint_secondoftwo + \fi + {\ifnum\POL@degB<\z@ + \expandafter\xint_firstoftwo\else\expandafter\xint_secondoftwo + \fi + {\def\POL@result{-1.\empty{0/1[0]}}}% + {\xintAssignArray\POL@polB\to\POL@arrayB + \POL@normalize{B}% + \POL@gcd@exit BA}}% + {\ifnum\POL@degB<\z@ + \expandafter\xint_firstoftwo\else\expandafter\xint_secondoftwo + \fi + {\xintAssignArray\POL@polA\to\POL@arrayA + \POL@normalize{A}% + \POL@gcd@exit AB}% + {\ifnum\POL@degA<\POL@degB\space + \let\POL@tmp\POL@B\let\POL@B\POL@A\let\POL@A\POL@tmp + \let\POL@tmp\POL@degB\let\POL@degB\POL@degA\let\POL@degA\POL@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 + \XINT_global + \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{\xintDiv + {\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 + {\xintSub + {\csname POL@array#1\the\count4\endcsname}% + {\xintMul + {\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% +}% +% +%% 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.% +}% +\def\PolDiff{\POL@chkopt\POL@oPolDiff[1]}% +\def\POL@oPolDiff[#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\xint_firstoftwo + \else + \expandafter\xint_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 + \XINT_global\@namedef{POLuserpol@#2}{-1.\empty{0/1[0]}}% + \else + \edef\POL@var@coeffs{\expandafter\POL@diff\POL@var@coeffs\relax}% + \XINT_global\expandafter\edef\csname POLuserpol@#2\endcsname + {\the\numexpr\POL@var@deg-\@ne.\noexpand\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 + \XINT_global + \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.% +}% +\def\PolAntiDiff{\POL@chkopt\POL@oPolAntiDiff[1]}% +\def\POL@oPolAntiDiff[#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\xint_firstoftwo + \else + \expandafter\xint_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@ + \XINT_global\@namedef{POLuserpol@#2}{-1.\empty{0/1[0]}}% + \else + \edef\POL@var@coeffs{\expandafter\POL@antidiff\POL@var@coeffs\relax}% + \XINT_global\expandafter\edef\csname POLuserpol@#2\endcsname + {\the\numexpr\POL@var@deg+\@ne.\noexpand\empty{0/1[0]}\POL@var@coeffs}% + \fi +}% +% +%% +%% Localization of roots +%% +% this is big. It provides also output macros, of both expandable and +% non-expandable type +\input polexprsturm.tex\relax +% +% +%% Non-expandable output macros +% +\def\PolTypesetCmdPrefix#1{\xintiiifSgn{#1}{}{+}{+}}% +\def\PolTypesetCmd#1{\xintifOne{\xintiiAbs{#1}}% + {\ifnum\PolIndex=\z@\xintiiSgn{#1}\else + \xintiiifSgn{#1}{-}{}{}\fi + \let\PolIfCoeffIsPlusOrMinusOne\xint_firstoftwo}% + {\PolTypesetOne{#1}% + \let\PolIfCoeffIsPlusOrMinusOne\xint_secondoftwo}% + }% +\ifdefined\frac +\def\PolTypesetOne{\xintSignedFrac}% +\else +\def\PolTypesetOne{\xintSignedFwOver}% +\fi +\catcode`^ 7 +\def\PolTypesetMonomialCmd{% + \ifcase\PolIndex\space + % + \or\PolVar + \else\PolVar^{\PolIndex}% + \fi +}% +\catcode`^ 11 % normal xint catcode +\def\PolTypeset{\POL@ifstar + {\def\POL@ts@ascending{1}\POL@Typeset}% + {\def\POL@ts@ascending{0}\POL@Typeset}% +}% +%% +%% \PolTypeset +%% +%% extended at 0.8 to handle arbitrary expressions on input +%% +\def\POL@Typeset{\POL@chkopt\POL@oPOL@Typeset[x]}% +\def\POL@oPOL@Typeset[#1]#2{% + \ifmmode\let\POL@endtypeset\empty\else$\def\POL@endtypeset{$}\fi + \ifcsname POLuserpol@#2\endcsname + \expandafter\expandafter\expandafter\POL@split + \csname POLuserpol@#2\endcsname;\POL@var@deg\POL@var@coeffs + \else + \xintAssign\expandafter\xint_firstofone\romannumeral0\xintbareeval + subs((deg(x),coeffs(x)),x=subs(#2,\PolToExprInVar=pol([0,1])))\relax + \to\POL@var@deg\POL@var@coeffs + \fi + \if\POL@ts@ascending1% + \def\PolIndex{0}% + \let\POL@ts@reverse\xint_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 + \POL@endtypeset +}% +\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.% +}% +% +%% +%% Expandable output macros (legacy) +%% +\def\POL@eval@fork#1\At#2#3\krof{#2}% +\def\PolEval#1#2#3{\romannumeral`&&@\POL@eval@fork + #2\PolEvalAt + \At\PolEvalAtExpr\krof {#1}{#3}% +}% +\def\PolEvalAt#1#2{% + \xintpraw{\csname XINT_expr_polfunc_#1\endcsname{#2}}% +}% +\def\POL@eval#1#2{% + \csname XINT_expr_polfunc_#1\endcsname{#2}% +}% +\def\PolEvalAtExpr#1#2{\xinttheexpr #1(#2)\relax}% +% +\def\PolEvalReduced#1#2#3{\romannumeral`&&@\POL@eval@fork + #2\PolEvalReducedAt + \At\PolEvalReducedAtExpr\krof {#1}{#3}% +}% +\def\PolEvalReducedAt#1#2{% + \xintpraw % in order not to print denominator if the latter equals 1 + {\xintIrr{\csname XINT_expr_polfunc_#1\endcsname{#2}}[0]}% +}% +\def\PolEvalReducedAtExpr#1#2{% + \xintpraw + {\expandafter\xintIrr\romannumeral`&&@\xintthebareeval#1(#2)\relax[0]}% +}% +% +\def\PolFloatEval#1#2#3{\romannumeral`&&@\POL@eval@fork + #2\PolFloatEvalAt + \At\PolFloatEvalAtExpr\krof {#1}{#3}% +}% +\def\PolFloatEvalAt#1#2{% + \xintpfloat{\csname XINT_flexpr_polfunc_#1\endcsname{#2}}% +}% +\def\PolFloatEvalAtExpr#1#2{\xintthefloatexpr #1(#2)\relax}% +\def\PolLeadingCoeff#1{% + \romannumeral`&&@\expandafter\expandafter\expandafter\xintlastitem + \expandafter\expandafter\expandafter + {\csname POLuserpol@#1\endcsname}% +}% +% +\def\PolNthCoeff#1#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? +\def\PolDegree#1{\romannumeral`&&@\expandafter\expandafter\expandafter + \POL@degree\csname POLuserpol@#1\endcsname;}% +\def\POL@degree #1.#2;{#1}% +% +\def\PolToList#1{\romannumeral`&&@\expandafter\expandafter\expandafter + \xint_gob_til_dot\csname POLuserpol@#1\endcsname}% +% +\def\PolToCSV#1{\romannumeral0\xintlistwithsep{, }{\PolToList{#1}}}% +% +% \PolIContent (0.5) +% Why did I call this IContent and not Content? Ah, I see Maple terminology +% But I realize now I misread the Maple doc, its icontent() is the gcd of +% all coeffs of a multivariate polynomial. Whereas content(,) second argument +% specifies which variable to consider expression as being univariate in it +% +\def\POL@icontent#1{\romannumeral0\expandafter\XINT_fgcd_out + \romannumeral0\expandafter\XINT_fgcdof\romannumeral`&&@#1^}% +% Since xintexpr 1.4d, \xintGCDof always outputs an irreducible fraction A/B. +% (with B=1 if A/B integer). +\def\PolIContent#1{\xintGCDof{\PolToList{#1}}}% +% +\def\PolToExprCmd#1{\xintPRaw{\xintRawWithZeros{#1}}}% +\def\PolToFloatExprCmd#1{\xintFloat{#1}}% +% \def\PolTypesetCmdPrefix#1{\xintiiifSgn{#1}{}{+}{+}}% +\let\PolToExprTermPrefix\PolTypesetCmdPrefix +\def\PolToExprOneTermStyleA#1#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\PolToExprCaret\xintiiAbs{#2}% + \fi +}% +\let\PolToExprOneTerm\PolToExprOneTermStyleA +\def\PolToExprOneTermStyleB#1#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\PolToExprCaret\xintiiAbs{#2}% + \fi + \xintiiifOne{\xintDenominator{#1}}{}{/\xintDenominator{#1}}% +}% +\def\PolToFloatExprOneTerm#1#2{% + \ifnum#2=\z@ + \PolToFloatExprCmd{#1}% + \else + \PolToFloatExprCmd{#1}\PolToExprTimes + \fi + \ifcase\xintiiAbs{#2} %<-- space here mandatory + \or\PolToExprVar + \else\PolToExprVar\PolToExprCaret\xintiiAbs{#2}% + \fi +}% +\def\PolToExprTimes{*}% +\def\PolToExprVar{x}% +\def\PolToExprInVar{x}% +\edef\PolToExprCaret{\string ^}% +%% +%% \PolToExpr +%% +%% extended at 0.8 to handle arbitrary expressions on input +%% +\def\PolToExpr#1{% + \if*\noexpand#1\expandafter\xint_firstoftwo\else + \expandafter\xint_secondoftwo\fi + \PolToExprAscending\PolToExprDescending{#1}}% +\def\PolToFloatExpr#1{% + \if*\noexpand#1\expandafter\xint_firstoftwo\else + \expandafter\xint_secondoftwo\fi + \PolToFloatExprAscending\PolToFloatExprDescending{#1}}% +\def\PolToExpr@getit#1% +{% + \ifcsname XINT_expr_varvalue_#1\endcsname + \csname XINT_expr_varvalue_#1\expandafter\endcsname + \else + \expandafter\xint_firstofone\romannumeral0% + \xintbareeval subs(#1,\PolToExprInVar=pol([0,1]))\expandafter\relax + \fi +}% +\def\PolToExprAscending#1#2{% + \expandafter\POL@toexpr\romannumeral0\PolToExpr@getit{#2}% + \PolToExprOneTerm\POL@toexprA +}% +\def\PolToFloatExprAscending#1#2{% + \expandafter\POL@toexpr\romannumeral0\PolToExpr@getit{#2}% + \PolToFloatExprOneTerm\POL@toexprA +}% +\def\PolToExprDescending#1{% + \expandafter\POL@toexpr\romannumeral0\PolToExpr@getit{#1}% + \PolToExprOneTerm\POL@toexprD +}% +\def\PolToFloatExprDescending#1{% + \expandafter\POL@toexpr\romannumeral0\PolToExpr@getit{#1}% + \PolToFloatExprOneTerm\POL@toexprD +}% +\def\POL@toexpr#1#2#3{\POL@toexpr@fork#3#2#1\relax}% +\def\POL@toexpr@fork #1#2#3{% + \POL_Pfork + #3\POL@toexpr@pol + P\POL@toexpr@cst + \krof #1#2#3% +}% +\def\POL@toexpr@cst#1#2#3\relax{#2{#3}{0}}% +\def\POL@toexpr@pol#1#2P#3.{#1{#3}#2\empty}% +% now back to legacy pre 0.8 code +\def\POL@toexprA #1#2\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 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.% +}% +\XINT_restorecatcodes_endinput% diff --git a/macros/generic/polexpr/polexprcore.tex b/macros/generic/polexpr/polexprcore.tex new file mode 100644 index 0000000000..e071729e42 --- /dev/null +++ b/macros/generic/polexpr/polexprcore.tex @@ -0,0 +1,1366 @@ +%% This file polexprcore.tex is part of the polexpr package (0.8, 2021/03/29) +%% Core routines to match infix operators +, -, *, //, /:, ^, ** and some +%% functions +%% The atoms representing polynomials inside \xintexpr are +%% - for constants: a numeric value (indistinguishable. from scalars) +%% - for degree at least 1: P.{c0}{c1}....{cN} with N = degree +%% Auxiliaries +\long\def\POL_Pfork #1P#2#3\krof{#2}% +\long\def\POL_PPfork #1PP#2#3\krof{#2}% +\long\def\POL_zeroPfork #10P#2#3\krof{#2}% +\long\def\POL_secondofthree#1#2#3{#2}% +% \long\def\POL_Apply:x #1#2% +% {% +% \POL_apply:x_loop {#1}#2% +% \xint_Bye\xint_Bye\xint_Bye\xint_Bye +% \xint_Bye\xint_Bye\xint_Bye\xint_Bye\xint_bye +% }% +\long\def\POL_bBye#1\xint_Bye{}% +\long\def\POL_apply:x_loop #1#2#3#4#5#6#7#8#9% +{% + \POL_bBye #2\xint_Bye{#1{#2}}% + \POL_bBye #3\xint_Bye{#1{#3}}% + \POL_bBye #4\xint_Bye{#1{#4}}% + \POL_bBye #5\xint_Bye{#1{#5}}% + \POL_bBye #6\xint_Bye{#1{#6}}% + \POL_bBye #7\xint_Bye{#1{#7}}% + \POL_bBye #8\xint_Bye{#1{#8}}% + \POL_bBye #9\xint_Bye{#1{#9}}% + \POL_apply:x_loop {#1}% +}% +\long\def\POL_apply:x_iloop #1#2#3#4#5#6#7#8#9% +{% + \POL_bBye #2\xint_Bye{#10{#2}}% + \POL_bBye #3\xint_Bye{#11{#3}}% + \POL_bBye #4\xint_Bye{#12{#4}}% + \POL_bBye #5\xint_Bye{#13{#5}}% + \POL_bBye #6\xint_Bye{#14{#6}}% + \POL_bBye #7\xint_Bye{#15{#7}}% + \POL_bBye #8\xint_Bye{#16{#8}}% + \POL_bBye #9\xint_Bye{#17{#9}}% + \POL_apply:x_iloop_a#1% +}% +\def\POL_apply:x_iloop_a#1#2.% +{% + \expandafter\POL_apply:x_iloop + \expandafter{\expandafter#1\the\numexpr\xint_c_viii+#1.}% +}% +\long\def\POL_apply:x_iloop #1#2#3#4#5#6#7#8#9% +{% + \POL_bBye #2\xint_Bye{#10{#2}}% + \POL_bBye #3\xint_Bye{#11{#3}}% + \POL_bBye #4\xint_Bye{#12{#4}}% + \POL_bBye #5\xint_Bye{#13{#5}}% + \POL_bBye #6\xint_Bye{#14{#6}}% + \POL_bBye #7\xint_Bye{#15{#7}}% + \POL_bBye #8\xint_Bye{#16{#8}}% + \POL_bBye #9\xint_Bye{#17{#9}}% + \POL_apply:x_iloop_a#1% +}% +\def\POL_apply:x_iloop_a#1#2.% +{% + \expandafter\POL_apply:x_iloop + \expandafter{\expandafter#1\the\numexpr\xint_c_viii+#1.}% +}% +%% +%% ADDITION +%% +\def\xintPolAdd #1% +{% + \expanded\expandafter\POL_add_in\romannumeral`&&@#1\xint: +}% +\def\POL_add_in #1\xint:#2% +{% + {% + \expandafter\POL_add_fork +% Fragile but this macro is not public anyhow and won't get arbitrary input +% At odds with systematic \xint: style further down + \romannumeral`&&@#2\xint_bye\xint_bye\xint_bye\xint_bye\empty + #1\xint_bye\xint_bye\xint_bye\xint_bye\empty + \empty + }% +}% +% Careful that first means "first here" i.e. the original second argument, +% and vice versa +\def\POL_add_fork #1#2\empty#3% +{% + \POL_PPfork + #1#3{\POL_add_a}% + #1P{\POL_add_second_is_scalar}% + #3P{\POL_add_first_is_scalar}% + PP{\POL_add_both_are_scalar}% + \krof #1#2\empty#3% +}% +\def\POL_add_first_is_scalar #1\xint_bye#2\empty#3.#4% +{% + #3.{\xintAdd{#1}{#4}}% +}% +\def\POL_add_second_is_scalar #1.#2#3\empty#4\xint_bye#5\empty\empty +{% + #1.{\xintAdd{#2}{#4}}#3% +}% +\def\POL_add_both_are_scalar #1\xint_bye#2\empty#3\xint_bye#4\empty\empty +{% + \xintAdd{#1}{#3}% +}% +\def\POL_add_a P#1.#2#3#4#5\empty P#6.#7#8#9% +{% + \expandafter\POL_add_b + \expanded\bgroup\unexpanded{#1.#6.}% + \xint_bye #2\POL_add_Eb\xint_bye + \xint_bye #7\POL_add_Fb\xint_bye {\xintAdd{#2}{#7}}% + \xint_bye #3\POL_add_Ec\xint_bye + \xint_bye #8\POL_add_Fc\xint_bye {\xintAdd{#3}{#8}}% + \xint_bye #4\POL_add_Ed\xint_bye + \xint_bye #9\POL_add_Fd\xint_bye {\xintAdd{#4}{#9}}% + \POL_add_A #5\empty +}% +\def\POL_add_b #1.#2.% +{% + \ifnum#1=#2 \expandafter\POL_add_c + \else + \ifnum#1>#2 P#1.\else P#2.\fi + \fi +}% +% No brace stripping possible, because constant polynomials are really +% represented by scalars in all those internal contexts, so real +% polynomials have at least two coefficients +\def\POL_add_c #1\empty +{% + \expandafter\POL_add_d + \romannumeral0\XINT_revwbr_loop {}% + #1\xint:\xint:\xint:\xint:% + \xint:\xint:\xint:\xint:\xint_bye + \xint_bye +}% +% Attention, reused in various other locations. It is all f-expandable. +\def\POL_add_d #1% +{% +% abuse of \XINT_Sgn internals compatible to #1 being \xint_bye + \if0\XINT_Sgn#1\xint: + \xint_dothis\POL_add_d + \fi + \xint_orthat{\POL_add_e {#1}}% +}% +\def\POL_add_e #1% +{% + \xint_bye#1\POL_add_e_zero\xint_bye \POL_add_f\empty{#1}% +}% +\def\POL_add_e_zero\xint_bye\POL_add_f\empty #1{0/1[0]}% +% #1 starts with \empty to avoid brace stripping. +\def\POL_add_f #1\xint_bye +{% + \expandafter\POL_add_g + \the\numexpr + \xintLength{#1}-\xint_c_ii\expandafter.% + \romannumeral0\expandafter + \XINT_revwbr_loop\expandafter {\expandafter}% + #1\xint:\xint:\xint:\xint:% + \xint:\xint:\xint:\xint:\xint_bye +}% +\def\POL_add_g #1.% +{% + \ifnum#1=\xint_c_\expandafter\POL_add_h\fi + P#1.% +}% +\def\POL_add_h P0.#1{#1}% +% Attention reused in \POL_mul_d and \POL_quorem_c +\def\POL_add_A #1#2#3#4#5\empty#6#7#8#9% +{% + \xint_bye #1\POL_add_Ea\xint_bye + \xint_bye #6\POL_add_Fa\xint_bye {\xintAdd{#1}{#6}}% + \xint_bye #2\POL_add_Eb\xint_bye + \xint_bye #7\POL_add_Fb\xint_bye {\xintAdd{#2}{#7}}% + \xint_bye #3\POL_add_Ec\xint_bye + \xint_bye #8\POL_add_Fc\xint_bye {\xintAdd{#3}{#8}}% + \xint_bye #4\POL_add_Ed\xint_bye + \xint_bye #9\POL_add_Fd\xint_bye {\xintAdd{#4}{#9}}% + \POL_add_A #5\empty +}% +\def\POL_add_Ea\xint_bye + \xint_bye #1\POL_add_Fa\xint_bye #2\xint_bye\xint_bye + \POL_add_Eb\xint_bye\xint_bye#3\POL_add_Fb\xint_bye #4\xint_bye\xint_bye + \POL_add_Ec\xint_bye\xint_bye#5\POL_add_Fc\xint_bye #6\xint_bye\xint_bye + \POL_add_Ed\xint_bye\xint_bye#7\POL_add_Fd\xint_bye #8% + \POL_add_A#9\empty +{% + \xint_bye #1\POL_add_G\xint_bye{#1}% + \xint_bye #3\POL_add_G\xint_bye{#3}% + \xint_bye #5\POL_add_G\xint_bye{#5}% + \xint_bye #7\POL_add_G\xint_bye{#7}% + \iffalse{\fi}% +}% +\def\POL_add_G#1\empty{\iffalse{\fi}}% +\def\POL_add_Fa\xint_bye #1% + \xint_bye #2\POL_add_Eb \xint_bye + \xint_bye\xint_bye\POL_add_Fb\xint_bye #3% + \xint_bye #4\POL_add_Ec \xint_bye + \xint_bye\xint_bye\POL_add_Fc\xint_bye #5% + \xint_bye #6\POL_add_Ed #7\POL_add_A + #8\empty#9\empty +{% + \expandafter\xint_bye\POL_secondofthree #1% + \POL_add_G\xint_bye{\POL_secondofthree#1}% + \xint_bye #2\POL_add_G\xint_bye{#2}% + \xint_bye #4\POL_add_G\xint_bye{#4}% + \xint_bye #6\POL_add_G\xint_bye{#6}% + \iffalse{\fi}#8\empty% +}% +\def\POL_add_Eb\xint_bye + \xint_bye #1\POL_add_Fb\xint_bye #2\xint_bye\xint_bye + \POL_add_Ec\xint_bye\xint_bye#3\POL_add_Fc\xint_bye #4\xint_bye\xint_bye + \POL_add_Ed\xint_bye\xint_bye#5\POL_add_Fd\xint_bye #6% + \POL_add_A#7\empty +{% + \xint_bye #1\POL_add_G\xint_bye{#1}% + \xint_bye #3\POL_add_G\xint_bye{#3}% + \xint_bye #5\POL_add_G\xint_bye{#5}% + \iffalse{\fi}% +}% +\def\POL_add_Fb\xint_bye #1% + \xint_bye #2\POL_add_Ec \xint_bye + \xint_bye\xint_bye\POL_add_Fc\xint_bye #3% + \xint_bye #4\POL_add_Ed #5\POL_add_A + #6\empty#7\empty +{% + \expandafter\xint_bye\POL_secondofthree #1% + \POL_add_G\xint_bye{\POL_secondofthree#1}% + \xint_bye #2\POL_add_G\xint_bye{#2}% + \xint_bye #4\POL_add_G\xint_bye{#4}% + \iffalse{\fi}#6\empty +}% +\def\POL_add_Ec\xint_bye + \xint_bye #1\POL_add_Fc\xint_bye #2\xint_bye\xint_bye + \POL_add_Ed\xint_bye\xint_bye#3\POL_add_Fd\xint_bye #4% + \POL_add_A#5\empty +{% + \xint_bye #1\POL_add_G\xint_bye{#1}% + \xint_bye #3\POL_add_G\xint_bye{#3}% + \iffalse{\fi}% +}% +\def\POL_add_Fc\xint_bye #1\xint_bye #2\POL_add_Ed #3\POL_add_A + #4\empty#5\empty +{% + \expandafter\xint_bye\POL_secondofthree #1% + \POL_add_G\xint_bye{\POL_secondofthree#1}% + \xint_bye #2\POL_add_G\xint_bye{#2}% + \iffalse{\fi}#4\empty +}% +\def\POL_add_Ed\xint_bye\xint_bye#1\POL_add_Fd\xint_bye#2\POL_add_A#3\empty +{% + \xint_bye #1\POL_add_G\xint_bye{#1}% + \iffalse{\fi}% +}% +\def\POL_add_Fd\xint_bye#1\POL_add_A #2\empty#3\empty +{% + \expandafter\xint_bye \POL_secondofthree #1% + \POL_add_G\xint_bye{\POL_secondofthree#1}% + \iffalse{\fi}#2\empty +}% +%% +%% OPPOSITE +%% +\def\xintPolOpp #1% +{% + \expanded{% + \expandafter\POL_opp_fork\romannumeral`&&@#1% + \xint_Bye\xint_Bye\xint_Bye\xint_Bye + \xint_Bye\xint_Bye\xint_Bye\xint_Bye\xint_bye + }% +}% +\def\POL_opp_fork #1% +{% + \if P#1\xint_dothis\POL_opp_a\fi + \xint_orthat\POL_opp_scalar #1% +}% +\def\POL_opp_scalar #1\xint_Bye#2\xint_bye +{% + \XINT_Opp #1% +}% +\def\POL_opp_a #1.% +{% + #1.\POL_apply:x_loop{\XINT_Opp}% +}% +%% +%% SUBTRACTION +%% +\def\xintPolSub #1% +{% + \expanded\expandafter\POL@sub\romannumeral`&&@#1\xint: +}% +\def\POL@sub #1\xint:#2% +{% + {% + \expandafter + \POL_add_fork\expanded{% + \expandafter\POL_opp_fork \romannumeral`&&@#2% + \xint_Bye\xint_Bye\xint_Bye\xint_Bye + \xint_Bye\xint_Bye\xint_Bye\xint_Bye\xint_bye + }% + \xint_bye\xint_bye\xint_bye\xint_bye\empty + #1\xint_bye\xint_bye\xint_bye\xint_bye\empty + \empty + }% +}% +%% +%% MULTIPLICATION +%% +\def\xintPolSqr #1% +{% + \expanded\expandafter\POL_sqr_in\romannumeral`&&@#1\xint: +}% +\def\POL_sqr_in #1\xint: +{% + {% + \expandafter\POL_mul_fork + #1\xint_bye + #1\xint_Bye\xint_Bye\xint_Bye\xint_Bye + \xint_Bye\xint_Bye\xint_Bye\xint_Bye\xint_bye + }% +}% +% +\def\xintPolMul #1% +{% + \expanded\expandafter\POL_mul_in\romannumeral`&&@#1\xint: +}% +\def\POL_mul_in #1\xint:#2% +{% + {% + \expandafter\POL_mul_fork + \romannumeral`&&@#2\xint_bye + #1\xint_Bye\xint_Bye\xint_Bye\xint_Bye + \xint_Bye\xint_Bye\xint_Bye\xint_Bye\xint_bye + }% +}% +\def\POL_mul_fork #1#2\xint_bye#3% +{% + \POL_PPfork + #1#3{\POL_mul_a}% + #1P{\POL_mul_second_is_scalar}% + #3P{\POL_mul_first_is_scalar}% + PP{\POL_mul_both_are_scalar}% + \krof #1#2\xint_bye#3% +}% +\def\POL_mul_both_are_scalar #1\xint_bye#2\xint_Bye#3\xint_bye +{% + \xintMul{#1}{#2}% +}% +\def\POL_mul_second_is_scalar #1\xint_bye#2\xint_Bye +{% + \POL_mul_first_is_scalar #2\xint_bye#1\xint_Bye +}% +\def\POL_mul_first_is_scalar #1% +{% + \xint_gob_til_zero#1\POL_mul_zero0\POL_mul_scalar #1% +}% +\def\POL_mul_zero0\POL_mul_scalar #1\xint_bye#2\xint_bye{0/1[0]}% +\def\POL_mul_scalar #1\xint_bye P#2.% +{% + P#2.\POL_apply:x_loop{\xintMul{#1}}% +}% +\def\POL_mul_a P#1.#2#3P#4.#5\xint_bye +{% + P\the\numexpr#1+#4.% + \expandafter\POL_mul_b + \expanded{\POL_apply:x_loop{\xintMul{#2}}#5\xint_bye}% + \xint: + #3\empty#5\xint_bye +}% +\def\POL_mul_b #1{{#1}\POL_mul_c\empty}% +\def\POL_mul_c #1\xint:#2% +{% + \xint_bye#2\POL_mul_E\xint_bye + \expandafter\POL_mul_d\expandafter{#1}{#2}% +}% +\def\POL_mul_d #1#2#3\empty#4\xint_bye +{% + \expandafter\POL_mul_b + \expanded\bgroup + \expandafter\POL_add_A + \expanded{\POL_apply:x_loop{\xintMul{#2}}#4\xint_bye}% + \xint_bye\xint_bye\xint_bye\xint_bye\empty + #1\xint_bye\xint_bye\xint_bye\xint_bye\empty + \xint: + #3\empty#4\xint_bye +}% +\def\POL_mul_E\xint_bye + \expandafter\POL_mul_d\expandafter#1#2\xint_bye +% This #1 starts with \empty +{% + #1% +}% +%% +%% POWERS +%% +\def\xintPolPow #1% +{% + \expanded\expandafter\POL_pow_in\romannumeral`&&@#1\xint: +}% +\def\POL_pow_in #1\xint:#2% +{% + {% + \expandafter\POL_pow_fork\the\numexpr \xintNum{#2}.#1\empty + }% +}% +\def\POL_pow_fork #1#2.% +{% + \xint_UDzerominusfork + #1-\POL_pow_zero + 0#1\POL_pow_neg + 0-\POL_pow_pos + \krof + #1#2.% +}% +\def\POL_pow_zero #1\empty{1/1[0]}% +\def\POL_pow_neg #1.#2% +{% + \POL_Pfork #2{\POL_pow_neg_pol}P{\POL_pow_scalar}\krof #1.#2% +}% +\def\POL_pow_pos #1.#2% +{% + \POL_Pfork #2{\POL_pow_a}P{\POL_pow_scalar}\krof #1.#2% +}% +\def\POL_pow_scalar #1.#2\empty +{% + \xintPow{#2}{#1}% +}% +\def\POL_pow_neg_pol #1.#2\empty +{% + \romannumeral0\XINT_signalcondition{InvalidOperation}% + {Not supported: polynomial to negative power #1}{}{1/1[0]}% +}% +\def\POL_pow_a #1.% +{% +% trailing \empty will disappear in expanded context (old comment) + \ifnum#1=\xint_c_i\xint_afterfi\xint_gob_til_dot\fi + \expandafter\POL_pow_b \the\numexpr#1-\xint_c_i.% +}% +\def\POL_pow_b #1.% +{% + \ifodd #1 \xint_dothis{\expandafter\POL_pow_even}\fi + \xint_orthat{\expandafter\POL_pow_odd}\the\numexpr#1/\xint_c_ii.% +}% +\def\POL_pow_even #1.#2\empty +{% + \expandafter\POL_pow_a + \expanded{\unexpanded{#1.}% + \POL_mul_a#2\xint_bye + #2\xint_Bye\xint_Bye\xint_Bye\xint_Bye + \xint_Bye\xint_Bye\xint_Bye\xint_Bye\xint_bye + }\empty +}% +\def\POL_pow_odd #1.#2\empty +{% + \expanded + {\unexpanded{\POL_mul_a #2\xint_bye}% + \expandafter\POL_pow_a + \expanded{\unexpanded{#1.}% + \POL_mul_a#2\xint_bye + #2\xint_Bye\xint_Bye\xint_Bye\xint_Bye + \xint_Bye\xint_Bye\xint_Bye\xint_Bye\xint_bye + }% + \empty + }% + \xint_Bye\xint_Bye\xint_Bye\xint_Bye + \xint_Bye\xint_Bye\xint_Bye\xint_Bye\xint_bye +}% +%% +%% DIVISION +%% +% +% / is deprecated for polynomial Euclidean division +% +\def\xintPolQuo #1% +{% + \romannumeral0\expandafter\xint_stop_atfirstoftwo + \expanded\expandafter\POL_quorem_in\romannumeral`&&@#1\xint: +}% +% there is no operator, for lack of obvious best notation +\def\xintPolRem #1% +{% + \romannumeral0\expandafter\xint_stop_atsecondoftwo + \expanded\expandafter\POL_quorem_in\romannumeral`&&@#1\xint: +}% +% // +\def\xintPolDivModQ #1% +{% + \romannumeral0\expandafter\xint_stop_atfirstoftwo + \expanded\expandafter\POL_divmod_in\romannumeral`&&@#1\xint: +}% +% /: +\def\xintPolDivModR #1% +{% + \romannumeral0\expandafter\xint_stop_atsecondoftwo + \expanded\expandafter\POL_divmod_in\romannumeral`&&@#1\xint: +}% +% "divmod" will apply coefficient per coefficient when divisor is scalar +% I have found it convenient to treat constant polynomials +% as really being scalars. But I need perhaps to think more about it. +\def\xintPolDivMod #1% +{% + \expanded\expandafter\POL_divmod_in\romannumeral`&&@#1\xint: +}% +% the euclidean division +\def\xintPolQuoRem #1% +{% + \expanded\expandafter\POL_quorem_in\romannumeral`&&@#1\xint: +}% +\def\POL_quorem_in #1\xint:#2% +{% + {% + \expandafter\POL_quorem_fork + \romannumeral`&&@#2\xint_bye#1\xint_bye + }% +}% +% the overloading of divmod which does euclidean division if divisor is not a scalar +\def\POL_divmod_in #1\xint:#2% +{% + {% + \expandafter\POL_divmod_fork + \romannumeral`&&@#2\xint_bye#1\xint_bye + }% +}% +% "first" and "second" refer to the actual positions, permuted compared +% to original arguments +\def\POL_quorem_fork #1#2\xint_bye#3% +{% + \POL_PPfork + #1#3{\POL_quorem_a}% both polynomials -> {eucl. quotient}{remainder} + #1P{\POL_quorem_second_is_scalar}% -> {zero quotient}{scalar} + #3P{\POL_quorem_first_is_scalar}% -> {polynomial/scalar}{zero} + PP{\POL_quorem_both_are_scalar}% -> {scalar/scalar}{zero} + \krof #1#2\xint_bye#3% +}% +\def\POL_quorem_first_is_scalar #1\xint_bye#2\xint_bye +{% + {\expandafter\POL_quorem_first_is_scalar_i\expandafter + {\romannumeral0\xintinv{#1}}% + #2\xint_Bye\xint_Bye\xint_Bye\xint_Bye + \xint_Bye\xint_Bye\xint_Bye\xint_Bye\xint_bye}{0/1[0]}% +}% +\def\POL_quorem_first_is_scalar_i #1#2.% +{% + #2.\POL_apply:x_loop{\xintMul{#1}}% +}% +% #2 was initial first argument and is scalar +\def\POL_quorem_second_is_scalar #1\xint_bye#2\xint_bye +{% + {0/1[0]}{#2}% +}% +\def\POL_quorem_both_are_scalar #1\xint_bye#2\xint_bye +{% + {\xintDiv{#2}{#1}}{0/1[0]}% +}% +% attention that "first", "second" refer to the actual arguments positions +\def\POL_divmod_fork #1#2\xint_bye#3% +{% + \POL_PPfork + #1#3{\POL_quorem_a}% both polynomials -> {eucl. quotient}{remainder} + #1P{\POL_quorem_second_is_scalar}% -> {zero quotient}{scalar} + #3P{\POL_divmod_first_is_scalar}% -> {per coeff//scalar}{per coeff/:scalar} + PP{\POL_divmod_both_are_scalar}% -> {s1//s2}{s1/:s2} + \krof #1#2\xint_bye#3% +}% +\def\POL_divmod_both_are_scalar #1\xint_bye#2\xint_bye +{% + \xintDivMod{#2}{#1}% +}% +\def\POL_divmod_first_is_scalar #1\xint_bye #2.#3\xint_bye +{% + \expandafter\POL_divmod_first_is_scalar_a + \expanded{\unexpanded{{#1}}\expandafter}% + \romannumeral0\XINT_revwbr_loop {}% + #3\xint:\xint:\xint:\xint:% + \xint:\xint:\xint:\xint:\xint_bye + \xint_Bye\xint_Bye\xint_Bye\xint_Bye + \xint_Bye\xint_Bye\xint_Bye\xint_Bye\xint_bye + \iffalse{\fi}% + \xint: +}% +\long\def\POL_exchange_args#1#2#3{#1{#3}{#2}}% +\def\POL_divmod_first_is_scalar_a #1% +{% + \expandafter\POL_divmod_first_is_scalar_b + \expanded\bgroup + \POL_apply:x_loop{\POL_exchange_args\xintDivMod{#1}}% +}% +% attention re-use of \POL_add_d +\def\POL_divmod_first_is_scalar_b #1\xint: +{% + {\expandafter\POL_add_d\expanded{% + \POL_apply:x_loop{\expandafter\xint_firstoftwo\xint_firstofone}% + #1\xint_Bye\xint_Bye\xint_Bye\xint_Bye + \xint_Bye\xint_Bye\xint_Bye\xint_Bye\xint_bye}\xint_bye}% + {\expandafter\POL_add_d\expanded{% + \POL_apply:x_loop{\expandafter\xint_secondoftwo\xint_firstofone}% + #1\xint_Bye\xint_Bye\xint_Bye\xint_Bye + \xint_Bye\xint_Bye\xint_Bye\xint_Bye\xint_bye}\xint_bye}% +}% +\def\POL_quorem_a P#1.#2\xint_bye P#3.#4\xint_bye +{% + \ifnum#1>#3 \xint_dothis{\POL_quorem_easy #3.}\fi + \xint_orthat + {\expandafter\POL_quorem_EQuo + \expanded\bgroup + \expandafter\POL_quorem_b\the\numexpr#3-#1\expandafter.% + \expanded\bgroup + \xintRevWithBraces + }% + {#2}% + \noexpand\xint_Bye + \xint: + \expandafter\POL_placemark_loop + \the\numexpr#1-\xint_c_vii\expandafter.% + \romannumeral0\xintrevwithbraces{#4}% +% This added {1} is related to termination clean-up (a bit annoying) process + {1}% + \the\numexpr#3-#1.% + \iffalse{\fi}% +}% +\def\POL_quorem_easy #1.#2\xintrevwithbraces#3#4.#5#6% +{% + {0/1[0]}{P#1.#3}% +}% +\def\POL_placemark_loop #1#2.% +{% + \xint_gob_til_minus#1\POL_placemark_loop_end-% + \expandafter\POL_placemark_step\the\numexpr#1#2-\xint_c_viii.% +}% +\def\POL_placemark_step #1.#2#3#4#5#6#7#8#9% +{% + {#2}{#3}{#4}{#5}{#6}{#7}{#8}{#9}\POL_placemark_loop#1.% +}% +\def\POL_placemark_loop_end-% + \expandafter\POL_placemark_step\the\numexpr-#1-\xint_c_viii.% +{% + \csname POL_placemark_end#1\endcsname +}% +\expandafter\def\csname POL_placemark_end1\endcsname + #1#2#3#4#5#6#7{{#1}{#2}{#3}{#4}{#5}{#6}{#7}\noexpand\xint_bye\xint:}% +\expandafter\def\csname POL_placemark_end2\endcsname + #1#2#3#4#5#6{{#1}{#2}{#3}{#4}{#5}{#6}\noexpand\xint_bye\xint:}% +\expandafter\def\csname POL_placemark_end3\endcsname + #1#2#3#4#5{{#1}{#2}{#3}{#4}{#5}\noexpand\xint_bye\xint:}% +\expandafter\def\csname POL_placemark_end4\endcsname + #1#2#3#4{{#1}{#2}{#3}{#4}\noexpand\xint_bye\xint:}% +\expandafter\def\csname POL_placemark_end5\endcsname + #1#2#3{{#1}{#2}{#3}\noexpand\xint_bye\xint:}% +\expandafter\def\csname POL_placemark_end6\endcsname + #1#2{{#1}{#2}\noexpand\xint_bye\xint:}% +\expandafter\def\csname POL_placemark_end7\endcsname + #1{{#1}\noexpand\xint_bye\xint:}% +\expandafter\def\csname POL_placemark_end8\endcsname + {\noexpand\xint_bye\xint:}% +\def\POL_quorem_b #1.#2#3\xint:#4#5\xint:#6% +{% +% \xintDiv FG computes F/G + \expandafter\POL_quorem_c\romannumeral0\xintdiv{\XINT_Opp#4}{#2}.% + #1.{#2}#3\xint: +% there is already \xint_Bye at ends of #3 + #3\xint_Bye\xint_Bye\xint_Bye + \xint_Bye\xint_Bye\xint_Bye\xint_Bye\xint_bye +% this terminates the \expanded from \POL_apply:x_loop + \iffalse{\fi}% + \xint_bye\xint_bye\xint_bye\xint_bye\empty + #5\xint_bye\xint_bye\xint_bye\empty +% a \iffalse{\fi} will get inserted by \POL_add_A here + {#6}\xint_bye\xint: +}% +\def\POL_quorem_c #1.#2.#3\xint:% +{% + {\XINT_Opp#1}% + \expandafter\POL_quorem_d\the\numexpr#2-\xint_c_i\expandafter.% + \expanded\bgroup + \unexpanded{#3}\xint: + \expandafter\POL_add_A + \expanded\bgroup + \POL_apply:x_loop{\xintMul{#1}}% +}% +\def\POL_quorem_d #1#2.% +{% + \xint_gob_til_minus#1\POL_quorem_E-% + \POL_quorem_b #1#2.% +}% +\def\POL_quorem_E-\POL_quorem_b-1.#1\xint:#2\xint_bye\xint:#3.% +{% +% this terminates the \POL_quorem_a \expanded + \iffalse{\fi}\xint:#3.% +% recycling some termination code from addition + {\expandafter\POL_quorem_ERem_fix\expanded{\POL_add_d#2\xint_bye}}% +}% +\def\POL_quorem_ERem_fix #1% +{% + \if P#1\expandafter\POL_quorem_ERem_fix_a\fi 0/1[0]% +}% +\def\POL_quorem_ERem_fix_a 0/1[0]#1.#2% +{% + \ifcase #1 + \or + \expandafter\xint_firstofone + \else + P\the\numexpr#1-\xint_c_i.% + \fi +}% +\def\POL_quorem_EQuo#1\xint:#2.% +{% + {\ifnum#2=\xint_c_ + #1% + \else + P#2.\romannumeral0\XINT_revwbr_loop {}% + #1\xint:\xint:\xint:\xint:% + \xint:\xint:\xint:\xint:\xint_bye + \fi}% +}% +\def\xintPolPRem #1% +{% + \expanded\expandafter\POL_prem_in\romannumeral`&&@#1\xint: +}% +\def\POL_prem_in #1\xint:#2% +{% + \bgroup + \expandafter\POL_prem_fork + \romannumeral`&&@#2\xint:#1\xint: + \POL_prem_end +}% +\def\POL_prem_fork #1#2\xint:#3% +{% + \POL_PPfork + #1#3{\POL_prem_a}% both polynomials + #1P{\POL_prem_second_is_scalar}% -> scalar + #3P{\POL_prem_first_is_scalar}% -> zero + PP{\POL_prem_both_are_scalar}% -> zero + \krof #1#2\xint:#3% +}% +\def\POL_prem_first_is_scalar #1\xint:#2\xint:\POL_prem_end +{% + \iffalse{\fi}{1/1[0]}{0/1[0]}% +}% +\def\POL_prem_second_is_scalar #1\xint:#2\xint:\POL_prem_end +{% + \iffalse{\fi}{1/1[0]}{#2}% +}% +\def\POL_prem_both_are_scalar #1\xint:#2\xint:\POL_prem_end +{% + \iffalse{\fi}{1/1[0]}{0/1[0]}% +}% +\def\POL_prem_a P#1.#2\xint: P#3.#4\xint: +{% + \ifnum#1>#3 \xint_dothis{\POL_prem_easy #3.}\fi + \xint_orthat + {\expandafter\POL_prem_b\the\numexpr#3-#1\expandafter.% + \expanded\bgroup + \xintRevWithBraces + }% + {#2}% + \noexpand\xint_Bye + \xint: + \expandafter\POL_placeBye_loop + \the\numexpr#1-\xint_c_vii\expandafter.% + \romannumeral0\xintrevwithbraces{#4}% + {1/1[0]}% + \iffalse{\fi}% +}% +\def\POL_prem_easy #1.#2\xintrevwithbraces#3#4\POL_prem_end +{% + \iffalse{\fi}{1/1[0]}{P#1.#3}% +}% +\def\POL_placeBye_loop #1#2.% +{% + \xint_gob_til_minus#1\POL_placeBye_loop_end-% + \expandafter\POL_placeBye_step\the\numexpr#1#2-\xint_c_viii.% +}% +\def\POL_placeBye_step #1.#2#3#4#5#6#7#8#9% +{% + {#2}{#3}{#4}{#5}{#6}{#7}{#8}{#9}\POL_placeBye_loop#1.% +}% +\def\POL_placeBye_loop_end-% + \expandafter\POL_placeBye_step\the\numexpr-#1-\xint_c_viii.% +{% + \csname POL_placeBye_end#1\endcsname +}% +\expandafter\def\csname POL_placeBye_end1\endcsname + #1#2#3#4#5#6#7{{#1}{#2}{#3}{#4}{#5}{#6}{#7}\noexpand\xint_Bye\xint:{1}}% +\expandafter\def\csname POL_placeBye_end2\endcsname + #1#2#3#4#5#6{{#1}{#2}{#3}{#4}{#5}{#6}\noexpand\xint_Bye\xint:{1}}% +\expandafter\def\csname POL_placeBye_end3\endcsname + #1#2#3#4#5{{#1}{#2}{#3}{#4}{#5}\noexpand\xint_Bye\xint:{1}}% +\expandafter\def\csname POL_placeBye_end4\endcsname + #1#2#3#4{{#1}{#2}{#3}{#4}\noexpand\xint_Bye\xint:{1}}% +\expandafter\def\csname POL_placeBye_end5\endcsname + #1#2#3{{#1}{#2}{#3}\noexpand\xint_Bye\xint:{1}}% +\expandafter\def\csname POL_placeBye_end6\endcsname + #1#2{{#1}{#2}\noexpand\xint_Bye\xint:{1}}% +\expandafter\def\csname POL_placeBye_end7\endcsname + #1{{#1}\noexpand\xint_Bye\xint:{1}}% +\expandafter\def\csname POL_placeBye_end8\endcsname + {\noexpand\xint_Bye\xint:{1}}% +\def\POL_prem_b_skip#1#2\unexpanded#3#4#5\xint_Bye#6\xint:#7#8#9% +{% + \iffalse{\fi\expandafter}\xint_gobble_i#5#1% +}% +\def\POL_prem_b #1.#2#3\xint:#4#5\xint:#6#7% +{% + \expandafter\POL_prem_c\the\numexpr#1-\xint_c_i\expandafter.% + \expanded\bgroup + \unexpanded{{#2}#3}\xint: + \if0\XINT_Sgn#4\xint:\xint_afterfi + {\expandafter\POL_prem_b_skip\expandafter + {\expandafter{\romannumeral0\xintmul{#6}{#7}}\xint_Bye\xint:{#6}}% + }% + \fi + \expandafter\POL_add_A + \expanded\bgroup + \expanded{\noexpand\POL_apply:x_loop{\noexpand\xintMul + {\if1\XINT_Sgn#2\xint:\expandafter\XINT_Opp\fi#4}}}% +% there is already \xint_Bye at ends of #3 + #3\xint_Bye\xint_Bye\xint_Bye + \xint_Bye\xint_Bye\xint_Bye\xint_Bye\xint_bye +% separator for \POL_add_A + \unexpanded{\xint_bye\xint_bye\xint_bye\xint_bye\empty}% +% there is already \xint_Bye at ends of #5 + \expanded{\noexpand\POL_apply:x_loop{\noexpand\xintMul{\XINT_Abs#2}}}% + #5\xint_Bye\xint_Bye\xint_Bye + \xint_Bye\xint_Bye\xint_Bye\xint_Bye\xint_bye + \unexpanded{\xint_bye\xint_bye\xint_bye\xint_bye\empty}% +% a \iffalse{\fi} will get inserted by \POL_add_A exit routine and if will +% terminate the \expanded triggered here after \POL_prem_c +% what is next will have already have been expanded + {\xintMul{\xintMul{\XINT_Abs#2}{#6}}{#7}}\noexpand\xint_Bye\xint: + {\xintMul{\XINT_Abs#2}{#6}}% +% This terminates the \expanded following \POL_add_A + \iffalse{\fi}% +}% +\def\POL_prem_c #1% +{% + \xint_gob_til_minus#1\POL_prem_E_-\POL_prem_b#1% +}% +% attention that #2 here has a two dummies at end +% advantage is that \POL_add_a will always think it is non scalar +\def\POL_prem_E_-\POL_prem_b-1.#1\xint:#2\xint_Bye\xint:#3% +{% + \expandafter\POL_prem_E\expanded{\POL_add_d#2\xint_bye}% +}% +\def\POL_prem_E #1% +{% + \if P#1\expandafter\POL_prem_E_i + \else\expandafter\POL_prem_E_zero + \fi #1% +}% +\def\POL_prem_E_zero #1\POL_prem_end{\iffalse{\fi}{#1}{0/1[0]}}% +\def\POL_prem_E_i P#1.% +{% + \ifnum #1>\xint_c_i\POL_prem_E_ii#1.\fi + \POL_prem_E_iii% +}% +\def\POL_prem_E_iii#1\POL_prem_end{\iffalse{\fi}#1}% +\def\POL_prem_E_ii#1.#2\POL_prem_E_iii#3% + {#2{#3}{P\the\numexpr#1-\xint_c_i\iffalse}\fi.}% +\def\POL_prem_end{\iffalse{{\fi}}}% +%% +%% SUPPORT FOR FUNCTIONAL INTERFACE +%% +% should I do a qpol([]) ?, i.e. without testing for leading zeros, hence +% would be faster ? but advantage would arise only for very high degree +% pol([]) this one checks for zeros in the right most coeffs +\def\xintPolPol#1{\romannumeral`&&@\expandafter\POL_add_d + \romannumeral0\expandafter\XINT_revwbr_loop\expandafter + {\expandafter}% + \romannumeral`&&@#1\xint:\xint:\xint:\xint: + \xint:\xint:\xint:\xint:\xint_bye + \xint_bye +}% +% attention to not overwrite macro names (there is a legacy \PolEvalAt) +\def\xintPolEvalAt#1#2% +{% +% generally, #2 will be scalar, but we allow also a polynomial here +% should I test for #2 being the monomial, hence handle it very quickly? + \romannumeral`&&@\expandafter\POL_evalat_in\romannumeral`&&@#2\xint: + #1\xint:\xint:\xint:\xint: + \xint:\xint:\xint:\xint:\xint_bye\xint: +}% +\def\POL_evalat_in #1\xint: +{% + \expandafter\POL_evalat_fork\expanded{\unexpanded{#1\xint:}\expandafter}% + \romannumeral`&&@% +}% +\def\POL_evalat_fork #1\xint:#2% +{% + \POL_Pfork + #2{\POL_evalat_pol}% + P{\POL_evalat_cst}% + \krof #1\xint:#2% +}% +\def\POL_evalat_cst #1\xint: #2\xint:#3\xint_bye\xint:{#2}% +\def\POL_evalat_pol #1\xint: P#2.% +{% + \expanded{\unexpanded{\POL_evalat_a#1\xint:}\expandafter}% + \romannumeral0\XINT_revwbr_loop{}% +}% +\def\POL_evalat_a#1\xint:#2% +{% + \POL_evalat_loop#2\xint:#1\xint: +}% +\def\POL_evalat_loop#1\xint:#2\xint:#3% +{% + \xint_gob_til_xint:#3\POL_evalat_E\xint: +% I have dropped here my old strict \xintFoo = \romannumeral0\xintfoo style +% ATTENTION! We must allow evaluating at a polynomial expression + \expandafter\POL_evalat_loop + \romannumeral`&&@\xintPolAdd{#3}{\xintPolMul{#2}{#1}}\xint:#2\xint: +}% +\def\POL_evalat_E\xint:\expandafter\POL_evalat_loop + \romannumeral`&&@\xintPolAdd #1#2\xint:#3\xint: +{% + \xint_thirdofthree#2% +}% +% +\def\xintPolDeg#1% +{% + \romannumeral`&&@\expandafter\POL_deg_fork\romannumeral`&&@#1\xint: +}% +\def\POL_deg_fork #1% +{% + \POL_zeroPfork + #1P{\POL_deg_zero}% + 0#1{\POL_deg_pol}% + 0P{\POL_deg_cst}% + \krof #1% +}% +% usual hesitations about using or not raw frac format +\def\POL_deg_zero#1\xint:{-1}% +\def\POL_deg_cst #1\xint:{0}% +\def\POL_deg_pol P#1.#2\xint:{#1}% +% +\def\xintPolCoeffs#1% +{% + \romannumeral`&&@\expandafter\POL_coeffs_fork\romannumeral`&&@#1\xint: +}% +\def\POL_coeffs_fork #1% +{% + \POL_Pfork + #1\POL_coeffs_pol + P\POL_coeffs_cst + \krof #1% +}% +% usual hesitations about using or not raw frac format +\def\POL_coeffs_cst #1\xint:{{#1}}% +% no brace stripping possible, at least two coefficients +% annoying that we had to put this delimiter \xint: +\def\POL_coeffs_pol P#1.#2\xint:{#2}% +% +\def\xintPolCoeff#1#2% +{% + \romannumeral`&&@\expandafter\POL_coeff_fork + \the\numexpr\xintNum{#2}\expandafter.% + \romannumeral`&&@#1\xint: +}% +\def\POL_coeff_fork #1.#2% +{% + \POL_Pfork + #2\POL_coeff_pol + P\POL_coeff_cst + \krof #1.#2% +}% +\def\POL_coeff_cst#1% +{% + \xint_UDzerofork + #1\POL_coeff_itself + 0\POL_coeff_zero + \krof #1% +}% +\def\POL_coeff_itself#1.#2\xint:{#2}% +\def\POL_coeff_zero#1\xint:{0/1[0]}% +\def\POL_coeff_pol #1.P#2.% +{% + \ifnum#1<\xint_c_\xint_dothis\POL_coeff_zero\fi + \ifnum#1>#2 \xint_dothis\POL_coeff_zero\fi + \xint_orthat\POL_coeff_a{#1}% +}% +\def\POL_coeff_a#1{\expandafter\POL_coeff_b\romannumeral\xintgobble{#1}}% +\def\POL_coeff_b#1#2\xint:{#1}% +% +\def\xintPolLCoeff#1% +{% + \romannumeral`&&@\expandafter\POL_lcoeff_fork + \romannumeral`&&@#1\xint: +}% +\def\POL_lcoeff_fork #1% +{% + \POL_Pfork + #1\POL_lcoeff_pol + P\POL_lcoeff_cst + \krof #1% +}% +\def\POL_lcoeff_cst#1\xint:{#1}% +\def\POL_lcoeff_pol P#1.% +{% + \expandafter\POL_lcoeff_a\romannumeral\xintgobble{#1}% +}% +\def\POL_lcoeff_a#1\xint:{#1}% +% +\def\xintPolMonicPart#1% +{% + \romannumeral`&&@\expandafter\POL_monicpart_fork + \romannumeral`&&@#1\xint: +}% +\def\POL_monicpart_fork #1% +{% + \POL_Pfork + #1\POL_monicpart_pol + P\POL_monicpart_cst + \krof #1% +}% +% monicpart(0) must be 0 to avoid breaking algorithms +\def\POL_monicpart_cst#1#2\xint:{\if#10\xint_dothis0\fi\xint_orthat1/1[0]}% +\def\POL_monicpart_pol P#1.#2\xint:% +{% + \expanded{% + P#1.% + \expandafter\POL_monicpart_a\romannumeral\xintgobble{#1}% + #2#2\xint_Bye\xint_Bye\xint_Bye\xint_Bye + \xint_Bye\xint_Bye\xint_Bye\xint_Bye\xint_bye + }% +}% +\def\POL@DivByFirstAndIrrAndREZ#1#2{\xintREZ{\xintIrr{\xintDiv{#2}{#1}}}}% +\def\POL_monicpart_a#1% +{% + \POL_apply:x_loop{\POL@DivByFirstAndIrrAndREZ{#1}}% +}% +% +\def\xintPolIContent#1% +{% + \romannumeral`&&@\expandafter\POL_icontent_fork + \romannumeral`&&@#1^% +}% +\def\POL_icontent_fork #1% +{% + \POL_Pfork + #1\POL_icontent_pol + P\POL_icontent_cst + \krof #1% +}% +\def\POL_icontent_cst #1^{\xintIrr{\xintAbs{#1}}[0]}% +\def\POL_icontent_pol P#1.% +{% +% 1.4d xintfrac \XINT_fgcdof much saner than 1.4 version ! +% \XINT_fgcd_out does \xintIrr + \expandafter\XINT_fgcd_out\romannumeral0\XINT_fgcdof +}% +% +\def\xintPolPrimPart#1% +{% + \romannumeral`&&@\expandafter\POL_primpart_fork + \romannumeral`&&@#1\xint: +}% +\def\POL_primpart_fork #1% +{% + \POL_Pfork + #1\POL_primpart_pol + P\POL_primpart_cst + \krof #1% +}% +\def\POL_primpart_cst#1#2\xint:{\if#10\xint_dothis0\fi\xint_orthat1/1[0]}% +\def\POL_primpart_pol P#1.#2\xint:% +{% + \expanded{% + P#1.\expandafter\POL_primpart_a + \romannumeral0\expandafter\XINT_fgcd_out + \romannumeral0\XINT_fgcdof#2^\xint: + #2\xint_Bye\xint_Bye\xint_Bye\xint_Bye + \xint_Bye\xint_Bye\xint_Bye\xint_Bye\xint_bye + }% +}% +% cf legacy \POL@makeprim@macro +\def\POL@DivByFirstAndNumAndREZ#1#2{\xintREZ{\xintNum{\xintDiv{#2}{#1}}}}% +\def\POL_primpart_a#1\xint:{\POL_apply:x_loop{\POL@DivByFirstAndNumAndREZ{#1}}}% +% +\def\xintPolRedCoeffs#1% +{% + \romannumeral`&&@\expandafter\POL_redcoeffs_fork + \romannumeral`&&@#1\xint: +}% +\def\POL_redcoeffs_fork #1% +{% + \POL_Pfork + #1\POL_redcoeffs_pol + P\POL_redcoeffs_cst + \krof #1% +}% +\def\POL_redcoeffs_cst#1\xint:{\xintIrr{#1}[0]}% +\def\POL_redcoeffs_pol P#1.#2\xint:% +{% + \expanded{% + P#1.\POL_apply:x_loop\POL@xintIrr + #2\xint_Bye\xint_Bye\xint_Bye\xint_Bye + \xint_Bye\xint_Bye\xint_Bye\xint_Bye\xint_bye + }% +}% +% +\def\xintPolSRedCoeffs#1% +{% + \romannumeral`&&@\expandafter\POL_sredcoeffs_fork + \romannumeral`&&@#1\xint: +}% +\def\POL_sredcoeffs_fork #1% +{% + \POL_Pfork + #1\POL_sredcoeffs_pol + P\POL_sredcoeffs_cst + \krof #1% +}% +\def\POL_sredcoeffs_cst#1\xint:{\xintREZ{\xintIrr{#1}[0]}}% +\def\POL_sredcoeffs_pol P#1.#2\xint:% +{% + \expanded{% + P#1.\POL_apply:x_loop\POL@xintIrrAndREZ + #2\xint_Bye\xint_Bye\xint_Bye\xint_Bye + \xint_Bye\xint_Bye\xint_Bye\xint_Bye\xint_bye + }% +}% +\def\POL@xintIrrAndREZ#1{\xintREZ{\xintIrr{#1}[0]}}% +% +\def\xintPolDiffOne#1% +{% + \romannumeral`&&@\expandafter\POL_diffone_fork + \romannumeral`&&@#1\xint: +}% +\def\POL_diffone_fork #1% +{% + \POL_Pfork + #1\POL_diffone_pol + P\POL_diffone_cst + \krof #1% +}% +\def\POL_diffone_cst#1\xint:{0/1[0]}% +\def\POL_diffone_pol P#1.#2#3\xint:% +{% + \expanded{% + \ifnum#1=\xint_c_i #3% + \else + P\the\numexpr#1-\xint_c_i.% + \POL_apply:x_iloop{\POL_diffone_diff1.}% + #3\xint_Bye\xint_Bye\xint_Bye\xint_Bye + \xint_Bye\xint_Bye\xint_Bye\xint_Bye\xint_bye + \fi + }% +}% +\def\POL_diffone_diff#1.#2#3{\xintMul{#1+#2}{#3}}% +% +\def\xintPolAntiOne#1% +{% + \romannumeral`&&@\expandafter\POL_antione_fork + \romannumeral`&&@#1\xint: +}% +\def\POL_antione_fork #1% +{% + \POL_Pfork + #1\POL_antione_pol + P\POL_antione_cst + \krof #1% +}% +\def\POL_antione_cst#1% +{% + \xint_gob_til_zero#1\POL_antione_zero0\POL_antione_cst_i#1% +}% +\def\POL_antione_cst_i#1\xint:{P1.{0/1[O]}{#1}}% +\def\POL_antione_zero#1\xint:{0/1[0]}% +\def\POL_antione_pol P#1.#2\xint:% +{% + \expanded{% + P\the\numexpr#1+\xint_c_i.{0/1[0]}% + \POL_apply:x_iloop{\POL_antione_anti1.}% + #2\xint_Bye\xint_Bye\xint_Bye\xint_Bye + \xint_Bye\xint_Bye\xint_Bye\xint_Bye\xint_bye + }% +}% +\def\POL_antione_anti#1.#2#3{\xintDiv{#3}{#1+#2}}% +% +% #2 can be a polynomial +\def\xintPolIntFrom#1%#2% +{% + \romannumeral`&&@\expandafter\POL_intfrom_a\expandafter + {\romannumeral`&&@\xintPolAntiOne{#1}}% +}% +\def\POL_intfrom_a #1#2% +{% + \xintPolSub{#1}{\xintPolEvalAt{#1}{#2}}% +}% +% +\def\xintPolIntegral#1#2% +{% + \romannumeral`&&@\expandafter\POL_integral_a\expanded + {\xintPolAntiOne{#1}\xint:#2\xint:}% +}% +\def\POL_integral_a #1\xint:#2#3\xint: +{% + \xintPolSub{\xintPolEvalAt{#1}{#3}}{\xintPolEvalAt{#1}{#2}}% +}% +% +\def\xintPolDiffTwo#1% +{% + \romannumeral`&&@\expandafter\POL_difftwo_fork + \romannumeral`&&@#1\xint: +}% +\def\POL_difftwo_fork #1% +{% + \POL_Pfork + #1\POL_difftwo_pol + P\POL_difftwo_cst + \krof #1% +}% +\def\POL_difftwo_cst#1\xint:{0/1[0]}% +\def\POL_difftwo_pol P#1.% +{% + \ifcase #1 + \or \expandafter\POL_difftwo_zeroout + \or \expandafter\POL_difftwo_cstout + \else\expandafter\POL_difftwo_polout + \fi #1.% +}% +\def\POL_difftwo_zeroout#1\xint:{0/1[0]}% +\def\POL_difftwo_cstout 2.#1#2#3\xint:{\xintMul{2}{#3}}% +\def\POL_difftwo_polout #1.#2#3#4\xint:% +{% + \expanded{% + P\the\numexpr#1-\xint_c_ii.% + \POL_apply:x_iloop{\POL_difftwo_diff2.}% + #4\xint_Bye\xint_Bye\xint_Bye\xint_Bye + \xint_Bye\xint_Bye\xint_Bye\xint_Bye\xint_bye + }% +}% +\def\POL_difftwo_diff#1.#2#3{\xintMul{\the\numexpr(#1+#2)*(#1+#2-\xint_c_i)\relax}{#3}}% +% +\def\POL_diffone_iter_fork #1% +{% + \POL_Pfork + #1\POL_diffone_iter_pol + P\POL_diffone_iter_cst + \krof #1% +}% +\def\POL_diffone_iter_cst#1\xint:{0/1[0]\xint:}% +\def\POL_diffone_iter_pol P#1.#2#3\xint:% +{% + \expanded{% + \ifnum#1=\xint_c_i #3% + \else + P\the\numexpr#1-\xint_c_i.% + \POL_apply:x_iloop{\POL_diffone_diff1.}% + #3\xint_Bye\xint_Bye\xint_Bye\xint_Bye + \xint_Bye\xint_Bye\xint_Bye\xint_Bye\xint_bye + \fi + }\xint: +}% +% +\def\POL_antione_iter_fork #1% +{% + \POL_Pfork + #1\POL_antione_iter_pol + P\POL_antione_iter_cst + \krof #1% +}% +\def\POL_antione_iter_cst#1% +{% + \xint_gob_til_zero#1\POL_antione_iter_zero0\POL_antione_iter_cst_i#1% +}% +\def\POL_antione_iter_cst_i#1\xint:{P1.{0/1[O]}{#1}\xint:}% +\def\POL_antione_iter_zero#1\xint:{0/1[0]\xint:}% +\def\POL_antione_iter_pol P#1.#2\xint:% +{% + \expanded{% + P\the\numexpr#1+\xint_c_i.{0/1[0]}% + \POL_apply:x_iloop{\POL_antione_anti1.}% + #2\xint_Bye\xint_Bye\xint_Bye\xint_Bye + \xint_Bye\xint_Bye\xint_Bye\xint_Bye\xint_bye + }\xint: +}% +% +\def\xintPolDiffN#1#2% +{% + \romannumeral`&&@\expandafter\POL_diffn_fork + \the\numexpr\xintNum{#2}\expandafter.% + \romannumeral`&&@#1\xint: +}% +\def\POL_diffn_fork #1% +{% + \xint_UDzerominusfork + #1-\POL_diffn_none + 0#1\POL_diffn_anti + 0-\POL_diffn_diff + \krof #1% +}% +\def\POL_diffn_none0.#1\xint:{#1}% +\def\POL_diffn_diff#1.%#2\xint:% +{% + \ifnum#1>\xint_c_i + \expandafter\POL_diffn_diff\the\numexpr#1-\xint_c_i\expandafter.% + \romannumeral`&&@\expandafter\POL_diffone_iter_fork + \else + \expandafter\POL_diffone_fork + \fi +}% +\def\POL_diffn_anti#1.%#2\xint:% +{% + \ifnum#1<-\xint_c_i + \expandafter\POL_diffn_anti\the\numexpr#1+\xint_c_i\expandafter.% + \romannumeral`&&@\expandafter\POL_antione_iter_fork + \else + \expandafter\POL_antione_fork + \fi +}% +% +% Support for (multi-variable) polgcd +% +\def\xintPolGCDof #1% +{% + \romannumeral`&&@\expandafter\POL_polgcdof\romannumeral`&&@#1^% +}% +\def\XINT_PolGCDof{\romannumeral`&&@\POL_polgcdof}% +\def\POL_polgcdof #1% +{% + \romannumeral`&&@\expandafter + \POL_polgcdof_chkempty\romannumeral`&&@#1\xint: +}% +\def\POL_polgcdof_chkempty #1% +{% + \xint_gob_til_^#1\POL_polgcdof_empty ^\POL_polgcdof_in #1% +}% +\def\POL_polgcdof_empty #1\xint:{1/1[0]}% hesitation +\def\POL_polgcdof_in #1\xint: +{% + \expandafter\POL_polgcdof_loop + \romannumeral`&&@\xintPolPrimPart{#1}\xint: +}% +\def\POL_polgcdof_loop #1\xint:#2% +{% + \expandafter\POL_polgcdof_chkend\romannumeral`&&@#2\xint:#1\xint:\xint: +}% +\def\POL_polgcdof_chkend #1% +{% + \xint_gob_til_^#1\POL_polgcdof_end ^\POL_polgcdof_loop_pair #1% +}% +% hesitation with returning a monic polynomial +%\def\POL_polgcdof_end #1\xint:#2\xint:\xint:{\xintPolMonicPart{#2}}% +\def\POL_polgcdof_end #1\xint:#2\xint:\xint:{#2}% +\def\POL_polgcdof_loop_pair #1\xint: +{% + \expandafter\POL_polgcdof_loop + \romannumeral`&&@\expandafter\POL_polgcd_pair + \romannumeral`&&@\xintPolPrimPart{#1}\xint: +}% +% MEMO comme le #2 sera au début le pgcd accumulé il sera souvent de plus +% petit degré donc il y aura souvent un premier mod "easy" un peu inutile +% J'hésite à faire une permutation avant de lancer le polgcd_pair +\def\POL_polgcd_pair#1\xint:#2\xint: +{% + \xintiiifSgn {\xintPolDeg {#1}}% + {#2}% + {1}% + {\expandafter\POL_polgcd_pair + \romannumeral`&&@\xintPolPrimPart + {\expandafter\xint_secondoftwo + \romannumeral`&&@\xintPolPRem {#2}{#1}}\xint: + #1\xint: + }% +}% +% +\endinput diff --git a/macros/generic/polexpr/polexprexpr.tex b/macros/generic/polexpr/polexprexpr.tex new file mode 100644 index 0000000000..9b60e7bab0 --- /dev/null +++ b/macros/generic/polexpr/polexprexpr.tex @@ -0,0 +1,179 @@ +%% This file polexprexpr.tex is part of the polexpr package (0.8, 2021/03/29) +%% Extending \xintexpr syntax: +%% +%% 1. Authorize ' in variable and function names +%% This currently breaks infix operators 'and', 'or', 'xor', 'mod' +%% hence forces usage everywhere of &&, ||, /: and xor() syntax +%% (if : is active then use /\string : input syntax!) +%% +%% 2. Map infix operators to the polexprcore macros +%% +%% Overloading of infix operators must be done even outside of \poldef's +%% scope else functions declared via \xintdeffunc would not be usable in +%% \poldef as they would be using the xintfrac macros unaware of polynomials +%% +%% The overloading of // and /: is experimental. +%% +%% 3. Support for the polynomial functions to work in \xintdeffunc +%% +%% 4. Support macros for the new functions acting on polynomial variables +% +% 1. +\def\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}% +}% +% 2. +% the minus sign as prefix +\def\POL_tmp #1#2% +{% + \expandafter\def\csname XINT_expr_exec_#1\endcsname##1##2##3% \XINT_expr_exec_ + {% + \expandafter ##1\expandafter ##2\expandafter + {\romannumeral`&&@\XINT:NEhook:f:one:from:one{\romannumeral`&&@#2##3}}% + }% +}% +\POL_tmp{-xii} \xintPolOpp +\POL_tmp{-xiv} \xintPolOpp +\POL_tmp{-xvi} \xintPolOpp +\POL_tmp{-xviii}\xintPolOpp +% infix operators +\def\POL_tmp #1#2% +{% + \expandafter\def\csname XINT_expr_exec_#1\endcsname##1##2##3##4% \XINT_expr_exec_ + {% + \expandafter##2\expandafter##3\expandafter + {\romannumeral`&&@\XINT:NEhook:f:one:from:two{\romannumeral`&&@#2##1##4}}% + }% +}% +\POL_tmp + \xintPolAdd +\POL_tmp - \xintPolSub +\POL_tmp * \xintPolMul +\POL_tmp / \xintPolQuo +% there is no infix operator mapped to \xintPolRem +% for lack of notation: perhaps /; ? advices welcome +\POL_tmp{//}\xintPolDivModQ +\POL_tmp{/:}\xintPolDivModR +\POL_tmp ^ \xintPolPow +\expandafter\let\csname XINT_expr_op_**\expandafter\endcsname + \csname XINT_expr_op_^\endcsname +% 3. +% Matches with "mysterious stuff" section of xintexpr source code +\let\POL:NEhook:polfunc\expandafter +\toks0\expandafter{\XINT_expr_redefinemacros}% +\toks2 {\let\POL:NEhook:polfunc\POL:NE:polfunc}% +\edef\XINT_expr_redefinemacros{\the\toks0 \the\toks2}% +\catcode`~ 12 +\def\POL@defpolfunc #1#2% +{% + \expandafter\POL@defpolfunc_a + \csname XINT_#2_func_#1\expandafter\endcsname + \csname XINT_#2_polfunc_#1\endcsname +}% +\def\POL@defpolfunc_a #1#2% +{% + \XINT_global + \def#1##1##2##3% + {% + \expandafter##1\expandafter##2\expandafter{% + \romannumeral`&&@\POL:NEhook:polfunc{\romannumeral`&&@#2##3}}% + }% +}% +\def\POL:NE:polfunc #1{% +\def\POL:NE:polfunc ##1% +{% + \if0\XINT:NE:hastilde ##1~!\relax % this ! of catcode 11 + \XINT:NE:hashash ##1#1!\relax 0\else + \xint_dothis\POL:NE:polfunc_a\fi + \xint_orthat\POL:NE:polfunc_b + ##1&&A% +}}\expandafter\POL:NE:polfunc\string#% +\def\POL:NE:polfunc_a\romannumeral`&&@#1#2&&A% +{% +% If we are here #2 was not braced; \string is done with \escapechar126 + \expandafter{\expanded{~romannumeral~POL:NE:usepolfunc% + {\expandafter\xint_gobble_i\string#1}}#2}% +}% +\def\POL:NE:polfunc_b#1{% +\def\POL:NE:polfunc_b\romannumeral`&&@##1##2&&A% +{% + \expandafter{% + \romannumeral`&&@% + \if0\XINT:NE:hastilde ##2~!\relax + \XINT:NE:hashash ##2#1!\relax 0\else + \POL:NE:polfunc_c\fi + ##1{##2}}% +}}\expandafter\POL:NE:polfunc_b\string#% +% In this case the \expandafter inserted by \POL:NE:usepolfunc +% expansion will be superfluous +\def\POL:NE:polfunc_c#1#2% #1=\fi +{% + \expanded{#1~romannumeral~POL:NE:usepolfunc% + {\expandafter\xint_gobble_i\string#2}}% +}% +% This \expandafter is in case there is an \expanded after that due +% to some slicing constructs +% Call: \romannumeral\POL:NE:usepolfunc +\def\POL:NE:usepolfunc#1{`&&@\csname#1\expandafter\endcsname}% +\catcode`~ 3 % its normal catcode in xint bundle +% 4. +\def\POL_tmp #1#2#3% +{% + \expandafter\def\csname XINT_expr_func_#1\endcsname##1##2##3% + {% + \expandafter ##1\expandafter ##2\expandafter + {% + \romannumeral`&&@#2{\romannumeral`&&@#3##3}% + }% + }% +}% +\POL_tmp {sqr} \XINT:NEhook:f:one:from:one \xintPolSqr +\POL_tmp {pol} \XINT:NEhook:f:one:from:one \xintPolPol +\POL_tmp {deg} \XINT:NEhook:f:one:from:one \xintPolDeg +\POL_tmp {coeffs} \XINT:NEhook:f:one:from:one \xintPolCoeffs +\POL_tmp {coeff} \XINT:NEhook:f:one:from:two \xintPolCoeff +\POL_tmp {lcoeff} \XINT:NEhook:f:one:from:one \xintPolLCoeff +\POL_tmp {monicpart} \XINT:NEhook:f:one:from:one \xintPolMonicPart +\POL_tmp {icontent} \XINT:NEhook:f:one:from:one \xintPolIContent +\POL_tmp {primpart} \XINT:NEhook:f:one:from:one \xintPolPrimPart +\POL_tmp {rdcoeffs} \XINT:NEhook:f:one:from:one \xintPolRedCoeffs +\POL_tmp {rdzcoeffs} \XINT:NEhook:f:one:from:one \xintPolSRedCoeffs +\POL_tmp {diff1} \XINT:NEhook:f:one:from:one \xintPolDiffOne +\POL_tmp {diff2} \XINT:NEhook:f:one:from:one \xintPolDiffTwo +\POL_tmp {diffn} \XINT:NEhook:f:one:from:two \xintPolDiffN +\POL_tmp {antider} \XINT:NEhook:f:one:from:one \xintPolAntiOne +\POL_tmp {integral} \XINT:NEhook:f:one:from:two \xintPolIntegral +\POL_tmp {quorem} \XINT:NEhook:f:one:from:two \xintPolQuoRem +\POL_tmp {quo} \XINT:NEhook:f:one:from:two \xintPolQuo +\POL_tmp {rem} \XINT:NEhook:f:one:from:two \xintPolRem +\POL_tmp {prem} \XINT:NEhook:f:one:from:two \xintPolPRem +\POL_tmp {divmod} \XINT:NEhook:f:one:from:two \xintPolDivMod +\POL_tmp {mod} \XINT:NEhook:f:one:from:two \xintPolDivModR +\POL_tmp {evalp} \XINT:NEhook:f:one:from:two \xintPolEvalAt +\def\XINT_expr_func_polgcd #1#2#3% +{% + \expandafter #1\expandafter #2\expandafter{\expandafter + {\romannumeral`&&@\XINT:NEhook:f:from:delim:u\XINT_PolGCDof#3^}}% +}% +% this is provisory +\xintdeffunc polpowmod_(P, m, Q) := + isone(m)? + % m=1: return P modulo Q + { mod(P,Q) } + % m > 1: test if odd or even and do recursive call + { odd(m)? { mod(P*sqr(polpowmod_(P, m//2, Q)), Q) } + { mod( sqr(polpowmod_(P, m//2, Q)), Q) } + } + ;% +\xintdeffunc polpowmod(P, m, Q) := (m)?{polpowmod_(P, m, Q)}{1};% +% +\endinput diff --git a/macros/generic/polexpr/polexprsturm.tex b/macros/generic/polexpr/polexprsturm.tex new file mode 100644 index 0000000000..3fa1861558 --- /dev/null +++ b/macros/generic/polexpr/polexprsturm.tex @@ -0,0 +1,1775 @@ +%% This file polexprsturm.tex is part of the polexpr package (0.8, 2021/03/29) +%% Sturm Algorithm (polexpr 0.4) +%% 0.5 uses primitive polynomials for faster evaluations afterwards +%% 0.6 corrects misuse of \@ifstar! (mumble). \PolToSturm* was broken. +%% 0.6's \PolToSturm* defines both normalized and unnormalized, the +%% unnormalized using two underscores, so both are available +%% Sole difference is that \PolToSturm* also declares them as +%% user polynomials, whereas the non-starred only keeps the macros +%% holding the coefficients in memory +%% 0.6 fixes the case of a constant polynomial P which caused division +%% by zero error from P'. +%% 0.8 - fixes 0.7.5 failure to have updated to xint 1.4 format the defined +%% \xintexpr variables holding the localization intervals extremities +%% - also, it uses the prem() in computing the Sturm chain, for a 3X +%% speed gain in the case of the "perturbed" first Wilkinson example +%% +\newcount\POL@count +\newif\ifPOL@tosturm@makefirstprimitive\POL@tosturm@makefirstprimitivetrue +\newif\ifPOL@isolz@nextwillneedrefine +%% +\def\PolToSturm{\POL@ifstar{\PolToSturm@@}{\PolToSturm@}}% +\def\POL@aux@toint#1{\xintREZ{\xintNum{#1}}}% for polynomials with int. coeffs! +%% Attention that some macros rely upon this one setting \POL@sturmname +%% and \POL@sturm@N as it does +\def\PolToSturm@#1#2{% + \edef\POL@sturmname{#2}% + % 0.6 uses 2 underscores (one before index, one after) to keep in memory + % the unnormalized chain + % This supposes #1 to be a genuine polynomial, not only a name with + % a \POLuserpol@#1 macro + \POL@let{\POL@sturmname _0_}{#1}% + \ifnum\PolDegree{#1}=\z@ + \def\POL@sturm@N{0}% + \POL@count\z@ + % if I applied the same as for positive degree, I should make it -1 + % if constant is negative. I also don't worry if polynomial is zero. + \XINT_global\@namedef{POLuserpol@\POL@sturmname _0}{0.\empty{1/1[0]}}% + \else + \ifPOL@tosturm@makefirstprimitive\POL@makeprimitive{\POL@sturmname _0_}\fi + \POL@tosturm@dosturm + \fi + \expandafter + \let\csname PolSturmChainLength_\POL@sturmname\endcsname\POL@sturm@N + % declare the normalized ones as full-fledged polynomials + % \POL@count\z@ + \xintloop + \POL@newpol{\POL@sturmname _\the\POL@count}% + \unless\ifnum\POL@sturm@N=\POL@count + \advance\POL@count\@ne + \repeat +}% +\def\PolToSturm@@#1#2{\PolToSturm@{#1}{#2}\POL@tosturm@declareunnormalized}% +\def\POL@tosturm@declareunnormalized{% + % optionally declare also the unnormalized ones + \POL@count\z@ + \xintloop + \POL@newpol{\POL@sturmname _\the\POL@count _}% + \unless\ifnum\POL@sturm@N=\POL@count + \advance\POL@count\@ne + \repeat +}% +\def\POL@tosturm@dosturm{% + \POL@Diff@@one{\POL@sturmname _0_}{\POL@sturmname _1_}% + % re-utiliser \POL@varcoeffs directement? + \POL@makeprimitive{\POL@sturmname _1_}% does not do \POL@newpol + \POL@count\@ne + \xintloop + % prior to 0.8, code was using here \POL@divide + \POL@getprem{\POL@sturmname _\the\numexpr\POL@count-\@ne\relax _}% + {\POL@sturmname _\the\POL@count _}% + \expandafter\POL@split\POL@R;\POL@degR\POL@polR + \unless\ifnum\POL@degR=\m@ne + \advance\POL@count\@ne + \XINT_global\expandafter\let + \csname POLuserpol@\POL@sturmname _\the\POL@count _\endcsname\POL@R + \edef\POL@makeprim@icontent{-\POL@icontent\POL@polR}% + % this avoids the \POL@newpol from \PolMapCoeffs + \POL@mapcoeffs\POL@makeprim@macro{\POL@sturmname _\the\POL@count _}% + \repeat + \edef\POL@sturm@N{\the\POL@count}% + % normalize (now always done even by starred variant) + \ifnum\PolDegree{\POL@sturmname _\POL@sturm@N _}>\z@ + % \POL@count\POL@sturm@N\relax + \xintloop + \advance\POL@count\m@ne + \POL@divide{\POL@sturmname _\the\POL@count _}% + {\POL@sturmname _\POL@sturm@N _}% + \XINT_global\expandafter + \let\csname POLuserpol@\POL@sturmname _\the\POL@count\endcsname\POL@Q + % quotient actually belongs to Z[X] and is primitive + \POL@mapcoeffs\POL@aux@toint{\POL@sturmname _\the\POL@count}% + \ifnum\POL@count>\z@ + \repeat + \XINT_global\@namedef{POLuserpol@\POL@sturmname _\POL@sturm@N}{0.\empty{1/1[0]}}% + \else % they are already normalized + \advance\POL@count\@ne % attention to include last one also + \xintloop + \advance\POL@count\m@ne + \XINT_global\expandafter\let + \csname POLuserpol@\POL@sturmname _\the\POL@count\expandafter\endcsname + \csname POLuserpol@\POL@sturmname _\the\POL@count _\endcsname + \ifnum\POL@count>\z@ + \repeat + \fi + % Back to \PolToSturm@, \POL@count holds 0 +}% +\def\PolSturmChainLength#1{% + \romannumeral`&&@\csname PolSturmChainLength_#1\endcsname +}% +\def\PolSetToSturmChainSignChangesAt{% + \POL@chkopt\POL@oPolSetToSturmChainSignChangesAt[\global]% +}% +\def\POL@oPolSetToSturmChainSignChangesAt[#1]#2#3#4{% + \edef\POL@sturmchain@X{\xintREZ{#4}}% + \edef\POL@sturmname{#3}% + \edef\POL@sturmlength{\PolSturmChainLength{\POL@sturmname}}% + \POL@sturmchain@getSV@at\POL@sturmchain@X + #1\let#2\POL@sturmchain@SV +}% +\def\POL@sturmchain@getSV@at#1{% ATTENTION USES \POL@count + \def\POL@sturmchain@SV{0}% + \edef\POL@sturmchain@sign{\xintiiSgn{\POL@eval{\POL@sturmname _0}{#1}}}% + \let\POL@isolz@lastsign\POL@sturmchain@sign + \POL@count \z@ + \ifnum\POL@isolz@lastsign=\z@ + \edef\POL@isolz@lastsign + {\xintiiSgn{\POL@eval{\POL@sturmname _1}{#1}}}% + \POL@count \@ne + \fi + \xintloop + \unless\ifnum\POL@sturmlength=\POL@count + \advance\POL@count \@ne + \edef\POL@isolz@newsign + {\xintiiSgn{\POL@eval{\POL@sturmname _\the\POL@count}{#1}}}% + \ifnum\POL@isolz@newsign=\numexpr-\POL@isolz@lastsign\relax + \edef\POL@sturmchain@SV{\the\numexpr\POL@sturmchain@SV+\@ne}% + \let\POL@isolz@lastsign=\POL@isolz@newsign + \fi + \repeat +}% +\def\PolSetToNbOfZerosWithin{% + \POL@chkopt\POL@oPolSetToNbOfZerosWithin[\global]% +}% +\def\POL@oPolSetToNbOfZerosWithin[#1]#2#3#4#5{% + \edef\POL@tmpA{\xintREZ{#4}}% + \edef\POL@tmpB{\xintREZ{#5}}% + \edef\POL@sturmname{#3}% + \edef\POL@sturmlength{\PolSturmChainLength{\POL@sturmname}}% + \POL@sturmchain@getSV@at\POL@tmpA + \let\POL@SVA\POL@sturmchain@SV + \POL@sturmchain@getSV@at\POL@tmpB + \let\POL@SVB\POL@sturmchain@SV + \ifnum\POL@SVA<\POL@SVB\space + #1\edef#2{\the\numexpr\POL@SVB-\POL@SVA}% + \else + #1\edef#2{\the\numexpr\POL@SVA-\POL@SVB}% + \fi +}% +% 0.6 added starred variant to count multiplicities +% 0.7 added double starred variant to locate all rational roots +\def\PolSturmIsolateZeros{\POL@ifstar + {\PolSturmIsolateZerosAndGetMultiplicities}% + {\PolSturmIsolateZeros@}% +}% +\def\PolSturmIsolateZerosAndGetMultiplicities{\POL@ifstar + {\PolSturmIsolateZerosGetMultiplicitiesAndRationalRoots}% + {\PolSturmIsolateZerosAndGetMultiplicities@}% +}% +% on aurait besoin de ça dans xint, mais il aurait un \xintRaw{#1} alors +\def\POL@xintfrac@getNDE #1% + {\expandafter\POL@xintfrac@getNDE@i\romannumeral`&&@#1}% +\def\POL@xintfrac@getNDE@i #1/#2[#3]#4#5#6{\def#4{#1}\def#5{#2}\def#6{#3}}% +% +\def\PolSturmIsolateZerosGetMultiplicitiesAndRationalRoots{% + \POL@chkopt\POL@oPolSturmIsolateZerosGetMultiplicitiesAndRationalRoots[\empty]% +}% +\def\POL@oPolSturmIsolateZerosGetMultiplicitiesAndRationalRoots[#1]#2{% + \PolSturmIsolateZerosAndFindRationalRoots[#1]{#2}% + \ifnum\POL@isolz@NbOfRoots>\z@ + % get multiplicities of irrational (real) roots, if any + \ifnum\POL@findrat@nbofirrroots>\z@ + \POL@findrat@getirrmult + \fi + \POL@isolzmult@defvar@M + \fi +}% +% added at 0.7 +\def\PolSturmIsolateZerosAndFindRationalRoots{% + \POL@chkopt\POL@oPolSturmIsolateZerosAndFindRationalRoots[\empty]% +}% +\def\POL@oPolSturmIsolateZerosAndFindRationalRoots[#1]#2{% + % #1 optional E such that roots are searched in -10^E < x < 10^E + % both -10^E and +10^E must not be roots! + % #2 name of Sturm chain (already pre-computed) + \edef\POL@sturmname{#2}% + \edef\POL@sturm@N{\@nameuse{PolSturmChainLength_\POL@sturmname}}% + % isolate the roots (detects case of constant polynomial) + \PolSturmIsolateZeros@{\POL@sturmname}% + \ifnum\POL@isolz@NbOfRoots=\z@ + % no real roots, define empty arrays nevertheless + \begingroup\globaldefs\@ne + \expandafter\xintAssignArray\expandafter\to\csname POL_ZM\POL@sturmname*\endcsname + \expandafter\xintAssignArray\expandafter\to\csname POL_RI\POL@sturmname*\endcsname + \endgroup + \else + % all we currently know is that multiplicities are at least one + \begingroup\globaldefs\@ne + \expandafter\POL@initarray\csname POL_ZM\POL@sturmname*\endcsname{1}% + \endgroup + % on ne va pas utiliser de Horner, mais des divisions par X - x, et ces + % choses vont évoluer, ainsi que le coefficient dominant entier + % (pour \POL@divide entre autres if faut des noms de user pol) + \XINT_global + \expandafter\let + \csname POLuserpol@\POL@sturmname\POL@sqfnorr\expandafter\endcsname + \csname POLuserpol@\POL@sturmname _0\endcsname + \XINT_global + \expandafter\let + \csname POLuserpol@\POL@sturmname\POL@norr\expandafter\endcsname + \csname POLuserpol@\POL@sturmname _0_\endcsname + % attention formé avec\xintREZ d'où le \xintAbs pas \xintiiAbs + % D and its exponent E will get updated along the way + \edef\POL@findrat@D{\xintAbs{\PolLeadingCoeff{\POL@sturmname _0}}}% + \POL@xintfrac@getNDE\POL@findrat@D\POL@findrat@Dint\POL@_\POL@findrat@Dexp + \xintiiifOne{\POL@findrat@Dint} + {\let\POL@findrat@E\POL@findrat@Dexp} % aussi ok pour 1[0] + {\edef\POL@findrat@E{\the\numexpr\xintLen{\POL@findrat@Dint}% + +\POL@findrat@Dexp}}% +% ATTENTION QUE LA CONVENTION DE SIGNE POUR \POL@findrat@E EST OPPOSÉE À CELLE +% POUR LE CODE PLUS ANCIEN FAISANT "REFINE" + \POL@initarray\POL@IfMultIsKnown\xint_secondoftwo + \let\POL@findrat@nbofirrroots\POL@isolz@NbOfRoots + % find all rational roots, and their multiplicities, + % factor them out in passing from original (Sturm root) polynomial + \ifnum\POL@findrat@E<7 + \def\POL@findrat@index{1}% + \POL@findrat@loop@secondpass@direct + \else + % we do a first pass scanning for "small" roots p/q (i.e. q < 1000) + \def\POL@findrat@index{1}% + \POL@findrat@loop@firstpass + % and now we do the final pass finding them all + \def\POL@findrat@index{1}% + \POL@findrat@loop@secondpass + \fi + % declare the new polynomials + \POL@newpol{\POL@sturmname\POL@sqfnorr}% without multiplicities + \POL@newpol{\POL@sturmname\POL@norr}% with multiplicities + % declare the array holding the interval indices for the rational roots + \expandafter\POL@findrat@doRRarray\csname POL_RI\POL@sturmname*\endcsname + \fi +}% +\def\POL@findrat@doRRarray#1{% + % il faudrait un \xintAssignArray* qui fasse même expansion que \xintFor* + \edef\POL@temp{% + \xintiloop[1+1] + \romannumeral0\csname POL_ZK\POL@sturmname*\xintiloopindex\endcsname + \xintbracediloopindex % I should have named it \xintiloopbracedindex... + {}% + \ifnum\xintiloopindex<\POL@isolz@NbOfRoots\space + \repeat }% + \begingroup\globaldefs\@ne + % attention de ne surtout pas faire un \expandafter ici, car en cas d'un + % seul item, \xintAssignArray l'unbraces... + \xintAssignArray\POL@temp\to#1% + \endgroup +}% +\def\POL@findrat@loop@firstpass{% + \PolSturmIfZeroExactlyKnown{\POL@sturmname}{\POL@findrat@index}% + \POL@findrat@loop@decimal% get its multiplicity + \POL@findrat@loop@aa % refine interval and check + \edef\POL@findrat@index{\the\numexpr\POL@findrat@index+\@ne}% + \ifnum\POL@findrat@index>\POL@isolz@NbOfRoots + \else + \expandafter\POL@findrat@loop@firstpass + \fi +}% +\def\POL@findrat@loop@aa{% + % we do a first pass to identify roots with denominators < 1000 + \PolEnsureIntervalLength{\POL@sturmname}{\POL@findrat@index}{-6}% + % attention that perhaps now the root is known! + \PolSturmIfZeroExactlyKnown{\POL@sturmname}{\POL@findrat@index}% + \POL@findrat@loop@decimal + \POL@findrat@loop@a +}% +\def\POL@findrat@loop@decimal{% we have an already found decimal root + % we do not go via @storeit, as it is already stored + % j'ai beaucoup hésité néanmoins, car je pourrais faire \xintIrr ici, + % mais attention aussi à l'interaction avec le \PolDecToString. Les racines + % trouvées directement (qui peuvent être des nombres décimaux) sont elles + % stockées comme fraction irréductibles (modulo action additionnelle de + % \PolDecToString). + \POL@xintfrac@getNDE + {\xintIrr{\POL@xintexprGetVar{\POL@sturmname L_\POL@findrat@index}}[0]}% + \POL@findrat@xN\POL@findrat@xD\POl@_ + % we can't move this to updatequotients because other branch will + % need to do the division first anyhow + \edef\POLuserpol@_findrat@oneterm{1.\noexpand\empty + {\xintiiOpp\POL@findrat@xN/1[0]}{\POL@findrat@xD/1[0]}}% + \POL@divide{\POL@sturmname\POL@sqfnorr}{_findrat@oneterm}% the one without mult. + %\expandafter\POL@split\POL@R;\POL@degR\POL@polR + \POL@findrat@loop@updatequotients + \POL@findrat@loop@getmultiplicity +}% +% lacking from xint 1.3c, but \xintSgn has overhead, so we define ii version +\def\xintiiifNeg{\romannumeral0\xintiiifneg }% +\def\xintiiifneg #1% +{% + \ifcase \xintiiSgn{#1} + \expandafter\xint_stop_atsecondoftwo + \or\expandafter\xint_stop_atsecondoftwo + \else\expandafter\xint_stop_atfirstoftwo + \fi +}% +\def\POL@findrat@getE #1/1[#2]{#2}% /1 as it should be there. +% so an error will arise if not but cf \POL@refine@getE where I did not put it +\def\POL@findrat@loop@a{% + % attention that the width may have been already smaller than 10^{-6} + \POL@get@IsoLeft@rawin + \POL@get@IsoRight@rawin + \edef\POL@findrat@localW + {\the\numexpr-\expandafter\POL@findrat@getE + % do I really need the \xintREZ? + \romannumeral0\xintrez + {\xintSub{\POL@IsoRight@rawin}{\POL@IsoLeft@rawin}}% + }% at least 6, maybe larger + \expandafter\POL@get@Int@aux + \POL@IsoLeft@rawin\POL@IsoLeft@Int{-\POL@findrat@localW}% + \expandafter\POL@get@Int@aux + \POL@IsoRight@rawin\POL@IsoRight@Int{-\POL@findrat@localW}% + % in case of odd, some waste here + \edef\POL@findrat@halflocalW{\the\numexpr(\POL@findrat@localW+1)/2-1}% + % Legendre Theorem will be used now but we separate a branch where + % everything can be done with \numexpr + \ifnum\POL@findrat@localW>9 + % not implemented yet by lazyness! + % this root will be handled in second pass only + \else + \POL@findrat@gcdloop + \fi +}% +\def\POL@findrat@gcdloop{% + % we must be careful with sign + % but we are certain no extremity is a root + \let\POL@findrat@ifnegative\xint_secondoftwo + \xintiiifSgn\POL@IsoLeft@Int + \POL@findrat@gcdloop@n + \POL@error@thisisimpossible + \POL@findrat@gcdloop@p +}% +\def\POL@findrat@gcdloop@n{% + \let\POL@findrat@ifnegative\xint_firstoftwo + \let\POL@temp\POL@IsoRight@Int + \edef\POL@IsoRight@Int{\xintiiOpp{\POL@IsoLeft@Int}}% + \edef\POL@IsoLeft@Int{\xintiiOpp{\POL@temp}}% + \POL@findrat@gcdloop@p +}% +\def\POL@findrat@gcdloop@p{% + \edef\POL@findrat@gcdloop@Ap{\xintDec{\xintDouble\POL@IsoRight@Int}}% + \edef\POL@findrat@gcdloop@A + % at most 2e9: this is acceptable to \numexpr + {2\romannumeral\xintreplicate\POL@findrat@localW{0}}% + \xintAssign + \xintiiDivision\POL@findrat@gcdloop@Ap\POL@findrat@gcdloop@A + \to\POL@findrat@gcdloop@B\POL@findrat@gcdloop@An + % on fait de la tambouille pour n'utiliser que \numexpr par la suite + % le reste @An est < 2.10^9 au pire donc ok pour \numexpr + % we will drop integral part in our updating P + \let\POL@findrat@gcdloop@Binitial\POL@findrat@gcdloop@B + \def\POL@findrat@gcdloop@B{0}% do as if B1 = 0 + \def\POL@findrat@gcdloop@Pp{1}% P0 + \def\POL@findrat@gcdloop@P{0}% P1 + \def\POL@findrat@gcdloop@Qp{0}% Q0 + \def\POL@findrat@gcdloop@Q{1}% Q1 + % A2=An can not be zero, as Ap (=A0) is odd and A (=A1=200...000) is even + % first Binitial + P1/Q1 ( = Binitial) can not be root + \let\POL@findrat@gcdloop@Ap\POL@findrat@gcdloop@A % A1 + \let\POL@findrat@gcdloop@A\POL@findrat@gcdloop@An % A2 + \def\next{\POL@findrat@gcdloop@update}% + \def\POL@findrat@gcdloop@done{0}% + \POL@findrat@gcdloop@body +}% +\def\POL@findrat@gcdloop@body{% + % annoying that \numexpr has no divmod... use counts? but groups annoying + \edef\POL@findrat@gcdloop@B + {\the\numexpr(\POL@findrat@gcdloop@Ap+\POL@findrat@gcdloop@A/2)/% + \POL@findrat@gcdloop@A - \@ne}% + \edef\POL@findrat@gcdloop@An + {\the\numexpr\POL@findrat@gcdloop@Ap-% + \POL@findrat@gcdloop@B*\POL@findrat@gcdloop@A}% + \edef\POL@findrat@gcdloop@Pn + {\the\numexpr\POL@findrat@gcdloop@Pp+% + \POL@findrat@gcdloop@B*\POL@findrat@gcdloop@P}% + \edef\POL@findrat@gcdloop@Qn + {\the\numexpr\POL@findrat@gcdloop@Qp+% + \POL@findrat@gcdloop@B*\POL@findrat@gcdloop@Q}% + \ifnum\expandafter\xintLength\expandafter{\POL@findrat@gcdloop@Qn}% + >\POL@findrat@halflocalW\space + \let\next\empty % no solution was found + \else + % with these conditions on denom, only candidates are by Legendre + % theorem among the convergents as computed here + \ifnum\POL@findrat@gcdloop@Qn>\POL@findrat@gcdloop@An\space + % means that P/Q is in interval and is thus a candidate + % it is automatically irreducible + \edef\POL@findrat@x{\xintiiAdd + {\xintiiMul{\POL@findrat@gcdloop@Qn}{\POL@findrat@gcdloop@Binitial}}% + {\POL@findrat@gcdloop@Pn}/\POL@findrat@gcdloop@Qn[0]}% + \POL@findrat@gcdloop@testit + \if1\POL@findrat@gcdloop@done + \let\next\empty % a solution was found + \fi + \fi + \fi + \next +}% +\def\POL@findrat@gcdloop@update{% + \ifnum\POL@findrat@gcdloop@An>\z@ + \let\POL@findrat@gcdloop@Ap\POL@findrat@gcdloop@A + \let\POL@findrat@gcdloop@A\POL@findrat@gcdloop@An + \let\POL@findrat@gcdloop@Pp\POL@findrat@gcdloop@P + \let\POL@findrat@gcdloop@P\POL@findrat@gcdloop@Pn + \let\POL@findrat@gcdloop@Qp\POL@findrat@gcdloop@Q + \let\POL@findrat@gcdloop@Q\POL@findrat@gcdloop@Qn + \expandafter\POL@findrat@gcdloop@body + \fi +}% +\def\POL@findrat@gcdloop@testit{% + % zero should never occur here + \POL@findrat@ifnegative{\edef\POL@findrat@x{-\POL@findrat@x}}{}% + \POL@xintfrac@getNDE\POL@findrat@x\POL@findrat@xN\POL@findrat@xD\POL@_ + \edef\POLuserpol@_findrat@oneterm{1.\noexpand\empty + {\xintiiOpp{\POL@findrat@xN}/1[0]}{\POL@findrat@xD/1[0]}}% + \POL@divide{\POL@sturmname\POL@sqfnorr}{_findrat@oneterm}% the one without mult. + \expandafter\POL@split\POL@R;\POL@degR\POL@polR + \ifnum\POL@degR=\m@ne % found a root + \POL@findrat@loop@storeit + \POL@findrat@loop@updatequotients + \POL@findrat@loop@getmultiplicity % will continue updating the mult. one + \def\POL@findrat@gcdloop@done{1}% + \fi +}% +% This is second phase +\def\POL@findrat@loop@secondpass{% + \PolSturmIfZeroExactlyKnown{\POL@sturmname}{\POL@findrat@index}% + {}% nothing more to be done, already stored + \POL@findrat@loop@bb % refine interval and check + \edef\POL@findrat@index{\the\numexpr\POL@findrat@index+\@ne}% + \ifnum\POL@findrat@index>\POL@isolz@NbOfRoots + \else + \expandafter\POL@findrat@loop@secondpass + \fi +}% +\def\POL@findrat@loop@secondpass@direct{% + \PolSturmIfZeroExactlyKnown{\POL@sturmname}{\POL@findrat@index}% + \POL@findrat@loop@decimal + \POL@findrat@loop@bb + \edef\POL@findrat@index{\the\numexpr\POL@findrat@index+\@ne}% + \ifnum\POL@findrat@index>\POL@isolz@NbOfRoots + \else + \expandafter\POL@findrat@loop@secondpass@direct + \fi +}% +\def\POL@findrat@loop@bb{% + \PolEnsureIntervalLength{\POL@sturmname}{\POL@findrat@index}{-\POL@findrat@E}% + % ATTENTION THAT PERHAPS NOW THE ROOT IS KNOWN! + \PolSturmIfZeroExactlyKnown{\POL@sturmname}{\POL@findrat@index}% + \POL@findrat@loop@decimal + \POL@findrat@loop@b +}% +\def\POL@findrat@loop@b{% + \edef\POL@findrat@Lscaled{\xintMul{\POL@findrat@D}% + {\POL@xintexprGetVar{\POL@sturmname L_\POL@findrat@index}}}% + \edef\POL@findrat@Rscaled{\xintMul{\POL@findrat@D}% + {\POL@xintexprGetVar{\POL@sturmname R_\POL@findrat@index}}}% + \xintiiifNeg{\POL@findrat@Lscaled}% using ii version is an abuse + {% negative interval (right bound possibly zero!) + % truncate towards zero (i.e. to the right) the left bound + \edef\POL@findrat@Num{\xintNum{\POL@findrat@Lscaled}/1[0]}% + % interval boundaries are not root hence in case that was exact + % this will not be found as a root; check if in interval + \xintifLt\POL@findrat@Num\POL@findrat@Rscaled + \POL@findrat@loop@c + {}% iterate + }% + {% positive interval (left bound possibly zero!) + % truncate towards zero (i.e. to the left) the right bound + \edef\POL@findrat@Num{\xintNum{\POL@findrat@Rscaled}/1[0]}% + % check if in interval + \xintifGt\POL@findrat@Num\POL@findrat@Lscaled + \POL@findrat@loop@c + {}% iterate + }% +}% +\def\POL@findrat@loop@c{% + % safer to do the edef as \POL@findrat@x used later in storeit + \edef\POL@findrat@x{\xintIrr{\xintDiv\POL@findrat@Num\POL@findrat@D}[0]}% + \POL@xintfrac@getNDE\POL@findrat@x\POL@findrat@xN\POL@findrat@xD\POL@_ + \edef\POLuserpol@_findrat@oneterm{1.\noexpand\empty + {\xintiiOpp{\POL@findrat@xN}/1[0]}{\POL@findrat@xD/1[0]}}% + \POL@divide{\POL@sturmname\POL@sqfnorr}{_findrat@oneterm}% the one without mult. + \expandafter\POL@split\POL@R;\POL@degR\POL@polR + \ifnum\POL@degR=\m@ne % found a root + \POL@findrat@loop@storeit + \POL@findrat@loop@updatequotients + \POL@findrat@loop@getmultiplicity % will continue updating the mult. one + \fi + % iterate +}% +\def\POL@findrat@loop@storeit{% + % update storage, I can not use storeleftandright here (due to rawout etc...) + \expandafter + \xdef\csname POL_ZL\POL@sturmname*\POL@findrat@index\endcsname + {\PolDecToString{\POL@findrat@x}}% + \global\expandafter + \let\csname POL_ZR\POL@sturmname*\POL@findrat@index\expandafter\endcsname + \csname POL_ZL\POL@sturmname*\POL@findrat@index\endcsname + \global\expandafter + \let\csname POL_ZK\POL@sturmname*\POL@findrat@index\endcsname + \xint_stop_atfirstoftwo + \begingroup\xintglobaldefstrue + % skip some overhead of \xintdefvar... + % BUT attention to changes in xint 1.4 internal format ! + \XINT_expr_defvar_one{\POL@sturmname L_\POL@findrat@index}% + {{\POL@findrat@x}}% + \XINT_expr_defvar_one{\POL@sturmname R_\POL@findrat@index}% + {{\POL@findrat@x}}% + \XINT_expr_defvar_one{\POL@sturmname Z_\POL@findrat@index _isknown}% + {{1}}% + \endgroup +}% +\def\POL@findrat@loop@updatequotients{% + % attention last division must have been one testing vanishing of\POL@sqfnorr + \XINT_global\expandafter\let\csname POLuserpol@\POL@sturmname\POL@sqfnorr\endcsname\POL@Q + % quotient belongs to Z[X] and is primitive + \POL@mapcoeffs\POL@aux@toint{\POL@sturmname\POL@sqfnorr}% + % update the one with multiplicities + \POL@divide{\POL@sturmname\POL@norr}{_findrat@oneterm}% + \XINT_global\expandafter\let\csname POLuserpol@\POL@sturmname\POL@norr\endcsname\POL@Q + \POL@mapcoeffs\POL@aux@toint{\POL@sturmname\POL@norr} + % updating of \POL@findrat@D at end of execution of getmultiplicity +}% +\def\POL@findrat@loop@getmultiplicity{% + % the one without multiplicity must not be divided again! + % check if we have remaining multiplicity + \POL@divide{\POL@sturmname\POL@norr}{_findrat@oneterm}% + \expandafter\POL@split\POL@R;\POL@degR\POL@polR + \ifnum\POL@degR=\m@ne % yes + \XINT_global\expandafter\let\csname POLuserpol@\POL@sturmname\POL@norr\endcsname\POL@Q + \POL@mapcoeffs\POL@aux@toint{\POL@sturmname\POL@norr}% + \expandafter + \xdef + \csname POL_ZM\POL@sturmname*\POL@findrat@index\endcsname + {\the\numexpr + \csname POL_ZM\POL@sturmname*\POL@findrat@index\endcsname+\@ne}% + \expandafter\POL@findrat@loop@getmultiplicity + \else + % done with multiplicity for this rational root, update stuff + \edef\POL@findrat@nbofirrroots + {\the\numexpr\POL@findrat@nbofirrroots-\@ne}% + \@namedef{POL@IfMultIsKnown\POL@findrat@index}{\xint_firstoftwo}% + \edef\POL@findrat@D{\xintAbs{\PolLeadingCoeff{\POL@sturmname\POL@sqfnorr}}}% + \POL@xintfrac@getNDE\POL@findrat@D\POL@findrat@Dint\POL@_\POL@findrat@Dexp + \xintiiifOne{\POL@findrat@Dint} + {\let\POL@findrat@E\POL@findrat@Dexp} % aussi ok pour 1[0] + {\edef\POL@findrat@E{\the\numexpr\xintLen{\POL@findrat@Dint}% + +\POL@findrat@Dexp}}% + \fi +}% +\def\POL@findrat@getirrmult{% + % first get the GCD of remaining pol with its derivative + \POL@divide{\POL@sturmname\POL@norr}{\POL@sturmname\POL@sqfnorr}% + \expandafter\let + % attention au _ (cf. grosse astuce pour \POL@isolzmult@loop) + \csname POLuserpol@@_1\POL@sturmname _\endcsname\POL@Q + \ifnum\PolDegree{@_1\POL@sturmname _}>\z@ + % il reste des multiplicités (mais peut-être pour des racines complexes) + % (ou pour des racines en-dehors de l'intervalle optionnel) + % attention recyclage ici de \POL@isolzmult@loop qui dépend de + % la grosse astuce avec \@gobble + \POL@makeprimitive{@_1\POL@sturmname _}% + \let\POL@originalsturmname\POL@sturmname + % trick to get isolzmult@loop to define @@lastGCD to @_1sturmname_ + % because it will do \POL@sturmname _\POL@sturm@N _ + \edef\POL@sturmname{@_1\POL@sturmname}% + \let\POL@sturm@N\@gobble% ! + \let\POL@isolz@NbOfRoots@with_unknown_mult\POL@findrat@nbofirrroots + \POL@tosturm@makefirstprimitivefalse +\expanded{\unexpanded{% + \unless\ifxintveryverbose\xintverbosefalse\polnewpolverbosefalse\fi + \POL@isolzmult@loop +}\ifxintverbose\noexpand\xintverbosetrue\fi + \ifpolnewpolverbose\noexpand\polnewpolverbosetrue\fi}% + \POL@tosturm@makefirstprimitivetrue + \let\POL@sturmname\POL@originalsturmname + \fi +}% +\def\PolSturmIsolateZerosAndGetMultiplicities@{% + \POL@chkopt\POL@oPolSturmIsolateZerosAndGetMultiplicities@[\empty]% +}% +\def\POL@oPolSturmIsolateZerosAndGetMultiplicities@[#1]#2{% + % #1 optional E such that roots are searched in -10^E < x < 10^E + % both -10^E and +10^E must not be roots! + % #2 name of Sturm chain (already pre-computed) + \edef\POL@sturmname{#2}% + \edef\POL@sturm@N{\@nameuse{PolSturmChainLength_\POL@sturmname}}% + % isolate the roots (detects case of constant polynomial) + \PolSturmIsolateZeros@{\POL@sturmname}% + \ifnum\POL@isolz@NbOfRoots=\z@ + % no roots, define empty array nevertheless + \begingroup\globaldefs\@ne + \expandafter\xintAssignArray\expandafter\to\csname POL_ZM\POL@sturmname*\endcsname + \endgroup + \else + % all we currently know is that multiplicities are at least one + \begingroup\globaldefs\@ne + \expandafter\POL@initarray\csname POL_ZM\POL@sturmname*\endcsname{1}% + \endgroup + % check if GCD had positive degree (hence some roots, maybe complex, have + % multiplicity) + \ifnum\PolDegree{\POL@sturmname _\POL@sturm@N _}>\z@ + % scratch array of flags to signal known multiplicities + \POL@initarray\POL@IfMultIsKnown\xint_secondoftwo + % this count has utility for the case there are other roots + % either complex or outside interval (in case of optional argument) + \let\POL@isolz@NbOfRoots@with_unknown_mult\POL@isolz@NbOfRoots + % store Sturm chain name, it is needed and altered in isolzmult@loop + \let\POL@originalsturmname\POL@sturmname + \POL@tosturm@makefirstprimitivefalse +\expanded{\unexpanded{% + \unless\ifxintveryverbose\xintverbosefalse\polnewpolverbosefalse\fi + \POL@isolzmult@loop +}\ifxintverbose\noexpand\xintverbosetrue\fi + \ifpolnewpolverbose\noexpand\polnewpolverbosetrue\fi}% + \POL@tosturm@makefirstprimitivetrue + \let\POL@sturmname\POL@originalsturmname + \fi + \POL@isolzmult@defvar@M + \fi +}% +\def\POL@isolzmult@defvar@M{% + % Attention that is used not only in ...GetMultiplicities@ but also + % in FindRationalRoots + \begingroup\xintglobaldefstrue + % added at 0.7 + \let\x\POL@isolz@NbOfRoots + \xintloop + % skip some overhead of \xintdefvar... + % ATTENTION to xint 1.4 internal changes ! + \XINT_expr_defvar_one{\POL@sturmname M_\x}% + {{\csname POL_ZM\POL@sturmname*\x\endcsname}}% + \edef\x{\the\numexpr\x-\@ne}% + \ifnum\x>\z@ + \repeat + \endgroup +}% +\def\POL@isolzmult@loop{% + % we are here only if last iteration gave a new GCD still of degree > 0 + % \POL@sturm@N is the one from last iteration + % Attention to not use \POL@sturmname directly in first arg. of \PolToSturm + % Attention that we need for the case of known roots also to have the last + % GCD (with its multiplicities) known as a genuine polynomial + % - because of usage of \POL@eval in @isknown branch + % - because \PolToSturm@ does a \POL@let which would be anomalous + % if the extended structure is not existing + \edef\POL@isolzmult@lastGCD{\POL@sturmname _\POL@sturm@N _}% + \edef\POL@isolzmult@newsturmname{@_1\POL@sturmname}% + \POL@newpol{\POL@isolzmult@lastGCD}% + \PolToSturm@{\POL@isolzmult@lastGCD}{\POL@isolzmult@newsturmname}% + % now both \POL@sturmname and \POL@sturm@N have changed + \edef\POL@isolzmult@newGCDdegree{\PolDegree{\POL@sturmname _\POL@sturm@N _}}% + \let\POL@isolzmult@index\POL@isolz@NbOfRoots + \xintloop + % ATTENTION that this executes macros which also modifies \POL@sturmname! + % (but not \POL@sturm@N) + \POL@isolzmult@doone + \edef\POL@isolzmult@index{\the\numexpr\POL@isolzmult@index-\@ne}% + \if1\ifnum\POL@isolz@NbOfRoots@with_unknown_mult=\z@ 0\fi + \ifnum\POL@isolzmult@index=\z@ 0\fi 1% + \repeat + \let\POL@sturmname\POL@isolzmult@newsturmname + \if1\ifnum\POL@isolz@NbOfRoots@with_unknown_mult=\z@ 0\fi + % (if new GCD is constant, time to abort) + \ifnum\POL@isolzmult@newGCDdegree=\z@ 0\fi 1% + \expandafter\POL@isolzmult@loop + \fi +}% +\def\POL@isolzmult@doone{% + \csname POL@IfMultIsKnown\POL@isolzmult@index\endcsname + {}% nothing to do + {\POL@SturmIfZeroExactlyKnown{\POL@originalsturmname}% + {\POL@isolzmult@index}% + \POL@isolzmult@loop@isknown + \POL@isolzmult@loop@isnotknown + \POL@isolzmult@loop@sharedbody + }% +}% +\def\POL@isolzmult@loop@isknown{% + \xintifZero + % attention that \POL@eval requires a declared polynomial + {\POL@eval{\POL@isolzmult@lastGCD}% + {\POL@xintexprGetVar{\POL@originalsturmname L_\POL@isolzmult@index}}}% + {\let\POL@isolzmult@haszero\@ne}% + {\let\POL@isolzmult@haszero\z@}% +}% +\def\POL@isolzmult@loop@isnotknown{% + \edef\POL@isolzmult@loop@A + {\POL@xintexprGetVar{\POL@originalsturmname L_\POL@isolzmult@index}} + \edef\POL@isolzmult@loop@B + {\POL@xintexprGetVar{\POL@originalsturmname + R_\POL@isolzmult@index}} + % attention that \PolSetToNbOfZerosWithin sets \POL@sturmname to 2nd argument + \PolSetToNbOfZerosWithin + \POL@isolzmult@haszero % nb of zeros A < x <= B, here 0 or 1 + \POL@isolzmult@newsturmname + \POL@isolzmult@loop@A + \POL@isolzmult@loop@B +}% +\def\POL@isolzmult@loop@sharedbody{% + \ifnum\POL@isolzmult@haszero>\z@ + \expandafter + \xdef + \csname POL_ZM\POL@originalsturmname*\POL@isolzmult@index\endcsname + {\the\numexpr + \csname POL_ZM\POL@originalsturmname + *\POL@isolzmult@index\endcsname+\@ne}% + \else + % multiplicity now known, no need to check this index in future + \@namedef{POL@IfMultIsKnown\POL@isolzmult@index}{\xint_firstoftwo}% + \edef\POL@isolz@NbOfRoots@with_unknown_mult + {\the\numexpr\POL@isolz@NbOfRoots@with_unknown_mult-\@ne}% + \fi +}% +\def\PolSturmIsolateZeros@{% + \POL@chkopt\POL@oPolSturmIsolateZeros@[\empty]% +}% +\def\POL@oPolSturmIsolateZeros@[#1]#2{% + % #1 optional E such that roots are searched in -10^E < x < 10^E + % both -10^E and +10^E must not be roots! + % #2 name of Sturm chain (already pre-computed from a given polynomial) + % For reasons I have forgotten (no time now) this code **must** be used + % with a *normalized* Sturm chain. + \edef\POL@sturmname{#2}% + \edef\POL@sturmlength{\PolSturmChainLength{#2}}% + % attention to constant polynomial, we must redefine the arrays then + \ifnum\POL@sturmlength>\z@ + \ifx\empty#1\relax + \POL@isolz@getsignchanges@plusinf + \POL@isolz@getsignchanges@minusinf + \else + \edef\POL@isolz@E{\the\numexpr\xint_zapspaces #1 \xint_gobble_i\relax}% + \POL@sturmchain@getSV@at{1[\POL@isolz@E]}% + \let\POL@isolz@plusinf@SV \POL@sturmchain@SV + \let\POL@isolz@plusinf@sign\POL@sturmchain@sign + \POL@sturmchain@getSV@at{-1[\POL@isolz@E]}% + \let\POL@isolz@minusinf@SV \POL@sturmchain@SV + \let\POL@isolz@minusinf@sign\POL@sturmchain@sign + \ifnum\POL@isolz@plusinf@sign=\z@ + \PackageError{polexpr}% +{The polynomial #2 vanishes at set upper bound 10^\POL@isolz@E}% +{Compile again with a bigger exponent in source. (X to abort).}% + \fi + \ifnum\POL@isolz@minusinf@sign=\z@ + \PackageError{polexpr}% +{The polynomial #2 vanishes at set lower bound -10^\POL@isolz@E}% +{Compile again with a bigger exponent in source. (X to abort).}% + \fi + \fi + \edef\POL@isolz@NbOfRoots + {\the\numexpr\POL@isolz@minusinf@SV-\POL@isolz@plusinf@SV}% + \else + % constant polynomial + \def\POL@isolz@NbOfRoots{0}% + \fi + \ifnum\POL@isolz@NbOfRoots=\z@ + \begingroup\globaldefs\@ne + \expandafter\xintAssignArray\expandafter\to\csname POL_ZL#2*\endcsname + \expandafter\xintAssignArray\expandafter\to\csname POL_ZR#2*\endcsname + \expandafter\xintAssignArray\expandafter\to\csname POL_ZK#2*\endcsname + \endgroup + \else + \begingroup\globaldefs\@ne + \expandafter\POL@initarray\csname POL_ZL#2*\endcsname{0}% + \expandafter\POL@initarray\csname POL_ZR#2*\endcsname{0}% + \expandafter\POL@initarray\csname POL_ZK#2*\endcsname + \xint_stop_atsecondoftwo + \endgroup + \ifx\empty#1\relax\expandafter\POL@isolz@getaprioribound\fi + \expandafter\POL@isolz@main + \fi +}% +\def\POL@initarray#1#2{% +% ATTENTION, if only one item, \xintAssignArray UNBRACES IT +% so we use an \empty trick to avoid that. Maybe considered a bug of xinttools? + \expandafter\xintAssignArray\expandafter\empty + \romannumeral\xintreplicate{\POL@isolz@NbOfRoots}{{#2}}\to#1% +}% +\def\POL@isolz@getsignchanges@plusinf{% + % Count number of sign changes at plus infinity in Sturm sequence + \def\POL@isolz@plusinf@SV{0}% + \edef\POL@isolz@lastsign{\xintiiSgn{\PolLeadingCoeff{\POL@sturmname _0}}}% + \let\POL@isolz@plusinf@sign\POL@isolz@lastsign + \POL@count\@ne + \xintloop + \edef\POL@isolz@newsign + {\xintiiSgn{\PolLeadingCoeff{\POL@sturmname _\the\POL@count}}}% + \unless\ifnum\POL@isolz@newsign=\POL@isolz@lastsign + \edef\POL@isolz@plusinf@SV{\the\numexpr\POL@isolz@plusinf@SV+\@ne}% + \fi + \let\POL@isolz@lastsign=\POL@isolz@newsign + \ifnum\POL@sturmlength>\POL@count + \advance\POL@count\@ne + \repeat +}% +\def\POL@isolz@getsignchanges@minusinf{% + % Count number of sign changes at minus infinity in Sturm sequence + \def\POL@isolz@minusinf@SV{0}% + \edef\POL@isolz@lastsign{\xintiiSgn{\PolLeadingCoeff{\POL@sturmname _0}}}% + \ifodd\PolDegree{\POL@sturmname _0} + \edef\POL@isolz@lastsign{\xintiiOpp{\POL@isolz@lastsign}}% + \fi + \let\POL@isolz@minusinf@sign\POL@isolz@lastsign + \POL@count\@ne + \xintloop + \edef\POL@isolz@newsign + {\xintiiSgn{\PolLeadingCoeff{\POL@sturmname _\the\POL@count}}}% + \ifodd\PolDegree{\POL@sturmname _\the\POL@count} + \edef\POL@isolz@newsign{\xintiiOpp{\POL@isolz@newsign}}% + \fi + \unless\ifnum\POL@isolz@newsign=\POL@isolz@lastsign + \edef\POL@isolz@minusinf@SV{\the\numexpr\POL@isolz@minusinf@SV+\@ne}% + \fi + \let\POL@isolz@lastsign=\POL@isolz@newsign + \ifnum\POL@sturmlength>\POL@count + \advance\POL@count\@ne + \repeat +}% +% utility macro for a priori bound on root decimal exponent, via Float Rounding +\def\POL@isolz@updateE #1e#2;% + {\unless\ifnum#2<\POL@isolz@E\space\edef\POL@isolz@E{\the\numexpr#2+\@ne}\fi}% +\def\POL@isolz@getaprioribound{% + \PolAssign{\POL@sturmname _0}\toarray\POL@arrayA + \edef\POL@isolz@leading{\POL@arrayA{\POL@arrayA{0}}}% + \POL@count\z@ + \xintloop + \advance\POL@count\@ne + \ifnum\POL@arrayA{0}>\POL@count + \expandafter\edef\csname POL@arrayA\the\POL@count\endcsname + {\xintDiv{\POL@arrayA\POL@count}\POL@isolz@leading}% + \repeat + \def\POL@isolz@E{1}% WE SEEK SMALLEST E SUCH HAT -10^E < roots < +10^E + \advance\POL@count\m@ne + \xintloop + \ifnum\POL@count>\z@ + \expandafter\POL@isolz@updateE + % use floating point to get decimal exponent + \romannumeral0\xintfloat[4]% should I use with [2] rather? (should work) + {\xintAdd{1/1[0]}{\xintAbs{\POL@arrayA\POL@count}}};% + \advance\POL@count\m@ne + \repeat + % \ifxintverbose\xintMessage{polexpr}{Info}% + % {Roots a priori bounded in absolute value by 10 to the \POL@isolz@E.}% + % \fi +}% +\def\POL@IsoRight@raw{\POL@IsoRight@Int/1[\POL@isolz@E]}% +\def\POL@IsoLeft@raw {\POL@IsoLeft@Int/1[\POL@isolz@E]}% +\def\POL@IsoRight@rawout{% + \ifnum\POL@IsoRightSign=\z@\expandafter\xintREZ\fi\POL@IsoRight@raw +}% +\def\POL@IsoLeft@rawout{% + \ifnum\POL@IsoRightSign=\z@ + \expandafter\xint_firstoftwo\else\expandafter\xint_secondoftwo + \fi{\xintREZ\POL@IsoRight@raw}% + {\POL@IsoLeft@Int/1[\POL@isolz@E]}% +}% +\def\POL@isolz@main {% +% NOTE 2018/02/16. THIS WILL PRESUMABLY BE RE-ORGANIZED IN FUTURE TO DO +% FIRST POSITIVE ROOTS THEN NEGATIVE ROOTS VIA CHANGE OF VARIABLE TO OPPOSITE. + \global\POL@isolz@nextwillneedrefinefalse + \def\POL@IsoRight@Int{0}% + \POL@sturmchain@getSV@at\POL@IsoRight@raw + \let\POL@IsoRightSV \POL@sturmchain@SV + \let\POL@IsoRightSign\POL@sturmchain@sign + \let\POL@IsoAtZeroSV \POL@IsoRightSV + \let\POL@IsoAtZeroSign\POL@IsoRightSign + \ifnum\POL@IsoAtZeroSign=\z@ + \xdef\POL@isolz@IntervalIndex + {\the\numexpr\POL@isolz@minusinf@SV-\POL@IsoRightSV}% + \POL@refine@storeleftandright % store zero root, \POL@IsoRightSign is zero + \edef\POL@IsoRightSV{\the\numexpr\POL@IsoRightSV+\@ne}% +% subtlety here if original polynomial had multiplicities, but ok. I checked! + \edef\POL@IsoRightSign % evaluated twice, but that's not so bad + {\xintiiOpp{\xintiiSgn{\POL@eval{\POL@sturmname _1}{0/1[0]}}}}% + \fi + \def\POL@IsoLeft@Int{-1}% -10^E isn't a root! + \let\POL@IsoLeftSV \POL@isolz@minusinf@SV + \let\POL@IsoLeftSign\POL@isolz@minusinf@sign + % \POL@IsoRight@SV was modified if zero is a root + \edef\POL@isolz@NbOfNegRoots{\the\numexpr\POL@IsoLeftSV-\POL@IsoRightSV}% + \gdef\POL@isolz@IntervalIndex{0}% + \let\POL@isolz@@E\POL@isolz@E + \ifnum\POL@isolz@NbOfNegRoots>\z@ +% refactored at 0.7 to fix cases leading to an intervals with zero as end-point + \POL@isolz@findroots@neg + \fi + \let\POL@isolz@E\POL@isolz@@E + \def\POL@IsoLeft@Int{0}% + \let\POL@IsoLeftSV \POL@IsoAtZeroSV % véritable SV en zéro + \let\POL@IsoLeftSign\POL@IsoAtZeroSign% véritable signe en zéro + \ifnum\POL@IsoLeftSign=\z@ + \xdef\POL@isolz@IntervalIndex{\the\numexpr\POL@isolz@IntervalIndex+\@ne}% + \fi + \let\POL@@IsoRightSV \POL@isolz@plusinf@SV + \let\POL@@IsoRightSign\POL@isolz@plusinf@sign % 10^E not a root! + \edef\POL@isolz@NbOfPosRoots + {\the\numexpr\POL@IsoLeftSV-\POL@@IsoRightSV}% attention @@ + \ifnum\POL@isolz@NbOfPosRoots>\z@ + % always do that to avoid zero as end-point whether it is a root or not + \global\POL@isolz@nextwillneedrefinetrue + \POL@isolz@findroots@pos + \fi +}% +\def\POL@isolz@findroots@neg{% + \def\POL@IsoRight@Int{-1}% + \POL@isolz@findnextzeroboundeddecade@neg + \def\POL@IsoLeft@Int{-10}% + \let\POL@@IsoRightSign\POL@IsoRightSign % a zero there is possible + \let\POL@@IsoRightSV \POL@IsoRightSV + % this will do possibly recursive \POL@isolz@check's + \POL@isolz@explorenexteightsubdecades@neg + \ifnum\POL@isolz@IntervalIndex<\POL@isolz@NbOfNegRoots\space + % above did not explore -2, -1 for this optimization (SV known at Right) + \def\POL@IsoRight@Int{-1}% + \let\POL@IsoRightSign\POL@@IsoRightSign + \let\POL@IsoRightSV \POL@@IsoRightSV + \POL@isolz@check + \ifnum\POL@isolz@IntervalIndex<\POL@isolz@NbOfNegRoots\space + \def\POL@IsoLeft@Int{-1}% + \let\POL@IsoLeftSign\POL@@IsoRightSign + \let\POL@IsoLeftSV \POL@@IsoRightSV + % I don't like being inside TeX conditionals + \expandafter\expandafter\expandafter\POL@isolz@findroots@neg + \fi + \fi +}% +\def\POL@isolz@findnextzeroboundeddecade@neg{% + \xintloop + \edef\POL@isolz@E{\the\numexpr\POL@isolz@E-\@ne}% + \POL@sturmchain@getSV@at\POL@IsoRight@raw + \let\POL@IsoRightSV \POL@sturmchain@SV + \let\POL@IsoRightSign\POL@sturmchain@sign + % would an \ifx test be quicker? (to be checked) + \ifnum\POL@IsoRightSV=\POL@IsoLeftSV\space + % no roots in-between, iterate + \repeat +}% +\def\POL@isolz@explorenexteightsubdecades@neg{% + \xintloop + \edef\POL@IsoRight@Int{\the\numexpr\POL@IsoLeft@Int+\@ne}% + % we could arguably do a more efficient dichotomy here + \POL@sturmchain@getSV@at\POL@IsoRight@raw + \let\POL@IsoRightSV \POL@sturmchain@SV + \let\POL@IsoRightSign\POL@sturmchain@sign + \POL@isolz@check % may recurse if multiple roots are to be found + \ifnum\POL@isolz@IntervalIndex=\POL@isolz@NbOfNegRoots\space + \expandafter\xintbreakloop + \fi + \let\POL@IsoLeft@Int\POL@IsoRight@Int + \let\POL@IsoLeftSign\POL@IsoRightSign + \let\POL@IsoLeftSV\POL@IsoRightSV + \ifnum\POL@IsoRight@Int < -\tw@ + \repeat +}% +\def\POL@isolz@findroots@pos{% + % remark (2018/12/08), this needs some refactoring, I hardly understand + % the logic and it hides most into the recursion done by \POL@isolz@check + % It would probably make more sense to proceed like done for the negative + % but here finding the largest roots first. + \def\POL@IsoRight@Int{1}% + \POL@isolz@findnextzeroboundeddecade@pos + \unless\ifnum\POL@IsoRightSV=\POL@IsoLeftSV\space + % this actually explores the whole of some interval (0, 10^{e-1}] + % in a context where some roots are known to be in (10^{e-1}, 10^{e}] + % and none are larger + \POL@isolz@check % will recurse inside groups if needed with modified E + \fi + % we know get the roots in the last 9 decades from 10^{e-1} to 10^{e} + % we should arguably do a more efficient dichotomy here + \def\POL@IsoLeft@Int{1}% + \let\POL@IsoLeftSV\POL@IsoRightSV + \let\POL@IsoLeftSign\POL@IsoRightSign + \xintloop + \edef\POL@IsoRight@Int{\the\numexpr\POL@IsoLeft@Int+\@ne}% + \POL@sturmchain@getSV@at\POL@IsoRight@raw + \let\POL@IsoRightSV \POL@sturmchain@SV + \let\POL@IsoRightSign\POL@sturmchain@sign + \POL@isolz@check % recurses in needed + \let\POL@IsoLeft@Int\POL@IsoRight@Int + \let\POL@IsoLeftSign\POL@IsoRightSign + \let\POL@IsoLeftSV\POL@IsoRightSV + \ifnum\POL@isolz@IntervalIndex=\POL@isolz@NbOfRoots\space + \expandafter\xintbreakloop + \fi + \ifnum\POL@IsoLeft@Int < \xint_c_ix + \repeat + \ifnum\POL@isolz@IntervalIndex<\POL@isolz@NbOfRoots\space + % get now the last, rightmost, root (or roots) + \def\POL@IsoRight@Int{10}% + \let\POL@IsoRightSign\POL@@IsoRightSign + \let\POL@IsoRightSV\POL@@IsoRightSV + \POL@isolz@check + \fi +}% +\def\POL@isolz@findnextzeroboundeddecade@pos{% + \xintloop + \edef\POL@isolz@E{\the\numexpr\POL@isolz@E-\@ne}% + \POL@sturmchain@getSV@at\POL@IsoRight@raw + \let\POL@IsoRightSV \POL@sturmchain@SV + \let\POL@IsoRightSign\POL@sturmchain@sign + \ifnum\POL@IsoRightSV=\POL@@IsoRightSV\space + \let\POL@@IsoRightSign\POL@IsoRightSign % root here possible! + \repeat +}% +\def\POL@isolz@check{% \POL@IsoRightSign must be ready for use here +% \ifxintverbose +% \xintMessage{polexpr}{Info}% +% {\the\numexpr\POL@IsoLeftSV-\POL@IsoRightSV\relax\space roots +% in (\POL@IsoLeft@raw,\POL@IsoRight@raw] (E = \POL@isolz@E)}% +% \fi + \ifcase\numexpr\POL@IsoLeftSV-\POL@IsoRightSV\relax + % no root in ]left, right] + \global\POL@isolz@nextwillneedrefinefalse + \or + % exactly one root in ]left, right] + \xdef\POL@isolz@IntervalIndex{\the\numexpr\POL@isolz@IntervalIndex+\@ne}% + \ifnum\POL@IsoRightSign=\z@ + % if right boundary is a root, ignore previous flag + \global\POL@isolz@nextwillneedrefinefalse + \fi + % if left boundary is known to have been a root we refine interval + \ifPOL@isolz@nextwillneedrefine + \expandafter\expandafter\expandafter\POL@isolz@refine + \else + % \POL@IsoRightSign is zero iff root now exactly known + \POL@refine@storeleftandright + \ifnum\POL@IsoRightSign=\z@ + \global\POL@isolz@nextwillneedrefinetrue + \fi + \fi + \else + % more than one root, we need to recurse + \expandafter\POL@isolz@recursedeeper + \fi +}% +\def\POL@isolz@recursedeeper{% +% NOTE 2018/02/16. I SHOULD DO A REAL BINARY DICHOTOMY HERE WHICH ON AVERAGE +% SHOULD BRING SOME GAIN (LIKE WHAT IS ALREADY DONE FOR THE "refine" MACROS. +% THUS IN FUTURE THIS MIGHT BE REFACTORED. +\begingroup + \edef\POL@isolz@E{\the\numexpr\POL@isolz@E-\@ne}% + \edef\POL@@IsoRight@Int{\xintDSL{\POL@IsoRight@Int}}% + \let\POL@@IsoRightSign \POL@IsoRightSign + \let\POL@@IsoRightSV \POL@IsoRightSV + \edef\POL@IsoLeft@Int {\xintDSL{\POL@IsoLeft@Int}}% + \xintiloop[1+1] + \edef\POL@IsoRight@Int{\xintInc{\POL@IsoLeft@Int}}% + \POL@sturmchain@getSV@at\POL@IsoRight@raw + \let\POL@IsoRightSV \POL@sturmchain@SV + \let\POL@IsoRightSign\POL@sturmchain@sign + \POL@isolz@check + \let\POL@IsoLeft@Int\POL@IsoRight@Int + \let\POL@IsoLeftSV\POL@IsoRightSV + \let\POL@IsoLeftSign\POL@IsoRightSign% not used, actually + \ifnum\POL@IsoLeftSV=\POL@@IsoRightSV\space + \expandafter\xintbreakiloop + \fi + \ifnum\xintiloopindex < \xint_c_ix + \repeat + \let\POL@IsoRight@Int\POL@@IsoRight@Int + \let\POL@IsoRightSign\POL@@IsoRightSign + \let\POL@IsoRightSV \POL@@IsoRightSV + % if we exited the loop via breakiloop this is superfluous + % but it only costs one \ifnum + \POL@isolz@check +\endgroup +}% +\def\POL@isolz@refine{% + % starting point is first root = left < unique second root < right + % even if we hit exactly via refinement second root, we set flag false as + % processing will continue with original right end-point, which isn't a root + \global\POL@isolz@nextwillneedrefinefalse +\begingroup + \let\POL@@IsoRightSign\POL@IsoRightSign % already evaluated + \xintloop + \edef\POL@isolz@E{\the\numexpr\POL@isolz@E-\@ne}% + \edef\POL@IsoLeft@Int {\xintDSL{\POL@IsoLeft@Int}}% + \edef\POL@IsoRight@Int{\xintInc{\POL@IsoLeft@Int}}% + \edef\POL@IsoRightSign + {\xintiiSgn{\POL@eval{\POL@sturmname _0}{\POL@IsoRight@raw}}}% + \ifnum\POL@IsoRightSign=\POL@@IsoRightSign\space + \repeat + % now second root has been separated from the one at left end point +% we update the storage of the root at left for it to have the same number +% of digits in mantissa. No, I decided not to do that to avoid complications. + % \begingroup + % \let\POL@IsoRight@Int\POL@IsoLeft@Int + % \def\POL@IsoRightSign{0}% + % \edef\POL@isolz@IntervalIndex{\the\numexpr\POL@isolz@IntervalIndex-\@ne}% + % \POL@refine@storeleftandright + % \endgroup + \edef\POL@@IsoRight@Int{\xintDSL{\xintInc{\xintDSR{\POL@IsoLeft@Int}}}}% + \let\POL@IsoLeft@Int\POL@IsoRight@Int + \let\POL@IsoLeftSign\POL@IsoRightSign + \ifnum\POL@IsoRightSign=\z@ % check if new Left is actually a root + \else + \edef\POL@IsoRight@Int{\xintDec{\POL@@IsoRight@Int}}% + \edef\POL@IsoRightSign + {\xintiiSgn{\POL@eval{\POL@sturmname _0}{\POL@IsoRight@raw}}}% + \ifnum\POL@IsoRightSign=\POL@@IsoRightSign\space + \POL@refine@doonce % we need to locate in interval (1, 9) in local scale + \else + \let\POL@IsoLeft@Int\POL@IsoRight@Int + \ifnum\POL@IsoRightSign=\z@ + \def\POL@IsoLeftSign{0}% + \else + \let\POL@IsoRight@Int\POL@@IsoRight@Int + % the IsoRightSign is now wrong but here we don't care + \fi\fi + \fi + % on exit, exact root found iff \POL@IsoRightSign is zero + \POL@refine@storeleftandright +\endgroup +}% +\def\POL@refine@doonce{% if exact root is found, always in IsoRight on exit +% NOTE: FUTURE REFACTORING WILL GET RID OF \xintiiAdd WHICH ARE A BIT COSTLY +% BUT BASICALLY NEEDED TO HANDLE BOTH NEGATIVE AND POSITIVE HERE. +% I WILL RE-ORGANIZE THE WHOLE THING IN FUTURE TO GET ROOTS STARTING FROM +% THE ORIGIN AND SIMPLY RE-LABEL THE NEGATIVE ONE AT THE END. 2018/02/16. + \let\POL@@IsoRight@Int\POL@IsoRight@Int % 9 + \let\POL@@IsoRightSign\POL@IsoRightSign + \edef\POL@IsoRight@Int{\xintiiAdd{4}{\POL@IsoLeft@Int}}% 5 + \edef\POL@IsoRightSign + {\xintiiSgn{\POL@eval{\POL@sturmname _0}{\POL@IsoRight@raw}}}% + \ifnum\POL@IsoRightSign=\POL@IsoLeftSign\space + \let\POL@IsoLeft@Int\POL@IsoRight@Int % 5 + \edef\POL@IsoRight@Int{\xintiiAdd{2}{\POL@IsoLeft@Int}}% + \edef\POL@IsoRightSign + {\xintiiSgn{\POL@eval{\POL@sturmname _0}{\POL@IsoRight@raw}}}% + \ifnum\POL@IsoRightSign=\POL@IsoLeftSign\space + \let\POL@IsoLeft@Int\POL@IsoRight@Int % 7 + \edef\POL@IsoRight@Int{\xintInc{\POL@IsoLeft@Int}}% + \edef\POL@IsoRightSign + {\xintiiSgn{\POL@eval{\POL@sturmname _0}{\POL@IsoRight@raw}}}% + \ifnum\POL@IsoRightSign=\POL@IsoLeftSign\space + \let\POL@IsoLeft@Int\POL@IsoRight@Int % 8 + \let\POL@IsoRight@Int\POL@@IsoRight@Int % 9 + \let\POL@IsoRightSign\POL@@IsoRightSign % opposite of one at left + \fi % else 7, 8 with possible root at 8 + \else + \ifnum\POL@IsoRightSign=\z@ + \let\POL@IsoLeft@Int\POL@IsoRight@Int % root at 7 + \def\POL@IsoLeftSign{0}% + \else + \let\POL@@IsoRight@Int\POL@IsoRight@Int % 7 + \edef\POL@IsoRight@Int{\xintInc{\POL@IsoLeft@Int}}% 6 + \edef\POL@IsoRightSign + {\xintiiSgn{\POL@eval{\POL@sturmname _0}{\POL@IsoRight@raw}}}% + \ifnum\POL@IsoRightSign=\POL@IsoLeftSign\space + \let\POL@IsoLeft@Int\POL@IsoRight@Int % 6 + \let\POL@IsoRight@Int\POL@@IsoRight@Int % 7 + \let\POL@IsoRightSign\POL@@IsoRightSign + \fi % else 5, 6 with possible root at 6 + \fi\fi + \else + \ifnum\POL@IsoRightSign=\z@ + \let\POL@IsoLeft@Int\POL@IsoRight@Int % root at 5 + \def\POL@IsoLeftSign{0}% + \else + \let\POL@@IsoRight@Int\POL@IsoRight@Int % 5 + \edef\POL@IsoRight@Int{\xintiiAdd{2}{\POL@IsoLeft@Int}}% + \edef\POL@IsoRightSign + {\xintiiSgn{\POL@eval{\POL@sturmname _0}{\POL@IsoRight@raw}}}% + \ifnum\POL@IsoRightSign=\POL@IsoLeftSign\space + \let\POL@IsoLeft@Int\POL@IsoRight@Int % 3 + \edef\POL@IsoRight@Int{\xintInc{\POL@IsoLeft@Int}}% 4 + \edef\POL@IsoRightSign + {\xintiiSgn{\POL@eval{\POL@sturmname _0}{\POL@IsoRight@raw}}}% + \ifnum\POL@IsoRightSign=\POL@IsoLeftSign\space + \let\POL@IsoLeft@Int\POL@IsoRight@Int % 4 + \let\POL@IsoRight@Int\POL@@IsoRight@Int % 5 + \let\POL@IsoRightSign\POL@@IsoRightSign + \fi % else 3, 4 with possible root at 4 + \else + \ifnum\POL@IsoRightSign=\z@ + \let\POL@IsoLeft@Int\POL@IsoRight@Int % root at 3 + \def\POL@IsoLeftSign{0}% + \else + \let\POL@@IsoRight@Int\POL@IsoRight@Int % 3 + \edef\POL@IsoRight@Int{\xintInc{\POL@IsoLeft@Int}}% 2 + \edef\POL@IsoRightSign + {\xintiiSgn{\POL@eval{\POL@sturmname _0}{\POL@IsoRight@raw}}}% + \ifnum\POL@IsoRightSign=\POL@IsoLeftSign\space + \let\POL@IsoLeft@Int\POL@IsoRight@Int % 2 + \let\POL@IsoRight@Int\POL@@IsoRight@Int % 3 + \let\POL@IsoRightSign\POL@@IsoRightSign + \fi % else 1, 2 with possible root at 2 + \fi\fi + \fi\fi +}% +\def\POL@refine@storeleftandright{% + \expandafter + \xdef\csname POL_ZL\POL@sturmname*\POL@isolz@IntervalIndex\endcsname + {\PolDecToString{\POL@IsoLeft@rawout}}% + \expandafter + \xdef\csname POL_ZR\POL@sturmname*\POL@isolz@IntervalIndex\endcsname + {\PolDecToString{\POL@IsoRight@rawout}}% + % added at 0.6 + \ifnum\POL@IsoRightSign=\z@ + \global + \expandafter + \let\csname POL_ZK\POL@sturmname*\POL@isolz@IntervalIndex\endcsname + \xint_stop_atfirstoftwo + \fi + \begingroup\xintglobaldefstrue + % skip some overhead of \xintdefvar... + % Let me repeat: ATTENTION to change of internal format at xint 1.4 + \XINT_expr_defvar_one{\POL@sturmname L_\POL@isolz@IntervalIndex}% + {{\POL@IsoLeft@rawout}}% + \XINT_expr_defvar_one{\POL@sturmname R_\POL@isolz@IntervalIndex}% + {{\POL@IsoRight@rawout}}% + % added at 0.7 + \XINT_expr_defvar_one{\POL@sturmname Z_\POL@isolz@IntervalIndex _isknown}% + {{\ifnum\POL@IsoRightSign=\z@ 1\else 0\fi}}% + \endgroup +}% +%% \PolRefineInterval +%% ATTENTION TO xint 1.4 INTERNAL CHANGES +\def\POL@xintexprGetVar#1{\expandafter\expandafter\expandafter\xint_firstofone + \csname XINT_expr_varvalue_#1\endcsname}% +% attention, also used by \POL@findrat@loop@a +\def\POL@get@IsoLeft@rawin{% + \edef\POL@IsoLeft@rawin + {\POL@xintexprGetVar{\POL@sturmname L_\POL@isolz@IntervalIndex}}% +}% +% attention, also used by \POL@findrat@loop@a +\def\POL@get@IsoRight@rawin{% + \edef\POL@IsoRight@rawin + {\POL@xintexprGetVar{\POL@sturmname R_\POL@isolz@IntervalIndex}}% +}% +% attention, also used by \POL@findrat@loop@a +\def\POL@get@Int@aux #1/1[#2]#3#4{\edef#3{\xintDSH{#4-#2}{#1}}}% +\def\POL@get@IsoLeft@Int{% + \expandafter\POL@get@Int@aux\POL@IsoLeft@rawin\POL@IsoLeft@Int\POL@isolz@E +}% +\def\PolRefineInterval{\POL@ifstar\POL@srefine@start\POL@refine@start}% +\def\POL@refine@start{% + \POL@chkopt\POL@oPOL@refine@start[1]% +}% +\def\POL@oPOL@refine@start[#1]#2#3{% + \edef\POL@isolz@IntervalIndex{\the\numexpr#3}% + \edef\POL@sturmname{#2}% + \expandafter\POL@refine@sharedbody\expandafter + {\expandafter\POL@refine@loop\expandafter{\the\numexpr#1}}% +}% +\def\POL@srefine@start#1#2{% + \edef\POL@isolz@IntervalIndex{\the\numexpr#2}% + \edef\POL@sturmname{#1}% + \POL@refine@sharedbody + {\let\POL@refine@left@next\POL@refine@main % we want to recurse if needed + \let\POL@refine@right@next\POL@refine@main % we want to recurse if needed + \POL@refine@main}% +}% +\def\POL@refine@sharedbody#1{% + \POL@get@IsoLeft@rawin + \edef\POL@IsoLeftSign + {\xintiiSgn{\POL@eval{\POL@sturmname _0}{\POL@IsoLeft@rawin}}}% + \ifnum\POL@IsoLeftSign=\z@ + % do nothing if that interval was already a singleton + \else + % else both end-points are not roots and there is a single one in-between + \POL@get@IsoRight@rawin + \edef\POL@IsoRightSign{\the\numexpr-\POL@IsoLeftSign}% + \edef\POL@isolz@E{\expandafter\POL@refine@getE + % je pense que le xintrez ici est superflu + \romannumeral0\xintrez{\xintSub{\POL@IsoRight@rawin}{\POL@IsoLeft@rawin}}}% + \POL@get@IsoLeft@Int + \edef\POL@IsoRight@Int{\xintInc{\POL@IsoLeft@Int}}% + #1% + \POL@refine@storeleftandright % \POL@IsoRightSign not zero + \fi +}% +\def\POL@refine@loop#1{% + \let\POL@refine@left@next \empty % no recursion at end sub-intervals + \let\POL@refine@right@next\empty + \xintiloop[1+1] + \POL@refine@main + \ifnum\POL@IsoRightSign=\z@ + \expandafter\xintbreakiloop + \fi + \ifnum\xintiloopindex<#1 + \repeat +}% +\def\POL@refine@main{% + \edef\POL@isolz@E{\the\numexpr\POL@isolz@E-\@ne}% + \edef\POL@IsoLeft@Int{\xintDSL{\POL@IsoLeft@Int}}% + \edef\POL@IsoRight@Int{\xintDSL{\POL@IsoRight@Int}}% + \let\POL@@IsoRight@Int\POL@IsoRight@Int + \let\POL@@IsoRightSign\POL@IsoRightSign + \edef\POL@IsoRight@Int{\xintInc{\POL@IsoLeft@Int}}% + \edef\POL@IsoRightSign + {\xintiiSgn{\POL@eval{\POL@sturmname _0}{\POL@IsoRight@raw}}}% + \ifnum\POL@IsoRightSign=\z@ + \let\POL@IsoLeft@Int\POL@IsoRight@Int % root at 1 + \def\POL@IsoLeftSign{0}% + \let\POL@next\empty + \else + \ifnum\POL@IsoRightSign=\POL@@IsoRightSign\space + \let\POL@next\POL@refine@left@next % may be \empty or \POL@refine@main for recursion + \let\POL@refine@right@next\empty + \else + \let\POL@IsoLeft@Int\POL@IsoRight@Int + \edef\POL@IsoRight@Int{\xintDec{\POL@@IsoRight@Int}}% + \edef\POL@IsoRightSign + {\xintiiSgn{\POL@eval{\POL@sturmname _0}{\POL@IsoRight@raw}}}% + \ifnum\POL@IsoRightSign=\z@ + \let\POL@IsoLeft@Int\POL@IsoRight@Int % root at 9 + \def\POL@IsoLeftSign{0}% + \let\POL@next\empty + \else + \ifnum\POL@IsoRightSign=\POL@@IsoRightSign\space + \let\POL@next\POL@refine@doonce + \else + \let\POL@IsoLeft@Int\POL@IsoRight@Int + \let\POL@IsoRight@Int\POL@@IsoRight@Int + \let\POL@IsoRightSign\POL@@IsoRightSign + \let\POL@next\POL@refine@right@next + \let\POL@refine@left@next\empty + \fi + \fi + \fi\fi + \POL@next +}% +% lacking pre-defined xintfrac macro here (such as an \xintRawExponent) +\def\POL@refine@getE#1[#2]{#2}% \xintREZ already applied, for safety +% +% +\def\PolIntervalWidth#1#2{% +% le \xintRez est à cause des E positifs, car trailing zéros explicites +% si je travaillais à partir des variables xintexpr directement ne devrait +% pas être nécessaire, mais trop fragile par rapport à chgt internes possibles + \romannumeral0\xintrez{\xintSub{\@nameuse{POL_ZR#1*}{#2}}% + {\@nameuse{POL_ZL#1*}{#2}}} +}% +\def\PolEnsureIntervalLengths#1#2{% #1 = Sturm chain name, + % localize roots in intervals of length at most 10^{#2} + \edef\POL@sturmname{#1}% + \edef\POL@ensure@targetE{\the\numexpr#2}% + \edef\POL@nbofroots{\csname POL_ZL\POL@sturmname*0\endcsname}% + \ifnum\POL@nbofroots>\z@ + \expandafter\POL@ensureintervallengths + \fi +}% +\def\POL@ensureintervallengths{% + \POL@count\z@ + % \POL@count used by \POL@sturmchain@getSV@at but latter not used + \xintloop + \advance\POL@count\@ne + \edef\POL@isolz@IntervalIndex{\the\POL@count}% + \POL@ensure@one + \ifnum\POL@nbofroots>\POL@count + \repeat +}% +\def\PolEnsureIntervalLength#1#2#3{% #1 = Sturm chain name, + % #2 = index of interval + % localize roots in intervals of length at most 10^{#3} + \edef\POL@sturmname{#1}% + \edef\POL@ensure@targetE{\the\numexpr#3}% + \edef\POL@isolz@IntervalIndex{\the\numexpr#2}% +% peut-être autoriser -1, -2, ... ? + \ifnum\POL@isolz@IntervalIndex>\z@ +% 0.7, add this safeguard but attention means this structure must be in place + \ifnum\csname POL_ZL\POL@sturmname*0\endcsname>\z@ +% je ne fais pas les \expandafter mais je préfèrerais ne pas être à l'intérieur + \POL@ensure@one + \fi + \fi +}% +\def\POL@ensure@one{% + \POL@get@IsoLeft@rawin + \POL@get@IsoRight@rawin + \edef\POL@ensure@delta{\xintREZ{\xintSub{\POL@IsoRight@rawin}{\POL@IsoLeft@rawin}}}% + \xintiiifZero{\POL@ensure@delta} + {} + {\edef\POL@isolz@E{\expandafter\POL@refine@getE\POL@ensure@delta}% + \POL@get@IsoLeft@Int + \edef\POL@IsoRight@Int{\xintInc{\POL@IsoLeft@Int}}% + \ifnum\POL@isolz@E>\POL@ensure@targetE\space + \edef\POL@IsoLeftSign + {\xintiiSgn{\POL@eval{\POL@sturmname _0}{\POL@IsoLeft@raw}}}% + % at start left and right are not roots, and values of opposite signs + % \edef\POL@IsoRightSign{\the\numexpr-\POL@IsoLeftSign}% + \xintloop + \POL@ensure@Eloopbody % decreases E by one at each iteration + % if separation level is still too coarse we recurse at deeper level + \ifnum\POL@isolz@E>\POL@ensure@targetE\space + \repeat + % will check if right is at a zero, it needs \POL@IsoRightSign set up + \POL@refine@storeleftandright + \fi + }% +}% +\def\POL@ensure@Eloopbody {% + \edef\POL@isolz@E{\the\numexpr\POL@isolz@E-\@ne}% + \edef\POL@IsoLeft@Int{\xintDSL{\POL@IsoLeft@Int}}% + % this will loop at most ten times + \xintloop + \edef\POL@IsoRight@Int{\xintInc{\POL@IsoLeft@Int}}% + \edef\POL@IsoRightSign + {\xintiiSgn{\POL@eval{\POL@sturmname _0}{\POL@IsoRight@raw}}}% + % if we have found a zero at right boundary the \ifnum test will fail + % and we exit the loop + % else we exit the loop if sign at right boundary is opposite of + % sign at left boundary (the latter is +1 or -1, never 0) + % this is a bit wasteful if we go ten times to the right, because + % we know that there the sign will be opposite, evaluation was superfluous + \ifnum\POL@IsoLeftSign=\POL@IsoRightSign\space + \let\POL@IsoLeft@Int\POL@IsoRight@Int + \repeat + % check for case when we exited the inner loop because we actually + % found a zero, then we force exit from the main (E decreasing) loop + \ifnum\POL@IsoRightSign=\z@ + \expandafter\xintbreakloop + \fi +}% +% +%% \PolPrintIntervals +\catcode`_ 8 +\catcode`& 4 +\def\PolPrintIntervals{\POL@ifstar{\PolPrintIntervals@@}{\PolPrintIntervals@}}% +% As explained in the docs, this is an example of customization so is not +% itself customizable, apart from redefining it entirely! +\def\PolPrintIntervals@@{% + \begingroup + \def\POL@AfterPrintIntervals{\endgroup}% + \let\PolPrintIntervalsPrintExactZero\POL@@PrintIntervalsPrintExactZero + \let\PolPrintIntervalsUnknownRoot\POL@@PrintIntervalsUnknownRoot + \let\PolPrintIntervalsKnownRoot\POL@@PrintIntervalsKnownRoot +\ifdefined\array + \def\arraystretch{2}% + \def\PolPrintIntervalsBeginEnv{\[\begin{array}{cl}}%\] + \def\PolPrintIntervalsEndEnv{\end{array}\]}% +\else + \def\PolPrintIntervalsBeginEnv{$$\tabskip0pt plus 1000pt minus 1000pt + \halign to\displaywidth\bgroup + \hfil\vrule height 2\ht\strutbox + depth 2\dp\strutbox + width \z@ + $####$\tabskip6pt&$####$\hfil + \tabskip0pt plus 1000pt minus 1000pt\cr}%$$ + \def\PolPrintIntervalsEndEnv{\crcr\egroup$$}%$$ +\fi + \PolPrintIntervals@ +}% +\def\PolPrintIntervals@{% + \POL@chkopt\POL@oPolPrintIntervals@[Z]% +}% +\def\POL@oPolPrintIntervals@[#1]#2{% + \def\PolPrintIntervalsTheVar{#1}% + \def\PolPrintIntervalsTheSturmName{#2}% + \ifnum\@nameuse{POL_ZL#2*}{0}=\z@ + \PolPrintIntervalsNoRealRoots + \else + \gdef\PolPrintIntervalsTheIndex{1}% + \POL@PrintIntervals@DoDefs + \begingroup\edef\POL@tmp{\endgroup + \unexpanded\expandafter{\PolPrintIntervalsBeginEnv}% + \unexpanded\expandafter{\POL@PrintIntervals@Loop}% + \unexpanded\expandafter{\PolPrintIntervalsEndEnv}% + }\POL@tmp + \fi + \POL@AfterPrintIntervals + \def\PolPrintIntervalsTheVar{#1}% + \def\PolPrintIntervalsTheSturmName{#2}% +}% +\let\POL@AfterPrintIntervals\empty +\let\PolPrintIntervalsNoRealRoots\empty +\def\PolPrintIntervalsArrayStretch{1}% +\ifdefined\array + \def\PolPrintIntervalsBeginEnv{\[\begin{array}{rcccl}}% + \def\PolPrintIntervalsEndEnv{\end{array}\]}% +\else + \def\PolPrintIntervalsBeginEnv + {$$\tabskip 0pt plus 1000pt minus 1000pt + \halign to\displaywidth\bgroup + \hfil\vrule height\PolPrintIntervalsArrayStretch\ht\strutbox + depth \PolPrintIntervalsArrayStretch\dp\strutbox + width \z@ + $##$\tabskip 6pt &\hfil $##$\hfil &\hfil $##$\hfil &\hfil $##$\hfil &$##$\hfil + \tabskip 0pt plus 1000pt minus 1000pt \cr + }%$$ + \def\PolPrintIntervalsEndEnv{\crcr\egroup$$}%$$ +\fi +\def\PolPrintIntervalsKnownRoot{% + &&\PolPrintIntervalsTheVar_{\PolPrintIntervalsTheIndex}% + &=&\PolPrintIntervalsPrintExactZero +}% +\def\PolPrintIntervalsUnknownRoot{% + \PolPrintIntervalsPrintLeftEndPoint&<&% + \PolPrintIntervalsTheVar_{\PolPrintIntervalsTheIndex}&<&% + \PolPrintIntervalsPrintRightEndPoint +}% +\def\PolPrintIntervalsPrintExactZero {\PolPrintIntervalsTheLeftEndPoint}% +\def\PolPrintIntervalsPrintLeftEndPoint {\PolPrintIntervalsTheLeftEndPoint}% +\def\PolPrintIntervalsPrintRightEndPoint{\PolPrintIntervalsTheRightEndPoint}% +% +\ifdefined\mbox +\def\PolPrintIntervalsPrintMultiplicity{(\mbox{mult. }\PolPrintIntervalsTheMultiplicity)}% +\else +\def\PolPrintIntervalsPrintMultiplicity{(\hbox{mult. }\PolPrintIntervalsTheMultiplicity)}% +\fi +% +\def\POL@@PrintIntervalsKnownRoot{% + \PolPrintIntervalsPrintMultiplicity&% + \PolPrintIntervalsTheVar_{\PolPrintIntervalsTheIndex}=% + \PolPrintIntervalsPrintExactZero +}% +\ifdefined\frac +\def\POL@@PrintIntervalsPrintExactZero{% + \displaystyle + \xintSignedFrac{\PolPrintIntervalsTheLeftEndPoint}% +}% +\else +\def\POL@@PrintIntervalsPrintExactZero{% + \displaystyle + \xintSignedFwOver{\PolPrintIntervalsTheLeftEndPoint}% +}% +\fi +\def\POL@@PrintIntervalsUnknownRoot{% + \PolPrintIntervalsPrintMultiplicity&% + \xintifSgn{\PolPrintIntervalsTheLeftEndPoint}% + {\xintifSgn{\PolPrintIntervalsTheRightEndPoint} + {\PolPrintIntervalsTheVar_{\PolPrintIntervalsTheIndex}=% + \PolPrintIntervalsPrintRightEndPoint\dots}% + {0>\PolPrintIntervalsTheVar_{\PolPrintIntervalsTheIndex}>% + \PolPrintIntervalsPrintLeftEndPoint}% + {\PolErrorThisShouldNotHappenPleaseReportToAuthorA}}% + {\xintifSgn{\PolPrintIntervalsTheRightEndPoint} + {\PolErrorThisShouldNotHappenPleaseReportToAuthorB}% + {\PolErrorThisShouldNotHappenPleaseReportToAuthorC}% + {0<\PolPrintIntervalsTheVar_{\PolPrintIntervalsTheIndex}<% + \PolPrintIntervalsPrintRightEndPoint}}% + {\xintifSgn{\PolPrintIntervalsTheRightEndPoint} + {\PolErrorThisShouldNotHappenPleaseReportToAuthorD}% + {\PolErrorThisShouldNotHappenPleaseReportToAuthorE}% + {\PolPrintIntervalsTheVar_{\PolPrintIntervalsTheIndex}=% + \PolPrintIntervalsPrintLeftEndPoint\dots}}% +}% +\catcode`& 7 +\catcode`_ 11 +\def\POL@PrintIntervals@Loop#1{% +\def\POL@PrintIntervals@Loop{% + \POL@SturmIfZeroExactlyKnown\PolPrintIntervalsTheSturmName + \PolPrintIntervalsTheIndex + \PolPrintIntervalsKnownRoot + \PolPrintIntervalsUnknownRoot + \xdef\PolPrintIntervalsTheIndex{\the\numexpr\PolPrintIntervalsTheIndex+\@ne}% + \unless\ifnum\PolPrintIntervalsTheIndex> + \@nameuse{POL_ZL\PolPrintIntervalsTheSturmName*0} + \POL@PrintIntervals@DoDefs + \xint_afterfi{#1\POL@PrintIntervals@Loop}% + \fi +}}% +\ifdefined\array\POL@PrintIntervals@Loop{\\}\else\POL@PrintIntervals@Loop{\cr}\fi +\def\POL@PrintIntervals@DoDefs{% + \xdef\PolPrintIntervalsTheLeftEndPoint{% + \csname POL_ZL\PolPrintIntervalsTheSturmName*\PolPrintIntervalsTheIndex + \endcsname + }% + \xdef\PolPrintIntervalsTheRightEndPoint{% + \csname POL_ZR\PolPrintIntervalsTheSturmName*\PolPrintIntervalsTheIndex + \endcsname + }% + \xdef\PolPrintIntervalsTheMultiplicity{% + \ifcsname POL_ZM\PolPrintIntervalsTheSturmName*\PolPrintIntervalsTheIndex + \endcsname + \csname POL_ZM\PolPrintIntervalsTheSturmName*\PolPrintIntervalsTheIndex + \endcsname + \else + ?% or use 0 ? + \fi + }% +}% +% +%% Expandable interface +% +\def\PolSturmIfZeroExactlyKnown#1#2{% #1 = sturmname, #2=index + \romannumeral0\csname POL_ZK#1*\endcsname{#2}% +}% +\def\POL@SturmIfZeroExactlyKnown#1#2{% #1 = sturmname, #2=index + \romannumeral0\csname POL_ZK#1*\the\numexpr#2\endcsname +}% +\def\PolSturmIsolatedZeroMultiplicity#1#2{% + \romannumeral`&&@\csname POL_ZM#1*\endcsname{#2}% +}% +\def\PolSturmIsolatedZeroLeft#1#2{% + \romannumeral`&&@\csname POL_ZL#1*\endcsname{#2}% +}% +\def\PolSturmIsolatedZeroRight#1#2{% + \romannumeral`&&@\csname POL_ZR#1*\endcsname{#2}% +}% +\def\PolSturmNbOfIsolatedZeros#1{% + \romannumeral`&&@\csname POL_ZL#1*0\endcsname +}% +\def\PolSturmRationalRoot#1#2{% + \romannumeral`&&@\csname POL_ZL#1*% + \csname POL_RI#1*\endcsname{#2}\endcsname +}% +\def\PolSturmRationalRootIndex#1#2{% + \romannumeral`&&@\csname POL_RI#1*\endcsname{#2}% +}% +\def\PolSturmRationalRootMultiplicity#1#2{% + \romannumeral`&&@\csname POL_ZM#1% + *\csname POL_RI#1*\endcsname{#2}\endcsname +}% +\def\PolSturmNbOfRationalRoots#1{% + \romannumeral`&&@\csname POL_RI#1*0\endcsname +}% +\def\PolSturmNbOfRationalRootsWithMultiplicities#1{% +% means the \POL@norr must not have been changed in-between... + \the\numexpr\PolDegree{#1}-\PolDegree{#1\POL@norr}\relax +}% +\def\PolSturmIntervalIndex#1#2#3{\the\numexpr\POL@eval@fork + #2\PolSturmIntervalIndexAt + \At\PolSturmIntervalIndexAtExpr\krof {#1}{#3}% +}% +\def\PolSturmIntervalIndexAtExpr#1#2{% + \PolSturmIntervalIndexAt{#1}{\xinttheexpr#2\relax}% +}% +% ! is of catcode 11 in all of polexpr +\def\PolSturmIntervalIndexAt#1#2{% + \expandafter\POL@sturm@index@at\romannumeral`&&@#2!{#1}\xint_bye\relax +}% +\def\POL@sturm@index@at#1!#2% +{% + \expandafter\POL@sturm@index@at@iloop + \romannumeral`&&@\PolSturmNbOfIsolatedZeros{#2}!{#2}{#1}% +}% +% implementation is sub-optimal as it should use some kind of binary tree +% search rather than comparing to the intervals from right to left as here +\def\POL@sturm@index@at@iloop #1!% +{% + \ifnum #1=\z@ 0\expandafter\xint_bye\fi + \POL@sturm@index@at@iloop@a #1!% +}% +\def\POL@sturm@index@at@iloop@a #1!#2#3% +{% #1 = index, #2 = sturmname, #3 value + \PolSturmIfZeroExactlyKnown{#2}{#1} + {\xintifCmp{#3}{\POL@xintexprGetVar{#2L_#1}}% + {}% + {#1\xint_bye}% + {0\xint_bye}% + }% + {\xintifGt{#3}{\POL@xintexprGetVar{#2L_#1}}% + {\xintifLt{#3}{\POL@xintexprGetVar{#2R_#1}}% + {#1\xint_bye}% + {0\xint_bye}% + }% + {}% + }% + % attention that catcode of ! is 11 in polexpr.sty + \expandafter\POL@sturm@index@at@iloop\the\numexpr#1-\@ne !{#2}{#3}% +}% +% +\def\POL@leq@fork#1\LessThanOrEqualTo#2#3\krof{#2}% +\def\PolSturmNbOfRootsOf#1#2#3{\romannumeral`&&@\POL@leq@fork + #2\PolNbOfRootsLessThanOrEqualTo + \LessThanOrEqualTo\PolNbOfRootsLessThanOrEqualToExpr\krof {#1}{#3}% +}% +\def\PolNbOfRootsLessThanOrEqualToExpr#1#2 + {\PolNbOfRootsLessThanOrEqualTo{#1}{\xinttheexpr#2\relax}}% +\def\PolNbOfRootsLessThanOrEqualTo#1{% + \ifnum\PolSturmNbOfIsolatedZeros{#1}=\z@ + \expandafter\xint_firstofthree\expandafter0% + \else + \expandafter\PolNbOfRootsLessThanOrEqualTo@% + \fi {#1}% +}% +\def\PolNbOfRootsLessThanOrEqualTo@ #1#2% +{% + \expandafter\POL@nbofrootsleq@prep\romannumeral`&&@#2!{#1}% +}% +\def\POL@nbofrootsleq@prep#1!#2% +{% + \expandafter\POL@nbofrootsleq@iloop\expandafter 1\expandafter !% + \romannumeral0\xintsgn{\POL@eval{#2_0}{#1}}!% + #1!{#2}% +}% +\def\POL@nbofrootsleq@iloop#1!#2!#3!#4% +{% #1 = index, #2 = sign of evaluation at value, #3 = value, #4 = sturmname + \xintifCmp{#3}{\POL@xintexprGetVar{#4L_#1}}% + {\POL@nbofrootsleq@return #1-\@ne !}% + {\POL@nbofrootsleq@return + \PolSturmIfZeroExactlyKnown{#4}{#1}{#1}{#1-\@ne}!% + }% + % in third branch we are sure that if root is exactly known + % the test \xintifLt will be negative + {\xintifLt{#3}{\POL@xintexprGetVar{#4R_#1}}% + {\POL@nbofrootsleq@return + #1\ifnum#2=\xintSgn{\POL@eval{#4_0}{\POL@xintexprGetVar{#4L_#1}}} + -\@ne\fi !% + }% + {\ifnum#1=\PolSturmNbOfIsolatedZeros{#4} + \expandafter\POL@nbofrootsleq@rightmost + \fi \expandafter\POL@nbofrootsleq@iloop \the\numexpr\@ne+% + }% + }% + #1!#2!#3!{#4}% +}% +\def\POL@nbofrootsleq@return #1!#2!#3!#4!#5{\the\numexpr #1\relax}% +\def\POL@nbofrootsleq@rightmost\expandafter\POL@nbofrootsleq@iloop + \the\numexpr\@ne+#1!#2!#3!#4{#1}% +% +\def\PolSturmNbWithMultOfRootsOf#1#2#3{% + \the\numexpr0\POL@leq@fork + #2\PolNbWithMultOfRootsLessThanOrEqualTo + \LessThanOrEqualTo\PolNbWithMultOfRootsLessThanOrEqualToExpr\krof {#1}{#3}% +}% +\def\PolNbWithMultOfRootsLessThanOrEqualToExpr#1#2{% + \PolNbWithMultOfRootsLessThanOrEqualTo{#1}{\xinttheexpr#2\relax}% +}% +\def\PolNbWithMultOfRootsLessThanOrEqualTo#1{% + \ifnum\PolSturmNbOfIsolatedZeros{#1}=\z@ + \expandafter\POL@nbwmofroots@noroots + \else + \expandafter\PolNbWithMultOfRootsLessThanOrEqualTo@% + \fi {#1}% +}% +\def\POL@nbwmofroots@noroots#1#2{\relax}% +\def\PolNbWithMultOfRootsLessThanOrEqualTo@ #1#2% +{% + \expandafter\POL@nbwmofrootsleq@prep\romannumeral`&&@#2!{#1}% +}% +\def\POL@nbwmofrootsleq@prep#1!#2% +{% + \expandafter\POL@nbwmofrootsleq@iloop\expandafter 1\expandafter !% + \romannumeral0\xintsgn{\POL@eval{#2_0}{#1}}!% + #1!{#2}% +}% +\def\POL@nbwmofrootsleq@iloop#1!#2!#3!#4% +{% #1 = index, #2 = sign of evaluation at value, #3 = value, #4 = sturmname + \xintifCmp{#3}{\POL@xintexprGetVar{#4L_#1}}% + {\POL@nbwmofrootsleq@return !}% + {\POL@nbwmofrootsleq@return + \PolSturmIfZeroExactlyKnown{#4}{#1}% + {+\PolSturmIsolatedZeroMultiplicity{#4}{#1}}{}!% + }% + % in third branch we are sure that if root is exactly known + % the test \xintifLt will be negative + {\xintifLt{#3}{\POL@xintexprGetVar{#4R_#1}}% + {\POL@nbwmofrootsleq@return + \unless + \ifnum#2=\xintSgn{\POL@eval{#4_0}{\POL@xintexprGetVar{#4L_#1}}} + +\PolSturmIsolatedZeroMultiplicity{#4}{#1}\fi !% + }% + {+\PolSturmIsolatedZeroMultiplicity{#4}{#1}% + \ifnum#1=\PolSturmNbOfIsolatedZeros{#4} + \expandafter\POL@nbwmofrootsleq@return\expandafter !% + \fi + \expandafter\POL@nbwmofrootsleq@iloop \the\numexpr\@ne+% + }% + }% + #1!#2!#3!{#4}% +}% +\def\POL@nbwmofrootsleq@return #1!#2!#3!#4!#5{#1\relax}% +\endinput diff --git a/macros/generic/xint/CHANGES.html b/macros/generic/xint/CHANGES.html index 32fcfbe4ee..20b940dad7 100644 --- a/macros/generic/xint/CHANGES.html +++ b/macros/generic/xint/CHANGES.html @@ -4,7 +4,7 @@ - + CHANGE LOG - - -
-

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 diff --git a/macros/latex/contrib/srdp-mathematik/README.txt b/macros/latex/contrib/srdp-mathematik/README.txt index 98bf90021a..af803dd1eb 100644 --- a/macros/latex/contrib/srdp-mathematik/README.txt +++ b/macros/latex/contrib/srdp-mathematik/README.txt @@ -1,28 +1,28 @@ -__________________________________ - - The - srdp-mathematik package - v1.8 - - 2021/03/18 -___________________________________ - -Maintainer: Christoph Weberndorfer -E-mail : c.weberndorfer@gmail.com - Comments, bug reports and suggestions are welcome. -Licence : Released under the LaTeX Project Public License 1.3c or - later, see http://www.latex-project.org/lppl.txt - ----------------------------------------------------------------------- - -This package provides basic commands for the defined formats of the Austrian sRDP in mathematics. -Furthermore, it includes ways to implement answers in the tex file, which can be voluntarily displayed in the pdf file and -it offers a way of varying the answers in order to create different groups (e.g. for tests) easily. - ----------------------------------------------------------------------- - -Dieses Paket bietet grundlegende Befehle für die vorgegebenen Formate der österreichischen, standardisierten -Reife- und Diplomprüfung (sRDP) in Mathematik. -Es bietet darüber hinaus die Möglichkeit, Antworten in die tex Datei zu implentieren und diese bei Bedarf in der pdf Datei anzuzeigen. -Außerdem können die eingegeben Antwortmöglichkeiten variiert werden, um mehrere Gruppe für zum Beispiel Schularbeiten einfach zu erstellen. - +__________________________________ + + The + srdp-mathematik package + v1.9.0 + + 2021/03/29 +___________________________________ + +Maintainer: Christoph Weberndorfer +E-mail : c.weberndorfer@gmail.com + Comments, bug reports and suggestions are welcome. +Licence : Released under the LaTeX Project Public License 1.3c or + later, see http://www.latex-project.org/lppl.txt + +---------------------------------------------------------------------- + +This package provides basic commands for the defined formats of the Austrian sRDP in mathematics. +Furthermore, it includes ways to implement answers in the tex file, which can be voluntarily displayed in the pdf file and +it offers a way of varying the answers in order to create different groups (e.g. for tests) easily. + +---------------------------------------------------------------------- + +Dieses Paket bietet grundlegende Befehle für die vorgegebenen Formate der österreichischen, standardisierten +Reife- und Diplomprüfung (sRDP) in Mathematik. +Es bietet darüber hinaus die Möglichkeit, Antworten in die tex Datei zu implentieren und diese bei Bedarf in der pdf Datei anzuzeigen. +Außerdem können die eingegeben Antwortmöglichkeiten variiert werden, um mehrere Gruppe für zum Beispiel Schularbeiten einfach zu erstellen. + diff --git a/macros/latex/contrib/srdp-mathematik/srdp-mathematik.pdf b/macros/latex/contrib/srdp-mathematik/srdp-mathematik.pdf index 610ae58130..81b5cbc449 100644 Binary files a/macros/latex/contrib/srdp-mathematik/srdp-mathematik.pdf and b/macros/latex/contrib/srdp-mathematik/srdp-mathematik.pdf differ diff --git a/macros/latex/contrib/srdp-mathematik/srdp-mathematik.sty b/macros/latex/contrib/srdp-mathematik/srdp-mathematik.sty index 6176e19db1..09bf9fd8ab 100644 --- a/macros/latex/contrib/srdp-mathematik/srdp-mathematik.sty +++ b/macros/latex/contrib/srdp-mathematik/srdp-mathematik.sty @@ -10,7 +10,7 @@ % \NeedsTeXFormat{LaTeX2e}[1996/12/26] -\ProvidesPackage{srdp-mathematik}[2021/03/18 v1.8 Standard-Schularbeitsformate] +\ProvidesPackage{srdp-mathematik}[2021/03/29 v1.9.0 Standard-Schularbeitsformate] \usepackage{color} @@ -309,54 +309,38 @@ % %%%%%%%%%%%% NOTENSCHLUESSEL %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % -% -\newcommand{\notenschluessel}[5][]{ -\vfill +% + +\NewDocumentCommand{\notenschluessel}{ O{} O{} O{} m m m m}{ +\null\vfill \STautoround{3}\renewcommand{\arraystretch}{0}\tiny \begin{spreadtab}[\STsavecell\gutgenau{b2} \STsavecell\befriedigendgenau{c2} \STsavecell\genuegendgenau{d2} \STsavecell\nichtgenuegendgenau{e2} \STsavecell\gut{b3} \STsavecell\befriedigend{c3} \STsavecell\genuegend{d3} \STsavecell\nichtgenuegend{e3} \STsavecell\gutpluseins{b4} \STsavecell\befriedigendpluseins{c4} \STsavecell\genuegendpluseins{d4} \STsavecell\nichtgenuegendpluseins{e4} \STsavecell\gutminuseins{b5} \STsavecell\befriedigendminuseins{c5} \STsavecell\genuegendminuseins{d5} \STsavecell\nichtgenuegendminuseins{e5}]{{tabular}{ccccc}} - \SThidecol\thepunkte &\SThidecol 1 &\SThidecol2 &\SThidecol 3 &\SThidecol 4 \\ - & a1*#2 &a1*#3 & a1*#4 & a1*#5 \\ - &trunc(a1*#2,0) & trunc(a1*#3,0) &trunc(a1*#4,0) &trunc(a1*#5,0) \\ - &b3+1 &c3+1 &d3+1 & e3+1\\ - &b3-1 &c3-1 &d3-1 & e3-1\\ + \SThidecol\thepunkte &\SThidecol 1 &\SThidecol2 &\SThidecol 3 &\SThidecol 4 \\ + & a1*#4 &a1*#5 & a1*#6 & a1*#7 \\ + &trunc(a1*#4,0) & trunc(a1*#5,0) &trunc(a1*#6,0) &trunc(a1*#7,0) \\ + &b3+1 &c3+1 &d3+1 & e3+1\\ + &b3-1 &c3-1 &d3-1 & e3-1\\ \end{spreadtab} \footnotesize\renewcommand{\arraystretch}{1} \begin{center} \begin{tabular}{|C{3cm}C{3cm}C{3cm}C{3cm}C{3cm}|} \hline +\ifthenelse{\equal{#2}{prozent}}{% Erreichte Punkte & Prozent & Gesamt & \multicolumn{2}{c|}{Note} \\ -&& \multirow{2}{*}{\thepunkte} && \\ -&&&& \\ \hline \hline -\scriptsize{Sehr gut} & \scriptsize{Gut} & \scriptsize{Befriedigend} & \scriptsize{Gen\"ugend} & \scriptsize{Nicht gen\"ugend} \\ -\scriptsize{\thepunkte ~ - \ifdim\gut pt=\gutgenau pt \gut\else\gutpluseins\fi}& \scriptsize{\ifdim\gut pt=\gutgenau pt \gutminuseins\else\gut\fi \ifthenelse{\equal{#1}{1/2}}{,5}{} ~ - \ifdim\befriedigend pt=\befriedigendgenau pt \befriedigend\else\befriedigendpluseins\fi} & \scriptsize{\ifdim\befriedigend pt=\befriedigendgenau pt \befriedigendminuseins\else\befriedigend\fi \ifthenelse{\equal{#1}{1/2}}{,5}{} ~ - \ifdim\genuegend pt=\genuegendgenau pt \genuegend\else\genuegendpluseins\fi} & \scriptsize{\ifdim\genuegend pt=\genuegendgenau pt \genuegendminuseins\else\genuegend\fi \ifthenelse{\equal{#1}{1/2}}{,5}{} ~ - \ifdim\nichtgenuegend pt=\nichtgenuegendgenau pt \nichtgenuegend\else\nichtgenuegendpluseins\fi} & \scriptsize{unter \ifdim\nichtgenuegend pt=\nichtgenuegendgenau pt \nichtgenuegend\else\nichtgenuegendpluseins\fi} \\ \hline -\end{tabular} -\end{center}\STautoround{}\normalsize} - -\newcommand{\notenschluesselop}[5][]{ -\vfill -\STautoround{3}\renewcommand{\arraystretch}{0}\tiny -\begin{spreadtab}[\STsavecell\gutgenau{b2} \STsavecell\befriedigendgenau{c2} \STsavecell\genuegendgenau{d2} -\STsavecell\gut{b3} \STsavecell\befriedigend{c3} \STsavecell\genuegend{d3} \STsavecell\nichtgenuegend{e3} -\STsavecell\gutpluseins{b4} \STsavecell\befriedigendpluseins{c4} \STsavecell\genuegendpluseins{d4} \STsavecell\gutminuseins{b5} \STsavecell\befriedigendminuseins{c5} \STsavecell\genuegendminuseins{d5}]{{tabular}{ccccc}} - \SThidecol \thepunkte &\SThidecol 1 &\SThidecol 2 &\SThidecol 3 &\SThidecol 4 \\ - & a1*#2 &a1*#3 & a1*#4 & a1*#5 \\ - &trunc(a1*#2,0) & trunc(a1*#3,0) &trunc(a1*#4,0) &trunc(a1*#5,0) \\ - &b3+1 &c3+1 &d3+1 & e3+1\\ - &b3-1 &c3-1 &d3-1 & e3-1\\ -\end{spreadtab} -\footnotesize\renewcommand{\arraystretch}{1} -\begin{center} -\begin{tabular}{|C{3cm}C{3cm}C{3cm}C{3cm}C{3cm}|} \hline +&& \multirow{2}{*}{\thepunkte} && \\% +}{% Erreichte Punkte &Gesamt&\multicolumn{2}{c}{Note}& \\ -&\multirow{2}{*}{\thepunkte} &&& \\ +&\multirow{2}{*}{\thepunkte} &&& \\} &&&& \\ \hline \hline -\scriptsize{Sehr gut} & \scriptsize{Gut} & \scriptsize{Befriedigend} & \scriptsize{Gen\"ugend} & \scriptsize{Nicht gen\"ugend} \\ -\scriptsize{\thepunkte ~ - \ifdim\gut pt=\gutgenau pt \gut\else\gutpluseins\fi}& \scriptsize{\ifdim\gut pt=\gutgenau pt \gutminuseins\else\gut\fi \ifthenelse{\equal{#1}{1/2}}{,5}{} ~ - \ifdim\befriedigend pt=\befriedigendgenau pt \befriedigend\else\befriedigendpluseins\fi} & \scriptsize{\ifdim\befriedigend pt=\befriedigendgenau pt \befriedigendminuseins\else\befriedigend\fi \ifthenelse{\equal{#1}{1/2}}{,5}{} ~ - \ifdim\genuegend pt=\genuegendgenau pt \genuegend\else\genuegendpluseins\fi} & \scriptsize{\ifdim\genuegend pt=\genuegendgenau pt \genuegendminuseins\else\genuegend\fi \ifthenelse{\equal{#1}{1/2}}{,5}{} ~ - \ifdim\nichtgenuegend pt=\nichtgenuegendgenau pt \nichtgenuegend\else\nichtgenuegendpluseins\fi} & \scriptsize{unter \ifdim\nichtgenuegend pt=\nichtgenuegendgenau pt \nichtgenuegend\else\nichtgenuegendpluseins\fi} \\ \hline +\ifthenelse{\equal{#3}{ms}}{}{\scriptsize{Sehr gut}} & \ifthenelse{\equal{#3}{ms}}{}{\scriptsize{Gut}} & \scriptsize{Befriedigend} & \scriptsize{Gen\"ugend} & \scriptsize{Nicht gen\"ugend} \\ +\ifthenelse{\equal{#3}{ms}}{}{\scriptsize{\thepunkte ~ - \ifdim\gut pt=\gutgenau pt \gut\else\gutpluseins\fi}}& \ifthenelse{\equal{#3}{ms}}{}{\scriptsize{\ifdim\gut pt=\gutgenau pt \gutminuseins\else\gut\fi \ifthenelse{\equal{#1}{1/2}}{,5}{} ~ - \ifdim\befriedigend pt=\befriedigendgenau pt \befriedigend\else\befriedigendpluseins\fi}} & \scriptsize{\ifdim\befriedigend pt=\befriedigendgenau pt \befriedigendminuseins\else\befriedigend\fi \ifthenelse{\equal{#1}{1/2}}{,5}{} ~ - \ifdim\genuegend pt=\genuegendgenau pt \genuegend\else\genuegendpluseins\fi} & \scriptsize{\ifdim\genuegend pt=\genuegendgenau pt \genuegendminuseins\else\genuegend\fi \ifthenelse{\equal{#1}{1/2}}{,5}{} ~ - \ifdim\nichtgenuegend pt=\nichtgenuegendgenau pt \nichtgenuegend\else\nichtgenuegendpluseins\fi} & \scriptsize{unter \ifdim\nichtgenuegend pt=\nichtgenuegendgenau pt \nichtgenuegend\else\nichtgenuegendpluseins\fi} \\ \hline \end{tabular} \end{center}\STautoround{}\normalsize} - +% for legacy reason +\newcommand{\notenschluesselop}[5][1]{\notenschluessel[#1]{#2}{#3}{#4}{#5}} +%%%%%%%%%%%%%%%%% % %%%%%%%%%%%%%%%%%% GAP DEFINITION diff --git a/macros/latex/contrib/srdp-mathematik/srdp-mathematik.tex b/macros/latex/contrib/srdp-mathematik/srdp-mathematik.tex index 8dbc5baae6..1df75d3560 100644 --- a/macros/latex/contrib/srdp-mathematik/srdp-mathematik.tex +++ b/macros/latex/contrib/srdp-mathematik/srdp-mathematik.tex @@ -55,7 +55,7 @@ hidelinks \vfill -\Huge The \textit{srdp-mathematik} package v1.8\\[1cm] +\Huge The \textit{srdp-mathematik} package v1.9.0\\[1cm] Documentation \\ [1cm] @@ -93,7 +93,7 @@ Die \textit{allgemeinen Befehle} erleichtern das Erstellen von Tests, Schularbei Jedes Beispiel sollte innerhalb einer Beispielumgebung gesetzt werden, welche bei der Formatierung und der Verarbeitung der Punkte unterstützt. Dabei werden zwei Beispiel-Typen unterschieden: \texttt{beispiel} und \texttt{langesbeispiel}. \vspace{1cm} -\subsubsection{\texttt{beispiel}-Umbgebung} +\subsubsection{\texttt{\textbackslash begin\{beispiel\} \ldots\ \textbackslash end\{beispiel\}}} Die \texttt{beispiel}-Umgebung dient zur Erstellung eines Beispiels einer Schularbeit, einer Prüfung, usw. Dabei sind praktisch alle Funktionen von \LaTeX, wie Text, Formeln oder Grafiken möglich. Diese Umgebung erlaubt jedoch keinen Seitenumbruch! Die Beispiele werden automatisch nummeriert. @@ -127,7 +127,7 @@ $\frac{x^2+x+5}{\sqrt{x^3}}$ \vspace{1cm} -\subsubsection{\texttt{langesbeispiel}-Umbgebung} +\subsubsection{\texttt{\textbackslash begin\{langesbeispiel\} \ldots\ \textbackslash end\{langesbeispiel\}}} Die \texttt{langesbeispiel}-Umgebung dient ebenso zur Erstellung eines Beispiels, funktioniert analog, erlaubt aber im Gegensatz zur \texttt{beispiel}-Umgebung Seitenumbrüche. Auch lange Beispiele werden weiterführend nummeriert. @@ -159,48 +159,83 @@ $\frac{x^2+x+5}{\sqrt{x^3}}$. -\subsubsection{Notenschlüssel} +\subsubsection{\texttt{\textbackslash notenschluessel}} Werden für alle Beispiele die \texttt{beispiel}- oder die \texttt{langesbeispiel}-Umgebung verwendet, wird die Gesamtpunktezahl sowie der Notenschlüssel automatisch berechnet. Die Werte in Klammer geben dabei den prozentualen Notenschlüssel vor und können beliebig variiert werden: \vspace{0.4cm} +\setcounter{punkte}{48} + + +\vspace{0.3cm} + +\textsc{Ausgabe:} +\notenschluessel{0.91}{0.8}{0.64}{0.5} +\vfill + +Der Befehl \texttt{notenschluessel} bietet auch die Eingabe von drei unterschiedlichen Optionen, die alle einzeln oder gemeinsam verwendet werden können. Wichtig: Wird Option 3 benötigt (jedoch Option 1 \& 2 nicht) müssen die Optionen 1 \& 2 jeweils mit leerer Klammer \texttt{[]} angegeben werden. + +\subsubsection*{\texttt{\textbackslash notenschluessel} -- Option 1: Halbe Punkte-Schritte} + +Durch die Option 1 \texttt{[1/2]} wird die Anzeige auf halbe Punkte-Schritten geändert. \textsc{Eingabe:} \begin{verbatim} -\notenschluessel{0.91}{0.8}{0.64}{0.5} +\notenschluessel[1/2]{0.91}{0.8}{0.64}{0.5} \end{verbatim} -\vspace{0.3cm} - \textsc{Ausgabe:} -\notenschluessel{0.91}{0.8}{0.64}{0.5} +\notenschluessel[1/2]{0.91}{0.8}{0.64}{0.5} + -\normalsize +\subsubsection*{\texttt{\textbackslash notenschluessel} -- Option 2: Prozentanzeige} -\paragraph{Notenschlüssel - Ohne Prozentangabe} -Analog zum \texttt{notenschluessel} funktioniert auch der Befehl \texttt{notenschluesselop}. Es wird dabei jedoch die Prozentspalte nicht angezeigt. +Die Option 2 \texttt{[prozent]} ergänzt die erste Zeile des Notenschlüssel mit einer prozentuellen Angabe. \textsc{Eingabe:} \begin{verbatim} -\notenschluesselop{0.91}{0.8}{0.64}{0.5} +\notenschluessel[][prozent]{0.91}{0.8}{0.64}{0.5} \end{verbatim} \textsc{Ausgabe:} -\notenschluessel{0.91}{0.8}{0.64}{0.5} +\notenschluessel[][prozent]{0.91}{0.8}{0.64}{0.5} -\paragraph{Notenschlüssel - Option: [1/2]} +\subsubsection*{\texttt{\textbackslash notenschluessel} -- Option 3: Standard der Mittelschule} -Die Option 1/2 ermöglicht die Anzeige des Notenschlüssel (mit oder ohne Prozentspalte) mit halben Punkten. +Die option 3 \texttt{ms} ermöglicht die Darstellung des Notenschlüssels entsprechend des Standards der Mittelschule. \textsc{Eingabe:} \begin{verbatim} -\notenschluessel[1/2]{0.91}{0.8}{0.64}{0.5} +\notenschluessel[][][ms]{0.91}{0.8}{0.64}{0.5} \end{verbatim} \textsc{Ausgabe:} -\notenschluessel[1/2]{0.91}{0.8}{0.64}{0.5} +\notenschluessel[][][ms]{0.91}{0.8}{0.64}{0.5} + +%\paragraph{Notenschlüssel - Ohne Prozentangabe} +%Analog zum \texttt{notenschluessel} funktioniert auch der Befehl \texttt{notenschluesselop}. Es wird dabei jedoch die Prozentspalte nicht angezeigt. +% +%\textsc{Eingabe:} +%\begin{verbatim} +%\notenschluesselop{0.91}{0.8}{0.64}{0.5} +%\end{verbatim} +% +%\textsc{Ausgabe:} +%\notenschluessel{0.91}{0.8}{0.64}{0.5} + +%\paragraph{Notenschlüssel - Option: [1/2]} +% +%Die Option 1/2 ermöglicht die Anzeige des Notenschlüssel (mit oder ohne Prozentspalte) mit halben Punkten. +% +%\textsc{Eingabe:} +%\begin{verbatim} +%\notenschluessel[1/2]{0.91}{0.8}{0.64}{0.5} +%\end{verbatim} +% +%\textsc{Ausgabe:} +%\notenschluessel[1/2]{0.91}{0.8}{0.64}{0.5} -\subsubsection{Beurteilungsraster} +\subsubsection{\texttt{\textbackslash beurteilungsraster}} Der Befehl \texttt{beurteilungsraster} entspricht der aktuellen Vorgabe der Beurteilung der standardisierten Reifeprüfung (Stand 2019). Dabei müssen die Punkte für Teil 1, Ausgleichspunkte und Teil 2 angegeben werden. Die in Klammer angegebenen Werte entsprechen der bei der Reifeprüfung verwendeten prozentuellen Notenverteilung, können jedoch jederzeit verändert werden. \leer @@ -227,7 +262,7 @@ siehe nächste Seite \newpage \newpage -\subsection{Lösungseingabe -- Option: \texttt{[solution\_on/off]}} +\subsection{Paketoption -- Lösungseingabe: \texttt{[solution\_on/off]}} Es besteht die Möglichkeit, die Lösung in der erstellten \texttt{tex}-Datei zu implementieren und diese bei Bedarf in der \texttt{PDF}-Datei ein- bzw. auszublenden. Um diese Funktion nutzen zu können, muss die \texttt{solution\_on/off} Option im \texttt{srdp-mathematik}-Paket geladen werden. Diese kann durch hinzufügen durch "`\texttt{solution\_on}"' bzw. "`\texttt{solution\_off}"' aktiviert bzw. deaktiviert werden. Also: @@ -245,11 +280,11 @@ oder Die korrekte Lösungseingabe der vorformatierten Typ-1-Aufgaben wird bei den entsprechenden Befehlen direkt erklärt. Unabhängig davon, gibt es die Möglichkeit mithilfe des \texttt{antwort}-Befehls, beliebige Abschnitte als \textit{Antwort} zu deklarieren. (verwendeter counter: \texttt{Antworten=0, 1}) -\subsubsection{\texttt{antwort}-Befehl} +\subsubsection{\texttt{\textbackslash antwort}} Für offene Antworten kann der \texttt{\textbackslash antwort\{\}}-Befehl verwendet werden. Darin können Textpassagen, mathematische Formeln oder Grafiken angegeben werden, die ausschließlich bei aktivierter Lösung (\texttt{[solution\_on]}) und in \textcolor[rgb]{1,0,0}{rot} angegeben werden. \leer -\subsubsection{Zusatz: Optionales Argument} +\subsubsection*{\texttt{\textbackslash antwort} -- Option: Anzeige nur bei \texttt{solution\_off}} Der \texttt{antwort}-Befehl erlaubt ein optionales Argument. Es können dadurch zusätzliche Textpassagen angegeben werden, die \underline{ausschließlich} angezeigt werden, wenn die Lösungsanzeige deaktiviert (\texttt{[solution\_off])} ist. Das heißt: \leer @@ -278,7 +313,7 @@ wird.] \texttt{'solution\_on'} eingestellt ist.} -\subsection{Erstellung von Gruppen -- Option: \texttt{[random=0,1,2,\ldots]}} +\subsection{Paketoption -- Erstellung von Gruppen: \texttt{[random=0,1,2,\ldots]}} Das \texttt{srdp-mathematik}-Paket ermöglicht auch die automatische Erstellung von Gruppen für Schularbeiten, durch Vertauschung der Antwortmöglichkeiten aller Typ-1-Aufgaben. Dazu kann die \texttt{random}-Option im \texttt{srdp-mathematik}-Paket eingebunden geladen werden. Also: \begin{verbatim} @@ -299,93 +334,9 @@ Durch das Ersetzen der Zahl "`0"' durch die Zahlen $1, 2, 3, \ldots$ (und anschl Die Erstellung der Gruppen ist dabei reproduzierbar. Die Reihenfolge der Antwortmöglichkeiten einer Gruppe bleibt daher immer gleich. Mithilfe des Befehls \texttt{\textbackslash Gruppe} kann die Gruppennummer innerhalb der \texttt{PDF}-Datei (z.B. am Deckblatt) angezeigt werden. (verwendeter counter: \texttt{Zufall=0, 1, 2, \ldots}) -%Dieses Prinzip wird exemplarisch am Multiplechoice-Format gezeigt:\leer -% -%\textsc{Eingabe:} -%\begin{verbatim} -%\multiplechoice[5]{ %Anzahl der Antwortmoeglichkeiten, Standard: 5 - %L1={Antwort 1}, %1. Antwortmoeglichkeit - %L2={Antwort 2}, %2. Antwortmoeglichkeit - %L3={Antwort 3}, %3. Antwortmoeglichkeit - %L4={Antwort 4}, %4. Antwortmoeglichkeit - %L5={Antwort 5}, %5. Antwortmoeglichkeit - %L6={}, %6. Antwortmoeglichkeit - %L7={}, %7. Antwortmoeglichkeit - %L8={}, %8. Antwortmoeglichkeit - %L9={}, %9. Antwortmoeglichkeit - %%% LOESUNG: %% - %A1=2, % 1. Antwort - %A2=4, % 2. Antwort - %A3=0, % 3. Antwort - %A4=0, % 4. Antwort - %A5=0, % 5. Antwort - %} -%\end{verbatim} -% -%\setcounter{Zufall}{0} -%\textsc{Ausgabe:}, bei \texttt{\textbackslash setcounter\{Zufall\}\{0\}}: -% -%\multiplechoice[5]{ %Anzahl der Antwortmoeglichkeiten, Standard: 5 - %L1={Antwort 1}, %1. Antwortmoeglichkeit - %L2={Antwort 2}, %2. Antwortmoeglichkeit - %L3={Antwort 3}, %3. Antwortmoeglichkeit - %L4={Antwort 4}, %4. Antwortmoeglichkeit - %L5={Antwort 5}, %5. Antwortmoeglichkeit - %L6={}, %6. Antwortmoeglichkeit - %L7={}, %7. Antwortmoeglichkeit - %L8={}, %8. Antwortmoeglichkeit - %L9={}, %9. Antwortmoeglichkeit - %%% LOESUNG: %% - %A1=2, % 1. Antwort - %A2=4, % 2. Antwort - %A3=0, % 3. Antwort - %A4=0, % 4. Antwort - %A5=0, % 5. Antwort - %} - % -%\setcounter{Zufall}{1} -%\textsc{Ausgabe:}, bei \texttt{\textbackslash setcounter\{Zufall\}\{1\}}: -% -%\multiplechoice[5]{ %Anzahl der Antwortmoeglichkeiten, Standard: 5 - %L1={Antwort 1}, %1. Antwortmoeglichkeit - %L2={Antwort 2}, %2. Antwortmoeglichkeit - %L3={Antwort 3}, %3. Antwortmoeglichkeit - %L4={Antwort 4}, %4. Antwortmoeglichkeit - %L5={Antwort 5}, %5. Antwortmoeglichkeit - %L6={}, %6. Antwortmoeglichkeit - %L7={}, %7. Antwortmoeglichkeit - %L8={}, %8. Antwortmoeglichkeit - %L9={}, %9. Antwortmoeglichkeit - %%% LOESUNG: %% - %A1=2, % 1. Antwort - %A2=4, % 2. Antwort - %A3=0, % 3. Antwort - %A4=0, % 4. Antwort - %A5=0, % 5. Antwort - %} -% -%\setcounter{Zufall}{2} -%\textsc{Ausgabe:}, bei \texttt{\textbackslash setcounter\{Zufall\}\{2\}}: -% -%\multiplechoice[5]{ %Anzahl der Antwortmoeglichkeiten, Standard: 5 - %L1={Antwort 1}, %1. Antwortmoeglichkeit - %L2={Antwort 2}, %2. Antwortmoeglichkeit - %L3={Antwort 3}, %3. Antwortmoeglichkeit - %L4={Antwort 4}, %4. Antwortmoeglichkeit - %L5={Antwort 5}, %5. Antwortmoeglichkeit - %L6={}, %6. Antwortmoeglichkeit - %L7={}, %7. Antwortmoeglichkeit - %L8={}, %8. Antwortmoeglichkeit - %L9={}, %9. Antwortmoeglichkeit - %%% LOESUNG: %% - %A1=2, % 1. Antwort - %A2=4, % 2. Antwort - %A3=0, % 3. Antwort - %A4=0, % 4. Antwort - %A5=0, % 5. Antwort - %} - -\subsection{Informationseingabe -- Option: \texttt{[info\_on/off]}} + + +\subsection{Paketoption -- Informationseingabe: \texttt{[info\_on/off]}} Es ist darüber hinaus möglich zusätzliche Informationen (Erläuterungen, Hilfestellungen, \ldots) hinzuzufügen und einzublenden. Dazu kann die Option \texttt{info\_on/off} im \texttt{srdp-mathematik}-Paket eingebunden werden. Die Infos werden in \textcolor[rgb]{0,0,1}{blau} angegeben und nur dann angezeigt, wenn die Option \texttt{info\_on} gewählt wurde (verwendeter counter: \texttt{info=0,1}). Also: @@ -403,13 +354,13 @@ oder \subsection{Weitere allgemeine Befehle} Außerdem gibt es noch weitere Befehle, die die Formatierung erleichtern. -\subsubsection*{\texttt{\textbackslash leer} - Befehl} +\subsubsection{\texttt{\textbackslash leer}} Eine Leerzeile, etwa zwischen zwei Beispielen. \\ (Nach dem Befehl \texttt{\textbackslash leer} muss in der \texttt{.tex}-Datei ein Leerzeile eingefügt werden!) \leer -\subsubsection*{\texttt{\textbackslash meinlr} - Befehl} +\subsubsection{\texttt{\textbackslash meinlr}} \begin{verbatim} \meinlr{Dieser Teil steht links. Wenn dieser Text länger sein sollte, geht er automatisch in der nächsten Zeile weiter ohne auf die andere @@ -426,7 +377,7 @@ geht er automatisch in der nächsten Zeile weiter ohne auf die andere Seite über zu gehen.}{Dieser Teil steht rechts.} -\subsubsection*{Zusatz: Variable Spaltenbreiten bei \texttt{\textbackslash meinlr}} +\subsubsection*{\texttt{\textbackslash meinlr} -- Option: Variable Spaltenbreiten} Der Befehl \texttt{\textbackslash meinlr} erlaubt durch die optionale Eingabe eine Abänderung der beiden Spaltenbreiten. Durch die Eingabe zwischen ca. -0.3 und 0.3 kann die Spaltenmitte nach links oder rechts verschoben und somit die Spaltenbreite variiert werden. @@ -446,7 +397,7 @@ die Spaltenbreiten nach Belieben zu variieren.} \leer -\subsubsection*{\texttt{\textbackslash meinlcr} - Befehl} +\subsubsection{\texttt{\textbackslash meinlcr}} \begin{verbatim} \meinlcr{Text ganz links}{Text in der Mitte. Auch hier sind längere Texte möglich.}{Text auf der rechten Seite.} @@ -459,15 +410,54 @@ Teilt die Seite in drei gleich große Teile \meinlcr{Text ganz links. Was passiert hier wenn dieser Teil länger ist?}{Text in der Mitte. Auch hier sind längere Texte möglich.}{Text auf der rechten Seite.} + +\subsubsection{\texttt{\textbackslash dint} und \texttt{\textbackslash dx}} +Die Befehle \texttt{\textbackslash dint} und \texttt{\textbackslash dx} vereinfacht die Eingabe eines Integrals (bestimmt oder unbestimmt).\leer + +\textsc{Eingabe:} + +\begin{verbatim} +$\dint_1^3 x^2 \dx$ +\end{verbatim} + +\textsc{Ausgabe:}\leer + +$\dint_1^3 x^2 \dx$ \leer +Wird eine Variable ungleich $x$ integriert, kann diese mithilfe der Option \texttt{\textbackslash{dx[Variable]}} dargestellt werden.\leer + +\textsc{Eingabe:} + +\begin{verbatim} +$\dint t^2 \dx[t]$ +\end{verbatim} + +\textsc{Ausgabe:}\leer + +$\dint t^2 \dx[t]$ + +\subsubsection{\texttt{\textbackslash degre}} + +Der \texttt{\textbackslash degre} Befehl stellt das Einheitszeichen für Grad dar. Er kann im Text- und im Mathematik-Modus verwendet werden. + +\textsc{Eingabe:} + +\begin{verbatim} +Der Winkel Alpha ist 30\degre. ($\alpha = 30 \degre$) +\end{verbatim} + +\textsc{Ausgabe:}\leer + +Der Winkel Alpha ist 30\degre. ($\alpha = 30 \degre$) + \section{Typ-1-Aufgaben} Das \texttt{srdp-mathematik}-Paket ermöglicht die Verwendung vorgefertigter Aufgabenformate, die bei der österreichischen, standardisierten Reifeprüfung Anwendung finden. -\subsection{\texttt{multiplechoice}-Befehl} +\subsection{\texttt{\textbackslash multiplechoice}} Dieser Befehl liefert eine vollständige Formatierung für eine Multiplechoice-Aufgabe. Die Anzahl der Antwortmöglichkeiten kann dabei frei (max. 9) gewählt werden. Als Standard ist dabei das Format mit fünf Antwortmöglichkeiten eingestellt. \leer @@ -669,7 +659,7 @@ Die Eingabe in eckiger Klammer ist dabei optional. Wird sie nicht angegeben, wir \leer \newpage -\subsubsection{Add-on: \texttt{langmultiplechoice}-Befehl} +\subsubsection{Add-on: \texttt{\textbackslash langmultiplechoice}} Der \texttt{langmultiplechoice}-Befehl ist analog zum \texttt{multiplechoice}-Befehl zu verwenden. Der Unterschied besteht darin, dass Antwortmöglichkeiten auf zwei Spalten aufgeteilt werden. Dies ist vor allem dann sinnvoll, wenn Geogebra-Grafiken importiert werden, um eine bessere Lesbarkeit zu ermöglichen. @@ -790,7 +780,7 @@ Der \texttt{langmultiplechoice}-Befehl ist analog zum \texttt{multiplechoice}-Be \newpage -\subsection{\texttt{lueckentext}-Befehl} +\subsection{\texttt{\textbackslash lueckentext}} Dieser Befehl dient zur Erstellung eines Lückentexts, basierend auf dem standardisierten Format des BIFIE. Der einleitenden Satz: \textit{"`Ergänze die Textlücken im folgenden Satz durch Ankreuzen der jeweils richtigen Satzteile so, dass eine mathematisch korrekte Aussage entsteht!"'} wird als Standard angenommen und automatisch angegeben. Im Bereich \texttt{\textbackslash text=\{\}} wird der Lückentext angegeben, wobei die Lücken immer mit \texttt{\textbackslash gap} eingefügt werden und automatisch nummeriert werden. Analog zu den Lösungsangaben der Multiplechoice-Aufgabe, werden die korrekten Antworten bei A1, A2 oder A3 mit 1,2 oder 3 angegeben. (hier: Lösung links: 3 und rechts: 2) \vspace{0.35cm} @@ -829,7 +819,7 @@ die \gap, die Lücke zwei hat die \gap.}, %Lueckentext Luecke=\gap } -\subsubsection{Zusatz: Variable Breiten der Boxen} +\subsubsection{\texttt{\textbackslash lueckentext} -- Option: Variable Breiten der Boxen} Der \texttt{lueckentext}-Befehl erlaubt ein optionales Argument, um die Größen der beiden Boxen zu variieren. Dabei werden in den eckigen Klammern $[~]$ die Veränderung der linken Box (Änderung ca. zwischen -0.3 und 0.3) angegeben und die rechte Box wird automatisch angepasst. Will man etwa die linke Box vergrößern, gilt:\leer @@ -877,7 +867,7 @@ die \gap, die Lücke zwei hat die \gap.}, %Lueckentext Luecke=\gap Analog funktioniert die Verkleinerung der linken Box. Dazu müssen Werte kleiner 0 angegeben werden. -\subsubsection{\texttt{Zusatz}: Englischer Lückentext} +\subsubsection{\texttt{\textbackslash lueckentext} -- Option: Englischer Lückentext} Der Befehl \texttt{englueckentext} ist analog zum \texttt{lueckentext}-Befehl zu verwenden. Jedoch wird der als Standard angenommene, einleitende Satz bei Lückentextaufgaben in Englisch angezeigt. \textsc{Eingabe:} @@ -911,7 +901,7 @@ Der Befehl \texttt{englueckentext} ist analog zum \texttt{lueckentext}-Befehl zu A2=3 % Antwort rechts } -\subsection{\texttt{zuordnen}-Befehl} +\subsection{\texttt{\textbackslash zuordnen}} Dieser Befehl dient zum Erstellen des Zuordnungsformats von vier aus sechs Möglichkeiten. Die korrekten Antworten können hier frei als Buchstaben eingegeben werden. (hier: F, C, A, D) @@ -961,7 +951,7 @@ Die korrekten Antworten können hier frei als Buchstaben eingegeben werden. (hie A4={D}, % 4. richtige Zuordnung } -\subsubsection{Zusatz: Variable Breiten der Boxen} +\subsubsection{\texttt{\textbackslash zuordnen} -- Option: Variable Breiten der Boxen} Der \texttt{zuordnen}-Befehl erlaubt ein optionales Argument, um die Größen der beiden Boxen zu variieren. Dabei werden in den eckigen Klammern $[~]$ die Veränderung der linken Box (Änderung ca. zwischen -0.3 und 0.3) angegeben und die rechte Box wird automatisch angepasst. Will man etwa die linke Box vergrößern, gilt: @@ -1019,16 +1009,16 @@ Analog funktioniert die Verkleinerung der linken Box. Dazu müssen Werte kleiner \section{Typ-2-Aufgaben} Um die Struktur der Teil-2-Aufgaben ähnlich jener bei der standardisierten Reifeprüfung einhalten zu können, beinhaltet das Paket einige Befehle, die die Erstellung von Typ-2-Aufagben erleichtern. -\subsection{\texttt{aufgabenstellung}-Umgebung} +\subsection{\texttt{\textbackslash begin\{aufgabenstellung\} \ldots\ \textbackslash end\{aufgabenstellung\}}} Typ-2-Aufgaben sollten innerhalb einer Prüfung mithilfe der \texttt{langesbeispiel}-Umgebung eingegeben werden, um die volle Funktionalität (wie \texttt{notenschluessel}) des Pakets zu ermöglichen. Der einleitenden Aufgabentext kann dann eingegeben werden. Für die Eingabe der Aufgabenstellung kann dann die \texttt{aufgabenstellung}-Umgebung verwendet werden.\leer \subsubsection{\texttt{\textbackslash item} und \texttt{\textbackslash Subitem\{\}}} Innerhalb dieser Umgebung können dann mithilfe von \texttt{\textbackslash item} (nummeriert mit a), b) ,c), \ldots) und \texttt{\textbackslash Subitem\{\}} (nummeriert mit 1), 2), \ldots) die jeweiligen Items und Subitems der Aufgabe eingegeben werden. \leer -\subsubsection{Ausgleichspunkte} +\subsubsection{Ausgleichspunkte -- \texttt{\textbackslash Aitem} und \texttt{\textbackslash ASubitem\{\}}} Um die notwendigen Ausgleichspunkte in den Typ-2-Aufgaben zu markieren, können die Befehle \texttt{item} und \texttt{Subitem} durch \texttt{\textbackslash Aitem} und \texttt{\textbackslash ASubitem\{\}} ersetzt werden. Dabei wird vor den geschriebenen Text noch ein \fbox{A} eingefügt -- die Nummerierung wird aber wie gewohnt fortgesetzt. -\subsection{\texttt{loesung}-Umgebung} +\subsection{\texttt{\textbackslash begin\{loesung\} \ldots\ \textbackslash end\{loesung\}}} Mithilfe der \texttt{loesung}-Umgebung kann die Lösungserwartung sowie der Lösungsschlüssel eingegeben werden. Analog zur Aufgabenstellung können \texttt{\textbackslash item} und \texttt{\textbackslash Subitem\{\}} verwendet werden. Die Eingabe innerhalb der \texttt{loesung}-Umgebung werden nur dann angezeigt, wenn die Lösungsanzeige aktiviert wurde. Die Nummerierung der Subitems kann man mit folgender Eingabe zurücksetzen: \texttt{\textbackslash setcounter{subitemcounter}{0}}.\leer Eine vollständige Typ-2-Aufgabe könnte also wie folgt aussehen: @@ -1063,7 +1053,7 @@ Hier steht der einleitende Text der Typ-2-Aufgabe. \end{langesbeispiel} \end{verbatim} -\newpage + \textsc{Ausgabe:} \setcounter{Antworten}{1} diff --git a/macros/xetex/latex/xesoul/README b/macros/xetex/latex/xesoul/README new file mode 100644 index 0000000000..90f09481cf --- /dev/null +++ b/macros/xetex/latex/xesoul/README @@ -0,0 +1,5 @@ +The xesoul package is a little hack that allows users to use +the soul package with XeLaTeX with no problem. + +Apostolos Syropoulos +2021/03/29 diff --git a/macros/xetex/latex/xesoul/xesoul.dtx b/macros/xetex/latex/xesoul/xesoul.dtx new file mode 100644 index 0000000000..acf496065b --- /dev/null +++ b/macros/xetex/latex/xesoul/xesoul.dtx @@ -0,0 +1,103 @@ +%% \CharacterTable +%% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z +%% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z +%% Digits \0\1\2\3\4\5\6\7\8\9 +%% Exclamation \! Double quote \" Hash (number) \# +%% Dollar \$ Percent \% Ampersand \& +%% Acute accent \' Left paren \( Right paren \) +%% Asterisk \* Plus \+ Comma \, +%% Minus \- Point \. Solidus \/ +%% Colon \: Semicolon \; Less than \< +%% Equals \= Greater than \> Question mark \? +%% Commercial at \@ Left bracket \[ Backslash \\ +%% Right bracket \] Circumflex \^ Underscore \_ +%% Grave accent \` Left brace \{ Vertical bar \| +%% Right brace \} Tilde \~} +%\iffalse +% +% (c) copyright 2021 A Syropoulos +% +% This program can be redistributed and/or modified under the +% terms of the LaTeX Project Public License Distributed from +% http://www.latex-project.org/lppl.txt; either +% version 1.3c of the License, or any later version. +% +% This work has the LPPL maintenance status `maintained'. +% +% Please report errors or suggestions for improvement to +% +% Apostolos Syropoulos (asyropoulos@yahoo.com) +% +%\fi +% \CheckSum{15} +% \iffalse This is a Metacomment +% +%\ProvidesFile{xesoul.sty} +% +% [2021/02/23 v1.0 Package `xesoul.sty'] +% +% \begin{macrocode} +%<*driver> +\documentclass{ltxdoc} +\GetFileInfo{xesoul.drv} +\usepackage{xltxtra} +\usepackage{hyperref} +\begin{document} +\setmainfont[Mapping=tex-text,Script=Greek, + SmallCapsFeatures={Contextuals=Alternate}]{Universal Modern} +\setmonofont{UM Typewriter} +\setsansfont[Mapping=tex-text]{GFS Neohellenic} + + \DocInput{xesoul.dtx} +\end{document} +% +% \end{macrocode} +% \fi +%\StopEventually{} +%\MakeShortVerb{\|} +%\title{\textsf{xesoul}: Using the \textsf{soul} package with \XeLaTeX} +%\author{Apostolos Syropoulos\\ +% Xanthi, Greece\\ +% \texttt{asyropoulos@yahoo.com}} +% \date{2021/02/23} +%\maketitle +% \begin{abstract} +% This is a small package that allows users to successfully use the \textsf{soul} package +% when preparing documents with \XeLaTeX. +%\end{abstract} +% +%\section{Introduction} +% +% While I was trying to underline Greek text using the |\ul| command of the \textsf{soul} package with \XeLaTeX, +% I discovered that this is not possible. I was getting several errors and the output was looking terrible. +% I did not know what was wrong. I did a Google search and I found that another user had the same problem. +% He posted his case to the \XeTeX\ mailing list and Ulrike Fischer helped him and gave a solution to the problem. +% However, since the solution is not part of the \textsf{soul} package, I thought it would be a good idea to create +% a new package that would solve the problem. +% +% \section{The Implementation} +% +% The code of the package is very simple. It loads the \textsf{soul} package and then it sets the |\SOUL@tt| +% font to be a Unicode encoded font. The most natural choice is to use a font that is included in every +% distribution of \TeX{live}. Thus the package uses the freemono font. If for some reason the font is not available +% in your installtion, then you can download it from the following URL: +% \begin{center} +% \href{https://fontmeme.com/fonts/freemono-font/}{https://fontmeme.com/fonts/freemono-font/} +% \end{center} +% Since the \textsf{soul} package uses the |\SOUL@ttwidth| variable, we need to reset it. Now one can use +% the \textsf{soul} package with no problem. +% \begin{macrocode} +%<*xesoul> +\RequirePackage{iftex} +\ifXeTeX +\RequirePackage{soul} +\font\SOUL@tt="[FreeMono.ttf]" +\setbox\z@\hbox{\SOUL@tt-} +\SOUL@ttwidth\wd\z@ +\else +\typeout{The `xesoul' package can be used only with XeLaTeX.} +\fi +% +% \end{macrocode} +% +% \Finale diff --git a/macros/xetex/latex/xesoul/xesoul.ins b/macros/xetex/latex/xesoul/xesoul.ins new file mode 100644 index 0000000000..d8bd52e18d --- /dev/null +++ b/macros/xetex/latex/xesoul/xesoul.ins @@ -0,0 +1,53 @@ +%% Copyright 2021 Apostolos Syropoulos +%% +%% This file is part of the `xesoul' package. +%% You are not allowed to modify its contents. +%% +\def\filedate{2021/02/23} +\def\batchfile{xesoul.ins} +\input docstrip.tex + +{\ifx\generate\undefined +\Msg{**********************************************} +\Msg{*} +\Msg{* This installation requires docstrip} +\Msg{* version 2.3c or later.} +\Msg{*} +\Msg{* An older version of docstrip has been input} +\Msg{*} +\Msg{**********************************************} +\errhelp{Move or rename old docstrip.tex.} +\errmessage{Old docstrip in input path} +\batchmode +\csname @@end\endcsname +\fi} + +\preamble + + (c) copyright 2021 Apostolos Syropoulos + This program can be redistributed and/or modified under the + terms of the LaTeX Project Public License Distributed from + http://www.latex-project.org/lppl.txt; either + version 1.3c of the License, or any later version. + + This work has the LPPL maintenance status `maintained'. + + Please report errors or suggestions for improvement to + + Apostolos Syropoulos (asyropoulos@yahoo.com) + +\endpreamble + + +\keepsilent + +\generate{\generateFile{xesoul.sty}{f}{\from{xesoul.dtx}{xesoul}}} +\postamble +\endpostamble +\Msg{**********************************************************} +\Msg{* To finish installation you have to copy} +\Msg{* the generated .sty file in the appropriate} +\Msg{* directory and then to rehash your TeX system} +\Msg{* Happy XeTeXing!} +\Msg{**********************************************************} +\endinput diff --git a/macros/xetex/latex/xesoul/xesoul.pdf b/macros/xetex/latex/xesoul/xesoul.pdf new file mode 100644 index 0000000000..af76885c17 Binary files /dev/null and b/macros/xetex/latex/xesoul/xesoul.pdf differ -- cgit v1.2.3