;;; This is a -*-Lisp-*- file.
;;;
;;; **********************************************************************
;;; This code was written as part of the Spice Lisp project at
;;; Carnegie-Mellon University, and has been placed in the public domain.
;;; Spice Lisp is currently incomplete and under active development.
;;; If you want to use this code or any part of Spice Lisp, please contact
;;; Scott Fahlman (FAHLMAN@CMUC). 
;;; **********************************************************************
;;;
;;; Functions to implement irrational functions for Spice Lisp 
;;; Maintained by Steven Handerson.
;;;
;;; The irrational functions are part of the standard Spicelisp environment.
;;;
;;; **********************************************************************
;;;
;;; Note: long-float constants are in the file lfloat-consts.
;;;

(eval-when (compile)
(defmacro **short-float** (f e)
  `(scale-float (decode-float (coerce ,f 'short-float)) ,e))
)

;;; From perqinit.

(defconstant most-positive-single-float
  (%primitive make-immediate-type most-positive-fixnum 18))
(defconstant most-negative-single-float
  (%primitive make-immediate-type 1 19))
;; This should be fixed whenever the printer is.
(defconstant least-positive-single-float
  (%primitive make-immediate-type 1 18))
(defconstant least-negative-single-float
  (%primitive make-immediate-type most-positive-fixnum 19))
(defconstant %single-float-exponent-length 8)
(defconstant %single-float-mantissa-length 20)
(defconstant single-float-epsilon (**short-float** 1 -20))
(defconstant single-float-negative-epsilon (**short-float** 1 -21))

(defconstant most-positive-short-float most-positive-single-float)
(defconstant least-positive-short-float least-positive-single-float)
(defconstant least-negative-short-float least-negative-single-float)
(defconstant most-negative-short-float most-negative-single-float)
(defconstant short-float-epsilon single-float-epsilon)
(defconstant short-float-negative-epsilon single-float-negative-epsilon)


(defconstant %double-float-exponent-length 11)
(defconstant %double-float-mantissa-length 53)


(defconstant %short-float-one 1.0)
(defconstant %short-float-half .5)
(defconstant %short-sqrt-half (**short-float** #o5520236 0))
(defconstant %short-pi 3.1415926535897932384)
(defconstant %short-pi/2 1.5707963267948966192)
(defconstant %short-pi/4 .7853981633974)
(defconstant %short-e 2.71828187
  "Euler's number, short float version.")

(defconstant sixth-pi 0.5236)
(defconstant sqrt3 1.7305248)
(defconstant 2-sqrt3 0.26794816)
(defconstant inv-2-sqrt3 3.7320704)


(defconstant %short-tan-max-x 53526.413)
(defconstant %short-2/pi .6366197723)
(defconstant %short-tan-eps (**short-float** 1 -10))
(defconstant %short-tan-c1 (**short-float** #o1444 1))
(Defconstant %short-tan-c2 4.838067948s-4)

(defconstant %short-tan-p1 -.957017723s-1)
(defconstant %short-tan-q0 1.0s0)
(defconstant %short-tan-q1 -.429135777s0)
(defconstant %short-tan-q2 .971685835s-2)

(defconstant %short-asin-eps (**short-float** 1 -10))

(defconstant %short-asin-p1 .0833331098s0)
(defconstant %short-asin-p2 -.0450065898s0)
(defconstant %short-asin-q0 .5)
(defconstant %short-asin-q1 -.4950779396s0)
(defconstant %short-asin-q2 .0892278749s0)


(defconstant %short-log-base2-e 1.44269504088)
(defconstant %short-exp-c1 (**short-float** #o543 0))
(defconstant %short-exp-c2 -2.1219444005e-4)
(defconstant %short-exp-p0 .2499999995)
(defconstant %short-exp-p1 .4160288626e-2)
(defconstant %short-exp-q1 .49987178778e-1)

(defconstant %short-exp-array
  '#(1.0
     1.09050773
     1.1892071
     1.29683954
     1.41421354
     1.54221082
     1.68179283
     1.83400811)
  "Used in the exp function.  (aref %short-exp-array i) <-> (expt 2.0 (/ i 8))")

(defconstant %short-log-C0 0.7071067811)
(defconstant %short-log-C1 (**short-float** #o543 0))
(defconstant %short-log-C2 -0.0002121944400546905827679)
(defconstant %short-float-half .5)
(defconstant %short-log-a0 -0.5527074855)
(defconstant %short-log-b0 -6.632718214)
(defconstant %short-log-b1 1.0)

(defconstant %short-sin-maximum 1.0s10)
(defconstant %short-sin-epsilon (**short-float** 1 -10))

(defconstant %short-c2-sin  0.000967653589793)
(defconstant %short-sin-r1 -0.1666665668)
(defconstant %short-sin-r2 0.008333025139)
(defconstant %short-sin-r3 -0.0001980741872)
(defconstant %short-sin-r4 0.000002601903036)
(defconstant %short-zero 0.0)

(defconstant %short-c1-sin 3.140625)


  (defconstant %short-expt-a1
  (make-array 17. :initial-contents
	      (list (**short-float** 1 1)
		    (**short-float** #o7522256 0)
		    (**short-float** #o7254030 0)
		    (**short-float** #o7014632 0)
		    (**short-float** #o6564236 0)
		    (**short-float** #o6342220 0)
		    (**short-float** #o6126344 0)
		    (**short-float** #o5720424 0)
		    (**short-float** #o5520236 0)
		    (**short-float** #o5325406 0)
		    (**short-float** #o5137732 0)
		    (**short-float** #o4757246 0)
		    (**short-float** #o4603376 0)
		    (**short-float** #o4434172 0)
		    (**short-float** #o4271270 0)
		    (**short-float** #o4132530 0)
		    (**short-float** #o4 0))))
  (defconstant %short-expt-a2
  (make-array 8. :initial-contents
	      (list (**short-float** #o1505222 #o-24)
		    (**short-float** #o1673024 #o-24)
		    (**short-float** #o1405216 #o-24)
		    (**short-float** #o347654 #o-26)
		    (**short-float** #o1672440 #o-24)
		    (**short-float** #o230110 #o-26)
		    (**short-float** #o334724 #o-26)
		    (**short-float** #o331746 #o-26))))


(defconstant %short-expt-p1 .83357541s-1)

(defconstant %short-expt-q1 .69314675s0)
(defconstant %short-expt-q2 .24018510s0)
(defconstant %short-expt-q3 .54360383s-1)

(defconstant %short-expt-k .44269504s0)



;;; Warning: multiple evaluation in these macros is the rule, 
;;; not the exception.

;;; Some floating macros.

(eval-when (compile)
  (defmacro float-op (a b short-float long-float)
    `(cond ((typep ,a 'long-float) (,long-float ,a (%primitive float-long ,b)))
	   ((typep ,b 'long-float) (,long-float (%primitive float-long ,a) ,b))
	   (t (,short-float (%primitive float-short ,a) (%primitive float-short ,b)))))

  (defmacro floatize (f)
    `(if (floatp ,f) ,f (setq ,f (float ,f))))
  
  (defmacro scale-float-by (e f) `(scale-float ,f ,e)))
  

(eval-when (compile load)
  (defmacro poly-eval (x &rest list)
    (do ((list (cdr (reverse list)) (cdr list))
	 (form (car (last list)) `(+ ,(car list) (* ,x ,form))))
	((null list) form))))


(defun %signum (x)
  (let ((res (cond ((plusp x) 1)
		   ((zerop x) 0)	
		   (t -1))))
    (if (floatp x) (float res x) res)))



;;; Integer square root - (<= (expt result 2) input).
;;; Performs in (log input) time.

;;; Successively approximates the result using two bounds and their average,
;;; repeated until the bounds differ by at most 1 (for input=1, both start
;;; equal).

(defun isqrt (x)
  (if (and (integerp x) (not (minusp x)))
      (if (zerop  x) 0
	  (do* ((p () (<= (* m m) x))
		(b 1 (if p m b))
		(h x (if p h m))
		(m (ash x -1) (ash (+ b h) -1)))
	       ((<= h (1+ b)) b)))
      (error "Isqrt: ~S argument must be a nonnegative integer" x)))



;;; Assumes that the argument is a nonnegative long-float.
;;; Uses Newton's method with a special initial estimate, described
;;; in Cody and Waite.

(eval-when (compile)
  (defmacro short-sqrt (x)
    `(multiple-value-bind (f N) (decode-float ,x)
       (let ((y (+ (* f .59016) .41731)))
	 (dotimes (i 2) (setq y (scale-float (+ y (/ f y)) -1)))
	 (cond ((oddp N)
		(setq y (* y %short-sqrt-half))
		(setq N (1+ N))))
	 (scale-float y (/ N 2))))))

(eval-when (compile)
  (defmacro long-sqrt (x)
    `(multiple-value-bind (f N) (decode-float ,x)
       (let ((y (+ %long-sqrt-c1 (* %long-sqrt-c2 f))))
	 (dotimes (i 3) (setq y (scale-float (+ y (/ f y)) -1)))
	 (cond ((oddp N)
		(setq y (* y %long-sqrt-half))
		(setq N (1+ N))))
	 (scale-float y (/ N 2))))))

(defun sqrt (x)
  (cond ((minusp x) (error "~S Argument out of range." x)))
  (typecase x
    (short-float (short-sqrt x))
    (long-float (long-sqrt x))
    (number (sqrt (float x)))
    (t (error "Sqrt: Argument not number - ~A" x))
    ))


    
;;; Function calculates the value of x raised to the nth power.
;;; This function calculates the successive squares of base,
;;; storing them in newbase, halving n at the same time.  If
;;; n is odd total is multiplied by base, at any given time (fix later)

;(declare (inline intexp))

(defun intexp (base power)
  (cond ((minusp power)
	 (/ (intexp base (- power))))
	((and (rationalp base)
	      (not (integerp base)))  ; Could be a make-rational.
	 (/ (intexp (numerator base) power) (intexp (denominator base) power)))
	((and (integerp base) (= base 2))
	 (ash 1 power))
	(t (do ((nextn (ash power -1) (ash power -1))
		(total (if (oddp power) base 1)
		       (if (oddp power) (* base total) total)))
	       ((zerop nextn) total)
	     (setq base (* base base))
	     (setq power nextn)))))

;;; This function calculates x raised to the nth power.  It separates
;;; the  cases by the type of n, for efficiency reasons, as powers can
;;; be calculated more efficiently if n is a positive integer,  Therefore,
;;; All integer values of n are calculated as positive integers, and
;;; inverted if negative.

(defun expt (x n)
  (cond ((and (rationalp x) (integerp n)) (intexp x n))
	((zerop x) (if (plusp n) x
		       (error "~A to a non-positive power ~A." x n)))
	((and (not (integerp n)) (minusp x))
	 (error "Negative number ~A to non-integral power ~A." x n))
	(t (float-op x n short-expt long-expt))))




(eval-when (compile)
  (defmacro short-exp (x)
    `(let* ((n (round (* ,x %short-log-base2-e)))
	    (xn (float n 1.0))
	    (g (- (- ,x (* xn %short-exp-c1)) (* xn %short-exp-c2)))
	    (z (* g g))
	    (g*Pz (* g (poly-eval z %short-exp-p0 %short-exp-p1))))
       (scale-float-by
	(1+ n) (+ .5 (/ g*Pz (- (poly-eval z .5 %short-exp-q1) g*Pz)))))))

(eval-when (compile)
  (Defmacro long-exp (x)
    `(let* ((n (round (* ,x %long-log-base2-e)))
	    (xn (float n %long-log-base2-e))
	    (g (- (- ,x (* xn %long-exp-c1)) (* xn %long-exp-c2)))
	    (z (* g g))
	    (g*Pz (* g (poly-eval z %long-exp-p0 %long-exp-p1 %long-exp-p2))))
       (scale-float-by
	(1+ n) (+ %long-half
		  (/ g*Pz (- (poly-eval z %long-half %long-exp-q1 %long-exp-q2)
			     g*Pz)))))))


(defun exp (num)
  "Calculates e to the given power, where e is the base of natural logarithms."
  (if (integerp num) (setq num (float num)))
  (typecase num
    (short-float (short-exp num))
    (long-float (long-exp num))
    (number (exp (float num)))
    (t (error "Exp: object not a number - ~A" num))))



;;; Hyperbolic trig functions.
;;; Each of the hyperbolic trig functions is calculated directly from 
;;; their definition.  Exp(x) is calculated only once for efficiency.

(defun sinh (x)
  (let ((z (exp x)))
    (/ (- z (/ z)) 2)))

(defun cosh (x)
  (let ((z (exp x)))
    (/ (+ z (/ z)) 2)))

;;; Different form than in the manual.  Does much better.
(defun tanh (x)
  (let* ((z (exp (* 2 x)))
	 (y (/ z)))
    (- (/ (1+ y)) (/ (1+ z)))))

(eval-when (compile)
  (Defmacro short-log (x) 
    `(cond ((plusp ,x)
	    (multiple-value-bind (f n) (decode-float ,x)
	      (declare (short-float f) (fixnum n))
	      (let (znum zden)
		(cond ((> f %short-log-C0)
		       (setq znum (- (- f .5) .5))
		       (setq zden (+ .5 (* .5 f))))
		      (t (decf N)
			 (setq zden
			       (+ .5
				  (* .5
				     (setq znum (- f .5)))))))
		(let* ((z (/ znum zden))
		       (w (* z z))
		       (rz2 (/ (* w (poly-eval w %short-log-a0))
			       (poly-eval w %short-log-b0 %short-log-b1)))
		       (big-Rz (+ z (* z rz2)))
		       (XN (%primitive float-short N)))
		  (+ (+ (* XN %short-log-c2) big-Rz)
		     (* XN %short-log-c1))))))
	   ((zerop ,x) (error "Log of ~A undefined." ,x))
	   (t (error "Complex numbers not yet implemented - (log ~A)" ,x)))) 

  (defmacro long-log (l)
    `(cond ((zerop ,l) (error "Log: log of zero not defined"))
	   ((plusp ,l)
	    (multiple-value-bind (f N) (decode-float ,l)
	      (let (znum zden)
		(cond ((> f %long-sqrt-half)
		       (setq zden (+ %long-half (* %long-half f)))
		       (setq znum (- (- f %long-half) %long-half)))
		      (t (decf N)
			 (setq zden
			       (+ %long-half (* %long-half
						(setq znum (- f %long-half)))))))
		(let* ((z (/ znum zden))
		       (w (* z z))
		       (rz↑2 (/ (* w (poly-eval w %long-log-a0 %long-log-a1
						%long-log-a2))
				(poly-eval w %long-log-b0
					   %long-log-b1 %long-log-b2
					   %long-float-one)))
		       (bigrz (+ z (* z rz↑2)))
		       (xn (%primitive float-long N)))
		  (+ (+ (* xn %long-log-c2) bigrz)
		     (* xn %long-log-c1))))))
	   (t (error "Complex numbers not yet implemented - (log ~A)" ,l))))
)

(eval-when (compile)
  (defmacro log-base (x base)
    `(/ (log ,x) (log ,base))))

(defun log (number &optional (base nil base-supplied))
  (cond (base-supplied (float-op number base log-base log-base))
	(t (typecase number
	     (short-float (short-log number))
	     (long-float (long-log number))
	     (number (log (float number)))
	     (t (error "Log: argument not number - ~A" number))))))


(defun asinh (x)
  (log (+ x (sqrt (+ (* x x) 1)))))

(defun acosh (x)
  (if (plusp x)
      (log (+ x (sqrt (- (* x x) 1))))
      (error "~S argument out of range." x)))

(defun atanh (x)
  (if (< -1 x 1)
      (* 0.5 (log (/ (1+ x) (- 1 x))))
      (error "~S argument out of range." x)))





(Eval-when (compile)
  (Defmacro short-sin (x cos-flag)
    `(let ((sgn ,(if cos-flag 1 `(if (minusp ,x) -1 1)))
	   (y ,(if cos-flag `(+ (abs ,x) %short-pi/2) `(abs ,x))))
       (if (> y %short-sin-maximum)
	   ,(if cos-flag
		`(cerror "Cos: absolute value of argument, ~s, too large."
			 "Will return an imprecise result." ,x)
		`(cerror "Sin: absolute value of argument, ~s, too large."
			 "Will return an imprecise result." ,x)))
       (let* ((n (round Y %short-pi))
	      (xn (float n))
	      (f (- (- Y (* xn %short-c1-sin))
		    (* xn %short-c2-sin))))
	 (if (oddp n) (setq sgn (- sgn)))
	 (* (if (< (abs f) %short-sin-epsilon) f
		(let ((g (* f f)))
		  (+ f (* f (* g (poly-eval g %short-sin-r1 %short-sin-r2
					    %short-sin-r3 %short-sin-r4))))))
	    sgn))))

  (defmacro long-sin (x cos-flag)
    `(let ((sgn ,(if cos-flag 1 `(if (minusp ,x) -1 1)))
	   (y ,(if cos-flag `(+ (abs ,x) %long-pi/2) `(abs ,x))))
       (if (> y %long-sin-max)
	   ,(if cos-flag
		`(cerror "Cos: absolute value of argument, ~S too large."
			 "Will return an imprecise result." ,x)
		`(cerror "Sin: absolute value of argument, ~S too large."
			 "Will return an imprecise result." ,x)))
       (let* ((N (round Y pi))
	      (XN (float N %long-half))
	      (f (- (- Y (* XN %long-sin-c1))
		    (* XN %long-sin-c2))))
	 (if (oddp N) (setq sgn (- sgn)))
	 (* (cond ((< (abs f) %long-sin-epsilon) f)
		  (t (let* ((g (* f f)))
		       (+ f (* f (* g (poly-eval g %long-sin-r1 %long-sin-r2
						 %long-sin-r3 %long-sin-r4
						 %long-sin-r5 %long-sin-r6
						 %long-sin-r7 %long-sin-r8)))))))
	    sgn))))
)

