X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fcode%2Firrat.lisp;h=7b18f5a7856b53bc65b7aba06ed5ad31dc15b835;hb=322be917a6b06f5741b0da8a6621a4f1e881bf11;hp=9e71d15c8457b354c81a1137b5f927cae06db7db;hpb=a530bbe337109d898d5b4a001fc8f1afa3b5dc39;p=sbcl.git diff --git a/src/code/irrat.lisp b/src/code/irrat.lisp index 9e71d15..7b18f5a 100644 --- a/src/code/irrat.lisp +++ b/src/code/irrat.lisp @@ -11,36 +11,26 @@ ;;;; files for more information. (in-package "SB!KERNEL") - -(file-comment - "$Header$") ;;;; miscellaneous constants, utility functions, and macros (defconstant pi 3.14159265358979323846264338327950288419716939937511L0) ;(defconstant e 2.71828182845904523536028747135266249775724709369996L0) -;;; Make these INLINE, since the call to C is at least as compact as a Lisp -;;; call, and saves number consing to boot. -;;; -;;; FIXME: This should be (EVAL-WHEN (COMPILE-EVAL) (SB!XC:DEFMACRO ..)), -;;; I think. -(defmacro def-math-rtn (name num-args) - (let ((function (intern (concatenate 'simple-string - "%" - (string-upcase name))))) +;;; Make these INLINE, since the call to C is at least as compact as a +;;; Lisp call, and saves number consing to boot. +(eval-when (:compile-toplevel :execute) + +(sb!xc:defmacro def-math-rtn (name num-args) + (let ((function (symbolicate "%" (string-upcase name)))) `(progn (proclaim '(inline ,function)) - (let ((sb!int::*rogue-export* "DEF-MATH-RTN")) - (export ',function)) (sb!alien:def-alien-routine (,name ,function) double-float - ,@(let ((results nil)) - (dotimes (i num-args (nreverse results)) - (push (list (intern (format nil "ARG-~D" i)) - 'double-float) - results))))))) - -(eval-when (:compile-toplevel :load-toplevel :execute) + ,@(let ((results nil)) + (dotimes (i num-args (nreverse results)) + (push (list (intern (format nil "ARG-~D" i)) + 'double-float) + results))))))) (defun handle-reals (function var) `((((foreach fixnum single-float bignum ratio)) @@ -54,7 +44,7 @@ ;;; Please refer to the Unix man pages for details about these routines. -;;; Trigonometric. +;;; trigonometric #!-x86 (def-math-rtn "sin" 1) #!-x86 (def-math-rtn "cos" 1) #!-x86 (def-math-rtn "tan" 1) @@ -69,7 +59,7 @@ (def-math-rtn "acosh" 1) (def-math-rtn "atanh" 1) -;;; Exponential and Logarithmic. +;;; exponential and logarithmic #!-x86 (def-math-rtn "exp" 1) #!-x86 (def-math-rtn "log" 1) #!-x86 (def-math-rtn "log10" 1) @@ -77,81 +67,6 @@ #!-x86 (def-math-rtn "sqrt" 1) (def-math-rtn "hypot" 2) #!-(or hpux x86) (def-math-rtn "log1p" 1) - -#!+x86 ;; These are needed for use by byte-compiled files. -(progn - (defun %sin (x) - (declare (double-float x) - (values double-float)) - (%sin x)) - (defun %sin-quick (x) - (declare (double-float x) - (values double-float)) - (%sin-quick x)) - (defun %cos (x) - (declare (double-float x) - (values double-float)) - (%cos x)) - (defun %cos-quick (x) - (declare (double-float x) - (values double-float)) - (%cos-quick x)) - (defun %tan (x) - (declare (double-float x) - (values double-float)) - (%tan x)) - (defun %tan-quick (x) - (declare (double-float x) - (values double-float)) - (%tan-quick x)) - (defun %atan (x) - (declare (double-float x) - (values double-float)) - (%atan x)) - (defun %atan2 (x y) - (declare (double-float x y) - (values double-float)) - (%atan2 x y)) - (defun %exp (x) - (declare (double-float x) - (values double-float)) - (%exp x)) - (defun %log (x) - (declare (double-float x) - (values double-float)) - (%log x)) - (defun %log10 (x) - (declare (double-float x) - (values double-float)) - (%log10 x)) - #+nil ;; notyet - (defun %pow (x y) - (declare (type (double-float 0d0) x) - (double-float y) - (values (double-float 0d0))) - (%pow x y)) - (defun %sqrt (x) - (declare (double-float x) - (values double-float)) - (%sqrt x)) - (defun %scalbn (f ex) - (declare (double-float f) - (type (signed-byte 32) ex) - (values double-float)) - (%scalbn f ex)) - (defun %scalb (f ex) - (declare (double-float f ex) - (values double-float)) - (%scalb f ex)) - (defun %logb (x) - (declare (double-float x) - (values double-float)) - (%logb x)) - (defun %log1p (x) - (declare (double-float x) - (values double-float)) - (%log1p x)) - ) ; progn ;;;; power functions @@ -166,16 +81,16 @@ ;;; INTEXP -- Handle the rational base, integer power case. -;;; FIXME: As long as the -;;; system dies on stack overflow or memory exhaustion, it seems reasonable -;;; to have this, but its default should be NIL, and when it's NIL, -;;; anything should be accepted. +;;; FIXME: As long as the system dies on stack overflow or memory +;;; exhaustion, it seems reasonable to have this, but its default +;;; should be NIL, and when it's NIL, anything should be accepted. (defparameter *intexp-maximum-exponent* 10000) -;;; This function precisely calculates base raised to an integral power. It -;;; separates the cases by the sign of power, for efficiency reasons, as powers -;;; can be calculated more efficiently if power is a positive integer. Values -;;; of power are calculated as positive integers, and inverted if negative. +;;; This function precisely calculates base raised to an integral +;;; power. It separates the cases by the sign of power, for efficiency +;;; reasons, as powers can be calculated more efficiently if power is +;;; a positive integer. Values of power are calculated as positive +;;; integers, and inverted if negative. (defun intexp (base power) (when (> (abs power) *intexp-maximum-exponent*) ;; FIXME: should be ordinary error, not CERROR. (Once we set the @@ -197,13 +112,14 @@ (setq power nextn))))) ;;; If an integer power of a rational, use INTEXP above. Otherwise, do -;;; floating point stuff. If both args are real, we try %POW right off, -;;; assuming it will return 0 if the result may be complex. If so, we call -;;; COMPLEX-POW which directly computes the complex result. We also separate -;;; the complex-real and real-complex cases from the general complex case. +;;; floating point stuff. If both args are real, we try %POW right +;;; off, assuming it will return 0 if the result may be complex. If +;;; so, we call COMPLEX-POW which directly computes the complex +;;; result. We also separate the complex-real and real-complex cases +;;; from the general complex case. (defun expt (base power) #!+sb-doc - "Returns BASE raised to the POWER." + "Return BASE raised to the POWER." (if (zerop power) (1+ (* base power)) (labels (;; determine if the double float is an integer. @@ -312,16 +228,18 @@ (let ((pow (sb!kernel::%pow abs-x y))) (declare (double-float pow)) (case yisint - (1 ; Odd + (1 ; odd (coerce (* -1d0 pow) rtype)) - (2 ; Even + (2 ; even (coerce pow rtype)) - (t ; Non-integer + (t ; non-integer (let ((y*pi (* y pi))) (declare (double-float y*pi)) (complex - (coerce (* pow (%cos y*pi)) rtype) - (coerce (* pow (%sin y*pi)) rtype))))))))))))) + (coerce (* pow (%cos y*pi)) + rtype) + (coerce (* pow (%sin y*pi)) + rtype))))))))))))) (declare (inline real-expt)) (number-dispatch ((base number) (power number)) (((foreach fixnum (or bignum ratio) (complex rational)) integer) @@ -394,7 +312,7 @@ (defun abs (number) #!+sb-doc - "Returns the absolute value of the number." + "Return the absolute value of the number." (number-dispatch ((number number)) (((foreach single-float double-float fixnum rational)) (abs number)) @@ -413,7 +331,7 @@ (defun phase (number) #!+sb-doc - "Returns the angle part of the polar representation of a complex number. + "Return the angle part of the polar representation of a complex number. For complex numbers, this is (atan (imagpart number) (realpart number)). For non-complex positive numbers, this is 0. For non-complex negative numbers this is PI." @@ -465,10 +383,9 @@ (defun cis (theta) #!+sb-doc - "Return cos(Theta) + i sin(Theta), AKA exp(i Theta)." - (if (complexp theta) - (error "Argument to CIS is complex: ~S" theta) - (complex (cos theta) (sin theta)))) + "Return cos(Theta) + i sin(Theta), i.e. exp(i Theta)." + (declare (type real theta)) + (complex (cos theta) (sin theta))) (defun asin (number) #!+sb-doc @@ -540,12 +457,6 @@ ;; error. These bad definitions also mean that sin and cos for ;; complex numbers can also lose big. -#+nil -(defun sinh (number) - #!+sb-doc - "Return the hyperbolic sine of NUMBER." - (/ (- (exp number) (exp (- number))) 2)) - (defun sinh (number) #!+sb-doc "Return the hyperbolic sine of NUMBER." @@ -557,12 +468,6 @@ (complex (* (sinh x) (cos y)) (* (cosh x) (sin y))))))) -#+nil -(defun cosh (number) - #!+sb-doc - "Return the hyperbolic cosine of NUMBER." - (/ (+ (exp number) (exp (- number))) 2)) - (defun cosh (number) #!+sb-doc "Return the hyperbolic cosine of NUMBER." @@ -625,9 +530,11 @@ ((complex) (complex-atanh number)))) -;;; HP-UX does not supply a C version of log1p, so -;;; use the definition. - +;;; HP-UX does not supply a C version of log1p, so use the definition. +;;; +;;; FIXME: This is really not a good definition. As per Raymond Toy +;;; working on CMU CL, "The definition really loses big-time in +;;; roundoff as x gets small." #!+hpux #!-sb-fluid (declaim (inline %log1p)) #!+hpux @@ -635,4 +542,441 @@ (declare (double-float number) (optimize (speed 3) (safety 0))) (the double-float (log (the (double-float 0d0) (+ number 1d0))))) - + +;;;; not-OLD-SPECFUN stuff +;;;; +;;;; (This was conditional on #-OLD-SPECFUN in the CMU CL sources, +;;;; but OLD-SPECFUN was mentioned nowhere else, so it seems to be +;;;; the standard special function system.) +;;;; +;;;; This is a set of routines that implement many elementary +;;;; transcendental functions as specified by ANSI Common Lisp. The +;;;; implementation is based on Kahan's paper. +;;;; +;;;; I believe I have accurately implemented the routines and are +;;;; correct, but you may want to check for your self. +;;;; +;;;; These functions are written for CMU Lisp and take advantage of +;;;; some of the features available there. It may be possible, +;;;; however, to port this to other Lisps. +;;;; +;;;; Some functions are significantly more accurate than the original +;;;; definitions in CMU Lisp. In fact, some functions in CMU Lisp +;;;; give the wrong answer like (acos #c(-2.0 0.0)), where the true +;;;; answer is pi + i*log(2-sqrt(3)). +;;;; +;;;; All of the implemented functions will take any number for an +;;;; input, but the result will always be a either a complex +;;;; single-float or a complex double-float. +;;;; +;;;; general functions: +;;;; complex-sqrt +;;;; complex-log +;;;; complex-atanh +;;;; complex-tanh +;;;; complex-acos +;;;; complex-acosh +;;;; complex-asin +;;;; complex-asinh +;;;; complex-atan +;;;; complex-tan +;;;; +;;;; utility functions: +;;;; scalb logb +;;;; +;;;; internal functions: +;;;; square coerce-to-complex-type cssqs complex-log-scaled +;;;; +;;;; references: +;;;; Kahan, W. "Branch Cuts for Complex Elementary Functions, or Much +;;;; Ado About Nothing's Sign Bit" in Iserles and Powell (eds.) "The +;;;; State of the Art in Numerical Analysis", pp. 165-211, Clarendon +;;;; Press, 1987 +;;;; +;;;; The original CMU CL code requested: +;;;; Please send any bug reports, comments, or improvements to +;;;; Raymond Toy at toy@rtp.ericsson.se. + +;;; FIXME: In SBCL, the floating point infinity constants like +;;; SB!EXT:DOUBLE-FLOAT-POSITIVE-INFINITY aren't available as +;;; constants at cross-compile time, because the cross-compilation +;;; host might not have support for floating point infinities. Thus, +;;; they're effectively implemented as special variable references, +;;; and the code below which uses them might be unnecessarily +;;; inefficient. Perhaps some sort of MAKE-LOAD-TIME-VALUE hackery +;;; should be used instead? + +(declaim (inline square)) +(defun square (x) + (declare (double-float x)) + (* x x)) + +;;; original CMU CL comment, apparently re. SCALB and LOGB and +;;; perhaps CSSQS: +;;; If you have these functions in libm, perhaps they should be used +;;; instead of these Lisp versions. These versions are probably good +;;; enough, especially since they are portable. + +;;; Compute 2^N * X without computing 2^N first. (Use properties of +;;; the underlying floating-point format.) +(declaim (inline scalb)) +(defun scalb (x n) + (declare (type double-float x) + (type double-float-exponent n)) + (scale-float x n)) + +;;; This is like LOGB, but X is not infinity and non-zero and not a +;;; NaN, so we can always return an integer. +(declaim (inline logb-finite)) +(defun logb-finite (x) + (declare (type double-float x)) + (multiple-value-bind (signif exponent sign) + (decode-float x) + (declare (ignore signif sign)) + ;; DECODE-FLOAT is almost right, except that the exponent is off + ;; by one. + (1- exponent))) + +;;; Compute an integer N such that 1 <= |2^N * x| < 2. +;;; For the special cases, the following values are used: +;;; x logb +;;; NaN NaN +;;; +/- infinity +infinity +;;; 0 -infinity +(defun logb (x) + (declare (type double-float x)) + (cond ((float-nan-p x) + x) + ((float-infinity-p x) + sb!ext:double-float-positive-infinity) + ((zerop x) + ;; The answer is negative infinity, but we are supposed to + ;; signal divide-by-zero, so do the actual division + (/ -1.0d0 x) + ) + (t + (logb-finite x)))) + +;;; This function is used to create a complex number of the +;;; appropriate type: +;;; Create complex number with real part X and imaginary part Y +;;; such that has the same type as Z. If Z has type (complex +;;; rational), the X and Y are coerced to single-float. +#!+long-float (eval-when (:compile-toplevel :load-toplevel :execute) + (error "needs work for long float support")) +(declaim (inline coerce-to-complex-type)) +(defun coerce-to-complex-type (x y z) + (declare (double-float x y) + (number z)) + (if (subtypep (type-of (realpart z)) 'double-float) + (complex x y) + ;; Convert anything that's not a DOUBLE-FLOAT to a SINGLE-FLOAT. + (complex (float x 1f0) + (float y 1f0)))) + +;;; Compute |(x+i*y)/2^k|^2 scaled to avoid over/underflow. The +;;; result is r + i*k, where k is an integer. +#!+long-float (eval-when (:compile-toplevel :load-toplevel :execute) + (error "needs work for long float support")) +(defun cssqs (z) + (let ((x (float (realpart z) 1d0)) + (y (float (imagpart z) 1d0))) + ;; Would this be better handled using an exception handler to + ;; catch the overflow or underflow signal? For now, we turn all + ;; traps off and look at the accrued exceptions to see if any + ;; signal would have been raised. + (with-float-traps-masked (:underflow :overflow) + (let ((rho (+ (square x) (square y)))) + (declare (optimize (speed 3) (space 0))) + (cond ((and (or (float-nan-p rho) + (float-infinity-p rho)) + (or (float-infinity-p (abs x)) + (float-infinity-p (abs y)))) + (values sb!ext:double-float-positive-infinity 0)) + ((let ((threshold #.(/ least-positive-double-float + double-float-epsilon)) + (traps (ldb sb!vm::float-sticky-bits + (sb!vm:floating-point-modes)))) + ;; Overflow raised or (underflow raised and rho < + ;; lambda/eps) + (or (not (zerop (logand sb!vm:float-overflow-trap-bit traps))) + (and (not (zerop (logand sb!vm:float-underflow-trap-bit + traps))) + (< rho threshold)))) + ;; If we're here, neither x nor y are infinity and at + ;; least one is non-zero.. Thus logb returns a nice + ;; integer. + (let ((k (- (logb-finite (max (abs x) (abs y)))))) + (values (+ (square (scalb x k)) + (square (scalb y k))) + (- k)))) + (t + (values rho 0))))))) + +;;; principal square root of Z +;;; +;;; Z may be any NUMBER, but the result is always a COMPLEX. +(defun complex-sqrt (z) + (declare (number z)) + (multiple-value-bind (rho k) + (cssqs z) + (declare (type (or (member 0d0) (double-float 0d0)) rho) + (type fixnum k)) + (let ((x (float (realpart z) 1.0d0)) + (y (float (imagpart z) 1.0d0)) + (eta 0d0) + (nu 0d0)) + (declare (double-float x y eta nu)) + + (locally + ;; space 0 to get maybe-inline functions inlined. + (declare (optimize (speed 3) (space 0))) + + (if (not (float-nan-p x)) + (setf rho (+ (scalb (abs x) (- k)) (sqrt rho)))) + + (cond ((oddp k) + (setf k (ash k -1))) + (t + (setf k (1- (ash k -1))) + (setf rho (+ rho rho)))) + + (setf rho (scalb (sqrt rho) k)) + + (setf eta rho) + (setf nu y) + + (when (/= rho 0d0) + (when (not (float-infinity-p (abs nu))) + (setf nu (/ (/ nu rho) 2d0))) + (when (< x 0d0) + (setf eta (abs nu)) + (setf nu (float-sign y rho)))) + (coerce-to-complex-type eta nu z))))) + +;;; Compute log(2^j*z). +;;; +;;; This is for use with J /= 0 only when |z| is huge. +(defun complex-log-scaled (z j) + (declare (number z) + (fixnum j)) + ;; The constants t0, t1, t2 should be evaluated to machine + ;; precision. In addition, Kahan says the accuracy of log1p + ;; influences the choices of these constants but doesn't say how to + ;; choose them. We'll just assume his choices matches our + ;; implementation of log1p. + (let ((t0 #.(/ 1 (sqrt 2.0d0))) + (t1 1.2d0) + (t2 3d0) + (ln2 #.(log 2d0)) + (x (float (realpart z) 1.0d0)) + (y (float (imagpart z) 1.0d0))) + (multiple-value-bind (rho k) + (cssqs z) + (declare (optimize (speed 3))) + (let ((beta (max (abs x) (abs y))) + (theta (min (abs x) (abs y)))) + (coerce-to-complex-type (if (and (zerop k) + (< t0 beta) + (or (<= beta t1) + (< rho t2))) + (/ (%log1p (+ (* (- beta 1.0d0) + (+ beta 1.0d0)) + (* theta theta))) + 2d0) + (+ (/ (log rho) 2d0) + (* (+ k j) ln2))) + (atan y x) + z))))) + +;;; log of Z = log |Z| + i * arg Z +;;; +;;; Z may be any number, but the result is always a complex. +(defun complex-log (z) + (declare (number z)) + (complex-log-scaled z 0)) + +;;; KLUDGE: Let us note the following "strange" behavior. atanh 1.0d0 +;;; is +infinity, but the following code returns approx 176 + i*pi/4. +;;; The reason for the imaginary part is caused by the fact that arg +;;; i*y is never 0 since we have positive and negative zeroes. -- rtoy +;;; Compute atanh z = (log(1+z) - log(1-z))/2. +(defun complex-atanh (z) + (declare (number z)) + (let* (;; constants + (theta (/ (sqrt most-positive-double-float) 4.0d0)) + (rho (/ 4.0d0 (sqrt most-positive-double-float))) + (half-pi (/ pi 2.0d0)) + (rp (float (realpart z) 1.0d0)) + (beta (float-sign rp 1.0d0)) + (x (* beta rp)) + (y (* beta (- (float (imagpart z) 1.0d0)))) + (eta 0.0d0) + (nu 0.0d0)) + ;; Shouldn't need this declare. + (declare (double-float x y)) + (locally + (declare (optimize (speed 3))) + (cond ((or (> x theta) + (> (abs y) theta)) + ;; To avoid overflow... + (setf eta (float-sign y half-pi)) + ;; nu is real part of 1/(x + iy). This is x/(x^2+y^2), + ;; which can cause overflow. Arrange this computation so + ;; that it won't overflow. + (setf nu (let* ((x-bigger (> x (abs y))) + (r (if x-bigger (/ y x) (/ x y))) + (d (+ 1.0d0 (* r r)))) + (if x-bigger + (/ (/ x) d) + (/ (/ r y) d))))) + ((= x 1.0d0) + ;; Should this be changed so that if y is zero, eta is set + ;; to +infinity instead of approx 176? In any case + ;; tanh(176) is 1.0d0 within working precision. + (let ((t1 (+ 4d0 (square y))) + (t2 (+ (abs y) rho))) + (setf eta (log (/ (sqrt (sqrt t1))) + (sqrt t2))) + (setf nu (* 0.5d0 + (float-sign y + (+ half-pi (atan (* 0.5d0 t2)))))))) + (t + (let ((t1 (+ (abs y) rho))) + ;; Normal case using log1p(x) = log(1 + x) + (setf eta (* 0.25d0 + (%log1p (/ (* 4.0d0 x) + (+ (square (- 1.0d0 x)) + (square t1)))))) + (setf nu (* 0.5d0 + (atan (* 2.0d0 y) + (- (* (- 1.0d0 x) + (+ 1.0d0 x)) + (square t1)))))))) + (coerce-to-complex-type (* beta eta) + (- (* beta nu)) + z)))) + +;;; Compute tanh z = sinh z / cosh z. +(defun complex-tanh (z) + (declare (number z)) + (let ((x (float (realpart z) 1.0d0)) + (y (float (imagpart z) 1.0d0))) + (locally + ;; space 0 to get maybe-inline functions inlined + (declare (optimize (speed 3) (space 0))) + (cond ((> (abs x) + #-(or linux hpux) #.(/ (asinh most-positive-double-float) 4d0) + ;; This is more accurate under linux. + #+(or linux hpux) #.(/ (+ (log 2.0d0) + (log most-positive-double-float)) + 4d0)) + (coerce-to-complex-type (float-sign x) + (float-sign y) z)) + (t + (let* ((tv (%tan y)) + (beta (+ 1.0d0 (* tv tv))) + (s (sinh x)) + (rho (sqrt (+ 1.0d0 (* s s))))) + (if (float-infinity-p (abs tv)) + (coerce-to-complex-type (/ rho s) + (/ tv) + z) + (let ((den (+ 1.0d0 (* beta s s)))) + (coerce-to-complex-type (/ (* beta rho s) + den) + (/ tv den) + z))))))))) + +;;; Compute acos z = pi/2 - asin z. +;;; +;;; Z may be any NUMBER, but the result is always a COMPLEX. +(defun complex-acos (z) + ;; Kahan says we should only compute the parts needed. Thus, the + ;; REALPART's below should only compute the real part, not the whole + ;; complex expression. Doing this can be important because we may get + ;; spurious signals that occur in the part that we are not using. + ;; + ;; However, we take a pragmatic approach and just use the whole + ;; expression. + ;; + ;; NOTE: The formula given by Kahan is somewhat ambiguous in whether + ;; it's the conjugate of the square root or the square root of the + ;; conjugate. This needs to be checked. + ;; + ;; I checked. It doesn't matter because (conjugate (sqrt z)) is the + ;; same as (sqrt (conjugate z)) for all z. This follows because + ;; + ;; (conjugate (sqrt z)) = exp(0.5*log |z|)*exp(-0.5*j*arg z). + ;; + ;; (sqrt (conjugate z)) = exp(0.5*log|z|)*exp(0.5*j*arg conj z) + ;; + ;; and these two expressions are equal if and only if arg conj z = + ;; -arg z, which is clearly true for all z. + (declare (number z)) + (let ((sqrt-1+z (complex-sqrt (+ 1 z))) + (sqrt-1-z (complex-sqrt (- 1 z)))) + (with-float-traps-masked (:divide-by-zero) + (complex (* 2 (atan (/ (realpart sqrt-1-z) + (realpart sqrt-1+z)))) + (asinh (imagpart (* (conjugate sqrt-1+z) + sqrt-1-z))))))) + +;;; Compute acosh z = 2 * log(sqrt((z+1)/2) + sqrt((z-1)/2)) +;;; +;;; Z may be any NUMBER, but the result is always a COMPLEX. +(defun complex-acosh (z) + (declare (number z)) + (let ((sqrt-z-1 (complex-sqrt (- z 1))) + (sqrt-z+1 (complex-sqrt (+ z 1)))) + (with-float-traps-masked (:divide-by-zero) + (complex (asinh (realpart (* (conjugate sqrt-z-1) + sqrt-z+1))) + (* 2 (atan (/ (imagpart sqrt-z-1) + (realpart sqrt-z+1)))))))) + +;;; Compute asin z = asinh(i*z)/i. +;;; +;;; Z may be any NUMBER, but the result is always a COMPLEX. +(defun complex-asin (z) + (declare (number z)) + (let ((sqrt-1-z (complex-sqrt (- 1 z))) + (sqrt-1+z (complex-sqrt (+ 1 z)))) + (with-float-traps-masked (:divide-by-zero) + (complex (atan (/ (realpart z) + (realpart (* sqrt-1-z sqrt-1+z)))) + (asinh (imagpart (* (conjugate sqrt-1-z) + sqrt-1+z))))))) + +;;; Compute asinh z = log(z + sqrt(1 + z*z)). +;;; +;;; Z may be any number, but the result is always a complex. +(defun complex-asinh (z) + (declare (number z)) + ;; asinh z = -i * asin (i*z) + (let* ((iz (complex (- (imagpart z)) (realpart z))) + (result (complex-asin iz))) + (complex (imagpart result) + (- (realpart result))))) + +;;; Compute atan z = atanh (i*z) / i. +;;; +;;; Z may be any number, but the result is always a complex. +(defun complex-atan (z) + (declare (number z)) + ;; atan z = -i * atanh (i*z) + (let* ((iz (complex (- (imagpart z)) (realpart z))) + (result (complex-atanh iz))) + (complex (imagpart result) + (- (realpart result))))) + +;;; Compute tan z = -i * tanh(i * z) +;;; +;;; Z may be any number, but the result is always a complex. +(defun complex-tan (z) + (declare (number z)) + ;; tan z = -i * tanh(i*z) + (let* ((iz (complex (- (imagpart z)) (realpart z))) + (result (complex-tanh iz))) + (complex (imagpart result) + (- (realpart result)))))