diff options
-rw-r--r-- | Master/texmf-dist/doc/latex/polexpr/README | 373 | ||||
-rw-r--r-- | Master/texmf-dist/tex/latex/polexpr/polexpr.sty | 1009 | ||||
-rwxr-xr-x | Master/tlpkg/bin/tlpkg-ctan-check | 2 | ||||
-rw-r--r-- | Master/tlpkg/tlpsrc/collection-mathscience.tlpsrc | 1 | ||||
-rw-r--r-- | Master/tlpkg/tlpsrc/polexpr.tlpsrc | 0 |
5 files changed, 1384 insertions, 1 deletions
diff --git a/Master/texmf-dist/doc/latex/polexpr/README b/Master/texmf-dist/doc/latex/polexpr/README new file mode 100644 index 00000000000..a258011f9dd --- /dev/null +++ b/Master/texmf-dist/doc/latex/polexpr/README @@ -0,0 +1,373 @@ +-*- fill-column: 72; mode: text; -*- + +Package polexpr +=============== + +License +------- + +Copyright (C) 2018 Jean-Francois Burnol + +See documentation of package 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 + + http://www.latex-project.org/lppl/lppl-1-3c.txt + +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-Francois Burnol. + +This Work consists of the package file polexpr.sty and this README. + + +Abstract +-------- + +The package provides "\poldef": a parser of polynomial expressions +based upon the "\xintdeffunc" mechanism of package xintexpr. + +The syntax is + + \poldef <name>(x):=<expression in variable x>; + +where in place of "x" an arbitrary letter is authorized. The expression +uses the operations of algebra (including composition of functions) with +standard operators, fractional numbers (possibly in scientific notation) +and previously defined polynomial functions or other constructs as +recognized by the \xintexpr numerical parser. + +The so-defined name() \xintexpr-function is also known to the package +via its polynomial coefficients, thus allowing dedicated macros to +implement polynomial algorithmics. + +Examples +-------- + +\poldef f(x):= 1-x+x^2; + +This defines polynomial "f". Polynomial names must start with a letter +and may contain letters, digits, and underscores. 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". + +\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}{df_dx} sets "df_dx" to the derivative of "f". + +\PolDiff{df_dx}{f_xx} obtains second derivative + +\PolDiff[3]{f}{d3f_dx3} computes directly the third derivative + +$f(z) = \PolTypeset[z]{f} $\newline +$f'(z) = \PolTypeset[z]{df_dx}$\newline +$f''(z) = \PolTypeset[z]{f_xx}$\newline +$f'''(z)= \PolTypeset[z]{d3f_dx3}$\par + +*Important*: the package does not currently know rational functions. +and "/" 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. + +*Attention*: "1/2 x" skips the space and is treated like "1/(2x)" +because of the tacit multiplication rules of \xintexpr. But this means +it gives zero! Thus one must use (1/2)x or 1/2*x or (1/2)*x for +disambiguation. + +\poldef k(x):= (x-1)(x-2)(x-3)(x-4)/(x^2-5x+4);% + +\PolTypeset{k} gives the expected x^2-5x+6 + +\poldef f1(x):= 25(x-1)(x^2-2)(x-3)(x-4)(x-5);% +\poldef f2(x):= 37(x-1)(x^2-2)(x-6)(x-7)(x-8);% + +\PolGCD{f1}{f2}{k} sets "k" to the (unitary) GCD of "f1" and "f2". + +\PolToExpr{k} expandably gives 2-2*x^1-1*x^2+1*x^3 for console +or file output (this is Maple-compatible input syntax). + +Non-expandable macros +--------------------- + +\poldef name(letter):= polynomial expression using letter; + This evaluates the polynomial expression and stores the + coefficients in a private structure accessible later via other + package macros, under the user-chosen "name". Of course + previously defined polynomials are allowed in a new expression. + Names must start with a letter and are constituted of letters, + digits and underscore characters. See Examples above. + + As a side effect the function name() is recognized as a genuine + \xintexpr...\relax function for (exact) numerical evaluation. It + computes values not according to the original expression but via + the Horner scheme corresponding to the polynomial coefficients. + + 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{name}{P(x)} + Does the same but the variable is assumed to be "x". To use another + letter, pass it as first optional argument. + +\PolLet{g}{f} + Makes a copy of already defined polynomial f to new one g. + Same effect as \PolDef{g}{f(x)} but faster. + +\PolAssign{f}\toarray\Array + Defines a one-argument expandable macro \Array{#1} which expands + to the (raw) #1th polynomial coefficient. + + - Attention, coefficients here are indexed starting at 1. + + - With #1=-1, -2, ..., \Array{#1} returns leading coefficients. + + - With #1=0, returns the number of coefficients, i.e. 1+degree(f) + for non-zero polynomials. + + - Out-of-range #1's return 0/1[0]. + +\PolGet{f}\fromarray\Array + Does the reverse operation to \PolAssign{f}\toarray\Array. No error + checks on validity of coefficients as numbers. Each \Array{index} + is expanded in an \edef before being assigned to a coefficient. + Leading zero coefficients 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; + However the coefficients are still in their original form (i.e. + they were not subjected to \xintRaw or similar xintfrac macro.) + +\PolFromCSV{f}{comma separated coefficients} + Defines a polynomial directly from the comma separated list (or a + macro expanding to such a list) of its coefficients, the constant + term being the first item. No validity checks. Spaces from the list + argument are trimmed. List items are expanded in an \edef, but + currently they are left in their original form like e.g. 1.5e3 + which is not converted to 15/1[2] "raw" xintfrac format (this may + change). + + Leading zero coefficients are removed: + \PolFromCSV{J}{0, 0, 0, 0, 0, 0, 0, 0, 0, 0} defines the zero + polynomial, which has only one (zero) coefficient. + + See also expandable macro \PolToCSV. + +\PolTypeset[x]{name} + Typesets in descending powers in math mode using the specified + variable (default x.) By default zero coefficients are skipped + (issue \poltypesetalltrue to get all of them in output). + + Macros \PolTypesetCmd, \PolTypesetPlus, \PolTypesetMonomial + can help configure the output. See the package code. + +\PolTypeset*[x]{name} + Typesets in ascending powers. + +\PolDiff{f1}{f2} + This sets f2 to the first derivative of f1. It is allowed to issue + \PolDiff{f}{f}, effectively replacing f by f'. + + Coefficients of the result f2 are irreducible fractions + (see `Technicalities`_ for the whole story.) + +\PolDiff[N]{f1}{f2} + This sets f2 to the Nth derivative of f1. Identical arguments + is allowed. With N=0, same effect as \PolLet{f2}{f1}. + With negative N, switched to using \PolAntiDiff. + +\PolAntiDiff{f1}{f2} + This sets f2 to the primitive of f1 vanishing at zero. + + Coefficients of the result f2 are irreducible fractions + (see `Technicalities`_ for the whole story.) + +\PolAntiDiff[N]{f1}{f2} + This sets f2 to the result of N successive integrations on f1. + With negative N, it switches to using \PolDiff. + +\PolDivide{f1}{f2}{Q}{R} + This sets Q and R to be the quotient and remainder in the Euclidean + division of f1 by f2. + +\PolGCD{f}{g}{k} + This sets k to be the G.C.D. It is a unitary polynomial except if + both f and g vanish, then k is the zero polynomial. + +\PolMapCoeffs{\macro}{name} + It modifies 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^2\relax}} + to replace nth coefficient f_n by f_n * n^2. + +\PolReduceCoeffs{name} + About the same as \PolMapCoeffs{\xintIrr}{name} (but adds [0] + postfix which speeds up xintfrac operations when evaluating.) + +Expandable macros +----------------- + +All these macros expand completely in two steps except \PolToExpr +which needs a \write, \edef or a \csname...\endcsname context. + +\PolEval{name}\At{value} + It boils down to \xinttheexpr reduce(name(value))\relax. + +\PolNthCoeff{name}{N} + It expands to the raw Nth coefficient (0/1[0] if index is out of + range). With N=-1, -2, ... expands to the leading coefficients. + +\PolDegree{name} + It expands to the degree. This is -1 if zero polynomial but this may + change in future. Should it then expand to -\infty ? + +\PolToExpr{f} + Expands to f_0 + f_1*x + f_2*x^2 + ... (ascending powers). [1, 2] + + [1] in a \write, \edef, or \csname...\endcsname, but not under + \romannumeral-`0 + + [2] the letter x is (in this release) not customizable. + + By default zero coefficients are skipped (issue \poltoexprtrue to + get all of them in output). + + No + sign before negative coefficients, for compliance with Maple + input format. This means though that parsing the result back via + naive delimited macros is difficult, see \PolToList and \PolToCSV + for more low-level formats making it easier to get expandably some + output of one's choice, which may possibly be parsed later on by + other macros of one's design, or from other packages. + + 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 simply "f(x)" will be more efficient for the + identical result. + + \PolToExprCmd is the one-argument macro used by \PolToExpr for the + coefficients, it defaults to \xintPRaw{\xintRawWithZeros{#1}}. One + will have to redefine it to use \xintIrr{#1} in place of + \xintRawWithZeros{#1} to get in output reduced coefficients. + +\PolToList{f} + Expands to {f_0}{f_1}...{f_N} with N = degree of f (except zero + polynomial which does give {0/1[0]} and not an empty output.) + +\PolToCSV{f} + Expands to f_0, f_1, f_2, ....., f_N. Converse of \PolFromCSV. + +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 rather. + The colon in := may be active with no consequences. + +- Beware the 1/2 x problem: as mentioned above, it will be give zero due + to the tacit multiplication rules of \xintexpr and to the fact that + the package will do the Euclidean division of 1 by polynomial 2x. + +- During execution of polynomial operations by \poldef (but not during + the initial purely numerical parsing of the expression), the xintfrac + macro \xintAdd is temporarily patched to always express a/b + c/d with + L.C.M.(b,d) as denominator. Indeed the current (xint 1.2p) \xintAdd + uses (ad+bc)/bd formula except if b divides d or d divides b, which + quickly leads in real life to big denominators. + + It is probable that this convention will be backported as default + behaviour of xintfrac's \xintAdd in a future xint release. When this + change is merged, there will be an impact on coefficients computed by + \poldef because the change will apply even to the pure numerical + evaluations arising during the initial stage of the parsing. Of course + the coefficients are still the same rational numbers, only + representation as fractions may change. + +- As a consequence of previous rule, user-chosen common denominators + survive addition and multiplications: + + \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 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 (which in this + example is the l.c.m of the denominators of the reduced coefficients.) + +- \PolDiff always applies \xintIrr to the resulting coefficients, except + that the "decimal" 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. + +- If f was created from comma separated values by macro \PolFromCSV, + then the exact same coefficients (except those zero coefficients + beyond the leading monomial) will be in the output of \PolToList and + \PolToCSV in their original input form: a 1.3e2 will again be a 1.3e2. + + In contrast when such coefficients are used in a \poldef (or \PolDef) + expression, they get transformed during the parsing to the xintfrac + "raw" format. This is an unavoidable consequence of usage by \poldef + of \xintdeffunc which itself is based on \xintexpr. This "raw" format + speeds up expansion of xintfrac macros for numerical evaluations. + +- Currently, the package does not as a result of \poldef add to the TeX + memory an already pre-computed "array" structure for the polynomial + coefficients, as would be constructed by \PolAssign{f}\toarray\Macro. + Such structures are used, but for internal calculations in temporarily + restricted scopes. Apart from the function f() known to the + (numerical) \xintexpr parser (whose meaning can be found in the log + file after \xintverbosetrue), the data is (currently) stored in a + single other macro encapsulating the degree, and the coefficients as a + list. This may evolve in 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: + + *. differentiation and anti-differentiation, + *. Euclidean division and GCDs, + *. various utilities such as \PolFromCSV, \PolToCSV, \PolToExpr. + + Only one-variable polynomials so far. + + Due to lack of available time I have not really yet set-up a + sufficient enough test suite. Bug reports very welcome! diff --git a/Master/texmf-dist/tex/latex/polexpr/polexpr.sty b/Master/texmf-dist/tex/latex/polexpr/polexpr.sty new file mode 100644 index 00000000000..983016987ea --- /dev/null +++ b/Master/texmf-dist/tex/latex/polexpr/polexpr.sty @@ -0,0 +1,1009 @@ +% author: Jean-François Burnol +% License: LPPL 1.3c (author-maintained) +\ProvidesPackage{polexpr}% + [2018/01/11 v0.1 Polynomial expressions with rational coefficients (JFB)]% +\RequirePackage{xintexpr}[2016/03/19]% xint 1.2g (or 1.2c 2015/11/16 at least) +\edef\POL@restorecatcodes + {\catcode`\noexpand\_ \the\catcode`\_ \catcode0 \the\catcode0\relax}% +\catcode`\_ 11 \catcode0 12 + +%% AUXILIARIES +\newif\ifPOL@pol % (cf core algebra macros) +%% the main exchange structure (stored in macros \POLuserpol@<name>) +%% is: degree.\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 \empty token is to avoid brace removal issues for degree 0 polynomials + {\def#3{#1}\expandafter\def\expandafter#4\expandafter{#2}}% +% +\def\POL@resultfromarray #1{% ATTENTION, **MUST** be executed with +% \count@ set to 1 + degree (thus \count@ = 0 for zero polynomial) + \edef\POL@result{% + \the\numexpr\count@-\@ne.\noexpand\empty + \xintiloop [1+1]% +% always done at least once with index 1, hence ok for zero polynomial + \expandafter\POL@braceit\csname POL@array#1\xintiloopindex\endcsname + \ifnum\xintiloopindex<\count@ + \repeat}% +}% +\def\POL@braceit#1{{#1}}% needed as \xintiloopindex can not "see" through braces + + +\newif\ifxintveryverbose +\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 + \let\POL@original_redefinemacros\XINT_expr_redefinemacros + \let\XINT_expr_redefinemacros\POL@redefinemacros + \edef\POL@tmp{\ifxintverbose1\else0\fi}% + \unless\ifxintveryverbose\xintverbosefalse\fi + \xintdeffunc __pol(#2):=#4;\if1\POL@tmp\xintverbosetrue\fi + \let\XINT_expr_redefinemacros\POL@original_redefinemacros + \edef\POL@polname{\xint_zapspaces #1 \xint_gobble_i}% + \begingroup + \setbox0\hbox{% + \let\xintScalarAdd\xintAdd + \let\XINT_fadd_C\POL_fadd_C % patch Add to use l.c.m. + \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 to get \POL@result defined even if numerical only expression + \xintAdd{0}% + {\csname XINT_expr_userfunc___pol\endcsname +% comma delimited from xintexpr at 1.2p or earlier. Might change. + {\global\POL@poltrue\def\POL@result{1.\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{% + \expandafter\POL@ifZero\csname POLuserpol@#1\endcsname + {\@namedef{XINT_expr_userfunc_#1}##1,{0/1[0]}}% + {\POL@newpolhorner{#1}}% + \unless\ifcsname XINT_expr_userfuncNE:#1\endcsname\POL@addtoextras{#1}\fi + \expandafter\XINT_expr_defuserfunc + \csname XINT_expr_func_#1\expandafter\endcsname + \csname XINT_expr_userfunc_#1\endcsname + \ifxintverbose\POL@info{#1}\fi +}% +\def\POL@info #1{% + \xintMessage {polexpr}{Info}% + {Function #1 for the \string\xintexpr\space parser is + associated to \string\XINT_expr_userfunc_#1\space + whose meaning uses Horner scheme: + \expandafter\meaning + \csname XINT_expr_userfunc_#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 + \endgroup + \expandafter\let\csname XINT_expr_userfunc_#1\endcsname\POL@tmp +}% +\def\POL@newpol@horner#1{\let\xintAdd\relax\let\xintMul\relax + \gdef\POL@tmp##1,{#1}\POL@newpol@horner@loop.}% +\def\POL@newpol@horner@loop.#1{% + \if\relax#1\expandafter\xint_gob_til_dot\fi + \xdef\POL@tmp##1,{\xintiiifZero{#1}{\@firstofone}{\xintAdd{#1}}% + {\xintMul{##1}{\POL@tmp##1,}}}% + \POL@newpol@horner@loop.% +}% +%% Customizes xintexpr.sty's \XINT_NewExpr (\POL@addtoextras{name}) +\begingroup +\catcode`~ 12 +\catcode`$ 12 % $ +\catcode`! 11 +\gdef\POL@NEfork_one #1#2!#3#4{% + \if ###1\xint_dothis {\POL__settopol{#4}}\fi + \if ~#1\xint_dothis {\POL__userfunc{#4}}\fi + \if $#1\xint_dothis {~xintApply::csv{~POL_userfunc{#4}}}\fi %$ + \xint_orthat {#3}#1#2\endcsname +}% +\gdef\POL@@redefineone #1#2#3{% #3 = name + % Used for immediate f(numerical) + \let#2#1% + % \XINT_expr_userfunc_name + \def#1##1\endcsname % key trick is to fetch up to \endcsname! + {\expandafter\POL@NEfork_one\romannumeral`^^@##1!#2{#3}}% +}% + % Used when f(x) is encountered: great gain here! +\gdef\POL__settopol#1#2,{~POL_settopol{#1}}% +\gdef\POL_settopol#1{\global\POL@poltrue\expandafter\let\expandafter + \POL@result\csname POLuserpol@#1\endcsname}% + % Used when argument is neither numerical nor a macro parameter + % Quite some magic here! (braces couldn't be used this way in \xintexpr) +\gdef\POL__userfunc#1#2,\endcsname{~POL_userfunc{#1}{#2},\endcsname}% +\gdef\POL_userfunc#1{\csname XINT_expr_userfunc_#1\endcsname}% +\endgroup +\def\POL@addtoextras#1{% + \oodef\POL@redefineextras{\expandafter\POL@redefineextras + \expandafter\POL@@redefineone + \csname XINT_expr_userfunc_#1\expandafter\endcsname + \csname XINT_expr_userfuncNE:#1\endcsname{#1}}% +}% +%\let\POL@original@redefinemacros\XINT_expr_redefinemacros % do locally +\def\POL@redefinemacros{\POL@original_redefinemacros\POL@redefineextras}% +\let\POL@redefineextras\@empty +% +\newcommand\PolLet[2]{% + \expandafter\let\csname POLuserpol@#1\expandafter\endcsname + \csname POLuserpol@#2\endcsname + \unless\ifcsname XINT_expr_userfuncNE:#1\endcsname\POL@addtoextras{#1}\fi + \expandafter\let\csname XINT_expr_userfunc_#1\expandafter\endcsname + \csname XINT_expr_userfunc_#2\endcsname + \expandafter\XINT_expr_defuserfunc + \csname XINT_expr_func_#1\expandafter\endcsname + \csname XINT_expr_userfunc_#1\endcsname + \ifxintverbose\POL@info{#1}\fi +}% + + +\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[1]{\def\POL@polname{#1}% zap spaces in #1? + \begingroup % closed in \POL@getfrom + \POL@getfrom}% +% attention au name clash proche avec \POL@get auxiliaire de \POL@add etc.. +\def\POL@getfrom#1\fromarray#2{% + \count@#2{0} % must be > 0, else could create infinite loop + % \ifnum\count@>\z@\else\InvalidArrayError_PolGet\fi + \xintloop + \edef\POL@tmp{#2{\count@}}% + \xintiiifZero{\POL@tmp}% + {\iftrue}% + {\iffalse}% + \advance\count@\m@ne + \repeat +% should I use \xintRaw ? but if #2 expands only in an \edef, problem +% (but it is not very probable the #2 macro does not already give completely +% expanded contents), I would need to proceed in two steps. Or the \xintRaw +% could get injected at level of \POL@newpol + \def\POL@tmp##1.{{#2{##1}}}% + \edef\POL@result{\the\numexpr\count@-\@ne.\noexpand\empty + \xintiloop[1+1]% + \expandafter\POL@tmp\xintiloopindex.% + \ifnum\xintiloopindex<\count@ + \repeat}% + \expandafter + \endgroup + \expandafter + \def\csname POLuserpol@\POL@polname\expandafter\endcsname + \expandafter{\POL@result}% + \expandafter\POL@newpol\expandafter{\POL@polname}% +}% + + +\newcommand\PolFromCSV[2]{\def\POL@polname{#1}% + \begingroup % closed in \POL@getfrom + \xintAssignArray\xintCSVtoList{#2}\to\POL@arrayA + \POL@getfrom\fromarray\POL@arrayA +% semble un peu indirect et sous-optimal +% mais je veux élaguer les coefficients nuls. Peut-être à revoir. +}% + + +\newif\ifpoltypesetall +\newcommand\PolTypesetPlus[1]{\xintiiifSgn{#1}{}{+}{+}}% +\newcommand\PolTypesetCmd[1]{\xintifOne{\xintiiAbs{#1}}% + {\ifnum\PolIndex=\z@\xintiiSgn{#1}\else + \xintiiifSgn{#1}{-}{}{}\fi}% + {\xintSignedFrac{#1}}}% +\newcommand\PolTypesetMonomial{% + \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]}% + \else + \ifnum\POL@var@deg=\z@ + \expandafter\PolTypesetCmd\POL@var@coeffs + \else + \def\POL@ts@plus##1{\let\POL@ts@plus\PolTypesetPlus}% + \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@plus{#1}\PolTypesetCmd{#1}\PolTypesetMonomial}% + \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@plus{#1}\PolTypesetCmd{#1}\PolTypesetMonomial + \edef\PolIndex{\the\numexpr\PolIndex+\POL@@ne@or@m@ne}\POL@ts@nocheck.% +}% + + +\newcommand\PolMapCoeffs[2]{% #1 = macro, #2 = name + \begingroup + \def\POL@map@macro{#1}% + \expandafter\expandafter\expandafter\POL@split + \csname POLuserpol@#2\endcsname;\POL@var@deg\POL@var@coeffs +% attention à ne pas faire un \expandafter ici, car brace removal si 1 item + \xintAssignArray\POL@var@coeffs\to\POL@arrayA + \def\index{0}% + \count@\z@ + \expandafter\POL@map@loop\expandafter.\POL@var@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}% + \POL@newpol{#2}% +}% +\def\POL@map@loop.#1{\if\relax#1\expandafter\xint_gob_til_dot\fi + \advance\count@\@ne + \edef\POL@map@coeff{\POL@map@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[1]{\PolMapCoeffs{\POL@xintIrr}{#1}}% + + +%% EUCLIDEAN DIVISION +\newcommand\PolDivide[4]{% #3=quotient, #4=remainder of #1 by #2 + \begingroup + \let\xintScalarSub\xintSub + \let\XINT_fadd_C\POL_fadd_C + \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 POLuserpol@#3\expandafter\expandafter\expandafter\endcsname + \expandafter\expandafter\expandafter{\expandafter\POL@Q\expandafter}% + \expandafter + \def\csname POLuserpol@#4\expandafter\endcsname\expandafter{\POL@R}% + \POL@newpol{#3}% + \POL@newpol{#4}% +}% + + +%% GCD +\newcommand\PolGCD[3]{% sets #3 to the (unitary) G.C.D. of #1 and #2 + \begingroup + \let\xintScalarSub\xintSub + \let\XINT_fadd_C\POL_fadd_C + \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 + {\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@degB + \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}% + \POL@newpol{#3}% +}% +\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{\PolLet{#2}{#1}}% +\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.\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.\noexpand\empty\POL@var@coeffs}% + \fi + \POL@newpol{#2}% +}% +% 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{% + \expandafter\expandafter\expandafter\POL@split + \csname POLuserpol@#1\endcsname;\POL@var@deg\POL@var@coeffs + \ifnum\POL@var@deg<\z@ + \@namedef{POLuserpol@#2}{-1.\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.\noexpand\empty{0/1[0]}\POL@var@coeffs}% + \fi + \POL@newpol{#2}% +}% + + +%% CORE ALGEBRA MACROS +%% We do this non-expandably, but in a nestable way... this is the whole +%% point because \xintdeffunc as used by \PolNewPol 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. +\def\POL@get#1#2#3{% + \global\POL@polfalse + \begingroup + \def\POL@result{#3}% + #3% + \expandafter + \endgroup + \expandafter\def\expandafter#1\expandafter{\POL@result}% + \unless\ifPOL@pol + % avoid expanding more than twice #3 + \edef#1{#3}% + \xintiiifZero{#1}% + {\def#1{-1.\empty{0/1[0]}}}% + {\edef#1{0.\noexpand\empty{#1}}}% + \fi + #2% +}% +%% 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{% + \global\POL@poltrue + \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{% + \global\POL@poltrue + \POL@ifZero\POL@A + {\def\POL@result{-1.\empty{0/1[0]}}}% + {\POL@ifZero\POL@B + {\def\POL@result{-1.\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{% + \global\POL@polfalse + \begingroup + \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}% + {\def\POL@result{-1.\empty{0/1[0]}}}% + {\edef\POL@result{0.\noexpand\empty{\POL@A}}}% + \else + \edef\POL@B{\numexpr\xintNum{#2}\relax}% no check on exponent >= 0 + \ifcase\POL@B + \def\POL@result{0.\empty{1/1[0]}}% + \or + \let\POL@result\POL@A + \else + \POL@@pow@check + \fi + \fi + \global\POL@poltrue +}% +\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 + \edef\POL@A{\expandafter\xintScalarPow\romannumeral`^^@% + \expandafter\xint_gob_til_dot\POL@A{\POL@B}}% + \xintiiifZero{\POL@A}% + {\def\POL@result{-1.\empty{0/1[0]}}}% + {\edef\POL@result{0.\noexpand\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} + {\edef\POL@result + {\the\POL@B.% here at least 2. + \noexpand\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}\global\POL@poltrue}% + {\def\POL@result{#1}\global\POL@poltrue}% +}% + +%% 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{% + \global\POL@poltrue + \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{% + \global\POL@polfalse + \begingroup + \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}% + {\def\POL@result{-1.\empty{0/1[0]}}}% + {\edef\POL@result{0.\noexpand\empty{\POL@A}}}% + \else + \edef\POL@B{0.\noexpand\empty{-1/1[0]}}% + \POL@@mul + \fi + \global\POL@poltrue +}% + + +%% EXPANDABLE MACROS +\newcommand\PolEval{}% +\def\PolEval#1#2\At#3{\romannumeral`^^@\xinttheexpr reduce(#1(#3))\relax}% +% +\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}}}% + + +\newif\ifpoltoexprall +\newcommand\PolToExprCmd[1]{\xintPRaw{\xintRawWithZeros{#1}}}% +\newcommand\PolToExprCmdWithSep[1]{\xintiiifSgn{#1}{}{+}{+}\PolToExprCmd{#1}}% +\newcommand\PolToExpr[1]{\expandafter\expandafter\expandafter + \POL@toexpr\csname POLuserpol@#1\endcsname\relax}% +% +\def\POL@toexpr#1.\empty{% + \ifnum#1<\z@ + \PolToExprCmd{0/1[0]}\expandafter\@gobbletwo + \else + \expandafter\POL@toexpr@a + \fi}% +% +\def\POL@toexpr@a #1{\ifpoltoexprall\expandafter\POL@toexprall@b + \else\expandafter\POL@toexpr@b + \fi{#1}{}\PolToExprCmd0.}% +% +\def\POL@toexpr@b #1#2#3{% + \xintiiifZero{#1}% + {\expandafter\POL@toexpr@loop\expandafter#3}% + {#3{#1}#2\expandafter\POL@toexpr@loop\expandafter\PolToExprCmdWithSep}% + \expandafter\POL@toexpr@b\the\numexpr\@ne+% +}% +\def\POL@toexprall@b #1#2#3{% + #3{#1}#2% + \expandafter\POL@toexpr@loop\expandafter\PolToExprCmdWithSep + \expandafter\POL@toexprall@b\the\numexpr\@ne+% +}% +\def\POL@toexpr@loop#1#2#3.#4{% + \if\relax#4\expandafter\xint_gob_til_dot\fi #2{#4}{*x^#3}#1#3.% +}% + + +%% Patch of xintfrac.sty's \xintAdd: for a/b + c/d, use lcm(b,d) +\RequirePackage{xintgcd} +\def\POL_fadd_C #1#2#3% +{% + \expandafter\POL_fadd_D + \romannumeral0\xintiigcd{#2}{#3}.% + {#2}{#3}{#1}% +}% +\def\POL_fadd_D #1.#2#3% +{% + \expandafter\POL_fadd_E + \romannumeral0\xintiiquo{#3}{#1}.% + {\romannumeral0\xintiiquo{#2}{#1}}{#2}{#3}% +}% +\def\POL_fadd_E #1.#2#3#4#5% +{% + \expandafter\POL_fadd_F\romannumeral0\xintiimul{#1}{#3}.{#2}% + {\xintiiMul{#1}{#5}}% +}% +\def\POL_fadd_F #1.#2#3#4% +{% + \expandafter\POL_fadd_G + \romannumeral0\xintiiadd{#3}{\xintiiMul{#2}{#4}}/#1% +}% +\def\POL_fadd_G #1{% +\def\POL_fadd_G ##1{\if0##1\expandafter\XINT_fadd_iszero\fi#1##1}% +}\POL_fadd_G{ }% + +\POL@restorecatcodes +\endinput diff --git a/Master/tlpkg/bin/tlpkg-ctan-check b/Master/tlpkg/bin/tlpkg-ctan-check index 59200dcd795..c84f493946e 100755 --- a/Master/tlpkg/bin/tlpkg-ctan-check +++ b/Master/tlpkg/bin/tlpkg-ctan-check @@ -493,7 +493,7 @@ my @TLP_working = qw( plain-doc plainpkg plari plantslabels plates platex platex-tools platexcheat play playfair plex plex-otf plipsum plnfss plstmary plweb pm-isomath pmgraph pmx pmxchords pnas2009 - poemscol poetry poetrytex polski poltawski + poemscol poetry poetrytex polexpr polski poltawski polyglossia polynom polynomial polytable postcards poster-mac powerdot powerdot-FUBerlin ppr-prv pracjourn preprint prerex present presentations presentations-en diff --git a/Master/tlpkg/tlpsrc/collection-mathscience.tlpsrc b/Master/tlpkg/tlpsrc/collection-mathscience.tlpsrc index c43e710357a..b22c1aa86d0 100644 --- a/Master/tlpkg/tlpsrc/collection-mathscience.tlpsrc +++ b/Master/tlpkg/tlpsrc/collection-mathscience.tlpsrc @@ -117,6 +117,7 @@ depend oubraces depend perfectcut depend physics depend pm-isomath +depend polexpr depend prftree depend proba depend prooftrees diff --git a/Master/tlpkg/tlpsrc/polexpr.tlpsrc b/Master/tlpkg/tlpsrc/polexpr.tlpsrc new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/Master/tlpkg/tlpsrc/polexpr.tlpsrc |