(defun sin (x)
  (if (not (floatp x)) (setq x (float x)))
  (typecase x
    (short-float (short-sin x nil))
    (long-float (long-sin x nil))))

(defun cos (x)
  (if (not (floatp x)) (setq x (float x)))
  (typecase x
    (short-float (short-sin x T))
    (long-float (long-sin x T))))


;;; The arc tangent is determined using a power series described in 
;;; Computer Approximations by Hart & Cheney p122 (6.5.15)
;;; As the radius of convergence is 1 there is a very poor performance
;;; When the value of x is near 1.  This should be fixed at a later date.


;(declare (inline short-atan))

(defun atan1 (x)
  (cond ((minusp x) (- (atan1 (- x))))
	((< x 2-sqrt3) (- %short-pi/2 (atan2 (/ x))))
	((< x 1)
	 (- (+ %short-pi/2 sixth-pi) (atan2 (/ (+ sqrt3 x) (1- (* x sqrt3))))))
	((< x inv-2-sqrt3)
	 (let* ((inv (/ x)))
	   (- (atan2 (/ (+ sqrt3 inv) (1- (* sqrt3 inv)))) sixth-pi)))
	(t (atan2 x))))

(defun atan2 (x)
  (do* ((sqr (- (/ (* x x))))
	(int 1 (+ 2 int))
	(old 0 val)
	(pow (/ x) (* pow sqr))
	(val pow (+ val (/ pow int))))
       ((= old val)
	(- %short-pi/2  val))))

