Xmaxima: ~/.xmaximrc should probably be ~/.xmaximarc.
[maxima/cygwin.git] / src / hyp.lisp
blob6051656fee22ef2dddfb915151428548269bcc8a
1 ;;; -*- LISP -*-
2 ;;; ** (c) Copyright 1979 Massachusetts Institute of Technology **
4 (in-package :maxima)
6 ;;; References:
7 ;;;
8 ;;; Definite integration using the generalized hypergeometric functions
9 ;;; Avgoustis, Ioannis Dimitrios
10 ;;; Thesis. 1977. M.S.--Massachusetts Institute of Technology. Dept.
11 ;;; of Electrical Engineering and Computer Science
12 ;;; http://dspace.mit.edu/handle/1721.1/16269
13 ;;;
14 ;;; Avgoustis, I. D., Symbolic Laplace Transforms of Special Functions,
15 ;;; Proceedings of the 1977 MACSYMA Users' Conference, pp 21-41
17 (macsyma-module hyp)
19 (declare-top (special $true $false))
21 (declare-top (special var *par* checkcoefsignlist $exponentialize
22 $radexpand))
24 (defvar *debug-hyp* nil)
26 (defmvar $prefer_whittaker nil)
28 ;; When T give result in terms of gamma_incomplete and not gamma_incomplete_lower
29 (defmvar $prefer_gamma_incomplete nil)
31 ;; When NIL do not automatically expand polynomials as a result
32 (defmvar $expand_polynomials t)
34 (eval-when
35 #+gcl (eval compile)
36 #-gcl (:execute :compile-toplevel)
37 (defmacro simp (x) `(simplifya ,x ()))
39 ;; The macro MABS has been renamed to HYP-MABS in order to
40 ;; avoid conflict with the Maxima symbol MABS. The other
41 ;; M* macros defined here should probably be similarly renamed
42 ;; for consistency. jfa 03/27/2002
44 (defmacro hyp-mabs (x) `(simp `((mabs) ,,x)))
46 (defmacro msqrt (x) `(m^t ,x 1//2))
48 (defmacro mexpt (x) `(m^t '$%e ,x))
50 (defmacro mlog (x) `(simp `((%log) ,,x)))
52 (defmacro msin (x) `(simp `((%sin) ,,x)))
54 (defmacro mcos (x) `(simp `((%cos) ,,x)))
56 (defmacro masin (x) `(simp `((%asin) ,,x)))
58 (defmacro matan (x) `(simp `((%atan) ,,x)))
60 (defmacro mgamma (x) `(simp `((%gamma) ,,x)))
62 (defmacro mbinom (x y) `(simp `((%binomial) ,,x ,,y)))
64 (defmacro merf (x) `(simp `((%erf) ,,x)))
66 (defmacro =1//2 (x) `(alike1 ,x 1//2))
69 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
71 ;;; Functions moved from hypgeo.lisp to this place.
72 ;;; These functions are no longer used in hypgeo.lisp.
74 ;; Gamma function
75 (defun gm (expr)
76 (simplifya (list '(%gamma) expr) nil))
78 ;; sin(x)
79 (defun sin% (arg)
80 (list '(%sin) arg))
82 ;; cos(x)
83 (defun cos% (arg)
84 (list '(%cos) arg))
86 ;; Test if X is a number, either Lisp number or a maxima rational.
87 (defun nump (x)
88 (cond ((atom x)
89 (numberp x))
90 ((not (atom x))
91 (eq (caar (simplifya x nil)) 'rat))))
93 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
95 (defun hyp-integerp (x)
96 ;; In this file, maxima-integerp was used in many places. But it
97 ;; seems that this code expects maxima-integerp to return T when it
98 ;; is really an integer, not something that was declared an integer.
99 ;; But I'm not really sure if this is true everywhere, but it is
100 ;; true in some places.
102 ;; Thus, we replace all calls to maxima-integerp with hyp-integerp,
103 ;; which, for now, returns T only when the arg is an integer.
104 ;; Should we do something more?
105 (and (maxima-integerp x) (integerp x)))
107 ;; Main entry point for simplification of hypergeometric functions.
109 ;; F(a1,a2,a3,...;b1,b2,b3;z)
111 ;; L1 is a (maxima) list of an's, L2 is a (maxima) list of bn's.
112 (defmfun $hgfred (arg-l1 arg-l2 arg)
113 (flet ((arg-ok (a)
114 (and (listp a)
115 (eq (caar a) 'mlist))))
116 (unless (arg-ok arg-l1)
117 (merror (intl:gettext "hgfred: first argument must be a list; found: ~:M") arg-l1))
118 (unless (arg-ok arg-l2)
119 (merror (intl:gettext "hgfred: second argument must be a list; found: ~:M") arg-l2)))
121 ;; Do we really want $radexpand set to '$all? This is probably a
122 ;; bad idea in general, but we'll leave this in for now until we can
123 ;; verify find all of the code that does or does not need this and
124 ;; until we can verify all of the test cases are correct.
125 (let (;;($radexpand '$all)
126 (var arg)
127 (*par* arg)
128 (checkcoefsignlist nil))
129 (hgfsimp-exec (cdr arg-l1) (cdr arg-l2) arg)))
131 (defun hgfsimp-exec (arg-l1 arg-l2 arg)
132 (let* ((l1 (copy-tree arg-l1))
133 (l2 (copy-tree arg-l2))
134 ($exponentialize nil)
135 (res (hgfsimp l1 l2 arg)))
136 ;; I think hgfsimp returns FAIL and UNDEF for cases where it
137 ;; couldn't reduce the function.
138 (cond ((eq res 'fail)
139 (fpqform l1 l2 arg))
140 ((eq res 'undef)
141 '$und)
143 res))))
145 (defun hgfsimp (arg-l1 arg-l2 var)
146 (prog (resimp listcmdiff)
147 (setq arg-l1 (macsimp arg-l1)
148 arg-l2 (macsimp arg-l2)
149 resimp (simpg arg-l1 arg-l2))
150 (cond ((not (eq (and (consp resimp) (car resimp)) 'fail))
151 (return resimp))
152 ((and (not (zerop1 var)) ; Do not call splitfpq for a zero argument
153 (setq listcmdiff
154 (intdiffl1l2 (cadr resimp) (caddr resimp))))
155 (return (splitpfq listcmdiff
156 (cadr resimp)
157 (caddr resimp))))
159 (return (dispatch-spec-simp (cadr resimp)
160 (caddr resimp)))))))
162 (defun macsimp (expr)
163 (mapcar #'(lambda (index) ($expand index)) expr))
165 ;; Simplify the parameters. If L1 and L2 have common elements, remove
166 ;; them from both L1 and L2.
167 (defun simpg (arg-l1 arg-l2)
168 (let ((il (zl-intersection arg-l1 arg-l2)))
169 (cond ((null il)
170 (simpg-exec arg-l1 arg-l2))
172 (simpg-exec (del il arg-l1)
173 (del il arg-l2))))))
175 (defun del (a b)
176 (cond ((null a) b)
178 (del (cdr a) (delete (car a) b :count 1 :test #'equal)))))
180 ;; Handle the simple cases where the result is either a polynomial, or
181 ;; is undefined because we divide by zero.
182 (defun simpg-exec (arg-l1 arg-l2)
183 (let (n)
184 (cond ((zerop-in-l arg-l1)
185 ;; A zero in the first index means the series terminates
186 ;; after the first term, so the result is always 1.
188 ((setq n (hyp-negp-in-l arg-l1))
189 ;; A negative integer in the first series means we have a
190 ;; polynomial.
191 (create-poly arg-l1 arg-l2 n))
192 ((and (or (zerop-in-l arg-l2)
193 (hyp-negp-in-l arg-l2))
194 (every #'mnump arg-l1)
195 (every #'mnump arg-l2))
196 ;; A zero or negative number in the second index means we
197 ;; eventually divide by zero, so we're undefined. But only
198 ;; do this if both indices contain numbers. See Bug
199 ;; 1858964 for discussion.
200 'undef)
202 ;; We failed so more complicated stuff needs to be done.
203 (append (list 'fail) (list arg-l1) (list arg-l2))))))
205 (defun intdiffl1l2 (arg-l1 arg-l2)
206 (cond ((null arg-l1)
207 nil)
209 (intdiff arg-l1 arg-l2))))
211 ;; For each element x in arg-l1 and y in arg-l2, compute d = x - y.
212 ;; Find the smallest such non-negative integer d and return (list x
213 ;; d).
214 (defun intdiff (arg-l1 arg-l2)
215 (let ((result nil))
216 ;; Compute all possible differences between elements in arg-l1 and
217 ;; arg-l2. Only save the ones where the difference is a positive
218 ;; integer
219 (dolist (x arg-l1)
220 (dolist (y arg-l2)
221 (let ((diff (sub x y)))
222 (when (nni diff)
223 (push (list x diff) result)))))
224 ;; Find the smallest one and return it.
225 (let ((min (first result)))
226 (dolist (x (rest result))
227 (when (< (second x) (second min))
228 (setf min x)))
229 min)))
231 ;; Create the appropriate polynomial for the hypergeometric function.
232 (defun create-poly (arg-l1 arg-l2 n)
233 (let ((len1 (length arg-l1))
234 (len2 (length arg-l2)))
235 ;; n is the smallest (in magnitude) negative integer in L1. To
236 ;; make everything come out right, we need to make sure this value
237 ;; is first in L1. This is ok, the definition of the
238 ;; hypergeometric function does not depend on the order of values
239 ;; in L1.
240 (setf arg-l1 (cons n (remove n arg-l1 :count 1)))
241 (cond ((and (equal len1 2)
242 (equal len2 1))
243 (2f1polys arg-l1 arg-l2 n))
244 ((and (equal len1 1)
245 (equal len2 1))
246 (1f1polys arg-l2 n))
247 ((and (equal len1 2)
248 (zerop len2))
249 (2f0polys arg-l1 n))
250 (t (create-any-poly arg-l1 arg-l2 (mul -1 n))))))
252 (defun 1f1polys (arg-l2 n)
253 (let* ((c (car arg-l2))
254 (n (mul -1 n))
255 (fact1 (mul (power 2 n)
256 (take '(mfactorial) n)
257 (inv (power -1 n))))
258 ;; For all of the polynomials here, I think it's ok to
259 ;; replace sqrt(z^2) with z because when everything is
260 ;; expanded out they evaluate to exactly the same thing. So
261 ;; $radexpand $all is ok here.
262 (fact2 (let (($radexpand '$all))
263 (mul (power 2 '((rat simp) 1 2))
264 (power var '((rat simp) 1 2))))))
265 (cond ((alike1 c '((rat simp) 1 2))
266 ;; A&S 22.5.56
267 ;; hermite(2*n,x) = (-1)^n*(2*n)!/n!*M(-n,1/2,x^2)
269 ;; So
270 ;; M(-n,1/2,x) = n!/(2*n)!*(-1)^n*hermite(2*n,sqrt(x))
272 ;; But hermite(m,x) = 2^(m/2)*He(sqrt(2)*sqrt(x)), so
274 ;; M(-n,1/2,x) = (-1)^n*n!*2^n/(2*n)!*He(2*n,sqrt(2)*sqrt(x))
275 (mul fact1
276 (inv (take '(mfactorial) (add n n)))
277 (hermpol (add n n) fact2)))
278 ((alike1 c '((rat simp) 3 2))
279 ;; A&S 22.5.57
280 ;; hermite(2*n+1,x) = (-1)^n*(2*n+1)!/n!*M(-n,3/2,x^2)*2*x
282 ;; So
283 ;; M(-n,3/2,x) = n!/(2*n+1)!*(-1)^n*hermite(2*n+1,sqrt(x))/2/sqrt(x)
285 ;; and in terms of He, we get
287 ;; M(-n,3/2,x) = (-1)^n*n!*2^(n-1/2)/(2*n+1)!/sqrt(x)*He(2*n+1,sqrt(2)*sqrt(x))
288 (mul fact1
289 (inv (power 2 '((rat simp) 1 2)))
290 (inv (take '(mfactorial) (add n n 1)))
291 (hermpol (add n n 1) fact2)
292 ;; Similarly, $radexpand here is ok to convert sqrt(z^2) to z.
293 (let (($radexpand '$all))
294 (inv (power var '((rat simp) 1 2))))))
295 ((alike1 c (neg (add n n)))
296 ;; 1F1(-n; -2*n; z)
297 (mul (power -1 n)
298 (inv (take '(%binomial) (add n n) n))
299 (lagpol n (sub c 1) var)))
301 ;; A&S 22.5.54:
303 ;; gen_laguerre(n,alpha,x) =
304 ;; binomial(n+alpha,n)*hgfred([-n],[alpha+1],x);
306 ;; Or hgfred([-n],[alpha],x) =
307 ;; gen_laguerre(n,alpha-1,x)/binomial(n+alpha-1,n)
309 ;; But 1/binomial(n+alpha-1,n) = n!*(alpha-1)!/(n+alpha-1)!
310 ;; = n! / (alpha * (alpha + 1) * ... * (alpha + n - 1)
311 ;; = n! / poch(alpha, n)
313 ;; See Bug 1858939.
315 ;; However, if c is not a number leave the result in terms
316 ;; of gamma functions. I (rtoy) think this makes for a
317 ;; simpler result, especially if n is rather large. If the
318 ;; user really wants the answer expanded out, makefact and
319 ;; minfactorial will do that.
320 (if (and (integerp n)
321 (numberp c))
322 (if (or (zerop c)
323 (and (minusp c) (> c (- n))))
324 (merror (intl:gettext "hgfred: 1F1(~M; ~M; ~M) not defined.")
325 (- n) c var)
326 (mul (take '(mfactorial) n)
327 (inv (take '($pochhammer) c n))
328 (lagpol n (sub c 1) var)))
329 (let (($gamma_expand t)) ; Expand Gamma function
330 (mul (take '(mfactorial) n)
331 (take '(%gamma) c)
332 (inv (take '(%gamma) (add c n)))
333 (lagpol n (sub c 1) var))))))))
335 ;; Hermite polynomial. Note: The Hermite polynomial used here is the
336 ;; He polynomial, defined as (A&S 22.5.18, 22.5.19)
338 ;; He(n,x) = 2^(-n/2)*H(n,x/sqrt(2))
340 ;; or
342 ;; H(n,x) = 2^(n/2)*He(x*sqrt(2))
344 ;; We want to use H, as used in specfun, so we need to convert it.
345 (defun hermpol (n arg)
346 (let ((fact (inv (power 2 (div n 2))))
347 (x (mul arg (inv (power 2 '((rat simp) 1 2))))))
348 (mul fact
349 (if (and $expand_polynomials (integerp n))
350 (mfuncall '$hermite n x)
351 (list '($hermite simp) n x)))))
353 ;; Generalized Laguerre polynomial
354 (defun lagpol (n a arg)
355 (if (and (numberp a) (zerop a))
356 (if (and $expand_polynomials (integerp n))
357 (mfuncall '$laguerre n arg)
358 (list '($laguerre simp) n arg))
359 (if (and $expand_polynomials (integerp n))
360 (mfuncall '$gen_laguerre n a arg)
361 (list '($gen_laguerre simp) n a arg))))
363 (defun 2f0polys (arg-l1 n)
364 (let ((a (car arg-l1))
365 (b (cadr arg-l1)))
366 (when (alike1 (sub b a) '((rat simp) -1 2))
367 (rotatef a b))
368 (cond ((alike1 (sub b a) '((rat simp) 1 2))
369 ;; 2F0(-n,-n+1/2,z) or 2F0(-n-1/2,-n,z)
370 (interhermpol n a b var))
372 ;; 2F0(a,b;z)
373 (let ((x (mul -1 (inv var)))
374 (order (mul -1 n)))
375 (mul (take '(mfactorial) order)
376 (inv (power x order))
377 (inv (power -1 order))
378 (lagpol order (mul -1 (add b order)) x)))))))
380 ;; Compute 2F0(-n,-n+1/2;z) and 2F0(-n-1/2,-n;z) in terms of Hermite
381 ;; polynomials.
383 ;; Ok. I couldn't find any references giving expressions for this, so
384 ;; here's a quick derivation.
386 ;; 2F0(-n,-n+1/2;z) = sum(pochhammer(-n,k)*pochhammer(-n+1/2,k)*z^k/k!, k, 0, n)
388 ;; It's easy to show pochhammer(-n,k) = (-1)^k*n!/(n-k)!
389 ;; Also, it's straightforward but tedious to show that
390 ;; pochhammer(-n+1/2,k) = (-1)^k*(2*n)!*(n-k)!/2^(2*k)/n!/(2*n-2*k)!
392 ;; Thus,
393 ;; 2F0 = (2*n)!*sum(z^k/2^(2*k)/k!/(2*n-2*k)!)
395 ;; Compare this to the expression for He(2*n,x) (A&S 22.3.11):
397 ;; He(2*n,x) = (2*n)! * x^(2*n) * sum((-1)^k*x^(-2*k)/2^k/k!/(2*n-2*k)!)
399 ;; Hence,
401 ;; 2F0(-n,-n+1/2;z) = y^n * He(2*n,y)
403 ;; where y = sqrt(-2/x)
405 ;; For 2F0(-n-1/2,-n;z) = sum(pochhammer(-n,k)*pochhammer(-n-1/2,k)*z^k/k!)
406 ;; we find that
408 ;; pochhammer(-n-1/2,k) = pochhammer(-(n+1)+1/2,k)
409 ;; =
411 ;; So 2F0 = (2*n+1)!*sum(z^k/z^(2*k)/k!/(2*n+1-2*k)!)
413 ;; and finally
415 ;; 2F0(-n-1/2,-n;z) = y^(2*n+1) * He(2*n+1,y)
417 ;; with y as above.
418 (defun interhermpol (n a b x)
419 (let ((arg (power (div 2 (mul -1 x)) '((rat simp) 1 2)))
420 (order (cond ((alike1 a n)
421 (mul -2 n))
422 ((alike1 b n)
423 (sub 1 (add n n))))))
424 ;; 2F0(-n,-n+1/2;z) = y^(-2*n)*He(2*n,y)
425 ;; 2F0(-n-1/2,-n;z) = y^(-(2*n+1))*He(2*n+1,y)
427 ;; where y = sqrt(-2/var);
428 (mul (power arg (mul -1 order))
429 (hermpol order arg))))
431 ;; F(n,b;c;z), where n is a negative integer (number or symbolic).
432 ;; The order of the arguments must be checked by the calling routine.
433 (defun 2f1polys (arg-l1 arg-l2 n)
434 (prog (l v lgf)
435 ;; Since F(a,b;c;z) = F(b,a;c;z), make sure L1 has the negative
436 ;; integer first, so we have F(-n,d;c;z)
437 ;; Remark: 2f1polys is only called from create-poly. create-poly calls
438 ;; 2f1polys with the correct order of arg-l1. This test is not necessary.
439 ; (cond ((not (alike1 (car arg-l1) n))
440 ; (setq arg-l1 (reverse arg-l1))))
442 (cond ((mnump *par*)
443 ;; The argument of the hypergeometric function is a number.
444 ;; Avoid the following check which does not work for this case.
445 (setq v (div (add (cadr arg-l1) n) 2)))
447 ;; Check if (b+n)/2 is free of the argument.
448 ;; At this point of the code there is no check of the return value
449 ;; of vfvp. When nil we have no solution and the result is wrong.
450 (setq l (vfvp (div (add (cadr arg-l1) n) 2)))
451 (setq v (cdr (assoc 'v l :test #'equal)))))
453 (cond ((and (or (not (integerp n))
454 (not $expand_polynomials))
455 ;; Assuming we have F(-n,b;c;z), then v is (b+n)/2.
456 ;; See if it can be a Legendre function.
457 ;; We call legpol-core because we know that arg-l1 has the
458 ;; correct order. This avoids questions about the parameter b
459 ;; from legpol-core, because legpol calls legpol-core with
460 ;; both order of arguments.
461 (setq lgf (legpol-core (car arg-l1)
462 (cadr arg-l1)
463 (car arg-l2))))
464 (return lgf))
465 ((and (or (not (integerp n))
466 (not $expand_polynomials))
467 (alike1 (sub (car arg-l2) v) '((rat simp) 1 2)))
468 ;; A&S 15.4.5:
469 ;; F(-n, n+2*a; a+1/2; x) = n!*gegen(n, a, 1-2*x)/pochhammer(2*a,n)
471 ;; So v = a, and (car arg-l2) = a + 1/2.
472 (return (mul
473 (cond ((zerop1 v) 1)
474 (t (mul (take '(mfactorial) (mul -1 n))
475 (inv (take '($pochhammer)
476 (mul 2 v)
477 (mul -1 n))))))
478 (gegenpol (mul -1 n)
480 (sub 1 (mul 2 *par*))))))
482 ;; A&S 15.4.6 says
483 ;; F(-n, n + a + 1 + b; a + 1; x)
484 ;; = n!*jacobi_p(n,a,b,1-2*x)/pochhammer(a+1,n)
485 (return (mul (take '(mfactorial) (mul -1 n))
486 (inv (take '($pochhammer) (car arg-l2) (mul -1 n)))
487 (jacobpol (mul -1 n)
488 (add (car arg-l2) -1)
489 (sub (mul 2 v) (car arg-l2))
490 (sub 1 (mul 2 *par*)))))))))
492 ;; Jacobi polynomial
493 (defun jacobpol (n a b x)
494 (if (and $expand_polynomials (integerp n))
495 (mfuncall '$jacobi_p n a b x)
496 (list '($jacobi_p simp) n a b x)))
498 ;; Gegenbauer (Ultraspherical) polynomial. We use ultraspherical to
499 ;; match specfun.
500 (defun gegenpol (n v x)
501 (cond ((equal v 0) (tchebypol n x))
503 (if (and $expand_polynomials (integerp n))
504 (mfuncall '$ultraspherical n v x)
505 `(($ultraspherical simp) ,n ,v ,x)))))
507 ;; Legendre polynomial
508 (defun legenpol (n x)
509 (if (and $expand_polynomials (integerp n))
510 (mfuncall '$legendre_p n x)
511 `(($legendre_p simp) ,n ,x)))
513 ;; Chebyshev polynomial
514 (defun tchebypol (n x)
515 (if (and $expand_polynomials (integerp n))
516 (mfuncall '$chebyshev_t n x)
517 `(($chebyshev_t simp) ,n ,x)))
519 ;; Expand the hypergeometric function as a polynomial. No real checks
520 ;; are made to ensure the hypergeometric function reduces to a
521 ;; polynomial.
522 (defmfun $hgfpoly (arg-l1 arg-l2 arg)
523 (let ((var arg)
524 (*par* arg)
525 (n (hyp-negp-in-l (cdr arg-l1))))
526 (create-any-poly (cdr arg-l1) (cdr arg-l2) (- n))))
528 (defun create-any-poly (arg-l1 arg-l2 n)
529 (prog (result exp prodnum proden)
530 (setq result 1 prodnum 1 proden 1 exp 1)
531 loop
532 (cond ((zerop n) (return result)))
533 (setq prodnum (mul prodnum (mull arg-l1))
534 proden (mul proden (mull arg-l2)))
535 (setq result
536 (add result
537 (mul prodnum
538 (power var exp)
539 (inv proden)
540 (inv (factorial exp)))))
541 (setq n (sub n 1)
542 exp (add exp 1)
543 arg-l1 (incr1 arg-l1)
544 arg-l2 (incr1 arg-l2))
545 (go loop)))
547 ;; Compute the product of the elements of the list L.
548 (defun mull (l)
549 (reduce #'mul l :initial-value 1))
551 ;; Add 1 to each element of the list L
552 (defun incr1 (l)
553 (mapcar #'(lambda (x) (add x 1)) l))
555 ;; Figure out the orders of generalized hypergeometric function we
556 ;; have and call the right simplifier.
557 (defun dispatch-spec-simp (arg-l1 arg-l2)
558 (let ((len1 (length arg-l1))
559 (len2 (length arg-l2)))
560 (cond ((and (< len1 2)
561 (< len2 2))
562 ;; pFq where p and q < 2.
563 (simp2>f<2 arg-l1 arg-l2 len1 len2))
564 ((and (equal len1 2)
565 (equal len2 1))
566 ;; 2F1
567 (simp2f1 arg-l1 arg-l2))
568 ((and (equal len1 2)
569 (equal len2 0))
570 ;; 2F0(a,b; ; z)
571 (cond ((and (maxima-integerp (car arg-l1))
572 (member ($sign (car arg-l1)) '($neg $nz)))
573 ;; 2F0(-n,b; ; z), n a positive integer
574 (2f0polys arg-l1 (car arg-l1)))
575 ((and (maxima-integerp (cadr arg-l1))
576 (member ($sign (cadr arg-l1)) '($neg $nz)))
577 ;; 2F0(a,-n; ; z), n a positive integer
578 (2f0polys (reverse arg-l1) (cadr arg-l1)))
580 (fpqform arg-l1 arg-l2 var))))
582 ;; We don't have simplifiers for any other hypergeometric
583 ;; function.
584 (fpqform arg-l1 arg-l2 var)))))
586 ;; Handle the cases where the number of indices is less than 2.
587 (defun simp2>f<2 (arg-l1 arg-l2 len1 len2)
588 (cond ((and (zerop len1) (zerop len2))
589 ;; hgfred([],[],z) = e^z
590 (power '$%e var))
591 ((and (zerop len1) (equal len2 1))
592 (cond
593 ((zerop1 var)
594 ;; hgfred([],[b],0) = 1
595 (add var 1))
597 ;; hgfred([],[b],z)
599 ;; The hypergeometric series is then
601 ;; 1+sum(z^k/k!/[b*(b+1)*...(b+k-1)], k, 1, inf)
603 ;; = 1+sum(z^k/k!*gamma(b)/gamma(b+k), k, 1, inf)
604 ;; = sum(z^k/k!*gamma(b)/gamma(b+k), k, 0, inf)
605 ;; = gamma(b)*sum(z^k/k!/gamma(b+k), k, 0, inf)
607 ;; Note that bessel_i(b,z) has the series
609 ;; (z/2)^(b)*sum((z^2/4)^k/k!/gamma(b+k+1), k, 0, inf)
611 ;; bessel_i(b-1,2*sqrt(z))
612 ;; = (sqrt(z))^(b-1)*sum(z^k/k!/gamma(b+k),k,0,inf)
613 ;; = z^((b-1)/2)*hgfred([],[b],z)/gamma(b)
615 ;; So this hypergeometric series is a Bessel I function:
617 ;; hgfred([],[b],z) = bessel_i(b-1,2*sqrt(z))*z^((1-b)/2)*gamma(b)
618 (bestrig (car arg-l2) var))))
619 ((zerop len2)
620 ;; hgfred([a],[],z) = 1 + sum(binomial(a+k,k)*z^k) = 1/(1-z)^a
621 (power (sub 1 var) (mul -1 (car arg-l1))))
623 ;; The general case of 1F1, the confluent hypergeomtric function.
624 (confl arg-l1 arg-l2 var))))
626 ;; Computes
628 ;; bessel_i(a-1,2*sqrt(x))*gamma(a)*x^((1-a)/2)
630 ;; if x > 0
632 ;; or
634 ;; bessel_j(a-1,2*sqrt(x))*gamma(a)*x^((1-a)/2)
636 ;; if x < 0.
638 ;; If a is half of an odd integer and small enough, the Bessel
639 ;; functions are expanded in terms of trig or hyperbolic functions.
641 (defun bestrig (b x)
642 ;; I think it's ok to have $radexpand $all here so that sqrt(z^2) is
643 ;; converted to z.
644 (let (($radexpand '$all))
645 (if (mminusp x)
646 ;; gamma(b)*(-x)^((1-b)/2)*bessel_j(b-1,2*sqrt(-x))
647 (sratsimp (mul (power (neg x) (div (sub 1 b) 2))
648 (take '(%gamma) b)
649 (take '(%bessel_j)
650 (sub b 1)
651 (mul 2 (power (neg x) '((rat simp) 1 2))))))
652 ;; gamma(b)*(x)^((1-b)/2)*bessel_i(b-1,2*sqrt(x))
653 (sratsimp (mul (power x (div (sub 1 b) 2))
654 (take '(%gamma) b)
655 (take '(%bessel_i)
656 (sub b 1)
657 (mul 2 (power x '((rat simp) 1 2)))))))))
659 ;; Kummer's transformation. A&S 13.1.27
661 ;; M(a,b,z) = e^z*M(b-a,b,-z)
662 (defun kummer (arg-l1 arg-l2)
663 (mul (list '(mexpt) '$%e var)
664 (confl (list (sub (car arg-l2) (car arg-l1)))
665 arg-l2 (mul -1 var))))
667 ;; Return non-NIL if any element of the list L is zero.
669 (defun zerop-in-l (l)
670 (some #'(lambda (x)
671 (and (numberp x) (zerop x)))
674 ;; If the list L contains a negative integer, return the most positive
675 ;; of the negative integers. Otherwise return NIL.
676 (defun hyp-negp-in-l (l)
677 (let ((max-neg nil))
678 (dolist (x l)
679 (when (and (integerp x) (minusp x))
680 (if max-neg
681 (setf max-neg (max max-neg x))
682 (setf max-neg x))))
683 max-neg))
685 ;; Compute the intersection of L1 and L2, possibly destructively
686 ;; modifying L2. Perserves duplications in L1.
687 (defun zl-intersection (arg-l1 arg-l2)
688 (cond ((null arg-l1) nil)
689 ((member (car arg-l1) arg-l2 :test #'equal)
690 (cons (car arg-l1)
691 (zl-intersection (cdr arg-l1)
692 (delete (car arg-l1) arg-l2 :count 1 :test #'equal))))
693 (t (zl-intersection (cdr arg-l1) arg-l2))))
695 ;; Whittaker M function. A&S 13.1.32 defines it as
697 ;; %m[k,u](z) = exp(-z/2)*z^(u+1/2)*M(1/2+u-k,1+2*u,z)
699 ;; where M is the confluent hypergeometric function.
700 (defun whitfun (k m var)
701 (list '(mqapply) (list '($%m array) k m) var))
703 (defvar $trace2f1 nil
704 "Enables simple tracing of simp2f1 so you can see how it decides
705 what approach to use to simplify hypergeometric functions")
707 (defun simp2f1 (arg-l1 arg-l2)
708 (prog (a b c lgf)
709 (setq a (car arg-l1) b (cadr arg-l1) c (car arg-l2))
711 (cond ((zerop1 var)
712 ;; F(a,b; c; 0) = 1
713 (return (add var 1))))
715 (when $trace2f1
716 (format t "Tracing SIMP2F1~%")
717 (format t " Test a or b negative integer ...~%"))
719 ;; Look if a or b is a symbolic negative integer. The routine
720 ;; 2f1polys handles this case.
721 (cond ((and (maxima-integerp a) (member ($sign a) '($neg $nz)))
722 (return (2f1polys arg-l1 arg-l2 a))))
723 (cond ((and (maxima-integerp b) (member ($sign b) '($neg $nz)))
724 (return (2f1polys (list b a) arg-l2 b))))
726 (when $trace2f1
727 (format t " Test F(1,1,2)...~%"))
729 (cond ((and (alike1 a 1)
730 (alike1 b 1)
731 (alike1 c 2))
732 ;; F(1,1;2;z) = -log(1-z)/z, A&S 15.1.3
733 (when $trace2f1
734 (format t " Yes~%"))
735 (return (mul (inv (mul -1 var))
736 (take '(%log) (add 1 (mul -1 var)))))))
738 (when $trace2f1
739 (format t " Test c = 1/2 or c = 3/2...~%"))
741 (cond ((or (alike1 c '((rat simp) 3 2))
742 (alike1 c '((rat simp) 1 2)))
743 ;; F(a,b; 3/2; z) or F(a,b;1/2;z)
744 (cond ((setq lgf (trig-log (list a b) (list c)))
745 (when $trace2f1
746 (format t " Yes: trig-log~%"))
747 (return lgf)))))
749 (when $trace2f1
750 (format t " Test |a-b|=1/2...~%"))
752 (cond ((or (alike1 (sub a b) '((rat simp) 1 2))
753 (alike1 (sub b a) '((rat simp) 1 2)))
754 ;; F(a,b;c;z) where |a-b|=1/2
755 (cond ((setq lgf (hyp-cos a b c))
756 (when $trace2f1
757 (format t " Yes: hyp-cos~%"))
758 (return lgf)))))
760 (when $trace2f1
761 (format t " Test a,b are integers, c is a numerical integer...~%"))
763 (cond ((and (maxima-integerp a)
764 (maxima-integerp b)
765 (hyp-integerp c))
766 ;; F(a,b;c;z) when a, and b are integers (or are declared
767 ;; to be integers) and c is a integral number.
768 (setf lgf (simpr2f1 (list a b) (list c)))
769 (unless (symbolp lgf) ; Should be more specific! (DK 01/2010)
770 (when $trace2f1
771 (format t " Yes: simpr2f1~%"))
772 (return lgf))))
774 (when $trace2f1
775 (format t " Test a+b and c+1/2 are numerical integers...~%"))
777 (cond ((and (hyp-integerp (add c (inv 2)))
778 (hyp-integerp (add a b)))
779 ;; F(a,b;c;z) where a+b is an integer and c+1/2 is an
780 ;; integer.
781 (when $trace2f1
782 (format t " Yes: step4~%"))
783 (return (step4 a b c))))
785 (when $trace2f1
786 (format t " Test a-b+1/2 is a numerical integer...~%"))
788 (cond ((hyp-integerp (add (sub a b) (inv 2)))
789 ;; F(a,b;c,z) where a-b+1/2 is an integer
790 (cond ((setq lgf (step7 a b c))
791 (unless (atom lgf)
792 (when $trace2f1
793 (format t " Yes: step7~%"))
794 (return lgf))))))
796 #+nil
797 (when (and (hyp-integerp (add c 1//2))
798 (or (and (hyp-integerp (add a 1//2))
799 (hyp-integerp b))
800 (and (hyp-integerp (add b 1//2))
801 (hyp-integerp a))))
802 (when $trace2f1
803 (format t " Test for atanh: a+1/2, b, and c+1/2 are integers~%"))
804 (return (hyp-atanh a b c)))
806 (when (hyp-integerp (add c 1//2))
807 (when $trace2f1
808 (format t " Test for atanh: c+1/2 is an integer~%"))
809 (cond ((and (hyp-integerp (add a 1//2))
810 (hyp-integerp b))
811 (when $trace2f1
812 (format t " atanh with integers a+1/2 and b ~%"))
813 (return (hyp-atanh a b c)))
814 ((and (hyp-integerp (add b 1//2))
815 (hyp-integerp a))
816 (when $trace2f1
817 (format t " atanh with integers a and b+1/2 ~%"))
818 (return (hyp-atanh b a c)))))
820 (when $trace2f1
821 (format t " Test for Legendre function...~%"))
823 (cond ((setq lgf (legfun a b c))
824 (unless (atom lgf)
825 ;; LEGFUN returned something interesting, so we're done.
826 (when $trace2f1
827 (format t " Yes: case 1~%"))
828 (return lgf))
829 ;; LEGFUN didn't return anything, so try it with the args
830 ;; reversed, since F(a,b;c;z) is F(b,a;c;z).
831 (setf lgf (legfun b a c))
832 (when lgf
833 (when $trace2f1
834 (format t " Yes: case 2~%"))
835 (return lgf))))
837 (when $trace2f1
838 (format t "'simp2f1-will-continue-in~%"))
839 (return (fpqform arg-l1 arg-l2 var))))
841 ;; As best as I (rtoy) can tell, step7 is meant to handle an extension
842 ;; of hyp-cos, which handles |a-b|=1/2 and either a+b-1/2 = c or
843 ;; c=a+b+1/2.
845 ;; Based on the code, step7 wants a = s + m/n and c = 2*s+k/l. For
846 ;; hyp-cos, we want c-2*a to be a integer. Or k/l-2*m/n is an
847 ;; integer.
848 #+(or)
849 (progn
850 (defun step7 (a b c)
851 (prog (l m n k mn kl sym sym1 r)
852 ;; Write a = sym + mn, c = sym1 + kl
853 (setq l (s+c a)
854 sym (cdras 'f l)
855 mn (cdras 'c l)
856 l (s+c c)
857 syrm1 (cdras 'f l))
858 ;; We only handle the case where 2*sym = sym1.
859 (cond ((not (equal (mul sym 2) sym1))
860 (return nil)))
861 (setq kl (cdras 'c l))
862 ;; a-b+1/2 is an integer.
863 (setq l (s+c b)
864 r (sub (add (inv 2) (cdras 'c l)) mn)
865 m ($num mn)
866 n ($denom mn)
867 k ($num kl)
868 l ($denom kl))
869 ;; We have a = m*s+m/n, c = 2*m*s+k/l.
870 (cond ((equal (* 2 l) n)
871 (cond ((hyp-integerp (/ (- k m) n))
872 (return (hyp-algv k l m n a b c))))))
873 (cond ((hyp-integerp (/ k (* 2 l)))
874 (cond ((hyp-integerp (/ m n))
875 (return (hyp-algv k l m n a b c)))
876 (t (return nil))))
877 ((hyp-integerp (/ m n))
878 (return nil))
879 ((hyp-integerp (/ (- (* k n) (* 2 l m)) (* 2 l n)))
880 (return (hyp-algv k l m n a b c))))
881 (return nil)))
883 (defun getxy (k l m n)
884 (prog (x y)
885 (setq y 0)
886 loop
887 (cond ((hyp-integerp (setq x (truncate (+ y
888 (truncate k l)
889 (* -2 (quot m n)))
890 2)))
891 (return (list x y))))
892 (incf y 2)
893 (go loop)))
895 (defun hyp-algv (k l m n a b c)
896 (prog (x y xy a-b w)
897 (setq a-b (sub a b))
898 (setq xy (getxy k l m n)
899 x (car xy)
900 y (cadr xy))
901 (cond ((< x 0)(go out)))
902 (cond ((< x y)
903 (cond ((< (add a-b x (inv 2)) 0)
904 (return (f88 x y a b c fun)))
905 (t (return (f87 x y a c fun)))))
907 (cond ((< (add a-b x (inv 2)) 0)
908 (return (f90 x y a c fun)))
909 (t (return (f89 x y a c fun))))))
911 (setq w (* x -1))
912 (cond ((< (- (add a-b (inv 2)) w) 0)
913 (return (f92 x y a c fun)))
914 (t (return (f91 x y a c fun))))))
916 (defun f87 (x y a c fun )
917 (mul
918 (inv (mul (factf c y)
919 (factf (sub (add c y) (add a x)) (- x y))
920 (factf (sub (add c y) (add a x (inv 2)))
921 (sub (add a x (inv 2)) (add a (inv 2))))))
922 (power 'ell (sub 1 c))
923 (power (sub 1 'ell)(sub (add y c) (add a (inv 2))))
924 ($diff (mul (power 'ell (add a x))
925 (power (sub 1 'ell)(mul -1 a))
926 ($diff (mul (power 'ell (sub (add (inv 2) x) y))
927 ($diff (mul (power 'ell (sub (add c y) 1))
928 (power (sub 1 'ell)
929 (sub (add (inv 2)
930 (mul 2 a)
931 (* 2 x))
932 (add c y)))
933 fun)
934 'ell x))
935 'ell (- x y)))
936 'ell y)))
938 (defun f88 (x y a b c fun )
939 (mul
940 (inv (mul (factf c y)
941 (factf (sub (add c y) (add a x)) (- x y))
942 (factf (add a (inv 2) x)
943 (sub b (sub x (sub a (inv 2)))))))
944 (power 'ell (sub 1 c))
945 (power (sub 1 'ell)(sub (add y c) (add a (inv 2))))
946 ($diff (mul (power 'ell (add a x))
947 (power (sub 1 'ell)(mul -1 a))
948 ($diff (mul (power 'ell (sub c (sub x (sub (inv 2) (mul a 2))))))
949 (power (sub 1 'ell) (sub (add a x b)(sub c y)))
950 ($diff (mul (power 'ell (sub b 1 ))
952 fun)
953 'ell (sub b (sub a (sub x (inv 2)))))
954 'ell (- x y)))
955 'ell y)))
958 ;; A new version of step7.
959 (defun step7 (a b c)
960 ;; To get here, we know that a-b+1/2 is an integer. To make further
961 ;; progress, we want a+b-1/2-c to be an integer too.
963 ;; Let a-b+1/2 = p and a+b+1/2-c = q where p and q are (numerical)
964 ;; integers.
966 ;; A&S 15.2.3 and 15.2.5 allows us to increase or decrease a. Then
967 ;; F(a,b;c;z) can be written in terms of F(a',b;c;z) where a' = a-p
968 ;; and a'-b = a-p-b = 1/2. Also, a'+b+1/2-c = a-p+b+1/2-c = q-p =
969 ;; r, which is also an integer.
971 ;; A&S 15.2.4 and 15.2.6 allows us to increase or decrese c. Thus,
972 ;; we can write F(a',b;c;z) in terms of F(a',b;c',z) where c' =
973 ;; c+r. Now a'-b=1/2 and a'+b+1/2-c' = a-p+b+1/2-c-r =
974 ;; a+b+1/2-c-p-r = q-p-(q-p)=0.
976 ;; Thus F(a',b;c';z) is exactly the form we want for hyp-cos. In
977 ;; fact, it's A&S 15.1.14: F(a,a+1/2,;1+2a;z) =
978 ;; 2^(2*a)*(1+sqrt(1-z))^(-2*a).
979 (declare (special var))
980 (let ((q (sub (add a b (inv 2))
981 c)))
982 (unless (hyp-integerp q)
983 ;; Wrong form, so return NIL
984 (return-from step7 nil))
985 ;; Since F(a,b;c;z) = F(b,a;c;z), we need to figure out which form
986 ;; to use. The criterion will be the fewest number of derivatives
987 ;; needed.
988 (let* ((p1 (add (sub a b) (inv 2)))
989 (r1 (sub q p1))
990 (p2 (add (sub b a) (inv 2)))
991 (r2 (sub q p2)))
992 (when $trace2f1
993 (format t "step 7:~%")
994 (format t " q, p1, r1 = ~A ~A ~A~%" q p1 r1)
995 (format t " p2, r2 = ~A ~A~%" p2 r2))
996 (cond ((<= (+ (abs p1) (abs r1))
997 (+ (abs p2) (abs r2)))
998 (step7-core a b c))
1000 (step7-core b a c))))))
1002 (defun step7-core (a b c)
1003 (let* ((p (add (sub a b) (inv 2)))
1004 (q (sub (add a b (inv 2))
1006 (r (sub q p))
1007 (a-prime (sub a p))
1008 (c-prime (add 1 (mul 2 a-prime))))
1009 ;; Ok, p and q are integers. We can compute something. There are
1010 ;; four cases to handle depending on the sign of p and r.
1012 ;; We need to differentiate some hypergeometric forms, so use 'ell
1013 ;; as the variable.
1014 (let ((fun (hyp-cos a-prime (add a-prime 1//2) c-prime 'ell)))
1015 ;; fun is F(a',a'+1/2;2*a'+1;z), or NIL
1016 (when fun
1017 (when $trace2f1
1018 (format t "step7-core~%")
1019 (format t " a,b,c = ~A ~A ~A~%" a b c)
1020 (format t " p,q,r = ~A ~A ~A~%" p q r)
1021 (format t " a', c' = ~A ~A~%" a-prime c-prime)
1022 (format t " F(a',a'+1/2; 1+2*a';z) =~%")
1023 (maxima-display fun))
1024 ;; Compute the result, and substitute the actual argument into
1025 ;; result.
1026 (maxima-substitute var 'ell
1027 (cond ((>= p 0)
1028 (cond ((>= r 0)
1029 (step-7-pp a-prime b c-prime p r 'ell fun))
1031 (step-7-pm a-prime b c-prime p r 'ell fun))))
1033 (cond ((>= r 0)
1034 (step-7-mp a-prime b c-prime p r 'ell fun))
1036 (step-7-mm a-prime b c-prime p r 'ell fun))))))))))
1038 ;; F(a,b;c;z) in terms of F(a',b;c';z)
1040 ;; F(a'+p,b;c'-r;z) where p >= 0, r >= 0.
1041 (defun step-7-pp (a b c p r z fun)
1042 ;; Apply A&S 15.2.4 and 15.2.3
1043 (let ((res (as-15.2.4 a b c r z fun)))
1044 (as-15.2.3 a b (sub c r) p z res)))
1046 ;; p >= 0, r < 0
1048 ;; Let r' = -r
1049 ;; F(a'+p,b;c'-r;z) = F(a'+p,b;c'+r';z)
1050 (defun step-7-pm (a b c p r z fun)
1051 ;; Apply A&S 15.2.6 and 15.2.3
1052 (let ((res (as-15.2.6 a b c (- r) z fun)))
1053 (as-15.2.3 a b (sub c r) p z res)))
1055 ;; p < 0, r >= 0
1057 ;; Let p' = -p
1058 ;; F(a'+p,b;c'-r;z) = F(a'-p',b;c'-r;z)
1059 (defun step-7-mp (a b c p r z fun)
1060 ;; Apply A&S 15.2.4 and 15.2.5
1061 (let ((res (as-15.2.4 a b c r z fun)))
1062 (as-15.2.5 a b (sub c r) (- p) z res)))
1064 ;; p < 0 r < 0
1066 ;; Let p' = - p, r' = -r
1068 ;; F(a'+p,b;c'-r;z) = F(a'-p',b;c'+r';z)
1069 (defun step-7-mm (a b c p r z fun)
1070 ;; Apply A&S 15.2.6 and A&S 15.2.5
1071 (let ((res (as-15.2.6 a b c (- r) z fun)))
1072 (as-15.2.5 a b (sub c r) (- p) z res)))
1074 ;; F(a,b;c;z) when a and b are integers (or declared to be integers)
1075 ;; and c is an integral number.
1076 (defun simpr2f1 (arg-l1 arg-l2)
1077 (destructuring-bind (a b)
1078 arg-l1
1079 (destructuring-bind (c)
1080 arg-l2
1081 (let ((inl1p (hyp-integerp a))
1082 (inl1bp (hyp-integerp b))
1083 (inl2p (hyp-integerp c)))
1084 (cond (inl2p
1085 ;; c is an integer
1086 (cond ((and inl1p inl1bp)
1087 ;; a, b, c are (numerical) integers
1088 (derivint a b c))
1089 (inl1p
1090 ;; a and c are integers
1091 (geredno2 b a c))
1092 (inl1bp
1093 ;; b and c are integers.
1094 (geredno2 a b c))
1095 (t 'fail1)))
1096 ;; Can't really do anything else if c is not an integer.
1097 (inl1p
1098 (cond (inl1bp
1101 'c)))
1102 ((eq (caaar arg-l1) 'rat)
1103 ;; How do we ever get here?
1104 (cond (inl1bp
1107 'd)))
1109 'failg))))))
1111 (defun geredno1
1112 (arg-l1 arg-l2)
1113 (cond ((and (> (car arg-l2)(car arg-l1))
1114 (> (car arg-l2)(cadr arg-l1)))
1115 (geredf (car arg-l1)(cadr arg-l1)(car arg-l2)))
1116 (t (gered1 arg-l1 arg-l2 #'hgfsimp))))
1118 (defun geredno2 (a b c)
1119 (cond ((> c b) (geredf b a c))
1120 (t (gered2 a b c))))
1122 ;; Consider F(1,1;2;z). A&S 15.1.3 says this is equal to -log(1-z)/z.
1124 ;; Apply A&S 15.2.2:
1126 ;; diff(F(1,1;2;z),z,ell) = poch(1,ell)*poch(1,ell)/poch(2,ell)*F(1+ell,1+ell;2+ell;z)
1128 ;; A&S 15.2.7 says:
1130 ;; diff((1-z)^(m+ell)*F(1+ell;1+ell;2+ell;z),z,m)
1131 ;; = (-1)^m*poch(1+ell,m)*poch(1,m)/poch(2+ell,m)*(1-z)^ell*F(1+ell+m,1+ell;2+ell+m;z)
1133 ;; A&S 15.2.6 gives
1135 ;; diff((1-z)^ell*F(1+ell+m,1+ell;2+ell+m;z),z,n)
1136 ;; = poch(1,n)*poch(1+m,n)/poch(2+ell+m,n)*(1-z)^(ell-n)*F(1+ell+m,1+ell;2+ell+m+n;z)
1138 ;; The derivation above assumes that ell, m, and n are all
1139 ;; non-negative integers. Thus, F(a,b;c;z), where a, b, and c are
1140 ;; integers and a <= b <= c, can be written in terms of F(1,1;2;z).
1141 ;; The result also holds for b <= a <= c, of course.
1143 ;; Also note that the last two differentiations can be combined into
1144 ;; one differention since the result of the first is in exactly the
1145 ;; form required for the second. The code below does one
1146 ;; differentiation.
1148 ;; So if a = 1+ell, b = 1+ell+m, and c = 2+ell+m+n, we have ell = a-1,
1149 ;; m = b - a, and n = c - ell - m - 2 = c - b - 1.
1151 (defun derivint (a b c)
1152 (if (> a b)
1153 (derivint b a c)
1154 (let ((l (- a 1))
1155 (m (- b a))
1156 (n (- c b 1))
1157 (psey (gensym))
1158 result)
1160 (setq result
1161 (mul (power -1 m)
1162 (factorial (+ n m l 1))
1163 (inv (factorial n))
1164 (inv (factorial l))
1165 (inv (factorial (+ n m)))
1166 (inv (factorial (+ m l)))
1167 (power (sub 1 psey) (sub n l))
1168 ($diff (mul (power (sub 1 psey) (+ m l))
1169 ($diff (mul (power psey -1)
1171 (take '(%log) (sub 1 psey)))
1172 psey
1174 psey
1175 (+ n m))))
1176 (if (onep1 var)
1177 ($limit result psey var)
1178 (maxima-substitute var psey result)))))
1180 ;; Handle F(a, b; c; z) for certain values of a, b, and c. See the
1181 ;; comments below for these special values. The optional arg z
1182 ;; defaults to var, which is usually the argument of hgfred.
1183 (defun hyp-cos (a b c &optional (z var))
1184 (let ((a1 (div (sub (add a b) (div 1 2)) 2))
1185 (z1 (sub 1 z)))
1186 ;; a1 = (a+b-1/2)/2
1187 ;; z1 = 1-z
1188 (cond ((eql 0 ($ratsimp (sub (sub (add a b)
1189 (div 1 2))
1190 c)))
1191 ;; a+b-1/2 = c
1193 ;; A&S 15.1.14
1195 ;; F(a,a+1/2;2*a;z)
1196 ;; = 2^(2*a-1)*(1-z)^(-1/2)*(1+sqrt(1-z))^(1-2*a)
1198 ;; But if 1-2*a is a negative integer, let's rationalize the answer to give
1200 ;; F(a,a+1/2;2*a;z)
1201 ;; = 2^(2*a-1)*(1-z)^(-1/2)*(1-sqrt(1-z))^(2*a-1)/z^(2*a-1)
1202 (when $trace2f1
1203 (format t " Case a+b-1/2=c~%"))
1204 (let ((2a-1 (sub (mul a1 2) 1)))
1205 (cond ((and (integerp 2a-1) (plusp 2a-1))
1206 ;; 2^(2*a-1)*(1-z)^(-1/2)*(1-sqrt(1-z))^(2*a-1)/z^(2*a-1)
1207 (mul (power 2 2a-1)
1208 (inv (power z1 1//2))
1209 (power (sub 1 (power z1 1//2)) 2a-1)
1210 (inv (power z 2a-1))))
1212 ;; 2^(2*a-1)*(1-z)^(-1/2)*(1+sqrt(1-z))^(1-2*a)
1213 (mul (power 2 (sub (mul a1 2) 1))
1214 (inv (power z1 (div 1 2)))
1215 (power (add 1
1216 (power z1
1217 (div 1
1218 2)))
1219 (sub 1 (mul 2 a1))))))))
1220 ((eql 0 ($ratsimp (sub (add 1 (mul 2 a1)) c)))
1221 ;; c = 1+2*a1 = a+b+1/2
1223 ;; A&S 15.1.13:
1225 ;; F(a,1/2+a;1+2*a;z) = 2^(2*a)*(1+sqrt(1-z))^(-2*a)
1227 ;; But if 2*a is a positive integer, let's rationalize the answer to give
1229 ;; F(a,1/2+a;1+2*a;z) = 2^(2*a)*(1-sqrt(1-z))^(2*a)/z^(2*a)
1230 (when $trace2f1
1231 (format t " Case c=1+2*a=a+b+1/2~%"))
1232 (let ((2a (sub c 1)))
1233 (cond ((and (integerp 2a) (plusp 2a))
1234 ;; 2^(2*a)*(1-sqrt(1-z))^(2*a)/z^(2*a)
1235 (mul (power 2 2a)
1236 (power (sub 1 (power z1 1//2))
1238 (power z (mul -1 2a))))
1240 ;; 2^(2*a)*(1+sqrt(1-z))^(-2*a)
1241 (mul (power 2 2a)
1242 (power (add 1 (power z1 1//2))
1243 (mul -1 2a))))))))))
1245 ;; Is A a non-negative integer?
1246 (defun nni (a)
1247 (cond ((hyp-integerp a)
1248 (not (minusp a)))))
1251 ;;; The following code doesn't appear to be used at all. Comment it all out for now.
1253 (defun degen2f1
1254 (a b c)
1255 (cond ((eq (quest (sub c b)) '$negative)
1256 (cond ((eq (quest (sub c a)) '$negative)
1257 (gered1 (list a b)(list c) #'hgfsimp))
1258 (t (gered2 a b c))))
1259 ((eq (quest (sub c a)) '$negative)(gered2 b a c))
1260 (t (rest-degen a b c))))
1263 (defun rest-degen
1264 (a b c)
1265 (prog(m n l)
1266 (cond ((nni (setq m (sub a 1)))
1267 (return (rest-degen-1 a b c m))))
1268 (cond ((ni b)(return (rest-degen-2 a b c))))
1269 (cond ((and (nni (setq n (sub c 1)))
1270 (nni (setq m (sub (sub a n) 1)))
1271 (nni (setq l (sub b a)))
1272 (eq (sub (sub c a) b)
1273 (mul -1 (add m m n l 1))))
1274 (return (gered1 (list a b)
1275 (list c)
1276 #'hgfsimp))))
1277 (return (hyp-deg b a c))))
1280 (defun rest-degen-1
1281 (a b c m)
1282 (prog(n l)
1283 (cond ((and (ni b)
1284 (ni (sub (sub c a) b))
1285 (nni (sub (sub c a) 1)))
1286 (return (deg299 a b c))))
1287 (cond ((and (nni (setq n (sub (sub c m) 2)))
1288 (nni (setq l (sub b c)))
1289 (equal (sub (sub c a) b)
1290 (mul -1 (add l m 1))))
1291 (return (gered1 (list a b)
1292 (list c)
1293 #'hgfsimp))))
1294 (cond ((nni (setq l (sub (sub b m) 1)))
1295 (return (rest-degen-1a a b c m l))))
1296 (return (hyp-deg b a c))))
1299 (defun rest-degen-1a
1300 (a b c m l)
1301 (prog(n)
1302 (cond ((and (nni (setq n
1303 (sub (sub (sub c m) l) 2)))
1304 (equal (sub n m)(sub (sub c a) b)))
1305 (return (deg2913 a b c))))
1306 (cond ((and (equal c (mul -1 n))
1307 (equal (sub (sub c a) b)
1308 (mul -1 (add m m l n 2))))
1309 (return (deg2918 a b c))))
1310 (return (hyp-deg b a c))))
1313 (defun rest-degen-2
1314 (a b c)
1315 (prog(m l)
1316 (cond ((and (ni c)(ni (sub (sub c a) b)))
1317 (return (rest-degen-2a a b c))))
1318 (cond ((and (nni (setq m (sub c 1)))
1319 (nni (setq l (sub a c)))
1320 (ni (sub (sub c a) b)))
1321 (return (deg292 a b c))))
1322 (return (hyp-deg b a c))))
1325 (defun rest-degen-2a
1326 (a b c)
1327 (prog()
1328 (cond ((nni (sub a c))
1329 (return (gered1 (list a b)
1330 (list c)
1331 #'hgfsimp))))
1332 (cond ((nni (sub (sub c a) 1))
1333 (return (deg2917 a b c))))
1334 (return (hyp-deg b a c))))
1336 (defun quest
1338 (cond ((numberp a)(checksigntm a))
1339 ((equal (caar a) 'rat)(checksigntm a))
1340 (t nil)))
1342 (defun ni(a)(not (hyp-integerp a)))
1345 (defun hyp-deg
1346 (a b c)
1347 (prog()
1348 (cond (fldeg (setq fldeg nil)
1349 (return (hgfsimp (list a b)
1350 (list c)
1351 var))))
1352 (setq fldeg t)
1353 (return (fpqform (list a b)(list c) var))))
1356 (defun deg2913
1357 (a b c)
1358 (mul (power (mul -1 var)(mul -1 b))
1359 (hgfsimp (list (add b 1 (mul -1 c)) b)
1360 (list (add b 1 (mul -1 a)))
1361 (inv var))))
1364 (defun deg2918
1365 (a b c)
1366 (mul (power var (sub 1 c))
1367 (power (sub 1 var)(add c (mul -1 a)(mul -1 b)))
1368 (hgfsimp (list (sub 1 a)(sub 1 b))
1369 (list (sub 2 c))
1370 var)))
1373 (defun deg2917
1374 (a b c)
1375 (mul (power var (sub 1 c))
1376 (hgfsimp (list (add a 1 (mul -1 c))
1377 (add b 1 (mul -1 c)))
1378 (list (sub 2 c))
1379 var)))
1382 (defun deg299
1383 (a b c)
1384 (mul (power (mul -1 var)(mul -1 a))
1385 (hgfsimp (list a (add a 1 (mul -1 c)))
1386 (list (add a 1 (mul -1 b)))
1387 (inv var))))
1392 ;; Is F(a, b; c; z) is Legendre function?
1394 ;; Lemma 29 (see ref) says F(a, b; c; z) can be reduced to a Legendre
1395 ;; function if two of the numbers 1-c, +/-(a-b), and +/- (c-a-b) are
1396 ;; equal to each other or one of them equals +/- 1/2.
1398 ;; This routine checks for each of the possibilities.
1399 (defun legfun (a b c)
1400 (let ((1-c (sub 1 c))
1401 (a-b (sub a b))
1402 (c-a-b (sub (sub c a) b))
1403 (inv2 (inv 2)))
1404 (cond ((alike1 a-b inv2)
1405 ;; a-b = 1/2
1406 (when $trace2f1
1407 (format t "Legendre a-b = 1/2~%"))
1408 (gered1 (list a b) (list c) #'legf24))
1410 ((alike1 a-b (mul -1 inv2))
1411 ;; a-b = -1/2
1413 ;; For example F(a,a+1/2;c;x)
1414 (when $trace2f1
1415 (format t "Legendre a-b = -1/2~%"))
1416 (legf24 (list a b) (list c) var))
1418 ((alike1 c-a-b '((rat simp) 1 2))
1419 ;; c-a-b = 1/2
1420 ;; For example F(a,b;a+b+1/2;z)
1421 (when $trace2f1
1422 (format t "Legendre c-a-b = 1/2~%"))
1423 (legf20 (list a b) (list c) var))
1425 ((and (alike1 c-a-b '((rat simp) 3 2))
1426 (not (alike1 c 1))
1427 (not (alike1 a -1//2))
1428 (not (alike1 b -1//2)))
1429 ;; c-a-b = 3/2 e.g. F(a,b;a+b+3/2;z) Reduce to
1430 ;; F(a,b;a+b+1/2) and use A&S 15.2.6. But if c = 1, we
1431 ;; don't want to reduce c to 0! Problem: The derivative of
1432 ;; assoc_legendre_p introduces a unit_step function and the
1433 ;; result looks very complicated. And this doesn't work if
1434 ;; a+1/2 or b+1/2 is zero, so skip that too.
1435 (when $trace2f1
1436 (format t "Legendre c-a-b = 3/2~%")
1437 (mformat t " : a = ~A~%" a)
1438 (mformat t " : b = ~A~%" b)
1439 (mformat t " : c = ~A~%" c))
1440 (let ((psey (gensym)))
1441 (maxima-substitute
1442 *par* psey
1443 (mul (power (sub 1 psey) '((rat simp) 3 2))
1444 (add a b '((rat simp) 1 2))
1445 (inv (add b '((rat simp) 1 2)))
1446 (inv (add a '((rat simp) 1 2)))
1447 ($diff (mul (power (sub 1 psey) '((rat simp) -1 2))
1448 (hgfsimp (list a b)
1449 (list (add a b '((rat simp) 1 2)))
1450 psey))
1451 psey
1452 1)))))
1454 ((alike1 c-a-b '((rat simp) -1 2))
1455 ;; c-a-b = -1/2, e.g. F(a,b; a+b-1/2; z)
1456 ;; This case is reduce to F(a,b; a+b+1/2; z) with
1457 ;; F(a,b;c;z) = (1-z)^(c-a-b)*F(c-a,c-b;c;z)
1458 (when $trace2f1
1459 (format t "Legendre c-a-b = -1/2~%"))
1460 (gered1 (list a b) (list c) #'legf20))
1462 ((alike1 1-c a-b)
1463 ;; 1-c = a-b, F(a,b; b-a+1; z)
1464 (when $trace2f1
1465 (format t "Legendre 1-c = a-b~%"))
1466 (gered1 (list a b) (list c) #'legf16))
1468 ((alike1 1-c (mul -1 a-b))
1469 ;; 1-c = b-a, e.g. F(a,b; a-b+1; z)
1470 (when $trace2f1
1471 (format t "Legendre 1-c = b-a~%"))
1472 (legf16 (list a b) (list c) var))
1474 ((alike1 1-c c-a-b)
1475 ;; 1-c = c-a-b, e.g. F(a,b; (a+b+1)/2; z)
1476 (when $trace2f1
1477 (format t "Legendre 1-c = c-a-b~%"))
1478 (gered1 (list a b) (list c) #'legf14))
1480 ((alike1 1-c (mul -1 c-a-b))
1481 ;; 1-c = a+b-c
1483 ;; For example F(a,1-a;c;x)
1484 (when $trace2f1
1485 (format t "Legendre 1-c = a+b-c~%"))
1486 (legf14 (list a b) (list c) var))
1488 ((alike1 a-b (mul -1 c-a-b))
1489 ;; a-b = a+b-c, e.g. F(a,b;2*b;z)
1490 (when $trace2f1
1491 (format t "Legendre a-b = a+b-c~%"))
1492 (legf36 (list a b) (list c) var))
1494 ((or (alike1 1-c inv2)
1495 (alike1 1-c (mul -1 inv2)))
1496 ;; 1-c = 1/2 or 1-c = -1/2
1497 ;; For example F(a,b;1/2;z) or F(a,b;3/2;z)
1498 (when $trace2f1
1499 (format t "Legendre |1-c| = 1/2~%"))
1500 ;; At this point it does not make sense to call legpol. legpol can
1501 ;; handle only cases with a negative integer in the first argument
1502 ;; list. This has been tested already. Therefore we can not get
1503 ;; a result from legpol. For this case a special algorithm is needed.
1504 ;; At this time we return nil.
1505 ;(legpol a b c)
1506 nil)
1508 ((alike1 a-b c-a-b)
1509 ;; a-b = c-a-b
1510 (when $trace2f1
1511 (format t "Legendre a-b = c-a-b~%"))
1512 'legendre-funct-to-be-discovered)
1514 nil))))
1516 ;;; The following legf<n> functions correspond to formulas in Higher
1517 ;;; Transcendental Functions. See the chapter on Legendre functions,
1518 ;;; in particular the table on page 124ff,
1520 ;; Handle the case c-a-b = 1/2
1522 ;; Formula 20:
1524 ;; P(n,m,z) = 2^m*(z^2-1)^(-m/2)/gamma(1-m)*F(1/2+n/2-m/2, -n/2-m/2; 1-m; 1-z^2)
1526 ;; See also A&S 15.4.12 and 15.4.13.
1528 ;; Let a = 1/2+n/2-m/2, b = -n/2-m/2, c = 1-m. Then, m = 1-c. And we
1529 ;; have two equivalent expressions for n:
1531 ;; n = c - 2*b - 1 or n = 2*a - c
1533 ;; The code below chooses the first solution. A&S chooses second.
1535 ;; F(a,b;c;w) = 2^(c-1)*gamma(c)*(-w)^((1-c)/2)*P(c-2*b-1,1-c,sqrt(1-w))
1538 (defun legf20 (arg-l1 arg-l2 var)
1539 ;; F(a,b;a+b+1/2;x)
1540 (let* (($radexpand nil)
1541 (b (cadr arg-l1))
1542 (c (car arg-l2))
1543 (a (sub (sub c b) '((rat simp) 1 2)))
1544 (m (sub 1 c))
1545 (n (mul -1 (add b b m))))
1546 ;; m = 1 - c
1547 ;; n = -(2*b+1-c) = c - 1 - 2*b
1548 ;; A&S 15.4.13
1550 ;; F(a,b;a+b+1/2;x) = 2^(a+b-1/2)*gamma(a+b+1/2)*x^((1/2-a-b)/2)
1551 ;; *assoc_legendre_p(a-b-1/2,1/2-a-b,sqrt(1-x))
1552 ;; This formula is correct for all arguments x.
1553 (mul (power 2 (add a b '((rat simp) -1 2)))
1554 (take '(%gamma) (add a b '((rat simp) 1 2)))
1555 (power var
1556 (div (sub '((rat simp) 1 2) (add a b))
1558 (legen n
1560 (power (sub 1 var) '((rat simp) 1 2))
1561 '$p))))
1563 ;; Handle the case a-b = -1/2.
1565 ;; Formula 24:
1567 ;; P(n,m,z) = 2^m*(z^2-1)^(-m/2)*z^(n+m)/gamma(1-m)*F(-n/2-m/2,1/2-n/2-m/2;1-m;1-1/z^2)
1569 ;; See also A&S 15.4.10 and 15.4.11.
1571 ;; Let a = -n/2-m/2, b = 1/2-n/2-m/2, c = 1-m. Then m = 1-c. Again,
1572 ;; we have 2 possible (equivalent) values for n:
1574 ;; n = -(2*a + 1 - c) or n = c-2*b
1576 ;; The code below chooses the first solution.
1578 ;; F(a,b;c;w) = 2^(c-1)*w^(1/2-c/2)*(1-w)^(c/2-a-1/2)*P(c-2*a-1,1-c,1/sqrt(1-w))
1580 ;; F(a,b;c;w) = 2^(c-1)*w^(1/2-c/2)*(1-w)^(c/2-b)*P(c-2*b,1-c,sqrt(1-w))
1582 ;; Is there a mistake in 15.4.10 and 15.4.11?
1584 (defun legf24 (arg-l1 arg-l2 var)
1585 (let* (($radexpand nil)
1586 (a (car arg-l1))
1587 (c (car arg-l2))
1588 (m (sub 1 c))
1589 ; (n (mul -1 (add a a m))) ; This is not 2*a-c
1590 (n (sub (add a a) c)) ; but this.
1591 (z (inv (power (sub 1 var) (inv 2)))))
1592 ;; A&S 15.4.10, 15.4.11
1593 (cond ((eq (asksign var) '$negative)
1594 ;; A&S 15.4.11
1596 ;; F(a,a+1/2;c;x) = 2^(c-1)*gamma(c)(-x)^(1/2-c/2)*(1-x)^(c/2-a-1/2)
1597 ;; *assoc_legendre_p(2*a-c,1-c,1/sqrt(1-x))
1598 (mul (inv (power 2 m))
1599 (gm (sub 1 m))
1600 (power (mul -1 var) (div m 2))
1601 (power (sub 1 var) (sub (div m -2) a))
1602 (legen n
1605 '$p)))
1607 (mul (inv (power 2 m))
1608 (gm (sub 1 m))
1609 (power var (div m 2))
1610 (power (sub 1 var) (sub (div m -2) a))
1611 (legen n
1614 '$p))))))
1616 ;; Handle 1-c = a-b
1618 ;; Formula 16:
1620 ;; P(n,m,z) = 2^(-n)*(z+1)^(m/2+n)*(z-1)^(-m/2)/gamma(1-m)*F(-n,-n-m;1-m;(z-1)/(z+1))
1622 ;; See also A&S 15.4.14 and 15.4.15.
1624 ;; Let a = -n, b = -n-m, c = 1-m. Then m = 1-c. We have 2 solutions
1625 ;; for n:
1627 ;; n = -a or n = c-b-1.
1629 ;; The code below chooses the first solution.
1631 ;; F(a,b;c;w) = gamma(c)*w^(1/2-c/2)*(1-w)^(-a)*P(-a,1-c,(1+w)/(1-w));
1633 ;; FIXME: We don't correctly handle the branch cut here!
1634 (defun legf16 (arg-l1 arg-l2 var)
1635 (let* (($radexpand nil)
1636 (a (car arg-l1))
1637 (c (car arg-l2))
1638 ;; m = 1-c = b-a
1639 (m (sub 1 c))
1640 ;; n = -b
1641 ;; m = b - a, so b = a + m
1642 (b (add a m))
1643 (n (mul -1 b))
1644 (z (div (add 1 var)
1645 (sub 1 var))))
1646 (when $trace2f1
1647 (format t "a, c = ~A ~A~%" a c)
1648 (format t "b = ~A~%" b))
1649 ;; A&S 15.4.14, 15.4.15
1650 (cond ((eq (asksign var) '$negative)
1651 ;; A&S 15.4.15
1653 ;; F(a,b;a-b+1,x) = gamma(a-b+1)*(1-x)^(-b)*(-x)^(b/2-a/2)
1654 ;; * assoc_legendre_p(-b,b-a,(1+x)/(1-x))
1656 ;; for x < 0
1657 (mul (take '(%gamma) c)
1658 (power (sub 1 var) (mul -1 b))
1659 (power (mul -1 var) (div m 2))
1660 (legen n m z '$p)))
1662 (mul (take '(%gamma) c)
1663 (power (sub 1 var) (mul -1 b))
1664 (power var (div m 2))
1665 (legen n m z '$p))))))
1667 ;; Handle the case 1-c = a+b-c.
1669 ;; See, for example, A&S 8.1.2 (which
1670 ;; might have a bug?) or
1671 ;; http://functions.wolfram.com/HypergeometricFunctions/LegendreP2General/26/01/02/
1673 ;; Formula 14:
1675 ;; P(n,m,z) = (z+1)^(m/2)*(z-1)^(-m/2)/gamma(1-m)*F(-n,1+n;1-m;(1-z)/2)
1677 ;; See also A&S 8.1.2, 15.4.16, 15.4.17
1679 ;; Let a=-n, b = 1+n, c = 1-m. Then m = 1-c and n has 2 solutions:
1681 ;; n = -a or n = b - 1.
1683 ;; The code belows chooses the first solution.
1685 ;; F(a,b;c;w) = gamma(c)*(-w)^(1/2-c/2)*(1-w)^(c/2-1/2)*P(-a,1-c,1-2*w)
1686 (defun legf14 (arg-l1 arg-l2 var)
1687 ;; Set $radexpand to NIL, because we don't want (-z)^x getting
1688 ;; expanded to (-1)^x*z^x because that's wrong for this.
1689 (let* (($radexpand nil)
1690 (a (first arg-l1))
1691 (b (second arg-l1))
1692 (c (first arg-l2))
1693 (m (sub 1 c))
1694 (n (mul -1 a))
1695 (z (sub 1 (mul 2 var))))
1696 (when $trace2f1
1697 (format t "~&legf14~%"))
1698 ;; A&S 15.4.16, 15.4.17
1699 (cond ((not (alike1 (add a b) 1))
1700 ;; I think 15.4.16 and 15.4.17 require the form
1701 ;; F(a,1-a;c;x). That is, a+b = 1. If we don't have it
1702 ;; exit now.
1703 nil)
1704 ((and (eq (asksign var) '$positive)
1705 (eq (asksign (sub 1 var)) '$positive))
1706 (when $trace2f1
1707 (format t " A&S 15.4.17~%"))
1708 ;; A&S 15.4.17
1710 ;; F(a,1-a;c;x) = gamma(c)*x^(1/2-c/2)*(1-x)^(c/2-1/2)*
1711 ;; assoc_legendre_p(-a,1-c,1-2*x)
1713 ;; for 0 < x < 1
1714 (mul (gm c)
1715 (power var (div (sub 1 c) 2))
1716 (power (sub 1 var) (div (sub c 1) 2))
1717 (legen n m z '$p)))
1719 ;; A&S 15.4.16
1721 ;; F(a,1-a;c;z) = gamma(c)*(-z)^(1/2-c/2)*(1-z)^(c/2-1/2)*
1722 ;; assoc_legendre_p(-a,1-c,1-2*z)
1723 (when $trace2f1
1724 (format t " A&S 15.4.17~%"))
1725 (mul (gm c)
1726 (power (mul -1 var) (div (sub 1 c) 2))
1727 (power (sub 1 var) (div (sub c 1) 2))
1728 (legen n m z '$p))))))
1730 ;; Handle a-b = a+b-c
1732 ;; Formula 36:
1734 ;; exp(-%i*m*%pi)*Q(n,m,z) =
1735 ;; 2^n*gamma(1+n)*gamma(1+n+m)*(z+1)^(m/2-n-1)*(z-1)^(-m/2)/gamma(2+2*n)
1736 ;; * hgfred([1+n-m,1+n],[2+2*n],2/(1+z))
1738 ;; Let a = 1+n-m, b = 1+n, c = 2+2*n. then n = b-1 and m = b - a.
1739 ;; (There are other solutions.)
1741 ;; F(a,b;c;z) = 2*gamma(2*b)/gamma(b)/gamma(2*b-a)*w^(-b)*(1-w)^((b-a)/2)
1742 ;; *Q(b-1,b-a,2/w-1)*exp(-%i*%pi*(b-a))
1744 (defun legf36 (arg-l1 arg-l2 var)
1745 (declare (ignore arg-l2))
1746 (let* ((a (car arg-l1))
1747 (b (cadr arg-l1))
1748 (n (sub b 1))
1749 (m (sub b a))
1750 ;;z (div (sub 2 var) var)
1751 (z (sub (div 2 var) 1)))
1752 (mul (inv (power 2 n))
1753 (inv (gm (add 1 n)))
1754 (inv (gm (add 1 n m)))
1755 (inv (power (add z 1)
1756 (add (div m 2)
1757 (mul -1 n)
1758 -1)))
1759 (inv (power (sub z 1) (div m -2)))
1760 (gm (add 2 n n))
1761 (power '$%e (mul -1 '$%i m '$%pi))
1762 (legen n m z '$q))))
1764 (defun legen (n m x pq)
1765 ;; A&S 8.2.1: P(-n-1,m,z) = P(n,m,z)
1766 (let ((n (if (or (member ($sign n) '($neg $nz)) ; negative sign or
1767 (mminusp n)) ; negative form like -n-1
1768 (mul -1 (add 1 n))
1769 n)))
1770 (cond ((equal m 0)
1771 (list (if (eq pq '$q)
1772 '($legendre_q simp)
1773 '($legendre_p simp))
1774 n x))
1776 (list (if (eq pq '$q)
1777 '($assoc_legendre_q simp)
1778 '($assoc_legendre_p simp))
1779 n m x)))))
1781 (defun legpol-core (a b c)
1782 ;; I think for this to be correct, we need a to be a negative integer.
1783 (unless (and (eq '$yes (ask-integerp a))
1784 (eq (asksign a) '$negative))
1785 (return-from legpol-core nil))
1786 (let* ((l (vfvp (div (add b a) 2)))
1787 (v (cdr (assoc 'v l :test #'equal))))
1788 ;; v is (a+b)/2
1789 (cond
1790 ((and (alike1 v '((rat simp) 1 2))
1791 (alike1 c 1))
1792 ;; A&S 22.5.49:
1793 ;; P(n,x) = F(-n,n+1;1;(1-x)/2)
1794 (legenpol (mul -1 a)
1795 (sub 1 (mul 2 var))))
1797 ((and (alike1 c '((rat simp) 1 2))
1798 (alike1 (add b a) '((rat simp) 1 2)))
1799 ;; c = 1/2, a+b = 1/2
1801 ;; A&S 22.5.52
1802 ;; P(2*n,x) = (-1)^n*(2*n)!/2^(2*n)/(n!)^2*F(-n,n+1/2;1/2;x^2)
1804 ;; F(-n,n+1/2;1/2;x^2) = P(2*n,x)*(-1)^n*(n!)^2/(2*n)!*2^(2*n)
1806 (let ((n (mul -1 a)))
1807 (mul (power -1 n)
1808 (power (gm (add n 1)) 2)
1809 (inv (gm (add 1 (mul 2 n))))
1810 (power 2 (mul 2 n))
1811 (legenpol (mul 2 n)
1812 (power var (div 1 2))))))
1814 ((and (alike1 c '((rat simp) 3 2))
1815 (alike1 (add b a) '((rat simp) 3 2)))
1816 ;; c = 3/2, a+b = 3/2
1818 ;; A&S 22.5.53
1819 ;; P(2*n+1,x) = (-1)^n*(2*n+1)!/2^(2*n)/(n!)^2*F(-n,n+3/2;3/2;x^2)*x
1821 ;; F(-n,n+3/2;3/2;x^2) = P(2*n+1,x)*(-1)^n*(n!)^2/(2*n+1)!*2^(2*n)/x
1823 (let ((n (mul -1 a)))
1824 (mul (power -1 n)
1825 (power (gm (add 1 n)) 2)
1826 (inv (gm (add 2 (mul 2 n))))
1827 (power 2 (mul 2 n))
1828 (legenpol (add 1 (mul 2 n))
1829 (power var (div 1 2)))
1830 (inv (power var (div 1 2))))))
1832 ((and (zerp (sub b a))
1833 (zerp (sub c (add a b))))
1834 ;; a = b, c = a + b
1836 ;; A&S 22.5.50
1837 ;; P(n,x) = binomial(2*n,n)*((x-1)/2)^n*F(-n,-n;-2*n;2/(1-x))
1839 ;; F(-n,-n;-2*n;x) = P(n,1-2/x)/binomial(2*n,n)(-1/x)^(-n)
1840 (mul (power (gm (add 1 (mul -1 a))) 2)
1841 (inv (gm (add 1 (mul -2 a))))
1842 (power (mul -1 var) (mul -1 a))
1843 (legenpol (mul -1 a)
1844 (add 1 (div -2 var)))))
1845 ((and (alike1 (sub a b) '((rat simp) 1 2))
1846 (alike1 (sub c (mul 2 b)) '((rat simp) 1 2)))
1847 ;; a - b = 1/2, c - 2*b = 1/2
1849 ;; A&S 22.5.51
1850 ;; P(n,x) = binomial(2*n,n)*(x/2)^n*F(-n/2,(1-n)/2;1/2-n;1/x^2)
1852 ;; F(-n/2,(1-n)/2;1/2-n,1/x^2) = P(n,x)/binomial(2*n,n)*(x/2)^(-n)
1853 (mul (power (gm (add 1 (mul -2 b))) 2)
1854 (inv (gm (add 1 (mul -4 b))))
1855 (power (mul 2 (power var (div 1 2))) (mul -2 b))
1856 (legenpol (mul -2 b)
1857 (power var (div -1 2)))))
1858 ((and (alike1 (sub b a) '((rat simp) 1 2))
1859 (alike1 (sub c (mul 2 a)) '((rat simp) 1 2)))
1860 ;; b - a = 1/2, c + 2*a = 1/2
1862 ;; A&S 22.5.51
1863 ;; P(n,x) = binomial(2*n,n)*(x/2)^n*F(-n/2,(1-n)/2;1/2-n;1/x^2)
1865 ;; F(-n/2,(1-n)/2;1/2-n,1/x^2) = P(n,x)/binomial(2*n,n)*(x/2)^(-n)
1866 (mul (power (gm (add 1 (mul -2 a))) 2)
1867 (inv (gm (add 1 (mul -4 a))))
1868 (power (mul 2 (power var (div 1 2))) (mul -2 a))
1869 (legenpol (mul -2 a)
1870 (power var (div -1 2)))))
1872 nil))))
1874 (defun legpol (a b c)
1875 ;; See if F(a,b;c;z) is a Legendre polynomial. If not, try
1876 ;; F(b,a;c;z).
1877 (or (legpol-core a b c)
1878 (legpol-core b a c)))
1880 ;; See A&S 15.3.3:
1882 ;; F(a,b;c;z) = (1-z)^(c-a-b)*F(c-a,c-b;c;z)
1883 (defun gered1 (arg-l1 arg-l2 simpflg)
1884 (destructuring-bind (a b)
1885 arg-l1
1886 (destructuring-bind (c)
1887 arg-l2
1888 (mul (power (sub 1 var)
1889 (add c
1890 (mul -1 a)
1891 (mul -1 b)))
1892 (funcall simpflg
1893 (list (sub c a)
1894 (sub c b))
1895 arg-l2
1896 var)))))
1898 ;; See A&S 15.3.4
1900 ;; F(a,b;c;z) = (1-z)^(-a)*F(a,c-b;c;z/(z-1))
1901 (defun gered2 (a b c)
1902 (mul (power (sub 1 var) (mul -1 a))
1903 (hgfsimp (list a (sub c b))
1904 (list c)
1905 (div var (sub var 1)))))
1907 ;; See A&S 15.3.9:
1909 ;; F(a,b;c;z) = A*z^(-a)*F(a,a-c+1;a+b-c+1;1-1/z)
1910 ;; + B*(1-z)^(c-a-b)*z^(a-c)*F(c-a,1-a;c-a-b+1,1-1/z)
1912 ;; where A = gamma(c)*gamma(c-a-b)/gamma(c-a)/gamma(c-b)
1913 ;; B = gamma(c)*gamma(a+b-c)/gamma(a)/gamma(b)
1915 (defun geredf (a b c)
1916 (let (($gamma_expand t))
1917 (add (div (mul (take '(%gamma) c)
1918 (take '(%gamma) (add c (mul -1 a) (mul -1 b)))
1919 (power var (mul -1 a))
1920 ($hgfred `((mlist) ,a ,(add a 1 (mul -1 c)))
1921 `((mlist) ,(add a b (mul -1 c) 1))
1922 (sub 1 (div 1 var))))
1923 (mul (take '(%gamma) (sub c a))
1924 (take '(%gamma) (sub c b))))
1925 (div (mul (take '(%gamma) c)
1926 (take '(%gamma) (add a b (mul -1 c)))
1927 (power (sub 1 var)
1928 (add c (mul -1 a) (mul -1 b)))
1929 (power var (sub a c))
1930 ($hgfred `((mlist) ,(sub c a) ,(sub 1 a))
1931 `((mlist) ,(add c (mul -1 a) (mul -1 b) 1))
1932 (sub 1 (div 1 var))))
1933 (mul (take '(%gamma) a) (take '(%gamma) b))))))
1935 (defun trig-log (arg-l1 arg-l2)
1936 (cond ((equal (simplifya (car arg-l2) nil) '((rat simp) 3 2))
1937 ;; c = 3/2
1938 (when $trace2f1
1939 (format t " trig-log: Test c=3/2~%"))
1940 (trig-log-3 arg-l1 arg-l2))
1941 ((equal (simplifya (car arg-l2) nil) '((rat simp) 1 2))
1942 ;; c = 1/2
1943 (when $trace2f1
1944 (format t " trig-log: Test c=1/2~%"))
1945 (trig-log-1 arg-l1 arg-l2))
1946 (t nil)))
1948 (defun trig-log-3 (arg-l1 arg-l2)
1949 (cond ((and (or (equal (car arg-l1) 1) (equal (cadr arg-l1) 1))
1950 (or (equal (car arg-l1) (div 1 2))
1951 (equal (cadr arg-l1) (div 1 2))))
1952 ;; (a = 1 or b = 1) and (a = 1/2 or b = 1/2)
1953 (when $trace2f1
1954 (format t " Case a or b is 1 and the other is 1/2~%"))
1955 (trig-log-3-exec arg-l1 arg-l2))
1956 ((and (equal (car arg-l1) (cadr arg-l1))
1957 (or (equal 1 (car arg-l1))
1958 (equal (div 1 2) (car arg-l1))))
1959 ;; a = b and (a = 1 or a = 1/2)
1960 (when $trace2f1
1961 (format t " Case a=b and a is 1 or 1/2~%"))
1962 (trig-log-3a-exec arg-l1 arg-l2))
1963 ((or (equal (add (car arg-l1) (cadr arg-l1)) 1)
1964 (equal (add (car arg-l1) (cadr arg-l1)) 2))
1965 ;; a + b = 1 or a + b = 2
1966 (when $trace2f1
1967 (format t " Case a+b is 1 or 2~%"))
1968 (trig-sin arg-l1 arg-l2))
1969 ((or (equal (sub (car arg-l1) (cadr arg-l1)) (div 1 2))
1970 (equal (sub (cadr arg-l1) (car arg-l1)) (div 1 2)))
1971 ;; a - b = 1/2 or b - a = 1/2
1972 (when $trace2f1
1973 (format t " Case a-b=1/2 or b-a=1/2~%"))
1974 (trig-3 arg-l1 arg-l2))
1975 (t nil)))
1977 (defun trig-3 (arg-l1 arg-l2)
1978 (declare (ignore arg-l2))
1979 ;; A&S 15.1.10
1981 ;; F(a,a+1/2,3/2,z^2) =
1982 ;; ((1+z)^(1-2*a) - (1-z)^(1-2*a))/2/z/(1-2*a)
1983 (let* (($radexpand '$all)
1984 (a (sub 1
1985 (sub (add (car arg-l1)
1986 (cadr arg-l1))
1987 (div 1 2))))
1988 (z (power var (div 1 2))))
1989 (mul (inv z)
1990 (inv 2)
1991 (inv a)
1992 (sub (power (add 1 z) a)
1993 (power (sub 1 z) a)))))
1995 (defun trig-sin (arg-l1 arg-l2)
1996 (declare (ignore arg-l2))
1997 ;; A&S 15.1.15, 15.1.16
1998 (destructuring-bind (a b)
1999 arg-l1
2000 ;; I think it's ok to convert sqrt(z^2) to z here, so $radexpand
2001 ;; is $all.
2002 (let (($radexpand '$all)
2003 a1 z1)
2004 (cond ((equal (add a b) 1)
2005 ;; A&S 15.1.15
2007 ;; F(a,1-a;3/2;sin(z)^2) =
2009 ;; sin((2*a-1)*z)/(2*a-1)/sin(z)
2010 (mul (inv (mul (mul -1 (sub a b))
2011 (msin (masin (msqrt var)))))
2012 (msin (mul (mul -1
2013 (sub a b))
2014 (masin (msqrt var))))))
2015 ((equal (add a b) 2)
2016 ;; A&S 15.1.16
2018 ;; F(a, 2-a; 3/2; sin(z)^2) =
2020 ;; sin((2*a-2)*z)/(a-1)/sin(2*z)
2021 (mul (msin (mul (setq z1
2022 (masin (msqrt
2023 var)))
2024 (setq a1
2025 (mul -1
2026 (sub a
2027 b)))))
2028 (inv (mul a1
2029 (msin z1)
2030 (mcos z1)))))
2032 nil)))))
2035 ;;Generates atan if arg positive else log
2036 (defun trig-log-3-exec (arg-l1 arg-l2)
2037 (declare (ignore arg-l1 arg-l2))
2038 ;; See A&S 15.1.4 and 15.1.5
2040 ;; F(a,b;3/2;z) where a = 1/2 and b = 1 (or vice versa).
2042 ;; I think it's ok to convert sqrt(z^2) to z here, so $radexpand is
2043 ;; $all.
2044 (let (($radexpand '$all))
2045 (cond ((equal (checksigntm var) '$positive)
2046 ;; A&S 15.1.4
2048 ;; F(1/2,1;3/2,z^2) =
2050 ;; log((1+z)/(1-z))/z/2
2052 ;; This is the same as atanh(z)/z. Should we return that
2053 ;; instead? This would make this match what hyp-atanh
2054 ;; returns.
2055 (let ((z (power var (div 1 2))))
2056 (mul (power z -1)
2057 (inv 2)
2058 (mlog (div (add 1 z)
2059 (sub 1 z))))))
2060 ((equal (checksigntm var) '$negative)
2061 ;; A&S 15.1.5
2063 ;; F(1/2,1;3/2,z^2) =
2064 ;; atan(z)/z
2065 (let ((z (power (mul -1 var)
2066 (div 1 2))))
2067 (mul (power z -1)
2068 (matan z)))))))
2070 (defun trig-log-3a-exec (arg-l1 arg-l2)
2071 ;; See A&S 15.1.6 and 15.1.7
2073 ;; F(a,b;3/2,z) where a = b and a = 1/2 or a = 1.
2075 ;; I think it's ok to convert sqrt(z^2) to z here, so $radexpand is
2076 ;; $all.
2077 (let ((a (first arg-l1))
2078 ($radexpand '$all))
2079 (cond ((equal (checksigntm var) '$positive)
2080 ;; A&S 15.1.6
2082 ;; F(1/2,1/2; 3/2; z^2) = sqrt(1-z^2)*F(1,1;3/2;z^2) =
2083 ;; asin(z)/z
2084 (let ((z (power var (div 1 2))))
2085 (if (equal a 1)
2086 (div (trig-log-3a-exec (list (div 1 2) (div 1 2)) arg-l2)
2087 (power (sub 1 (power z 2)) (div 1 2)))
2088 (div (masin z) z))))
2089 ((equal (checksigntm var) '$negative)
2090 ;; A&S 15.1.7
2092 ;; F(1/2,1/2; 3/2; -z^2) = sqrt(1+z^2)*F(1,1,3/2; -z^2) =
2093 ;;log(z + sqrt(1+z^2))/z
2094 (let* ((z (power (mul -1 var)
2095 (div 1 2)))
2096 (1+z^2 (add 1 (power z 2))))
2097 (if (equal a 1)
2098 (div (trig-log-3a-exec (list (div 1 2) (div 1 2))
2099 arg-l2)
2100 (power 1+z^2
2101 (div 1 2)))
2102 (div (mlog (add z (power 1+z^2
2103 (div 1 2))))
2104 z)))))))
2107 (defun trig-log-1 (arg-l1 arg-l2) ;; 2F1's with C = 1/2
2108 (declare (ignore arg-l2))
2109 ;; 15.1.17, 11, 18, 12, 9, and 19
2111 ;; I think it's ok to convert sqrt(z^2) to z here, so $radexpand is
2112 ;; $all.
2113 (let (($radexpand '$all)
2114 x z $exponentialize a b)
2115 (setq a (car arg-l1) b (cadr arg-l1))
2116 (cond ((=0 (m+t a b))
2117 ;; F(-a,a;1/2,z)
2119 (cond ((equal (checksigntm var) '$positive)
2120 ;; A&S 15.1.17:
2121 ;; F(-a,a;1/2;sin(z)^2) = cos(2*a*z)
2122 (trig-log-1-pos a var))
2123 ((equal (checksigntm var) '$negative)
2124 ;; A&X 15.1.11:
2125 ;; F(-a,a;1/2;-z^2) = 1/2*((sqrt(1+z^2)+z)^(2*a)
2126 ;; +(sqrt(1+z^2)-z)^(2*a))
2128 (trig-log-1-neg a b var))
2129 (t ())))
2130 ((equal (m+t a b) 1.)
2131 ;; F(a,1-a;1/2,z)
2132 (cond ((equal (checksigntm var) '$positive)
2133 ;; A&S 15.1.18:
2134 ;; F(a,1-a;1/2;sin(z)^2) = cos((2*a-1)*z)/cos(z)
2135 (m//t (mcos (m*t (m-t a b) (setq z (masin (msqrt var)))))
2136 (mcos z)))
2137 ((equal (checksigntm var) '$negative)
2138 ;; A&S 15.1.12
2139 ;; F(a,1-a;1/2;-z^2) = 1/2*(1+z^2)^(-1/2)*
2140 ;; {[(sqrt(1+z^2)+z]^(2*a-1)
2141 ;; +[sqrt(1+z^2)-z]^(2*a-1)}
2142 (m*t 1//2 (m//t (setq x (msqrt (m-t 1. var))))
2143 (m+t (m^t (m+t x (setq z (msqrt (m-t var))))
2144 (setq b (m-t a b)))
2145 (m^t (m-t x z) b))))
2146 (t ())))
2147 ((=1//2 (hyp-mabs (m-t b a)))
2148 ;; F(a, a+1/2; 1/2; z)
2149 (cond ((equal (checksigntm var) '$positive)
2150 ;; A&S 15.1.9
2151 ;; F(a,1/2+a;1/2;z^2) = ((1+z)^(-2*a)+(1-z)^(-2*a))/2
2152 (m*t 1//2
2153 (m+t (m^t (m1+t (setq z (msqrt var)))
2154 (setq b (m-t 1//2 (m+t a b))))
2155 (m^t (m-t 1. z) b))))
2156 ((equal (checksigntm var) '$negative)
2157 ;; A&S 15.1.19
2158 ;; F(a,1/2+a;1/2;-tan(z)^2) = cos(z)^(2*a)*cos(2*a*z)
2159 (m*t (m^t (mcos (setq z (matan (msqrt (m-t var)))))
2160 (setq b (m+t a b -1//2)))
2161 (mcos (m*t b z))))
2162 (t ())))
2163 (t ()))))
2165 (defun trig-log-1-pos (a z)
2166 ;; I think it's ok to convert sqrt(z^2) to z here, so $radexpand is
2167 ;; $all.
2168 (let (($radexpand '$all))
2169 (mcos (m*t 2. a (masin (msqrt z))))))
2171 (defun trig-log-1-neg (a b v)
2172 ;; Look to see a is of the form m*s+c where m and c
2173 ;; are numbers. If m is positive, swap a and b.
2174 ;; Basically we want F(-a,a;1/2;-z^2) =
2175 ;; F(a,-a;1/2;-z^2), as they should be.
2176 (let* (($radexpand '$all)
2177 (match (m*s+c a))
2178 (m (cdras 'm match))
2179 (s (cdras 's match))
2180 (b (if s
2181 (if (and m (eq (checksigntm m) '$positive))
2184 (if (eq (checksigntm a) '$negative)
2186 a)))
2187 (x (msqrt (m-t 1. v)))
2188 (z (msqrt (m-t v))))
2189 (m*t 1//2
2190 (m+t (m^t (m+t x z)
2191 (setq b (m*t 2. b)))
2192 (m^t (m-t x z) b)))))
2194 ;; Pattern match for m*s+c where a is a number, x is symbolic, and c
2195 ;; is a number.
2196 (defun m*s+c (exp)
2197 (m2 exp
2198 '((mplus) ((coeffpt) (m $numberp) (s nonnump))
2199 ((coeffpp) (c $numberp)))))
2201 ;; List L contains two elements first the numerator parameter that
2202 ;;exceeds the denumerator one and is called "C", second
2203 ;;the difference of the two parameters which is called "M".
2206 (defun diffintprop-gen-exec (l l1 l2)
2207 (prog (c m poly constfact )
2208 (setq c (car l)
2209 m (cadr l)
2210 l1 (delete c l1 :count 1 :test #'equal)
2211 c (sub c m)
2212 l2 (delete c l2 :count 1 :test equal)
2213 poly ($expand (constrpoly c m 'avgoustis))
2214 constfact (createconstfact c m))
2215 (return (yanmult constfact
2216 (diffintprop-exec poly l1 l2)))))
2218 (defun constrpoly (c m k)
2219 (cond ((zerop m) 1.)
2220 (t (mul (add c k (1- m)) (constrpoly c (1- m) k)))))
2222 (defun createconstfact (c m)
2223 (cond ((zerop m) 1.)
2224 (t (mul (inv (add c (1- m)))
2225 (createconstfact c (1- m))))))
2227 (defun diffintprop-exec (poly l1 l2)
2228 (distrdiffintprop (createcoefpowlist-exec poly) l1 l2))
2230 (defun distrdiffintprop (l l1 l2)
2231 (cond ((null l) 0.)
2232 (t (add (yanmult ($factor (cadar l))
2233 (diffintprop (caar l) l1 l2))
2234 (distrdiffintprop (cdr l) l1 l2)))))
2236 (defun diffintprop (pow l1 l2)
2237 (cond ((zerop pow) (hgfsimp l1 l2 var))
2238 ((equal pow 1.)
2239 (yanmult (mul (div (multpl l1) (multpl l2)) var)
2240 (hgfsimp (incr1 l1) (incr1 l2) var)))
2241 (t (searchaddserieslist pow l1 l2))))
2243 (defun searchaddserieslist (pow l1 l2)
2244 (prog (series res)
2245 (cond ((setq series (searchserieslistp serieslist pow))
2246 (return (eval series))))
2247 (setq
2248 serieslist
2249 (append
2250 serieslist
2251 (list
2252 (list
2254 (setq res
2255 '(yanmult (mul (div (multpl l1) (multpl l2))
2256 var)
2257 (diffintproprecurse (1- pow)
2258 (incr1 l1)
2259 (incr1 l2))))))))
2260 (return (eval res))))
2262 (defun diffintproprecurse (pow l1 l2)
2263 (prog (poly)
2264 (setq poly
2265 ($expand (power (add 'avgoustis 1.) pow)))
2266 (return (diffintprop-exec poly l1 l2))))
2268 (defun multpl (l)
2269 (cond ((null l) 1.) (t (mul (car l) (multpl (cdr l))))))
2271 (defun createcoefpowlist-exec (poly)
2272 (prog (hp conster)
2273 (setq conster (consterminit poly 'avgoustis)
2274 hp ($hipow poly 'avgoustis))
2275 (return (append (list (list 0. conster))
2276 (createcoefpowlist poly hp)))))
2278 (defun createcoefpowlist (poly hp)
2279 (cond ((equal hp 1.)
2280 (list (list 1. ($coeff poly 'avgoustis))))
2281 (t (append (createcoefpowlist poly (1- hp))
2282 (list (list hp
2283 ($coeff poly
2284 (power 'avgoustis
2285 hp))))))))
2287 (defun consterminit (fun var)
2288 (cond ((eq (caar fun) 'mplus) (consterm (cdr fun) var))
2289 (t (cond ((freevar fun) fun) (t 0.)))))
2291 (defun searchserieslistp (serieslist pow)
2292 (cond ((null serieslist) nil)
2293 ((equal (caar serieslist) pow) (cadar serieslist))
2294 (t (searchserieslistp (cdr serieslist) pow))))
2296 (defun yanmult (a b)
2297 (cond ((eq (caar b) 'mplus) (yanmul a (cdr b)))
2298 (t (mul a b))))
2300 (defun yanmul (a b)
2301 (cond ((null b) 0.)
2302 (t (add (mul a (car b)) (yanmul a (cdr b))))))
2306 (defun freevarpar (exp)
2307 (cond ((freevar exp) (freepar exp))
2308 (t nil)))
2310 ;; Why is this needed?
2311 (setq *par* '$p)
2313 (defun freepar (exp)
2314 (cond ((atom exp)
2315 (not (eq exp *par*)))
2316 (t (and (freepar (car exp))
2317 (freepar (cdr exp))))))
2319 ;; Confluent hypergeometric function.
2321 ;; F(a;c;z)
2322 (defun confl (arg-l1 arg-l2 var)
2323 (let* ((a (car arg-l1))
2324 (c (car arg-l2))
2325 (a-c (sub a c))
2327 (cond ((zerop1 var)
2328 ;; F(a;c;0) = 1
2329 (add 1 var))
2331 ((and (equal 1 c)
2332 (not (integerp a)) ; Do not handle an integer or
2333 (not (integerp (add a a)))) ; an half integral value
2334 ;; F(a;1;z) = laguerre(-a,z)
2335 (lagpol (neg a) 0 var))
2337 ((and (maxima-integerp a)
2338 (member ($sign a) '($neg nz)))
2339 ;; F(-n; c; z) and n a positive integer
2340 (1f1polys (list c) a))
2342 ((alike1 c (add a a))
2343 ;; F(a;2a;z)
2344 ;; A&S 13.6.6
2346 ;; F(n+1;2*n+1;2*z) =
2347 ;; gamma(3/2+n)*exp(z)*(z/2)^(-n-1/2)*bessel_i(n+1/2,z).
2349 ;; So
2351 ;; F(n,2*n,z) =
2352 ;; gamma(n+1/2)*exp(z/2)*(z/4)^(-n-3/2)*bessel_i(n-1/2,z/2);
2354 ;; If n is a negative integer, we use laguerre polynomial.
2355 (if (and (maxima-integerp a)
2356 (eq (asksign a) '$negative))
2357 ;; We have already checked a negative integer. This algorithm
2358 ;; is now present in 1f1polys and at this place never called.
2359 (let ((n (neg a)))
2360 (mul (power -1 n)
2361 (inv (take '(%binomial) (add n n) n))
2362 (lagpol n (sub c 1) var)))
2363 (let ((z (div var 2)))
2364 (mul (power '$%e z)
2365 (bestrig (add a '((rat simp) 1 2))
2366 (div (mul z z) 4))))))
2368 ((and (integerp (setq n (sub (add a a) c)))
2369 (plusp n)
2370 (not (integerp a))
2371 (not (integerp (add a a))))
2372 ;; F(a,2*a-n,z) and a not an integer or a half integral value
2373 (when *debug-hyp*
2374 (format t "~&Case 1F1(a,2*a-n,x):")
2375 (format t "~& ; a = ~A~%" a)
2376 (format t "~& ; c = ~A~%" c)
2377 (format t "~& : n = ~A~%" n))
2378 (sratsimp
2379 (mul (take '(%gamma) (sub a (add n '((rat simp) 1 2))))
2380 (power (div var 4)
2381 (sub (add '((rat simp) 1 2) n) a))
2382 (power '$%e (div var 2))
2383 (let ((index (gensym)))
2384 (dosum
2385 (mul (power -1 index)
2386 (take '($pochhammer) (- n) index)
2387 (take '($pochhammer) (add a a (* -2 n) -1) index)
2388 (add a index (- n) '((rat simp) -1 2))
2389 (inv (take '($pochhammer) (sub (add a a) n) index))
2390 (inv (take '(mfactorial) index))
2391 (take '(%bessel_i)
2392 (add a index (- n) '((rat simp) -1 2))
2393 (div var 2)))
2394 index 0 n t)))))
2396 ((and (integerp (setq n (sub c (add a a))))
2397 (plusp n)
2398 (not (integerp a))
2399 (not (integerp (add a a))))
2400 ;; F(a,2*a+n,z) and a not an integer or a half integral value
2401 (when *debug-hyp*
2402 (format t "~&Case 1F1(a,2*a+n,x):")
2403 (format t "~& ; a = ~A~%" a)
2404 (format t "~& ; c = ~A~%" c)
2405 (format t "~& : n = ~A~%" n))
2406 (sratsimp
2407 (mul (take '(%gamma) (sub a '((rat simp) 1 2)))
2408 (power (div var 4)
2409 (sub '((rat simp) 1 2) a))
2410 (power '$%e (div var 2))
2411 (let ((index (gensym)))
2412 (dosum
2413 (mul (take '($pochhammer) (- n) index)
2414 (take '($pochhammer) (add a a -1) index)
2415 (add a index '((rat simp) -1 2))
2416 (inv (take '($pochhammer) (add a a n) index))
2417 (inv (take '(mfactorial) index))
2418 (take '(%bessel_i)
2419 (add a index '((rat simp) -1 2))
2420 (div var 2)))
2421 index 0 n t)))))
2423 ((and (integerp (setq n (sub a '((rat simp) 1 2))))
2424 (>= n 0)
2425 (integerp c)
2426 (plusp c))
2427 (let ((m c)
2428 ($simpsum t)
2429 ($bessel_reduce t))
2430 (when *debug-hyp*
2431 (format t "~&Case 1F1(n+1/2,m,x):")
2432 (format t "~& ; a = ~A~%" a)
2433 (format t "~& ; c = ~A~%" c)
2434 (format t "~& : n = ~A~%" n)
2435 (format t "~& : m = ~A~%" m))
2436 (sratsimp
2437 (mul (power 2 (- 1 m))
2438 (power '$%e (div var 2))
2439 (factorial (- m 1))
2440 (factorial n)
2441 (inv (take '($pochhammer) '((rat simp) 1 2) (- m 1)))
2442 (inv (take '($pochhammer) '((rat simp) 1 2) n))
2443 (let ((index1 (gensumindex)))
2444 (dosum
2445 (mul (power 2 (neg index1))
2446 (power (neg var) index1)
2447 (inv (take '(mfactorial) index1))
2448 (mfuncall '$gen_laguerre
2449 (sub n index1)
2450 (sub index1 '((rat simp) 1 2))
2451 (neg var))
2452 (let ((index2 (gensumindex)))
2453 (dosum
2454 (mul (power -1 index2)
2455 (power 2 (neg index2))
2456 (take '(%binomial)
2457 (add index1 m -1)
2458 index2)
2459 (let ((index3 (gensumindex)))
2460 (dosum
2461 (mul (take '(%binomial) index2 index3)
2462 (take '(%bessel_i)
2463 (sub index2 (mul 2 index3))
2464 (div var 2)))
2465 index3 0 index2 t)))
2466 index2 0 (add index1 m -1) t)))
2467 index1 0 n t))))))
2469 ((and (integerp (setq n (sub a '((rat simp) 1 2))))
2470 (< n 0)
2471 (integerp c)
2472 (plusp c))
2473 (let ((n (- n))
2474 (m c)
2475 ($simpsum t)
2476 ($bessel_reduce t))
2477 (when *debug-hyp*
2478 (format t "~&Case 1F1(1/2-n,m,x):")
2479 (format t "~& ; a = ~A~%" a)
2480 (format t "~& ; c = ~A~%" c)
2481 (format t "~& : n = ~A~%" n)
2482 (format t "~& : m = ~A~%" m))
2483 (sratsimp
2484 (mul (power -1 n)
2485 (power 2 (- 1 m))
2486 (power '$%e (div var 2))
2487 (factorial (- m 1))
2488 (inv (take '($pochhammer) '((rat simp) 1 2) (- m 1)))
2489 (inv (take '($pochhammer) (sub m '((rat simp) 1 2)) n))
2490 (let ((index1 (gensumindex)))
2491 (dosum
2492 (mul (power 2 (neg index1))
2493 (power var index1)
2494 (take '(%binomial) n index1)
2495 (take '($pochhammer)
2496 (sub '((rat simp) 3 2) (+ m n))
2497 (sub n index1))
2498 (let ((index2 (gensumindex)))
2499 (dosum
2500 (mul (power '((rat simp) -1 2) index2)
2501 (take '(%binomial)
2502 (add index1 m -1)
2503 index2)
2504 (let ((index3 (gensumindex)))
2505 (dosum
2506 (mul (take '(%binomial) index2 index3)
2507 (take '(%bessel_i)
2508 (sub index2 (mul 2 index3))
2509 (div var 2)))
2510 index3 0 index2 t)))
2511 index2 0 (add index1 m -1) t)))
2512 index1 0 n t))))))
2514 ((not (hyp-integerp a-c))
2515 (cond ((hyp-integerp a)
2516 (kummer arg-l1 arg-l2))
2517 ($prefer_whittaker
2518 ;; A&S 15.1.32:
2520 ;; %m[k,u](z) = exp(-z/2)*z^(u+1/2)*M(1/2+u-k,1+2*u,z)
2522 ;; So
2524 ;; M(a,c,z) = exp(z/2)*z^(-c/2)*%m[c/2-a,c/2-1/2](z)
2526 ;; But if we apply Kummer's transformation, we can also
2527 ;; derive the expression
2529 ;; %m[k,u](z) = exp(z/2)*z^(u+1/2)*M(1/2+u+k,1+2*u,-z)
2531 ;; or
2533 ;; M(a,c,z) = exp(-z/2)*(-z)^(-c/2)*%m[a-c/2,c/2-1/2](-z)
2535 ;; For Laplace transforms it might be more beneficial to
2536 ;; return this second form instead.
2537 (let* ((m (div (sub c 1) 2))
2538 (k (add '((rat simp) 1 2) m (mul -1 a))))
2539 (mul (power var (mul -1 (add '((rat simp) 1 2) m)))
2540 (power '$%e (div var 2))
2541 (whitfun k m var))))
2543 (fpqform arg-l1 arg-l2 var))))
2544 ((minusp a-c)
2545 (sratsimp (erfgammared a c var)))
2547 (kummer arg-l1 arg-l2)))))
2549 ;; A&S 13.6.19:
2550 ;; M(1/2,3/2,-z^2) = sqrt(%pi)*erf(z)/2/sqrt(z)
2552 ;; So M(1/2,3/2,z) = sqrt(%pi)*erf(sqrt(-z))/2/sqrt(-z)
2553 ;; = sqrt(%pi)*erf(%i*sqrt(z))/2/(%i*sqrt(z))
2554 (defun hyprederf (x)
2555 (let ((x (mul '$%i (power x '((rat simp) 1 2 )))))
2556 (mul (power '$%pi '((rat simp) 1 2))
2557 '((rat simp) 1 2)
2558 (inv x)
2559 (take '(%erf) x))))
2561 ;; M(a,c,z), where a-c is a negative integer.
2562 (defun erfgammared (a c z)
2563 (cond ((and (nump a) (nump c))
2564 (erfgamnumred a c z))
2565 (t (gammareds a c z))))
2567 ;; I (rtoy) think this is what the function above is doing, but I'm
2568 ;; not sure. Plus, I think it's wrong.
2570 ;; For hgfred([n],[2+n],-z), the above returns
2572 ;; 2*n*(n+1)*z^(-n-1)*(gamma_incomplete_lower(n,z)*z-gamma_incomplete_lower(n+1,z))
2574 ;; But from A&S 13.4.3
2576 ;; -M(n,2+n,z) - n*M(n+1,n+2,z) + (n+1)*M(n,n+1,z) = 0
2578 ;; so M(n,2+n,z) = (n+1)*M(n,n+1,z)-n*M(n+1,n+2,z)
2580 ;; And M(n,n+1,-z) = n*z^(-n)*gamma_incomplete_lower(n,z)
2582 ;; This gives
2584 ;; M(n,2+n,z) = (n+1)*n*z^(-n)*gamma_incomplete_lower(n,z) - n*(n+1)*z^(-n-1)*gamma_incomplete_lower(n+1,z)
2585 ;; = n*(n+1)*z^(-n-1)*(gamma_incomplete_lower(n,z)*n-gamma_incomplete_lower(n+1,z))
2587 ;; So the version above is off by a factor of 2. But I think it's more than that.
2588 ;; Using A&S 13.4.3 again,
2590 ;; M(n,n+3,-z) = [n*M(n+1,n+3,-z) - (n+2)*M(n,n+2,-z)]/(-2);
2592 ;; The version above doesn't produce anything like this equation would
2593 ;; produce, given the value of M(n,n+2,-z) derived above.
2594 (defun gammareds (a c z)
2595 ;; M(a,c,z) where a-c is a negative integer.
2596 (let ((diff (sub c a)))
2597 (cond ((eql diff 1)
2598 ;; We have M(a,a+1,z).
2599 (hypredincgm a z))
2600 ((eql a 1)
2601 ;; We have M(1,a,z)
2602 ;; Apply Kummer's tranformation to get the form M(a-1,a,z)
2604 ;; (I don't think we ever get here, but just in case, we leave it.)
2605 (let ((var z))
2606 (kummer (list a) (list c))))
2608 ;; We have M(a, a+n, z)
2610 ;; A&S 13.4.3 says
2611 ;; (1+a-b)*M(a,b,z) - a*M(a+1,b,z)+(b-1)*M(a,b-1,z) = 0
2613 ;; So
2615 ;; M(a,b,z) = [a*M(a+1,b,z) - (b-1)*M(a,b-1,z)]/(1+a-b);
2617 ;; Thus, the difference between b and a is reduced, until
2618 ;; b-a=1, which we handle above.
2619 (mul (sub (mul a
2620 (gammareds (add 1 a) c z))
2621 (mul (sub c 1)
2622 (gammareds a (sub c 1) z)))
2623 (inv (sub (add 1 a) c)))))))
2625 ;; A&S 6.5.12:
2626 ;; gamma_incomplete_lower(a,x) = x^a/a*M(a,1+a,-x)
2627 ;; = x^a/a*exp(-x)*M(1,1+a,x)
2629 ;; where gamma_incomplete_lower(a,x) is the lower incomplete gamma function.
2631 ;; M(a,1+a,x) = a*(-x)^(-a)*gamma_incomplete_lower(a,-x)
2632 (defun hypredincgm (a z)
2633 (let ((-z (mul -1 z)))
2634 (if (not $prefer_gamma_incomplete)
2635 (mul a
2636 (power -z (mul -1 a))
2637 (take '(%gamma_incomplete_lower) a -z))
2638 (mul a
2639 (power -z (mul -1 a))
2640 (sub (take '(%gamma) a)
2641 (take '(%gamma_incomplete) a -z))))))
2643 ;; M(a,c,z), when a and c are numbers, and a-c is a negative integer
2644 (defun erfgamnumred (a c z)
2645 (cond ((hyp-integerp (sub c '((rat simp) 1 2)))
2646 (erfred a c z))
2647 (t (gammareds a c z))))
2649 ;; M(a,c,z) when a and c are numbers and c-1/2 is an integer and a-c
2650 ;; is a negative integer. Thus, we have M(p+1/2, q+1/2,z)
2651 (defun erfred (a c z)
2652 (prog (n m)
2653 (setq n (sub a '((rat simp) 1 2))
2654 m (sub c '((rat simp) 3 2)))
2655 ;; a = n + 1/2
2656 ;; c = m + 3/2
2657 ;; a - c < 0 so n - m - 1 < 0
2658 (cond ((not (or (> n m) (minusp n)))
2659 ;; 0 <= n <= m
2660 (return (thno33 n m z)))
2661 ((and (minusp n) (minusp m))
2662 ;; n < 0 and m < 0
2663 (return (thno35 (mul -1 n) (mul -1 m) z)))
2664 ((and (minusp n) (plusp m))
2665 ;; n < 0 and m > 0
2666 (return (thno34 (mul -1 n) m z)))
2668 ;; n = 0 or m = 0
2669 (return (gammareds (add n '((rat simp) 1 2))
2670 (add m '((rat simp) 3 2))
2671 z))))))
2673 ;; Compute M(n+1/2, m+3/2, z) with 0 <= n <= m.
2675 ;; I (rtoy) think this is what this routine is doing. (I'm guessing
2676 ;; that thno33 means theorem number 33 from Yannis Avgoustis' thesis.)
2678 ;; I don't have his thesis, but I see there are similar ways to derive
2679 ;; the result we want.
2681 ;; Method 1:
2682 ;; Use Kummer's transformation (A&S ) to get
2684 ;; M(n+1/2,m+3/2,z) = exp(z)*M(m-n+1,m+3/2,-z)
2686 ;; From A&S, we have
2688 ;; diff(M(1,n+3/2,z),z,m-n) = poch(1,m-n)/poch(n+3/2,m-n)*M(m-n+1,m+3/2,z)
2690 ;; Apply Kummer's transformation again:
2692 ;; M(1,n+3/2,z) = exp(z)*M(n+1/2,n+3/2,-z)
2694 ;; Apply the differentiation formula again:
2696 ;; diff(M(1/2,3/2,z),z,n) = poch(1/2,n)/poch(3/2,n)*M(n+1/2,n+3/2,z)
2698 ;; And we know that M(1/2,3/2,z) can be expressed in terms of erf.
2700 ;; Method 2:
2702 ;; Since n <= m, apply the differentiation formula:
2704 ;; diff(M(1/2,m-n+3/2,z),z,n) = poch(1/2,n)/poch(m-n+3/2,n)*M(n+1/2,m+3/2,z)
2706 ;; Apply Kummer's transformation:
2708 ;; M(1/2,m-n+3/2,z) = exp(z)*M(m-n+1,m-n+3/2,z)
2710 ;; Apply the differentiation formula again:
2712 ;; diff(M(1,3/2,z),z,m-n) = poch(1,m-n)/poch(3/2,m-n)*M(m-n+1,m-n+3/2,z)
2714 ;; I think this routine uses Method 2.
2715 (defun thno33 (n m x)
2716 ;; M(n+1/2,m+3/2,z) = diff(M(1/2,m-n+3/2,z),z,n)*poch(m-n+3/2,n)/poch(1/2,n)
2717 ;; M(1/2,m-n+3/2,z) = exp(z)*M(m-n+1,m-n+3/2,-z)
2718 ;; M(m-n+1,m-n+3/2,z) = diff(M(1,3/2,z),z,m-n)*poch(3/2,m-n)/poch(1,m-n)
2719 ;; diff(M(1,3/2,z),z,m-n) = (-1)^(m-n)*diff(M(1,3/2,-z),z,m-n)
2720 ;; M(1,3/2,-z) = exp(-z)*M(1/2,3/2,z)
2721 (let* ((yannis (gensym))
2722 (m-n (sub m n))
2723 ;; poch(m-n+3/2,n)/poch(1/2,n)
2724 (factor1 (div (take '($pochhammer) (add m-n '((rat simp) 3 2)) n)
2725 (take '($pochhammer) '((rat simp) 1 2) n)))
2726 ;; poch(3/2,m-n)/poch(1,m-n)
2727 (factor2 (div (take '($pochhammer) '((rat simp) 3 2) m-n)
2728 (take '($pochhammer) 1 m-n)))
2729 ;; M(1,3/2,-z) = exp(-z)*M(1/2,3/2,z)
2730 (hgferf (mul (power '$%e (mul -1 yannis))
2731 (hyprederf yannis)))
2732 ;; diff(M(1,3/2,z),z,m-n)
2733 (diff1 (meval (list '($diff) hgferf yannis m-n)))
2734 ;; exp(z)*M(m-n+1,m-n+3/2,-z)
2735 (kummer (mul (power '$%e yannis) diff1))
2736 ;; diff(M(1/2,m-n+3/2,z),z,n)
2737 (diff2 (meval (list '($diff) kummer yannis n))))
2738 ;; Multiply all the terms together.
2739 (sratsimp (mul (power -1 m-n)
2740 factor1
2741 factor2
2742 (maxima-substitute x yannis diff2)))))
2744 ;; M(n+1/2,m+3/2,z), with n < 0 and m > 0
2746 ;; Let's write it more explicitly as M(-n+1/2,m+3/2,z) with n > 0 and
2747 ;; m > 0.
2749 ;; First, use Kummer's transformation to get
2751 ;; M(-n+1/2,m+3/2,z) = exp(z)*M(m+n+1,m+3/2,-z)
2753 ;; We also have
2755 ;; diff(z^(n+m)*M(m+1,m+3/2,z),z,n) = poch(m+1,n)*z^m*M(m+n+1,m+3/2,z)
2757 ;; And finally
2759 ;; diff(M(1,3/2,z),z,m) = poch(1,m)/poch(3/2,m)*M(m+1,m+3/2,z)
2761 ;; Thus, we can compute M(-n+1/2,m+3/2,z) from M(1,3/2,z).
2763 ;; The second formula above can be derived easily by multiplying the
2764 ;; series for M(m+1,m+3/2,z) by z^(n+m) and differentiating n times.
2766 (defun thno34 (n m x)
2767 (let ((yannis (gensym)))
2768 (sratsimp
2769 (maxima-substitute
2771 yannis
2772 (mul (power -1 m)
2773 (div (mul (take '($pochhammer) '((rat simp) 3 2) m)
2774 (power '$%e yannis))
2775 (mul (take '($pochhammer) 1 m)
2776 (take '($pochhammer) (1+ m) n)
2777 (power yannis m)))
2778 (meval (list '($diff)
2779 (mul (power yannis (+ m n))
2780 (meval (list '($diff)
2781 (mul (power '$%e
2782 (mul -1 yannis))
2783 (hyprederf yannis))
2784 yannis
2785 m)))
2786 yannis
2787 n)))))))
2789 ;; M(n+1/2,m+3/2,z), with n < 0 and m < 0
2791 ;; Write it more explicitly as M(-n+1/2,-m+3/2,z) with n > 0 and m >
2792 ;; 0.
2794 ;; We know that
2796 ;; diff(sqrt(z)*M(-n+1/2,3/2,z),z,m) = poch(3/2-m,m)*M(-n+1/2,-m+3/2,z).
2798 ;; Apply Kummer's transformation:
2800 ;; M(-n+1/2,3/2,z) = exp(z) * M(n+1,3/2,-z)
2802 ;; Finally
2804 ;; diff(z^n*M(1,3/2,z),z,n) = n!*M(n+1,3/2,z)
2806 ;; So we can express M(-n+1/2,-m+3/2,z) in terms of M(1,3/2,z).
2808 ;; The first formula above follows from the more general formula
2810 ;; diff(z^(b-1)*M(a,b,z),z,n) = poch(b-n,n)*z^(b-n-1)*M(a,b-n,z)
2812 ;; The last formula follows from the general result
2814 ;; diff(z^(a+n-1)*M(a,b,z),z,n) = poch(a,n)*z^(a-1)*M(a+n,b,z)
2816 ;; Both of these are easily derived by using the series for M and
2817 ;; differentiating.
2819 (defun thno35 (n m x)
2820 (let ((yannis (gensym)))
2821 (sratsimp
2822 (maxima-substitute
2824 yannis
2825 (mul (div (power yannis (sub m '((rat simp) 1 2)))
2826 (mul (power -1 (* -1 m))
2827 (take '($pochhammer) 1 n)
2828 (take '($pochhammer) '((rat simp) -1 2) m)))
2829 (meval (list '($diff)
2830 (mul (power yannis '((rat simp) 1 2))
2831 (power '$%e yannis)
2832 (meval (list '($diff)
2833 (mul (power '$%e
2834 (mul -1 yannis))
2835 (power yannis n)
2836 (hyprederf yannis))
2837 yannis
2838 n)))
2839 yannis
2840 m)))))))
2842 ;; Pochhammer symbol. fctrl(a,n) = a*(a+1)*(a+2)*...*(a+n-1).
2844 ;; N must be a positive integer!
2846 ;; FIXME: This appears to be identical to factf below.
2847 (defun fctrl (a n)
2848 (cond ((zerop n)
2850 ((equal n 1)
2853 (mul (add a (1- n))
2854 (fctrl a (1- n))))))
2856 (setq *par* '$p)
2858 (defun vfvp (exp)
2859 (m2 exp '(v freevarpar)))
2862 (defun fpqform (arg-l1 arg-l2 arg)
2863 (list '(mqapply)
2864 (list '($%f simp array) (length arg-l1)(length arg-l2))
2865 (append (list '(mlist simp)) arg-l1)
2866 (append (list '(mlist simp)) arg-l2)
2867 arg))
2869 ;; Consider pFq([a_k]; [c_j]; z). If a_k = c_j + m for some k and j
2870 ;; and m >= 0, we can express pFq in terms of (p-1)F(q-1).
2872 ;; Here is a derivation for F(a,b;c;z), but it generalizes to the
2873 ;; generalized hypergeometric very easily.
2875 ;; From A&s 15.2.3:
2877 ;; diff(z^(a+n-1)*F(a,b;c;z), z, n) = poch(a,n)*z^(a-1)*F(a+n,b;c;z)
2879 ;; F(a+n,b;c;z) = diff(z^(a+n-1)*F(a,b;c;z), z, n)/poch(a,n)/z^(a-1)
2882 ;; So this expresses F(a+n,b;c;z) in terms of F(a,b;c;z). Let a = c +
2883 ;; n. This therefore gives F(c+n,b;c;z) in terms of F(c,b;c;z) =
2884 ;; 1F0(b;;z), which we know.
2886 ;; For simplicity, we will write F(z) for F(a,b;c;z).
2888 ;; Now,
2890 ;; n
2891 ;; diff(z^x*F(z),z,n) = sum binomial(n,k)*diff(z^x,z,n-k)*diff(F(z),z,k)
2892 ;; k=0
2894 ;; But diff(z^x,z,n-k) = x*(x-1)*...*(x-n+k+1)*z^(x-n+k)
2895 ;; = poch(x-n+k+1,n-k)*z^(x-n+k)
2897 ;; so
2899 ;; z^(-a+1)/poch(a,n)*diff(z^(a+n-1),z,n-k)
2900 ;; = poch(a+n-1-n+k+1,n-k)/poch(a,n)*z^(a+n-1-n+k)*z^(-a+1)
2901 ;; = poch(a+k,n-k)/poch(a,n)*z^k
2902 ;; = z^k/poch(a,k)
2904 ;; Combining these we have
2906 ;; n
2907 ;; F(a+n,b;c;z) = sum z^k/poch(a,k)*binomial(n,k)*diff(F(a,b;c;z),z,k)
2908 ;; k=0
2910 ;; Since a = c, we have
2912 ;; n
2913 ;; F(a+n,b;a;z) = sum z^k/poch(a,k)*binomial(n,k)*diff(F(a,b;a;z),z,k)
2914 ;; k=0
2916 ;; But F(a,b;a;z) = F(b;;z) and it's easy to see that A&S 15.2.2 can
2917 ;; be specialized to this case to give
2919 ;; diff(F(b;;z),z,k) = poch(b,k)*F(b+k;;z)
2921 ;; Finally, combining all of these, we have
2923 ;; n
2924 ;; F(a+n,b;c;z) = sum z^k/poch(a,k)*binomial(n,k)*poch(b,k)*F(b+k;;z)
2925 ;; k=0
2927 ;; Thus, F(a+n,b;c;z) is expressed in terms of 1F0(b+k;;z), as desired.
2928 (defun splitpfq (l arg-l1 arg-l2)
2929 (destructuring-bind (a1 k)
2931 (let* ((result 0)
2932 (prodnum 1)
2933 (proden 1)
2934 (b1 (sub a1 k))
2935 (prod-b 1)
2936 (arg-l1 (delete a1 arg-l1 :count 1 :test #'equal))
2937 (arg-l2 (delete b1 arg-l2 :count 1 :test #'equal)))
2938 (loop for count from 0 upto k
2940 (when $trace2f1
2941 (format t "splitpfg term:~%")
2942 (maxima-display (mul (combin k count)
2943 (div prodnum proden)
2944 (inv prod-b)
2945 (power var count)))
2946 (mtell "F(~:M, ~:M)~%"
2947 (cons '(mlist) arg-l1)
2948 (cons '(mlist) arg-l2)))
2949 (setq result (add result
2950 (mul (combin k count)
2951 (div prodnum proden)
2952 (inv prod-b)
2953 (power var count)
2954 (hgfsimp arg-l1 arg-l2 var))))
2955 (setq prod-b (mul prod-b (add b1 count)))
2956 (setq prodnum (mul prodnum (mull arg-l1))
2957 proden (mul proden (mull arg-l2)))
2958 (setq arg-l1 (incr1 arg-l1))
2959 (setq arg-l2 (incr1 arg-l2)))
2960 result)))
2962 ;; binomial(k,count)
2963 (defun combin (k count)
2964 (div (factorial k)
2965 (mul (factorial count)
2966 (factorial (sub k count)))))
2969 ;; We have something like F(s+m,-s+n;c;z)
2970 ;; Rewrite it like F(a'+d,-a';c;z) where a'=s-n=-b and d=m+n.
2972 (defun algii (a b)
2973 (let* ((sym (cdras 'f (s+c a)))
2974 (sign (cdras 'm (m2 sym '((mtimes) ((coefft) (m $numberp)) ((coefft) (s nonnump)))))))
2975 (when (and sign (minusp sign))
2976 (rotatef a b))
2977 (list nil (mul -1 b) (add a b))))
2980 ;;Algor. 2F1-RL from thesis:step 4:dispatch on a+m,-a+n,1/2+l cases
2981 (defun step4 (a b c)
2982 ;; F(a,b;c;z) where a+b is an integer and c+1/2 is an integer. If a
2983 ;; and b are not integers themselves, we can derive the result from
2984 ;; F(a1,-a1;1/2;z). However, if a and b are integers, we can't use
2985 ;; that because F(a1,-a1;1/2;z) is a polynomial. We need to derive
2986 ;; the result from F(1,1;3/2;z).
2987 (if (and (hyp-integerp a)
2988 (hyp-integerp b))
2989 (step4-int a b c)
2990 (step4-a a b c)))
2992 (defun step4-a (a b c)
2993 (let* ((alglist (algii a b))
2994 (aprime (cadr alglist))
2995 (m (caddr alglist))
2996 (n (sub c (inv 2)))
2997 ($ratsimpexpons $true)
2998 ($ratprint $false))
2999 ;; At this point, we have F(a'+m,-a';1/2+n;z) where m and n are
3000 ;; integers.
3001 (cond ((hyp-integerp (add aprime (inv 2)))
3002 ;; Ok. We have a problem if aprime + 1/2 is an integer.
3003 ;; We can't always use the algorithm below because we have
3004 ;; F(1/2,-1/2;1/2;z) which is 1F0(-1/2;;z) so the
3005 ;; derivation isn't quite right. Also, sometimes we'll end
3006 ;; up with a division by zero.
3008 ;; Thus, We need to do something else. So, use A&S 15.3.3
3009 ;; to change the problem:
3011 ;; F(a,b;c;z) = (1-z)^(c-a-b)*F(c-a, c-b; c; z)
3013 ;; which is
3015 ;; F('a+m,-a';1/2+n;z) = (1-z)^(1/2+n-m)*F(1/2+n-a'-m,1/2+n+a';1/2+n;z)
3017 ;; Recall that a' + 1/2 is an integer. Thus we have
3018 ;; F(<int>,<int>,1/2+n;z), which we know how to handle in
3019 ;; step4-int.
3020 (gered1 (list a b) (list c) #'hgfsimp))
3022 (let ((newf
3023 (cond ((equal (checksigntm var) '$positive)
3024 (trig-log-1-pos aprime 'ell))
3025 ((equal (checksigntm var) '$negative)
3026 (trig-log-1-neg (mul -1 aprime) aprime 'ell)))))
3027 ;; Ok, this uses F(a,-a;1/2;z). Since there are 2 possible
3028 ;; representations (A&S 15.1.11 and 15.1.17), we check the sign
3029 ;; of the var (as done in trig-log-1) to select which form we
3030 ;; want to use. The original didn't and seemed to want to use
3031 ;; the negative form.
3033 ;; With this change, F(a,-a;3/2;z) matches what A&S 15.2.6 would
3034 ;; produce starting from F(a,-a;1/2;z), assuming z < 0.
3036 (subst var 'ell
3037 (algiii newf
3038 m n aprime)))))))
3040 ;; F(a,b;c;z), where a and b are (positive) integers and c = 1/2+l.
3041 ;; This can be computed from F(1,1;3/2;z).
3043 ;; Assume a < b, without loss of generality.
3045 ;; F(m,n;3/2+L;z), m < n.
3047 ;; We start from F(1,1;3/2;z). Use A&S 15.2.3, differentiating m
3048 ;; times to get F(m,1;3/2;z). Swap a and b to get F(m,1;3/2;z) =
3049 ;; F(1,m;3/2;z) and use A&S 15.2.3 again to get F(n,m;3/2;z) by
3050 ;; differentiating n times. Finally, if L < 0, use A&S 15.2.4.
3051 ;; Otherwise use A&S 15.2.6.
3053 ;; I (rtoy) can't think of any way to do this with less than 3
3054 ;; differentiations.
3056 ;; Note that if d = (n-m)/2 is not an integer, we can write F(m,n;c;z)
3057 ;; as F(-d+u,d+u;c;z) where u = (n+m)/2. In this case, we could use
3058 ;; step4-a to compute the result.
3061 ;; Transform F(a,b;c;z) to F(a+n,b;c;z), given F(a,b;c;z)
3062 (defun as-15.2.3 (a bb cx n arg fun)
3063 (declare (ignore bb cx))
3064 (assert (>= n 0))
3065 ;; A&S 15.2.3:
3066 ;; F(a+n,b;c;z) = z^(1-a)/poch(a,n)*diff(z^(a+n-1)*fun,z,n)
3067 (mul (inv (factf a n))
3068 (power arg (sub 1 a))
3069 ($diff (mul (power arg (sub (add a n) 1))
3070 fun)
3071 arg n)))
3073 ;; Transform F(a,b;c;z) to F(a,b;c-n;z), given F(a,b;c;z)
3074 (defun as-15.2.4 (axax bb c n arg fun)
3075 (declare (ignore axax bb))
3076 (assert (>= n 0))
3077 ;; A&S 15.2.4
3078 ;; F(a,b;c-n;z) = 1/poch(c-n,n)/z^(c-n-1)*diff(z^(c-1)*fun,z,n)
3079 (mul (inv (factf (sub c n) n))
3080 (inv (power arg (sub (sub c n) 1)))
3081 ($diff (mul (power arg (sub c 1))
3082 fun)
3083 arg n)))
3085 ;; Transform F(a,b;c;z) to F(a-n,b;c;z), given F(a,b;c;z)
3086 (defun as-15.2.5 (a b c n arg fun)
3087 ;; A&S 15.2.5
3088 ;; F(a-n,b;c;z) = 1/poch(c-a,n)*z^(1+a-c)*(1-z)^(c+n-a-b)
3089 ;; *diff(z^(c-a+n-1)*(1-z)^(a+b-c)*F(a,b;c;z),z,n)
3090 (assert (>= n 0))
3091 (mul (inv (factf (sub c a) n))
3092 (power arg (sub (add a 1) c))
3093 (power (sub 1 arg)
3094 (sub (add c n) (add a b)))
3095 ($diff (mul (power arg (sub (add c n)
3096 (add a 1)))
3097 (power (sub 1 arg)
3098 (sub (add a b) c))
3099 fun)
3100 arg n)))
3102 ;; Transform F(a,b;c;z) to F(a,b;c+n;z), given F(a,b;c;z)
3103 (defun as-15.2.6 (a b c n arg fun)
3104 ;; A&S 15.2.6
3105 ;; F(a,b;c+n;z) = poch(c,n)/poch(c-a,n)/poch(c-b,n)*(1-z)^(c+n-a-b)
3106 ;; *diff((1-z)^(a+b-c)*fun,z,n)
3107 (assert (>= n 0))
3108 (mul (factf c n)
3109 (inv (factf (sub c a) n))
3110 (inv (factf (sub c b) n))
3111 (inv (power (sub 1 arg) (sub (add a b)
3112 (add c n))))
3113 ($diff (mul (power (sub 1 arg) (sub (add a b) c))
3114 fun)
3115 arg n)))
3117 ;; Transform F(a,b;c;z) to F(a+n, b; c+n; z)
3118 (defun as-15.2.7 (a b c n arg fun)
3119 ;; A&S 15.2.7
3120 ;; F(a+n,b;c+n;z) = (-1)^n*poch(c,n)/poch(a,n)/poch(c-b,n)*(1-z)^(1-a)
3121 ;; *diff((1-z)^(a+n-1)*fun, z, n)
3122 (assert (>= n 0))
3123 (mul (power -1 n)
3124 (factf c n)
3125 (inv (factf a n))
3126 (inv (factf (sub c b) n))
3127 (power (sub 1 arg) (sub 1 a))
3128 ($diff (mul (power (sub 1 arg) (sub (add a n) 1))
3129 fun)
3130 arg n)))
3132 ;; Transform F(a,b;c;z) to F(a-n, b; c-n; z)
3133 (defun as-15.2.8 (axax b c n arg fun)
3134 ;; A&S 15.2.8
3135 ;; F(a-n,b;c-n;z) = 1/poch(c-n,n)/(z^(c-n-1)*(1-z)^(b-c))
3136 ;; *diff(z^(c-1)*(1-z^(b-c+n)*fun, z, n))
3137 (declare (ignore axax))
3138 (assert (>= n 0))
3139 (mul (inv (factf (sub c n) n))
3140 (inv (mul (power arg (sub (sub c n) 1))
3141 (power (sub 1 arg) (sub b c))))
3142 ($diff (mul (power arg (sub c 1))
3143 (power (sub 1 arg) (add (sub b c) n))
3144 fun)
3145 arg n)))
3147 ;; Transform F(a,b;c;z) to F(a+n,b+n;c+n;z)
3148 (defun as-15.2.2 (a b c n arg fun)
3149 ;; A&S 15.2.2
3150 ;; F(a+n,b+n; c+n;z) = poch(c,n)/poch(a,n)/poch(b,n)
3151 ;; *diff(fun, z, n)
3152 (assert (>= n 0))
3153 (mul (factf c n)
3154 (inv (factf a n))
3155 (inv (factf b n))
3156 ($diff fun arg n)))
3158 ;; Transform F(a,b;c;z) to F(a-n,b-n;c-n;z)
3159 (defun as-15.2.9 (a b c n arg fun)
3160 ;; A&S 15.2.9
3161 ;; F(a-n,b-n; c-n;z) = 1/poch(c-n,n)/(z^(c-n-1)*(1-z)^(a+b-c-n))
3162 ;; *diff(z^(c-1)*(1-z)^(a+b-c)*fun, z, n)
3163 (assert (>= n 0))
3164 (mul (inv (factf (sub c n) n))
3165 (inv (mul (power arg (sub (sub c n) 1))
3166 (power (sub 1 arg) (sub (add a b)
3167 (add c n)))))
3168 ($diff (mul (power arg (sub c 1))
3169 (power (sub 1 arg) (sub (add a b) c))
3170 fun)
3171 arg n)))
3173 (defun step4-int (a b c)
3174 (if (> a b)
3175 (step4-int b a c)
3176 (let* ((s (gensym (symbol-name '#:step4-var-)))
3177 (m (1- a))
3178 (n (1- b))
3179 (ell (sub c 3//2))
3180 (res (cond ((eq (checksigntm var) '$negative)
3181 ;; F(1,1;3/2;z) =
3182 ;; -%i*log(%i*sqrt(zn)+sqrt(1-zn))/(sqrt(1-zn)*sqrt(zn))
3183 ;; for z < 0
3184 (let ((root1-z (power (sub 1 s) (inv 2)))
3185 (rootz (power s (inv 2))))
3186 (mul -1 '$%i
3187 (mlog (add (mul '$%i rootz)
3188 root1-z))
3189 (inv root1-z)
3190 (inv rootz))))
3192 ;; F(1,1;3/2;z) = asin(sqrt(zp))/(sqrt(1-zp)*sqrt(zp))
3193 ;; for z > 0
3194 (let ((root1-z (power (sub 1 s) (inv 2)))
3195 (rootz (power s (inv 2))))
3196 (mul (masin rootz)
3197 (inv root1-z)
3198 (inv rootz)))))))
3199 ;; Start with res = F(1,1;3/2;z). Compute F(m,1;3/2;z)
3200 (setf res (as-15.2.3 1 1 3//2 m s res))
3201 ;; We now have res = C*F(m,1;3/2;z). Compute F(m,n;3/2;z)
3202 (setf res (as-15.2.3 1 a 3//2 n s res))
3203 ;; We now have res = C*F(m,n;3/2;z). Now compute F(m,n;3/2+ell;z):
3204 (subst var s
3205 (cond ((minusp ell)
3206 (as-15.2.4 a b 3//2 (- ell) s res))
3208 (as-15.2.6 a b 3//2 ell s res)))))))
3210 ;;Pattern match for s(ymbolic) + c(onstant) in parameter
3211 (defun s+c (exp)
3212 (m2 exp '((mplus) ((coeffpt)(f nonnump)) ((coeffpp)(c $numberp)))))
3214 (defun nonnump (z)
3215 (cond ((not ($numberp z)) t)
3216 (t nil)))
3218 ;;Algor. III from thesis:determines which Differ. Formula to use
3219 (defun algiii (fun m n aprime)
3220 (let ((mm (abs m))
3221 (nn (abs n)))
3222 (cond ((and (nni m) (nni n))
3223 (cond ((< m n)
3224 (f81 fun m n aprime))
3226 (f85 fun mm nn aprime))))
3227 ((and (hyp-negp n) (hyp-negp m))
3228 (cond ((> (abs m) (abs n))
3229 (f86 fun mm nn aprime))
3231 (f82 fun mm nn aprime))))
3232 ((and (hyp-negp m) (nni n))
3233 (f83 fun mm nn aprime))
3235 (f84 fun mm nn aprime)))))
3237 ;; Factorial function:x*(x+1)*(x+2)...(x+n-1)
3239 ;; FIXME: This appears to be identical to fctrl above
3240 (defun factf (x n)
3241 (cond ((zerop n) 1)
3242 (t (mul x (factf (add x 1) (sub n 1))))))
3244 ;;Formula #85 from Yannis thesis:finds by differentiating F[2,1](a,b,c,z)
3245 ;; given F[2,1](a+m,b,c+n,z) where b=-a and c=1/2, n,m integers
3247 ;; Like F81, except m > n.
3249 ;; F(a+m,-a;c+n;z), m > n, c = 1/2, m and n are non-negative integers
3251 ;; A&S 15.2.3
3252 ;; diff(z^(a+m-n-1)*F(a,-a;1/2;z),z,m-n) = poch(a,m-n)*z^(a-1)*F(a+m-n,-a;1/2;z)
3254 ;; A&S 15.2.7
3255 ;; diff((1-z)^(a+m-1)*F(a+m-n,-a;1/2;z),z,n)
3256 ;; = (-1)^n*poch(a+m-n,n)*poch(1/2+a,n)/poch(1/2,n)*(1-z)^(a+m-n-1)
3257 ;; * F(a+m,-a;1/2+n;z)
3259 (defun f85 (fun m n a)
3260 (mul (factf (inv 2) n)
3261 (inv (power -1 n))
3262 (inv (factf (sub (add a m)
3265 (inv (factf (sub (inv 2)
3266 (mul a -1))
3268 (inv (factf a (- m n)))
3269 (power (sub 1 'ell) (sub (sub (add 1 n) m) a))
3270 ($diff (mul (power (sub 1 'ell) (sub (add a m) 1))
3271 (power 'ell (sub 1 a))
3272 ($diff (mul (power 'ell (sub (add a m -1) n))
3273 fun)
3274 'ell (- m n)))
3275 'ell n)))
3277 ;;Used to find negative things that are not integers,eg RAT's
3278 (defun hyp-negp (x)
3279 (cond ((equal (asksign x) '$negative)
3281 (t nil)))
3283 ;; F(a,-a+m; c+n; z) where m,n are non-negative integers, m < n, c = 1/2.
3285 ;; A&S 15.2.6
3286 ;; diff((1-z)^(a+b-c)*F(a,b;c;z),z,n)
3287 ;; = poch(c-a,n)*poch(c-b,n)/poch(c,n)*(1-z)^(a+b-c-n)*F(a,b;c+n;z)
3289 ;; A&S 15.2.7:
3290 ;; diff((1-z)^(a+m-1))*F(a,b;c;z),z,m)
3291 ;; = (-1)^m*poch(a,m)*poch(c-b,m)/poch(c,m)*(1-z)^(a-1)*F(a+m,b;c+m;z)
3293 ;; Rewrite F(a,-a+m; c+n;z) as F(-a+m, a; c+n; z). Then apply 15.2.6
3294 ;; to F(-a,a;1/2;z), differentiating n-m times:
3296 ;; diff((1-z)^(-1/2)*F(-a,a;1/2;z),z,n-m)
3297 ;; = poch(1/2+a,n-m)*poch(1/2-a,n-m)/poch(1/2,n-m)*(1-z)^(-1/2-n+m)*F(-a,a;1/2+n-m;z)
3299 ;; Multiply this result by (1-z)^(n-a-1/2) and apply 15.2.7, differentiating m times:
3301 ;; diff((1-z)^(m-a-1)*F(-a,a;1/2+n-m;z),z,m)
3302 ;; = (-1)^m*poch(-a,m)*poch(1/2+n-m-a,m)/poch(1/2+n-m)*(1-z)^(-a-1)*F(-a+m,a;1/2+n;z)
3304 ;; Which gives F(-a+m,a;1/2+n;z), which is what we wanted.
3305 (defun f81 (fun m n a)
3306 (mul (factf (add (inv 2) (- n m)) m)
3307 (factf (inv 2) (- n m))
3308 (inv (power -1 m))
3309 (inv (factf a m))
3310 (inv (factf (add (inv 2) n (sub a m)) m))
3311 (inv (factf (sub (inv 2) a) (- n m)))
3312 (inv (factf (add (inv 2) a) (- n m)))
3313 (power (sub 1 'ell) (sub 1 a))
3314 ($diff (mul (power (sub 1 'ell) (add a n (inv -2)))
3315 ($diff (mul (power (sub 1 'ell) (inv -2))
3316 fun)
3317 'ell (- n m)))
3318 'ell m)))
3320 ;; Like f86, but |n|>=|m|
3322 ;; F(a-m,-a;1/2-n;z) where n >= m >0
3324 ;; A&S 15.2.4
3325 ;; diff(z^(c-1)*F(a,b;c;z),z,n)
3326 ;; = poch(c-n,n)*z^(c-n-1)*F(a;b;c-n;z)
3328 ;; A&S 15.2.8:
3329 ;; diff(z^(c-1)*(1-z)^(b-c+n)*F(a,b;c;z),z,n)
3330 ;; = poch(c-n,n)*z^(c-n-1)*(1-z)^(b-c)*F(a-n,b;c-n;z)
3332 ;; For our problem:
3334 ;; diff(z^(-1/2)*F(a,-a;1/2;z),z,n-m)
3335 ;; = poch(1/2-n+m,n-m)*z^(m-n-1/2)*F(a,-a;1/2-n+m;z)
3337 ;; diff(z^(m-n-1/2)*(1-z)^(n-a-1/2)*F(a,-a;1/2-n+m;z),z,m)
3338 ;; = poch(1/2-n,m)*z^(-1/2-n)*(1-z)^(n-m-a-1/2)*F(a-m,-a;1/2-n;z)
3340 ;; So
3342 ;; G(z) = z^(m-n-1/2)*F(a,-a;1/2-n+m;z)
3343 ;; = z^(n-m+1/2)/poch(1/2-n+m,n-m)*diff(z^(-1/2)*F(a,-a;1/2;z),z,n-m)
3345 ;; F(a-m,-a;1/2-n;z)
3346 ;; = z^(n+1/2)*(1-z)^(m+a-1/2-n)/poch(1/2-n,m)*diff((1-z)^(n-a-1/2)*G(z),z,m)
3347 (defun f82 (fun m n a)
3348 (mul (inv (factf (sub (inv 2) n) m))
3349 (inv (factf (sub (add (inv 2) m) n) (- n m)))
3350 (power 'ell (add n (inv 2)))
3351 (power (sub 1 'ell) (sub (add m (inv 2) a) n))
3352 ($diff (mul (power (sub 1 'ell)
3353 (sub (sub n a) (inv 2)))
3354 ($diff (mul (power 'ell (inv -2)) fun)
3355 'ell
3356 (- n m)))
3357 'ell
3358 m)))
3360 ;; F(a+m,-a;1/2+n;z) with m,n integers and m < 0, n >= 0
3362 ;; Write this more clearly as F(a-m,-a;1/2+n;z), m > 0, n >= 0
3363 ;; or equivalently F(a-m,-a;c+n;z)
3365 ;; A&S 15.2.6
3366 ;; diff((1-z)^(-1/2)*F(a,-a;1/2;z),z,n)
3367 ;; = poch((1/2+a,n)*poch(1/2-a,n)/poch(1/2,n)*(1-z)^(-1/2-n)
3368 ;; * F(a,-a;1/2+n;z)
3370 ;; A&S 15.2.5
3371 ;; diff(z^(n+m-a-1/2)*(1-z)^(-1/2-n)*F(a,-a;1/2+n;z),z,m)
3372 ;; = poch(1/2+n-a,m)*z^(1/2+n-a-1)*(1-z)^(-1/2-n-m)*F(a-m,-a;1/2+n;z)
3373 ;; = poch(1/2+n-a,m)*z^(n-a-1/2)*(1-z)^(-1/2-n-m)*F(a-m,-a;1/2+n;z)
3375 ;; (1-z)^(-1/2-n)*F(a,-a;1/2+n;z)
3376 ;; = poch(1/2,n)/poch(1/2-a,n)/poch(1/2+a,n)*diff((1-z)^(-1/2)*F(a,-a;1/2;z),z,n)
3378 ;; F(a-m,-a;1/2+n;z)
3379 ;; = (1-z)^(n+m+1/2)*z^(a-n+1/2)/poch(1/2+n-a,m)
3380 ;; *diff(z^(n+m-a-1/2)*(1-z)^(-1/2-n)*F(a,-a;1/2+n;z),z,m)
3381 (defun f83 (fun m n a)
3382 (mul (factf (inv 2) n)
3383 (inv (factf (sub (inv 2) a) n))
3384 (inv (factf (sub (add n (inv 2)) a) m))
3385 (inv (factf (add (inv 2) a) n))
3386 (power (sub 1 'ell) (add m n (inv 2)))
3387 (power 'ell (add (sub a n) (inv 2)))
3388 ($diff (mul (power 'ell (sub (sub (+ m n) a) (inv 2)))
3389 ($diff (mul (power (sub 1 'ell)
3390 (inv -2))
3391 fun)
3392 'ell
3394 'ell
3395 m)))
3397 ;; The last case F(a+m,-a;c+n;z), m,n integers, m >= 0, n < 0
3399 ;; F(a+m,-a;1/2-n;z)
3401 ;; A&S 15.2.4:
3402 ;; diff(z^(c-1)*F(a,b;c;z),z,n) = poch(c-n,n)*z^(c-n-1)*F(a,b;c-n;z)
3404 ;; A&S 15.2.3:
3405 ;; diff(z^(a+m-1)*F(a,b;c;z),z,m) = poch(a,n)*z^(a-1)*F(a+n,b;c;z)
3407 ;; For our problem:
3409 ;; diff(z^(-1/2)*F(a,-a;1/2;z),z,n) = poch(1/2-n,n)*z^(-n-1/2)*F(a,-a;1/2-n;z)
3411 ;; diff(z^(a+m-1)*F(a,-a;1/2-n;z),z,m) = poch(a,m)*z^(a-1)*F(a+m,-a;1/2-n;z)
3412 (defun f84 (fun m n a)
3413 (mul (inv (mul (factf a m)
3414 (factf (sub (inv 2) n) n)))
3415 (power 'ell (sub 1 a))
3416 ($diff (mul (power 'ell (sub (add a m n) (inv 2)))
3417 ($diff (mul (power 'ell (inv -2)) fun)
3418 'ell
3420 'ell
3421 m)))
3423 ;; Like f82, but |n|<|m|
3425 ;; F(a-m,-a;1/2-n;z), 0 < n < m
3427 ;; A&S 15.2.5
3428 ;; diff(z^(c-a+n-1)*(1-z)^(a+b-c)*F(a,b;c;z),z,n)
3429 ;; = poch(c-a,n)*z^(c-a-1)*(1-z)^(a+b-c-n)*F(a-n,b;c;z)
3431 ;; A&S 15.2.8:
3432 ;; diff(z^(c-1)*(1-z)^(b-c+n)*F(a,b;c;z),z,n)
3433 ;; = poch(c-n,n)*z^(c-n-1)*(1-z)^(b-c)*F(a-n,b;c-n;z)
3435 ;; For our problem:
3437 ;; diff(z^(-a+m-n-1/2)*(1-z)^(-1/2)*F(a,-a;1/2;z),z,m-n)
3438 ;; = poch(1/2-a,m-n)*z^(-a-1/2)*(1-z)^(-1/2-m+n)*F(a-m+n,-a;1/2;z)
3440 ;; diff(z^(-1/2)*(1-z)^(-a-1/2+n)*F(a-m+n,-a;1/2;z),z,n)
3441 ;; = poch(1/2-n,n)*z^(-n-1/2)*(1-z)^(-a-1/2)*F(a-m,-a;1/2-n;z)
3443 ;; G(z) = z^(-a-1/2)*(1-z)^(-1/2-m+n)*F(a-m+n,-a;1/2;z)
3444 ;; = 1/poch(1/2-a,m-n)*diff(z^(-a+m-n-1/2)*(1-z)^(-1/2)*F(a,-a;1/2;z),z,m-n)
3446 ;; F(a-m,-a;1/2-n;z)
3447 ;; = z^(n+1/2)*(1-z)^(a+1/2)/poch(1/2-n,n)
3448 ;; *diff(z^(-1/2)*(1-z)^(-a-1/2+n)*F(a-m+n,-a;1/2;z),z,n)
3449 ;; = z^(n+1/2)*(1-z)^(a+1/2)/poch(1/2-n,n)
3450 ;; *diff(z^a*(1-z)^(m-a)*G(z),z,n)
3452 (defun f86 (fun m n a)
3453 (mul (inv (mul (factf (sub (inv 2) n) n)
3454 (factf (sub (inv 2) a) (- m n))))
3455 (power 'ell (add n (inv 2)))
3456 (power (sub 1 'ell) (add (inv 2) a))
3457 ($diff (mul (power 'ell a)
3458 (power (sub 1 'ell) (sub m a))
3459 ($diff (mul (power 'ell
3460 (sub (sub (sub m n) (inv 2)) a))
3461 (power (sub 1 'ell)
3462 (inv -2))
3463 fun)
3464 'ell (- m n)))
3465 'ell n)))
3467 ;; F(-1/2+n, 1+m; 1/2+l; z)
3468 (defun hyp-atanh (a b c)
3469 ;; We start with F(-1/2,1;1/2;z) = 1-sqrt(z)*atanh(sqrt(z)). We can
3470 ;; derive the remaining forms by differentiating this enough times.
3472 ;; FIXME: Do we need to assume z > 0? We do that anyway, here.
3473 (let* ((s (gensym (symbol-name '#:hyp-atanh-)))
3474 (n (add a 1//2))
3475 (m (sub b 1))
3476 (ell (sub c 1//2))
3477 (res (sub 1 (mul (power s '((rat simp) 1 2))
3478 (take '(%atanh) (power s '((rat simp) 1 2))))))
3479 (new-a -1//2)
3480 (new-b 1)
3481 (new-c 1//2))
3482 ;; The total number of derivates we compute is n + m + ell. We
3483 ;; should do something to reduce the number of derivatives.
3484 #+nil
3485 (progn
3486 (format t "a ,b ,c = ~a ~a ~a~%" a b c)
3487 (format t "n, m, ell = ~a ~a ~a~%" n m ell)
3488 (format t "init a b c = ~a ~a ~a~%" new-a new-b new-c))
3489 (cond ((alike1 (sub n ell) 0)
3490 ;; n = ell so we can use A&S 15.2.7 or A&S 15.2.8
3491 (cond ((plusp n)
3492 (setf res (as-15.2.7 new-a new-b new-c n s res)))
3494 (setf res (as-15.2.8 new-a new-b new-c (- n) s res))))
3495 (setf new-a (add new-a n))
3496 (setf new-c (add new-c n)))
3498 ;; Adjust ell and then n. (Does order matter?)
3499 (cond ((plusp ell)
3500 (setf res (as-15.2.6 new-a new-b new-c ell s res))
3501 (setf new-c (add new-c ell)))
3503 (setf res (as-15.2.4 new-a new-b new-c (- ell) s res))
3504 (setf new-c (add new-c ell))))
3505 #+nil
3506 (progn
3507 (maxima-display res)
3508 (format t "new a b c = ~a ~a ~a~%" new-a new-b new-c))
3509 (cond ((plusp n)
3510 ;; A&S 15.2.3
3511 (setf res (as-15.2.3 new-a new-b new-c n s res))
3512 (setf new-a (add new-a n)))
3514 ;; A&S 15.2.5
3515 (setf res (as-15.2.5 new-a new-b new-c (- n) s res))
3516 (setf new-a (add new-a n))))))
3517 #+nil
3518 (progn
3519 (format t "new a b c = ~a ~a ~a~%" new-a new-b new-c)
3520 (maxima-display res))
3521 ;; Finally adjust m by swapping the a and b parameters, since the
3522 ;; hypergeometric function is symmetric in a and b.
3523 (cond ((plusp m)
3524 (setf res (as-15.2.3 new-b new-a new-c m s res))
3525 (setf new-b (add new-b m)))
3527 (setf res (as-15.2.5 new-b new-a new-c (- m) s res))
3528 (setf new-b (add new-b m))))
3529 #+nil
3530 (progn
3531 (format t "new a b c = ~a ~a ~a~%" new-a new-b new-c)
3532 (maxima-display res))
3533 ;; Substitute the argument into the expression and simplify the result.
3534 (sratsimp (maxima-substitute var s res))))
3536 (eval-when
3537 #+gcl (compile)
3538 #-gcl (:compile-toplevel)
3539 (declare-top (unspecial var *par* checkcoefsignlist))