; .EnTete "Le-Lisp (c) version 15.2" " " "Les Complexes"
; .EnPied " " "%" " "
; .Chapitre IV "Les Nombres Complexes"
;
; .Centre "*****************************************************************"
; .Centre " Ce fichier est en lecture seule hors du projet ALE de l'INRIA.  "
; .Centre " Il est maintenu par ILOG SA, 2 Avenue Gallie'ni, 94250 Gentilly "
; .Centre " (c) Le-Lisp est une marque de'pose'e de l'INRIA                 "
; .Centre "*****************************************************************"

; .Centre "$Header: complex.ll,v 4.3 88/02/16 10:22:39 kuczynsk Rel213 $"
;==========================================================================
;
;      Le←Lisp v15.2  :  les complexes C.
;
;===========================================================================
;  (c)  Jean Vuillemin, 1986
;       Institut National de Recherche en Informatique et Automatique
;       B.P. 105, 78153 Le Chesnay Cedex, France. vuillemin@inria
;===========================================================================
(unless (>= (version) 15.2)
	(error 'load 'erricf 'ratio))

(setq #:sys-package:colon 'R)

(add-feature 'complex)

(unless (featurep 'genr) (loadmodule 'genr))

(setq #:backup:majuscules #:system:read-case-flag
      #:system:read-case-flag ())
; Un complexe est repre'sente' c par sa partie re'elle (:r c) et sa partie
; imaginaire (:i c).
(defstruct :C r i)

; Teste le type C:
(dmd :C:? (c) `(eq ':C (type-of ,c)))

; Construit un complexe [r:i]. Suppose i<>0 :
(dmd :C:x (r i) `(let ((:c (:C:make))) (:C:r :c ,r) (:C:i :c ,i) :c))

(setq #:sys-package:colon '#:R:C)

; Construit a+ib = [a:b] 
(de makecomplex (a b) (if (= 0 b) a (:x a b)))

; L'imaginaire pur i*i=-1 :
(defvar i (makecomplex 0 1))

; Teste si un nombre est un complexe pur:
(de complexp (c) (and (:? c) c))

; Teste si un objet Lisp est un re'el.
(de realp (r)                    ;  Les re'els sont forme's de
    (or (rationalp r)            ;  Q,
        (floatp r)               ;  des flottants,
        (and (numberp r)         ;  et de tous les nombres qui
             (send 'realp r))))  ;  re'pondent au message realp.

; Les complexes ne sont pas des re'els:
(de :realp (c) ())
; .Section "Fonctions propres aux complexes"
; Acce's aux parties re'elles et imaginaires :
(de realpart (c)
    (cond ((:? c) (:r c))
          ((numberp c) c)
          (T (#:R:error 'realpart 'ERRNNA (list 'realpart c)))))

(de imagpart (c)
    (cond ((:? c) (:i c))
          ((numberp c) 0)
          (T (#:R:error 'imagpart 'ERRNNA (list 'imagpart c)))))

; Le conjugue' de a+ib est a-ib
(de conjugate (c)
    (if (numberp c)
        (if (:? c) (:x (:r c) (- (:i c))) c)
        (#:R:error 'conjugate 'ERRNNA
               (list 'conjugate c))))

; Definition du module au carre:
(de :module2 (c)  (+ (* (:r c) (:r c)) (* (:i c) (:i c))))
; .Section "Arithme'tique Complexe"
(de :float (c) (:x (float (:r c)) (float (:i c))))

(de :truncate (c) (#:R:error 'truncate 'RINC (list 'fix c)))

; La comparaison est rarement bien de'finie sur les complexes:
; On renvoie le complexe forme' du re'sultat
; des comparaisons sur les 2 champs.
(de :<?> (a b)
    (if (:? b)
        (makecomplex (<?> (:r a) (:r b)) (<?> (:i a) (:i b)))
        (:x (<?> (:r a) b) (<?> (:i a) 0))))

; Addition binaire :
(de :+ (c r)
    (if (:? r)
        (makecomplex (+ (:r c) (:r r)) (+ (:i c) (:i r)))
        (:x (+ r (:r c)) (:i c))))

; Negation Unaire:
(de :0- (c) (:x (- (:r c)) (- (:i c))))

; Multiplication binaire :
(de :* (c r)
    (if (:? r)
        (makecomplex (- (* (:r c) (:r r)) (* (:i c) (:i r)))
            (+ (* (:r c) (:i r)) (* (:i c) (:r r))))
        (makecomplex (* r (:r c)) (* r (:i c)))))

; Inverse: 1/a+ib = a/m-ib/m  avec m=a*a+b*b
(de :1/ (c) 
    (let ((m (:module2 c)))
         (:x (/ (:r c) m) (- (/ (:i c) m)))))

; Par convention, a+ib s'e'crit [bi+a]
(de :prin (c)
    (cond ((eq (precision) 'cl)
           (princn #/#) (princn #/C) (princn #/()
           (prin (:r c)) (princn #\SP) (prin (:i c)) (princn #/)))
          (T (princn #/[)
             (unless (or (= 1 (:i c)) (= 1. (:i c)))
                     (if (or (= -1 (:i i)) (= -1. (:i c)))
                         (princn #/-)
                         (prin (:i c))))
             (princn #/i)
             (unless (= 0 (:r c)) 
                     (cond ((> (:r c) 0)
                            (princn #/+) (prin (:r c)))
                           (T (princn #/-) (prin (- (:r c))))))
             (princn #/])))
    c)

; Pour pouvoir relire les complexes:
(dmc  |[| ()
      (let ((r) (j))   
           (setq j (with ((typecn #/i 'csep))
                         (selectq (peekcn)
                          (#/i 1)
                          (#/- (readcn)
                               (if (eq (peekcn) #/i) -1 (- (read))))
                          (T (read))))
                 r (readcn))
        (selectq r
         (#/i (selectq (readcn)
                (#/] (setq r 0))
                (#/+ (setq r (read))
                     (if (eq (peekcn) #/]) (readcn)
                         (#:R:error 'read 'RNOTC r)))
                (#/- (setq r (- (read)))
                     (if (eq (peekcn) #/]) (readcn)
                         (#:R:error 'read 'RNOTC r)))
                (T (#:R:error 'read 'RNOTC r))))
         (#/] (setq r j j 0))
         (T  (#:R:error 'read 'RNOTC r)))
        (makecomplex r j)))

(defsharp |C| ()
   (setq n (read))
   (list (:x (car n) (cadr n))))
; .Section "Conversion en coordonne'es polaires"
; De'finition de pi : soit on prend la valeur exacte si on dispose des re'els,
; soit on approxime en flottant avec une pre'cision de'pendante de la machine:
(defvar pi (if (boundp 'pi) pi (* 4 (atan 1.))))

(defvar pi/2 (/ pi 2))

; Pour un complexe c=rho*e**i*theta, le module rho est donne' par (:abs c),
; la phase theta par (:phase c) et e**i*theta par (:signum c).

(de :abs (c) (if (zerop (:r c)) (abs (:i c)) (sqrt (:module2 c))))

; La phase est 0 pour un re'el, et atan(r/j) pour c=[ji+r]
(de phase (c)
    (cond ((:? c) (:arctan (:i c) (:r c)))
          ((numberp c)  (if (< c 0) pi 0))
          (T (#:R:error 'phase 'ERRNNA
                    (list 'phase c)))))

; i*r = r*e**i*pi/2   rc+ic=rho*e**i*theta, tan(theta)=ic/rc
(de :arctan (y x)
    (selectq (<?> y 0)
     (1 (selectq (<?> x 0)
         (1 (atan (/ y x)))
         (0 pi/2)
         (-1 (+ pi (atan (/ y x))))))
     (0 (if (< x 0) pi 0))
     (-1 (- (:arctan (- y) x)))))

; Pour un re'el, (signum r) donne le signe -1,0,1 de r.
; Pour un complexe c=rho*e**i*theta, c'est e**i*theta, soit c/(abs c).
(de signum (c)
    (cond ((:? c) (:* c (/ (:abs c))))
          ((numberp c) (<?> c 0))
          (T (#:R:error 'signum 'ERRNNA
                    (list 'signum c)))))
; .Section "Logarithme et Exponentielle sur C"
; log(r*e**i*theta) = log(r)+i*theta
(de :log (c) (:x (log (:abs c)) (:arctan (:i c) (:r c))))

; e**rc+ic = e**rc*(cosic+i*sinic)
(de :exp (c) (:*  (:cis (:i c)) (exp (:r c))))

(de :cis (theta) (:x (cos theta) (sin theta)))

; (cis r) = cos(r)+i*sin(r)
(de cis (r)
    (if (numberp r) (:cis r) 
        (#:R:error 'cis 'ERRNNA (list 'cis r))))

; (sqrt c) = c**1/2 = e**(log(c)/2)
(de :sqrt (c) (:exp (/ (:log c) 2)))
; .Section "Sinus, Cosinus et Arc Tangente sur C"
; sin(c)=1/2i(e**i*c-e**-i*c)
(de :sin (c)
    (* (- (exp (* i c)) (exp (* [-i]  c))) [-1/2i]))

; cos(c)=1/2(e**i*c+e**-i*c)
(de :cos (c)
    (/ (+ (exp (* i c)) (exp (* [-i] c))) 2))

; atan(z)=-ilog((1+iz)(sqrt1/(1+z*z)))
(de :atan (z)
    (if (= i z) (/ 0)
        (:* [-i] (log (/ (+ 1 (* i z)) (sqrt (+ 1 (* z z))))))))

; tan(z)  = sin(z)/co

(de tan (z) (/ (sin z) (cos z)))
; .Section "Extention sur C des Fonctions Transcendantes sur R"
(setq #:sys-package:colon '#:R)

(de :log (r)
    (selectq (<?> r 0)
      (1 (log (float r)))
      (0 (- (/ 0)))
      (-1 (:C:x (log (- r)) pi))))


(de #:fix:log (r) (:log r))

(de #:float:log (r) (:log r))

(de :sqrt (r)
    (selectq (<?> r 0)
      (1 (sqrt (float r)))
      (0 0)
      (-1 (:C:x 0 (sqrt (- r))))))

(de #:fix:sqrt (r) (:sqrt r))

(de #:float:sqrt (r) (:sqrt r))

; asin(z)=-ilog(iz+sqrt(1-z*z))
(de :asin (z)
    (* [-i] (log (+ (* z i) (sqrt (- 1 (* z z)))))))

(de #:fix:asin (r) (:asin r))

(de #:float:asin (r) (:asin r))

; acos(z)=pi/2-asinz
(de :acos (z) (- pi/2 (asin z)))

(de #:fix:acos (r) (:acos r))

(de #:float:acos (r) (:acos r))
; .SSection "Fonctions Hyperboliques"
; sinhx=(e**x-e**-x)/2   
(de sinh (x)
    (if (numberp x)
        (/ (- (exp x) (exp (- x))) 2)
        (#:R:error ':#:R:error 'ERRNNA
                  (list 'sinh x))))

; coshx=(e**x+e**-x)/2   
(de cosh (x)
    (if (numberp x)
        (/ (+ (exp x) (exp (- x))) 2)
        (#:R:error ':#:R:error 'ERRNNA
                  (list 'cosh x))))

; tanhx=sinhx/coshx
(de tanh (x)
    (if (numberp x)
        (/ (sinh x) (cosh x))
        (#:R:error ':#:R:error 'ERRNNA
                  (list 'tanh x))))

; asinhx=log(x+sqrt(1+x*x))
(de asinh (x)
    (if (numberp x)
        (log (+ x (sqrt (+ 1 (* x x)))))
        (#:R:error ':#:R:error 'ERRNNA
                  (list 'asinh x))))

; acoshx=log(x+(x+1)*sqrt(x-1/x+1))
(de acosh (x)
    (if (numberp x)
        (log (+ x (* (+ x 1) (sqrt (/ (- x 1) (+ x 1))))))
        (#:R:error ':#:R:error 'ERRNNA
                  (list 'acosh x))))

; atanh(x) = 1/2*log((1+x/1-x))
(de atanh (x)
    (cond
        ((not (numberp x))
         (#:R:error ':#:R:error 'ERRNNA (list 'atanh x)))
        ((= 1 x) (/ 0))
        ((= -1 x) (- (/ 0)))
        (t (/ (log (/ (+ x 1) (- 1 x))) 2))))

; .Section "Modifs a` genr.ll"
(de :+ (n m)
    (if (complexp m) (:C:+ m n) (plus (float n) (float m))))

(de :* (n m)
    (if (complexp m) (:C:* m n) (times (float n) (float m))))

(de :<?> (n m)
    (if (complexp m) (- (:C:<?> m n)) (<?> (float n) (float m))))

(setq #:system:read-case-flag  #:backup:majuscules)