(defun short-atan (y &optional x)
  (cond	((not x) 
	 (if (zerop y) 0.0 (atan1 y)))
	((= y x 0)
	 (error "Error in double entry atan both 0." ))
	((= x 0) (* (%signum y) %short-pi/2))
	((= y 0) (if (plusp x) 0 %short-pi))
	((and (plusp x)(plusp y)) (atan1 (/ y x)))
	((plusp y) (- %short-pi (atan1 (/ (- y) x))))
	((plusp x) (- (atan1 (/ y (- x)))))
	(t (- (atan1 (/ y x)) %short-pi))))


;;; Assume both args are floats of the same type.
(defun long-atan (x &optional (y %long-float-one))
  (cond ((zerop x)
	 (if (zerop y) (error "Attempt to divide ~S by ~S" x y) %long-pi/2))
	;; Check for underflow and overflow in the division.
	((multiple-value-bind (xf xe) (decode-float x)
	   (multiple-value-bind (yf ye) (decode-float y)
	     (multiple-value-bind (res rese)
				  (decode-float (/ xf yf))
	       (declare (ignore res))  
	       (let ((e (+ (- xe ye) rese)))
		 (cond ((> e %long-float-exp-max) %long-pi/2)
		       ((< e %long-float-exp-min) 0)
		       (t (setq x (/ x y)) 
			  nil)))))))
	(t (let* ((f (abs x))
		  (N (cond ((> f %long-float-one) (setq f (/ f)) 2)
			   (t 0))))
	     (cond ((> f %long-atan-2-sqrt3)
		    (setq f (/ (1- (* f %long-atan-sqrt3))
			       (+ f %long-atan-sqrt3)))
		    (setq N (1+ N))))
	     (let ((res (cond ((< (abs f) %long-atan-epsilon) f)
			      (t (let* ((g (* f f))
					(R (/ (* g (poly-eval g %long-atan-p0
							      %long-atan-p1
							      %long-atan-p2
							      %long-atan-p3))
					      (poly-eval g %long-atan-q0
							 %long-atan-q1
							 %long-atan-q2
							 %long-atan-q3
							 %long-atan-q4))))
				   (+ f (* f R)))))))
	       (if (> N 1) (setq res (- res)))
	       (setq res (+ res (svref %long-atan-vector N)))
	       (if (minusp x) (- res) res))))))


