diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/latex/contrib/polexpr/polexpr.txt |
Initial commit
Diffstat (limited to 'macros/latex/contrib/polexpr/polexpr.txt')
-rw-r--r-- | macros/latex/contrib/polexpr/polexpr.txt | 2593 |
1 files changed, 2593 insertions, 0 deletions
diff --git a/macros/latex/contrib/polexpr/polexpr.txt b/macros/latex/contrib/polexpr/polexpr.txt new file mode 100644 index 0000000000..46ea7e32fa --- /dev/null +++ b/macros/latex/contrib/polexpr/polexpr.txt @@ -0,0 +1,2593 @@ +.. comment: -*- fill-column: 72; mode: rst; -*- + +=============================== + Package polexpr documentation +=============================== + +0.7.4 (2019/02/12) +================== + +.. 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}<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;: + +``\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 + <warningtacit_>`_. + + 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 <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}{<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 <\\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 <PolToSturm_>`_ 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 <PolToSturm_>`_ to have been executed, + whereas the expandable variant requires prior execution of + `\\PolSturmIsolateZeros <PolSturmIsolateZeros_>`_. + + See also the expandable + `\\PolSturmNbWithMultOfRootsOf{sturmname}\\LessThanOrEqualTo{value}`_ + which requires prior execution of + `\\PolSturmIsolateZeros* <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 ``<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 <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} + <PolSturmIsolatedZeroLeft_>`_, and for a negative root it is given + by `\PolSturmIsolatedZeroRight{sturmname}{k} + <PolSturmIsolatedZeroRight_>`_. 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* <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 + <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 <PolEvalAt_>`_. + +.. _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 + <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 <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 <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}{\<csv\>}_>`_. + +.. _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} <PolRefineInterval[N]_>`_. + + The value is pre-formatted using `\\PolDecTostring + <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 + <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 + ``<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 ``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 <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 <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 + <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 + <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)} <PolDef_>`_ + 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 <poldef;_>`_ parser itself, + * Differentiation and anti-differentiation, + * Euclidean division and GCDs, + * Various utilities such as `\\PolFromCSV <PolFromCSV_>`_, + `\\PolMapCoeffs <PolMapCoeffs_>`_, + `\\PolToCSV <PolToCSV_>`_, `\\PolToExpr <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 <PolEvalAt_>`_ 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} <PolEvalAtExpr_>`_ syntax is needed for + using expressions in the second argument. + * incompatible or breaking changes: + + - `\\PolToExpr <PolToExpr_>`_ now by default uses *descending* + powers (it also treats differently coefficients equal to 1 or -1.) + Use `\\PolToExpr* <PolToExpr*_>`_ for *ascending* powers. + - `\\PolEval <PolEvalAt_>`_ 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 + <PolEvalReducedAt_>`_. + * new (or newly documented) macros: + + - `\\PolTypesetCmd <PolTypesetCmd_>`_ + - `\\PolTypesetCmdPrefix <PolTypesetCmdPrefix_>`_ + - `\\PolTypesetMonomialCmd <PolTypesetMonomialCmd_>`_ + - `\\PolEvalReducedAt <PolEvalReducedAt_>`_ + - `\\PolToFloatExpr <PolToFloatExpr_>`_ + - `\\PolToExprOneTerm <PolToExprOneTerm_>`_ + - `\\PolToFloatExprOneTerm <PolToFloatExprOneTerm_>`_ + - `\\PolToExprCmd <PolToExprCmd_>`_ + - `\\PolToFloatExprCmd <PolToFloatExprCmd_>`_ + - `\\PolToExprTermPrefix <PolToExprTermPrefix_>`_ + - `\\PolToExprVar <PolToExprVar_>`_ + - `\\PolToExprTimes <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 <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 <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 <PolMapCoeffs_>`_. + - `\\PolGCD <PolGCD_>`_ was buggy in case of first polynomial being + of lesser degree than the second one. + * breaking changes: + + - formerly `\\PolEval{P}\\At{foo} <PolEvalAt_>`_ 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} <PolEvalAtExpr_>`_ 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 <PolEvalReducedAt_>`_ + and `\\PolFloatEval <PolFloatEvalAt_>`_. + - 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 <PolGenFloatVariant_>`_. + * new non-expandable macros: + + - `\\PolGenFloatVariant <PolGenFloatVariant_>`_ + - `\\PolGlobalLet <PolGlobalLet_>`_ + - `\\PolTypesetOne <PolTypesetOne_>`_ + - `\\PolQuo <PolQuo_>`_ + - `\\PolRem <PolRem_>`_ + - `\\PolToSturm <PolToSturm_>`_ + - `\\PolToSturm\* <PolToSturm*_>`_ + - `\\PolSetToSturmChainSignChangesAt <PolSetToSturmChainSignChangesAt_>`_ + - `\\PolSetToNbOfZerosWithin <PolSetToNbOfZerosWithin_>`_ + - `\\PolSturmIsolateZeros <PolSturmIsolateZeros_>`_ + - `\\PolRefineInterval* <PolRefineInterval*_>`_ + - `\\PolRefineInterval[N] <PolRefineInterval[N]_>`_ + - `\\PolEnsureIntervalLength <PolEnsureIntervalLength_>`_ + - `\\PolEnsureIntervalLengths <PolEnsureIntervalLengths_>`_ + - `\\PolPrintIntervals <PolPrintIntervals_>`_ + - `\\PolPrintIntervalsPrintExactZero <PolPrintIntervalsPrintExactZero_>`_ + - `\\PolPrintIntervalsPrintLeftEndPoint <PolPrintIntervalsPrintLeftEndPoint_>`_ + - `\\PolPrintIntervalsPrintRightEndPoint <PolPrintIntervalsPrintRightEndPoint_>`_ + - `\\PolReduceCoeffs* <PolReduceCoeffs*_>`_ + - `\\PolMakeMonic <PolMakeMonic_>`_ + * new expandable macros: + + - `\\PolToExprOneTermStyleA <PolToExprOneTermStyleA_>`_ + - `\\PolIfCoeffIsPlusOrMinusOne <PolIfCoeffIsPlusOrMinusOne_>`_ + - `\\PolLeadingCoeff <PolLeadingCoeff_>`_ + - `\\PolSturmChainLength <PolSturmChainLength_>`_ + - `\\PolSturmNbOfIsolatedZeros <PolSturmNbOfIsolatedZeros_>`_ + - `\\PolSturmIfZeroExactlyKnown <PolSturmIfZeroExactlyKnown_>`_ + - `\\PolSturmIsolatedZeroLeft <PolSturmIsolatedZeroLeft_>`_ + - `\\PolSturmIsolatedZeroRight <PolSturmIsolatedZeroRight_>`_ + - ``\PolPrintIntervalsTheEndPoint`` (removed at 0.7) + - `\\PolPrintIntervalsTheIndex`_ + - ``\PolIfEndPointIsPositive`` (removed at 0.7) + - ``\PolIfEndPointIsNegative`` (removed at 0.7) + - ``\PolIfEndPointIsZero`` (removed at 0.7) + - `\\PolIntervalWidth <PolIntervalWidth_>`_ + - `\\PolDecToString <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 <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 <PolToSturm_>`_ with a constant polynomial + caused a division by zero error. + + * new macro: + + - `\\PolSturmIsolateZeros* <PolSturmIsolateZeros*_>`_ + acts like the `non-starred variant + <PolSturmIsolateZeros_>`_ 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* <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. + +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 |