Replace some constant expressions with functions
[maxima.git] / src / hyp.lisp
blobf2f154db4847580d264c35fb4f1d79e2841aa9dc
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 var))
21 (defvar *debug-hyp* nil)
23 (defmvar $prefer_whittaker nil)
25 ;; When T give result in terms of gamma_incomplete and not gamma_incomplete_lower
26 (defmvar $prefer_gamma_incomplete nil)
28 ;; When NIL do not automatically expand polynomials as a result
29 (defmvar $expand_polynomials t)
31 (eval-when
32 (:execute :compile-toplevel)
33 (defmacro simp (x) `(simplifya ,x ()))
35 ;; The macro MABS has been renamed to HYP-MABS in order to
36 ;; avoid conflict with the Maxima symbol MABS. The other
37 ;; M* macros defined here should probably be similarly renamed
38 ;; for consistency. jfa 03/27/2002
40 (defmacro hyp-mabs (x) `(simp `((mabs) ,,x)))
42 (defmacro msqrt (x) `(m^t ,x 1//2))
44 (defmacro mexpt (x) `(m^t '$%e ,x))
46 (defmacro mlog (x) `(simp `((%log) ,,x)))
48 (defmacro msin (x) `(simp `((%sin) ,,x)))
50 (defmacro mcos (x) `(simp `((%cos) ,,x)))
52 (defmacro masin (x) `(simp `((%asin) ,,x)))
54 (defmacro matan (x) `(simp `((%atan) ,,x)))
56 (defmacro mgamma (x) `(simp `((%gamma) ,,x)))
58 (defmacro mbinom (x y) `(simp `((%binomial) ,,x ,,y)))
60 (defmacro merf (x) `(simp `((%erf) ,,x)))
62 (defmacro =1//2 (x) `(alike1 ,x 1//2))
65 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
67 ;;; Functions moved from hypgeo.lisp to this place.
68 ;;; These functions are no longer used in hypgeo.lisp.
70 ;; Gamma function
71 (defun gm (expr)
72 (simplifya (list '(%gamma) expr) nil))
74 ;; sin(x)
75 (defun sin% (arg)
76 (list '(%sin) arg))
78 ;; cos(x)
79 (defun cos% (arg)
80 (list '(%cos) arg))
82 ;; Test if X is a number, either Lisp number or a maxima rational.
83 (defun nump (x)
84 (cond ((atom x)
85 (numberp x))
86 ((not (atom x))
87 (eq (caar (simplifya x nil)) 'rat))))
89 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
91 (defun hyp-integerp (x)
92 ;; In this file, maxima-integerp was used in many places. But it
93 ;; seems that this code expects maxima-integerp to return T when it
94 ;; is really an integer, not something that was declared an integer.
95 ;; But I'm not really sure if this is true everywhere, but it is
96 ;; true in some places.
98 ;; Thus, we replace all calls to maxima-integerp with hyp-integerp,
99 ;; which, for now, returns T only when the arg is an integer.
100 ;; Should we do something more?
101 (and (maxima-integerp x) (integerp x)))
103 ;; Main entry point for simplification of hypergeometric functions.
105 ;; F(a1,a2,a3,...;b1,b2,b3;z)
107 ;; L1 is a (maxima) list of an's, L2 is a (maxima) list of bn's.
108 (defmfun $hgfred (arg-l1 arg-l2 arg)
109 (flet ((arg-ok (a)
110 (and (listp a)
111 (eq (caar a) 'mlist))))
112 (unless (arg-ok arg-l1)
113 (merror (intl:gettext "hgfred: first argument must be a list; found: ~:M") arg-l1))
114 (unless (arg-ok arg-l2)
115 (merror (intl:gettext "hgfred: second argument must be a list; found: ~:M") arg-l2)))
117 ;; Do we really want $radexpand set to '$all? This is probably a
118 ;; bad idea in general, but we'll leave this in for now until we can
119 ;; verify find all of the code that does or does not need this and
120 ;; until we can verify all of the test cases are correct.
121 (let (;;($radexpand '$all)
122 (var arg)
123 (*par* arg)
124 (*checkcoefsignlist* nil))
125 (hgfsimp-exec (cdr arg-l1) (cdr arg-l2) arg)))
127 (defun hgfsimp-exec (arg-l1 arg-l2 arg)
128 (let* ((l1 (copy-tree arg-l1))
129 (l2 (copy-tree arg-l2))
130 ($exponentialize nil)
131 (res (hgfsimp l1 l2 arg)))
132 ;; I think hgfsimp returns FAIL and UNDEF for cases where it
133 ;; couldn't reduce the function.
134 (cond ((eq res 'fail)
135 (fpqform l1 l2 arg))
136 ((eq res 'undef)
137 '$und)
139 res))))
141 (defun hgfsimp (arg-l1 arg-l2 var)
142 (prog (resimp listcmdiff)
143 (setq arg-l1 (macsimp arg-l1)
144 arg-l2 (macsimp arg-l2)
145 resimp (simpg arg-l1 arg-l2))
146 (cond ((not (eq (and (consp resimp) (car resimp)) 'fail))
147 (return resimp))
148 ((and (not (zerop1 var)) ; Do not call splitfpq for a zero argument
149 (setq listcmdiff
150 (intdiffl1l2 (cadr resimp) (caddr resimp))))
151 (return (splitpfq listcmdiff
152 (cadr resimp)
153 (caddr resimp))))
155 (return (dispatch-spec-simp (cadr resimp)
156 (caddr resimp)))))))
158 (defun macsimp (expr)
159 (mapcar #'(lambda (index) ($expand index)) expr))
161 ;; Simplify the parameters. If L1 and L2 have common elements, remove
162 ;; them from both L1 and L2.
163 (defun simpg (arg-l1 arg-l2)
164 (let ((il (zl-intersection arg-l1 arg-l2)))
165 (cond ((null il)
166 (simpg-exec arg-l1 arg-l2))
168 (simpg-exec (del il arg-l1)
169 (del il arg-l2))))))
171 (defun del (a b)
172 (cond ((null a) b)
174 (del (cdr a) (delete (car a) b :count 1 :test #'equal)))))
176 ;; Handle the simple cases where the result is either a polynomial, or
177 ;; is undefined because we divide by zero.
178 (defun simpg-exec (arg-l1 arg-l2)
179 (let (n)
180 (cond ((zerop-in-l arg-l1)
181 ;; A zero in the first index means the series terminates
182 ;; after the first term, so the result is always 1.
184 ((setq n (hyp-negp-in-l arg-l1))
185 ;; A negative integer in the first series means we have a
186 ;; polynomial.
187 (create-poly arg-l1 arg-l2 n))
188 ((and (or (zerop-in-l arg-l2)
189 (hyp-negp-in-l arg-l2))
190 (every #'mnump arg-l1)
191 (every #'mnump arg-l2))
192 ;; A zero or negative number in the second index means we
193 ;; eventually divide by zero, so we're undefined. But only
194 ;; do this if both indices contain numbers. See Bug
195 ;; 1858964 for discussion.
196 'undef)
198 ;; We failed so more complicated stuff needs to be done.
199 (append (list 'fail) (list arg-l1) (list arg-l2))))))
201 (defun intdiffl1l2 (arg-l1 arg-l2)
202 (cond ((null arg-l1)
203 nil)
205 (intdiff arg-l1 arg-l2))))
207 ;; For each element x in arg-l1 and y in arg-l2, compute d = x - y.
208 ;; Find the smallest such non-negative integer d and return (list x
209 ;; d).
210 (defun intdiff (arg-l1 arg-l2)
211 (let ((result nil))
212 ;; Compute all possible differences between elements in arg-l1 and
213 ;; arg-l2. Only save the ones where the difference is a positive
214 ;; integer
215 (dolist (x arg-l1)
216 (dolist (y arg-l2)
217 (let ((diff (sub x y)))
218 (when (nni diff)
219 (push (list x diff) result)))))
220 ;; Find the smallest one and return it.
221 (let ((min (first result)))
222 (dolist (x (rest result))
223 (when (< (second x) (second min))
224 (setf min x)))
225 min)))
227 ;; Create the appropriate polynomial for the hypergeometric function.
228 (defun create-poly (arg-l1 arg-l2 n)
229 (let ((len1 (length arg-l1))
230 (len2 (length arg-l2)))
231 ;; n is the smallest (in magnitude) negative integer in L1. To
232 ;; make everything come out right, we need to make sure this value
233 ;; is first in L1. This is ok, the definition of the
234 ;; hypergeometric function does not depend on the order of values
235 ;; in L1.
236 (setf arg-l1 (cons n (remove n arg-l1 :count 1)))
237 (cond ((and (equal len1 2)
238 (equal len2 1))
239 (2f1polys arg-l1 arg-l2 n))
240 ((and (equal len1 1)
241 (equal len2 1))
242 (1f1polys arg-l2 n))
243 ((and (equal len1 2)
244 (zerop len2))
245 (2f0polys arg-l1 n))
246 (t (create-any-poly arg-l1 arg-l2 (mul -1 n))))))
248 (defun 1f1polys (arg-l2 n)
249 (let* ((c (car arg-l2))
250 (n (mul -1 n))
251 (fact1 (mul (power 2 n)
252 (take '(mfactorial) n)
253 (inv (power -1 n))))
254 ;; For all of the polynomials here, I think it's ok to
255 ;; replace sqrt(z^2) with z because when everything is
256 ;; expanded out they evaluate to exactly the same thing. So
257 ;; $radexpand $all is ok here.
258 (fact2 (let (($radexpand '$all))
259 (mul (power 2 '((rat simp) 1 2))
260 (power var '((rat simp) 1 2))))))
261 (cond ((alike1 c '((rat simp) 1 2))
262 ;; A&S 22.5.56
263 ;; hermite(2*n,x) = (-1)^n*(2*n)!/n!*M(-n,1/2,x^2)
265 ;; So
266 ;; M(-n,1/2,x) = n!/(2*n)!*(-1)^n*hermite(2*n,sqrt(x))
268 ;; But hermite(m,x) = 2^(m/2)*He(sqrt(2)*sqrt(x)), so
270 ;; M(-n,1/2,x) = (-1)^n*n!*2^n/(2*n)!*He(2*n,sqrt(2)*sqrt(x))
271 (mul fact1
272 (inv (take '(mfactorial) (add n n)))
273 (hermpol (add n n) fact2)))
274 ((alike1 c '((rat simp) 3 2))
275 ;; A&S 22.5.57
276 ;; hermite(2*n+1,x) = (-1)^n*(2*n+1)!/n!*M(-n,3/2,x^2)*2*x
278 ;; So
279 ;; M(-n,3/2,x) = n!/(2*n+1)!*(-1)^n*hermite(2*n+1,sqrt(x))/2/sqrt(x)
281 ;; and in terms of He, we get
283 ;; 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))
284 (mul fact1
285 (inv (power 2 '((rat simp) 1 2)))
286 (inv (take '(mfactorial) (add n n 1)))
287 (hermpol (add n n 1) fact2)
288 ;; Similarly, $radexpand here is ok to convert sqrt(z^2) to z.
289 (let (($radexpand '$all))
290 (inv (power var '((rat simp) 1 2))))))
291 ((alike1 c (neg (add n n)))
292 ;; 1F1(-n; -2*n; z)
293 (mul (power -1 n)
294 (inv (take '(%binomial) (add n n) n))
295 (lagpol n (sub c 1) var)))
297 ;; A&S 22.5.54:
299 ;; gen_laguerre(n,alpha,x) =
300 ;; binomial(n+alpha,n)*hgfred([-n],[alpha+1],x);
302 ;; Or hgfred([-n],[alpha],x) =
303 ;; gen_laguerre(n,alpha-1,x)/binomial(n+alpha-1,n)
305 ;; But 1/binomial(n+alpha-1,n) = n!*(alpha-1)!/(n+alpha-1)!
306 ;; = n! / (alpha * (alpha + 1) * ... * (alpha + n - 1)
307 ;; = n! / poch(alpha, n)
309 ;; See Bug 1858939.
311 ;; However, if c is not a number leave the result in terms
312 ;; of gamma functions. I (rtoy) think this makes for a
313 ;; simpler result, especially if n is rather large. If the
314 ;; user really wants the answer expanded out, makefact and
315 ;; minfactorial will do that.
316 (if (and (integerp n)
317 (numberp c))
318 (if (or (zerop c)
319 (and (minusp c) (> c (- n))))
320 (merror (intl:gettext "hgfred: 1F1(~M; ~M; ~M) not defined.")
321 (- n) c var)
322 (mul (take '(mfactorial) n)
323 (inv (take '($pochhammer) c n))
324 (lagpol n (sub c 1) var)))
325 (let (($gamma_expand t)) ; Expand Gamma function
326 (mul (take '(mfactorial) n)
327 (take '(%gamma) c)
328 (inv (take '(%gamma) (add c n)))
329 (lagpol n (sub c 1) var))))))))
331 ;; Hermite polynomial. Note: The Hermite polynomial used here is the
332 ;; He polynomial, defined as (A&S 22.5.18, 22.5.19)
334 ;; He(n,x) = 2^(-n/2)*H(n,x/sqrt(2))
336 ;; or
338 ;; H(n,x) = 2^(n/2)*He(x*sqrt(2))
340 ;; We want to use H, as used in specfun, so we need to convert it.
341 (defun hermpol (n arg)
342 (let ((fact (inv (power 2 (div n 2))))
343 (x (mul arg (inv (power 2 '((rat simp) 1 2))))))
344 (mul fact
345 (if (and $expand_polynomials (integerp n))
346 (mfuncall '$hermite n x)
347 (list '($hermite simp) n x)))))
349 ;; Generalized Laguerre polynomial
350 (defun lagpol (n a arg)
351 (if (and (numberp a) (zerop a))
352 (if (and $expand_polynomials (integerp n))
353 (mfuncall '$laguerre n arg)
354 (list '($laguerre simp) n arg))
355 (if (and $expand_polynomials (integerp n))
356 (mfuncall '$gen_laguerre n a arg)
357 (list '($gen_laguerre simp) n a arg))))
359 (defun 2f0polys (arg-l1 n)
360 (let ((a (car arg-l1))
361 (b (cadr arg-l1)))
362 (when (alike1 (sub b a) '((rat simp) -1 2))
363 (rotatef a b))
364 (cond ((alike1 (sub b a) '((rat simp) 1 2))
365 ;; 2F0(-n,-n+1/2,z) or 2F0(-n-1/2,-n,z)
366 (interhermpol n a b var))
368 ;; 2F0(a,b;z)
369 (let ((x (mul -1 (inv var)))
370 (order (mul -1 n)))
371 (mul (take '(mfactorial) order)
372 (inv (power x order))
373 (inv (power -1 order))
374 (lagpol order (mul -1 (add b order)) x)))))))
376 ;; Compute 2F0(-n,-n+1/2;z) and 2F0(-n-1/2,-n;z) in terms of Hermite
377 ;; polynomials.
379 ;; Ok. I couldn't find any references giving expressions for this, so
380 ;; here's a quick derivation.
382 ;; 2F0(-n,-n+1/2;z) = sum(pochhammer(-n,k)*pochhammer(-n+1/2,k)*z^k/k!, k, 0, n)
384 ;; It's easy to show pochhammer(-n,k) = (-1)^k*n!/(n-k)!
385 ;; Also, it's straightforward but tedious to show that
386 ;; pochhammer(-n+1/2,k) = (-1)^k*(2*n)!*(n-k)!/2^(2*k)/n!/(2*n-2*k)!
388 ;; Thus,
389 ;; 2F0 = (2*n)!*sum(z^k/2^(2*k)/k!/(2*n-2*k)!)
391 ;; Compare this to the expression for He(2*n,x) (A&S 22.3.11):
393 ;; He(2*n,x) = (2*n)! * x^(2*n) * sum((-1)^k*x^(-2*k)/2^k/k!/(2*n-2*k)!)
395 ;; Hence,
397 ;; 2F0(-n,-n+1/2;z) = y^n * He(2*n,y)
399 ;; where y = sqrt(-2/x)
401 ;; For 2F0(-n-1/2,-n;z) = sum(pochhammer(-n,k)*pochhammer(-n-1/2,k)*z^k/k!)
402 ;; we find that
404 ;; pochhammer(-n-1/2,k) = pochhammer(-(n+1)+1/2,k)
405 ;; =
407 ;; So 2F0 = (2*n+1)!*sum(z^k/z^(2*k)/k!/(2*n+1-2*k)!)
409 ;; and finally
411 ;; 2F0(-n-1/2,-n;z) = y^(2*n+1) * He(2*n+1,y)
413 ;; with y as above.
414 (defun interhermpol (n a b x)
415 (let ((arg (power (div 2 (mul -1 x)) '((rat simp) 1 2)))
416 (order (cond ((alike1 a n)
417 (mul -2 n))
418 ((alike1 b n)
419 (sub 1 (add n n))))))
420 ;; 2F0(-n,-n+1/2;z) = y^(-2*n)*He(2*n,y)
421 ;; 2F0(-n-1/2,-n;z) = y^(-(2*n+1))*He(2*n+1,y)
423 ;; where y = sqrt(-2/var);
424 (mul (power arg (mul -1 order))
425 (hermpol order arg))))
427 ;; F(n,b;c;z), where n is a negative integer (number or symbolic).
428 ;; The order of the arguments must be checked by the calling routine.
429 (defun 2f1polys (arg-l1 arg-l2 n)
430 (prog (l v lgf)
431 ;; Since F(a,b;c;z) = F(b,a;c;z), make sure L1 has the negative
432 ;; integer first, so we have F(-n,d;c;z)
433 ;; Remark: 2f1polys is only called from create-poly. create-poly calls
434 ;; 2f1polys with the correct order of arg-l1. This test is not necessary.
435 ; (cond ((not (alike1 (car arg-l1) n))
436 ; (setq arg-l1 (reverse arg-l1))))
438 (cond ((mnump *par*)
439 ;; The argument of the hypergeometric function is a number.
440 ;; Avoid the following check which does not work for this case.
441 (setq v (div (add (cadr arg-l1) n) 2)))
443 ;; Check if (b+n)/2 is free of the argument.
444 ;; At this point of the code there is no check of the return value
445 ;; of vfvp. When nil we have no solution and the result is wrong.
446 (setq l (vfvp (div (add (cadr arg-l1) n) 2)))
447 (setq v (cdr (assoc 'v l :test #'equal)))))
449 (cond ((and (or (not (integerp n))
450 (not $expand_polynomials))
451 ;; Assuming we have F(-n,b;c;z), then v is (b+n)/2.
452 ;; See if it can be a Legendre function.
453 ;; We call legpol-core because we know that arg-l1 has the
454 ;; correct order. This avoids questions about the parameter b
455 ;; from legpol-core, because legpol calls legpol-core with
456 ;; both order of arguments.
457 (setq lgf (legpol-core (car arg-l1)
458 (cadr arg-l1)
459 (car arg-l2))))
460 (return lgf))
461 ((and (or (not (integerp n))
462 (not $expand_polynomials))
463 (alike1 (sub (car arg-l2) v) '((rat simp) 1 2)))
464 ;; A&S 15.4.5:
465 ;; F(-n, n+2*a; a+1/2; x) = n!*gegen(n, a, 1-2*x)/pochhammer(2*a,n)
467 ;; So v = a, and (car arg-l2) = a + 1/2.
468 (return (mul
469 (cond ((zerop1 v) 1)
470 (t (mul (take '(mfactorial) (mul -1 n))
471 (inv (take '($pochhammer)
472 (mul 2 v)
473 (mul -1 n))))))
474 (gegenpol (mul -1 n)
476 (sub 1 (mul 2 *par*))))))
478 ;; A&S 15.4.6 says
479 ;; F(-n, n + a + 1 + b; a + 1; x)
480 ;; = n!*jacobi_p(n,a,b,1-2*x)/pochhammer(a+1,n)
481 (return (mul (take '(mfactorial) (mul -1 n))
482 (inv (take '($pochhammer) (car arg-l2) (mul -1 n)))
483 (jacobpol (mul -1 n)
484 (add (car arg-l2) -1)
485 (sub (mul 2 v) (car arg-l2))
486 (sub 1 (mul 2 *par*)))))))))
488 ;; Jacobi polynomial
489 (defun jacobpol (n a b x)
490 (if (and $expand_polynomials (integerp n))
491 (mfuncall '$jacobi_p n a b x)
492 (list '($jacobi_p simp) n a b x)))
494 ;; Gegenbauer (Ultraspherical) polynomial. We use ultraspherical to
495 ;; match specfun.
496 (defun gegenpol (n v x)
497 (cond ((equal v 0) (tchebypol n x))
499 (if (and $expand_polynomials (integerp n))
500 (mfuncall '$ultraspherical n v x)
501 `(($ultraspherical simp) ,n ,v ,x)))))
503 ;; Legendre polynomial
504 (defun legenpol (n x)
505 (if (and $expand_polynomials (integerp n))
506 (mfuncall '$legendre_p n x)
507 `(($legendre_p simp) ,n ,x)))
509 ;; Chebyshev polynomial
510 (defun tchebypol (n x)
511 (if (and $expand_polynomials (integerp n))
512 (mfuncall '$chebyshev_t n x)
513 `(($chebyshev_t simp) ,n ,x)))
515 ;; Expand the hypergeometric function as a polynomial. No real checks
516 ;; are made to ensure the hypergeometric function reduces to a
517 ;; polynomial.
518 (defmfun $hgfpoly (arg-l1 arg-l2 arg)
519 (let ((var arg)
520 (*par* arg)
521 (n (hyp-negp-in-l (cdr arg-l1))))
522 (create-any-poly (cdr arg-l1) (cdr arg-l2) (- n))))
524 (defun create-any-poly (arg-l1 arg-l2 n)
525 (prog (result exp prodnum proden)
526 (setq result 1 prodnum 1 proden 1 exp 1)
527 loop
528 (cond ((zerop n) (return result)))
529 (setq prodnum (mul prodnum (mull arg-l1))
530 proden (mul proden (mull arg-l2)))
531 (setq result
532 (add result
533 (mul prodnum
534 (power var exp)
535 (inv proden)
536 (inv (factorial exp)))))
537 (setq n (sub n 1)
538 exp (add exp 1)
539 arg-l1 (incr1 arg-l1)
540 arg-l2 (incr1 arg-l2))
541 (go loop)))
543 ;; Compute the product of the elements of the list L.
544 (defun mull (l)
545 (reduce #'mul l :initial-value 1))
547 ;; Add 1 to each element of the list L
548 (defun incr1 (l)
549 (mapcar #'(lambda (x) (add x 1)) l))
551 ;; Figure out the orders of generalized hypergeometric function we
552 ;; have and call the right simplifier.
553 (defun dispatch-spec-simp (arg-l1 arg-l2)
554 (let ((len1 (length arg-l1))
555 (len2 (length arg-l2)))
556 (cond ((and (< len1 2)
557 (< len2 2))
558 ;; pFq where p and q < 2.
559 (simp2>f<2 arg-l1 arg-l2 len1 len2))
560 ((and (equal len1 2)
561 (equal len2 1))
562 ;; 2F1
563 (simp2f1 arg-l1 arg-l2))
564 ((and (equal len1 2)
565 (equal len2 0))
566 ;; 2F0(a,b; ; z)
567 (cond ((and (maxima-integerp (car arg-l1))
568 (member ($sign (car arg-l1)) '($neg $nz)))
569 ;; 2F0(-n,b; ; z), n a positive integer
570 (2f0polys arg-l1 (car arg-l1)))
571 ((and (maxima-integerp (cadr arg-l1))
572 (member ($sign (cadr arg-l1)) '($neg $nz)))
573 ;; 2F0(a,-n; ; z), n a positive integer
574 (2f0polys (reverse arg-l1) (cadr arg-l1)))
576 (fpqform arg-l1 arg-l2 var))))
577 ((and (= len1 1)
578 (= len2 2))
579 ;; Some 1F2 forms
580 (simp1f2 arg-l1 arg-l2))
581 ((and (= len1 2)
582 (= len2 2))
583 (simp2f2 arg-l1 arg-l2))
585 ;; We don't have simplifiers for any other hypergeometric
586 ;; function.
587 (fpqform arg-l1 arg-l2 var)))))
589 ;; Handle the cases where the number of indices is less than 2.
590 (defun simp2>f<2 (arg-l1 arg-l2 len1 len2)
591 (cond ((and (zerop len1) (zerop len2))
592 ;; hgfred([],[],z) = e^z
593 (power '$%e var))
594 ((and (zerop len1) (equal len2 1))
595 (cond
596 ((zerop1 var)
597 ;; hgfred([],[b],0) = 1
598 (add var 1))
600 ;; hgfred([],[b],z)
602 ;; The hypergeometric series is then
604 ;; 1+sum(z^k/k!/[b*(b+1)*...(b+k-1)], k, 1, inf)
606 ;; = 1+sum(z^k/k!*gamma(b)/gamma(b+k), k, 1, inf)
607 ;; = sum(z^k/k!*gamma(b)/gamma(b+k), k, 0, inf)
608 ;; = gamma(b)*sum(z^k/k!/gamma(b+k), k, 0, inf)
610 ;; Note that bessel_i(b,z) has the series
612 ;; (z/2)^(b)*sum((z^2/4)^k/k!/gamma(b+k+1), k, 0, inf)
614 ;; bessel_i(b-1,2*sqrt(z))
615 ;; = (sqrt(z))^(b-1)*sum(z^k/k!/gamma(b+k),k,0,inf)
616 ;; = z^((b-1)/2)*hgfred([],[b],z)/gamma(b)
618 ;; So this hypergeometric series is a Bessel I function:
620 ;; hgfred([],[b],z) = bessel_i(b-1,2*sqrt(z))*z^((1-b)/2)*gamma(b)
621 (bestrig (car arg-l2) var))))
622 ((zerop len2)
623 ;; hgfred([a],[],z) = 1 + sum(binomial(a+k,k)*z^k) = 1/(1-z)^a
624 (power (sub 1 var) (mul -1 (car arg-l1))))
626 ;; The general case of 1F1, the confluent hypergeomtric function.
627 (confl arg-l1 arg-l2 var))))
629 ;; Computes
631 ;; bessel_i(a-1,2*sqrt(x))*gamma(a)*x^((1-a)/2)
633 ;; if x > 0
635 ;; or
637 ;; bessel_j(a-1,2*sqrt(x))*gamma(a)*x^((1-a)/2)
639 ;; if x < 0.
641 ;; If a is half of an odd integer and small enough, the Bessel
642 ;; functions are expanded in terms of trig or hyperbolic functions.
644 (defun bestrig (b x)
645 ;; I think it's ok to have $radexpand $all here so that sqrt(z^2) is
646 ;; converted to z.
647 (let (($radexpand '$all))
648 (if (mminusp x)
649 ;; gamma(b)*(-x)^((1-b)/2)*bessel_j(b-1,2*sqrt(-x))
650 (sratsimp (mul (power (neg x) (div (sub 1 b) 2))
651 (take '(%gamma) b)
652 (take '(%bessel_j)
653 (sub b 1)
654 (mul 2 (power (neg x) '((rat simp) 1 2))))))
655 ;; gamma(b)*(x)^((1-b)/2)*bessel_i(b-1,2*sqrt(x))
656 (sratsimp (mul (power x (div (sub 1 b) 2))
657 (take '(%gamma) b)
658 (take '(%bessel_i)
659 (sub b 1)
660 (mul 2 (power x '((rat simp) 1 2)))))))))
662 ;; Kummer's transformation. A&S 13.1.27
664 ;; M(a,b,z) = e^z*M(b-a,b,-z)
665 (defun kummer (arg-l1 arg-l2)
666 (mul (list '(mexpt) '$%e var)
667 (confl (list (sub (car arg-l2) (car arg-l1)))
668 arg-l2 (mul -1 var))))
670 ;; Return non-NIL if any element of the list L is zero.
672 (defun zerop-in-l (l)
673 (some #'(lambda (x)
674 (and (numberp x) (zerop x)))
677 ;; If the list L contains a negative integer, return the most positive
678 ;; of the negative integers. Otherwise return NIL.
679 (defun hyp-negp-in-l (l)
680 (let ((max-neg nil))
681 (dolist (x l)
682 (when (and (integerp x) (minusp x))
683 (if max-neg
684 (setf max-neg (max max-neg x))
685 (setf max-neg x))))
686 max-neg))
688 ;; Compute the intersection of L1 and L2, possibly destructively
689 ;; modifying L2. Perserves duplications in L1.
690 (defun zl-intersection (arg-l1 arg-l2)
691 (cond ((null arg-l1) nil)
692 ((member (car arg-l1) arg-l2 :test #'equal)
693 (cons (car arg-l1)
694 (zl-intersection (cdr arg-l1)
695 (delete (car arg-l1) arg-l2 :count 1 :test #'equal))))
696 (t (zl-intersection (cdr arg-l1) arg-l2))))
698 ;; Whittaker M function. A&S 13.1.32 defines it as
700 ;; %m[k,u](z) = exp(-z/2)*z^(u+1/2)*M(1/2+u-k,1+2*u,z)
702 ;; where M is the confluent hypergeometric function.
703 (defun whitfun (k m var)
704 (list '(mqapply) (list '($%m array) k m) var))
706 (defun simp1f2 (arg-l1 arg-l2)
707 "Simplify 1F2([a], [b,c], var). ARG-L1 is the list [a], and ARG-L2 is
708 the list [b, c]. The dependent variable is the (special variable)
709 VAR."
710 (let ((a (car arg-l1)))
711 (destructuring-bind (b c)
712 arg-l2
713 (cond
714 ((and (alike1 a 1//2)
715 (alike1 b 3//2)
716 (alike1 c 3//2))
717 ;; Sine integral, expintegral_si(z) = z*%f[1,2]([1/2],[3/2,3/2], -z^2/4)
718 ;; (See http://functions.wolfram.com/06.37.26.0001.01)
719 ;; Subst z = 2*sqrt(-y) into the above to get
721 ;; expintegral_si(2*sqrt(-y)) = 2*%f[1,2]([1/2],[3/2,3/2], y)*sqrt(-y)
723 ;; Hence %f[1,2]([1/2],[3/2,3/2], y) = expintegral_si(2*sqrt(-y))/2/sqrt(-y)
724 (div (ftake '%expintegral_si (mul 2 (power (neg var) 1//2)))
725 (mul 2 (power (neg var) 1//2))))
727 (fpqform arg-l1 arg-l2 var))))))
729 (defun simp2f2 (arg-l1 arg-l2)
730 (destructuring-bind (a1 a2)
731 arg-l1
732 (destructuring-bind (b1 b2)
733 arg-l2
734 (cond
735 ((and (= a1 1)
736 (= a2 1)
737 (= b1 2)
738 (= b2 2))
739 ;; Cosine integral
740 ;; expintegral_ci(z) = -z^2/4*%f[2,2]([1,1],[2,2], -z^2/4) + log(z) + %gamma
741 ;; (See http://functions.wolfram.com/06.38.26.0001.01)
743 ;; Subst z = 2*sqrt(-y) into the above to get
745 ;; expintegral_ci(2*sqrt(-y)) =
746 ;; %f[2,2]([1,1],[2,2],y)*y + log(2*sqrt(-y)) + %gamma
748 ;; Hence
750 ;; %f[2,2]([1,1],[2,2],y) =
751 ;; (expintegral_ci(2*sqrt(-y)) - log(2*sqrt(-y)) - %gamma)/y
753 ;; But see also http://functions.wolfram.com/06.35.26.0001.01
754 ;; which shows that expintegral_ei can be written in terms of
755 ;; %f[2,2]([1,1],[2,2],z) too. Not sure how to choose
756 ;; between the two representations.
757 (div (sub (ftake '%expintegral_ci (mul 2 (power (neg var) 1//2)))
758 (add (ftake '%log (mul 2 (power (neg var) 1//2)))
759 '$%gamma))
760 var))
762 (fpqform arg-l1 arg-l2 var))))))
764 (defvar $trace2f1 nil
765 "Enables simple tracing of simp2f1 so you can see how it decides
766 what approach to use to simplify hypergeometric functions")
768 (defun simp2f1 (arg-l1 arg-l2)
769 (prog (a b c lgf)
770 (setq a (car arg-l1) b (cadr arg-l1) c (car arg-l2))
772 (cond ((zerop1 var)
773 ;; F(a,b; c; 0) = 1
774 (return (add var 1))))
776 (when $trace2f1
777 (format t "Tracing SIMP2F1~%")
778 (format t " Test a or b negative integer ...~%"))
780 ;; Look if a or b is a symbolic negative integer. The routine
781 ;; 2f1polys handles this case.
782 (cond ((and (maxima-integerp a) (member ($sign a) '($neg $nz)))
783 (return (2f1polys arg-l1 arg-l2 a))))
784 (cond ((and (maxima-integerp b) (member ($sign b) '($neg $nz)))
785 (return (2f1polys (list b a) arg-l2 b))))
787 (when $trace2f1
788 (format t " Test F(1,1,2)...~%"))
790 (cond ((and (alike1 a 1)
791 (alike1 b 1)
792 (alike1 c 2))
793 ;; F(1,1;2;z) = -log(1-z)/z, A&S 15.1.3
794 (when $trace2f1
795 (format t " Yes~%"))
796 (return (mul (inv (mul -1 var))
797 (take '(%log) (add 1 (mul -1 var)))))))
799 (when $trace2f1
800 (format t " Test c = 1/2 or c = 3/2...~%"))
802 (cond ((or (alike1 c '((rat simp) 3 2))
803 (alike1 c '((rat simp) 1 2)))
804 ;; F(a,b; 3/2; z) or F(a,b;1/2;z)
805 (cond ((setq lgf (trig-log (list a b) (list c)))
806 (when $trace2f1
807 (format t " Yes: trig-log~%"))
808 (return lgf)))))
810 (when $trace2f1
811 (format t " Test |a-b|=1/2...~%"))
813 (cond ((or (alike1 (sub a b) '((rat simp) 1 2))
814 (alike1 (sub b a) '((rat simp) 1 2)))
815 ;; F(a,b;c;z) where |a-b|=1/2
816 (cond ((setq lgf (hyp-cos a b c))
817 (when $trace2f1
818 (format t " Yes: hyp-cos~%"))
819 (return lgf)))))
821 (when $trace2f1
822 (format t " Test a,b are integers, c is a numerical integer...~%"))
824 (cond ((and (maxima-integerp a)
825 (maxima-integerp b)
826 (hyp-integerp c))
827 ;; F(a,b;c;z) when a, and b are integers (or are declared
828 ;; to be integers) and c is a integral number.
829 (setf lgf (simpr2f1 (list a b) (list c)))
830 (unless (symbolp lgf) ; Should be more specific! (DK 01/2010)
831 (when $trace2f1
832 (format t " Yes: simpr2f1~%"))
833 (return lgf))))
835 (when $trace2f1
836 (format t " Test a+b and c+1/2 are numerical integers...~%"))
838 (cond ((and (hyp-integerp (add c (inv 2)))
839 (hyp-integerp (add a b)))
840 ;; F(a,b;c;z) where a+b is an integer and c+1/2 is an
841 ;; integer.
842 (when $trace2f1
843 (format t " Yes: step4~%"))
844 (return (step4 a b c))))
846 (when $trace2f1
847 (format t " Test a-b+1/2 is a numerical integer...~%"))
849 (cond ((hyp-integerp (add (sub a b) (inv 2)))
850 ;; F(a,b;c,z) where a-b+1/2 is an integer
851 (cond ((setq lgf (step7 a b c))
852 (unless (atom lgf)
853 (when $trace2f1
854 (format t " Yes: step7~%"))
855 (return lgf))))))
857 #+nil
858 (when (and (hyp-integerp (add c 1//2))
859 (or (and (hyp-integerp (add a 1//2))
860 (hyp-integerp b))
861 (and (hyp-integerp (add b 1//2))
862 (hyp-integerp a))))
863 (when $trace2f1
864 (format t " Test for atanh: a+1/2, b, and c+1/2 are integers~%"))
865 (return (hyp-atanh a b c)))
867 (when (hyp-integerp (add c 1//2))
868 (when $trace2f1
869 (format t " Test for atanh: c+1/2 is an integer~%"))
870 (cond ((and (hyp-integerp (add a 1//2))
871 (hyp-integerp b))
872 (when $trace2f1
873 (format t " atanh with integers a+1/2 and b ~%"))
874 (return (hyp-atanh a b c)))
875 ((and (hyp-integerp (add b 1//2))
876 (hyp-integerp a))
877 (when $trace2f1
878 (format t " atanh with integers a and b+1/2 ~%"))
879 (return (hyp-atanh b a c)))))
881 (when $trace2f1
882 (format t " Test for Legendre function...~%"))
884 (cond ((setq lgf (legfun a b c))
885 (unless (atom lgf)
886 ;; LEGFUN returned something interesting, so we're done.
887 (when $trace2f1
888 (format t " Yes: case 1~%"))
889 (return lgf))
890 ;; LEGFUN didn't return anything, so try it with the args
891 ;; reversed, since F(a,b;c;z) is F(b,a;c;z).
892 (setf lgf (legfun b a c))
893 (when lgf
894 (when $trace2f1
895 (format t " Yes: case 2~%"))
896 (return lgf))))
898 (when $trace2f1
899 (format t "'simp2f1-will-continue-in~%"))
900 (return (fpqform arg-l1 arg-l2 var))))
902 ;; As best as I (rtoy) can tell, step7 is meant to handle an extension
903 ;; of hyp-cos, which handles |a-b|=1/2 and either a+b-1/2 = c or
904 ;; c=a+b+1/2.
906 ;; Based on the code, step7 wants a = s + m/n and c = 2*s+k/l. For
907 ;; hyp-cos, we want c-2*a to be a integer. Or k/l-2*m/n is an
908 ;; integer.
909 #+(or)
910 (progn
911 (defun step7 (a b c)
912 (prog (l m n k mn kl sym sym1 r)
913 ;; Write a = sym + mn, c = sym1 + kl
914 (setq l (s+c a)
915 sym (cdras 'f l)
916 mn (cdras 'c l)
917 l (s+c c)
918 syrm1 (cdras 'f l))
919 ;; We only handle the case where 2*sym = sym1.
920 (cond ((not (equal (mul sym 2) sym1))
921 (return nil)))
922 (setq kl (cdras 'c l))
923 ;; a-b+1/2 is an integer.
924 (setq l (s+c b)
925 r (sub (add (inv 2) (cdras 'c l)) mn)
926 m ($num mn)
927 n ($denom mn)
928 k ($num kl)
929 l ($denom kl))
930 ;; We have a = m*s+m/n, c = 2*m*s+k/l.
931 (cond ((equal (* 2 l) n)
932 (cond ((hyp-integerp (/ (- k m) n))
933 (return (hyp-algv k l m n a b c))))))
934 (cond ((hyp-integerp (/ k (* 2 l)))
935 (cond ((hyp-integerp (/ m n))
936 (return (hyp-algv k l m n a b c)))
937 (t (return nil))))
938 ((hyp-integerp (/ m n))
939 (return nil))
940 ((hyp-integerp (/ (- (* k n) (* 2 l m)) (* 2 l n)))
941 (return (hyp-algv k l m n a b c))))
942 (return nil)))
944 (defun getxy (k l m n)
945 (prog (x y)
946 (setq y 0)
947 loop
948 (cond ((hyp-integerp (setq x (truncate (+ y
949 (truncate k l)
950 (* -2 (quot m n)))
951 2)))
952 (return (list x y))))
953 (incf y 2)
954 (go loop)))
956 (defun hyp-algv (k l m n a b c)
957 (prog (x y xy a-b w)
958 (setq a-b (sub a b))
959 (setq xy (getxy k l m n)
960 x (car xy)
961 y (cadr xy))
962 (cond ((< x 0)(go out)))
963 (cond ((< x y)
964 (cond ((< (add a-b x (inv 2)) 0)
965 (return (f88 x y a b c fun)))
966 (t (return (f87 x y a c fun)))))
968 (cond ((< (add a-b x (inv 2)) 0)
969 (return (f90 x y a c fun)))
970 (t (return (f89 x y a c fun))))))
972 (setq w (* x -1))
973 (cond ((< (- (add a-b (inv 2)) w) 0)
974 (return (f92 x y a c fun)))
975 (t (return (f91 x y a c fun))))))
977 (defun f87 (x y a c fun )
978 (mul
979 (inv (mul (factf c y)
980 (factf (sub (add c y) (add a x)) (- x y))
981 (factf (sub (add c y) (add a x (inv 2)))
982 (sub (add a x (inv 2)) (add a (inv 2))))))
983 (power 'ell (sub 1 c))
984 (power (sub 1 'ell)(sub (add y c) (add a (inv 2))))
985 ($diff (mul (power 'ell (add a x))
986 (power (sub 1 'ell)(mul -1 a))
987 ($diff (mul (power 'ell (sub (add (inv 2) x) y))
988 ($diff (mul (power 'ell (sub (add c y) 1))
989 (power (sub 1 'ell)
990 (sub (add (inv 2)
991 (mul 2 a)
992 (* 2 x))
993 (add c y)))
994 fun)
995 'ell x))
996 'ell (- x y)))
997 'ell y)))
999 (defun f88 (x y a b c fun )
1000 (mul
1001 (inv (mul (factf c y)
1002 (factf (sub (add c y) (add a x)) (- x y))
1003 (factf (add a (inv 2) x)
1004 (sub b (sub x (sub a (inv 2)))))))
1005 (power 'ell (sub 1 c))
1006 (power (sub 1 'ell)(sub (add y c) (add a (inv 2))))
1007 ($diff (mul (power 'ell (add a x))
1008 (power (sub 1 'ell)(mul -1 a))
1009 ($diff (mul (power 'ell (sub c (sub x (sub (inv 2) (mul a 2))))))
1010 (power (sub 1 'ell) (sub (add a x b)(sub c y)))
1011 ($diff (mul (power 'ell (sub b 1 ))
1013 fun)
1014 'ell (sub b (sub a (sub x (inv 2)))))
1015 'ell (- x y)))
1016 'ell y)))
1019 ;; A new version of step7.
1020 (defun step7 (a b c)
1021 ;; To get here, we know that a-b+1/2 is an integer. To make further
1022 ;; progress, we want a+b-1/2-c to be an integer too.
1024 ;; Let a-b+1/2 = p and a+b+1/2-c = q where p and q are (numerical)
1025 ;; integers.
1027 ;; A&S 15.2.3 and 15.2.5 allows us to increase or decrease a. Then
1028 ;; F(a,b;c;z) can be written in terms of F(a',b;c;z) where a' = a-p
1029 ;; and a'-b = a-p-b = 1/2. Also, a'+b+1/2-c = a-p+b+1/2-c = q-p =
1030 ;; r, which is also an integer.
1032 ;; A&S 15.2.4 and 15.2.6 allows us to increase or decrese c. Thus,
1033 ;; we can write F(a',b;c;z) in terms of F(a',b;c',z) where c' =
1034 ;; c+r. Now a'-b=1/2 and a'+b+1/2-c' = a-p+b+1/2-c-r =
1035 ;; a+b+1/2-c-p-r = q-p-(q-p)=0.
1037 ;; Thus F(a',b;c';z) is exactly the form we want for hyp-cos. In
1038 ;; fact, it's A&S 15.1.14: F(a,a+1/2,;1+2a;z) =
1039 ;; 2^(2*a)*(1+sqrt(1-z))^(-2*a).
1040 (declare (special var))
1041 (let ((q (sub (add a b (inv 2))
1042 c)))
1043 (unless (hyp-integerp q)
1044 ;; Wrong form, so return NIL
1045 (return-from step7 nil))
1046 ;; Since F(a,b;c;z) = F(b,a;c;z), we need to figure out which form
1047 ;; to use. The criterion will be the fewest number of derivatives
1048 ;; needed.
1049 (let* ((p1 (add (sub a b) (inv 2)))
1050 (r1 (sub q p1))
1051 (p2 (add (sub b a) (inv 2)))
1052 (r2 (sub q p2)))
1053 (when $trace2f1
1054 (format t "step 7:~%")
1055 (format t " q, p1, r1 = ~A ~A ~A~%" q p1 r1)
1056 (format t " p2, r2 = ~A ~A~%" p2 r2))
1057 (cond ((<= (+ (abs p1) (abs r1))
1058 (+ (abs p2) (abs r2)))
1059 (step7-core a b c))
1061 (step7-core b a c))))))
1063 (defun step7-core (a b c)
1064 (let* ((p (add (sub a b) (inv 2)))
1065 (q (sub (add a b (inv 2))
1067 (r (sub q p))
1068 (a-prime (sub a p))
1069 (c-prime (add 1 (mul 2 a-prime))))
1070 ;; Ok, p and q are integers. We can compute something. There are
1071 ;; four cases to handle depending on the sign of p and r.
1073 ;; We need to differentiate some hypergeometric forms, so use 'ell
1074 ;; as the variable.
1075 (let ((fun (hyp-cos a-prime (add a-prime 1//2) c-prime 'ell)))
1076 ;; fun is F(a',a'+1/2;2*a'+1;z), or NIL
1077 (when fun
1078 (when $trace2f1
1079 (format t "step7-core~%")
1080 (format t " a,b,c = ~A ~A ~A~%" a b c)
1081 (format t " p,q,r = ~A ~A ~A~%" p q r)
1082 (format t " a', c' = ~A ~A~%" a-prime c-prime)
1083 (format t " F(a',a'+1/2; 1+2*a';z) =~%")
1084 (maxima-display fun))
1085 ;; Compute the result, and substitute the actual argument into
1086 ;; result.
1087 (maxima-substitute var 'ell
1088 (cond ((>= p 0)
1089 (cond ((>= r 0)
1090 (step-7-pp a-prime b c-prime p r 'ell fun))
1092 (step-7-pm a-prime b c-prime p r 'ell fun))))
1094 (cond ((>= r 0)
1095 (step-7-mp a-prime b c-prime p r 'ell fun))
1097 (step-7-mm a-prime b c-prime p r 'ell fun))))))))))
1099 ;; F(a,b;c;z) in terms of F(a',b;c';z)
1101 ;; F(a'+p,b;c'-r;z) where p >= 0, r >= 0.
1102 (defun step-7-pp (a b c p r z fun)
1103 ;; Apply A&S 15.2.4 and 15.2.3
1104 (let ((res (as-15.2.4 a b c r z fun)))
1105 (as-15.2.3 a b (sub c r) p z res)))
1107 ;; p >= 0, r < 0
1109 ;; Let r' = -r
1110 ;; F(a'+p,b;c'-r;z) = F(a'+p,b;c'+r';z)
1111 (defun step-7-pm (a b c p r z fun)
1112 ;; Apply A&S 15.2.6 and 15.2.3
1113 (let ((res (as-15.2.6 a b c (- r) z fun)))
1114 (as-15.2.3 a b (sub c r) p z res)))
1116 ;; p < 0, r >= 0
1118 ;; Let p' = -p
1119 ;; F(a'+p,b;c'-r;z) = F(a'-p',b;c'-r;z)
1120 (defun step-7-mp (a b c p r z fun)
1121 ;; Apply A&S 15.2.4 and 15.2.5
1122 (let ((res (as-15.2.4 a b c r z fun)))
1123 (as-15.2.5 a b (sub c r) (- p) z res)))
1125 ;; p < 0 r < 0
1127 ;; Let p' = - p, r' = -r
1129 ;; F(a'+p,b;c'-r;z) = F(a'-p',b;c'+r';z)
1130 (defun step-7-mm (a b c p r z fun)
1131 ;; Apply A&S 15.2.6 and A&S 15.2.5
1132 (let ((res (as-15.2.6 a b c (- r) z fun)))
1133 (as-15.2.5 a b (sub c r) (- p) z res)))
1135 ;; F(a,b;c;z) when a and b are integers (or declared to be integers)
1136 ;; and c is an integral number.
1137 (defun simpr2f1 (arg-l1 arg-l2)
1138 (destructuring-bind (a b)
1139 arg-l1
1140 (destructuring-bind (c)
1141 arg-l2
1142 (let ((inl1p (hyp-integerp a))
1143 (inl1bp (hyp-integerp b))
1144 (inl2p (hyp-integerp c)))
1145 (cond (inl2p
1146 ;; c is an integer
1147 (cond ((and inl1p inl1bp)
1148 ;; a, b, c are (numerical) integers
1149 (derivint a b c))
1150 (inl1p
1151 ;; a and c are integers
1152 (geredno2 b a c))
1153 (inl1bp
1154 ;; b and c are integers.
1155 (geredno2 a b c))
1156 (t 'fail1)))
1157 ;; Can't really do anything else if c is not an integer.
1158 (inl1p
1159 (cond (inl1bp
1162 'c)))
1163 ((eq (caaar arg-l1) 'rat)
1164 ;; How do we ever get here?
1165 (cond (inl1bp
1168 'd)))
1170 'failg))))))
1172 (defun geredno1
1173 (arg-l1 arg-l2)
1174 (cond ((and (> (car arg-l2)(car arg-l1))
1175 (> (car arg-l2)(cadr arg-l1)))
1176 (geredf (car arg-l1)(cadr arg-l1)(car arg-l2)))
1177 (t (gered1 arg-l1 arg-l2 #'hgfsimp))))
1179 (defun geredno2 (a b c)
1180 (cond ((> c b) (geredf b a c))
1181 (t (gered2 a b c))))
1183 ;; Consider F(1,1;2;z). A&S 15.1.3 says this is equal to -log(1-z)/z.
1185 ;; Apply A&S 15.2.2:
1187 ;; 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)
1189 ;; A&S 15.2.7 says:
1191 ;; diff((1-z)^(m+ell)*F(1+ell;1+ell;2+ell;z),z,m)
1192 ;; = (-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)
1194 ;; A&S 15.2.6 gives
1196 ;; diff((1-z)^ell*F(1+ell+m,1+ell;2+ell+m;z),z,n)
1197 ;; = 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)
1199 ;; The derivation above assumes that ell, m, and n are all
1200 ;; non-negative integers. Thus, F(a,b;c;z), where a, b, and c are
1201 ;; integers and a <= b <= c, can be written in terms of F(1,1;2;z).
1202 ;; The result also holds for b <= a <= c, of course.
1204 ;; Also note that the last two differentiations can be combined into
1205 ;; one differention since the result of the first is in exactly the
1206 ;; form required for the second. The code below does one
1207 ;; differentiation.
1209 ;; So if a = 1+ell, b = 1+ell+m, and c = 2+ell+m+n, we have ell = a-1,
1210 ;; m = b - a, and n = c - ell - m - 2 = c - b - 1.
1212 (defun derivint (a b c)
1213 (if (> a b)
1214 (derivint b a c)
1215 (let ((l (- a 1))
1216 (m (- b a))
1217 (n (- c b 1))
1218 (psey (gensym))
1219 result)
1221 (setq result
1222 (mul (power -1 m)
1223 (factorial (+ n m l 1))
1224 (inv (factorial n))
1225 (inv (factorial l))
1226 (inv (factorial (+ n m)))
1227 (inv (factorial (+ m l)))
1228 (power (sub 1 psey) (sub n l))
1229 ($diff (mul (power (sub 1 psey) (+ m l))
1230 ($diff (mul (power psey -1)
1232 (take '(%log) (sub 1 psey)))
1233 psey
1235 psey
1236 (+ n m))))
1237 (if (onep1 var)
1238 ($limit result psey var)
1239 (maxima-substitute var psey result)))))
1241 ;; Handle F(a, b; c; z) for certain values of a, b, and c. See the
1242 ;; comments below for these special values. The optional arg z
1243 ;; defaults to var, which is usually the argument of hgfred.
1244 (defun hyp-cos (a b c &optional (z var))
1245 (let ((a1 (div (sub (add a b) (div 1 2)) 2))
1246 (z1 (sub 1 z)))
1247 ;; a1 = (a+b-1/2)/2
1248 ;; z1 = 1-z
1249 (cond ((eql 0 ($ratsimp (sub (sub (add a b)
1250 (div 1 2))
1251 c)))
1252 ;; a+b-1/2 = c
1254 ;; A&S 15.1.14
1256 ;; F(a,a+1/2;2*a;z)
1257 ;; = 2^(2*a-1)*(1-z)^(-1/2)*(1+sqrt(1-z))^(1-2*a)
1259 ;; But if 1-2*a is a negative integer, let's rationalize the answer to give
1261 ;; F(a,a+1/2;2*a;z)
1262 ;; = 2^(2*a-1)*(1-z)^(-1/2)*(1-sqrt(1-z))^(2*a-1)/z^(2*a-1)
1263 (when $trace2f1
1264 (format t " Case a+b-1/2=c~%"))
1265 (let ((2a-1 (sub (mul a1 2) 1)))
1266 (cond ((and (integerp 2a-1) (plusp 2a-1))
1267 ;; 2^(2*a-1)*(1-z)^(-1/2)*(1-sqrt(1-z))^(2*a-1)/z^(2*a-1)
1268 (mul (power 2 2a-1)
1269 (inv (power z1 1//2))
1270 (power (sub 1 (power z1 1//2)) 2a-1)
1271 (inv (power z 2a-1))))
1273 ;; 2^(2*a-1)*(1-z)^(-1/2)*(1+sqrt(1-z))^(1-2*a)
1274 (mul (power 2 (sub (mul a1 2) 1))
1275 (inv (power z1 (div 1 2)))
1276 (power (add 1
1277 (power z1
1278 (div 1
1279 2)))
1280 (sub 1 (mul 2 a1))))))))
1281 ((eql 0 ($ratsimp (sub (add 1 (mul 2 a1)) c)))
1282 ;; c = 1+2*a1 = a+b+1/2
1284 ;; A&S 15.1.13:
1286 ;; F(a,1/2+a;1+2*a;z) = 2^(2*a)*(1+sqrt(1-z))^(-2*a)
1288 ;; But if 2*a is a positive integer, let's rationalize the answer to give
1290 ;; F(a,1/2+a;1+2*a;z) = 2^(2*a)*(1-sqrt(1-z))^(2*a)/z^(2*a)
1291 (when $trace2f1
1292 (format t " Case c=1+2*a=a+b+1/2~%"))
1293 (let ((2a (sub c 1)))
1294 (cond ((and (integerp 2a) (plusp 2a))
1295 ;; 2^(2*a)*(1-sqrt(1-z))^(2*a)/z^(2*a)
1296 (mul (power 2 2a)
1297 (power (sub 1 (power z1 1//2))
1299 (power z (mul -1 2a))))
1301 ;; 2^(2*a)*(1+sqrt(1-z))^(-2*a)
1302 (mul (power 2 2a)
1303 (power (add 1 (power z1 1//2))
1304 (mul -1 2a))))))))))
1306 ;; Is A a non-negative integer?
1307 (defun nni (a)
1308 (cond ((hyp-integerp a)
1309 (not (minusp a)))))
1312 ;;; The following code doesn't appear to be used at all. Comment it all out for now.
1314 (defun degen2f1
1315 (a b c)
1316 (cond ((eq (quest (sub c b)) '$negative)
1317 (cond ((eq (quest (sub c a)) '$negative)
1318 (gered1 (list a b)(list c) #'hgfsimp))
1319 (t (gered2 a b c))))
1320 ((eq (quest (sub c a)) '$negative)(gered2 b a c))
1321 (t (rest-degen a b c))))
1324 (defun rest-degen
1325 (a b c)
1326 (prog(m n l)
1327 (cond ((nni (setq m (sub a 1)))
1328 (return (rest-degen-1 a b c m))))
1329 (cond ((ni b)(return (rest-degen-2 a b c))))
1330 (cond ((and (nni (setq n (sub c 1)))
1331 (nni (setq m (sub (sub a n) 1)))
1332 (nni (setq l (sub b a)))
1333 (eq (sub (sub c a) b)
1334 (mul -1 (add m m n l 1))))
1335 (return (gered1 (list a b)
1336 (list c)
1337 #'hgfsimp))))
1338 (return (hyp-deg b a c))))
1341 (defun rest-degen-1
1342 (a b c m)
1343 (prog(n l)
1344 (cond ((and (ni b)
1345 (ni (sub (sub c a) b))
1346 (nni (sub (sub c a) 1)))
1347 (return (deg299 a b c))))
1348 (cond ((and (nni (setq n (sub (sub c m) 2)))
1349 (nni (setq l (sub b c)))
1350 (equal (sub (sub c a) b)
1351 (mul -1 (add l m 1))))
1352 (return (gered1 (list a b)
1353 (list c)
1354 #'hgfsimp))))
1355 (cond ((nni (setq l (sub (sub b m) 1)))
1356 (return (rest-degen-1a a b c m l))))
1357 (return (hyp-deg b a c))))
1360 (defun rest-degen-1a
1361 (a b c m l)
1362 (prog(n)
1363 (cond ((and (nni (setq n
1364 (sub (sub (sub c m) l) 2)))
1365 (equal (sub n m)(sub (sub c a) b)))
1366 (return (deg2913 a b c))))
1367 (cond ((and (equal c (mul -1 n))
1368 (equal (sub (sub c a) b)
1369 (mul -1 (add m m l n 2))))
1370 (return (deg2918 a b c))))
1371 (return (hyp-deg b a c))))
1374 (defun rest-degen-2
1375 (a b c)
1376 (prog(m l)
1377 (cond ((and (ni c)(ni (sub (sub c a) b)))
1378 (return (rest-degen-2a a b c))))
1379 (cond ((and (nni (setq m (sub c 1)))
1380 (nni (setq l (sub a c)))
1381 (ni (sub (sub c a) b)))
1382 (return (deg292 a b c))))
1383 (return (hyp-deg b a c))))
1386 (defun rest-degen-2a
1387 (a b c)
1388 (prog()
1389 (cond ((nni (sub a c))
1390 (return (gered1 (list a b)
1391 (list c)
1392 #'hgfsimp))))
1393 (cond ((nni (sub (sub c a) 1))
1394 (return (deg2917 a b c))))
1395 (return (hyp-deg b a c))))
1397 (defun quest
1399 (cond ((numberp a)(checksigntm a))
1400 ((equal (caar a) 'rat)(checksigntm a))
1401 (t nil)))
1403 (defun ni(a)(not (hyp-integerp a)))
1406 (defun hyp-deg
1407 (a b c)
1408 (prog()
1409 (cond (fldeg (setq fldeg nil)
1410 (return (hgfsimp (list a b)
1411 (list c)
1412 var))))
1413 (setq fldeg t)
1414 (return (fpqform (list a b)(list c) var))))
1417 (defun deg2913
1418 (a b c)
1419 (mul (power (mul -1 var)(mul -1 b))
1420 (hgfsimp (list (add b 1 (mul -1 c)) b)
1421 (list (add b 1 (mul -1 a)))
1422 (inv var))))
1425 (defun deg2918
1426 (a b c)
1427 (mul (power var (sub 1 c))
1428 (power (sub 1 var)(add c (mul -1 a)(mul -1 b)))
1429 (hgfsimp (list (sub 1 a)(sub 1 b))
1430 (list (sub 2 c))
1431 var)))
1434 (defun deg2917
1435 (a b c)
1436 (mul (power var (sub 1 c))
1437 (hgfsimp (list (add a 1 (mul -1 c))
1438 (add b 1 (mul -1 c)))
1439 (list (sub 2 c))
1440 var)))
1443 (defun deg299
1444 (a b c)
1445 (mul (power (mul -1 var)(mul -1 a))
1446 (hgfsimp (list a (add a 1 (mul -1 c)))
1447 (list (add a 1 (mul -1 b)))
1448 (inv var))))
1453 ;; Is F(a, b; c; z) is Legendre function?
1455 ;; Lemma 29 (see ref) says F(a, b; c; z) can be reduced to a Legendre
1456 ;; function if two of the numbers 1-c, +/-(a-b), and +/- (c-a-b) are
1457 ;; equal to each other or one of them equals +/- 1/2.
1459 ;; This routine checks for each of the possibilities.
1460 (defun legfun (a b c)
1461 (let ((1-c (sub 1 c))
1462 (a-b (sub a b))
1463 (c-a-b (sub (sub c a) b))
1464 (inv2 (inv 2)))
1465 (cond ((alike1 a-b inv2)
1466 ;; a-b = 1/2
1467 (when $trace2f1
1468 (format t "Legendre a-b = 1/2~%"))
1469 (gered1 (list a b) (list c) #'legf24))
1471 ((alike1 a-b (mul -1 inv2))
1472 ;; a-b = -1/2
1474 ;; For example F(a,a+1/2;c;x)
1475 (when $trace2f1
1476 (format t "Legendre a-b = -1/2~%"))
1477 (legf24 (list a b) (list c) var))
1479 ((alike1 c-a-b '((rat simp) 1 2))
1480 ;; c-a-b = 1/2
1481 ;; For example F(a,b;a+b+1/2;z)
1482 (when $trace2f1
1483 (format t "Legendre c-a-b = 1/2~%"))
1484 (legf20 (list a b) (list c) var))
1486 ((and (alike1 c-a-b '((rat simp) 3 2))
1487 (not (alike1 c 1))
1488 (not (alike1 a -1//2))
1489 (not (alike1 b -1//2)))
1490 ;; c-a-b = 3/2 e.g. F(a,b;a+b+3/2;z) Reduce to
1491 ;; F(a,b;a+b+1/2) and use A&S 15.2.6. But if c = 1, we
1492 ;; don't want to reduce c to 0! Problem: The derivative of
1493 ;; assoc_legendre_p introduces a unit_step function and the
1494 ;; result looks very complicated. And this doesn't work if
1495 ;; a+1/2 or b+1/2 is zero, so skip that too.
1496 (when $trace2f1
1497 (format t "Legendre c-a-b = 3/2~%")
1498 (mformat t " : a = ~A~%" a)
1499 (mformat t " : b = ~A~%" b)
1500 (mformat t " : c = ~A~%" c))
1501 (let ((psey (gensym)))
1502 (maxima-substitute
1503 *par* psey
1504 (mul (power (sub 1 psey) '((rat simp) 3 2))
1505 (add a b '((rat simp) 1 2))
1506 (inv (add b '((rat simp) 1 2)))
1507 (inv (add a '((rat simp) 1 2)))
1508 ($diff (mul (power (sub 1 psey) '((rat simp) -1 2))
1509 (hgfsimp (list a b)
1510 (list (add a b '((rat simp) 1 2)))
1511 psey))
1512 psey
1513 1)))))
1515 ((alike1 c-a-b '((rat simp) -1 2))
1516 ;; c-a-b = -1/2, e.g. F(a,b; a+b-1/2; z)
1517 ;; This case is reduce to F(a,b; a+b+1/2; z) with
1518 ;; F(a,b;c;z) = (1-z)^(c-a-b)*F(c-a,c-b;c;z)
1519 (when $trace2f1
1520 (format t "Legendre c-a-b = -1/2~%"))
1521 (gered1 (list a b) (list c) #'legf20))
1523 ((alike1 1-c a-b)
1524 ;; 1-c = a-b, F(a,b; b-a+1; z)
1525 (when $trace2f1
1526 (format t "Legendre 1-c = a-b~%"))
1527 (gered1 (list a b) (list c) #'legf16))
1529 ((alike1 1-c (mul -1 a-b))
1530 ;; 1-c = b-a, e.g. F(a,b; a-b+1; z)
1531 (when $trace2f1
1532 (format t "Legendre 1-c = b-a~%"))
1533 (legf16 (list a b) (list c) var))
1535 ((alike1 1-c c-a-b)
1536 ;; 1-c = c-a-b, e.g. F(a,b; (a+b+1)/2; z)
1537 (when $trace2f1
1538 (format t "Legendre 1-c = c-a-b~%"))
1539 (gered1 (list a b) (list c) #'legf14))
1541 ((alike1 1-c (mul -1 c-a-b))
1542 ;; 1-c = a+b-c
1544 ;; For example F(a,1-a;c;x)
1545 (when $trace2f1
1546 (format t "Legendre 1-c = a+b-c~%"))
1547 (legf14 (list a b) (list c) var))
1549 ((alike1 a-b (mul -1 c-a-b))
1550 ;; a-b = a+b-c, e.g. F(a,b;2*b;z)
1551 (when $trace2f1
1552 (format t "Legendre a-b = a+b-c~%"))
1553 (legf36 (list a b) (list c) var))
1555 ((or (alike1 1-c inv2)
1556 (alike1 1-c (mul -1 inv2)))
1557 ;; 1-c = 1/2 or 1-c = -1/2
1558 ;; For example F(a,b;1/2;z) or F(a,b;3/2;z)
1559 (when $trace2f1
1560 (format t "Legendre |1-c| = 1/2~%"))
1561 ;; At this point it does not make sense to call legpol. legpol can
1562 ;; handle only cases with a negative integer in the first argument
1563 ;; list. This has been tested already. Therefore we can not get
1564 ;; a result from legpol. For this case a special algorithm is needed.
1565 ;; At this time we return nil.
1566 ;(legpol a b c)
1567 nil)
1569 ((alike1 a-b c-a-b)
1570 ;; a-b = c-a-b
1571 (when $trace2f1
1572 (format t "Legendre a-b = c-a-b~%"))
1573 'legendre-funct-to-be-discovered)
1575 nil))))
1577 ;;; The following legf<n> functions correspond to formulas in Higher
1578 ;;; Transcendental Functions. See the chapter on Legendre functions,
1579 ;;; in particular the table on page 124ff,
1581 ;; Handle the case c-a-b = 1/2
1583 ;; Formula 20:
1585 ;; 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)
1587 ;; See also A&S 15.4.12 and 15.4.13.
1589 ;; Let a = 1/2+n/2-m/2, b = -n/2-m/2, c = 1-m. Then, m = 1-c. And we
1590 ;; have two equivalent expressions for n:
1592 ;; n = c - 2*b - 1 or n = 2*a - c
1594 ;; The code below chooses the first solution. A&S chooses second.
1596 ;; F(a,b;c;w) = 2^(c-1)*gamma(c)*(-w)^((1-c)/2)*P(c-2*b-1,1-c,sqrt(1-w))
1599 (defun legf20 (arg-l1 arg-l2 var)
1600 ;; F(a,b;a+b+1/2;x)
1601 (let* (($radexpand nil)
1602 (b (cadr arg-l1))
1603 (c (car arg-l2))
1604 (a (sub (sub c b) '((rat simp) 1 2)))
1605 (m (sub 1 c))
1606 (n (mul -1 (add b b m))))
1607 ;; m = 1 - c
1608 ;; n = -(2*b+1-c) = c - 1 - 2*b
1609 ;; A&S 15.4.13
1611 ;; F(a,b;a+b+1/2;x) = 2^(a+b-1/2)*gamma(a+b+1/2)*x^((1/2-a-b)/2)
1612 ;; *assoc_legendre_p(a-b-1/2,1/2-a-b,sqrt(1-x))
1613 ;; This formula is correct for all arguments x.
1614 (mul (power 2 (add a b '((rat simp) -1 2)))
1615 (take '(%gamma) (add a b '((rat simp) 1 2)))
1616 (power var
1617 (div (sub '((rat simp) 1 2) (add a b))
1619 (legen n
1621 (power (sub 1 var) '((rat simp) 1 2))
1622 '$p))))
1624 ;; Handle the case a-b = -1/2.
1626 ;; Formula 24:
1628 ;; 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)
1630 ;; See also A&S 15.4.10 and 15.4.11.
1632 ;; Let a = -n/2-m/2, b = 1/2-n/2-m/2, c = 1-m. Then m = 1-c. Again,
1633 ;; we have 2 possible (equivalent) values for n:
1635 ;; n = -(2*a + 1 - c) or n = c-2*b
1637 ;; The code below chooses the first solution.
1639 ;; 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))
1641 ;; 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))
1643 ;; Is there a mistake in 15.4.10 and 15.4.11?
1645 (defun legf24 (arg-l1 arg-l2 var)
1646 (let* (($radexpand nil)
1647 (a (car arg-l1))
1648 (c (car arg-l2))
1649 (m (sub 1 c))
1650 ; (n (mul -1 (add a a m))) ; This is not 2*a-c
1651 (n (sub (add a a) c)) ; but this.
1652 (z (inv (power (sub 1 var) (inv 2)))))
1653 ;; A&S 15.4.10, 15.4.11
1654 (cond ((eq (asksign var) '$negative)
1655 ;; A&S 15.4.11
1657 ;; F(a,a+1/2;c;x) = 2^(c-1)*gamma(c)(-x)^(1/2-c/2)*(1-x)^(c/2-a-1/2)
1658 ;; *assoc_legendre_p(2*a-c,1-c,1/sqrt(1-x))
1659 (mul (inv (power 2 m))
1660 (gm (sub 1 m))
1661 (power (mul -1 var) (div m 2))
1662 (power (sub 1 var) (sub (div m -2) a))
1663 (legen n
1666 '$p)))
1668 (mul (inv (power 2 m))
1669 (gm (sub 1 m))
1670 (power var (div m 2))
1671 (power (sub 1 var) (sub (div m -2) a))
1672 (legen n
1675 '$p))))))
1677 ;; Handle 1-c = a-b
1679 ;; Formula 16:
1681 ;; 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))
1683 ;; See also A&S 15.4.14 and 15.4.15.
1685 ;; Let a = -n, b = -n-m, c = 1-m. Then m = 1-c. We have 2 solutions
1686 ;; for n:
1688 ;; n = -a or n = c-b-1.
1690 ;; The code below chooses the first solution.
1692 ;; F(a,b;c;w) = gamma(c)*w^(1/2-c/2)*(1-w)^(-a)*P(-a,1-c,(1+w)/(1-w));
1694 ;; FIXME: We don't correctly handle the branch cut here!
1695 (defun legf16 (arg-l1 arg-l2 var)
1696 (let* (($radexpand nil)
1697 (a (car arg-l1))
1698 (c (car arg-l2))
1699 ;; m = 1-c = b-a
1700 (m (sub 1 c))
1701 ;; n = -b
1702 ;; m = b - a, so b = a + m
1703 (b (add a m))
1704 (n (mul -1 b))
1705 (z (div (add 1 var)
1706 (sub 1 var))))
1707 (when $trace2f1
1708 (format t "a, c = ~A ~A~%" a c)
1709 (format t "b = ~A~%" b))
1710 ;; A&S 15.4.14, 15.4.15
1711 (cond ((eq (asksign var) '$negative)
1712 ;; A&S 15.4.15
1714 ;; F(a,b;a-b+1,x) = gamma(a-b+1)*(1-x)^(-b)*(-x)^(b/2-a/2)
1715 ;; * assoc_legendre_p(-b,b-a,(1+x)/(1-x))
1717 ;; for x < 0
1718 (mul (take '(%gamma) c)
1719 (power (sub 1 var) (mul -1 b))
1720 (power (mul -1 var) (div m 2))
1721 (legen n m z '$p)))
1723 (mul (take '(%gamma) c)
1724 (power (sub 1 var) (mul -1 b))
1725 (power var (div m 2))
1726 (legen n m z '$p))))))
1728 ;; Handle the case 1-c = a+b-c.
1730 ;; See, for example, A&S 8.1.2 (which
1731 ;; might have a bug?) or
1732 ;; http://functions.wolfram.com/HypergeometricFunctions/LegendreP2General/26/01/02/
1734 ;; Formula 14:
1736 ;; P(n,m,z) = (z+1)^(m/2)*(z-1)^(-m/2)/gamma(1-m)*F(-n,1+n;1-m;(1-z)/2)
1738 ;; See also A&S 8.1.2, 15.4.16, 15.4.17
1740 ;; Let a=-n, b = 1+n, c = 1-m. Then m = 1-c and n has 2 solutions:
1742 ;; n = -a or n = b - 1.
1744 ;; The code belows chooses the first solution.
1746 ;; 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)
1747 (defun legf14 (arg-l1 arg-l2 var)
1748 ;; Set $radexpand to NIL, because we don't want (-z)^x getting
1749 ;; expanded to (-1)^x*z^x because that's wrong for this.
1750 (let* (($radexpand nil)
1751 (a (first arg-l1))
1752 (b (second arg-l1))
1753 (c (first arg-l2))
1754 (m (sub 1 c))
1755 (n (mul -1 a))
1756 (z (sub 1 (mul 2 var))))
1757 (when $trace2f1
1758 (format t "~&legf14~%"))
1759 ;; A&S 15.4.16, 15.4.17
1760 (cond ((not (alike1 (add a b) 1))
1761 ;; I think 15.4.16 and 15.4.17 require the form
1762 ;; F(a,1-a;c;x). That is, a+b = 1. If we don't have it
1763 ;; exit now.
1764 nil)
1765 ((and (eq (asksign var) '$positive)
1766 (eq (asksign (sub 1 var)) '$positive))
1767 (when $trace2f1
1768 (format t " A&S 15.4.17~%"))
1769 ;; A&S 15.4.17
1771 ;; F(a,1-a;c;x) = gamma(c)*x^(1/2-c/2)*(1-x)^(c/2-1/2)*
1772 ;; assoc_legendre_p(-a,1-c,1-2*x)
1774 ;; for 0 < x < 1
1775 (mul (gm c)
1776 (power var (div (sub 1 c) 2))
1777 (power (sub 1 var) (div (sub c 1) 2))
1778 (legen n m z '$p)))
1780 ;; A&S 15.4.16
1782 ;; F(a,1-a;c;z) = gamma(c)*(-z)^(1/2-c/2)*(1-z)^(c/2-1/2)*
1783 ;; assoc_legendre_p(-a,1-c,1-2*z)
1784 (when $trace2f1
1785 (format t " A&S 15.4.17~%"))
1786 (mul (gm c)
1787 (power (mul -1 var) (div (sub 1 c) 2))
1788 (power (sub 1 var) (div (sub c 1) 2))
1789 (legen n m z '$p))))))
1791 ;; Handle a-b = a+b-c
1793 ;; Formula 36:
1795 ;; exp(-%i*m*%pi)*Q(n,m,z) =
1796 ;; 2^n*gamma(1+n)*gamma(1+n+m)*(z+1)^(m/2-n-1)*(z-1)^(-m/2)/gamma(2+2*n)
1797 ;; * hgfred([1+n-m,1+n],[2+2*n],2/(1+z))
1799 ;; Let a = 1+n-m, b = 1+n, c = 2+2*n. then n = b-1 and m = b - a.
1800 ;; (There are other solutions.)
1802 ;; F(a,b;c;z) = 2*gamma(2*b)/gamma(b)/gamma(2*b-a)*w^(-b)*(1-w)^((b-a)/2)
1803 ;; *Q(b-1,b-a,2/w-1)*exp(-%i*%pi*(b-a))
1805 (defun legf36 (arg-l1 arg-l2 var)
1806 (declare (ignore arg-l2))
1807 (let* ((a (car arg-l1))
1808 (b (cadr arg-l1))
1809 (n (sub b 1))
1810 (m (sub b a))
1811 ;;z (div (sub 2 var) var)
1812 (z (sub (div 2 var) 1)))
1813 (mul (inv (power 2 n))
1814 (inv (gm (add 1 n)))
1815 (inv (gm (add 1 n m)))
1816 (inv (power (add z 1)
1817 (add (div m 2)
1818 (mul -1 n)
1819 -1)))
1820 (inv (power (sub z 1) (div m -2)))
1821 (gm (add 2 n n))
1822 (power '$%e (mul -1 '$%i m '$%pi))
1823 (legen n m z '$q))))
1825 (defun legen (n m x pq)
1826 ;; A&S 8.2.1: P(-n-1,m,z) = P(n,m,z)
1827 (let ((n (if (or (member ($sign n) '($neg $nz)) ; negative sign or
1828 (mminusp n)) ; negative form like -n-1
1829 (mul -1 (add 1 n))
1830 n)))
1831 (cond ((equal m 0)
1832 (list (if (eq pq '$q)
1833 '($legendre_q simp)
1834 '($legendre_p simp))
1835 n x))
1837 (list (if (eq pq '$q)
1838 '($assoc_legendre_q simp)
1839 '($assoc_legendre_p simp))
1840 n m x)))))
1842 (defun legpol-core (a b c)
1843 ;; I think for this to be correct, we need a to be a negative integer.
1844 (unless (and (eq '$yes (ask-integerp a))
1845 (eq (asksign a) '$negative))
1846 (return-from legpol-core nil))
1847 (let* ((l (vfvp (div (add b a) 2)))
1848 (v (cdr (assoc 'v l :test #'equal))))
1849 ;; v is (a+b)/2
1850 (cond
1851 ((and (alike1 v '((rat simp) 1 2))
1852 (alike1 c 1))
1853 ;; A&S 22.5.49:
1854 ;; P(n,x) = F(-n,n+1;1;(1-x)/2)
1855 (legenpol (mul -1 a)
1856 (sub 1 (mul 2 var))))
1858 ((and (alike1 c '((rat simp) 1 2))
1859 (alike1 (add b a) '((rat simp) 1 2)))
1860 ;; c = 1/2, a+b = 1/2
1862 ;; A&S 22.5.52
1863 ;; P(2*n,x) = (-1)^n*(2*n)!/2^(2*n)/(n!)^2*F(-n,n+1/2;1/2;x^2)
1865 ;; F(-n,n+1/2;1/2;x^2) = P(2*n,x)*(-1)^n*(n!)^2/(2*n)!*2^(2*n)
1867 (let ((n (mul -1 a)))
1868 (mul (power -1 n)
1869 (power (gm (add n 1)) 2)
1870 (inv (gm (add 1 (mul 2 n))))
1871 (power 2 (mul 2 n))
1872 (legenpol (mul 2 n)
1873 (power var (div 1 2))))))
1875 ((and (alike1 c '((rat simp) 3 2))
1876 (alike1 (add b a) '((rat simp) 3 2)))
1877 ;; c = 3/2, a+b = 3/2
1879 ;; A&S 22.5.53
1880 ;; 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
1882 ;; 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
1884 (let ((n (mul -1 a)))
1885 (mul (power -1 n)
1886 (power (gm (add 1 n)) 2)
1887 (inv (gm (add 2 (mul 2 n))))
1888 (power 2 (mul 2 n))
1889 (legenpol (add 1 (mul 2 n))
1890 (power var (div 1 2)))
1891 (inv (power var (div 1 2))))))
1893 ((and (zerp (sub b a))
1894 (zerp (sub c (add a b))))
1895 ;; a = b, c = a + b
1897 ;; A&S 22.5.50
1898 ;; P(n,x) = binomial(2*n,n)*((x-1)/2)^n*F(-n,-n;-2*n;2/(1-x))
1900 ;; F(-n,-n;-2*n;x) = P(n,1-2/x)/binomial(2*n,n)(-1/x)^(-n)
1901 (mul (power (gm (add 1 (mul -1 a))) 2)
1902 (inv (gm (add 1 (mul -2 a))))
1903 (power (mul -1 var) (mul -1 a))
1904 (legenpol (mul -1 a)
1905 (add 1 (div -2 var)))))
1906 ((and (alike1 (sub a b) '((rat simp) 1 2))
1907 (alike1 (sub c (mul 2 b)) '((rat simp) 1 2)))
1908 ;; a - b = 1/2, c - 2*b = 1/2
1910 ;; A&S 22.5.51
1911 ;; P(n,x) = binomial(2*n,n)*(x/2)^n*F(-n/2,(1-n)/2;1/2-n;1/x^2)
1913 ;; F(-n/2,(1-n)/2;1/2-n,1/x^2) = P(n,x)/binomial(2*n,n)*(x/2)^(-n)
1914 (mul (power (gm (add 1 (mul -2 b))) 2)
1915 (inv (gm (add 1 (mul -4 b))))
1916 (power (mul 2 (power var (div 1 2))) (mul -2 b))
1917 (legenpol (mul -2 b)
1918 (power var (div -1 2)))))
1919 ((and (alike1 (sub b a) '((rat simp) 1 2))
1920 (alike1 (sub c (mul 2 a)) '((rat simp) 1 2)))
1921 ;; b - a = 1/2, c + 2*a = 1/2
1923 ;; A&S 22.5.51
1924 ;; P(n,x) = binomial(2*n,n)*(x/2)^n*F(-n/2,(1-n)/2;1/2-n;1/x^2)
1926 ;; F(-n/2,(1-n)/2;1/2-n,1/x^2) = P(n,x)/binomial(2*n,n)*(x/2)^(-n)
1927 (mul (power (gm (add 1 (mul -2 a))) 2)
1928 (inv (gm (add 1 (mul -4 a))))
1929 (power (mul 2 (power var (div 1 2))) (mul -2 a))
1930 (legenpol (mul -2 a)
1931 (power var (div -1 2)))))
1933 nil))))
1935 (defun legpol (a b c)
1936 ;; See if F(a,b;c;z) is a Legendre polynomial. If not, try
1937 ;; F(b,a;c;z).
1938 (or (legpol-core a b c)
1939 (legpol-core b a c)))
1941 ;; See A&S 15.3.3:
1943 ;; F(a,b;c;z) = (1-z)^(c-a-b)*F(c-a,c-b;c;z)
1944 (defun gered1 (arg-l1 arg-l2 simpflg)
1945 (destructuring-bind (a b)
1946 arg-l1
1947 (destructuring-bind (c)
1948 arg-l2
1949 (mul (power (sub 1 var)
1950 (add c
1951 (mul -1 a)
1952 (mul -1 b)))
1953 (funcall simpflg
1954 (list (sub c a)
1955 (sub c b))
1956 arg-l2
1957 var)))))
1959 ;; See A&S 15.3.4
1961 ;; F(a,b;c;z) = (1-z)^(-a)*F(a,c-b;c;z/(z-1))
1962 (defun gered2 (a b c)
1963 (mul (power (sub 1 var) (mul -1 a))
1964 (hgfsimp (list a (sub c b))
1965 (list c)
1966 (div var (sub var 1)))))
1968 ;; See A&S 15.3.9:
1970 ;; F(a,b;c;z) = A*z^(-a)*F(a,a-c+1;a+b-c+1;1-1/z)
1971 ;; + B*(1-z)^(c-a-b)*z^(a-c)*F(c-a,1-a;c-a-b+1,1-1/z)
1973 ;; where A = gamma(c)*gamma(c-a-b)/gamma(c-a)/gamma(c-b)
1974 ;; B = gamma(c)*gamma(a+b-c)/gamma(a)/gamma(b)
1976 (defun geredf (a b c)
1977 (let (($gamma_expand t))
1978 (add (div (mul (take '(%gamma) c)
1979 (take '(%gamma) (add c (mul -1 a) (mul -1 b)))
1980 (power var (mul -1 a))
1981 ($hgfred `((mlist) ,a ,(add a 1 (mul -1 c)))
1982 `((mlist) ,(add a b (mul -1 c) 1))
1983 (sub 1 (div 1 var))))
1984 (mul (take '(%gamma) (sub c a))
1985 (take '(%gamma) (sub c b))))
1986 (div (mul (take '(%gamma) c)
1987 (take '(%gamma) (add a b (mul -1 c)))
1988 (power (sub 1 var)
1989 (add c (mul -1 a) (mul -1 b)))
1990 (power var (sub a c))
1991 ($hgfred `((mlist) ,(sub c a) ,(sub 1 a))
1992 `((mlist) ,(add c (mul -1 a) (mul -1 b) 1))
1993 (sub 1 (div 1 var))))
1994 (mul (take '(%gamma) a) (take '(%gamma) b))))))
1996 (defun trig-log (arg-l1 arg-l2)
1997 (cond ((equal (simplifya (car arg-l2) nil) '((rat simp) 3 2))
1998 ;; c = 3/2
1999 (when $trace2f1
2000 (format t " trig-log: Test c=3/2~%"))
2001 (trig-log-3 arg-l1 arg-l2))
2002 ((equal (simplifya (car arg-l2) nil) '((rat simp) 1 2))
2003 ;; c = 1/2
2004 (when $trace2f1
2005 (format t " trig-log: Test c=1/2~%"))
2006 (trig-log-1 arg-l1 arg-l2))
2007 (t nil)))
2009 (defun trig-log-3 (arg-l1 arg-l2)
2010 (cond ((and (or (equal (car arg-l1) 1) (equal (cadr arg-l1) 1))
2011 (or (equal (car arg-l1) (div 1 2))
2012 (equal (cadr arg-l1) (div 1 2))))
2013 ;; (a = 1 or b = 1) and (a = 1/2 or b = 1/2)
2014 (when $trace2f1
2015 (format t " Case a or b is 1 and the other is 1/2~%"))
2016 (trig-log-3-exec arg-l1 arg-l2))
2017 ((and (equal (car arg-l1) (cadr arg-l1))
2018 (or (equal 1 (car arg-l1))
2019 (equal (div 1 2) (car arg-l1))))
2020 ;; a = b and (a = 1 or a = 1/2)
2021 (when $trace2f1
2022 (format t " Case a=b and a is 1 or 1/2~%"))
2023 (trig-log-3a-exec arg-l1 arg-l2))
2024 ((or (equal (add (car arg-l1) (cadr arg-l1)) 1)
2025 (equal (add (car arg-l1) (cadr arg-l1)) 2))
2026 ;; a + b = 1 or a + b = 2
2027 (when $trace2f1
2028 (format t " Case a+b is 1 or 2~%"))
2029 (trig-sin arg-l1 arg-l2))
2030 ((or (equal (sub (car arg-l1) (cadr arg-l1)) (div 1 2))
2031 (equal (sub (cadr arg-l1) (car arg-l1)) (div 1 2)))
2032 ;; a - b = 1/2 or b - a = 1/2
2033 (when $trace2f1
2034 (format t " Case a-b=1/2 or b-a=1/2~%"))
2035 (trig-3 arg-l1 arg-l2))
2036 (t nil)))
2038 (defun trig-3 (arg-l1 arg-l2)
2039 (declare (ignore arg-l2))
2040 ;; A&S 15.1.10
2042 ;; F(a,a+1/2,3/2,z^2) =
2043 ;; ((1+z)^(1-2*a) - (1-z)^(1-2*a))/2/z/(1-2*a)
2044 (let* (($radexpand '$all)
2045 (a (sub 1
2046 (sub (add (car arg-l1)
2047 (cadr arg-l1))
2048 (div 1 2))))
2049 (z (power var (div 1 2))))
2050 (mul (inv z)
2051 (inv 2)
2052 (inv a)
2053 (sub (power (add 1 z) a)
2054 (power (sub 1 z) a)))))
2056 (defun trig-sin (arg-l1 arg-l2)
2057 (declare (ignore arg-l2))
2058 ;; A&S 15.1.15, 15.1.16
2059 (destructuring-bind (a b)
2060 arg-l1
2061 ;; I think it's ok to convert sqrt(z^2) to z here, so $radexpand
2062 ;; is $all.
2063 (let (($radexpand '$all)
2064 a1 z1)
2065 (cond ((equal (add a b) 1)
2066 ;; A&S 15.1.15
2068 ;; F(a,1-a;3/2;sin(z)^2) =
2070 ;; sin((2*a-1)*z)/(2*a-1)/sin(z)
2071 (mul (inv (mul (mul -1 (sub a b))
2072 (msin (masin (msqrt var)))))
2073 (msin (mul (mul -1
2074 (sub a b))
2075 (masin (msqrt var))))))
2076 ((equal (add a b) 2)
2077 ;; A&S 15.1.16
2079 ;; F(a, 2-a; 3/2; sin(z)^2) =
2081 ;; sin((2*a-2)*z)/(a-1)/sin(2*z)
2082 (mul (msin (mul (setq z1
2083 (masin (msqrt
2084 var)))
2085 (setq a1
2086 (mul -1
2087 (sub a
2088 b)))))
2089 (inv (mul a1
2090 (msin z1)
2091 (mcos z1)))))
2093 nil)))))
2096 ;;Generates atan if arg positive else log
2097 (defun trig-log-3-exec (arg-l1 arg-l2)
2098 (declare (ignore arg-l1 arg-l2))
2099 ;; See A&S 15.1.4 and 15.1.5
2101 ;; F(a,b;3/2;z) where a = 1/2 and b = 1 (or vice versa).
2103 ;; I think it's ok to convert sqrt(z^2) to z here, so $radexpand is
2104 ;; $all.
2105 (let (($radexpand '$all))
2106 (cond ((equal (checksigntm var) '$positive)
2107 ;; A&S 15.1.4
2109 ;; F(1/2,1;3/2,z^2) =
2111 ;; log((1+z)/(1-z))/z/2
2113 ;; This is the same as atanh(z)/z. Should we return that
2114 ;; instead? This would make this match what hyp-atanh
2115 ;; returns.
2116 (let ((z (power var (div 1 2))))
2117 (mul (power z -1)
2118 (inv 2)
2119 (mlog (div (add 1 z)
2120 (sub 1 z))))))
2121 ((equal (checksigntm var) '$negative)
2122 ;; A&S 15.1.5
2124 ;; F(1/2,1;3/2,z^2) =
2125 ;; atan(z)/z
2126 (let ((z (power (mul -1 var)
2127 (div 1 2))))
2128 (mul (power z -1)
2129 (matan z)))))))
2131 (defun trig-log-3a-exec (arg-l1 arg-l2)
2132 ;; See A&S 15.1.6 and 15.1.7
2134 ;; F(a,b;3/2,z) where a = b and a = 1/2 or a = 1.
2136 ;; I think it's ok to convert sqrt(z^2) to z here, so $radexpand is
2137 ;; $all.
2138 (let ((a (first arg-l1))
2139 ($radexpand '$all))
2140 (cond ((equal (checksigntm var) '$positive)
2141 ;; A&S 15.1.6
2143 ;; F(1/2,1/2; 3/2; z^2) = sqrt(1-z^2)*F(1,1;3/2;z^2) =
2144 ;; asin(z)/z
2145 (let ((z (power var (div 1 2))))
2146 (if (equal a 1)
2147 (div (trig-log-3a-exec (list (div 1 2) (div 1 2)) arg-l2)
2148 (power (sub 1 (power z 2)) (div 1 2)))
2149 (div (masin z) z))))
2150 ((equal (checksigntm var) '$negative)
2151 ;; A&S 15.1.7
2153 ;; F(1/2,1/2; 3/2; -z^2) = sqrt(1+z^2)*F(1,1,3/2; -z^2) =
2154 ;;log(z + sqrt(1+z^2))/z
2155 (let* ((z (power (mul -1 var)
2156 (div 1 2)))
2157 (1+z^2 (add 1 (power z 2))))
2158 (if (equal a 1)
2159 (div (trig-log-3a-exec (list (div 1 2) (div 1 2))
2160 arg-l2)
2161 (power 1+z^2
2162 (div 1 2)))
2163 (div (mlog (add z (power 1+z^2
2164 (div 1 2))))
2165 z)))))))
2168 (defun trig-log-1 (arg-l1 arg-l2) ;; 2F1's with C = 1/2
2169 (declare (ignore arg-l2))
2170 ;; 15.1.17, 11, 18, 12, 9, and 19
2172 ;; I think it's ok to convert sqrt(z^2) to z here, so $radexpand is
2173 ;; $all.
2174 (let (($radexpand '$all)
2175 x z $exponentialize a b)
2176 (setq a (car arg-l1) b (cadr arg-l1))
2177 (cond ((=0 (m+t a b))
2178 ;; F(-a,a;1/2,z)
2180 (cond ((equal (checksigntm var) '$positive)
2181 ;; A&S 15.1.17:
2182 ;; F(-a,a;1/2;sin(z)^2) = cos(2*a*z)
2183 (trig-log-1-pos a var))
2184 ((equal (checksigntm var) '$negative)
2185 ;; A&X 15.1.11:
2186 ;; F(-a,a;1/2;-z^2) = 1/2*((sqrt(1+z^2)+z)^(2*a)
2187 ;; +(sqrt(1+z^2)-z)^(2*a))
2189 (trig-log-1-neg a b var))
2190 (t ())))
2191 ((equal (m+t a b) 1.)
2192 ;; F(a,1-a;1/2,z)
2193 (cond ((equal (checksigntm var) '$positive)
2194 ;; A&S 15.1.18:
2195 ;; F(a,1-a;1/2;sin(z)^2) = cos((2*a-1)*z)/cos(z)
2196 (m//t (mcos (m*t (m-t a b) (setq z (masin (msqrt var)))))
2197 (mcos z)))
2198 ((equal (checksigntm var) '$negative)
2199 ;; A&S 15.1.12
2200 ;; F(a,1-a;1/2;-z^2) = 1/2*(1+z^2)^(-1/2)*
2201 ;; {[(sqrt(1+z^2)+z]^(2*a-1)
2202 ;; +[sqrt(1+z^2)-z]^(2*a-1)}
2203 (m*t 1//2 (m//t (setq x (msqrt (m-t 1. var))))
2204 (m+t (m^t (m+t x (setq z (msqrt (m-t var))))
2205 (setq b (m-t a b)))
2206 (m^t (m-t x z) b))))
2207 (t ())))
2208 ((=1//2 (hyp-mabs (m-t b a)))
2209 ;; F(a, a+1/2; 1/2; z)
2210 (cond ((equal (checksigntm var) '$positive)
2211 ;; A&S 15.1.9
2212 ;; F(a,1/2+a;1/2;z^2) = ((1+z)^(-2*a)+(1-z)^(-2*a))/2
2213 (m*t 1//2
2214 (m+t (m^t (m1+t (setq z (msqrt var)))
2215 (setq b (m-t 1//2 (m+t a b))))
2216 (m^t (m-t 1. z) b))))
2217 ((equal (checksigntm var) '$negative)
2218 ;; A&S 15.1.19
2219 ;; F(a,1/2+a;1/2;-tan(z)^2) = cos(z)^(2*a)*cos(2*a*z)
2220 (m*t (m^t (mcos (setq z (matan (msqrt (m-t var)))))
2221 (setq b (m+t a b -1//2)))
2222 (mcos (m*t b z))))
2223 (t ())))
2224 (t ()))))
2226 (defun trig-log-1-pos (a z)
2227 ;; I think it's ok to convert sqrt(z^2) to z here, so $radexpand is
2228 ;; $all.
2229 (let (($radexpand '$all))
2230 (mcos (m*t 2. a (masin (msqrt z))))))
2232 (defun trig-log-1-neg (a b v)
2233 ;; Look to see a is of the form m*s+c where m and c
2234 ;; are numbers. If m is positive, swap a and b.
2235 ;; Basically we want F(-a,a;1/2;-z^2) =
2236 ;; F(a,-a;1/2;-z^2), as they should be.
2237 (let* (($radexpand '$all)
2238 (match (m*s+c a))
2239 (m (cdras 'm match))
2240 (s (cdras 's match))
2241 (b (if s
2242 (if (and m (eq (checksigntm m) '$positive))
2245 (if (eq (checksigntm a) '$negative)
2247 a)))
2248 (x (msqrt (m-t 1. v)))
2249 (z (msqrt (m-t v))))
2250 (m*t 1//2
2251 (m+t (m^t (m+t x z)
2252 (setq b (m*t 2. b)))
2253 (m^t (m-t x z) b)))))
2255 ;; Pattern match for m*s+c where a is a number, x is symbolic, and c
2256 ;; is a number.
2257 (defun m*s+c (exp)
2258 (m2 exp
2259 '((mplus) ((coeffpt) (m $numberp) (s nonnump))
2260 ((coeffpp) (c $numberp)))))
2262 ;; List L contains two elements first the numerator parameter that
2263 ;;exceeds the denumerator one and is called "C", second
2264 ;;the difference of the two parameters which is called "M".
2267 (defun diffintprop-gen-exec (l l1 l2)
2268 (prog (c m poly constfact )
2269 (setq c (car l)
2270 m (cadr l)
2271 l1 (delete c l1 :count 1 :test #'equal)
2272 c (sub c m)
2273 l2 (delete c l2 :count 1 :test equal)
2274 poly ($expand (constrpoly c m 'avgoustis))
2275 constfact (createconstfact c m))
2276 (return (yanmult constfact
2277 (diffintprop-exec poly l1 l2)))))
2279 (defun constrpoly (c m k)
2280 (cond ((zerop m) 1.)
2281 (t (mul (add c k (1- m)) (constrpoly c (1- m) k)))))
2283 (defun createconstfact (c m)
2284 (cond ((zerop m) 1.)
2285 (t (mul (inv (add c (1- m)))
2286 (createconstfact c (1- m))))))
2288 (defun diffintprop-exec (poly l1 l2)
2289 (distrdiffintprop (createcoefpowlist-exec poly) l1 l2))
2291 (defun distrdiffintprop (l l1 l2)
2292 (cond ((null l) 0.)
2293 (t (add (yanmult ($factor (cadar l))
2294 (diffintprop (caar l) l1 l2))
2295 (distrdiffintprop (cdr l) l1 l2)))))
2297 (defun diffintprop (pow l1 l2)
2298 (cond ((zerop pow) (hgfsimp l1 l2 var))
2299 ((equal pow 1.)
2300 (yanmult (mul (div (multpl l1) (multpl l2)) var)
2301 (hgfsimp (incr1 l1) (incr1 l2) var)))
2302 (t (searchaddserieslist pow l1 l2))))
2304 (defun searchaddserieslist (pow l1 l2)
2305 (prog (series res)
2306 (cond ((setq series (searchserieslistp serieslist pow))
2307 (return (eval series))))
2308 (setq
2309 serieslist
2310 (append
2311 serieslist
2312 (list
2313 (list
2315 (setq res
2316 '(yanmult (mul (div (multpl l1) (multpl l2))
2317 var)
2318 (diffintproprecurse (1- pow)
2319 (incr1 l1)
2320 (incr1 l2))))))))
2321 (return (eval res))))
2323 (defun diffintproprecurse (pow l1 l2)
2324 (prog (poly)
2325 (setq poly
2326 ($expand (power (add 'avgoustis 1.) pow)))
2327 (return (diffintprop-exec poly l1 l2))))
2329 (defun multpl (l)
2330 (cond ((null l) 1.) (t (mul (car l) (multpl (cdr l))))))
2332 (defun createcoefpowlist-exec (poly)
2333 (prog (hp conster)
2334 (setq conster (consterminit poly 'avgoustis)
2335 hp ($hipow poly 'avgoustis))
2336 (return (append (list (list 0. conster))
2337 (createcoefpowlist poly hp)))))
2339 (defun createcoefpowlist (poly hp)
2340 (cond ((equal hp 1.)
2341 (list (list 1. ($coeff poly 'avgoustis))))
2342 (t (append (createcoefpowlist poly (1- hp))
2343 (list (list hp
2344 ($coeff poly
2345 (power 'avgoustis
2346 hp))))))))
2348 (defun consterminit (fun var)
2349 (cond ((eq (caar fun) 'mplus) (consterm (cdr fun) var))
2350 (t (cond ((freevar fun) fun) (t 0.)))))
2352 (defun searchserieslistp (serieslist pow)
2353 (cond ((null serieslist) nil)
2354 ((equal (caar serieslist) pow) (cadar serieslist))
2355 (t (searchserieslistp (cdr serieslist) pow))))
2357 (defun yanmult (a b)
2358 (cond ((eq (caar b) 'mplus) (yanmul a (cdr b)))
2359 (t (mul a b))))
2361 (defun yanmul (a b)
2362 (cond ((null b) 0.)
2363 (t (add (mul a (car b)) (yanmul a (cdr b))))))
2367 (defun freevarpar (exp)
2368 (cond ((freevar exp) (freepar exp))
2369 (t nil)))
2371 ;; Why is this needed?
2372 (setq *par* '$p)
2374 (defun freepar (exp)
2375 (cond ((atom exp)
2376 (not (eq exp *par*)))
2377 (t (and (freepar (car exp))
2378 (freepar (cdr exp))))))
2380 ;; Confluent hypergeometric function.
2382 ;; F(a;c;z)
2383 (defun confl (arg-l1 arg-l2 var)
2384 (let* ((a (car arg-l1))
2385 (c (car arg-l2))
2386 (a-c (sub a c))
2388 (cond ((zerop1 var)
2389 ;; F(a;c;0) = 1
2390 (add 1 var))
2392 ((and (equal 1 c)
2393 (not (integerp a)) ; Do not handle an integer or
2394 (not (integerp (add a a)))) ; an half integral value
2395 ;; F(a;1;z) = laguerre(-a,z)
2396 (lagpol (neg a) 0 var))
2398 ((and (maxima-integerp a)
2399 (member ($sign a) '($neg nz)))
2400 ;; F(-n; c; z) and n a positive integer
2401 (1f1polys (list c) a))
2403 ((alike1 c (add a a))
2404 ;; F(a;2a;z)
2405 ;; A&S 13.6.6
2407 ;; F(n+1;2*n+1;2*z) =
2408 ;; gamma(3/2+n)*exp(z)*(z/2)^(-n-1/2)*bessel_i(n+1/2,z).
2410 ;; So
2412 ;; F(n,2*n,z) =
2413 ;; gamma(n+1/2)*exp(z/2)*(z/4)^(-n-3/2)*bessel_i(n-1/2,z/2);
2415 ;; If n is a negative integer, we use laguerre polynomial.
2416 (if (and (maxima-integerp a)
2417 (eq (asksign a) '$negative))
2418 ;; We have already checked a negative integer. This algorithm
2419 ;; is now present in 1f1polys and at this place never called.
2420 (let ((n (neg a)))
2421 (mul (power -1 n)
2422 (inv (take '(%binomial) (add n n) n))
2423 (lagpol n (sub c 1) var)))
2424 (let ((z (div var 2)))
2425 (mul (power '$%e z)
2426 (bestrig (add a '((rat simp) 1 2))
2427 (div (mul z z) 4))))))
2429 ((and (integerp (setq n (sub (add a a) c)))
2430 (plusp n)
2431 (not (integerp a))
2432 (not (integerp (add a a))))
2433 ;; F(a,2*a-n,z) and a not an integer or a half integral value
2434 (when *debug-hyp*
2435 (format t "~&Case 1F1(a,2*a-n,x):")
2436 (format t "~& ; a = ~A~%" a)
2437 (format t "~& ; c = ~A~%" c)
2438 (format t "~& : n = ~A~%" n))
2439 (sratsimp
2440 (mul (take '(%gamma) (sub a (add n '((rat simp) 1 2))))
2441 (power (div var 4)
2442 (sub (add '((rat simp) 1 2) n) a))
2443 (power '$%e (div var 2))
2444 (let ((index (gensym)))
2445 (dosum
2446 (mul (power -1 index)
2447 (take '($pochhammer) (- n) index)
2448 (take '($pochhammer) (add a a (* -2 n) -1) index)
2449 (add a index (- n) '((rat simp) -1 2))
2450 (inv (take '($pochhammer) (sub (add a a) n) index))
2451 (inv (take '(mfactorial) index))
2452 (take '(%bessel_i)
2453 (add a index (- n) '((rat simp) -1 2))
2454 (div var 2)))
2455 index 0 n t)))))
2457 ((and (integerp (setq n (sub c (add a a))))
2458 (plusp n)
2459 (not (integerp a))
2460 (not (integerp (add a a))))
2461 ;; F(a,2*a+n,z) and a not an integer or a half integral value
2462 (when *debug-hyp*
2463 (format t "~&Case 1F1(a,2*a+n,x):")
2464 (format t "~& ; a = ~A~%" a)
2465 (format t "~& ; c = ~A~%" c)
2466 (format t "~& : n = ~A~%" n))
2467 (sratsimp
2468 (mul (take '(%gamma) (sub a '((rat simp) 1 2)))
2469 (power (div var 4)
2470 (sub '((rat simp) 1 2) a))
2471 (power '$%e (div var 2))
2472 (let ((index (gensym)))
2473 (dosum
2474 (mul (take '($pochhammer) (- n) index)
2475 (take '($pochhammer) (add a a -1) index)
2476 (add a index '((rat simp) -1 2))
2477 (inv (take '($pochhammer) (add a a n) index))
2478 (inv (take '(mfactorial) index))
2479 (take '(%bessel_i)
2480 (add a index '((rat simp) -1 2))
2481 (div var 2)))
2482 index 0 n t)))))
2484 ((and (integerp (setq n (sub a '((rat simp) 1 2))))
2485 (>= n 0)
2486 (integerp c)
2487 (plusp c))
2488 (let ((m c)
2489 ($simpsum t)
2490 ($bessel_reduce t))
2491 (when *debug-hyp*
2492 (format t "~&Case 1F1(n+1/2,m,x):")
2493 (format t "~& ; a = ~A~%" a)
2494 (format t "~& ; c = ~A~%" c)
2495 (format t "~& : n = ~A~%" n)
2496 (format t "~& : m = ~A~%" m))
2497 (sratsimp
2498 (mul (power 2 (- 1 m))
2499 (power '$%e (div var 2))
2500 (factorial (- m 1))
2501 (factorial n)
2502 (inv (take '($pochhammer) '((rat simp) 1 2) (- m 1)))
2503 (inv (take '($pochhammer) '((rat simp) 1 2) n))
2504 (let ((index1 (gensumindex)))
2505 (dosum
2506 (mul (power 2 (neg index1))
2507 (power (neg var) index1)
2508 (inv (take '(mfactorial) index1))
2509 (mfuncall '$gen_laguerre
2510 (sub n index1)
2511 (sub index1 '((rat simp) 1 2))
2512 (neg var))
2513 (let ((index2 (gensumindex)))
2514 (dosum
2515 (mul (power -1 index2)
2516 (power 2 (neg index2))
2517 (take '(%binomial)
2518 (add index1 m -1)
2519 index2)
2520 (let ((index3 (gensumindex)))
2521 (dosum
2522 (mul (take '(%binomial) index2 index3)
2523 (take '(%bessel_i)
2524 (sub index2 (mul 2 index3))
2525 (div var 2)))
2526 index3 0 index2 t)))
2527 index2 0 (add index1 m -1) t)))
2528 index1 0 n t))))))
2530 ((and (integerp (setq n (sub a '((rat simp) 1 2))))
2531 (< n 0)
2532 (integerp c)
2533 (plusp c))
2534 (let ((n (- n))
2535 (m c)
2536 ($simpsum t)
2537 ($bessel_reduce t))
2538 (when *debug-hyp*
2539 (format t "~&Case 1F1(1/2-n,m,x):")
2540 (format t "~& ; a = ~A~%" a)
2541 (format t "~& ; c = ~A~%" c)
2542 (format t "~& : n = ~A~%" n)
2543 (format t "~& : m = ~A~%" m))
2544 (sratsimp
2545 (mul (power -1 n)
2546 (power 2 (- 1 m))
2547 (power '$%e (div var 2))
2548 (factorial (- m 1))
2549 (inv (take '($pochhammer) '((rat simp) 1 2) (- m 1)))
2550 (inv (take '($pochhammer) (sub m '((rat simp) 1 2)) n))
2551 (let ((index1 (gensumindex)))
2552 (dosum
2553 (mul (power 2 (neg index1))
2554 (power var index1)
2555 (take '(%binomial) n index1)
2556 (take '($pochhammer)
2557 (sub '((rat simp) 3 2) (+ m n))
2558 (sub n index1))
2559 (let ((index2 (gensumindex)))
2560 (dosum
2561 (mul (power '((rat simp) -1 2) index2)
2562 (take '(%binomial)
2563 (add index1 m -1)
2564 index2)
2565 (let ((index3 (gensumindex)))
2566 (dosum
2567 (mul (take '(%binomial) index2 index3)
2568 (take '(%bessel_i)
2569 (sub index2 (mul 2 index3))
2570 (div var 2)))
2571 index3 0 index2 t)))
2572 index2 0 (add index1 m -1) t)))
2573 index1 0 n t))))))
2575 ((not (hyp-integerp a-c))
2576 (cond ((hyp-integerp a)
2577 (kummer arg-l1 arg-l2))
2578 ($prefer_whittaker
2579 ;; A&S 15.1.32:
2581 ;; %m[k,u](z) = exp(-z/2)*z^(u+1/2)*M(1/2+u-k,1+2*u,z)
2583 ;; So
2585 ;; M(a,c,z) = exp(z/2)*z^(-c/2)*%m[c/2-a,c/2-1/2](z)
2587 ;; But if we apply Kummer's transformation, we can also
2588 ;; derive the expression
2590 ;; %m[k,u](z) = exp(z/2)*z^(u+1/2)*M(1/2+u+k,1+2*u,-z)
2592 ;; or
2594 ;; M(a,c,z) = exp(-z/2)*(-z)^(-c/2)*%m[a-c/2,c/2-1/2](-z)
2596 ;; For Laplace transforms it might be more beneficial to
2597 ;; return this second form instead.
2598 (let* ((m (div (sub c 1) 2))
2599 (k (add '((rat simp) 1 2) m (mul -1 a))))
2600 (mul (power var (mul -1 (add '((rat simp) 1 2) m)))
2601 (power '$%e (div var 2))
2602 (whitfun k m var))))
2604 (fpqform arg-l1 arg-l2 var))))
2605 ((minusp a-c)
2606 (sratsimp (erfgammared a c var)))
2608 (kummer arg-l1 arg-l2)))))
2610 ;; A&S 13.6.19:
2611 ;; M(1/2,3/2,-z^2) = sqrt(%pi)*erf(z)/2/sqrt(z)
2613 ;; So M(1/2,3/2,z) = sqrt(%pi)*erf(sqrt(-z))/2/sqrt(-z)
2614 ;; = sqrt(%pi)*erf(%i*sqrt(z))/2/(%i*sqrt(z))
2615 (defun hyprederf (x)
2616 (let ((x (mul '$%i (power x '((rat simp) 1 2 )))))
2617 (mul (power '$%pi '((rat simp) 1 2))
2618 '((rat simp) 1 2)
2619 (inv x)
2620 (take '(%erf) x))))
2622 ;; M(a,c,z), where a-c is a negative integer.
2623 (defun erfgammared (a c z)
2624 (cond ((and (nump a) (nump c))
2625 (erfgamnumred a c z))
2626 (t (gammareds a c z))))
2628 ;; I (rtoy) think this is what the function above is doing, but I'm
2629 ;; not sure. Plus, I think it's wrong.
2631 ;; For hgfred([n],[2+n],-z), the above returns
2633 ;; 2*n*(n+1)*z^(-n-1)*(gamma_incomplete_lower(n,z)*z-gamma_incomplete_lower(n+1,z))
2635 ;; But from A&S 13.4.3
2637 ;; -M(n,2+n,z) - n*M(n+1,n+2,z) + (n+1)*M(n,n+1,z) = 0
2639 ;; so M(n,2+n,z) = (n+1)*M(n,n+1,z)-n*M(n+1,n+2,z)
2641 ;; And M(n,n+1,-z) = n*z^(-n)*gamma_incomplete_lower(n,z)
2643 ;; This gives
2645 ;; 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)
2646 ;; = n*(n+1)*z^(-n-1)*(gamma_incomplete_lower(n,z)*n-gamma_incomplete_lower(n+1,z))
2648 ;; So the version above is off by a factor of 2. But I think it's more than that.
2649 ;; Using A&S 13.4.3 again,
2651 ;; M(n,n+3,-z) = [n*M(n+1,n+3,-z) - (n+2)*M(n,n+2,-z)]/(-2);
2653 ;; The version above doesn't produce anything like this equation would
2654 ;; produce, given the value of M(n,n+2,-z) derived above.
2655 (defun gammareds (a c z)
2656 ;; M(a,c,z) where a-c is a negative integer.
2657 (let ((diff (sub c a)))
2658 (cond ((eql diff 1)
2659 ;; We have M(a,a+1,z).
2660 (hypredincgm a z))
2661 ((eql a 1)
2662 ;; We have M(1,a,z)
2663 ;; Apply Kummer's tranformation to get the form M(a-1,a,z)
2665 ;; (I don't think we ever get here, but just in case, we leave it.)
2666 (let ((var z))
2667 (kummer (list a) (list c))))
2669 ;; We have M(a, a+n, z)
2671 ;; A&S 13.4.3 says
2672 ;; (1+a-b)*M(a,b,z) - a*M(a+1,b,z)+(b-1)*M(a,b-1,z) = 0
2674 ;; So
2676 ;; M(a,b,z) = [a*M(a+1,b,z) - (b-1)*M(a,b-1,z)]/(1+a-b);
2678 ;; Thus, the difference between b and a is reduced, until
2679 ;; b-a=1, which we handle above.
2680 (mul (sub (mul a
2681 (gammareds (add 1 a) c z))
2682 (mul (sub c 1)
2683 (gammareds a (sub c 1) z)))
2684 (inv (sub (add 1 a) c)))))))
2686 ;; A&S 6.5.12:
2687 ;; gamma_incomplete_lower(a,x) = x^a/a*M(a,1+a,-x)
2688 ;; = x^a/a*exp(-x)*M(1,1+a,x)
2690 ;; where gamma_incomplete_lower(a,x) is the lower incomplete gamma function.
2692 ;; M(a,1+a,x) = a*(-x)^(-a)*gamma_incomplete_lower(a,-x)
2693 (defun hypredincgm (a z)
2694 (let ((-z (mul -1 z)))
2695 (if (not $prefer_gamma_incomplete)
2696 (mul a
2697 (power -z (mul -1 a))
2698 (take '(%gamma_incomplete_lower) a -z))
2699 (mul a
2700 (power -z (mul -1 a))
2701 (sub (take '(%gamma) a)
2702 (take '(%gamma_incomplete) a -z))))))
2704 ;; M(a,c,z), when a and c are numbers, and a-c is a negative integer
2705 (defun erfgamnumred (a c z)
2706 (cond ((hyp-integerp (sub c '((rat simp) 1 2)))
2707 (erfred a c z))
2708 (t (gammareds a c z))))
2710 ;; M(a,c,z) when a and c are numbers and c-1/2 is an integer and a-c
2711 ;; is a negative integer. Thus, we have M(p+1/2, q+1/2,z)
2712 (defun erfred (a c z)
2713 (prog (n m)
2714 (setq n (sub a '((rat simp) 1 2))
2715 m (sub c '((rat simp) 3 2)))
2716 ;; a = n + 1/2
2717 ;; c = m + 3/2
2718 ;; a - c < 0 so n - m - 1 < 0
2719 (cond ((not (or (> n m) (minusp n)))
2720 ;; 0 <= n <= m
2721 (return (thno33 n m z)))
2722 ((and (minusp n) (minusp m))
2723 ;; n < 0 and m < 0
2724 (return (thno35 (mul -1 n) (mul -1 m) z)))
2725 ((and (minusp n) (plusp m))
2726 ;; n < 0 and m > 0
2727 (return (thno34 (mul -1 n) m z)))
2729 ;; n = 0 or m = 0
2730 (return (gammareds (add n '((rat simp) 1 2))
2731 (add m '((rat simp) 3 2))
2732 z))))))
2734 ;; Compute M(n+1/2, m+3/2, z) with 0 <= n <= m.
2736 ;; I (rtoy) think this is what this routine is doing. (I'm guessing
2737 ;; that thno33 means theorem number 33 from Yannis Avgoustis' thesis.)
2739 ;; I don't have his thesis, but I see there are similar ways to derive
2740 ;; the result we want.
2742 ;; Method 1:
2743 ;; Use Kummer's transformation (A&S ) to get
2745 ;; M(n+1/2,m+3/2,z) = exp(z)*M(m-n+1,m+3/2,-z)
2747 ;; From A&S, we have
2749 ;; 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)
2751 ;; Apply Kummer's transformation again:
2753 ;; M(1,n+3/2,z) = exp(z)*M(n+1/2,n+3/2,-z)
2755 ;; Apply the differentiation formula again:
2757 ;; 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)
2759 ;; And we know that M(1/2,3/2,z) can be expressed in terms of erf.
2761 ;; Method 2:
2763 ;; Since n <= m, apply the differentiation formula:
2765 ;; 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)
2767 ;; Apply Kummer's transformation:
2769 ;; M(1/2,m-n+3/2,z) = exp(z)*M(m-n+1,m-n+3/2,z)
2771 ;; Apply the differentiation formula again:
2773 ;; 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)
2775 ;; I think this routine uses Method 2.
2776 (defun thno33 (n m x)
2777 ;; 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)
2778 ;; M(1/2,m-n+3/2,z) = exp(z)*M(m-n+1,m-n+3/2,-z)
2779 ;; 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)
2780 ;; diff(M(1,3/2,z),z,m-n) = (-1)^(m-n)*diff(M(1,3/2,-z),z,m-n)
2781 ;; M(1,3/2,-z) = exp(-z)*M(1/2,3/2,z)
2782 (let* ((yannis (gensym))
2783 (m-n (sub m n))
2784 ;; poch(m-n+3/2,n)/poch(1/2,n)
2785 (factor1 (div (take '($pochhammer) (add m-n '((rat simp) 3 2)) n)
2786 (take '($pochhammer) '((rat simp) 1 2) n)))
2787 ;; poch(3/2,m-n)/poch(1,m-n)
2788 (factor2 (div (take '($pochhammer) '((rat simp) 3 2) m-n)
2789 (take '($pochhammer) 1 m-n)))
2790 ;; M(1,3/2,-z) = exp(-z)*M(1/2,3/2,z)
2791 (hgferf (mul (power '$%e (mul -1 yannis))
2792 (hyprederf yannis)))
2793 ;; diff(M(1,3/2,z),z,m-n)
2794 (diff1 (meval (list '($diff) hgferf yannis m-n)))
2795 ;; exp(z)*M(m-n+1,m-n+3/2,-z)
2796 (kummer (mul (power '$%e yannis) diff1))
2797 ;; diff(M(1/2,m-n+3/2,z),z,n)
2798 (diff2 (meval (list '($diff) kummer yannis n))))
2799 ;; Multiply all the terms together.
2800 (sratsimp (mul (power -1 m-n)
2801 factor1
2802 factor2
2803 (maxima-substitute x yannis diff2)))))
2805 ;; M(n+1/2,m+3/2,z), with n < 0 and m > 0
2807 ;; Let's write it more explicitly as M(-n+1/2,m+3/2,z) with n > 0 and
2808 ;; m > 0.
2810 ;; First, use Kummer's transformation to get
2812 ;; M(-n+1/2,m+3/2,z) = exp(z)*M(m+n+1,m+3/2,-z)
2814 ;; We also have
2816 ;; 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)
2818 ;; And finally
2820 ;; diff(M(1,3/2,z),z,m) = poch(1,m)/poch(3/2,m)*M(m+1,m+3/2,z)
2822 ;; Thus, we can compute M(-n+1/2,m+3/2,z) from M(1,3/2,z).
2824 ;; The second formula above can be derived easily by multiplying the
2825 ;; series for M(m+1,m+3/2,z) by z^(n+m) and differentiating n times.
2827 (defun thno34 (n m x)
2828 (let ((yannis (gensym)))
2829 (sratsimp
2830 (maxima-substitute
2832 yannis
2833 (mul (power -1 m)
2834 (div (mul (take '($pochhammer) '((rat simp) 3 2) m)
2835 (power '$%e yannis))
2836 (mul (take '($pochhammer) 1 m)
2837 (take '($pochhammer) (1+ m) n)
2838 (power yannis m)))
2839 (meval (list '($diff)
2840 (mul (power yannis (+ m n))
2841 (meval (list '($diff)
2842 (mul (power '$%e
2843 (mul -1 yannis))
2844 (hyprederf yannis))
2845 yannis
2846 m)))
2847 yannis
2848 n)))))))
2850 ;; M(n+1/2,m+3/2,z), with n < 0 and m < 0
2852 ;; Write it more explicitly as M(-n+1/2,-m+3/2,z) with n > 0 and m >
2853 ;; 0.
2855 ;; We know that
2857 ;; 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).
2859 ;; Apply Kummer's transformation:
2861 ;; M(-n+1/2,3/2,z) = exp(z) * M(n+1,3/2,-z)
2863 ;; Finally
2865 ;; diff(z^n*M(1,3/2,z),z,n) = n!*M(n+1,3/2,z)
2867 ;; So we can express M(-n+1/2,-m+3/2,z) in terms of M(1,3/2,z).
2869 ;; The first formula above follows from the more general formula
2871 ;; diff(z^(b-1)*M(a,b,z),z,n) = poch(b-n,n)*z^(b-n-1)*M(a,b-n,z)
2873 ;; The last formula follows from the general result
2875 ;; diff(z^(a+n-1)*M(a,b,z),z,n) = poch(a,n)*z^(a-1)*M(a+n,b,z)
2877 ;; Both of these are easily derived by using the series for M and
2878 ;; differentiating.
2880 (defun thno35 (n m x)
2881 (let ((yannis (gensym)))
2882 (sratsimp
2883 (maxima-substitute
2885 yannis
2886 (mul (div (power yannis (sub m '((rat simp) 1 2)))
2887 (mul (power -1 (* -1 m))
2888 (take '($pochhammer) 1 n)
2889 (take '($pochhammer) '((rat simp) -1 2) m)))
2890 (meval (list '($diff)
2891 (mul (power yannis '((rat simp) 1 2))
2892 (power '$%e yannis)
2893 (meval (list '($diff)
2894 (mul (power '$%e
2895 (mul -1 yannis))
2896 (power yannis n)
2897 (hyprederf yannis))
2898 yannis
2899 n)))
2900 yannis
2901 m)))))))
2903 ;; Pochhammer symbol. fctrl(a,n) = a*(a+1)*(a+2)*...*(a+n-1).
2905 ;; N must be a positive integer!
2907 ;; FIXME: This appears to be identical to factf below.
2908 (defun fctrl (a n)
2909 (cond ((zerop n)
2911 ((equal n 1)
2914 (mul (add a (1- n))
2915 (fctrl a (1- n))))))
2917 (setq *par* '$p)
2919 (defun vfvp (exp)
2920 (m2 exp '(v freevarpar)))
2923 (defun fpqform (arg-l1 arg-l2 arg)
2924 (list '(mqapply)
2925 (list '($%f simp array) (length arg-l1)(length arg-l2))
2926 (append (list '(mlist simp)) arg-l1)
2927 (append (list '(mlist simp)) arg-l2)
2928 arg))
2930 ;; Consider pFq([a_k]; [c_j]; z). If a_k = c_j + m for some k and j
2931 ;; and m >= 0, we can express pFq in terms of (p-1)F(q-1).
2933 ;; Here is a derivation for F(a,b;c;z), but it generalizes to the
2934 ;; generalized hypergeometric very easily.
2936 ;; From A&s 15.2.3:
2938 ;; diff(z^(a+n-1)*F(a,b;c;z), z, n) = poch(a,n)*z^(a-1)*F(a+n,b;c;z)
2940 ;; F(a+n,b;c;z) = diff(z^(a+n-1)*F(a,b;c;z), z, n)/poch(a,n)/z^(a-1)
2943 ;; So this expresses F(a+n,b;c;z) in terms of F(a,b;c;z). Let a = c +
2944 ;; n. This therefore gives F(c+n,b;c;z) in terms of F(c,b;c;z) =
2945 ;; 1F0(b;;z), which we know.
2947 ;; For simplicity, we will write F(z) for F(a,b;c;z).
2949 ;; Now,
2951 ;; n
2952 ;; diff(z^x*F(z),z,n) = sum binomial(n,k)*diff(z^x,z,n-k)*diff(F(z),z,k)
2953 ;; k=0
2955 ;; But diff(z^x,z,n-k) = x*(x-1)*...*(x-n+k+1)*z^(x-n+k)
2956 ;; = poch(x-n+k+1,n-k)*z^(x-n+k)
2958 ;; so
2960 ;; z^(-a+1)/poch(a,n)*diff(z^(a+n-1),z,n-k)
2961 ;; = poch(a+n-1-n+k+1,n-k)/poch(a,n)*z^(a+n-1-n+k)*z^(-a+1)
2962 ;; = poch(a+k,n-k)/poch(a,n)*z^k
2963 ;; = z^k/poch(a,k)
2965 ;; Combining these we have
2967 ;; n
2968 ;; F(a+n,b;c;z) = sum z^k/poch(a,k)*binomial(n,k)*diff(F(a,b;c;z),z,k)
2969 ;; k=0
2971 ;; Since a = c, we have
2973 ;; n
2974 ;; F(a+n,b;a;z) = sum z^k/poch(a,k)*binomial(n,k)*diff(F(a,b;a;z),z,k)
2975 ;; k=0
2977 ;; But F(a,b;a;z) = F(b;;z) and it's easy to see that A&S 15.2.2 can
2978 ;; be specialized to this case to give
2980 ;; diff(F(b;;z),z,k) = poch(b,k)*F(b+k;;z)
2982 ;; Finally, combining all of these, we have
2984 ;; n
2985 ;; F(a+n,b;c;z) = sum z^k/poch(a,k)*binomial(n,k)*poch(b,k)*F(b+k;;z)
2986 ;; k=0
2988 ;; Thus, F(a+n,b;c;z) is expressed in terms of 1F0(b+k;;z), as desired.
2989 (defun splitpfq (l arg-l1 arg-l2)
2990 (destructuring-bind (a1 k)
2992 (let* ((result 0)
2993 (prodnum 1)
2994 (proden 1)
2995 (b1 (sub a1 k))
2996 (prod-b 1)
2997 (arg-l1 (delete a1 arg-l1 :count 1 :test #'equal))
2998 (arg-l2 (delete b1 arg-l2 :count 1 :test #'equal)))
2999 (loop for count from 0 upto k
3001 (when $trace2f1
3002 (format t "splitpfg term:~%")
3003 (maxima-display (mul (combin k count)
3004 (div prodnum proden)
3005 (inv prod-b)
3006 (power var count)))
3007 (mtell "F(~:M, ~:M)~%"
3008 (cons '(mlist) arg-l1)
3009 (cons '(mlist) arg-l2)))
3010 (setq result (add result
3011 (mul (combin k count)
3012 (div prodnum proden)
3013 (inv prod-b)
3014 (power var count)
3015 (hgfsimp arg-l1 arg-l2 var))))
3016 (setq prod-b (mul prod-b (add b1 count)))
3017 (setq prodnum (mul prodnum (mull arg-l1))
3018 proden (mul proden (mull arg-l2)))
3019 (setq arg-l1 (incr1 arg-l1))
3020 (setq arg-l2 (incr1 arg-l2)))
3021 result)))
3023 ;; binomial(k,count)
3024 (defun combin (k count)
3025 (div (factorial k)
3026 (mul (factorial count)
3027 (factorial (sub k count)))))
3030 ;; We have something like F(s+m,-s+n;c;z)
3031 ;; Rewrite it like F(a'+d,-a';c;z) where a'=s-n=-b and d=m+n.
3033 (defun algii (a b)
3034 (let* ((sym (cdras 'f (s+c a)))
3035 (sign (cdras 'm (m2 sym '((mtimes) ((coefft) (m $numberp)) ((coefft) (s nonnump)))))))
3036 (when (and sign (minusp sign))
3037 (rotatef a b))
3038 (list nil (mul -1 b) (add a b))))
3041 ;;Algor. 2F1-RL from thesis:step 4:dispatch on a+m,-a+n,1/2+l cases
3042 (defun step4 (a b c)
3043 ;; F(a,b;c;z) where a+b is an integer and c+1/2 is an integer. If a
3044 ;; and b are not integers themselves, we can derive the result from
3045 ;; F(a1,-a1;1/2;z). However, if a and b are integers, we can't use
3046 ;; that because F(a1,-a1;1/2;z) is a polynomial. We need to derive
3047 ;; the result from F(1,1;3/2;z).
3048 (if (and (hyp-integerp a)
3049 (hyp-integerp b))
3050 (step4-int a b c)
3051 (step4-a a b c)))
3053 (defun step4-a (a b c)
3054 (let* ((alglist (algii a b))
3055 (aprime (cadr alglist))
3056 (m (caddr alglist))
3057 (n (sub c (inv 2)))
3058 ($ratsimpexpons $true)
3059 ($ratprint $false))
3060 ;; At this point, we have F(a'+m,-a';1/2+n;z) where m and n are
3061 ;; integers.
3062 (cond ((hyp-integerp (add aprime (inv 2)))
3063 ;; Ok. We have a problem if aprime + 1/2 is an integer.
3064 ;; We can't always use the algorithm below because we have
3065 ;; F(1/2,-1/2;1/2;z) which is 1F0(-1/2;;z) so the
3066 ;; derivation isn't quite right. Also, sometimes we'll end
3067 ;; up with a division by zero.
3069 ;; Thus, We need to do something else. So, use A&S 15.3.3
3070 ;; to change the problem:
3072 ;; F(a,b;c;z) = (1-z)^(c-a-b)*F(c-a, c-b; c; z)
3074 ;; which is
3076 ;; 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)
3078 ;; Recall that a' + 1/2 is an integer. Thus we have
3079 ;; F(<int>,<int>,1/2+n;z), which we know how to handle in
3080 ;; step4-int.
3081 (gered1 (list a b) (list c) #'hgfsimp))
3083 (let ((newf
3084 (cond ((equal (checksigntm var) '$positive)
3085 (trig-log-1-pos aprime 'ell))
3086 ((equal (checksigntm var) '$negative)
3087 (trig-log-1-neg (mul -1 aprime) aprime 'ell)))))
3088 ;; Ok, this uses F(a,-a;1/2;z). Since there are 2 possible
3089 ;; representations (A&S 15.1.11 and 15.1.17), we check the sign
3090 ;; of the var (as done in trig-log-1) to select which form we
3091 ;; want to use. The original didn't and seemed to want to use
3092 ;; the negative form.
3094 ;; With this change, F(a,-a;3/2;z) matches what A&S 15.2.6 would
3095 ;; produce starting from F(a,-a;1/2;z), assuming z < 0.
3097 (subst var 'ell
3098 (algiii newf
3099 m n aprime)))))))
3101 ;; F(a,b;c;z), where a and b are (positive) integers and c = 1/2+l.
3102 ;; This can be computed from F(1,1;3/2;z).
3104 ;; Assume a < b, without loss of generality.
3106 ;; F(m,n;3/2+L;z), m < n.
3108 ;; We start from F(1,1;3/2;z). Use A&S 15.2.3, differentiating m
3109 ;; times to get F(m,1;3/2;z). Swap a and b to get F(m,1;3/2;z) =
3110 ;; F(1,m;3/2;z) and use A&S 15.2.3 again to get F(n,m;3/2;z) by
3111 ;; differentiating n times. Finally, if L < 0, use A&S 15.2.4.
3112 ;; Otherwise use A&S 15.2.6.
3114 ;; I (rtoy) can't think of any way to do this with less than 3
3115 ;; differentiations.
3117 ;; Note that if d = (n-m)/2 is not an integer, we can write F(m,n;c;z)
3118 ;; as F(-d+u,d+u;c;z) where u = (n+m)/2. In this case, we could use
3119 ;; step4-a to compute the result.
3122 ;; Transform F(a,b;c;z) to F(a+n,b;c;z), given F(a,b;c;z)
3123 (defun as-15.2.3 (a bb cx n arg fun)
3124 (declare (ignore bb cx))
3125 (assert (>= n 0))
3126 ;; A&S 15.2.3:
3127 ;; F(a+n,b;c;z) = z^(1-a)/poch(a,n)*diff(z^(a+n-1)*fun,z,n)
3128 (mul (inv (factf a n))
3129 (power arg (sub 1 a))
3130 ($diff (mul (power arg (sub (add a n) 1))
3131 fun)
3132 arg n)))
3134 ;; Transform F(a,b;c;z) to F(a,b;c-n;z), given F(a,b;c;z)
3135 (defun as-15.2.4 (axax bb c n arg fun)
3136 (declare (ignore axax bb))
3137 (assert (>= n 0))
3138 ;; A&S 15.2.4
3139 ;; F(a,b;c-n;z) = 1/poch(c-n,n)/z^(c-n-1)*diff(z^(c-1)*fun,z,n)
3140 (mul (inv (factf (sub c n) n))
3141 (inv (power arg (sub (sub c n) 1)))
3142 ($diff (mul (power arg (sub c 1))
3143 fun)
3144 arg n)))
3146 ;; Transform F(a,b;c;z) to F(a-n,b;c;z), given F(a,b;c;z)
3147 (defun as-15.2.5 (a b c n arg fun)
3148 ;; A&S 15.2.5
3149 ;; F(a-n,b;c;z) = 1/poch(c-a,n)*z^(1+a-c)*(1-z)^(c+n-a-b)
3150 ;; *diff(z^(c-a+n-1)*(1-z)^(a+b-c)*F(a,b;c;z),z,n)
3151 (assert (>= n 0))
3152 (mul (inv (factf (sub c a) n))
3153 (power arg (sub (add a 1) c))
3154 (power (sub 1 arg)
3155 (sub (add c n) (add a b)))
3156 ($diff (mul (power arg (sub (add c n)
3157 (add a 1)))
3158 (power (sub 1 arg)
3159 (sub (add a b) c))
3160 fun)
3161 arg n)))
3163 ;; Transform F(a,b;c;z) to F(a,b;c+n;z), given F(a,b;c;z)
3164 (defun as-15.2.6 (a b c n arg fun)
3165 ;; A&S 15.2.6
3166 ;; F(a,b;c+n;z) = poch(c,n)/poch(c-a,n)/poch(c-b,n)*(1-z)^(c+n-a-b)
3167 ;; *diff((1-z)^(a+b-c)*fun,z,n)
3168 (assert (>= n 0))
3169 (mul (factf c n)
3170 (inv (factf (sub c a) n))
3171 (inv (factf (sub c b) n))
3172 (inv (power (sub 1 arg) (sub (add a b)
3173 (add c n))))
3174 ($diff (mul (power (sub 1 arg) (sub (add a b) c))
3175 fun)
3176 arg n)))
3178 ;; Transform F(a,b;c;z) to F(a+n, b; c+n; z)
3179 (defun as-15.2.7 (a b c n arg fun)
3180 ;; A&S 15.2.7
3181 ;; F(a+n,b;c+n;z) = (-1)^n*poch(c,n)/poch(a,n)/poch(c-b,n)*(1-z)^(1-a)
3182 ;; *diff((1-z)^(a+n-1)*fun, z, n)
3183 (assert (>= n 0))
3184 (mul (power -1 n)
3185 (factf c n)
3186 (inv (factf a n))
3187 (inv (factf (sub c b) n))
3188 (power (sub 1 arg) (sub 1 a))
3189 ($diff (mul (power (sub 1 arg) (sub (add a n) 1))
3190 fun)
3191 arg n)))
3193 ;; Transform F(a,b;c;z) to F(a-n, b; c-n; z)
3194 (defun as-15.2.8 (axax b c n arg fun)
3195 ;; A&S 15.2.8
3196 ;; F(a-n,b;c-n;z) = 1/poch(c-n,n)/(z^(c-n-1)*(1-z)^(b-c))
3197 ;; *diff(z^(c-1)*(1-z^(b-c+n)*fun, z, n))
3198 (declare (ignore axax))
3199 (assert (>= n 0))
3200 (mul (inv (factf (sub c n) n))
3201 (inv (mul (power arg (sub (sub c n) 1))
3202 (power (sub 1 arg) (sub b c))))
3203 ($diff (mul (power arg (sub c 1))
3204 (power (sub 1 arg) (add (sub b c) n))
3205 fun)
3206 arg n)))
3208 ;; Transform F(a,b;c;z) to F(a+n,b+n;c+n;z)
3209 (defun as-15.2.2 (a b c n arg fun)
3210 ;; A&S 15.2.2
3211 ;; F(a+n,b+n; c+n;z) = poch(c,n)/poch(a,n)/poch(b,n)
3212 ;; *diff(fun, z, n)
3213 (assert (>= n 0))
3214 (mul (factf c n)
3215 (inv (factf a n))
3216 (inv (factf b n))
3217 ($diff fun arg n)))
3219 ;; Transform F(a,b;c;z) to F(a-n,b-n;c-n;z)
3220 (defun as-15.2.9 (a b c n arg fun)
3221 ;; A&S 15.2.9
3222 ;; F(a-n,b-n; c-n;z) = 1/poch(c-n,n)/(z^(c-n-1)*(1-z)^(a+b-c-n))
3223 ;; *diff(z^(c-1)*(1-z)^(a+b-c)*fun, z, n)
3224 (assert (>= n 0))
3225 (mul (inv (factf (sub c n) n))
3226 (inv (mul (power arg (sub (sub c n) 1))
3227 (power (sub 1 arg) (sub (add a b)
3228 (add c n)))))
3229 ($diff (mul (power arg (sub c 1))
3230 (power (sub 1 arg) (sub (add a b) c))
3231 fun)
3232 arg n)))
3234 (defun step4-int (a b c)
3235 (if (> a b)
3236 (step4-int b a c)
3237 (let* ((s (gensym (symbol-name '#:step4-var-)))
3238 (m (1- a))
3239 (n (1- b))
3240 (ell (sub c 3//2))
3241 (res (cond ((eq (checksigntm var) '$negative)
3242 ;; F(1,1;3/2;z) =
3243 ;; -%i*log(%i*sqrt(zn)+sqrt(1-zn))/(sqrt(1-zn)*sqrt(zn))
3244 ;; for z < 0
3245 (let ((root1-z (power (sub 1 s) (inv 2)))
3246 (rootz (power s (inv 2))))
3247 (mul -1 '$%i
3248 (mlog (add (mul '$%i rootz)
3249 root1-z))
3250 (inv root1-z)
3251 (inv rootz))))
3253 ;; F(1,1;3/2;z) = asin(sqrt(zp))/(sqrt(1-zp)*sqrt(zp))
3254 ;; for z > 0
3255 (let ((root1-z (power (sub 1 s) (inv 2)))
3256 (rootz (power s (inv 2))))
3257 (mul (masin rootz)
3258 (inv root1-z)
3259 (inv rootz)))))))
3260 ;; Start with res = F(1,1;3/2;z). Compute F(m,1;3/2;z)
3261 (setf res (as-15.2.3 1 1 3//2 m s res))
3262 ;; We now have res = C*F(m,1;3/2;z). Compute F(m,n;3/2;z)
3263 (setf res (as-15.2.3 1 a 3//2 n s res))
3264 ;; We now have res = C*F(m,n;3/2;z). Now compute F(m,n;3/2+ell;z):
3265 (subst var s
3266 (cond ((minusp ell)
3267 (as-15.2.4 a b 3//2 (- ell) s res))
3269 (as-15.2.6 a b 3//2 ell s res)))))))
3271 ;;Pattern match for s(ymbolic) + c(onstant) in parameter
3272 (defun s+c (exp)
3273 (m2 exp '((mplus) ((coeffpt)(f nonnump)) ((coeffpp)(c $numberp)))))
3275 (defun nonnump (z)
3276 (cond ((not ($numberp z)) t)
3277 (t nil)))
3279 ;;Algor. III from thesis:determines which Differ. Formula to use
3280 (defun algiii (fun m n aprime)
3281 (let ((mm (abs m))
3282 (nn (abs n)))
3283 (cond ((and (nni m) (nni n))
3284 (cond ((< m n)
3285 (f81 fun m n aprime))
3287 (f85 fun mm nn aprime))))
3288 ((and (hyp-negp n) (hyp-negp m))
3289 (cond ((> (abs m) (abs n))
3290 (f86 fun mm nn aprime))
3292 (f82 fun mm nn aprime))))
3293 ((and (hyp-negp m) (nni n))
3294 (f83 fun mm nn aprime))
3296 (f84 fun mm nn aprime)))))
3298 ;; Factorial function:x*(x+1)*(x+2)...(x+n-1)
3300 ;; FIXME: This appears to be identical to fctrl above
3301 (defun factf (x n)
3302 (cond ((zerop n) 1)
3303 (t (mul x (factf (add x 1) (sub n 1))))))
3305 ;;Formula #85 from Yannis thesis:finds by differentiating F[2,1](a,b,c,z)
3306 ;; given F[2,1](a+m,b,c+n,z) where b=-a and c=1/2, n,m integers
3308 ;; Like F81, except m > n.
3310 ;; F(a+m,-a;c+n;z), m > n, c = 1/2, m and n are non-negative integers
3312 ;; A&S 15.2.3
3313 ;; 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)
3315 ;; A&S 15.2.7
3316 ;; diff((1-z)^(a+m-1)*F(a+m-n,-a;1/2;z),z,n)
3317 ;; = (-1)^n*poch(a+m-n,n)*poch(1/2+a,n)/poch(1/2,n)*(1-z)^(a+m-n-1)
3318 ;; * F(a+m,-a;1/2+n;z)
3320 (defun f85 (fun m n a)
3321 (mul (factf (inv 2) n)
3322 (inv (power -1 n))
3323 (inv (factf (sub (add a m)
3326 (inv (factf (sub (inv 2)
3327 (mul a -1))
3329 (inv (factf a (- m n)))
3330 (power (sub 1 'ell) (sub (sub (add 1 n) m) a))
3331 ($diff (mul (power (sub 1 'ell) (sub (add a m) 1))
3332 (power 'ell (sub 1 a))
3333 ($diff (mul (power 'ell (sub (add a m -1) n))
3334 fun)
3335 'ell (- m n)))
3336 'ell n)))
3338 ;;Used to find negative things that are not integers,eg RAT's
3339 (defun hyp-negp (x)
3340 (cond ((equal (asksign x) '$negative)
3342 (t nil)))
3344 ;; F(a,-a+m; c+n; z) where m,n are non-negative integers, m < n, c = 1/2.
3346 ;; A&S 15.2.6
3347 ;; diff((1-z)^(a+b-c)*F(a,b;c;z),z,n)
3348 ;; = poch(c-a,n)*poch(c-b,n)/poch(c,n)*(1-z)^(a+b-c-n)*F(a,b;c+n;z)
3350 ;; A&S 15.2.7:
3351 ;; diff((1-z)^(a+m-1))*F(a,b;c;z),z,m)
3352 ;; = (-1)^m*poch(a,m)*poch(c-b,m)/poch(c,m)*(1-z)^(a-1)*F(a+m,b;c+m;z)
3354 ;; Rewrite F(a,-a+m; c+n;z) as F(-a+m, a; c+n; z). Then apply 15.2.6
3355 ;; to F(-a,a;1/2;z), differentiating n-m times:
3357 ;; diff((1-z)^(-1/2)*F(-a,a;1/2;z),z,n-m)
3358 ;; = 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)
3360 ;; Multiply this result by (1-z)^(n-a-1/2) and apply 15.2.7, differentiating m times:
3362 ;; diff((1-z)^(m-a-1)*F(-a,a;1/2+n-m;z),z,m)
3363 ;; = (-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)
3365 ;; Which gives F(-a+m,a;1/2+n;z), which is what we wanted.
3366 (defun f81 (fun m n a)
3367 (mul (factf (add (inv 2) (- n m)) m)
3368 (factf (inv 2) (- n m))
3369 (inv (power -1 m))
3370 (inv (factf a m))
3371 (inv (factf (add (inv 2) n (sub a m)) m))
3372 (inv (factf (sub (inv 2) a) (- n m)))
3373 (inv (factf (add (inv 2) a) (- n m)))
3374 (power (sub 1 'ell) (sub 1 a))
3375 ($diff (mul (power (sub 1 'ell) (add a n (inv -2)))
3376 ($diff (mul (power (sub 1 'ell) (inv -2))
3377 fun)
3378 'ell (- n m)))
3379 'ell m)))
3381 ;; Like f86, but |n|>=|m|
3383 ;; F(a-m,-a;1/2-n;z) where n >= m >0
3385 ;; A&S 15.2.4
3386 ;; diff(z^(c-1)*F(a,b;c;z),z,n)
3387 ;; = poch(c-n,n)*z^(c-n-1)*F(a;b;c-n;z)
3389 ;; A&S 15.2.8:
3390 ;; diff(z^(c-1)*(1-z)^(b-c+n)*F(a,b;c;z),z,n)
3391 ;; = poch(c-n,n)*z^(c-n-1)*(1-z)^(b-c)*F(a-n,b;c-n;z)
3393 ;; For our problem:
3395 ;; diff(z^(-1/2)*F(a,-a;1/2;z),z,n-m)
3396 ;; = poch(1/2-n+m,n-m)*z^(m-n-1/2)*F(a,-a;1/2-n+m;z)
3398 ;; diff(z^(m-n-1/2)*(1-z)^(n-a-1/2)*F(a,-a;1/2-n+m;z),z,m)
3399 ;; = poch(1/2-n,m)*z^(-1/2-n)*(1-z)^(n-m-a-1/2)*F(a-m,-a;1/2-n;z)
3401 ;; So
3403 ;; G(z) = z^(m-n-1/2)*F(a,-a;1/2-n+m;z)
3404 ;; = 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)
3406 ;; F(a-m,-a;1/2-n;z)
3407 ;; = 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)
3408 (defun f82 (fun m n a)
3409 (mul (inv (factf (sub (inv 2) n) m))
3410 (inv (factf (sub (add (inv 2) m) n) (- n m)))
3411 (power 'ell (add n (inv 2)))
3412 (power (sub 1 'ell) (sub (add m (inv 2) a) n))
3413 ($diff (mul (power (sub 1 'ell)
3414 (sub (sub n a) (inv 2)))
3415 ($diff (mul (power 'ell (inv -2)) fun)
3416 'ell
3417 (- n m)))
3418 'ell
3419 m)))
3421 ;; F(a+m,-a;1/2+n;z) with m,n integers and m < 0, n >= 0
3423 ;; Write this more clearly as F(a-m,-a;1/2+n;z), m > 0, n >= 0
3424 ;; or equivalently F(a-m,-a;c+n;z)
3426 ;; A&S 15.2.6
3427 ;; diff((1-z)^(-1/2)*F(a,-a;1/2;z),z,n)
3428 ;; = poch((1/2+a,n)*poch(1/2-a,n)/poch(1/2,n)*(1-z)^(-1/2-n)
3429 ;; * F(a,-a;1/2+n;z)
3431 ;; A&S 15.2.5
3432 ;; diff(z^(n+m-a-1/2)*(1-z)^(-1/2-n)*F(a,-a;1/2+n;z),z,m)
3433 ;; = 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)
3434 ;; = 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)
3436 ;; (1-z)^(-1/2-n)*F(a,-a;1/2+n;z)
3437 ;; = 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)
3439 ;; F(a-m,-a;1/2+n;z)
3440 ;; = (1-z)^(n+m+1/2)*z^(a-n+1/2)/poch(1/2+n-a,m)
3441 ;; *diff(z^(n+m-a-1/2)*(1-z)^(-1/2-n)*F(a,-a;1/2+n;z),z,m)
3442 (defun f83 (fun m n a)
3443 (mul (factf (inv 2) n)
3444 (inv (factf (sub (inv 2) a) n))
3445 (inv (factf (sub (add n (inv 2)) a) m))
3446 (inv (factf (add (inv 2) a) n))
3447 (power (sub 1 'ell) (add m n (inv 2)))
3448 (power 'ell (add (sub a n) (inv 2)))
3449 ($diff (mul (power 'ell (sub (sub (+ m n) a) (inv 2)))
3450 ($diff (mul (power (sub 1 'ell)
3451 (inv -2))
3452 fun)
3453 'ell
3455 'ell
3456 m)))
3458 ;; The last case F(a+m,-a;c+n;z), m,n integers, m >= 0, n < 0
3460 ;; F(a+m,-a;1/2-n;z)
3462 ;; A&S 15.2.4:
3463 ;; 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)
3465 ;; A&S 15.2.3:
3466 ;; diff(z^(a+m-1)*F(a,b;c;z),z,m) = poch(a,n)*z^(a-1)*F(a+n,b;c;z)
3468 ;; For our problem:
3470 ;; 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)
3472 ;; 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)
3473 (defun f84 (fun m n a)
3474 (mul (inv (mul (factf a m)
3475 (factf (sub (inv 2) n) n)))
3476 (power 'ell (sub 1 a))
3477 ($diff (mul (power 'ell (sub (add a m n) (inv 2)))
3478 ($diff (mul (power 'ell (inv -2)) fun)
3479 'ell
3481 'ell
3482 m)))
3484 ;; Like f82, but |n|<|m|
3486 ;; F(a-m,-a;1/2-n;z), 0 < n < m
3488 ;; A&S 15.2.5
3489 ;; diff(z^(c-a+n-1)*(1-z)^(a+b-c)*F(a,b;c;z),z,n)
3490 ;; = poch(c-a,n)*z^(c-a-1)*(1-z)^(a+b-c-n)*F(a-n,b;c;z)
3492 ;; A&S 15.2.8:
3493 ;; diff(z^(c-1)*(1-z)^(b-c+n)*F(a,b;c;z),z,n)
3494 ;; = poch(c-n,n)*z^(c-n-1)*(1-z)^(b-c)*F(a-n,b;c-n;z)
3496 ;; For our problem:
3498 ;; diff(z^(-a+m-n-1/2)*(1-z)^(-1/2)*F(a,-a;1/2;z),z,m-n)
3499 ;; = poch(1/2-a,m-n)*z^(-a-1/2)*(1-z)^(-1/2-m+n)*F(a-m+n,-a;1/2;z)
3501 ;; diff(z^(-1/2)*(1-z)^(-a-1/2+n)*F(a-m+n,-a;1/2;z),z,n)
3502 ;; = poch(1/2-n,n)*z^(-n-1/2)*(1-z)^(-a-1/2)*F(a-m,-a;1/2-n;z)
3504 ;; G(z) = z^(-a-1/2)*(1-z)^(-1/2-m+n)*F(a-m+n,-a;1/2;z)
3505 ;; = 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)
3507 ;; F(a-m,-a;1/2-n;z)
3508 ;; = z^(n+1/2)*(1-z)^(a+1/2)/poch(1/2-n,n)
3509 ;; *diff(z^(-1/2)*(1-z)^(-a-1/2+n)*F(a-m+n,-a;1/2;z),z,n)
3510 ;; = z^(n+1/2)*(1-z)^(a+1/2)/poch(1/2-n,n)
3511 ;; *diff(z^a*(1-z)^(m-a)*G(z),z,n)
3513 (defun f86 (fun m n a)
3514 (mul (inv (mul (factf (sub (inv 2) n) n)
3515 (factf (sub (inv 2) a) (- m n))))
3516 (power 'ell (add n (inv 2)))
3517 (power (sub 1 'ell) (add (inv 2) a))
3518 ($diff (mul (power 'ell a)
3519 (power (sub 1 'ell) (sub m a))
3520 ($diff (mul (power 'ell
3521 (sub (sub (sub m n) (inv 2)) a))
3522 (power (sub 1 'ell)
3523 (inv -2))
3524 fun)
3525 'ell (- m n)))
3526 'ell n)))
3528 ;; F(-1/2+n, 1+m; 1/2+l; z)
3529 (defun hyp-atanh (a b c)
3530 ;; We start with F(-1/2,1;1/2;z) = 1-sqrt(z)*atanh(sqrt(z)). We can
3531 ;; derive the remaining forms by differentiating this enough times.
3533 ;; FIXME: Do we need to assume z > 0? We do that anyway, here.
3534 (let* ((s (gensym (symbol-name '#:hyp-atanh-)))
3535 (n (add a 1//2))
3536 (m (sub b 1))
3537 (ell (sub c 1//2))
3538 (res (sub 1 (mul (power s '((rat simp) 1 2))
3539 (take '(%atanh) (power s '((rat simp) 1 2))))))
3540 (new-a -1//2)
3541 (new-b 1)
3542 (new-c 1//2))
3543 ;; The total number of derivates we compute is n + m + ell. We
3544 ;; should do something to reduce the number of derivatives.
3545 #+nil
3546 (progn
3547 (format t "a ,b ,c = ~a ~a ~a~%" a b c)
3548 (format t "n, m, ell = ~a ~a ~a~%" n m ell)
3549 (format t "init a b c = ~a ~a ~a~%" new-a new-b new-c))
3550 (cond ((alike1 (sub n ell) 0)
3551 ;; n = ell so we can use A&S 15.2.7 or A&S 15.2.8
3552 (cond ((plusp n)
3553 (setf res (as-15.2.7 new-a new-b new-c n s res)))
3555 (setf res (as-15.2.8 new-a new-b new-c (- n) s res))))
3556 (setf new-a (add new-a n))
3557 (setf new-c (add new-c n)))
3559 ;; Adjust ell and then n. (Does order matter?)
3560 (cond ((plusp ell)
3561 (setf res (as-15.2.6 new-a new-b new-c ell s res))
3562 (setf new-c (add new-c ell)))
3564 (setf res (as-15.2.4 new-a new-b new-c (- ell) s res))
3565 (setf new-c (add new-c ell))))
3566 #+nil
3567 (progn
3568 (maxima-display res)
3569 (format t "new a b c = ~a ~a ~a~%" new-a new-b new-c))
3570 (cond ((plusp n)
3571 ;; A&S 15.2.3
3572 (setf res (as-15.2.3 new-a new-b new-c n s res))
3573 (setf new-a (add new-a n)))
3575 ;; A&S 15.2.5
3576 (setf res (as-15.2.5 new-a new-b new-c (- n) s res))
3577 (setf new-a (add new-a n))))))
3578 #+nil
3579 (progn
3580 (format t "new a b c = ~a ~a ~a~%" new-a new-b new-c)
3581 (maxima-display res))
3582 ;; Finally adjust m by swapping the a and b parameters, since the
3583 ;; hypergeometric function is symmetric in a and b.
3584 (cond ((plusp m)
3585 (setf res (as-15.2.3 new-b new-a new-c m s res))
3586 (setf new-b (add new-b m)))
3588 (setf res (as-15.2.5 new-b new-a new-c (- m) s res))
3589 (setf new-b (add new-b m))))
3590 #+nil
3591 (progn
3592 (format t "new a b c = ~a ~a ~a~%" new-a new-b new-c)
3593 (maxima-display res))
3594 ;; Substitute the argument into the expression and simplify the result.
3595 (sratsimp (maxima-substitute var s res))))
3597 (eval-when
3598 (:compile-toplevel)
3599 (declare-top (unspecial var))