(ns e2-7-8-9-10-11
  (:use [clojure.contrib.test-is :only (deftest is run-tests)])
  (:use [util.util :only (avg)])
  (:use [util.interval :only (make-interval lower-bound upper-bound mul-interval add-interval div-interval)])
  (:use [clojure.contrib.generic.math-functions :only ()]))
(defn sub-interval [a b]
  (let [l (- (lower-bound a) (lower-bound b))
        u (- (upper-bound a) (upper-bound b))]
    (make-interval (min l u) (max l u))))
(deftest sub-interval-test
  ; 1. ----|---\----|---\-------
  ;        a0  a1  b0   b1
  (is (= (sub-interval (make-interval 1 3) (make-interval 2 5)) (make-interval -2 -1)))
  ; 2. ---\--------\-|--------|-
  ;      a1       b1 a0       b0
  (is (= (sub-interval (make-interval 4 6) (make-interval 1 3)) (make-interval 3 3)))
  ; 3. ---|--------|-\--------\-
  ;      a0       b0 a1       b1
  (is (= (sub-interval (make-interval 1 3) (make-interval 4 6)) (make-interval -3 -3)))
  ; 4. ---\----|---\----|-------
  ;      a1   a0   b1   b0
  (is (= (sub-interval (make-interval 2 5) (make-interval 1 3)) (make-interval 1 2))))
(defn radius-interval [z]
  (avg (upper-bound z) (lower-bound z)))

Takes an operation on intervals, an op on numbers and two intervals. Returns true if the result is proportional to the input values, false otherwise.

(defn proportional?
  [fint f a b]
  (let [result (fint a b)]
    (= (radius-interval result) (f (radius-interval a) (radius-interval b)))))
(deftest addition-subtraction-test
  (is (proportional? sub-interval - (make-interval 1 6) (make-interval 3 15)))
  (is (proportional? sub-interval - (make-interval -1 6) (make-interval -6 -5)))
  (is (proportional? add-interval + (make-interval 1 6) (make-interval 3 15)))
  (is (proportional? add-interval + (make-interval -1 6) (make-interval -6 -5)))
  (is (not (proportional? mul-interval * (make-interval 1 6) (make-interval 3 15))))
  (is (not (proportional? mul-interval * (make-interval -1 6) (make-interval -6 -5))))
  (is (not (proportional? div-interval / (make-interval 1 6) (make-interval 3 15))))
  (is (not (proportional? div-interval / (make-interval -1 6) (make-interval -6 -5)))))
(defn contains-interval [a n]
  (if (or (> (lower-bound a) n) (< (upper-bound a) n))
      false
      true))
(defn div-interval-mod [a b]
  (if (contains-interval b 0) (println "Cannot divide by interval containing a zero!" b)
  (mul-interval a
    (make-interval (/ 1 (upper-bound b))
                   (/ 1 (lower-bound b))))))
(defn mul-interval-2 [a b]
  (let [la (lower-bound a)
        ua (upper-bound a)
        lb (lower-bound b)
        ub (upper-bound b)]
          ; 1.
          ; --*--|---\----|---\-------
          ;   0  la  lb  ua   ub
    (cond (and (pos? la) (pos? lb))
          (make-interval (* la lb) (* ua ub))
          ; 2.
          ; -----|---\----|---\---*---
          ;      la  lb  ua   ub  0
          (and (neg? ua) (neg? ub))
          (make-interval (* ua ub) (* la lb))
          ; 3.
          ; -\--------\-*-|--------|-
          ;  lb      ub 0 la       ua
          (and (pos? la) (neg? ub))
          (make-interval (* ua lb) (* la ub))
          ; 4. opposite of 3
          ; -|--------|-*-\--------\-
          ;  la      ua 0 lb       ub
          (and (pos? lb) (neg? ua))
          (make-interval (* la ub) (* lb ua))
          ; 5.
          ; -\--------\--|----*---|---
          ;  lb      ub  la   0   ua
          (and (neg? la) (pos? ua) (neg? ub))
          (make-interval (* ua lb) (* la lb))
          ; 6. opposite of 5
          ; -|--------|--\----*---\---
          ;  la      ua  lb   0   ub
          (and (neg? lb) (pos? ub) (neg? ua))
          (make-interval (* ub la) (* la lb))
          ; 7.
          ; -\---*----\--|--------|---
          ;  lb  0   ub  la       ua
          (and (neg? lb) (pos? ub) (pos? la))
          (make-interval (* lb ua) (* ub ua))
          ; 8. opposite of 7
          ; -|---*----|--\--------\---
          ;  la  0   ua  lb       ub
          (and (neg? la) (pos? ua) (pos? lb))
          (make-interval (* la ub) (* ua ub))
          ; 9. the last one where two multiplications are required
          ; ----|--\--*--|---\---------
          ;     la lb 0 ua   ub
          :else
          (make-interval (min (* la ub) (* lb ua)) (max (* la lb) (* ua ub))))))