; Le pre'sent fichier ne marche qu'en 15.2, 15.21
(unless (>= (version) 15.2)
	(syserror 'load "fichier non compatible" 'q))

; Arithmetique Entie`re: fichier q.ll
(add-feature 'q)

; Suppose que l'on connaisse llm5.ll:
(unless (featurep 'z) (libload "bnz.ll"))

; Pour se pre'munir contre les fanatiques du CASE de'pendant:
(setq #:backup:majuscules #:system:read-case-flag
      #:system:read-case-flag ())

(dmd package (l) `(setq #:sys-package:colon ,l))

; .Section "Structure Interne de N,Z et Q"
; Nous e'tendons les fix et float LeLisp, par des objets 
; auto-type's. Le tout forme l'ensemble R des nouveaux nombres, qui comprend :
; #:R:Q:N, les grands entiers positifs,
; #:R:Q:Z, les grands entiers ne'gatifs,
; #:R:Q:/, les rationnels par numerateur et de'nominateur.

; .Section "Cre'ation d'un rationnel"
(package '#:R:Q)
(defstruct #:R:Q:/ n d)

(de #:R:Q:/x (n d)
    (let ((q (#:R:Q:/:make)))
         (#:R:Q:/:n q n)
         (#:R:Q:/:d q d)
         q))

; Ve'rifie avant de construire si d=1:
(de Qx (n d) (if (eq d 1) n (:/x n d)))

(de :N:1/ (n) (:/x 1 n))

(de :Z:1/ (z) (:/x -1 (abs z)))

; Calcul du nume'rateur et du de'nominateur :
(de numerator (r)
    (cond ((eq ':/ (type-of r)) (:/:n r))
          ((numberp r) r)
          (T (#:R:error 'numerator 'ERRNNA (list 'numerator r)))))

(de denominator (r)
    (cond ((eq ':/ (type-of r)) (:/:d r))
          ((numberp r) 1)
          (T (#:R:error 'denominator 'ERRNNA (list 'denominator r)))))

; .Section "Tests de type"
; Les Q sont des rationnels:
(de :rationalp (q) q)

(package ':/); le package courant est #:R:Q:/

; Un Q peut e↑tre entier, si son de'nominateur vaut 1 apre`s re'duction :
(de :integerp (q)
    (let ((n (:n q)) (d (:d q)) (gcd))
         (setq gcd (pgcd n d))
         (if (eq 0 gcd)
	     ; q vaut 0 ou +/- 1
             (unless (eq 0 n) (:n q (quotient n (abs n))))
             (unless (eq 1 gcd)
		     ; on met a` jour la repre'sentation de q
                     (:n q (setq n (quotient n gcd)))
                     (:d q (setq d (quotient d gcd)))))
         (and (eq d 1) n)))

; .Section "Oppose' 0-"
; (0- x) vaut -x: le signe est celui du nume'rateur.
(de :0- (q) (Qx (0- (:n q)) (:d q)))

; .Section "Valeur absolue abs"
(de :abs (q)
    (if (< (:n q) 0) (Qx (abs (:n q)) (:d q)) q))

; .Section "Partie entie`re floor"
; q = n/d, n=a*d+r, floor=a, #:ex:mod=r/d.
(de :floor (q) 
    (let ((quo  (quomod (:n q) (:d q))))
         (setq  #:ex:mod (Qx #:ex:mod (:d q)))
         quo))

(de :truncate (q)
    (let ((quo (:floor q)))
         (when (< quo 0) (setq quo (+ 1 quo) #:ex:mod (- #:ex:mod 1)))
         quo))

; .Section "Convertion flottante float"
(de :float (q)
    (if (neq 0 (:d q))
        (fdiv (float (:n q)) (float (:d q)))
        (#:R:error 'float 'RDIV0 (list 'float q))))

; .Section "Inverse 1/"
; (1/ x) vaut 1/x:
(de :1/ (q)
    (if (< (:n q) 0)
        (Qx (0- (:d q)) (abs (:n q)))
        (Qx (:d q) (:n q))))

; .Section "La Comparaison <?>"
; (<?> x y) renvoie 1 ssi x>y, 0 ssi x=y et -1 ssi x<y:
(de :<?> (q y)
    (selectq (type-of y)
      (#:R:Q:/ (<?> (* (:n q) (:d y)) (* (:n y) (:d q))))
      (T (if (integerp y)
             (<?> (:n q) (* y (:d q)))
             (0- (<?> y q))))))

; .Section "Addition"
(de :+ (x y)
    (selectq (type-of y)
      (#:R:Q:/ (if (eq (:d x) (:d y))
              (Qx (+ (:n x) (:n y)) (:d x))
              (Qx (+ (* (:n x) (:d y)) (* (:d x) (:n y)))
                  (* (:d x) (:d y)))))
      (T (if (integerp y)
             (:Q+Z x y)
             (+ y x)))))

(de :Q+Z (q z) (Qx (+ (:n q) (* (:d q) z)) (:d q)))

; .Section "La multiplication"
; (* x y) vaut le produit x*y:

(de :* (x y)
    (selectq (type-of y)
      (#:R:Q:/ (Qx (* (:n x) (:n y))
                   (* (:d x) (:d y))))
      (T (if (integerp y)
             (:Q*Z x y)
             (* y x)))))

(de :Q*Z (q r) (Qx (* (:n q) r) (:d q)))

; .Section "Le print"
(de :prin (q)
    (cond ((= 0 (:d q)) (if (eq 0 (:n q)) (prin "0/0") (prin "1/0")))
          ((:integerp q) (prin (:n q)))
          ((let ((p (precision)) (n (:n q)) (d (:d q)))
                (selectq (type-of p)
                   (fix (ecrit-fc n d (if (= 0 p) 1 p) (abs p)))
                   (float (if (> (setq p (fix p)) 0) 
                              (ecrit-10 n d 10 p)
                              (ecrit-fc n d 0 (- p))))
                   (T (prin (:n q)) (princn #//) (prin (:d q))))))))

(de division- (n d)
    (if (< n d)
        (setq #:ex:mod n n 0)
        (setq #:ex:mod (- n d) n 1))
    n)

; Produit l'e'criture de p termes de l'e'criture en fraction continue
; normale du type (z) (1) (n) suivant le signe de z.
(de ecrit-fc (n d z p)
    (prin "/")
    (while (> p 0)
           (prin (selectq (<?> z 0) 
                    (1 (quomod n d))
                    (0 (division- n d))
                    (-1 (round n d))))
           (setq n d d #:ex:mod)
           (if (eq d 0)
               (setq p 0)
               (unless (= 0 z) (prin " "))
               (setq p (- p 1))))
    (unless (= 0 d)
            (when (= 0 z) (prin " "))
            (if (< 0 d 100)
                (if (> n 0) 
                    (ecrit-10 n d 10 1) 
                    (prin (:n q)) (princn #//) (prin (:d q)))))
    (prin "/"))

; Ecrire n/d en base b avec p chiffres apre`s la virgule:
(de ecrit-10 (n d b p)
    (when (< n 0) (prin "-") (setq n (- n)))
    (prin (quomod n d)) (setq n #:ex:mod gcd (pgcd d b))
    (unless (= 1 gcd) (prin "."))
    (while (> gcd 1)
           (setq d (quotient d gcd) p (-  p 1))
           (prin (quomod (* (quotient b gcd) n) d)) 
           (setq n #:ex:mod gcd (pgcd d b)))
    (unless (= 0 n)
     (prin "," (quotient (* b n) d)) (setq gcd n n #:ex:mod p (- p 1))
     (until (= gcd n)
            (prin (quomod (* b n) d)) (setq n #:ex:mod) 
            (when (< (decr p) 1) (prin ".") (setq n gcd)))))

; .Section "Ope'rations avec les fixp Lisp"
(package 'fix)

(de :1/ (n) (if (ge n 0) (Qx 1 n) (Qx -1 (0- n))))

(de :/ (n d) (* (1/ d) n))

(package '#:R:Q)
; .Section "La division"
; (quomod x y) renvoie le quotient entier q de x par y, et affecte
; le reste r dans #:ex:mod. Mathe'matiquement, q et r sont de'finis
; par x=q*y+r avec q entier relatif et r re'el, 0<=r<abs(y);  x est un
; #:R:Q:/.
(de :quomod (x y)
    (#:R:quomod x y))