2 * poly - calculate with polynomials of one variable
4 * Copyright (C) 1999 Ernest Bowen
6 * Calc is open software; you can redistribute it and/or modify it under
7 * the terms of the version 2.1 of the GNU Lesser General Public License
8 * as published by the Free Software Foundation.
10 * Calc is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
13 * Public License for more details.
15 * A copy of version 2.1 of the GNU Lesser General Public License is
16 * distributed with calc under the filename COPYING-LGPL. You should have
17 * received a copy with calc; if not, write to Free Software Foundation, Inc.
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * @(#) $Revision: 30.1 $
21 * @(#) $Id: poly.cal,v 30.1 2007/03/16 11:09:54 chongo Exp $
22 * @(#) $Source: /usr/local/src/bin/calc/cal/RCS/poly.cal,v $
24 * Under source code control: 1990/02/15 01:50:35
25 * File existed as early as: before 1990
27 * Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/
31 * A collection of functions designed for calculations involving
32 * polynomials in one variable (by Ernest W. Bowen).
34 * On starting the program the independent variable has identifier x
35 * and name "x", i.e. the user can refer to it as x, the
36 * computer displays it as "x". The name of the independent
37 * variable is stored as varname, so, for example, varname = "alpha"
38 * will change its name to "alpha". At any time, the independent
39 * variable has only one name. For some purposes, a name like
40 * "sin(t)" or "(a + b)" or "\lambda" might be useful;
41 * names like "*" or "-27" are legal but might give expressions
42 * that are difficult to intepret.
44 * Polynomial expressions may be constructed from numbers and the
45 * independent variable and other polynomials by the algebraic
46 * operations +, -, *, ^, and if the result is a polynomial /.
47 * The operations // and % are defined to have the quotient and
48 * remainder meanings as usually defined for polynomials.
50 * When polynomials are assigned to idenfifiers, it is convenient to
51 * think of the polynomials as values. For example, p = (x - 1)^2
52 * assigns to p a polynomial value in the same way as q = (7 - 1)^2
53 * would assign to q a number value. As with number expressions
54 * involving operations, the expression used to define the
55 * polynomial is usually lost; in the above example, the normal
56 * computer display for p will be x^2 - 2x + 1. Different
57 * identifiers may of course have the same polynomial value.
59 * The polynomial we think of as a_0 + a_1 * x + ... + a_n * x^n,
60 * for number coefficients a_0, a_1, ... a_n may also be
61 * constructed as pol(a_0, a_1, ..., a_n). Note that here the
62 * coefficients are to be in ascending power order. The independent
63 * variable is pol(0,1), so to use t, say, as an identifier for
64 * this, one may assign t = pol(0,1). To simultaneously specify
65 * an identifier and a name for the independent variable, there is
66 * the instruction var, used as in identifier = var(name). For
67 * example, to use "t" in the way "x" is initially, one may give
68 * the instruction t = var("t").
70 * There are four parameters pmode, order, iod and ims for controlling
71 * the format in which polynomials are displayed.
72 * The parameter pmode may have values "alg" or "list": the
73 * former gives a display as an algebraic formula, while
74 * the latter only lists the coefficients. Whether the terms or
75 * coefficients are in ascending or descending power order is
76 * controlled by order being "up" or "down". If the
77 * parameter iod (for integer-only display), the polynomial
78 * is expressed in terms of a polynomial whose coefficients are
79 * integers with gcd = 1, the leading coefficient having positive
80 * real part, with where necessary a leading multiplying integer,
81 * a Gaussian integer multiplier if the coefficients are complex
82 * with a common complex factor, and a trailing divisor integer.
83 * If a non-zero value is assigned to the parameter ims,
84 * multiplication signs will be inserted where appropriate;
85 * this may be useful if the expression is to be copied to a
86 * program or a string to be used with eval.
88 * For evaluation of polynomials the standard function is ev(p, t).
89 * If p is a polynomial and t anything for which the relevant
90 * operations can be performed, this returns the value of p
91 * at t. The function ev(p, t) also accepts lists or matrices
92 * as possible values for p; each element of p is then evaluated
93 * at t. For other p, t is ignored and the value of p is returned.
94 * If an identifier, a, say, is used for the polynomial, list or
95 * matrix p, the definition
96 * define a(t) = ev(a, t);
97 * permits a(t) to be used for the value of a at t as if the
98 * polynomial, list or matrix were a function. For example,
99 * if a = 1 + x^2, a(2) will return the value 5, just as if
100 * define a(t) = 1 + t^2;
101 * had been used. However, when the polynomial definition is
102 * used, changing the polynomial a will change a(t) to the value
103 * of the new polynomial at t. For example,
105 * L = list(x, x^2, x^3, x^4);
106 define a(t) = ev(a, t);
108 * for (i = 0; i < 4; i++)
109 * print ev(L[[i]], 5);
111 * for (i = 0; i < 4; i++) {
116 * Matrices with polynomial elements may be added, subtracted and
117 * multiplied as long as the usual rules for compatibility are
118 * observed. Also, matrices may be multiplied by polynomials,
119 * i.e. if p is a polynomial and A a matrix whose elements
120 * may be numbers or polynomials, p * A returns the matrix of
121 * the same shape as A with each element multiplied by p.
122 * Square matrices may also be 'substituted for the variable' in
123 * polynomials, e.g. if A is an m x m matrix, and
124 * p = x^2 + 3 * x + 2, ev(p, A) returns the same as
125 * A^2 + 3 * A + 2 * I, where I is the unit m x m matrix.
127 * On starting this program, three demonstration polynomials a, b, c
128 * have been defined. The functions a(t), b(t), c(t) corresponding
129 * to a, b, c, and x(t) corresponding to x, have also been
130 * defined, so the usual function notation can be used for
131 * evaluations of a, b, c and x. For x, as long as x identifies
132 * the independent variable, x(t) should return the value of t,
133 * i.e. it acts as an identity function.
135 * Functions defined include:
137 * monic(a) returns the monic multiple of a, i.e., if a != 0,
138 * the multiple of a with leading coefficient 1
139 * conj(a) returns the complex conjugate of a
140 * ispmult(a,b) returns 1 or 0 according as a is or is not
141 * a polynomial multiple of b
142 * pgcd(a,b) returns the monic gcd of a and b
143 * pfgcd(a,b) returns a list of three polynomials (g, u, v)
144 * where g = pgcd(a,b) and g = u * a + v * b.
145 * plcm(a,b) returns the monic lcm of a and b
147 * interp(X,Y,t) returns the value at t of the polynomial given
148 * by Newtonian divided difference interpolation, where
149 * X is a list of x-values, Y a list of corresponding
150 * y-values. If t is omitted, the interpolating
151 * polynomial is returned. A y-value may be replaced by
152 * list (y, y_1, y_2, ...), where y_1, y_2, ... are
153 * the reduced derivatives at the corresponding x;
154 * i.e. y_r is the r-th derivative divided by fact(r).
155 * mdet(A) returns the determinant of the square matrix A,
156 * computed by an algorithm that does not require
157 * inverses; the built-in det function usually fails
158 * for matrices with polynomial elements.
159 * D(a,n) returns the n-th derivative of a; if n is omitted,
160 * the first derivative is returned.
162 * A first-time user can see what the initially defined polynomials
163 * a, b and c are, and experiment with the algebraic operations
164 * and other functions that have been defined by giving
181 * mat A[2,2] = {1 + x, x^2, 3, 4*x}
185 * define A(t) = ev(A, t)
188 * define L(t) = ev(L, t)
189 * L = list(x, x^2, x^3, x^4)
192 * interp(list(0,1,2,3), list(2,3,5,7))
193 * interp(list(0,1,2), list(0,list(1,0),2))
195 * One check on some of the functions is provided by the Cayley-Hamilton
196 * theorem: if A is any m x m matrix and I the m x m unit matrix,
198 * ev(mdet(x * I - A), A)
199 * should return the zero m x m matrix.
209 for (i=1; i<= param(0); i++) append (s,param(i));
211 while (i>=0 && s[[i]]==0) {i--; remove(s)}
223 if (ispoly(a)) return a.p;
224 if (a) return list(a);
228 pmode = "alg"; /* The other acceptable pmode is "list" */
229 ims = 0; /* To be non-zero if multiplication signs to be inserted */
230 iod = 0; /* To be non-zero for integer-only display */
231 order = "down" /* Determines order in which coefficients displayed */
233 define poly_print(a) {
235 if (size(a.p) == 0) {
241 t = a.p[[size(a.p) - 1]] / g;
242 if (re(t) < 0) { t = -t; g = -g;}
245 if (im(t) > re(t)) g *= 1i;
246 else if (im(t) <= -re(t)) g *= -1i;
248 if (isreal(g)) f = g;
249 else f = gcd(re(g), im(g));
258 if (pmode == "alg") print"(":;
260 if (pmode == "alg") print")":;
261 if (den(f) > 1) print "/":den(f):;
268 define polyprint(a) {
277 for (i++ ; i <= n; i++) {
281 if (c > 0) print" + ":;
293 if (order == "down") {
295 for (i=n-1; i>=0; i--) {
299 if (c > 0) print" + ":;
311 quit "order to be up or down";
317 print pmode,:"is unknown mode";
325 for (i=0; i< size(s); i++) s[[i]] = -s[[i]];
330 define poly_conj(a) {
334 for (i=0; i < size(s); i++) s[[i]] = conj(s[[i]]);
339 define poly_inv(a) = pol(1)/a; /* This exists only for a of zero degree */
341 define poly_add(a,b) {
344 sa=findlist(a); sb=findlist(b);
345 if (size(sa) > size(sb)) swap(sa,sb);
346 for (i=0; i< size(sa); i++) sa[[i]] += sb[[i]];
347 while (i < size(sb)) append (sa, sb[[i++]]);
348 while (i > 0 && sa[[--i]]==0) remove (sa);
353 define poly_sub(a,b) {
357 define poly_cmp(a,b) {
364 define poly_mul(a,b) {
366 if (ismat(a)) swap(a,b);
369 for (i=matmin(b,1); i <= matmax(b,1); i++)
370 for (j = matmin(b,2); j<= matmax(b,2); j++)
375 sa=findlist(a); sb=findlist(b);
376 y.p = listmul(sa,sb);
380 define listmul(a,b) {
381 local da,db, s, i, j, u;
382 da=size(a)-1; db=size(b)-1;
384 if (da >= 0 && db >= 0) {
385 for (i=0; i<= da+db; i++) { u=0;
386 for (j = max(0,i-db); j <= min(i, da); j++)
387 u += a[[j]]*b[[i-j]]; append (s,u);}}
395 for (i = matmin(a,1); i <= matmax(a,1); i++)
396 for (j = matmin(a,2); j <= matmax(a,2); j++)
397 v[i,j] = ev(a[i,j], t);
402 for (i = 0; i < size(a); i++)
403 append(v, ev(a[[i]], t));
406 if (!ispoly(a)) return a;
409 for (i = 0; i < size(t); i++)
410 append(v, ev(a, t[[i]]));
413 if (ismat(t)) return evpm(a.p, t);
422 for (i = n - 2; i >= 0; i--) v=t * v +s[[i]];
429 m = matmax(t,1) - matmin(t,1);
430 if (matmax(t,2) - matmin(t,2) != m) quit "Non-square matrix";
436 for (i = n - 2; i >= 0; i--) V = t * V + s[[i]] * I;
442 define x(t) = ev(x, t);
450 define isstring(a) = istype(a, " ");
453 if (!isstring(name)) quit "Argument of var is to be a string";
459 if (isreal(a)) print a:;
460 else print "(":a:")":;
480 print varname:"^":n:;
488 for (i=0; i< n-1 ; i++)
490 if (n) print s[[i]],")":;
494 if (n) print s[[n-1]]:;
495 for (i = n - 2; i >= 0; i--)
501 define deg(a) = size(a.p) - 1;
503 define polydiv(a,b) {
504 local d, u, i, m, n, sa, sb, sq;
507 sa=findlist(a); sb = findlist(b); sq = list();
508 m=size(sa)-1; n=size(sb)-1;
509 if (n<0) quit "Zero divisor";
510 if (m<n) return list(pzero, a);
512 while ( m >= n) { u = sa[[m]]/d;
513 for (i = 0; i< n; i++) sa[[m-n+i]] -= u*sb[[i]];
514 push(sq,u); remove(sa); m--;
515 while (m>=n && sa[[m]]==0) { m--; remove(sa); push(sq,0)}}
516 while (m>=0 && sa[[m]]==0) { m--; remove(sa);}
520 define poly_mod(a,b) {
526 define poly_quo(a,b) {
532 define ispmult(a,b) = iszero(a % b);
534 define poly_div(a,b) {
535 if (!ispmult(a,b)) quit "Result not a polynomial";
536 return poly_quo(a,b);
541 if (iszero(a) && iszero(b)) return pzero;
550 define plcm(a,b) = monic( a * b // pgcd(a,b));
553 local u, v, u1, v1, s, q, r, d, w;
554 u = v1 = pol(1); v = u1 = pol(0);
555 while (size(b.p) > 0) {s = polydiv(a,b);
557 a = b; b = s[[1]]; u -= q*u1; v -= -q*v1;
558 swap(u,u1); swap(v,v1);}
559 d=size(a.p)-1; if (d>=0 && (w= 1/a.p[[d]]) !=1)
560 { a *= w; u *= w; v *= w;}
566 if (iszero(a)) return pzero;
570 for (i=0; i<=d; i++) s[[i]] /= s[[d]];
575 define coefficient(a,n) = (n < size(a.p)) ? a.p[[n]] : 0;
579 if (isnull(n)) n = 1;
580 if (!isint(n) || n < 1) quit "Bad order for derivative";
583 for (i = matmin(a,1); i <= matmax(a,1); i++)
584 for (j = matmin(a,2); j <= matmax(a,2); j++)
585 v[i,j] = D(a[i,j], n);
588 if (!ispoly(a)) return 0;
594 if (n > 1) return Dp(Dp(a, n-1), 1);
597 for (i=1; i<size(a.p); i++) append (v.p, i*a.p[[i]]);
603 if (isreal(a) && isreal(b)) return gcd(a,b);
605 b -= bround(b/a) * a;
608 if (re(b) < 0) b = -b;
609 if (im(b) > re(b)) b *= -1i;
610 else if (im(b) <= -re(b)) b *= 1i;
614 define gcdcoeffs(a) {
618 for (i=0; i < size(s) && g != 1; i++)
619 if (c = s[[i]]) g = cgcd(g, c);
623 define interp(X, Y, t) = evalfd(makediffs(X,Y), t);
625 define makediffs(X,Y) {
626 local U, D, d, x, y, i, j, k, m, n, s;
629 if (size(Y) != n) quit"Arguments to be lists of same size";
630 for (i = n-1; i >= 0; i--) {
636 for (j = 0; j < m; j++) {
637 d = D[[j]] = (D[[j]]-d)/(U[[j]] - x);
644 for (k = 0; k < s ; k++) {
646 for (j = 0; j < m; j++) {
647 d = D[[j]] = (D[[j]] - d)/(U[[j]] - x);
650 for (j=s-1; j >=0; j--) {
659 define evalfd(T, t) {
661 if (isnull(t)) t = pol(0,1);
666 for (i = n-2; i >= 0; i--)
667 v = v * (t - U[[i]]) + D[[i]];
673 local n, i, j, k, I, J;
674 n = matmax(A,1) - (i = matmin(A,1));
675 if (matmax(A,2) - (j = matmin(A,2)) != n)
676 quit "Non-square matrix for mdet";
683 return M(A, n+1, I, J);
686 define M(A, n, I, J) {
687 local v, J0, i, j, j1;
688 if (n == 1) return A[ I[[0]], J[[0]] ];
691 for (j = 0; j < n; j++) {
694 v += (-1)^(n-1+j) * A[i, j1] * M(A, n-1, I, J0);
701 if (!ismat(A)) quit "Argument to be a matrix";
702 for (i = matmin(A,1); i <= matmax(A,1); i++) {
703 for (j = matmin(A,2); j <= matmax(A,2); j++)
704 printf("%8.4d ", A[i,j]);
713 define a(t) = ev(a,t);
714 define b(t) = ev(b,t);
715 define c(t) = ev(c,t);
719 c=pol(1+2i,3+4i,5+6i);
721 if (config("resource_debug") & 3) {
722 print "obj poly {p} defined";