(defun atan (x &optional y)
  (cond (y (if (or (complexp x) (complexp y))
	       (error "~S and ~S both complex." x y))
	   (float-op x y short-atan long-atan))
	(t (typecase (floatize x)
	     (short-float (short-atan x))
	     (long-float (long-atan x))))))




;;;
;;; Short-float expt.  Still computes (exp (* y (log x))), but
;;; does so as to minimize loss of precision to exp,
;;; since normally the error in the result depends upon the 
;;; magnitude of the base.
;;;


;;; Used in the expt function.  "The main requirement is that
;;; (< (abs (- x (reduce x))) 1/16)".
(eval-when (compile load)
  (defmacro short-expt-reduce (x)
    `(multiple-value-bind (f e s) (decode-float ,x)
       (if (zerop f) f				;To prevent making thing.
	   (let ((off (- 16 e)))
	     (* s (scale-float
		   (if (plusp off)
		       (let ((thing (scale-float .5s0 off)))
			 (- (+ f thing) thing))
		       f)
		   e)))))))


;;; Not for negative or zero base.
;;;
;;; Algorithm from Cody and Waite.
;;; Altered for (gee wow) zero-based array accessing.
;;;
(defun short-expt (x y)
  (multiple-value-bind (g m) (decode-float x)
    (let ((p 1))
      (if (<= g (svref %short-expt-A1 8)) (setq p 9))
      (if (<= g (svref %short-expt-A1 (+ p 3))) (setq p (+ p 4)))
      (if (<= g (svref %short-expt-A1 (1+ p))) (setq p (+ p 2)))
      (let* ((z (scale-float-by
		 1 (/ (- (- g (svref %short-expt-a1 p))
			 ;; 1- for 0-based array access.
			 (svref %short-expt-a2 (1- (/ (1+ p) 2))))
		      (+ g (svref %short-expt-a1 p)))))
	     (v (* z z))
	     (R (* z v (poly-eval v %short-expt-p1)))
	     (U2 (+ (+ (+ R (* R %short-expt-K))
		       (* z %short-expt-K))
		    z))
	     (U1 (scale-float
		  (%primitive float-short (- (* 16 m) p)) -4))
	     (Y1 (short-expt-reduce y))
	     (Y2 (- Y y1))
	     (W (+ (* u2 y) (* u1 y2)))
	     (W1 (short-expt-reduce W))
	     (W2 (- W W1)))
	(setq W (+ W1 (* U1 Y1)))
	(setq W1 (short-expt-reduce W))
	(setq W2 (+ W2 (- W W1)))
	(setq W (short-expt-reduce W2))
	(let ((IW1 (truncate (scale-float-by 4 (+ W1 W)))))
	  (setq W2 (- W2 W))
;	  (cond ((> IW1 %short-expt-max)
;		 (error "~S to the ~S has overflowed." x y))
;		((< IW1 %short-expt-min)
;		 (error "~S to the ~S has underflowed." x y)))
	  (cond ((plusp W2) (incf IW1) (decf W2 .0625s0)))
	  (let* ((mprime (if (minusp IW1)
			     (truncate IW1 16)
			     (1+ (truncate IW1 16))))
		 (pprime (- (* 16 mprime) IW1)))
	    (scale-float-by
	     mprime (+ (svref %short-expt-a1 pprime)
		       (* (svref %short-expt-a1 pprime)
			  W2 (poly-eval W2 %short-expt-q1 %short-expt-q2
					   %short-expt-q3))))))))))



;;;
;;; Same thing, for long-floats.
;;;

;;; Truncating by bugout is slower, although plusp...
(eval-when (compile load)
  (defmacro long-expt-reduce (x)
    `(multiple-value-bind (f e s) (decode-float ,x)
       (let ((off (- 49 e)))
	 (* s (scale-float
	       (if (plusp off)
		   (let ((thing (scale-float %long-half off)))
		     (- (+ f thing) thing))
		   f)
	       e))))))


