;;; useful numeric procedures

(define int->real (lambda (x)
  (* 1.0 x) ))

(define real->int (lambda (x)
  (if (>= x 0)
      (floor x)
      (ceiling x) ) ))

(define abs (lambda (x)
  (if (number? x)
      (if (>= x 0)
          x
          (- x) )
      'error ) ))

(define square (lambda (x)
  (if (number? x)
      (* x x)
      'error ) ))

(define power (lambda (b n)
  (cond ((= n 0) 1)
        ((> n 0) (* b (power b (- n 1))))
        ( else   (/ 1.0 (power b (- n)))) ) )) 

(define total (lambda num-list
  (if (= (length num-list) 0)
      0
      (+ (car num-list)
         (apply total (cdr num-list))) ) ))           

(define average (lambda num-list
  (cond ((= (length num-list) 0)
           'error )
        ((= (length num-list) 1)
           (car num-list) )
        ( else
           (/ (apply total num-list)
              (int->real (length num-list))) ) ) ))

(define maximum (lambda num-list
  (if (= (length num-list) 1)
      (let ((a (car num-list)))
           (if (number? a)
               a
               'error ) )
      (let ((a (car num-list)) (b (cadr num-list)))
           (if (and (number? a) (number? b))
               (if (>= a b)
                   (apply maximum (cons a (cddr num-list)))
                   (apply maximum (cons b (cddr num-list))) )
               'error ) ) ) ))

(define max maximum)

(define minimum (lambda num-list
  (if (= (length num-list) 1)
      (let ((a (car num-list)))
           (if (number? a)
               a
               'error ) )
      (let ((a (car num-list)) (b (cadr num-list)))
           (if (and (number? a) (number? b))
               (if (<= a b)
                   (apply minimum (cons a (cddr num-list)))
                   (apply minimum (cons b (cddr num-list))) )
               'error ) ) ) ))

(define min minimum)

(define quotient (lambda (x y)
  (if (and (integer? x) (integer? y) (not (zero? y)))
      (/ x y)
      'error ) ))

(define remainder (lambda (x y)
  (if (and (integer? x) (integer? y) (not (zero? y)))
      (- x (* y (quotient x y)))
      'error ) ))

(define modulo (lambda (x y)
  (if (and (integer? x) (integer? y) (positive? y))
      (if (>= x 0)
          (remainder x y)
          (+ (remainder x y) y) )
      'error ) ))


;;; useful predicates

(define zero? (lambda (x)
  (if (number? x)
      (= x 0)
      'error ) ))

(define positive? (lambda (x)
  (if (number? x)
      (> x 0)
      'error ) ))

(define negative? (lambda (x)
  (if (number? x)
      (< x 0)
      'error ) ))

(define even? (lambda (x)
  (if (integer? x)
      (zero? (modulo x 2))
      'error ) ))

(define odd? (lambda (x)
  (if (integer? x)                    
      (not (zero? (modulo x 2)))
      'error ) )) 

(define prime? (lambda (x) 
  (define test (lambda (test-value)
    (if (> (square test-value) x)
        true
        (if (zero? (remainder x test-value))
            false
            (test (+ test-value 1)) ) ) ))
  (if (and (integer? x) (positive? x))
      (if (= x 1)
          'one-divides-everything
          (if (= x 2)
              true
              (if (even? x)
                  false
                  (test 3) ) ) )
      'error ) ))


;;; useful integer procedures

(define inc (lambda (n)
  (if (integer? n)
      (+ n 1)
      'error ) ))

(define dec (lambda (n)
  (if (integer? n)
      (- n 1)
      'error ) ))

(define gcd (lambda (a b)
  (if (and (integer? a) (integer? b)
           (>= a 0)     (>= b 0) )
      (if (zero? (modulo a b))
          b
          (gcd b (modulo a b)) )
      'error ) ))

(define lcm (lambda (a b)
  (if (and (integer? a) (integer? b)
           (>= a 0)     (>= b 0) )
      (/ (* a b) (gcd a b))
      'error ) ))

(define factorial (lambda (n)
  (if (integer? n)
      (if (>= n 0)
          (if (or (= n 0) (= n 1))
              1
              (* n (factorial (- n 1))) )
          'negative )
      'non-integer ) ))

(define fibonacci (lambda (n)
  (if (integer? n)
      (if (>= n 0)
          (if (or (= n 0) (= n 1))
              1
              (+ (fibonacci (- n 1)) (fibonacci (- n 2))) )
          'negative )
      'non-integer ) ))
