Add note that the lapack package needs to loaded to get the functions.
[maxima.git] / src / csimp2.lisp
blob5f172ec03d56c5a995c105d70132ef1aa2a5aa68
1 ;;; -*- Mode: Lisp; Package: Maxima; Syntax: Common-Lisp; Base: 10 -*- ;;;;
2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;;; The data in this file contains enhancements. ;;;;;
4 ;;; ;;;;;
5 ;;; Copyright (c) 1984,1987 by William Schelter,University of Texas ;;;;;
6 ;;; All rights reserved ;;;;;
7 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
8 ;;; (c) Copyright 1982 Massachusetts Institute of Technology ;;;
9 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
11 (in-package :maxima)
13 (macsyma-module csimp2)
15 (load-macsyma-macros rzmac)
17 (declare-top (special var sign))
19 (defmvar $gammalim 10000
20 "Controls simplification of gamma for rational number arguments.")
22 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
24 ;;; Implementation of the plog function
26 (def-simplifier plog (x)
27 (prog (varlist dd y z)
28 (cond ((equal 0 x)
29 (merror (intl:gettext "plog: plog(0) is undefined.")))
30 ((among var x)
31 ;; This is used in DEFINT. 1/19/81. -JIM
33 ;; In particular, this is the call to KEYHOLE in ZTORAT
34 ;; for an integral like integrate(1/(x^2+x+1),x,0,inf).
35 ;; Without this, the integral is computed incorrectly.
37 ;; FIXME: We should be doing this.
38 (return (give-up))))
39 (newvar x)
40 (cond
41 ((and (member '$%i varlist)
42 (not (some #'(lambda (v)
43 (and (atom v) (not (eq v '$%i))))
44 varlist)))
45 (setq dd (trisplit x))
46 (cond ((setq z (patan (car dd) (cdr dd)))
47 (return (add2* (ftake '%log
48 (power ($expand (add* (power (car dd) 2)
49 (power (cdr dd) 2)))
50 1//2))
51 (mul* z '$%i))))))
52 ((and (free x '$%i)
53 (eq ($sign x) '$pnz))
54 (return (give-up)))
55 ((and (equal ($imagpart x) 0)
56 (setq y ($asksign x)))
57 (cond ((eq y '$pos)
58 (return (ftake '%log x)))
59 ((and plogabs (eq y '$neg))
60 (return (ftake '%log (list '(mtimes) -1 x))))
61 ((eq y '$neg)
62 (return (add2 %p%i
63 (ftake '%log (mul -1 x)))))
64 (t (merror (intl:gettext "plog: plog(0) is undefined.")))))
65 ((and (equal ($imagpart (setq z (div* x '$%i))) 0)
66 (setq y ($asksign z)))
67 (cond
68 ((equal y '$zero)
69 (merror (intl:gettext "plog: plog(0) is undefined.")))
70 (t (cond ((eq y '$pos)
71 (setq y 1))
72 ((eq y '$neg)
73 (setq y -1)))
74 (return (add2* (ftake '%log (mul y z))
75 (mul y 1//2 '$%i '$%pi)))))))
76 (return (give-up))))
78 (defun patan (r i)
79 (let (($numer $numer))
80 (prog (a b var)
81 (setq i (simplifya i nil) r (simplifya r nil))
82 (cond ((zerop1 r)
83 (if (floatp i) (setq $numer t))
84 (setq i ($asksign i))
85 (cond ((equal i '$pos) (return (simplify half%pi)))
86 ((equal i '$neg)
87 (return (mul2 -1 (simplify half%pi))))
88 (t (merror (intl:gettext "plog: encountered atan(0/0).")))))
89 ((zerop1 i)
90 (cond ((floatp r) (setq $numer t)))
91 (setq r ($asksign r))
92 (cond ((equal r '$pos) (return 0))
93 ((equal r '$neg) (return (simplify '$%pi)))
94 (t (merror (intl:gettext "plog: encountered atan(0/0).")))))
95 ((and (among '%cos r) (among '%sin i))
96 ;; genvar and varlist, used by the rational function system,
97 ;; are bound in order to prevent the symbol 'xz from leaking
98 ;; out of this function.
99 (let ((var 'xz) genvar varlist)
100 (numden (div* r i))
101 (cond ((and (eq (caar nn*) '%cos) (eq (caar dn*) '%sin))
102 (return (cadr nn*)))))))
103 (setq a ($sign r) b ($sign i))
104 (cond ((eq a '$pos) (setq a 1))
105 ((eq a '$neg) (setq a -1))
106 ((eq a '$zero) (setq a 0)))
107 (cond ((eq b '$pos) (setq b 1))
108 ((eq b '$neg) (setq b -1))
109 ((eq a '$zero) (setq b 0)))
110 (cond ((equal i 0)
111 (return (if (equal a 1) 0 (simplify '$%pi))))
112 ((equal r 0)
113 (return (cond ((equal b 1) (simplify half%pi))
114 (t (mul2 '((rat simp) -1 2)
115 (simplify '$%pi)))))))
116 (setq r (simptimes (list '(mtimes) a b (div* i r)) 1 nil))
117 (return (cond ((onep1 r)
118 (archk a b (list '(mtimes) '((rat) 1 4) '$%pi)))
119 ((alike1 r '((mexpt) 3 ((rat) 1 2)))
120 (archk a b (list '(mtimes) '((rat) 1 3) '$%pi)))
121 ((alike1 r '((mexpt) 3 ((rat) -1 2)))
122 (archk a b (list '(mtimes) '((rat) 1 6) '$%pi))))))))
124 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
126 ;;; Implementation of the Binomial coefficient
128 ;; Binomial has Mirror symmetry
129 (defprop %binomial t commutes-with-conjugate)
131 (def-simplifier binomial (u v)
132 (let (y)
133 (cond ((integerp v)
134 (cond ((minusp v)
135 (if (and (integerp u) (minusp u) (< v u))
136 (bincomp u (- u v))
138 ((or (zerop v) (equal u v)) 1)
139 ((and (integerp u) (not (minusp u)))
140 (bincomp u (min v (- u v))))
141 (t (bincomp u v))))
142 ((integerp (setq y (sub u v)))
143 (cond ((zerop1 y)
144 ;; u and v are equal, simplify not if argument can be negative
145 (if (member ($csign u) '($pnz $pn $neg $nz))
146 (give-up)
147 (bincomp u y)))
148 (t (bincomp u y))))
149 ((complex-float-numerical-eval-p u v)
150 ;; Numercial evaluation for real and complex floating point numbers.
151 (let (($numer t) ($float t))
152 ($rectform
153 ($float
154 ($makegamma (list '(%binomial) ($float u) ($float v)))))))
155 ((complex-bigfloat-numerical-eval-p u v)
156 ;; Numerical evaluation for real and complex bigfloat numbers.
157 ($rectform
158 ($bfloat
159 ($makegamma (list '(%binomial) ($bfloat u) ($bfloat v))))))
160 (t (give-up)))))
162 (defun bincomp (u v)
163 (cond ((minusp v) 0)
164 ((zerop v) 1)
165 ((mnump u) (binocomp u v))
166 (t (muln (bincomp1 u v) nil))))
168 (defun bincomp1 (u v)
169 (if (equal v 1)
170 (ncons u)
171 (list* u (list '(mexpt) v -1) (bincomp1 (add2 -1 u) (1- v)))))
173 (defun binocomp (u v)
174 (prog (ans)
175 (setq ans 1)
176 loop (if (zerop v) (return ans))
177 (setq ans (timesk (timesk u ans) (simplify (list '(rat) 1 v))))
178 (setq u (addk -1 u) v (1- v))
179 (go loop)))
181 ;;; gradient of binomial
183 (defprop %binomial
184 ((a b)
185 ((mtimes) -1 ((%binomial) a b)
186 ((mplus)
187 ((mtimes) -1
188 ((mqapply) (($psi array) 0) ((mplus) 1 a)))
189 ((mqapply) (($psi array) 0)
190 ((mplus) 1 a ((mtimes) -1 b)))))
192 ((mtimes) -1 ((%binomial) a b)
193 ((mplus)
194 ((mtimes) -1
195 ((mqapply) (($psi array) 0)
196 ((mplus) 1 a ((mtimes) -1 b))))
197 ((mqapply) (($psi array) 0) ((mplus) 1 b))))) grad)
199 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
201 ;;; Implementation of the Beta function
203 (declare-top (special $gammalim))
205 (defmvar $beta_args_sum_to_integer nil)
207 ;;; The Beta function has mirror symmetry
208 (defprop %beta t commutes-with-conjugate)
210 (def-simplifier beta (u v)
211 (let (x)
212 (cond ((or (zerop1 u) (zerop1 v))
213 (if errorsw
214 (throw 'errorsw t)
215 (merror
216 (intl:gettext "beta: expected nonzero arguments; found ~M, ~M")
217 u v)))
219 ;; Check for numerical evaluation in float precision
220 ((complex-float-numerical-eval-p u v)
221 (cond
222 ;; We use gamma(u)*gamma(v)/gamma(u+v) for numerical evaluation.
223 ;; Therefore u, v or u+v can not be a negative integer or a
224 ;; floating point representation of a negative integer.
225 ((and (or (not (numberp u))
226 (> u 0)
227 (not (= (nth-value 1 (truncate u)) 0)))
228 (and (or (not (numberp v))
229 (> v 0)
230 (not (= (nth-value 1 (truncate v)) 0)))
231 (and (or (not (numberp (add u v)))
232 (> (add v u) 0)
233 (not (= (nth-value 1 ($truncate (add u v))) 0))))))
234 ($rectform
235 (power ($float '$%e)
236 (add ($log_gamma ($float u))
237 ($log_gamma ($float v))
238 (mul -1 ($log_gamma ($float (add u v))))))))
239 ((or (and (numberp u)
240 (> u 0)
241 (= (nth-value 1 (truncate u)) 0)
242 (not (and (mnump v)
243 (eq ($sign (sub ($truncate v) v)) '$zero)
244 (eq ($sign v) '$neg)
245 (eq ($sign (add u v)) '$pos)))
246 (setq u (truncate u)))
247 (and (numberp v)
248 (> v 0)
249 (= (nth-value 1 (truncate u)) 0)
250 (not (and (mnump u)
251 (eq ($sign (sub ($truncate u) u)) '$zero)
252 (eq ($sign u) '$neg)
253 (eq ($sign (add u v)) '$pos)))
254 (setq v (truncate v))))
255 ;; One value is representing a negative integer, the other a
256 ;; positive integer and the sum is negative. Expand.
257 ($rectform ($float (beta-expand-integer u v))))
259 (give-up))))
261 ;; Check for numerical evaluation in bigfloat precision
262 ((complex-bigfloat-numerical-eval-p u v)
263 (let (($ratprint nil))
264 (cond
265 ((and (or (not (mnump u))
266 (eq ($sign u) '$pos)
267 (not (eq ($sign (sub ($truncate u) u)) '$zero)))
268 (or (not (mnump v))
269 (eq ($sign v) '$pos)
270 (not (eq ($sign (sub ($truncate v) v)) '$zero)))
271 (or (not (mnump (add u v)))
272 (eq ($sign (add u v)) '$pos)
273 (not (eq ($sign (sub ($truncate (add u v))
274 (add u v)))
275 '$zero))))
276 ($rectform
277 (power ($bfloat'$%e)
278 (add ($log_gamma ($bfloat u))
279 ($log_gamma ($bfloat v))
280 (mul -1 ($log_gamma ($bfloat (add u v))))))))
281 ((or (and (mnump u)
282 (eq ($sign u) '$pos)
283 (eq ($sign (sub ($truncate u) u)) '$zero)
284 (not (and (mnump v)
285 (eq ($sign (sub ($truncate v) v)) '$zero)
286 (eq ($sign v) '$neg)
287 (eq ($sign (add u v)) '$pos)))
288 (setq u ($truncate u)))
289 (and (mnump v)
290 (eq ($sign v) '$pos)
291 (eq ($sign (sub ($truncate v) v)) '$zero)
292 (not (and (mnump u)
293 (eq ($sign (sub ($truncate u) u)) '$zero)
294 (eq ($sign u) '$neg)
295 (eq ($sign (add u v)) '$pos)))
296 (setq v ($truncate v))))
297 ($rectform ($bfloat (beta-expand-integer u v))))
299 (give-up)))))
301 ((or (and (and (integerp u)
302 (plusp u))
303 (not (and (mnump v)
304 (eq ($sign (sub ($truncate v) v)) '$zero)
305 (eq ($sign v) '$neg)
306 (eq ($sign (add u v)) '$pos))))
307 (and (and (integerp v)
308 (plusp v))
309 (not (and (mnump u)
310 (eq ($sign (sub ($truncate u) u)) '$zero)
311 (eq ($sign u) '$neg)
312 (eq ($sign (add u v)) '$pos)))))
313 ;; Expand for a positive integer. But not if the other argument is
314 ;; a negative integer and the sum of the integers is not negative.
315 (beta-expand-integer u v))
317 ;;; At this point both integers are negative. This code does not work for
318 ;;; negative integers. The factorial function is not defined.
319 ; ((and (integerp u) (integerp v))
320 ; (mul2* (div* (list '(mfactorial) (1- u))
321 ; (list '(mfactorial) (+ u v -1)))
322 ; (list '(mfactorial) (1- v))))
324 ((or (and (ratnump u) (ratnump v) (integerp (setq x (addk u v))))
325 (and $beta_args_sum_to_integer
326 (integerp (setq x (expand1 (add2 u v) 1 1)))))
327 (let ((w (if (symbolp v) v u)))
328 (div* (mul2* '$%pi
329 (list '(%binomial)
330 (add2 (1- x) (neg w))
331 (1- x)))
332 `((%sin) ((mtimes) ,w $%pi)))))
334 ((and $beta_expand (mplusp u) (integerp (cadr u)))
335 ;; Expand beta(a+n,b) where n is an integer.
336 (let ((n (cadr u))
337 (u (simplify (cons '(mplus) (cddr u)))))
338 (beta-expand-add-integer n u v)))
340 ((and $beta_expand (mplusp v) (integerp (cadr v)))
341 ;; Expand beta(a,b+n) where n is an integer.
342 (let ((n (cadr v))
343 (v (simplify (cons '(mplus) (cddr v)))))
344 (beta-expand-add-integer n v u)))
346 (t (give-up)))))
348 (defun beta-expand-integer (u v)
349 ;; One of the arguments is a positive integer. Do an expansion.
350 ;; BUT for a negative integer as second argument the expansion is only
351 ;; possible when the sum of the integers is negative too.
352 ;; This routine expects that the calling routine has checked this.
353 (let ((x (add u v)))
354 (power
355 (mul (sub x 1)
356 (simplify
357 (list '(%binomial)
358 (sub x 2)
359 (sub (if (and (integerp u) (plusp u)) u v) 1))))
360 -1)))
362 (defun beta-expand-add-integer (n u v)
363 (if (plusp n)
364 (mul (simplify (list '($pochhammer) u n))
365 (power (simplify (list '($pochhammer) (add u v) n)) -1)
366 (ftake* '%beta u v))
367 (mul (simplify (list '($pochhammer) (add u v n) (- n)))
368 (power (simplify (list '($pochhammer) (add u n) (- n))) -1)
369 (ftake* '%beta u v))))
371 ;; Derivative of beta function
372 ;; https://en.wikipedia.org/wiki/Beta_function
373 ;; https://functions.wolfram.com/GammaBetaErf/Beta/
374 (defprop %beta
375 ((a b)
376 ; derivative wrt a
377 ((mtimes)
378 (($beta) a b)
379 ((mplus)
380 ((mqapply) (($psi array) 0) a)
381 ((mtimes) -1
382 ((mqapply) (($psi array) 0) ((mplus) a b)))))
383 ; derivative wrt b
384 ((mtimes)
385 (($beta) a b)
386 ((mplus)
387 ((mqapply) (($psi) 0) b)
388 ((mtimes) -1
389 ((mqapply) (($psi array) 0) ((mplus) a b))))))
390 grad)
392 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
394 ;;; Implementation of the Gamma function
396 (def-simplifier gamma (j)
397 (cond ((and (floatp j)
398 (or (zerop j)
399 (and (< j 0)
400 (zerop (nth-value 1 (truncate j))))))
401 (merror (intl:gettext "gamma: gamma(~:M) is undefined.") j))
402 ((float-numerical-eval-p j) (gammafloat ($float j)))
403 ((and ($bfloatp j)
404 (or (zerop1 j)
405 (and (eq ($sign j) '$neg)
406 (zerop1 (sub j ($truncate j))))))
407 (merror (intl:gettext "gamma: gamma(~:M) is undefined.") j))
408 ((bigfloat-numerical-eval-p j)
409 ;; Adding 4 digits in the call to bffac. For $fpprec up to about 256
410 ;; and an argument up to about 500.0 the accuracy of the result is
411 ;; better than 10^(-$fpprec).
412 (let ((z (bigfloat:to ($bfloat j))))
413 (cond
414 ((bigfloat:<= (bigfloat:abs z) (bigfloat:sqrt (bigfloat:epsilon z)))
415 ;; For small z, use gamma(z) = gamma(z+1)/z = z!/z
416 (div (mfuncall '$bffac
417 ($bfloat j)
418 (+ $fpprec 4))
419 ($bfloat j)))
421 (let ((result (mfuncall '$bffac (m+ ($bfloat j) -1) (+ $fpprec 4))))
422 ;; bigfloatp will round the result to the correct fpprec
423 (bigfloatp result))))))
424 ((complex-float-numerical-eval-p j)
425 (complexify (gamma-lanczos (complex ($float ($realpart j))
426 ($float ($imagpart j))))))
427 ((complex-bigfloat-numerical-eval-p j)
428 (let ((z (bigfloat:to ($bfloat j))))
429 (cond
430 ((bigfloat:<= (bigfloat:abs z)
431 (bigfloat:sqrt (bigfloat:epsilon z)))
432 ;; For small z, use gamma(z) = gamma(z+1)/z = z!/z
433 (to (bigfloat:/ (bigfloat:to (mfuncall '$cbffac
434 (to z)
435 (+ $fpprec 4)))
436 z)))
438 ;; Adding 4 digits in the call to cbffac. See comment above.
439 (let ((result
440 (mfuncall '$cbffac
441 (add -1 ($bfloat ($realpart j))
442 (mul '$%i ($bfloat ($imagpart j))))
443 (+ $fpprec 4))))
444 (add (bigfloatp ($realpart result))
445 (mul '$%i (bigfloatp ($imagpart result)))))))))
446 ((taylorize (mop form) (cadr form)))
447 ((eq j '$inf) '$inf) ; Simplify to $inf to be more consistent.
448 ((and $gamma_expand
449 (mplusp j)
450 (integerp (cadr j)))
451 ;; Expand gamma(z+n) for n an integer.
452 (let ((n (cadr j))
453 (z (simplify (cons '(mplus) (cddr j)))))
454 (cond
455 ((> n 0)
456 (mul (simplify (list '($pochhammer) z n))
457 (simplify (list '(%gamma) z))))
458 ((< n 0)
459 (setq n (- n))
460 (div (mul (power -1 n) (simplify (list '(%gamma) z)))
461 ;; We factor to get the order (z-1)*(z-2)*...
462 ;; and not (1-z)*(2-z)*...
463 ($factor
464 (simplify (list '($pochhammer) (sub 1 z) n))))))))
465 ((integerp j)
466 (cond ((> j 0)
467 (cond ((<= j $factlim)
468 ;; Positive integer less than $factlim. Evaluate.
469 (simplify (list '(mfactorial) (1- j))))
470 ;; Positive integer greater $factlim. Noun form.
471 (t (give-up))))
472 ;; Negative integer. Throw a Maxima error.
473 (errorsw (throw 'errorsw t))
474 (t (merror (intl:gettext "gamma: gamma(~:M) is undefined.") j))))
475 ((alike1 j '((rat) 1 2))
476 (list '(mexpt simp) '$%pi j))
477 ((and (mnump j)
478 (ratgreaterp $gammalim (simplify (list '(mabs) j)))
479 (or (ratgreaterp j 1) (ratgreaterp 0 j)))
480 ;; Expand for rational numbers less than $gammalim.
481 (gammared j))
482 (t (give-up))))
484 ;; A sign function for gamma(x); when x > 0 return pos; when x < 0 or x > 0, return pn;
485 ;;; otherwise, return pnz (that is, nothing known).
486 (defun gamma-sign (x)
487 (let ((sgn ($csign (second x)))) ;; careful! x = ((%gamma) XXX)
488 (setq sign
489 (cond ((eql sgn '$pos) '$pos)
490 ((or (eql sgn '$neg) (eql sgn '$pn)) '$pn)
491 (t '$pnz)))))
493 (putprop '%gamma 'gamma-sign 'sign-function)
495 (defun gamma (y) ;;; numerical evaluation for 0 < y < 1
496 (prog (sum coefs)
497 (setq coefs (list 0.035868343 -0.193527817 0.48219939
498 -0.75670407 0.91820685 -0.89705693
499 0.98820588 -0.57719165))
500 (unless (atom y) (setq y (fpcofrat y)))
501 (setq sum (car coefs) coefs (cdr coefs))
502 loop (setq sum (+ (* sum y) (car coefs)))
503 (when (setq coefs (cdr coefs)) (go loop))
504 (return (+ (/ y) sum))))
506 (defun gammared (a) ;A is assumed to
507 (prog (m q n) ;be '((RAT) M N)
508 (cond ((floatp a) (return (gammafloat a))))
509 (setq m (cadr a) ;Numerator
510 n (caddr a) ;denominator
511 q (abs (truncate m n))) ;integer part
512 (cond ((minusp m)
513 (setq q (1+ q) m (+ m (* n q)))
514 (return
515 (simptimes (list '(mtimes)
516 (list '(mexpt) n q)
517 (ftake* '%gamma (list '(rat) m n))
518 (list '(mexpt) (gammac m n q) -1))
520 nil))))
521 (return (m* (gammac m n q)
522 (ftake* '%gamma (list '(rat) (rem m n) n))
523 (m^ n (- q))))))
525 (defun gammac (m n q)
526 (do ((ans 1))
527 ((< q 1) ans)
528 (setq q (1- q) m (- m n) ans (* m ans))))
531 ;; This implementation is based on Lanczos convergent formula for the
532 ;; gamma function for Re(z) > 0. We can use the reflection formula
534 ;; -z*Gamma(z)*Gamma(-z) = pi/sin(pi*z)
536 ;; to handle the case of Re(z) <= 0.
538 ;; See http://my.fit.edu/~gabdo/gamma.m for some matlab code to
539 ;; compute this and http://my.fit.edu/~gabdo/gamma.txt for a nice
540 ;; discussion of Lanczos method and an improvement of Lanczos method.
543 ;; The document says this should give about 15 digits of accuracy for
544 ;; double-precision IEEE floats. The document also indicates how to
545 ;; compute a new set of coefficients if you need more range or
546 ;; accuracy.
548 (defun gamma-lanczos (z)
549 (declare (type (complex flonum) z)
550 (optimize (safety 3)))
551 (let ((c (make-array 15 :element-type 'flonum
552 :initial-contents
553 '(0.99999999999999709182
554 57.156235665862923517
555 -59.597960355475491248
556 14.136097974741747174
557 -0.49191381609762019978
558 .33994649984811888699e-4
559 .46523628927048575665e-4
560 -.98374475304879564677e-4
561 .15808870322491248884e-3
562 -.21026444172410488319e-3
563 .21743961811521264320e-3
564 -.16431810653676389022e-3
565 .84418223983852743293e-4
566 -.26190838401581408670e-4
567 .36899182659531622704e-5))))
568 (declare (type (simple-array flonum (15)) c))
569 (cond
570 ((minusp (realpart z))
571 ;; Use the reflection formula
572 ;; -z*Gamma(z)*Gamma(-z) = pi/sin(pi*z)
573 ;; or
574 ;; Gamma(z) = pi/z/sin(pi*z)/Gamma(-z)
576 ;; If z is a negative integer, Gamma(z) is infinity. Should
577 ;; we test for this? Throw an error?
578 ;; The test must be done by the calling routine.
579 (/ (float pi)
580 (* (- z) (sin (* (float pi) z))
581 (gamma-lanczos (- z)))))
582 ((<= (abs z) (sqrt +flonum-epsilon+))
583 ;; For |z| small, use Gamma(z) = Gamma(z+1)/z
584 (/ (gamma-lanczos (+ 1 z))
587 (let* ((z (- z 1))
588 (zh (+ z 1/2))
589 (zgh (+ zh 607/128))
590 (ss
591 (do ((sum 0.0)
592 (pp (1- (length c)) (1- pp)))
593 ((< pp 1)
594 sum)
595 (incf sum (/ (aref c pp) (+ z pp))))))
596 (let ((result
597 ;; We check for an overflow. The last positive value in
598 ;; double-float precicsion for which Maxima can calculate
599 ;; gamma is ~171.6243 (CLISP 2.46 and GCL 2.6.8)
600 (ignore-errors
601 (let ((zp (expt zgh (/ zh 2))))
602 (* (sqrt (float (* 2 pi)))
603 (+ ss (aref c 0))
604 (* (/ zp (exp zgh)) zp))))))
605 (cond ((null result)
606 ;; No result. Overflow.
607 (merror (intl:gettext "gamma: overflow in GAMMA-LANCZOS.")))
608 ((or (float-nan-p (realpart result))
609 (float-inf-p (realpart result)))
610 ;; Result, but beyond extreme values. Overflow.
611 (merror (intl:gettext "gamma: overflow in GAMMA-LANCZOS.")))
612 (t result))))))))
614 (defun gammafloat (a)
615 (let ((a (float a)))
616 (cond ((minusp a)
617 ;; Reflection formula to make it positive: gamma(x) =
618 ;; %pi/sin(%pi*x)/x/gamma(-x)
619 (/ (float (- pi))
620 (* a (sin (* (float pi) a)))
621 (gammafloat (- a))))
622 ((<= a (sqrt +flonum-epsilon+))
623 ;; Use gamma(x) = gamma(1+x)/x when x is very small
624 (/ (gammafloat (+ 1 a))
626 ((< a 10)
627 (slatec::dgamma a))
629 (let ((result
630 (let ((c (* (sqrt (* 2 (float pi)))
631 (exp (slatec::d9lgmc a)))))
632 (let ((v (expt a (- (* .5e0 a) 0.25e0))))
633 (* v
634 (/ v (exp a))
635 c)))))
636 (if (or (float-nan-p result)
637 (float-inf-p result))
638 (merror (intl:gettext "gamma: overflow in GAMMAFLOAT."))
639 result))))))
641 (defmfun $zeromatrix (m n) ($ematrix m n 0 1 1))
643 (defmfun $ematrix (m n var i j)
644 (prog (ans row)
645 (cond ((equal m 0) (return (ncons '($matrix simp))))
646 ((and (equal n 0) (fixnump m) (> m 0))
647 (return (cons '($matrix simp) (list-of-mlists m))))
648 ((not (and (fixnump m) (fixnump n)
649 (fixnump i) (fixnump j)
650 (> m 0) (> n 0) (> i 0) (> j 0)))
651 (merror (intl:gettext "ematrix: arguments must be positive integers; found ~M")
652 (list '(mlist simp) m n i j) )))
653 loop (cond ((= m i) (setq row (onen j n var 0)) (go on))
654 ((zerop m) (return (cons '($matrix) (mxc ans)))))
655 (setq row nil)
656 (do ((n n (1- n))) ((zerop n)) (setq row (cons 0 row)))
657 on (setq ans (cons row ans) m (1- m))
658 (go loop)))
660 (defun list-of-mlists (n)
661 (do ((n n (1- n))
662 (l nil (cons (ncons '(mlist simp)) l)))
663 ((= n 0) l)))
665 (defmfun $coefmatrix (eql varl) (coefmatrix eql varl nil))
667 (defmfun $augcoefmatrix (eql varl) (coefmatrix eql varl t))
669 (defun coefmatrix (eql varl ind)
670 (prog (ans row a b elem)
671 (if (not ($listp eql)) (improper-arg-err eql '$coefmatrix))
672 (if (not ($listp varl)) (improper-arg-err varl '$coefmatrix))
673 (dolist (v (cdr varl))
674 (if (and (not (atom v)) (member (caar v) '(mplus mtimes) :test #'eq))
675 (merror (intl:gettext "coefmatrix: variables cannot be '+' or '*' expressions; found ~M") v)))
676 (setq eql (nreverse (mapcar #'meqhk (cdr eql)))
677 varl (reverse (cdr varl)))
678 loop1(if (null eql) (return (cons '($matrix) (mxc ans))))
679 (setq a (car eql) eql (cdr eql) row nil)
680 (if ind (setq row (cons (const1 a varl) row)))
681 (setq b varl)
682 loop2(setq elem (ratcoef a (car b)))
683 (setq row (cons (if $ratmx elem (ratdisrep elem)) row))
684 (if (setq b (cdr b)) (go loop2))
685 (setq ans (cons row ans))
686 (go loop1)))
688 (defun const1 (e varl)
689 (dolist (v varl) (setq e (maxima-substitute 0 v e))) e)
691 (defmfun $entermatrix (rows columns)
692 (prog (row column vector matrix sym symvector)
693 (cond ((or (not (fixnump rows))
694 (not (fixnump columns)))
695 (merror (intl:gettext "entermatrix: arguments must be integers; found ~M, ~M") rows columns)))
696 (setq row 0)
697 (unless (= rows columns) (setq sym nil) (go oloop))
698 quest (format t "~%Is the matrix 1. Diagonal 2. Symmetric 3. Antisymmetric 4. General~%")
699 (setq sym (retrieve "Answer 1, 2, 3 or 4 : " nil))
700 (unless (member sym '(1 2 3 4)) (go quest))
701 oloop (cond ((> (incf row) rows)
702 (format t "~%Matrix entered.~%")
703 (return (cons '($matrix) (mxc matrix)))))
704 (cond ((equal sym 1)
705 (setq column row)
706 (let ((prompt (format nil "Row ~a Column ~a: " row column)))
707 (setq matrix
708 (nconc matrix
709 (ncons (onen row columns
710 (meval (retrieve prompt nil)) 0)))))
711 (go oloop))
712 ((equal sym 2)
713 (setq column (1- row))
714 (cond ((equal row 1) (go iloop)))
715 (setq symvector
716 (cons (nthcdr column vector) symvector)
717 vector (nreverse (mapcar 'car symvector))
718 symvector (mapcar 'cdr symvector))
719 (go iloop))
720 ((equal sym 3)
721 (setq column row)
722 (cond ((equal row 1) (setq vector (ncons 0)) (go iloop)))
723 (setq symvector
724 (cons (mapcar #'neg (nthcdr (1- column) vector))
725 symvector)
726 vector (nreconc (mapcar 'car symvector) (ncons 0))
727 symvector (mapcar 'cdr symvector))
728 (go iloop)))
729 (setq column 0 vector nil)
730 iloop (cond ((> (incf column) columns)
731 (setq matrix (nconc matrix (ncons vector)))
732 (go oloop)))
733 (let ((prompt (format nil "Row ~a Column ~a: " row column)))
734 (setq vector (nconc vector (ncons (meval (retrieve prompt nil))))))
735 (go iloop)))
737 (declare-top (special sn* sd* rsn*))
739 (defmfun $xthru (e)
740 (cond ((atom e) e)
741 ((mtimesp e) (muln (mapcar '$xthru (cdr e)) nil))
742 ((mplusp e) (simplify (comdenom (mapcar '$xthru (cdr e)) t)))
743 ((mexptp e) (power ($xthru (cadr e)) (caddr e)))
744 ((mbagp e) (cons (car e) (mapcar '$xthru (cdr e))))
745 (t e)))
747 (defun comdenom (l ind)
748 (prog (n d)
749 (prodnumden (car l))
750 (setq n (m*l sn*) sn* nil)
751 (setq d (m*l sd*) sd* nil)
752 loop (setq l (cdr l))
753 (cond ((null l)
754 (return (cond (ind (div* (cond (rsn* ($ratsimp n))
755 (t n))
757 (t (list n d))))))
758 (prodnumden (car l))
759 (setq d (comdenom1 n d (m*l sn*) (m*l sd*)))
760 (setq n (car d))
761 (setq d (cadr d))
762 (go loop)))
764 (defun prodnumden (e)
765 (cond ((atom e) (prodnd (list e)))
766 ((eq (caar e) 'mtimes) (prodnd (cdr e)))
767 (t (prodnd (list e)))))
769 (defun prodnd (l)
770 (prog (e)
771 (setq l (reverse l))
772 (setq sn* nil sd* nil)
773 loop (cond ((null l) (return nil)))
774 (setq e (car l))
775 (cond ((atom e) (setq sn* (cons e sn*)))
776 ((ratnump e)
777 (cond ((not (equal 1 (cadr e)))
778 (setq sn* (cons (cadr e) sn*))))
779 (setq sd* (cons (caddr e) sd*)))
780 ((and (eq (caar e) 'mexpt)
781 (mnegp (caddr e)))
782 (setq sd* (cons (power (cadr e)
783 (timesk -1 (caddr e)))
784 sd*)))
785 (t (setq sn* (cons e sn*))))
786 (setq l (cdr l))
787 (go loop)))
789 (defun comdenom1 (a b c d)
790 (prog (b1 c1)
791 (prodnumden (div* b d))
792 (setq b1 (m*l sn*) sn* nil)
793 (setq c1 (m*l sd*) sd* nil)
794 (return
795 (list (add2 (m* a c1) (m* c b1))
796 (mul2 d b1)))))
798 (declare-top (special ax
799 *mosesflag))
801 (defun xrutout (ax n m varl ind)
802 (let (($linsolve_params (and $backsubst $linsolve_params)))
803 (prog (ix imin ans zz m-1 sol tim chk zzz)
804 (setq ax (get-array-pointer ax) tim 0)
805 (if $linsolve_params (setq $%rnum_list (list '(mlist))))
806 (setq imin (min (setq m-1 (1- m)) n))
807 (setq ix (max imin (length varl)))
808 loop (if (zerop ix) (if ind (go out) (return (cons '(mlist) zz))))
809 (when (or (> ix imin) (equal (car (aref ax ix ix)) 0))
810 (setf (aref ax 0 ix)
811 (rform (if $linsolve_params (make-param) (ith varl ix))))
812 (if $linsolve_params (go saval) (go next)))
813 (setq ans (aref ax ix m))
814 (setf (aref ax ix m) nil)
815 (do ((j (1+ ix) (1+ j)))
816 ((> j m-1))
817 (setq ans (ratdif ans (rattimes (aref ax ix j) (aref ax 0 j) t)))
818 (setf (aref ax ix j) nil))
819 (setf (aref ax 0 ix) (ratquotient ans (aref ax ix ix)))
820 (setf (aref ax ix ix) nil)
821 (setq ans nil)
822 saval (push (cond (*mosesflag (aref ax 0 ix))
823 (t (list (if $globalsolve '(msetq) '(mequal))
824 (ith varl ix)
825 (simplify (rdis (aref ax 0 ix))))))
827 (if (not $backsubst)
828 (setf (aref ax 0 ix) (rform (ith varl ix))))
829 (and $globalsolve (meval (car zz)))
830 next (decf ix)
831 (go loop)
833 (cond ($dispflag (mtell (intl:gettext "Solution:~%"))))
834 (setq sol (list '(mlist)) chk (checklabel $linechar))
835 (do ((ll zz (cdr ll)))
836 ((null ll))
837 (setq zzz (car ll))
838 (setq zzz (list '(mlabel)
839 (progn
840 (if chk
841 (setq chk nil)
842 (incf $linenum))
843 (let (($nolabels nil))
844 (makelabel $linechar))
845 *linelabel*)
846 (setf (symbol-value *linelabel*) zzz)))
847 (nconc sol (ncons *linelabel*))
848 (cond ($dispflag
849 (setq tim (get-internal-run-time))
850 (mtell-open "~%~M" zzz)
851 (timeorg tim))
853 (putprop *linelabel* t 'nodisp))))
854 (return sol))))