;;; Not for negative or zero base.
;;;
;;; Algorithm from Cody and Waite.
;;; Altered for (gee wow) zero-based array accessing.
;;;
(defun long-expt (x y)
  (multiple-value-bind (g m) (decode-float x)
    (let ((p 1))
      (if (<= g (svref %long-expt-A1 8)) (setq p 9))
      (if (<= g (svref %long-expt-A1 (+ p 3))) (setq p (+ p 4)))
      (if (<= g (svref %long-expt-A1 (1+ p))) (setq p (+ p 2)))
      (let* ((z (scale-float-by
		 1 (/ (- (- g (svref %long-expt-a1 p))
			 ;; 1- for 0-based array access.
			 (svref %long-expt-a2 (1- (/ (1+ p) 2))))
		      (+ g (svref %long-expt-a1 p)))))
	     (v (* z z))
	     (R (* z v (poly-eval v %long-expt-p1 %long-expt-p2
				  %long-expt-p3 %long-expt-p4)))
	     (U2 (+ (+ (+ R (* R %long-expt-K))
		       (* z %long-expt-K))
		    z))
	     (U1 (scale-float
		  (%primitive float-long (- (* 16 m) p)) -4))
	     (Y1 (long-expt-reduce y))
	     (Y2 (- Y y1))
	     (W (+ (* u2 y) (* u1 y2)))
	     (W1 (long-expt-reduce W))
	     (W2 (- W W1)))
	(setq W (+ W1 (* U1 Y1)))
	(setq W1 (long-expt-reduce W))
	(setq W2 (+ W2 (- W W1)))
	(setq W (long-expt-reduce W2))
	(let ((IW1 (truncate (scale-float-by 4 (+ W1 W)))))
	  (setq W2 (- W2 W))
 ;	  (cond ((> IW1 %long-expt-max)
;		 (error "~S to the ~S has overflowed." x y))
;		((< IW1 %long-expt-min)
;		 (error "~S to the ~S has underflowed." x y)))
	  (cond ((plusp W2) (incf IW1) (decf W2 %long-float-sixteenth)))
	  (let* ((mprime (if (minusp IW1)
			     (truncate IW1 16)
			     (1+ (truncate IW1 16))))
		 (pprime (- (* 16 mprime) IW1)))
	    (scale-float-by
	     mprime (+ (svref %long-expt-a1 pprime)
		       (* (svref %long-expt-a1 pprime)
			  W2 (poly-eval W2 %long-expt-q1 %long-expt-q2
					   %long-expt-q3 %long-expt-q4
					   %long-expt-q5 %long-expt-q6
					   %long-expt-q7))))))))))




