Sync to HEAD
[bpt/emacs.git] / lisp / calc / calc-poly.el
CommitLineData
3132f345
CW
1;;; calc-poly.el --- polynomial functions for Calc
2
bf77c646 3;; Copyright (C) 1990, 1991, 1992, 1993, 2001 Free Software Foundation, Inc.
3132f345
CW
4
5;; Author: David Gillespie <daveg@synaptics.com>
a1506d29 6;; Maintainers: D. Goel <deego@gnufans.org>
6e1c888a 7;; Colin Walters <walters@debian.org>
136211a9
EZ
8
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is distributed in the hope that it will be useful,
12;; but WITHOUT ANY WARRANTY. No author or distributor
13;; accepts responsibility to anyone for the consequences of using it
14;; or for whether it serves any particular purpose or works at all,
15;; unless he says so in writing. Refer to the GNU Emacs General Public
16;; License for full details.
17
18;; Everyone is granted permission to copy, modify and redistribute
19;; GNU Emacs, but only under the conditions described in the
20;; GNU Emacs General Public License. A copy of this license is
21;; supposed to have been given to you along with GNU Emacs so you
22;; can know your rights and responsibilities. It should be in a
23;; file named COPYING. Among other things, the copyright notice
24;; and this notice must be preserved on all copies.
25
3132f345 26;;; Commentary:
136211a9 27
3132f345 28;;; Code:
136211a9
EZ
29
30;; This file is autoloaded from calc-ext.el.
31(require 'calc-ext)
32
33(require 'calc-macs)
34
35(defun calc-Need-calc-poly () nil)
36
37
38(defun calcFunc-pcont (expr &optional var)
39 (cond ((Math-primp expr)
40 (cond ((Math-zerop expr) 1)
41 ((Math-messy-integerp expr) (math-trunc expr))
42 ((Math-objectp expr) expr)
43 ((or (equal expr var) (not var)) 1)
44 (t expr)))
45 ((eq (car expr) '*)
46 (math-mul (calcFunc-pcont (nth 1 expr) var)
47 (calcFunc-pcont (nth 2 expr) var)))
48 ((eq (car expr) '/)
49 (math-div (calcFunc-pcont (nth 1 expr) var)
50 (calcFunc-pcont (nth 2 expr) var)))
51 ((and (eq (car expr) '^) (Math-natnump (nth 2 expr)))
52 (math-pow (calcFunc-pcont (nth 1 expr) var) (nth 2 expr)))
53 ((memq (car expr) '(neg polar))
54 (calcFunc-pcont (nth 1 expr) var))
55 ((consp var)
56 (let ((p (math-is-polynomial expr var)))
57 (if p
58 (let ((lead (nth (1- (length p)) p))
59 (cont (math-poly-gcd-list p)))
60 (if (math-guess-if-neg lead)
61 (math-neg cont)
62 cont))
63 1)))
64 ((memq (car expr) '(+ - cplx sdev))
65 (let ((cont (calcFunc-pcont (nth 1 expr) var)))
66 (if (eq cont 1)
67 1
68 (let ((c2 (calcFunc-pcont (nth 2 expr) var)))
69 (if (and (math-negp cont)
70 (if (eq (car expr) '-) (math-posp c2) (math-negp c2)))
71 (math-neg (math-poly-gcd cont c2))
72 (math-poly-gcd cont c2))))))
73 (var expr)
bf77c646 74 (t 1)))
136211a9
EZ
75
76(defun calcFunc-pprim (expr &optional var)
77 (let ((cont (calcFunc-pcont expr var)))
78 (if (math-equal-int cont 1)
79 expr
bf77c646 80 (math-poly-div-exact expr cont var))))
136211a9
EZ
81
82(defun math-div-poly-const (expr c)
83 (cond ((memq (car-safe expr) '(+ -))
84 (list (car expr)
85 (math-div-poly-const (nth 1 expr) c)
86 (math-div-poly-const (nth 2 expr) c)))
bf77c646 87 (t (math-div expr c))))
136211a9
EZ
88
89(defun calcFunc-pdeg (expr &optional var)
90 (if (Math-zerop expr)
91 '(neg (var inf var-inf))
92 (if var
93 (or (math-polynomial-p expr var)
94 (math-reject-arg expr "Expected a polynomial"))
bf77c646 95 (math-poly-degree expr))))
136211a9
EZ
96
97(defun math-poly-degree (expr)
98 (cond ((Math-primp expr)
99 (if (eq (car-safe expr) 'var) 1 0))
100 ((eq (car expr) 'neg)
101 (math-poly-degree (nth 1 expr)))
102 ((eq (car expr) '*)
103 (+ (math-poly-degree (nth 1 expr))
104 (math-poly-degree (nth 2 expr))))
105 ((eq (car expr) '/)
106 (- (math-poly-degree (nth 1 expr))
107 (math-poly-degree (nth 2 expr))))
108 ((and (eq (car expr) '^) (natnump (nth 2 expr)))
109 (* (math-poly-degree (nth 1 expr)) (nth 2 expr)))
110 ((memq (car expr) '(+ -))
111 (max (math-poly-degree (nth 1 expr))
112 (math-poly-degree (nth 2 expr))))
bf77c646 113 (t 1)))
136211a9
EZ
114
115(defun calcFunc-plead (expr var)
116 (cond ((eq (car-safe expr) '*)
117 (math-mul (calcFunc-plead (nth 1 expr) var)
118 (calcFunc-plead (nth 2 expr) var)))
119 ((eq (car-safe expr) '/)
120 (math-div (calcFunc-plead (nth 1 expr) var)
121 (calcFunc-plead (nth 2 expr) var)))
122 ((and (eq (car-safe expr) '^) (math-natnump (nth 2 expr)))
123 (math-pow (calcFunc-plead (nth 1 expr) var) (nth 2 expr)))
124 ((Math-primp expr)
125 (if (equal expr var)
126 1
127 expr))
128 (t
129 (let ((p (math-is-polynomial expr var)))
130 (if (cdr p)
131 (nth (1- (length p)) p)
bf77c646 132 1)))))
136211a9
EZ
133
134
135
136
137
138;;; Polynomial quotient, remainder, and GCD.
139;;; Originally by Ove Ewerlid (ewerlid@mizar.DoCS.UU.SE).
140;;; Modifications and simplifications by daveg.
141
3132f345 142(defvar math-poly-modulus 1)
136211a9
EZ
143
144;;; Return gcd of two polynomials
145(defun calcFunc-pgcd (pn pd)
146 (if (math-any-floats pn)
147 (math-reject-arg pn "Coefficients must be rational"))
148 (if (math-any-floats pd)
149 (math-reject-arg pd "Coefficients must be rational"))
150 (let ((calc-prefer-frac t)
151 (math-poly-modulus (math-poly-modulus pn pd)))
bf77c646 152 (math-poly-gcd pn pd)))
136211a9
EZ
153
154;;; Return only quotient to top of stack (nil if zero)
155(defun calcFunc-pdiv (pn pd &optional base)
156 (let* ((calc-prefer-frac t)
157 (math-poly-modulus (math-poly-modulus pn pd))
158 (res (math-poly-div pn pd base)))
159 (setq calc-poly-div-remainder (cdr res))
bf77c646 160 (car res)))
136211a9
EZ
161
162;;; Return only remainder to top of stack
163(defun calcFunc-prem (pn pd &optional base)
164 (let ((calc-prefer-frac t)
165 (math-poly-modulus (math-poly-modulus pn pd)))
bf77c646 166 (cdr (math-poly-div pn pd base))))
136211a9
EZ
167
168(defun calcFunc-pdivrem (pn pd &optional base)
169 (let* ((calc-prefer-frac t)
170 (math-poly-modulus (math-poly-modulus pn pd))
171 (res (math-poly-div pn pd base)))
bf77c646 172 (list 'vec (car res) (cdr res))))
136211a9
EZ
173
174(defun calcFunc-pdivide (pn pd &optional base)
175 (let* ((calc-prefer-frac t)
176 (math-poly-modulus (math-poly-modulus pn pd))
177 (res (math-poly-div pn pd base)))
bf77c646 178 (math-add (car res) (math-div (cdr res) pd))))
136211a9
EZ
179
180
181;;; Multiply two terms, expanding out products of sums.
182(defun math-mul-thru (lhs rhs)
183 (if (memq (car-safe lhs) '(+ -))
184 (list (car lhs)
185 (math-mul-thru (nth 1 lhs) rhs)
186 (math-mul-thru (nth 2 lhs) rhs))
187 (if (memq (car-safe rhs) '(+ -))
188 (list (car rhs)
189 (math-mul-thru lhs (nth 1 rhs))
190 (math-mul-thru lhs (nth 2 rhs)))
bf77c646 191 (math-mul lhs rhs))))
136211a9
EZ
192
193(defun math-div-thru (num den)
194 (if (memq (car-safe num) '(+ -))
195 (list (car num)
196 (math-div-thru (nth 1 num) den)
197 (math-div-thru (nth 2 num) den))
bf77c646 198 (math-div num den)))
136211a9
EZ
199
200
201;;; Sort the terms of a sum into canonical order.
202(defun math-sort-terms (expr)
203 (if (memq (car-safe expr) '(+ -))
204 (math-list-to-sum
205 (sort (math-sum-to-list expr)
206 (function (lambda (a b) (math-beforep (car a) (car b))))))
bf77c646 207 expr))
136211a9
EZ
208
209(defun math-list-to-sum (lst)
210 (if (cdr lst)
211 (list (if (cdr (car lst)) '- '+)
212 (math-list-to-sum (cdr lst))
213 (car (car lst)))
214 (if (cdr (car lst))
215 (math-neg (car (car lst)))
bf77c646 216 (car (car lst)))))
136211a9
EZ
217
218(defun math-sum-to-list (tree &optional neg)
219 (cond ((eq (car-safe tree) '+)
220 (nconc (math-sum-to-list (nth 1 tree) neg)
221 (math-sum-to-list (nth 2 tree) neg)))
222 ((eq (car-safe tree) '-)
223 (nconc (math-sum-to-list (nth 1 tree) neg)
224 (math-sum-to-list (nth 2 tree) (not neg))))
bf77c646 225 (t (list (cons tree neg)))))
136211a9
EZ
226
227;;; Check if the polynomial coefficients are modulo forms.
228(defun math-poly-modulus (expr &optional expr2)
229 (or (math-poly-modulus-rec expr)
230 (and expr2 (math-poly-modulus-rec expr2))
bf77c646 231 1))
136211a9
EZ
232
233(defun math-poly-modulus-rec (expr)
234 (if (and (eq (car-safe expr) 'mod) (Math-natnump (nth 2 expr)))
235 (list 'mod 1 (nth 2 expr))
236 (and (memq (car-safe expr) '(+ - * /))
237 (or (math-poly-modulus-rec (nth 1 expr))
bf77c646 238 (math-poly-modulus-rec (nth 2 expr))))))
136211a9
EZ
239
240
241;;; Divide two polynomials. Return (quotient . remainder).
3132f345 242(defvar math-poly-div-base nil)
136211a9
EZ
243(defun math-poly-div (u v &optional math-poly-div-base)
244 (if math-poly-div-base
245 (math-do-poly-div u v)
bf77c646 246 (math-do-poly-div (calcFunc-expand u) (calcFunc-expand v))))
136211a9
EZ
247
248(defun math-poly-div-exact (u v &optional base)
249 (let ((res (math-poly-div u v base)))
250 (if (eq (cdr res) 0)
251 (car res)
bf77c646 252 (math-reject-arg (list 'vec u v) "Argument is not a polynomial"))))
136211a9
EZ
253
254(defun math-do-poly-div (u v)
255 (cond ((math-constp u)
256 (if (math-constp v)
257 (cons (math-div u v) 0)
258 (cons 0 u)))
259 ((math-constp v)
260 (cons (if (eq v 1)
261 u
262 (if (memq (car-safe u) '(+ -))
263 (math-add-or-sub (math-poly-div-exact (nth 1 u) v)
264 (math-poly-div-exact (nth 2 u) v)
265 nil (eq (car u) '-))
266 (math-div u v)))
267 0))
268 ((Math-equal u v)
269 (cons math-poly-modulus 0))
270 ((and (math-atomic-factorp u) (math-atomic-factorp v))
271 (cons (math-simplify (math-div u v)) 0))
272 (t
273 (let ((base (or math-poly-div-base
274 (math-poly-div-base u v)))
275 vp up res)
276 (if (or (null base)
277 (null (setq vp (math-is-polynomial v base nil 'gen))))
278 (cons 0 u)
279 (setq up (math-is-polynomial u base nil 'gen)
280 res (math-poly-div-coefs up vp))
281 (cons (math-build-polynomial-expr (car res) base)
bf77c646 282 (math-build-polynomial-expr (cdr res) base)))))))
136211a9
EZ
283
284(defun math-poly-div-rec (u v)
285 (cond ((math-constp u)
286 (math-div u v))
287 ((math-constp v)
288 (if (eq v 1)
289 u
290 (if (memq (car-safe u) '(+ -))
291 (math-add-or-sub (math-poly-div-rec (nth 1 u) v)
292 (math-poly-div-rec (nth 2 u) v)
293 nil (eq (car u) '-))
294 (math-div u v))))
295 ((Math-equal u v) math-poly-modulus)
296 ((and (math-atomic-factorp u) (math-atomic-factorp v))
297 (math-simplify (math-div u v)))
298 (math-poly-div-base
299 (math-div u v))
300 (t
301 (let ((base (math-poly-div-base u v))
302 vp up res)
303 (if (or (null base)
304 (null (setq vp (math-is-polynomial v base nil 'gen))))
305 (math-div u v)
306 (setq up (math-is-polynomial u base nil 'gen)
307 res (math-poly-div-coefs up vp))
308 (math-add (math-build-polynomial-expr (car res) base)
309 (math-div (math-build-polynomial-expr (cdr res) base)
bf77c646 310 v)))))))
136211a9
EZ
311
312;;; Divide two polynomials in coefficient-list form. Return (quot . rem).
313(defun math-poly-div-coefs (u v)
314 (cond ((null v) (math-reject-arg nil "Division by zero"))
315 ((< (length u) (length v)) (cons nil u))
316 ((cdr u)
317 (let ((q nil)
318 (urev (reverse u))
319 (vrev (reverse v)))
320 (while
321 (let ((qk (math-poly-div-rec (math-simplify (car urev))
322 (car vrev)))
323 (up urev)
324 (vp vrev))
325 (if (or q (not (math-zerop qk)))
326 (setq q (cons qk q)))
327 (while (setq up (cdr up) vp (cdr vp))
328 (setcar up (math-sub (car up) (math-mul-thru qk (car vp)))))
329 (setq urev (cdr urev))
330 up))
331 (while (and urev (Math-zerop (car urev)))
332 (setq urev (cdr urev)))
333 (cons q (nreverse (mapcar 'math-simplify urev)))))
334 (t
335 (cons (list (math-poly-div-rec (car u) (car v)))
bf77c646 336 nil))))
136211a9
EZ
337
338;;; Perform a pseudo-division of polynomials. (See Knuth section 4.6.1.)
339;;; This returns only the remainder from the pseudo-division.
340(defun math-poly-pseudo-div (u v)
341 (cond ((null v) nil)
342 ((< (length u) (length v)) u)
343 ((or (cdr u) (cdr v))
344 (let ((urev (reverse u))
345 (vrev (reverse v))
346 up)
347 (while
348 (let ((vp vrev))
349 (setq up urev)
350 (while (setq up (cdr up) vp (cdr vp))
351 (setcar up (math-sub (math-mul-thru (car vrev) (car up))
352 (math-mul-thru (car urev) (car vp)))))
353 (setq urev (cdr urev))
354 up)
355 (while up
356 (setcar up (math-mul-thru (car vrev) (car up)))
357 (setq up (cdr up))))
358 (while (and urev (Math-zerop (car urev)))
359 (setq urev (cdr urev)))
360 (nreverse (mapcar 'math-simplify urev))))
bf77c646 361 (t nil)))
136211a9
EZ
362
363;;; Compute the GCD of two multivariate polynomials.
364(defun math-poly-gcd (u v)
365 (cond ((Math-equal u v) u)
366 ((math-constp u)
367 (if (Math-zerop u)
368 v
369 (calcFunc-gcd u (calcFunc-pcont v))))
370 ((math-constp v)
371 (if (Math-zerop v)
372 v
373 (calcFunc-gcd v (calcFunc-pcont u))))
374 (t
375 (let ((base (math-poly-gcd-base u v)))
376 (if base
377 (math-simplify
378 (calcFunc-expand
379 (math-build-polynomial-expr
380 (math-poly-gcd-coefs (math-is-polynomial u base nil 'gen)
381 (math-is-polynomial v base nil 'gen))
382 base)))
bf77c646 383 (calcFunc-gcd (calcFunc-pcont u) (calcFunc-pcont u)))))))
136211a9
EZ
384
385(defun math-poly-div-list (lst a)
386 (if (eq a 1)
387 lst
388 (if (eq a -1)
389 (math-mul-list lst a)
bf77c646 390 (mapcar (function (lambda (x) (math-poly-div-exact x a))) lst))))
136211a9
EZ
391
392(defun math-mul-list (lst a)
393 (if (eq a 1)
394 lst
395 (if (eq a -1)
396 (mapcar 'math-neg lst)
397 (and (not (eq a 0))
bf77c646 398 (mapcar (function (lambda (x) (math-mul x a))) lst)))))
136211a9
EZ
399
400;;; Run GCD on all elements in a list.
401(defun math-poly-gcd-list (lst)
402 (if (or (memq 1 lst) (memq -1 lst))
403 (math-poly-gcd-frac-list lst)
404 (let ((gcd (car lst)))
405 (while (and (setq lst (cdr lst)) (not (eq gcd 1)))
406 (or (eq (car lst) 0)
407 (setq gcd (math-poly-gcd gcd (car lst)))))
408 (if lst (setq lst (math-poly-gcd-frac-list lst)))
bf77c646 409 gcd)))
136211a9
EZ
410
411(defun math-poly-gcd-frac-list (lst)
412 (while (and lst (not (eq (car-safe (car lst)) 'frac)))
413 (setq lst (cdr lst)))
414 (if lst
415 (let ((denom (nth 2 (car lst))))
416 (while (setq lst (cdr lst))
417 (if (eq (car-safe (car lst)) 'frac)
418 (setq denom (calcFunc-lcm denom (nth 2 (car lst))))))
419 (list 'frac 1 denom))
bf77c646 420 1))
136211a9
EZ
421
422;;; Compute the GCD of two monovariate polynomial lists.
423;;; Knuth section 4.6.1, algorithm C.
424(defun math-poly-gcd-coefs (u v)
425 (let ((d (math-poly-gcd (math-poly-gcd-list u)
426 (math-poly-gcd-list v)))
427 (g 1) (h 1) (z 0) hh r delta ghd)
428 (while (and u v (Math-zerop (car u)) (Math-zerop (car v)))
429 (setq u (cdr u) v (cdr v) z (1+ z)))
430 (or (eq d 1)
431 (setq u (math-poly-div-list u d)
432 v (math-poly-div-list v d)))
433 (while (progn
434 (setq delta (- (length u) (length v)))
435 (if (< delta 0)
436 (setq r u u v v r delta (- delta)))
437 (setq r (math-poly-pseudo-div u v))
438 (cdr r))
439 (setq u v
440 v (math-poly-div-list r (math-mul g (math-pow h delta)))
441 g (nth (1- (length u)) u)
442 h (if (<= delta 1)
443 (math-mul (math-pow g delta) (math-pow h (- 1 delta)))
444 (math-poly-div-exact (math-pow g delta)
445 (math-pow h (1- delta))))))
446 (setq v (if r
447 (list d)
448 (math-mul-list (math-poly-div-list v (math-poly-gcd-list v)) d)))
449 (if (math-guess-if-neg (nth (1- (length v)) v))
450 (setq v (math-mul-list v -1)))
451 (while (>= (setq z (1- z)) 0)
452 (setq v (cons 0 v)))
bf77c646 453 v))
136211a9
EZ
454
455
456;;; Return true if is a factor containing no sums or quotients.
457(defun math-atomic-factorp (expr)
458 (cond ((eq (car-safe expr) '*)
459 (and (math-atomic-factorp (nth 1 expr))
460 (math-atomic-factorp (nth 2 expr))))
461 ((memq (car-safe expr) '(+ - /))
462 nil)
463 ((memq (car-safe expr) '(^ neg))
464 (math-atomic-factorp (nth 1 expr)))
bf77c646 465 (t t)))
136211a9
EZ
466
467;;; Find a suitable base for dividing a by b.
468;;; The base must exist in both expressions.
469;;; The degree in the numerator must be higher or equal than the
470;;; degree in the denominator.
471;;; If the above conditions are not met the quotient is just a remainder.
472;;; Return nil if this is the case.
473
474(defun math-poly-div-base (a b)
475 (let (a-base b-base)
476 (and (setq a-base (math-total-polynomial-base a))
477 (setq b-base (math-total-polynomial-base b))
478 (catch 'return
479 (while a-base
480 (let ((maybe (assoc (car (car a-base)) b-base)))
481 (if maybe
482 (if (>= (nth 1 (car a-base)) (nth 1 maybe))
483 (throw 'return (car (car a-base))))))
bf77c646 484 (setq a-base (cdr a-base)))))))
136211a9
EZ
485
486;;; Same as above but for gcd algorithm.
487;;; Here there is no requirement that degree(a) > degree(b).
488;;; Take the base that has the highest degree considering both a and b.
489;;; ("a^20+b^21+x^3+a+b", "a+b^2+x^5+a^22+b^10") --> (a 22)
490
491(defun math-poly-gcd-base (a b)
492 (let (a-base b-base)
493 (and (setq a-base (math-total-polynomial-base a))
494 (setq b-base (math-total-polynomial-base b))
495 (catch 'return
496 (while (and a-base b-base)
497 (if (> (nth 1 (car a-base)) (nth 1 (car b-base)))
498 (if (assoc (car (car a-base)) b-base)
499 (throw 'return (car (car a-base)))
500 (setq a-base (cdr a-base)))
501 (if (assoc (car (car b-base)) a-base)
502 (throw 'return (car (car b-base)))
bf77c646 503 (setq b-base (cdr b-base)))))))))
136211a9
EZ
504
505;;; Sort a list of polynomial bases.
506(defun math-sort-poly-base-list (lst)
507 (sort lst (function (lambda (a b)
508 (or (> (nth 1 a) (nth 1 b))
509 (and (= (nth 1 a) (nth 1 b))
bf77c646 510 (math-beforep (car a) (car b))))))))
136211a9
EZ
511
512;;; Given an expression find all variables that are polynomial bases.
513;;; Return list in the form '( (var1 degree1) (var2 degree2) ... ).
514;;; Note dynamic scope of mpb-total-base.
515(defun math-total-polynomial-base (expr)
516 (let ((mpb-total-base nil))
517 (math-polynomial-base expr 'math-polynomial-p1)
bf77c646 518 (math-sort-poly-base-list mpb-total-base)))
136211a9
EZ
519
520(defun math-polynomial-p1 (subexpr)
521 (or (assoc subexpr mpb-total-base)
522 (memq (car subexpr) '(+ - * / neg))
523 (and (eq (car subexpr) '^) (natnump (nth 2 subexpr)))
524 (let* ((math-poly-base-variable subexpr)
525 (exponent (math-polynomial-p mpb-top-expr subexpr)))
526 (if exponent
527 (setq mpb-total-base (cons (list subexpr exponent)
528 mpb-total-base)))))
bf77c646 529 nil)
136211a9
EZ
530
531
532
533
534(defun calcFunc-factors (expr &optional var)
535 (let ((math-factored-vars (if var t nil))
536 (math-to-list t)
537 (calc-prefer-frac t))
538 (or var
539 (setq var (math-polynomial-base expr)))
540 (let ((res (math-factor-finish
541 (or (catch 'factor (math-factor-expr-try var))
542 expr))))
543 (math-simplify (if (math-vectorp res)
544 res
bf77c646 545 (list 'vec (list 'vec res 1)))))))
136211a9
EZ
546
547(defun calcFunc-factor (expr &optional var)
548 (let ((math-factored-vars nil)
549 (math-to-list nil)
550 (calc-prefer-frac t))
551 (math-simplify (math-factor-finish
552 (if var
553 (let ((math-factored-vars t))
554 (or (catch 'factor (math-factor-expr-try var)) expr))
bf77c646 555 (math-factor-expr expr))))))
136211a9
EZ
556
557(defun math-factor-finish (x)
558 (if (Math-primp x)
559 x
560 (if (eq (car x) 'calcFunc-Fac-Prot)
561 (math-factor-finish (nth 1 x))
bf77c646 562 (cons (car x) (mapcar 'math-factor-finish (cdr x))))))
136211a9
EZ
563
564(defun math-factor-protect (x)
565 (if (memq (car-safe x) '(+ -))
566 (list 'calcFunc-Fac-Prot x)
bf77c646 567 x))
136211a9
EZ
568
569(defun math-factor-expr (expr)
570 (cond ((eq math-factored-vars t) expr)
571 ((or (memq (car-safe expr) '(* / ^ neg))
572 (assq (car-safe expr) calc-tweak-eqn-table))
573 (cons (car expr) (mapcar 'math-factor-expr (cdr expr))))
574 ((memq (car-safe expr) '(+ -))
575 (let* ((math-factored-vars math-factored-vars)
576 (y (catch 'factor (math-factor-expr-part expr))))
577 (if y
578 (math-factor-expr y)
579 expr)))
bf77c646 580 (t expr)))
136211a9
EZ
581
582(defun math-factor-expr-part (x) ; uses "expr"
583 (if (memq (car-safe x) '(+ - * / ^ neg))
584 (while (setq x (cdr x))
585 (math-factor-expr-part (car x)))
586 (and (not (Math-objvecp x))
587 (not (assoc x math-factored-vars))
588 (> (math-factor-contains expr x) 1)
589 (setq math-factored-vars (cons (list x) math-factored-vars))
bf77c646 590 (math-factor-expr-try x))))
136211a9
EZ
591
592(defun math-factor-expr-try (x)
593 (if (eq (car-safe expr) '*)
594 (let ((res1 (catch 'factor (let ((expr (nth 1 expr)))
595 (math-factor-expr-try x))))
596 (res2 (catch 'factor (let ((expr (nth 2 expr)))
597 (math-factor-expr-try x)))))
598 (and (or res1 res2)
599 (throw 'factor (math-accum-factors (or res1 (nth 1 expr)) 1
600 (or res2 (nth 2 expr))))))
601 (let* ((p (math-is-polynomial expr x 30 'gen))
602 (math-poly-modulus (math-poly-modulus expr))
603 res)
604 (and (cdr p)
605 (setq res (math-factor-poly-coefs p))
bf77c646 606 (throw 'factor res)))))
136211a9
EZ
607
608(defun math-accum-factors (fac pow facs)
609 (if math-to-list
610 (if (math-vectorp fac)
611 (progn
612 (while (setq fac (cdr fac))
613 (setq facs (math-accum-factors (nth 1 (car fac))
614 (* pow (nth 2 (car fac)))
615 facs)))
616 facs)
617 (if (and (eq (car-safe fac) '^) (natnump (nth 2 fac)))
618 (setq pow (* pow (nth 2 fac))
619 fac (nth 1 fac)))
620 (if (eq fac 1)
621 facs
622 (or (math-vectorp facs)
623 (setq facs (if (eq facs 1) '(vec)
624 (list 'vec (list 'vec facs 1)))))
625 (let ((found facs))
626 (while (and (setq found (cdr found))
627 (not (equal fac (nth 1 (car found))))))
628 (if found
629 (progn
630 (setcar (cdr (cdr (car found))) (+ pow (nth 2 (car found))))
631 facs)
632 ;; Put constant term first.
633 (if (and (cdr facs) (Math-ratp (nth 1 (nth 1 facs))))
634 (cons 'vec (cons (nth 1 facs) (cons (list 'vec fac pow)
635 (cdr (cdr facs)))))
636 (cons 'vec (cons (list 'vec fac pow) (cdr facs))))))))
bf77c646 637 (math-mul (math-pow fac pow) facs)))
136211a9
EZ
638
639(defun math-factor-poly-coefs (p &optional square-free) ; uses "x"
640 (let (t1 t2)
641 (cond ((not (cdr p))
642 (or (car p) 0))
643
644 ;; Strip off multiples of x.
645 ((Math-zerop (car p))
646 (let ((z 0))
647 (while (and p (Math-zerop (car p)))
648 (setq z (1+ z) p (cdr p)))
649 (if (cdr p)
650 (setq p (math-factor-poly-coefs p square-free))
651 (setq p (math-sort-terms (math-factor-expr (car p)))))
652 (math-accum-factors x z (math-factor-protect p))))
653
654 ;; Factor out content.
655 ((and (not square-free)
656 (not (eq 1 (setq t1 (math-mul (math-poly-gcd-list p)
657 (if (math-guess-if-neg
658 (nth (1- (length p)) p))
659 -1 1))))))
660 (math-accum-factors t1 1 (math-factor-poly-coefs
661 (math-poly-div-list p t1) 'cont)))
662
663 ;; Check if linear in x.
664 ((not (cdr (cdr p)))
665 (math-add (math-factor-protect
666 (math-sort-terms
667 (math-factor-expr (car p))))
668 (math-mul x (math-factor-protect
669 (math-sort-terms
670 (math-factor-expr (nth 1 p)))))))
671
672 ;; If symbolic coefficients, use FactorRules.
673 ((let ((pp p))
674 (while (and pp (or (Math-ratp (car pp))
675 (and (eq (car (car pp)) 'mod)
676 (Math-integerp (nth 1 (car pp)))
677 (Math-integerp (nth 2 (car pp))))))
678 (setq pp (cdr pp)))
679 pp)
680 (let ((res (math-rewrite
681 (list 'calcFunc-thecoefs x (cons 'vec p))
682 '(var FactorRules var-FactorRules))))
683 (or (and (eq (car-safe res) 'calcFunc-thefactors)
684 (= (length res) 3)
685 (math-vectorp (nth 2 res))
686 (let ((facs 1)
687 (vec (nth 2 res)))
688 (while (setq vec (cdr vec))
689 (setq facs (math-accum-factors (car vec) 1 facs)))
690 facs))
691 (math-build-polynomial-expr p x))))
692
693 ;; Check if rational coefficients (i.e., not modulo a prime).
694 ((eq math-poly-modulus 1)
695
696 ;; Check if there are any squared terms, or a content not = 1.
697 (if (or (eq square-free t)
698 (equal (setq t1 (math-poly-gcd-coefs
699 p (setq t2 (math-poly-deriv-coefs p))))
700 '(1)))
701
702 ;; We now have a square-free polynomial with integer coefs.
703 ;; For now, we use a kludgey method that finds linear and
704 ;; quadratic terms using floating-point root-finding.
705 (if (setq t1 (let ((calc-symbolic-mode nil))
706 (math-poly-all-roots nil p t)))
707 (let ((roots (car t1))
708 (csign (if (math-negp (nth (1- (length p)) p)) -1 1))
709 (expr 1)
710 (unfac (nth 1 t1))
711 (scale (nth 2 t1)))
712 (while roots
713 (let ((coef0 (car (car roots)))
714 (coef1 (cdr (car roots))))
715 (setq expr (math-accum-factors
716 (if coef1
717 (let ((den (math-lcm-denoms
718 coef0 coef1)))
719 (setq scale (math-div scale den))
720 (math-add
721 (math-add
722 (math-mul den (math-pow x 2))
723 (math-mul (math-mul coef1 den) x))
724 (math-mul coef0 den)))
725 (let ((den (math-lcm-denoms coef0)))
726 (setq scale (math-div scale den))
727 (math-add (math-mul den x)
728 (math-mul coef0 den))))
729 1 expr)
730 roots (cdr roots))))
731 (setq expr (math-accum-factors
732 expr 1
733 (math-mul csign
734 (math-build-polynomial-expr
735 (math-mul-list (nth 1 t1) scale)
736 x)))))
737 (math-build-polynomial-expr p x)) ; can't factor it.
738
739 ;; Separate out the squared terms (Knuth exercise 4.6.2-34).
740 ;; This step also divides out the content of the polynomial.
741 (let* ((cabs (math-poly-gcd-list p))
742 (csign (if (math-negp (nth (1- (length p)) p)) -1 1))
743 (t1s (math-mul-list t1 csign))
744 (uu nil)
745 (v (car (math-poly-div-coefs p t1s)))
746 (w (car (math-poly-div-coefs t2 t1s))))
747 (while
748 (not (math-poly-zerop
749 (setq t2 (math-poly-simplify
750 (math-poly-mix
751 w 1 (math-poly-deriv-coefs v) -1)))))
752 (setq t1 (math-poly-gcd-coefs v t2)
753 uu (cons t1 uu)
754 v (car (math-poly-div-coefs v t1))
755 w (car (math-poly-div-coefs t2 t1))))
756 (setq t1 (length uu)
757 t2 (math-accum-factors (math-factor-poly-coefs v t)
758 (1+ t1) 1))
759 (while uu
760 (setq t2 (math-accum-factors (math-factor-poly-coefs
761 (car uu) t)
762 t1 t2)
763 t1 (1- t1)
764 uu (cdr uu)))
765 (math-accum-factors (math-mul cabs csign) 1 t2))))
766
767 ;; Factoring modulo a prime.
768 ((and (= (length (setq temp (math-poly-gcd-coefs
769 p (math-poly-deriv-coefs p))))
770 (length p)))
771 (setq p (car temp))
772 (while (cdr temp)
773 (setq temp (nthcdr (nth 2 math-poly-modulus) temp)
774 p (cons (car temp) p)))
775 (and (setq temp (math-factor-poly-coefs p))
776 (math-pow temp (nth 2 math-poly-modulus))))
777 (t
bf77c646 778 (math-reject-arg nil "*Modulo factorization not yet implemented")))))
136211a9
EZ
779
780(defun math-poly-deriv-coefs (p)
781 (let ((n 1)
782 (dp nil))
783 (while (setq p (cdr p))
784 (setq dp (cons (math-mul (car p) n) dp)
785 n (1+ n)))
bf77c646 786 (nreverse dp)))
136211a9
EZ
787
788(defun math-factor-contains (x a)
789 (if (equal x a)
790 1
791 (if (memq (car-safe x) '(+ - * / neg))
792 (let ((sum 0))
793 (while (setq x (cdr x))
794 (setq sum (+ sum (math-factor-contains (car x) a))))
795 sum)
796 (if (and (eq (car-safe x) '^)
797 (natnump (nth 2 x)))
798 (* (math-factor-contains (nth 1 x) a) (nth 2 x))
bf77c646 799 0))))
136211a9
EZ
800
801
802
803
804
805;;; Merge all quotients and expand/simplify the numerator
806(defun calcFunc-nrat (expr)
807 (if (math-any-floats expr)
808 (setq expr (calcFunc-pfrac expr)))
809 (if (or (math-vectorp expr)
810 (assq (car-safe expr) calc-tweak-eqn-table))
811 (cons (car expr) (mapcar 'calcFunc-nrat (cdr expr)))
812 (let* ((calc-prefer-frac t)
813 (res (math-to-ratpoly expr))
814 (num (math-simplify (math-sort-terms (calcFunc-expand (car res)))))
815 (den (math-simplify (math-sort-terms (calcFunc-expand (cdr res)))))
816 (g (math-poly-gcd num den)))
817 (or (eq g 1)
818 (let ((num2 (math-poly-div num g))
819 (den2 (math-poly-div den g)))
820 (and (eq (cdr num2) 0) (eq (cdr den2) 0)
821 (setq num (car num2) den (car den2)))))
bf77c646 822 (math-simplify (math-div num den)))))
136211a9
EZ
823
824;;; Returns expressions (num . denom).
825(defun math-to-ratpoly (expr)
826 (let ((res (math-to-ratpoly-rec expr)))
bf77c646 827 (cons (math-simplify (car res)) (math-simplify (cdr res)))))
136211a9
EZ
828
829(defun math-to-ratpoly-rec (expr)
830 (cond ((Math-primp expr)
831 (cons expr 1))
832 ((memq (car expr) '(+ -))
833 (let ((r1 (math-to-ratpoly-rec (nth 1 expr)))
834 (r2 (math-to-ratpoly-rec (nth 2 expr))))
835 (if (equal (cdr r1) (cdr r2))
836 (cons (list (car expr) (car r1) (car r2)) (cdr r1))
837 (if (eq (cdr r1) 1)
838 (cons (list (car expr)
839 (math-mul (car r1) (cdr r2))
840 (car r2))
841 (cdr r2))
842 (if (eq (cdr r2) 1)
843 (cons (list (car expr)
844 (car r1)
845 (math-mul (car r2) (cdr r1)))
846 (cdr r1))
847 (let ((g (math-poly-gcd (cdr r1) (cdr r2))))
848 (let ((d1 (and (not (eq g 1)) (math-poly-div (cdr r1) g)))
849 (d2 (and (not (eq g 1)) (math-poly-div
850 (math-mul (car r1) (cdr r2))
851 g))))
852 (if (and (eq (cdr d1) 0) (eq (cdr d2) 0))
853 (cons (list (car expr) (car d2)
854 (math-mul (car r2) (car d1)))
855 (math-mul (car d1) (cdr r2)))
856 (cons (list (car expr)
857 (math-mul (car r1) (cdr r2))
858 (math-mul (car r2) (cdr r1)))
859 (math-mul (cdr r1) (cdr r2)))))))))))
860 ((eq (car expr) '*)
861 (let* ((r1 (math-to-ratpoly-rec (nth 1 expr)))
862 (r2 (math-to-ratpoly-rec (nth 2 expr)))
863 (g (math-mul (math-poly-gcd (car r1) (cdr r2))
864 (math-poly-gcd (cdr r1) (car r2)))))
865 (if (eq g 1)
866 (cons (math-mul (car r1) (car r2))
867 (math-mul (cdr r1) (cdr r2)))
868 (cons (math-poly-div-exact (math-mul (car r1) (car r2)) g)
869 (math-poly-div-exact (math-mul (cdr r1) (cdr r2)) g)))))
870 ((eq (car expr) '/)
871 (let* ((r1 (math-to-ratpoly-rec (nth 1 expr)))
872 (r2 (math-to-ratpoly-rec (nth 2 expr))))
873 (if (and (eq (cdr r1) 1) (eq (cdr r2) 1))
874 (cons (car r1) (car r2))
875 (let ((g (math-mul (math-poly-gcd (car r1) (car r2))
876 (math-poly-gcd (cdr r1) (cdr r2)))))
877 (if (eq g 1)
878 (cons (math-mul (car r1) (cdr r2))
879 (math-mul (cdr r1) (car r2)))
880 (cons (math-poly-div-exact (math-mul (car r1) (cdr r2)) g)
881 (math-poly-div-exact (math-mul (cdr r1) (car r2))
882 g)))))))
883 ((and (eq (car expr) '^) (integerp (nth 2 expr)))
884 (let ((r1 (math-to-ratpoly-rec (nth 1 expr))))
885 (if (> (nth 2 expr) 0)
886 (cons (math-pow (car r1) (nth 2 expr))
887 (math-pow (cdr r1) (nth 2 expr)))
888 (cons (math-pow (cdr r1) (- (nth 2 expr)))
889 (math-pow (car r1) (- (nth 2 expr)))))))
890 ((eq (car expr) 'neg)
891 (let ((r1 (math-to-ratpoly-rec (nth 1 expr))))
892 (cons (math-neg (car r1)) (cdr r1))))
bf77c646 893 (t (cons expr 1))))
136211a9
EZ
894
895
896(defun math-ratpoly-p (expr &optional var)
897 (cond ((equal expr var) 1)
898 ((Math-primp expr) 0)
899 ((memq (car expr) '(+ -))
900 (let ((p1 (math-ratpoly-p (nth 1 expr) var))
901 p2)
902 (and p1 (setq p2 (math-ratpoly-p (nth 2 expr) var))
903 (max p1 p2))))
904 ((eq (car expr) '*)
905 (let ((p1 (math-ratpoly-p (nth 1 expr) var))
906 p2)
907 (and p1 (setq p2 (math-ratpoly-p (nth 2 expr) var))
908 (+ p1 p2))))
909 ((eq (car expr) 'neg)
910 (math-ratpoly-p (nth 1 expr) var))
911 ((eq (car expr) '/)
912 (let ((p1 (math-ratpoly-p (nth 1 expr) var))
913 p2)
914 (and p1 (setq p2 (math-ratpoly-p (nth 2 expr) var))
915 (- p1 p2))))
916 ((and (eq (car expr) '^)
917 (integerp (nth 2 expr)))
918 (let ((p1 (math-ratpoly-p (nth 1 expr) var)))
919 (and p1 (* p1 (nth 2 expr)))))
920 ((not var) 1)
921 ((math-poly-depends expr var) nil)
bf77c646 922 (t 0)))
136211a9
EZ
923
924
925(defun calcFunc-apart (expr &optional var)
926 (cond ((Math-primp expr) expr)
927 ((eq (car expr) '+)
928 (math-add (calcFunc-apart (nth 1 expr) var)
929 (calcFunc-apart (nth 2 expr) var)))
930 ((eq (car expr) '-)
931 (math-sub (calcFunc-apart (nth 1 expr) var)
932 (calcFunc-apart (nth 2 expr) var)))
933 ((not (math-ratpoly-p expr var))
934 (math-reject-arg expr "Expected a rational function"))
935 (t
936 (let* ((calc-prefer-frac t)
937 (rat (math-to-ratpoly expr))
938 (num (car rat))
939 (den (cdr rat))
940 (qr (math-poly-div num den))
941 (q (car qr))
942 (r (cdr qr)))
943 (or var
944 (setq var (math-polynomial-base den)))
945 (math-add q (or (and var
946 (math-expr-contains den var)
947 (math-partial-fractions r den var))
bf77c646 948 (math-div r den)))))))
136211a9
EZ
949
950
951(defun math-padded-polynomial (expr var deg)
952 (let ((p (math-is-polynomial expr var deg)))
bf77c646 953 (append p (make-list (- deg (length p)) 0))))
136211a9
EZ
954
955(defun math-partial-fractions (r den var)
956 (let* ((fden (calcFunc-factors den var))
957 (tdeg (math-polynomial-p den var))
958 (fp fden)
959 (dlist nil)
960 (eqns 0)
961 (lz nil)
962 (tz (make-list (1- tdeg) 0))
963 (calc-matrix-mode 'scalar))
964 (and (not (and (= (length fden) 2) (eq (nth 2 (nth 1 fden)) 1)))
965 (progn
966 (while (setq fp (cdr fp))
967 (let ((rpt (nth 2 (car fp)))
968 (deg (math-polynomial-p (nth 1 (car fp)) var))
969 dnum dvar deg2)
970 (while (> rpt 0)
971 (setq deg2 deg
972 dnum 0)
973 (while (> deg2 0)
974 (setq dvar (append '(vec) lz '(1) tz)
975 lz (cons 0 lz)
976 tz (cdr tz)
977 deg2 (1- deg2)
978 dnum (math-add dnum (math-mul dvar
979 (math-pow var deg2)))
980 dlist (cons (and (= deg2 (1- deg))
981 (math-pow (nth 1 (car fp)) rpt))
982 dlist)))
983 (let ((fpp fden)
984 (mult 1))
985 (while (setq fpp (cdr fpp))
986 (or (eq fpp fp)
987 (setq mult (math-mul mult
988 (math-pow (nth 1 (car fpp))
989 (nth 2 (car fpp)))))))
990 (setq dnum (math-mul dnum mult)))
991 (setq eqns (math-add eqns (math-mul dnum
992 (math-pow
993 (nth 1 (car fp))
994 (- (nth 2 (car fp))
995 rpt))))
996 rpt (1- rpt)))))
997 (setq eqns (math-div (cons 'vec (math-padded-polynomial r var tdeg))
998 (math-transpose
999 (cons 'vec
1000 (mapcar
1001 (function
1002 (lambda (x)
1003 (cons 'vec (math-padded-polynomial
1004 x var tdeg))))
1005 (cdr eqns))))))
1006 (and (math-vectorp eqns)
1007 (let ((res 0)
1008 (num nil))
1009 (setq eqns (nreverse eqns))
1010 (while eqns
1011 (setq num (cons (car eqns) num)
1012 eqns (cdr eqns))
1013 (if (car dlist)
1014 (setq num (math-build-polynomial-expr
1015 (nreverse num) var)
1016 res (math-add res (math-div num (car dlist)))
1017 num nil))
1018 (setq dlist (cdr dlist)))
bf77c646 1019 (math-normalize res)))))))
136211a9
EZ
1020
1021
1022
1023(defun math-expand-term (expr)
1024 (cond ((and (eq (car-safe expr) '*)
1025 (memq (car-safe (nth 1 expr)) '(+ -)))
1026 (math-add-or-sub (list '* (nth 1 (nth 1 expr)) (nth 2 expr))
1027 (list '* (nth 2 (nth 1 expr)) (nth 2 expr))
1028 nil (eq (car (nth 1 expr)) '-)))
1029 ((and (eq (car-safe expr) '*)
1030 (memq (car-safe (nth 2 expr)) '(+ -)))
1031 (math-add-or-sub (list '* (nth 1 expr) (nth 1 (nth 2 expr)))
1032 (list '* (nth 1 expr) (nth 2 (nth 2 expr)))
1033 nil (eq (car (nth 2 expr)) '-)))
1034 ((and (eq (car-safe expr) '/)
1035 (memq (car-safe (nth 1 expr)) '(+ -)))
1036 (math-add-or-sub (list '/ (nth 1 (nth 1 expr)) (nth 2 expr))
1037 (list '/ (nth 2 (nth 1 expr)) (nth 2 expr))
1038 nil (eq (car (nth 1 expr)) '-)))
1039 ((and (eq (car-safe expr) '^)
1040 (memq (car-safe (nth 1 expr)) '(+ -))
1041 (integerp (nth 2 expr))
1042 (if (> (nth 2 expr) 0)
1043 (or (and (or (> mmt-many 500000) (< mmt-many -500000))
1044 (math-expand-power (nth 1 expr) (nth 2 expr)
1045 nil t))
1046 (list '*
1047 (nth 1 expr)
1048 (list '^ (nth 1 expr) (1- (nth 2 expr)))))
1049 (if (< (nth 2 expr) 0)
1050 (list '/ 1 (list '^ (nth 1 expr) (- (nth 2 expr))))))))
bf77c646 1051 (t expr)))
136211a9
EZ
1052
1053(defun calcFunc-expand (expr &optional many)
bf77c646 1054 (math-normalize (math-map-tree 'math-expand-term expr many)))
136211a9
EZ
1055
1056(defun math-expand-power (x n &optional var else-nil)
1057 (or (and (natnump n)
1058 (memq (car-safe x) '(+ -))
1059 (let ((terms nil)
1060 (cterms nil))
1061 (while (memq (car-safe x) '(+ -))
1062 (setq terms (cons (if (eq (car x) '-)
1063 (math-neg (nth 2 x))
1064 (nth 2 x))
1065 terms)
1066 x (nth 1 x)))
1067 (setq terms (cons x terms))
1068 (if var
1069 (let ((p terms))
1070 (while p
1071 (or (math-expr-contains (car p) var)
1072 (setq terms (delq (car p) terms)
1073 cterms (cons (car p) cterms)))
1074 (setq p (cdr p)))
1075 (if cterms
1076 (setq terms (cons (apply 'calcFunc-add cterms)
1077 terms)))))
1078 (if (= (length terms) 2)
1079 (let ((i 0)
1080 (accum 0))
1081 (while (<= i n)
1082 (setq accum (list '+ accum
1083 (list '* (calcFunc-choose n i)
1084 (list '*
1085 (list '^ (nth 1 terms) i)
1086 (list '^ (car terms)
1087 (- n i)))))
1088 i (1+ i)))
1089 accum)
1090 (if (= n 2)
1091 (let ((accum 0)
1092 (p1 terms)
1093 p2)
1094 (while p1
1095 (setq accum (list '+ accum
1096 (list '^ (car p1) 2))
1097 p2 p1)
1098 (while (setq p2 (cdr p2))
1099 (setq accum (list '+ accum
1100 (list '* 2 (list '*
1101 (car p1)
1102 (car p2))))))
1103 (setq p1 (cdr p1)))
1104 accum)
1105 (if (= n 3)
1106 (let ((accum 0)
1107 (p1 terms)
1108 p2 p3)
1109 (while p1
1110 (setq accum (list '+ accum (list '^ (car p1) 3))
1111 p2 p1)
1112 (while (setq p2 (cdr p2))
1113 (setq accum (list '+
1114 (list '+
1115 accum
1116 (list '* 3
1117 (list
1118 '*
1119 (list '^ (car p1) 2)
1120 (car p2))))
1121 (list '* 3
1122 (list
1123 '* (car p1)
1124 (list '^ (car p2) 2))))
1125 p3 p2)
1126 (while (setq p3 (cdr p3))
1127 (setq accum (list '+ accum
1128 (list '* 6
1129 (list '*
1130 (car p1)
1131 (list
1132 '* (car p2)
1133 (car p3))))))))
1134 (setq p1 (cdr p1)))
1135 accum))))))
1136 (and (not else-nil)
bf77c646 1137 (list '^ x n))))
136211a9
EZ
1138
1139(defun calcFunc-expandpow (x n)
bf77c646 1140 (math-normalize (math-expand-power x n)))
136211a9 1141
6b61353c 1142;;; arch-tag: d2566c51-2ccc-45f1-8c50-f3462c2953ff
bf77c646 1143;;; calc-poly.el ends here