(context 'Q)

;; rational library functions written by Eddie Rucker September 24, 2004
;;
;; Updated on November 30, 2004. The relations were not working properly
;; major code cleanup
;; Updated on December 7th, 2006. Now uses built-in 'gcd' introduced in 9.0
;;
;; This library has no warranty of any kind
;;
;; include the library by adding (load "rational.lsp") before use
;;
;; rational numbers can have the form
;; a        :: integer
;; '(a)     :: integer
;; '(a b)   :: a = numerator, b = denominator
;; '(a b c) :: mixed number a = whole part, b = numerator, c = denominator
;; 
;; rational numbers must NOT be quoted
;; example
;; (Q:+ 1 '(-1 2) '(1 1 2)) => 2
;; (Q:+ '(1 2) '(1 3)) => (5 6)
;;
;; operators
;; Q:+  :: add rationals - multiple arguments
;; Q:-  :: subtract rationals - multiple arguments
;; Q:*  :: multiply rationals - multiple arguments
;; Q:/  :: divide rationals - multiple arguments
;; Q:neg :: take the opposite of a rational - single argument
;; Q:abs :: take the absolute value of a rational - single argument
;; Q:recip :: take the reciprical of a rational - single argument
;; Q:min :: return the smallest rational value - multiple arguments
;; Q:max :: return the largest rational value - multiple arguments
;;
;; relations
;; Q:=  :: compare rationals for equality (multiple arguments)
;; Q:!=  :: not equals with multiple arguments
;; Q:<  :: less than with multiple arguments
;; Q:>  :: greater than with multiple arguments
;; Q:<=  :: less than or equal to with multiple arguments
;; Q:>=  :: greater than or equal to with multiple arguments

(define (frac-form a b)
  ;; fix negatives so that -a/-b => a/b, a/-b => -a/b
  (if 
    (= a 0)  '(0 1)
    (= b 0)  (throw "rational-number-error")
    (and (< a 0) (< b 0))  (list (MAIN:abs a) (MAIN:abs b))
    (and (>= a 0) (< b 0))  (list (MAIN:abs a) (MAIN:abs b))
    (list a b)))

(define (improper A)
  ;; convert a -> a/1 and  a b/c -> (c*a+b)/c
  (apply frac-form
	 (if (integer? A)
	     (list A 1)
	   (case (length A)
	     (1 (list (first A) 1))
	     (2 A)
	     (3 (list (+ (nth 1 A) (* (first A) (last A))) (last A)))
	     (throw "rational-number-error")))))

(define (reduce-frac A)
  (letn (a (first A) b (last A) dd (gcd (MAIN:abs a) b))
    (if (= dd b)
	(/ a dd)
      (list (/ a dd) (/ b dd)))))

(define (neg_ A)
  (list (- 0 (first A)) (last A)))

(define (add_ A B)
  (let (n0 (first A) d0 (last A) n1 (first B) d1 (last B))
    (list (+ (* n0 d1) (* n1 d0)) (* d0 d1))))

(define (sub_ A B)
  (add_ A (neg_ B)))

(define (mul_ A B)
  (let (n0 (first A) d0 (last A) n1 (first B) d1 (last B))
    (list (* n0 n1) (* d0 d1))))

(define (recip_ A)
  (list (last A) (first A)))

(define (div_ A B)
  (mul_ A (recip_ B)))

(define (min_ A B)
  (if (< (* (first A) (last B)) (* (first B) (last A)))  A  B))

(define (max_ A B)
  (if (> (* (first A) (last B)) (* (first B) (last A)))  A  B))

(define (bop sm vals)
  ;; binary operator
  (reduce-frac (apply sm (map improper vals) 2)))

(define (uop sm val)
  ;; unary operator
  (reduce-frac (sm (improper val))))

(define (rel_ sm a b L tf)
  (if 
    (= tf nil) nil
    (= L '()) tf
    (let (n (first (first L)) d (last (first L)))
      (rel_ sm n d (rest L) (sm (* a d) (* b n))))))

(define (rel sm vals)
  (let (L (map improper vals))
    (rel_ sm (first (first L)) (last (first L)) (rest L) true)))

;; functions for use in  Q
(constant 'Q:+ (lambda-macro () (bop add_ (map eval (args)))))
(constant 'Q:- (lambda-macro () (bop sub_ (map eval (args)))))
(constant 'Q:* (lambda-macro () (bop mul_ (map eval (args)))))
(constant 'Q:/ (lambda-macro () (bop div_ (map eval (args)))))
(constant 'Q:min (lambda-macro () (bop min_ (map eval (args)))))
(constant 'Q:max (lambda-macro () (bop max_ (map eval (args)))))

(constant 'Q:= (lambda-macro () (rel MAIN:= (map eval (args)))))
(constant 'Q:!= (lambda-macro () (not (rel MAIN:= (map eval (args))))))
(constant 'Q:< (lambda-macro () (rel MAIN:< (map eval (args)))))
(constant 'Q:> (lambda-macro () (rel MAIN:> (map eval (args)))))
(constant 'Q:<= (lambda-macro () (rel MAIN:<= (map eval (args)))))
(constant 'Q:>= (lambda-macro () (rel MAIN:>= (map eval (args)))))

(constant 'Q:abs (fn (A) (list (MAIN:abs (first A)) (MAIN:abs (last A)))))
(constant 'Q:neg (fn (A) (uop neg_ A)))
(constant 'Q:recip (fn (A) (uop recip_ A)))

(context 'MAIN)


syntax highlighting with newLISP and syntax.cgi