(defun short-asin (x flag) 
  (declare (short-float x) (fixnum flag))
  (let* ((y (abs x))
	 (test (> y %short-float-half))
	 (i (cond (test (if (> y %short-float-one)
			    (error "~A - argument out of range" x))
			(1- flag))
		  (t flag)))
	 (result (if (and (not test) (< y %short-asin-eps))
		     y
		     (let* ((g (if test
				   (let ((temp (scale-float
						(- %short-float-one y) -1)))
				     (setq y (- (scale-float (sqrt temp) 1)))
				     temp)
				   (* y y))))
		       (+ y (* y (/ (* g (poly-eval g %short-asin-p1
						    %short-asin-p2))
				    (poly-eval g %short-asin-q0 %short-asin-q1
					       %short-asin-q2))))))))
	 (cond ((zerop flag)
		(let ((res (if (zerop i)
			       result
			       (+ %short-pi/4 result %short-pi/4))))
		     (if (minusp x) (- res) res)))
	       ((minusp x)
		(if (zerop i) (+ %short-pi/2 result %short-pi/2)
		    (+ %short-pi/4 result %short-pi/4)))
	       ((zerop i) (- result))
	       (t (+ (- %short-pi/4 result) %short-pi/4)))))



(defun long-asin (x flag) 
  (declare (long-float x) (fixnum flag))
  (let* ((y (abs x))
	 (test (> y %long-float-half))
	 (i (cond (test (if (> y %long-float-one)
			    (error t nil "argument out of range"))
			(1- flag))
		  (t flag)))
	 (result (if (and (not test) (< y %long-asin-eps))
		     y
		     (let* ((g (if test
				   (let ((temp (scale-float
						(- %long-float-one y) -1)))
				     (setq y (- (scale-float (sqrt temp) 1)))
				     temp)
				   (* y y))))
		       (+ y (* y (/ (* g (poly-eval g %long-asin-p1
						    %long-asin-p2 %long-asin-p3
						    %long-asin-p4
						    %long-asin-p5))
				    (poly-eval g %long-asin-q0 %long-asin-q1
					       %long-asin-q2 %long-asin-q3
					       %long-asin-q4
					       %long-asin-q5))))))))
    (cond ((zerop flag)
	   (let ((res (if (zerop i)
			  result
			  (+ %long-pi/4 result %long-pi/4))))
	     (if (minusp x) (- res) res)))
	  ((minusp x)
	   (if (zerop i) (+ %long-pi/2 result %long-pi/2)
	       (+ %long-pi/4 result %long-pi/4)))
	  ((zerop i) (- result))
	  (t (+ (- %long-pi/4 result) %long-pi/4)))))


