;;; -*-  Mode: Lisp; Package: Maxima; Syntax: Common-Lisp; Base: 10 -*- ;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;     The data in this file contains enhancments.                    ;;;;;
;;;                                                                    ;;;;;
;;;  Copyright (c) 1984,1987 by William Schelter,University of Texas   ;;;;;
;;;     All rights reserved                                            ;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;     (c) Copyright 1980 Massachusetts Institute of Technology         ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(in-package "MAXIMA")
(macsyma-module specfn)

   ;*********************************************************************
   ;****************                                   ******************
   ;**************** Macsyma Special Function Routines ******************
   ;****************                                   ******************
   ;*********************************************************************

(load-macsyma-macros rzmac)
(load-macsyma-macros mhayat)

(defmacro mnumericalp (arg)
	  `(or (floatp ,arg) (and (or $numer $float) (integerp ,arg))))

		(comment Subtitle Polylogarithm Routines)

(declare-top(splitfile plylog))

(declare-top(special $zerobern ivars key-vars tlist)
	 (notype (li2numer flonum) (li3numer flonum)))

(defun lisimp (exp vestigial z)
       vestigial ;; Ignored
       (let ((s (simpcheck (car (subfunsubs exp)) z))
	     ($zerobern t)
	     (a))
	    (subargcheck exp 1 1 '$li)
	    (setq a (simpcheck (car (subfunargs exp)) z))
	    (or (cond ((zerop1 a) 0)
		      ((not (integerp s)) ())
		      ((= s 1) (m- `((%log) ,(m- 1 a))))
		      ((and (integerp a) (> s 1)
			    (cond ((= a 1) ($zeta s))
				  ((= a -1)
				   (m*t (m1- `((rat) 1 ,(expt 2 (f- s 1))))
					($zeta s))))))
		      ((= s 2) (li2simp a))
		      ((= s 3) (li3simp a)))
		(eqtest (subfunmakes '$li (ncons s) (ncons a))
			exp))))

(defun li2simp (arg)
       (cond ((mnumericalp arg) (li2numer (float arg)))
	     ((alike1 arg '((rat) 1 2))
	      (m+t (m// ($zeta 2) 2)
		   (m*t '((rat simp) -1 2)
			(m^ '((%log) 2) 2))))))

(defun li3simp (arg)
       (cond ((mnumericalp arg) (li3numer (float arg)))
	     ((alike1 arg '((rat) 1 2))
	      (m+t (m* '((rat simp) 7 8) '(($zeta) 3))
		   (m*t (m// ($zeta 2) -2) (simplify '((%log) 2)))
		   (m*t '((rat simp) 1 6) (m^ '((%log) 2) 3))))))


(declare-top (flonum x))

;; Numerical evaluation for Chebyschev expansions of the first kind

(defun cheby (x chebarr)
       (declare (flonum x))
       (let ((Bn+2 0.0) (Bn+1 0.0))
	    (declare (flonum Bn+1 Bn+2))
	    (do ((i (fix (arraycall flonum chebarr 0)) (f1- i)))
		((< i 1) (-$ Bn+1 (*$ Bn+2 x)))
		(setq Bn+2
		      (prog1 Bn+1 (setq Bn+1
					(+$ (arraycall flonum chebarr i)
					    (-$ (*$ 2.0 x Bn+1)
						Bn+2))))))))

(defun cheby-prime (x chebarr)
       (declare (flonum x))
       (-$ (cheby x chebarr)
	   (*$ (arraycall flonum chebarr 1) .5)))

;; These should really be calculated with minimax rational approximations.
;; Someone has done LI[2] already, and this should be updated; I haven't
;; seen any results for LI[3] yet.

(defun li2numer (x)
       (cond ((= x 0.0) 0.0)
	     ((= x 1.0) 1.64493407)
	     ((= x -1.0) -.822467033)
	     ((< x -1.0) 
	      (-$ (+$ (chebyli2 (//$ x)) 1.64493407
		      (//$ (expt (log (-$ x)) 2) 2.0))))
	     ((not (> x .5)) (chebyli2 x))
	     ((< x 1.0)
	      (-$ 1.64493407 (*$ (log x) (log (-$ 1.0 x)))
		  (chebyli2 (-$ 1.0 x))))
	     (t (m+t (-$ 3.28986813 (//$ (expt (log x) 2) 2.0)
			 (li2numer (//$ x)))
		     (m*t (-$ (*$ 3.14159265 (log x))) '$%i)))))

(defun li3numer (x)
       (cond ((= x 0.0) 0.0)
	     ((= x 1.0) 1.20205690)
	     ((< x -1.0)
	      (-$ (chebyli3 (//$ x)) (*$ 1.64493407 (log (-$ x)))
		  (//$ (expt (log (-$ x)) 3) 6.0)))
	     ((not (> x .5)) (chebyli3 x))
	     ((not (> x 2.0))
	      (let ((fac (*$ (expt (log x) 2) .5))) 
		   (m+t (+$ 1.20205690 
			    (-$ (*$ (log x)
				    (-$ 1.64493407 (chebyli2 (-$ 1.0 x))))
				(chebyS12 (-$ 1.0 x))
				(*$ fac
				    (log (cond ((< x 1.0) (-$ 1.0 x))
					       ((1-$ x)))))))
			(cond ((< x 1.0) 0)
			      ((m*t (*$ fac -3.14159265) '$%i))))))
	     (t (m+t (+$ (chebyli3 (//$ x)) (*$ 3.28986813 (log x))
			 (//$ (expt (log x) 3) -6.0))
		     (m*t (*$ -1.57079633 (expt (log x) 2)) '$%i)))))

;(array *li2* flonum 15.)
;(array *li3* flonum 15.)
;(array *S12* flonum 18.)

(defvar *li2* (*array nil 'flonum 15.))
(eval-when (load eval)
(fillarray *li2* 
	   '(14.0       1.93506430 .166073033 2.48793229e-2 4.68636196e-3
	     1.0016275e-3 2.32002196e-4 5.68178227e-5 1.44963006e-5
	     3.81632946e-6 1.02990426e-6 2.83575385e-7 7.9387055e-8
	     2.2536705e-8 6.474338e-9))
)

(defvar *li3* (*array nil 'flonum 15.))
(eval-when (load eval)
(fillarray *li3*
	   '(14.0       1.95841721 8.51881315e-2 8.55985222e-3 1.21177214e-3
	     2.07227685e-4 3.99695869e-5 8.38064066e-6 1.86848945e-6
	     4.36660867e-7 1.05917334e-7 2.6478920e-8 6.787e-9 
	     1.776536e-9 4.73417e-10))
)
(defvar *s12* (*array nil  'flonum 18.))
(eval-when (load eval)
(fillarray *S12*
	   '(17.0      1.90361778 .431311318 .100022507 2.44241560e-2
	     6.22512464e-3 1.64078831e-3 4.44079203e-4 1.22774942e-4
	     3.45398128e-5 9.85869565e-6 2.84856995e-6 8.31708473e-7
	     2.45039499e-7 7.2764962e-8 2.1758023e-8 6.546158e-9
	     1.980328e-9))
)
(defun chebyli2 (x)
       (*$ x (cheby-prime (//$ (1+$ (*$ x 4.0)) 3.0) *li2*)))

;		      #+Maclisp '#,(get '*li2* 'array)
;		      #+Franz (getd '*li2*)
;		      #-(Or Maclisp Franz) (get-array-pointer '*li2*))))

(defun chebyli3 (x)
       (*$ x (cheby-prime (//$ (1+$ (*$ 4.0 x)) 3.0) *li3*)))
;		      #+Maclisp '#,(get '*li3* 'array)
;		      #+Franz (getd '*li3*)
;		      #-(or Maclisp Franz) (get-array-pointer '*li3*))))

(defun chebyS12 (x)
  (*$ (//$ (expt x 2) 4.0)
      (cheby-prime (//$ (1+$ (*$ 4.0 x)) 3.0) *s12*)))
;	       #+Maclisp '#,(get '*S12* 'array)
;	       #+Franz (getd '*S12*)
;	       #-(or Maclisp Franz) (get-array-pointer '*S12*))))

(declare-top(notype x))

		(comment Subtitle Polygamma Routines)

(declare-top(splitfile plygam))

;; gross efficiency hack, exp is a function of *k*, *k* should be mbind'ed

(defun msum (exp lo hi)
  (if (< hi lo)
      0
      (let ((sum 0))
	(do ((*k* lo (f1+ *k*)))
	    ((> *k* hi) sum)
       (declare (special *k*))
	  (setq sum (add2 sum (meval exp)))))))


(defun pole-err (exp)
       (declare (special errorsw))
       (cond (errorsw (throw 'errorsw t))
	     (t (merror "Pole encountered in: ~M" exp)
		)))


(declare-top (special
	  $maxpsiposint $maxpsinegint $maxpsifracnum $maxpsifracdenom)
	 (fixnum 
	  $maxpsiposint $maxpsinegint $maxpsifracnum $maxpsifracdenom)
	 (*lexpr $diff))

(defprop $psi psisimp specsimp)

(mapcar (function (lambda (var val)
			  (and (not (boundp var)) (set var val))))
	'($maxpsiposint $maxpsinegint $maxpsifracnum $maxpsifracdenom)
	'(20. -10. 4 4))

(defun psisimp (exp a z)
       (let ((s (simpcheck (car (subfunsubs exp)) z)))
	    (subargcheck exp 1 1 '$psi)
	    (setq a (simpcheck (car (subfunargs exp)) z))
	    (and (integerp a) (< a 1) (pole-err exp))
	    (eqtest (psisimp1 s a) exp)))

;; This gets pretty hairy now.

(defun psisimp1 (s a)
 (let ((*k*))
      (declare (special *k*))
      (or
       (and (not $numer) (not $float) (integerp s) (> s -1)
	(cond
	 ((integerp a)
	  (and (not (> a $maxpsiposint))    ; integer values
	       (m*t (expt -1 s) (factorial s)
		    (m- (msum (inv (m^t '*k* (f1+ s))) 1 (f1- a))
			(cond ((zerop s) '$%gamma)
			      (($zeta (f1+ s))))))))
	 ((or (not (ratnump a)) (ratgreaterp a $maxpsiposint)) ())
	 ((ratgreaterp a 0)
	  (cond
	   ((ratgreaterp a 1)
	    (let* ((int ($entier a))    ; reduction to fractional values
		   (frac (m-t a int)))
		  (m+t
		   (psisimp1 s frac)
		   (if (> int $maxpsiposint)
		       (subfunmakes '$psi (ncons s) (ncons int))
		       (m*t (expt -1 s) (factorial s)
			    (msum (m^t (m+t (m-t a int) '*k*)
				       (f1- (f- s)))
				  0 (f1- int)))))))
	   ((= s 0)
	    (let ((p (cadr a)) (q (caddr a)))
	     (cond
	      ((or (greaterp p $maxpsifracnum)
		   (greaterp q $maxpsifracdenom) (bigp p) (bigp q)) ())
	      ((and (= p 1)
		(cond ((= q 2)
		       (m+ (m* -2 '((%log) 2)) (m- '$%gamma)))
		      ((= q 3)                            
		       (m+ (m* '((rat simp) -1 2)
			       (m^t 3 '((rat simp) -1 2)) '$%pi)
			   (m* '((rat simp) -3 2) '((%log) 3))
			   (m- '$%gamma)))
		      ((= q 4)
		       (m+ (m* '((rat simp) -1 2) '$%pi)
			   (m* -3 '((%log) 2)) (m- '$%gamma))))))
	      ((and (= p 2) (= q 3))
	       (m+ (m* '((rat simp) 1 2)
		       (m^t 3 '((rat simp) -1 2)) '$%pi)
		   (m* '((rat simp) -3 2) '((%log) 3))
		   (m- '$%gamma)))
	      ((and (= p 3) (= q 4))
	       (m+ (m* '((rat simp) 1 2) '$%pi)
		   (m* -3 '((%log) 2)) (m- '$%gamma)))
	      ;; Gauss's Formula
	      ((let ((f (m* `((%cos) ,(m* 2 a '$%pi '*k*))
			    `((%log) ,(m-t 2 (m* 2 `((%cos) 
						     ,(m//t (m* 2 '$%pi '*k*)
							    q))))))))
		    (m+t (msum f 1 (f1- (// q 2)))
			 (let ((*k* (// q 2)))
			   (declare (special *k*))
			      (m*t (meval f)
				   (cond ((oddp q) 1)
					 ('((rat simp) 1 2)))))
			 (m-t (m+ (m* '$%pi '((rat simp) 1 2)
				      `((%cot) ((mtimes simp) ,a $%pi)))
				  `((%log) ,q)
				  '$%gamma))))))))
	   ((alike1 a '((rat) 1 2))
	    (m*t (expt -1 (f1+ s)) (factorial s)
		 (f1- (expt 2 (f1+ s))) (simplify ($zeta (f1+ s)))))))
	 ((ratgreaterp a $maxpsinegint)  ;;; Reflection Formula
	  (m*t
	   (^ -1 s)
	   (m+t (m+t (psisimp1 s (m- a))
		     (let ((dif ($diff `((%cot) ,(m* '$%pi '$z)) '$z s))
			   ($z (m-t a)))
			 (declare (special $z))
			 (meval dif)))
		(m*t (factorial s) (m^t (m-t a) (f1- (f- s)))))))))
       (subfunmakes '$psi (ncons s) (ncons a)))))


		(comment Subtitle Polygamma Tayloring Routines)

;; These routines are specially coded to be as fast as possible given the
;; current $TAYLOR; too bad they have to be so ugly.

(declare-top(special var subl last sign last-exp))

(defun expgam-fun (pw temp)
       (setq temp (get-datum (get-key-var (car var))))
       (let-pw temp pw
	       (pstimes
		(let-pw temp (e1+ pw)
			(psexpt-fn (getexp-fun '(($PSI) -1) var (e1+ pw))))
		(make-ps var (ncons pw) '(((-1 . 1) 1 . 1))))))

(defun expplygam-funs (pw subl l) ; l is a irrelevant here
       (setq subl (car subl))
       (if (or (not (integerp subl)) (lessp subl -1))
	   (tay-err "Unable to expand at a subscript in")
	   (prog ((e 0) (sign 0) npw)
	    (declare (flonum npw) (fixnum e) #-Multics (fixnum sign))
	    (setq npw (//$ (float (car pw)) (float (cdr pw))))
	    (setq
	     l (cond ((= subl -1)
		      `(((1 . 1) . ,(prep1 '((mtimes) -1 $%gamma)))))
		     ((= subl 0)
		      (cons '((-1 . 1) -1 . 1)
			    (if (> 0.0 npw) ()
				`(((0 . 1)
				   . ,(prep1 '((mtimes) -1 $%gamma)))))))
		     (T (setq last (factorial subl))
			`(((,(f- (f1+ subl)) . 1)
			   ,(times (^ -1 (f1+ subl))
				   (factorial subl)) . 1))))
	     e (if (< subl 1) (f- subl) -1)
	     sign (if (< subl 1) -1 (^ -1 subl)))
	  a (setq e (f1+ e) sign (f- sign))
	    (if (greaterp e npw) (return l)
		(rplacd (last l)
			`(((,e . 1)
			   . ,(rctimes (rcplygam e)
				       (prep1 ($zeta (f+ (f1+ subl) e))))))))
	    (go a))))

(defun rcplygam (k)
       (declare (fixnum k) )
       (cond ((= subl -1) (cons sign k))
	     ((= subl 0) (cons sign 1))
	     (t (prog1 (cons (times sign last) 1)
		  
		       (setq last
			     (*quo (times last (plus subl (add1 k)))
				   (add1 k)))))))

(defun plygam-ord (subl)
       (if (equal (car subl) -1) (ncons (rcone))
	   `((,(f- (f1+ (car subl))) . 1))))

(defun plygam-pole (a c func)
       (if (rcmintegerp c)
	   (let ((ps (get-lexp (m- a (rcdisrep c)) () t)))
		(rplacd (cddr ps) (cons `((0 . 1) . ,c) (cdddr ps)))
		(if (atom func) (gam-const a ps func)
		    (plygam-const a ps func)))
	   (prep1 (simplifya
		   (if (atom func) `((%GAMMA) ,(rcdisrep c))
		       `((MQAPPLY) ,func ,(rcdisrep c)))
		   () ))))

(defun gam-const (a arg func)
   (let ((const (ps-lc* arg)) (arg-c))
	(ifn (rcintegerp const)
	     (taylor2 (diff-expand `((%GAMMA) ,a) tlist))
	     (setq const (car const))
	     ;; Try to get the datum
	     (if (pscoefp arg)
		 (setq arg-c (get-lexp (m+t a (minus const)) (rcone)
				       (signp le const))))
	     (if (and arg-c (not (psp arg-c)))	; must be zero
		 (taylor2 (simplify `((%GAMMA) ,const)))
		 (let ((datum (get-datum (get-key-var
					  (gvar (or arg-c arg)))))
		       (ord (if arg-c (le (terms arg-c))
				(le (n-term (terms arg))))))
		    (setq func (current-trunc datum))
		    (if (greaterp const 0)
			(pstimes 
			 (let-pw datum (e- func ord)
				 (expand (m+t a (minus const)) '%GAMMA))
			 (let-pw datum (e+ func ord)
				 (tsprsum (m+t a (m-t '%%taylor-index%%))
					  `(%%taylor-index%% 1 ,const)
					  '%PRODUCT)))
			(pstimes 
			 (expand (m+t a (minus const)) '%GAMMA)
			 (let-pw datum (e+ func ord)
				 (psexpt 
				  (tsprsum (m+t a '%%taylor-index%%)
					   `(%%taylor-index%% 0
					       ,(minus (add1 const))) '%PRODUCT)
				  (rcmone))))))))))

(defun plygam-const (a arg func)
   (let ((const (ps-lc* arg)) (sub (cadr func)))
	(cond 
	 ((or (not (integerp sub)) (< sub -1))
	  (tay-err "Unable to expand at a subscript in"))
	 ((not (rcintegerp const))
	  (taylor2 (diff-expand `((MQAPPLY) ,func ,a) tlist)))
	 (T (setq const (car const))
	    (psplus
	     (expand (m+t a (f- const)) func)
	     (if (> const 0)
		 (pstimes
		  (cons (times (^ -1 sub) (factorial sub)) 1)
		  (tsprsum `((MEXPT) ,(m+t a (m-t '%%taylor-index%%)) ,(f- (f1+ sub)))
			   `(%%taylor-index%% 1 ,const) '%SUM))
		 (pstimes
		  (cons (times (^ -1 (f1+ sub)) (factorial sub)) 1)
		  (tsprsum `((MEXPT) ,(m+t a '%%taylor-index%%) ,(f- (f1+ sub)))
			   `(%%taylor-index%% 0 ,(f- (f1+ const))) '%SUM))))))))

(declare-top(unspecial var subl last sign last-exp))

;; Not done correctly
;;
;; (defun beta-trans (argl funname)
;;   funname ;ignored
;;   (let ((sum (m+t (car argl) (cadr argl))) (PSI[-1] '(($PSI ARRAY) -1)))
;;     (if (zerop sum) (unfam-sing-err)
;; 	(taylor2 `((MTIMES)
;; 		   ((MEXPT) $%E ((MPLUS) ((MQAPPLY) ,PSI[-1] ,(car argl))
;; 					 ((MQAPPLY) ,PSI[-1] ,(cadr argl))
;; 					 ((MTIMES) -1
;; 						   ((MQAPPLY) ,PSI[-1] ,sum))))
;; 		   ((MPLUS) ((MEXPT) ,(car argl) -1)
;; 			    ((MEXPT) ,(cadr argl) -1)))))))
