1 ;;; -*- Mode: Lisp; Package: Maxima; Syntax: Common-Lisp; Base: 10 -*- ;;;;
2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;;; The data in this file contains enhancements. ;;;;;
5 ;;; Copyright (c) 1984,1987 by William Schelter,University of Texas ;;;;;
6 ;;; All rights reserved ;;;;;
7 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
8 ;;; (c) Copyright 1980 Massachusetts Institute of Technology ;;;
9 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
13 (macsyma-module rat3a
)
15 ;; This is the new Rational Function Package Part 1.
16 ;; It includes most of the coefficient and polynomial routines required
17 ;; by the rest of the functions. Certain specialized functions are found
18 ;; elsewhere. Most low-level utility programs are also in this file.
20 (declare-top (unspecial coef var exp p y
))
22 ;;These do not need to be special for this file and they
23 ;;slow it down on lispm. We also eliminated the special
28 ;; Calculate the quotient of two coefficients, which should be numbers. If
29 ;; MODULUS is non-nil, we try to take the reciprocal of A with respect to the
30 ;; modulus (using CRECIP) and then multiply by B. Note that this fails if B
31 ;; divides A as an integer, but B is not a unit in the ring of integers modulo
32 ;; MODULUS. For example,
34 ;; (let ((modulus 20)) (cquotient 10 5)) => ERROR
36 ;; If MODULUS is nil, then we work over the ring of integers when A and B are
37 ;; integers, and raise a RAT-ERROR if A is not divisible by B. If either A or B
38 ;; is a float then the division is done in floating point. Floats can get as far
39 ;; as the rational function code if $KEEPFLOAT is true.
41 ;; Before May 2023, this code used (rem a b) along with (/ a b) instead of
42 ;; just (floor a b). For Clozure CL the floor method is faster.
43 (defun cquotient (a b
)
44 (cond ((eql a
0) 0) ;not sure this is OK--what if b=0 too?
46 (cond ((or (floatp a
) (floatp b
)) (/ a b
)) ;not sure about floats here!
48 (multiple-value-bind (q r
) (floor a b
)
49 ;; when the remainder vanishes, return the quotient; else rat-error.
50 (if (eql 0 r
) q
(rat-error "CQUOTIENT: quotient is not exact"))))))
52 (ctimes a
(crecip b
)))))
55 ;; Get any value stored on the tellrat property of (car l). Returns NIL if L
56 ;; turns out not to be a list or if $ALGEBRAIC is false.
58 (unless (atom l
) (algv (car l
))))
62 ;; Return T if either X is a bare coefficient or X is a polynomial whose main
63 ;; variable has a declared value as an algebraic integer. Otherwise return NIL.
65 (and (or (pcoefp x
) (alg x
))
70 ;; Return the leading term of POLY as a polynomial itself.
71 (defun leadterm (poly)
74 (make-poly (p-var poly
) (p-le poly
) (p-lc poly
))))
78 ;; Takes the inverse of an integer N mod MODULUS. If there is a modulus then the
79 ;; result is constrained to lie in (-modulus/2, modulus/2]
81 ;; This just uses the extended Euclidean algorithm: once you have found a,b such
82 ;; that a*n + b*modulus = 1 then a must be the reciprocal you're after.
84 ;; When MODULUS is greater than 2^15, we use exactly the same algorithm in
85 ;; CRECIP-GENERAL, but it can't use fixnum arithmetic. Note: There's no
86 ;; particular reason to target 32 bits except that trying to work out the right
87 ;; types on the fly looks complicated and this lisp compiler, at least, uses 32
88 ;; bit words. Since we have to take a product, we constrain the types to 16 bit
91 ;; Punt on anything complicated
92 (unless (and modulus
(typep modulus
'(unsigned-byte 15)))
93 (return-from crecip
(crecip-general n
)))
95 ;; And make sure that -MODULUS < N < MODULUS
96 (unless (<= (- modulus
) n modulus
)
97 (merror "N is out of range [-MODULUS, MODULUS] in crecip."))
100 (when (minusp n
) (setf n
(+ n modulus
)))
102 ;; The mod-copy parameter stores a copy of MODULUS on the stack, which is
103 ;; useful because the lisp implementation doesn't know that the special
104 ;; variable MODULUS is still an (unsigned-byte 15) when we get to the end
105 ;; (since it can't tell that our function calls don't change it behind our
107 (let ((mod modulus
) (remainder n
) (a 1) (b 0)
109 ;; On SBCL in 2013 at least, the compiler doesn't spot that MOD and
110 ;; REMAINDER are unsigned and bounded above by MODULUS, a 16-bit integer. So
111 ;; we have to tell it. Also, the lisp implementation can't really be
112 ;; expected to know that Bezout coefficients are bounded by the modulus and
113 ;; remainder, so we have to tell it that too.
114 (declare (type (unsigned-byte 15) mod mod-copy remainder
)
115 (type (signed-byte 16) a b
))
118 until
(= remainder
1)
120 when
(zerop remainder
) do
121 (merror (intl:gettext
"CRECIP: ~M does not have an inverse with modulus=~M")
124 (multiple-value-bind (quot rem
)
125 (truncate mod remainder
)
128 (psetf a
(- b
(* a quot
))
132 ;; Since this isn't some general purpose Euclidean algorithm, but
133 ;; instead is trying to find a modulo inverse, we need to ensure that
134 ;; the Bezout coefficient we found (called A) is actually in [0,
137 ;; The general code calls CMOD here, but that doesn't know about the
138 ;; types of A and MODULUS, so we do it by hand, special-casing the easy
139 ;; case of modulus=2.
143 (let ((nn (mod a mod-copy
)))
144 ;; nn here is in [0, modulus)
145 (if (<= (* 2 nn
) mod-copy
)
147 (- nn mod-copy
))))))))
151 ;; The general algorithm for CRECIP, valid when the modulus is any integer. See
152 ;; CRECIP for more details.
153 (defun crecip-general (n)
154 ;; We assume that |n| < modulus, so n+modulus is always positive
156 (remainder (if (minusp n
) (+ n modulus
) n
))
159 until
(= remainder
1)
161 when
(zerop remainder
) do
162 (merror (intl:gettext
"CRECIP: ~M does not have an inverse with modulus=~M")
165 (let ((quotient (truncate mod remainder
)))
167 remainder
(- mod
(* quotient remainder
)))
168 (psetf a
(- b
(* a quotient
))
171 finally
(return (cmod a
)))))
175 ;; Raise an coefficient to a positive integral power. BASE should be a
176 ;; number. POW should be a non-negative integer.
177 (defun cexpt (base pow
)
178 (unless (typep pow
'(integer 0))
179 (error "CEXPT only defined for non-negative integral exponents."))
182 (do ((pow (ash pow -
1) (ash pow -
1))
183 (s (if (oddp pow
) base
1)))
185 (setq base
(rem (* base base
) modulus
))
186 (when (oddp pow
) (setq s
(rem (* s base
) modulus
))))))
190 ;; When MODULUS is null, this is the identity. Otherwise, it normalises N, which
191 ;; should be a number, to lie in the range (-modulus/2, modulus/2].
193 (declare (type number n
))
196 (let ((rem (mod n modulus
)))
197 (if (<= (* 2 rem
) modulus
)
201 (defun cplus (a b
) (cmod (+ a b
)))
202 (defun ctimes (a b
) (cmod (* a b
)))
203 (defun cdifference (a b
) (cmod (- a b
)))
207 ;; Set the base in which the rational function package works. This does
208 ;; sanity-checking on the value chosen and is probably the way you should set
211 ;; Valid values for M are either a positive integer or NULL.
212 (defun set-modulus (m)
213 (if (or (null m
) (typep m
'(integer 1)))
215 (error "modulus must be a positive integer or nil"))
220 ;; Prepend a term to an existing polynomial. EXPONENT should be the exponent of
221 ;; the term to add; COEFF should be its coefficient; REMAINDER is a list of
222 ;; polynomial terms. The function returns polynomial terms that correspond to
223 ;; adding the given term.
225 ;; The function doesn't check that EXPONENT is higher than the highest exponent
226 ;; in REMAINDER, so you have to do this yourself.
227 (defun pcoefadd (exponent coeff remainder
)
230 (cons exponent
(cons coeff remainder
))))
234 ;; Add together two polynomials.
236 (cond ((pcoefp x
) (pcplus x y
))
237 ((pcoefp y
) (pcplus y x
))
238 ((eq (p-var x
) (p-var y
))
239 (psimp (p-var x
) (ptptplus (p-terms y
) (p-terms x
))))
240 ((pointergp (p-var x
) (p-var y
))
241 (psimp (p-var x
) (ptcplus y
(p-terms x
))))
242 (t (psimp (p-var y
) (ptcplus x
(p-terms y
))))))
246 ;; Add together two lists of polynomial terms.
247 (defun ptptplus (x y
)
248 (cond ((ptzerop x
) y
)
250 ((= (pt-le x
) (pt-le y
))
252 (pplus (pt-lc x
) (pt-lc y
))
253 (ptptplus (pt-red x
) (pt-red y
))))
254 ((> (pt-le x
) (pt-le y
))
255 (cons (pt-le x
) (cons (pt-lc x
) (ptptplus (pt-red x
) y
))))
256 (t (cons (pt-le y
) (cons (pt-lc y
) (ptptplus x
(pt-red y
)))))))
260 ;; Add a coefficient to a polynomial
265 (ptcplus c
(p-terms p
)))))
269 ;; Add a coefficient to a list of terms. C should be a used as a coefficient;
270 ;; TERMS is a list of a polynomial's terms. Note that we don't assume that C is
271 ;; a number: it might be a polynomial in a variable that isn't the main variable
272 ;; of the polynomial.
273 (defun ptcplus (c terms
)
275 ;; Adding zero doesn't do anything.
277 ;; Adding to zero, you just get the coefficient.
278 ((null terms
) (list 0 c
))
279 ;; If terms are from a constant polynomial, we can just add C to its leading
280 ;; coefficient (which might not be a number in the multivariate case, so you
281 ;; have to use PPLUS)
282 ((zerop (pt-le terms
))
283 (pcoefadd 0 (pplus c
(pt-lc terms
)) nil
))
284 ;; If TERMS is a polynomial with degree > 0, recurse.
286 (cons (pt-le terms
) (cons (pt-lc terms
) (ptcplus c
(pt-red terms
)))))))
290 ;; Compute the difference of two polynomials
291 (defun pdifference (x y
)
293 ;; If Y is a coefficient, it's a number, so we can just add -Y to X using
294 ;; pcplus. If, however, X is the coefficient, we have to negate all the
295 ;; coefficients in Y, so defer to a utility function.
296 ((pcoefp x
) (pcdiffer x y
))
297 ((pcoefp y
) (pcplus (cminus y
) x
))
298 ;; If X and Y have the same variable, work down their lists of terms.
299 ((eq (p-var x
) (p-var y
))
300 (psimp (p-var x
) (ptptdiffer (p-terms x
) (p-terms y
))))
301 ;; Treat Y as a coefficient in the main variable of X.
302 ((pointergp (p-var x
) (p-var y
))
303 (psimp (p-var x
) (ptcdiffer-minus (p-terms x
) y
)))
304 ;; Treat X as a coefficient in the main variable of Y.
305 (t (psimp (p-var y
) (ptcdiffer x
(p-terms y
))))))
309 ;; Compute the difference of two lists of polynomial terms (assumed to represent
310 ;; two polynomials in the same variable).
311 (defun ptptdiffer (x y
)
313 ((ptzerop x
) (ptminus y
))
315 ((= (pt-le x
) (pt-le y
))
317 (pdifference (pt-lc x
) (pt-lc y
))
318 (ptptdiffer (pt-red x
) (pt-red y
))))
319 ((> (pt-le x
) (pt-le y
))
320 (cons (pt-le x
) (cons (pt-lc x
) (ptptdiffer (pt-red x
) y
))))
321 (t (cons (pt-le y
) (cons (pminus (pt-lc y
))
322 (ptptdiffer x
(pt-red y
)))))))
325 ;; Subtract the polynomial P from the coefficient C to form c - p.
326 (defun pcdiffer (c p
)
329 (psimp (p-var p
) (ptcdiffer c
(p-terms p
)))))
333 ;; Subtract a polynomial represented by the list of terms, TERMS, from the
335 (defun ptcdiffer (c terms
)
337 ;; Unlike in the plus case or in PTCDIFFER-MINUS, we don't have a shortcut
338 ;; if C=0. However, if TERMS is null then we are calculating C-0, which is
341 (if (pzerop c
) nil
(list 0 c
)))
342 ;; If the leading exponent is zero (in the main variable), then we can
343 ;; subtract the coefficients. Of course, these might actually be polynomials
344 ;; in other variables, so do this using pdifference.
345 ((zerop (pt-le terms
))
346 (pcoefadd 0 (pdifference c
(pt-lc terms
)) nil
))
347 ;; Otherwise we have to negate the leading coefficient (using pminus of
348 ;; course, because it might be a polynomial in other variables) and recurse.
351 (cons (pminus (pt-lc terms
)) (ptcdiffer c
(pt-red terms
)))))))
355 ;; Subtract a coefficient, C, from a polynomial represented by a list of terms,
356 ;; TERMS, to form "p-c". This is the same as PTCDIFFER but the opposite sign (we
357 ;; don't implement it by (pminus (ptcdiffer c terms)) because that would require
358 ;; walking the polynomial twice)
359 (defun ptcdiffer-minus (terms c
)
361 ;; We're subtracting zero from a polynomial, which is easy!
363 ;; We're subtracting a coefficient from zero, which just comes out as the
364 ;; negation of the coefficient (compute it using pminus)
365 ((null terms
) (list 0 (pminus c
)))
366 ;; If the leading exponent is zero, subtract the coefficients just like in
368 ((zerop (pt-le terms
))
369 (pcoefadd 0 (pdifference (pt-lc terms
) c
) nil
))
370 ;; Otherwise recurse.
373 (cons (pt-lc terms
) (ptcdiffer-minus (pt-red terms
) c
))))))
377 ;; Substitute values for variables in the polynomial P. VARS and VALS should be
378 ;; list of variables to substitute for and values to substitute, respectively.
380 ;; The code assumes that if VAR1 precedes VAR2 in the list then (POINTERGP VAR1
381 ;; VAR2). As such, VAR1 won't appear in the coefficients of a polynomial whose
382 ;; main variable is VAR2.
383 (defun pcsub (p vals vars
)
385 ;; Nothing to substitute, or P has no variables in it.
386 ((or (null vals
) (pcoefp p
)) p
)
387 ;; The first variable in our list is the main variable of P.
388 ((eq (p-var p
) (first vars
))
389 (ptcsub (p-terms p
) (first vals
)
390 (cdr vals
) (cdr vars
)))
391 ;; If the first var should appear before the main variable of P, we know it
392 ;; doesn't appear in any of the coefficients, so can (tail-)recurse on vals
394 ((pointergp (car vars
) (p-var p
))
395 (pcsub p
(cdr vals
) (cdr vars
)))
396 ;; Else, the main variable shouldn't get clobbered, but maybe we should
397 ;; replace variables in the coefficients.
398 (t (psimp (p-var p
) (ptcsub-args (p-terms p
) vals vars
)))))
402 ;; Substitute VAL for VAR in a polynomial. Like PCSUB, but with only a single
403 ;; var to be substituted.
405 ;; (The logic of this function is exactly the same as PCSUB, but is marginally
406 ;; simpler because there are no more vars afterwards. Presumably, it was
407 ;; thought worth separating this case out from PCSUB to avoid spurious
408 ;; consing. I'm not convinced. RJS)
409 (defun pcsubst (p val var
)
411 ((eq (p-var p
) var
) (ptcsub (cdr p
) val nil nil
))
412 ((pointergp var
(p-var p
)) p
)
413 (t (psimp (car p
) (ptcsub-args (cdr p
) (list val
) (list var
))))))
417 ;; Substitute a constant, VAL, for the main variable in TERMS, which represent
418 ;; the terms of a polynomial. The coefficients might themselves be polynomials
419 ;; and, if so, we might substitute values for them too. To do so, pass VALS and
420 ;; VARS, with the same ordering requirements as in PCSUB.
421 (defun ptcsub (terms val vals vars
)
423 ;; If we're substituting 0 for the value, then we just extract the
425 (pcsub (ptterm terms
0) vals vars
)
426 ;; Otherwise, walk through the polynomial using Horner's scheme to
427 ;; evaluate it. Because the polynomial is sparse, you can't just multiply
428 ;; by VAL every step, and instead have to keep track of the jump in
429 ;; exponents, which is what the LAST-LE variable does.
430 (do ((terms (pt-red terms
) (pt-red terms
))
431 (ans (pcsub (pt-lc terms
) vals vars
)
432 (pplus (ptimes ans
(pexpt val
(- last-le
(pt-le terms
))))
433 (pcsub (pt-lc terms
) vals vars
)))
434 (last-le (pt-le terms
) (pt-le terms
)))
436 (ptimes ans
(pexpt val last-le
))))))
440 ;; Substitute values for vars in TERMS, which should be the terms of some
441 ;; polynomial. Unlike PTCSUB, we assume that the main variable of the polynomial
442 ;; isn't being substituted. VARS and VALS should be ordered as in PCSUB.
443 (defun ptcsub-args (terms vals vars
)
445 for
(exp coef
) on terms by
#'cddr
446 unless
(pzerop (setq coef
(pcsub coef vals vars
)))
447 nconc
(list exp coef
)))
451 ;; Like PCSUB, but with arguments in a different order and with a special case
452 ;; that you can pass atoms for VALS and VARS, in which case they will be treated
453 ;; as one-element lists. The big difference with PCSUB is that we don't assume
454 ;; that VARS and VALS come pre-sorted, and sort them here.
455 (defun pcsubsty (vals vars p
)
457 ;; If there is nothing to do, the answer is just P.
459 ;; When there's only one variable, we don't need to do any sorting, so skip
460 ;; it and call PCSUB directly.
461 ((atom vars
) (pcsub p
(list vals
) (list vars
)))
462 ;; Otherwise, call PCSUB with a sorted list of VARS and VALS.
464 (let ((pairs (sort (mapcar #'cons vars vals
) #'pointergp
:key
#'car
)))
465 (pcsub p
(mapcar #'cdr pairs
) (mapcar #'car pairs
))))))
469 ;; Compute the derivative of the polynomial P with respect to the variable VARI.
470 (defun pderivative (p vari
)
472 ;; The derivative of a constant is zero.
474 ;; If we have the same variable, do the differentiation term-by-term.
476 (psimp (p-var p
) (ptderivative (p-terms p
))))
477 ;; If VARI > (P-VAR P) then we know it doesn't occur in any of the
478 ;; coefficients either, so return zero. This test comes after the one above
479 ;; because we expect more univariate polynomials and eq is cheaper than
481 ((pointergp vari
(p-var p
)) 0)
482 ;; The other possibility is that (P-VAR P) > VARI, so the coefficients might
483 ;; need differentiating.
485 (psimp (p-var p
) (ptderivative-coeffs (p-terms p
) vari
)))))
489 ;; Formally differentiate TERMS, which is a list of the terms of some
490 ;; polynomial, with respect to that polynomial's main variable.
491 (defun ptderivative (terms)
492 (if (or (null terms
) (zerop (pt-le terms
)))
493 ;; Zero or constant polynomials -> 0
495 ;; Recurse, adding up "k . x^(k-1)" each time.
496 (pcoefadd (1- (pt-le terms
))
497 (pctimes (cmod (pt-le terms
)) (pt-lc terms
))
498 (ptderivative (pt-red terms
)))))
500 ;; PTDERIVATIVE-COEFFS
502 ;; Differentiate TERMS, which is a list of the terms of some polynomial, with
503 ;; respect to the variable VARI. We assume that VARI is not the main variable of
504 ;; the polynomial, but it might crop up in the coefficients.
505 (defun ptderivative-coeffs (terms vari
)
507 ;; Recurse down the list of terms, calling PDERIVATIVE to actually
508 ;; differentiate each coefficient, then PTDERIVATIVE-COEFFS to do the rest.
509 (pcoefadd (pt-le terms
)
510 (pderivative (pt-lc terms
) vari
)
511 (ptderivative-coeffs (pt-red terms
) vari
))))
515 ;; Polynomial division with remainder. X and Y should be polynomials. If V
516 ;; denotes the main variable of X, then we are carrying out the division in a
517 ;; ring of polynomials over Q where all variables that occur after V have been
518 ;; formally inverted. This is a Euclidean ring, and PDIVIDE implements division
519 ;; with remainder in this ring.
521 ;; The result is a list of two elements (Q R). Each is a rational function (a
522 ;; cons pair of polynomials), representing an element of F[V].
525 ((pzerop y
) (rat-error "PDIVIDE: Quotient by zero"))
526 ;; If Y is a coefficient, it doesn't matter what X is: we can always do the
528 ((pacoefp y
) (list (ratreduce x y
) (rzero)))
529 ;; If X is a coefficient but Y isn't then the quotient must be zero
530 ((pacoefp x
) (list (rzero) (cons x
1)))
531 ;; If neither is a coefficient then compare the variables. If V is greater
532 ;; than the main variable of Y, then Y is invertible in F[V].
533 ((pointergp (p-var x
) (p-var y
)) (list (ratreduce x y
) (rzero)))
534 ;; If we've got to here, V might occur in the coefficients of Y, but it
535 ;; needn't be the main variable.
537 (do* ((lcy (cons (p-lc y
) 1))
540 (k (- (pdegree x
(p-var y
)) (p-le y
))
541 (- (pdegree (car r
) (p-var y
)) (p-le y
))))
543 ;; k is the degree of the numerator of the remainder minus the degree
544 ;; of y, both in the leading variable of y. For there to be further
545 ;; factors of y to subtract from q, this must be non-negative.
546 ((minusp k
) (list q r
))
548 ;; Divide the leading coefficient of r (which means the leading term of
549 ;; the numerator, divided by the denominator) by the leading coefficient
552 ;; The quotient gets added to q and gets multiplied back up by y and the
553 ;; result is subtracted from r.
554 (let* ((lcr (cons (p-lc (car r
)) (cdr r
)))
555 (quot (ratquotient lcr lcy
))
556 (quot-simp (cons (psimp (p-var y
) (list k
(car quot
)))
558 (setf q
(ratplus q quot-simp
)
559 r
(ratplus r
(rattimes (cons (pminus y
) 1) quot-simp t
))))))))
563 ;; Polynomial exponentiation. Raise the polynomial P to the power N (which
564 ;; should be an integer)
571 ((minusp n
) (pquotient 1 (pexpt p
(- n
))))
572 ;; When p is a coefficient, we can the simpler cexpt (which expects n >= 0,
573 ;; guaranteed by the previous clause)
574 ((pcoefp p
) (cexpt p n
))
575 ;; If the main variable of P is an algebraic integer, calculate the power by
576 ;; repeated squaring (which will correctly take the remainder wrt the
577 ;; minimal polynomial for the variable)
578 ((alg p
) (pexptsq p n
))
579 ;; If p is a monomial in the main variable, we're doing something like
580 ;; (x^2(y+1))^n, which is x^2n (y+1)^n, exponentiate the coefficient by
581 ;; recursion and just multiply the exponent. The call to PCOEFADD is just to
582 ;; ensure that we get zero if the coefficient raises to the power
583 ;; zero. (Possible when the coefficient is an algebraic integer)
586 (pcoefadd (* n
(p-le p
)) (pexpt (p-lc p
) n
) nil
)))
587 ;; In the general case, expand using the binomial theorem. Write the
590 ;; (b + rest)^n = sum (binomial (n,k) * rest^k * b^(n-k), k, 0, n)
592 ;; We pre-compute a list of descending powers of B and use the formula
594 ;; binomial(n,k)/binomial(n,k-1) = (n+1-k) / k
596 ;; to keep track of the binomial coefficient.
598 (let ((descending-powers (p-descending-powers
599 (make-poly (p-var p
) (p-le p
) (p-lc p
)) n
))
600 (rest (psimp (p-var p
) (p-red p
))))
601 (do* ((b-list descending-powers
(rest b-list
))
603 (n-choose-k 1 (truncate (* n-choose-k
(- (1+ n
) k
)) k
))
607 (t (ptimes rest rest-pow
))))
608 (sum (first descending-powers
)
611 (ptimes (pctimes (cmod n-choose-k
) rest-pow
)
613 (pctimes (cmod n-choose-k
) rest-pow
)))))
616 ;; P-DESCENDING-POWERS
618 ;; Return a list of the powers of the polynomial P in descending order, starting
619 ;; with P^N and ending with P.
620 (defun p-descending-powers (p n
)
621 (let ((lst (list p
)))
622 (dotimes (i (1- n
)) (push (ptimes p
(car lst
)) lst
))
627 ;; Returns true if the coefficient of the leading monomial of the polynomial is
628 ;; negative. Note that this depends on the variable ordering (for example,
631 ;; (pminusp '(y 1 -1 0 (x 1 1))) => T but
632 ;; (pminusp '(x 1 1 0 (y 1 -1))) => NIL
634 (if (realp p
) (minusp p
)
639 ;; Unary negation for polynomials.
641 (if (pcoefp p
) (cminus p
)
642 (cons (p-var p
) (ptminus (p-terms p
)))))
646 ;; Negate a list of polynomial terms.
648 (loop for
(exp coef
) on x by
#'cddr
649 nconc
(list exp
(pminus coef
))))
653 ;; Reduce a polynomial modulo the current value of MODULUS.
655 (if (pcoefp p
) (cmod p
)
657 (loop for
(exp coef
) on
(p-terms p
) by
#'cddr
658 unless
(pzerop (setq coef
(pmod coef
)))
659 nconc
(list exp coef
)))))
663 ;; Calculate x/y in the polynomial ring over the integers. Y should divide X
664 ;; without remainder.
665 (defun pquotient (x y
)
667 (cond ((pzerop x
) (pzero))
668 ((pcoefp y
) (cquotient x y
))
669 ((alg y
) (paquo x y
))
670 (t (rat-error "PQUOTIENT: Quotient by a polynomial of higher degree (case 1)"))))
673 (cond ((pzerop y
) (rat-error "PQUOTIENT: Quotient by zero"))
674 (modulus (pctimes (crecip y
) x
))
675 (t (pcquotient x y
))))
677 ;; If (alg y) is true, then y is a polynomial in some variable that
678 ;; itself has a minimum polynomial. Moreover, the $algebraic flag must
679 ;; be true. We first try to compute an exact quotient ignoring that
680 ;; minimal polynomial, by binding $algebraic to nil. If that fails, we
681 ;; try to invert y and then multiply the results together.
682 ((alg y
) (or (let ($algebraic
)
683 (ignore-rat-err (pquotient x y
)))
684 (patimes x
(rainv y
))))
686 ;; If the main variable of Y comes after the main variable of X, Y must
687 ;; be free of that variable, so must divide each coefficient in X. Thus
688 ;; we can use PCQUOTIENT.
689 ((pointergp (p-var x
) (p-var y
)) (pcquotient x y
))
691 ;; Either Y contains a variable that is not in X, or they have the same
692 ;; main variable and Y has a higher degree. There can't possibly be an
694 ((pointergp (p-var y
) (p-var x
))
695 (rat-error "PQUOTIENT: Quotient by a polynomial of higher degree (case 2a)"))
696 ((> (p-le y
) (p-le x
))
697 (rat-error "PQUOTIENT: Quotient by a polynomial of higher degree (case 2b)"))
699 ;; If we got to here then X and Y have the same main variable and Y has
700 ;; a degree less than or equal to that of X. We can now forget about the
701 ;; main variable and work on the terms, with PTPTQUOTIENT.
703 (psimp (p-var x
) (ptptquotient (p-terms x
) (p-terms y
))))))
707 ;; Divide the polynomial P by Q. Q should be either a coefficient (so that
708 ;; (pcoefp q) => T), or should be a polynomial in a later variable than the main
709 ;; variable of P. Either way, Q is free of the main variable of P. The division
710 ;; is done at each coefficient.
711 (defun pcquotient (p q
)
714 for
(exp coef
) on
(p-terms p
) by
#'cddr
715 nconc
(list exp
(pquotient coef q
)))))
719 ;; Exactly divide two polynomials in the same variable, represented here by the
720 ;; list of their terms.
721 (defun ptptquotient (u v
)
722 ;; The algorithm is classic long division. You notice that if X/Y = Q then X =
723 ;; QY, so lc(X) = lc(Q)lc(Y) (where lc(Q)=Q when Q is a bare coefficient). Now
724 ;; divide again in the ring of coefficients to see that lc(X)/lc(Y) =
725 ;; lc(Q). Of course, you also know that le(Q) = le(X) - le(Y).
727 ;; Once you know lc(Q), you can subtract Y * lc(Q)*(var^le(Q)) from X and
728 ;; repeat. You know that you'll remove the leading term, so the algorithm will
729 ;; always terminate. To do the subtraction, use PTPT-SUBTRACT-POWERED-PRODUCT.
731 (u u
(ptpt-subtract-powered-product (pt-red u
) (pt-red v
)
732 (first q-terms
) (second q-terms
))))
735 ;; If B didn't divide A after all, then eventually we'll end up with the
736 ;; remainder in u, which has lower degree than that of B.
737 (when (< (pt-le u
) (pt-le v
))
738 (rat-error "PTPTQUOTIENT: Polynomial quotient is not exact"))
739 (let ((le-q (- (pt-le u
) (pt-le v
)))
740 (lc-q (pquotient (pt-lc u
) (pt-lc v
))))
741 ;; We've calculated the leading exponent and coefficient of q. Push them
742 ;; backwards onto q-terms (which holds the terms in reverse order).
743 (setf q-terms
(cons lc-q
(cons le-q q-terms
))))))
745 ;; PTPT-SUBTRACT-POWERED-PRODUCT
747 ;; U and V are the terms of two polynomials, A and B, in the same variable, x. Q
748 ;; is free of x. This function computes the terms of A - x^k * B * Q. This
749 ;; rather specialised function is used to update a numerator when doing
750 ;; polynomial long division.
751 (defun ptpt-subtract-powered-product (u v q k
)
753 ;; A - x^k * 0 * Q = A
755 ;; 0 - x^k * B * Q = x^k * B * (- Q)
756 ((null u
) (pcetimes1 v k
(pminus q
)))
758 ;; hipow is the highest exponent in x^k*B*Q.
759 (let ((hipow (+ (pt-le v
) k
)))
761 ;; If hipow is greater than the highest exponent in A, we have to
762 ;; prepend the first coefficient, which will be Q * lc(B). We can then
763 ;; recurse to this function to sort out the rest of the sum.
766 (ptimes q
(pminus (pt-lc v
)))
767 (ptpt-subtract-powered-product u
(pt-red v
) q k
)))
768 ;; If hipow is equal to the highest exponent in A, we can just subtract
769 ;; the two leading coefficients and recurse to sort out the rest.
772 (pdifference (pt-lc u
) (ptimes q
(pt-lc v
)))
773 (ptpt-subtract-powered-product (pt-red u
) (pt-red v
) q k
)))
774 ;; If hipow is lower than the highest exponent in A then keep the first
775 ;; term of A and recurse.
777 (list* (pt-le u
) (pt-lc u
)
778 (ptpt-subtract-powered-product (pt-red u
) v q k
))))))))
781 (and $algebraic
(get var
'algord
)))
785 ;; Return a "simplified" polynomial whose main variable is VAR and whose terms
788 ;; If the polynomial is free of X, the result is the zero'th order coefficient:
789 ;; either a polynomial in later variables or a number. PSIMP also deals with
790 ;; reordering variables when $ALGEBRAIC is true, behaviour which is triggered by
791 ;; the ALGORD property on the main variable.
793 (cond ((ptzerop x
) 0)
795 ((zerop (pt-le x
)) (pt-lc x
))
797 ;; Fix wrong alg ordering: We deal with the case that the main variable
798 ;; of a coefficient should precede VAR.
799 (do ((p x
(cddr p
)) (sum 0))
803 (pplus sum
(p-delete-zeros var x
))))
804 ;; We only need to worry about the wrong ordering if a coefficient is
805 ;; a polynomial in another variable, and that variable should precede
807 (unless (or (pcoefp (pt-lc p
))
808 (pointergp var
(p-var (pt-lc p
))))
810 (if (zerop (pt-le p
)) (pt-lc p
)
811 (ptimes (make-poly var
(pt-le p
) 1)
813 ;; When we finish, we'll call PPLUS to add SUM and the remainder of
814 ;; X, and this line zeroes out this term in X (through P) to avoid
815 ;; double counting. The term will be deleted by the call to
816 ;; P-DELETE-ZEROS later.
817 (setf (pt-lc p
) 0))))
824 ;; Destructively operate on X, deleting any terms that have a zero coefficient.
825 (defun p-delete-zeros (var x
)
826 ;; The idea is that P always points one before the term in which we're
827 ;; interested. When that term has zero coefficient, it is trimmed from P by
828 ;; replacing the cdr. Consing NIL to the front of X allows us to throw away
829 ;; the first term if necessary.
830 (do ((p (setq x
(cons nil x
))))
832 ;; Switch off $algebraic so that we can recurse to PSIMP without any fear
833 ;; of an infinite recursion - PSIMP only calls this function when (ALGORD
834 ;; VAR) is true, and that only happens when $algebraic is true.
835 (let (($algebraic
)) (psimp var
(cdr x
))))
836 (if (pzerop (pt-lc (cdr p
)))
837 (setf (cdr p
) (pt-red (cdr p
)))
842 ;; Given X representing the terms of a polynomial in a variable z, return the
843 ;; coefficient of z^n.
845 (do ((x x
(pt-red x
)))
846 ((ptzerop x
) (pzero))
847 (cond ((< (pt-le x
) n
) (return (pzero)))
848 ((= (pt-le x
) n
) (return (pt-lc x
))))))
851 (cond ((pcoefp x
) (if (pzerop x
) (pzero) (pctimes x y
)))
852 ((pcoefp y
) (if (pzerop y
) (pzero) (pctimes y x
)))
853 ((eq (p-var x
) (p-var y
))
854 (palgsimp (p-var x
) (ptimes1 (p-terms x
) (p-terms y
)) (alg x
)))
855 ((pointergp (p-var x
) (p-var y
))
856 (psimp (p-var x
) (pctimes1 y
(p-terms x
))))
857 (t (psimp (p-var y
) (pctimes1 x
(p-terms y
))))))
859 (defun ptimes1 (x y-orig
&aux uuu
)
860 (do ((vvv (setq uuu
(pcetimes1 y-orig
(pt-le x
) (pt-lc x
))))
861 (x (pt-red x
) (pt-red x
)))
863 (let ((y y-orig
) (xe (pt-le x
)) (xc (pt-lc x
)))
865 a1
(cond ((null y
) (return nil
)))
866 (setq e
(+ xe
(car y
)))
867 (setq c
(ptimes (cadr y
) xc
))
868 (cond ((pzerop c
) (setq y
(cddr y
)) (go a1
))
869 ((or (null vvv
) (> e
(car vvv
)))
870 (setq uuu
(setq vvv
(ptptplus uuu
(list e c
))))
871 (setq y
(cddr y
)) (go a1
))
873 (setq c
(pplus c
(cadr vvv
)))
875 (setq uuu
(setq vvv
(ptptdiffer uuu
(list (car vvv
) (cadr vvv
))))))
876 (t (rplaca (cdr vvv
) c
)))
880 (cond ((and (cddr vvv
) (> (caddr vvv
) e
))
881 (setq vvv
(cddr vvv
)) (go a
)))
883 b
(cond ((or (null (cdr u
)) (< (cadr u
) e
))
884 (rplacd u
(cons e
(cons c
(cdr u
)))) (go e
)))
885 (cond ((pzerop (setq c
(pplus (caddr u
) c
)))
886 (rplacd u
(cdddr u
)) (go d
))
887 (t (rplaca (cddr u
) c
)))
890 (cond ((null y
) (return nil
)))
891 (setq e
(+ xe
(car y
)))
892 (setq c
(ptimes (cadr y
) xc
))
893 c
(cond ((and (cdr u
) (> (cadr u
) e
)) (setq u
(cddr u
)) (go c
)))
897 (defun pcetimes1 (y e c
) ;C*V^E*Y
898 (loop for
(exp coef
) on y by
#'cddr
899 unless
(pzerop (setq coef
(ptimes c coef
)))
900 nconc
(list (+ e exp
) coef
)))
903 (if (pcoefp p
) (ctimes c p
)
904 (psimp (p-var p
) (pctimes1 c
(p-terms p
)))))
906 (defun pctimes1 (c terms
)
907 (loop for
(exp coef
) on terms by
#'cddr
908 unless
(pzerop (setq coef
(ptimes c coef
)))
909 nconc
(list exp coef
)))
911 (defun leadalgcoef (p)
912 (cond ((pacoefp p
) p
)
913 (t (leadalgcoef (p-lc p
))) ))
916 (cond ((pcoefp q
) (crecip q
))
917 (t (paquo (list (car q
) 0 1) q
))))
919 (defun palgsimp (var p tell
) ;TELL=(N X) -> X^(1/N)
920 (psimp var
(cond ((or (null tell
) (null p
)
921 (< (car p
) (car tell
))) p
)
922 ((null (cddr tell
)) (pasimp1 p
(car tell
) (cadr tell
)))
923 (t (pgcd1 p tell
)) )))
925 (defun pasimp1 (p deg kernel
) ;assumes deg>=(car p)
926 (do ((a p
(pt-red a
))
928 ((or (null a
) (< (pt-le a
) deg
))
930 (ptptplus (pctimes1 kernel p
) a
))
931 (rplaca a
(- (pt-le a
) deg
))))
934 (cond ((pcoefp p
) (if (pzerop p
) p
1))
935 (t (cons (p-var p
) (pmonicize (copy-list (p-terms p
)))))))
937 (defun pmonicize (p) ;CLOBBERS POLY
938 (cond ((equal (pt-lc p
) 1) p
)
939 (t (pmon1 (painvmod (leadalgcoef (pt-lc p
))) p
) p
)))
941 (defun pmon1 (mult l
)
942 (cond (l (pmon1 mult
(pt-red l
))
943 (setf (pt-lc l
) (ptimes mult
(pt-lc l
))))))
945 (defun pmonz (poly &aux lc
) ;A^(N-1)*P(X/A)
946 (setq poly
(pabs poly
))
947 (cond ((equal (setq lc
(p-lc poly
)) 1) poly
)
948 (t (do ((p (p-red poly
) (pt-red p
))
949 (p1 (make-poly (p-var poly
) (p-le poly
) 1))
951 (deg (1- (p-le poly
)) (pt-le p
)))
953 (setq mult
(ptimes mult
(pexpt lc
(- deg
(pt-le p
)))))
954 (nconc p1
(list (pt-le p
) (ptimes mult
(pt-lc p
))))))))
956 ;; THESE ARE ROUTINES FOR MANIPULATING ALGEBRAIC NUMBERS
958 (defun algnormal (p) (car (rquotient p
(leadalgcoef p
))))
960 (defun algcontent (p)
961 (destructuring-let* ((lcf (leadalgcoef p
))
962 ((prim . denom
) (rquotient p lcf
)))
963 (list (ratreduce lcf denom
) prim
)))
965 (defun rquotient (p q
&aux algfac
* a e
) ;FINDS PSEUDO QUOTIENT IF PSEUDOREM=0
966 (cond ((equal p q
) (cons 1 1))
967 ((pcoefp q
) (ratreduce p q
))
968 ((setq a
(testdivide p q
)) (cons a
1))
969 ((alg q
) (rattimes (cons p
1) (rainv q
) t
))
970 (t (cond ((alg (setq a
(leadalgcoef q
)))
972 (setq p
(ptimes p
(car a
)))
973 (setq q
(ptimes q
(car a
)))
975 (cond ((minusp (setq e
(+ 1 (- (cadr q
)) (pdegree p
(car q
)))))
976 (rat-error "RQUOTIENT: Quotient by a polynomial of higher degree")))
978 (ratreduce (or (testdivide (ptimes a p
) q
)
979 (prog2 (setq a
(pexpt (p-lc q
) e
))
980 (pquotient (ptimes a p
) q
)))
983 (defun patimes (x r
) (pquotientchk (ptimes x
(car r
)) (cdr r
)))
985 (defun paquo (x y
) (patimes x
(rainv y
)))
988 (cond ((null (setq var
(alg var
))) nil
)
990 (t (list (car var
) 1 0 (pminus (cadr var
))))))
995 (cond (modulus (cons (crecip q
) 1))
997 (t (let ((var (car q
)) (p (mpget q
)))
998 (declare (special var
)) ;who uses this? --gsb
999 (cond ((null p
) (cons 1 q
))
1000 (t (setq p
(car (let ($ratalgdenom
)
1001 (bprog q
(cons var p
) var
))))
1002 (rattimes (cons (car p
) 1) (rainv (cdr p
)) t
)))))))
1004 (defun pexptsq (p n
)
1005 (do ((n (ash n -
1) (ash n -
1))
1006 (s (if (oddp n
) p
1)))
1008 (setq p
(ptimes p p
))
1009 (and (oddp n
) (setq s
(ptimes s p
))) ))
1011 ;; THIS IS THE END OF THE NEW RATIONAL FUNCTION PACKAGE PART 1.