(Defun asin (x)
  (typecase x
    (long-float (long-asin x 0))
    (short-float (short-asin x 0))
    (number (asin (float x)))))

(defun acos (x)
  (typecase x
    (long-float (long-asin x 1))
    (short-float (short-asin x 1))
    (number (acos (float x)))))


(defun short-tan (x)
  (declare (short-float x))
  (let ((y (abs x)))
    (declare (short-float y))
    (if (< %short-tan-max-x y)
	(error "~s out of range." x)
	(let* ((N (round (* x %short-2/pi)))
	       (xn (%primitive float-short N))
	       (f (- (- x (* xn %short-tan-c1))
		     (* xn %short-tan-c2))))
	  (if (< (abs f) %short-tan-eps)
	      (if (evenp n) f (- (/ f)))
	      (let* ((g (* f f))
		     (p (+ f (* f g (poly-eval g %short-tan-p1))))
		     (q (poly-eval g %short-tan-q0 %short-tan-q1
				   %short-tan-q2)))
		(declare (short-float g p q))
		(if (evenp N)
		    (/ p q)
		    (/ q (- p)))))))))




(defun long-tan (x)
  (declare (long-float x))
  (let ((y (abs x)))
    (declare (long-float y))
    (if (< %long-tan-max-x y)
	(error "~s out of range." x)
	(let* ((N (round (* x %long-2/pi)))
	       (xn (%primitive float-long N))
	       (f (- (- x (* xn %long-tan-c1))
		     (* xn %long-tan-c2))))
	  (if (< (abs f) %long-tan-eps)
	      (if (evenp n) f (- (/ f)))
	      (let* ((g (* f f))
		     (p (+ f (* f g (poly-eval g %long-tan-p1 %long-tan-p2
					       %long-tan-p3))))
		     (q (poly-eval g %long-tan-q0 %long-tan-q1
				   %long-tan-q2 %long-tan-q3 %long-tan-q4)))
		(declare (long-float g p q))
		(if (evenp N)
		    (/ p q)
		    (/ q (- p)))))))))

(Defun tan (x)
  (if (not (floatp x)) (setq x (float x)))
  (typecase x
    (long-float (long-tan x))
    (short-float (short-tan x))))