(ns challenge.discrete-value-range-test (:require [clojure.test :refer [deftest testing is]] [challenge.discrete-value-range :as range])) (deftype IntInclusiveDiscreteValueRange [^int start ^int end] range/DiscreteValueRange (abuts [_this other] (or (= 1 (abs (- start (.end other)))) (= 1 (abs (- end (.start other)))))) (value-before [__this] (dec start)) (value-after [_this] (inc end)) (start [_this] start) (end [_this] end) (range-type [_this] :int-range-inclusive) (union [this other] (when-not (range/abuts this other) (throw (ex-info "Cannot union non-abutting ranges" {}))) (range/->discrete-value-range (.range-type this) (min start (.start other)) (max end (.end other)))) Object (toString [_this] (str start ".." end)) (equals [_this other] (and (= start (.start other)) (= end (.end other))))) (defn int-range-inclusive [start end] (assert (<= start end)) (->IntInclusiveDiscreteValueRange start end)) (defmethod range/->discrete-value-range :int-range-inclusive [_ start end] (int-range-inclusive start end)) (deftest integer-ranges-sanity-test (testing "value equality" (is (= (int-range-inclusive 0 1) (int-range-inclusive 0 1)))) (testing "abuts" (is (= true (range/abuts (int-range-inclusive 0 1) (int-range-inclusive 2 3)))) (is (= true (range/abuts (int-range-inclusive 1 1) (int-range-inclusive 2 3)))) (is (= true (range/abuts (int-range-inclusive 4 7) (int-range-inclusive 2 3)))) (is (= false (range/abuts (int-range-inclusive 4 7) (int-range-inclusive 1 2))))) (testing "value-before" (is (= 0 (range/value-before (int-range-inclusive 1 8))))) (testing "value-after" (is (= 9 (range/value-after (int-range-inclusive 1 8))))) (testing "start" (is (= 1 (range/start (int-range-inclusive 1 8))))) (testing "end" (is (= 8 (range/end (int-range-inclusive 1 8)))))) ;; test against integer ranges for easy of expression and interpretation (deftest ordered-range-values (testing "ordered range values are sorted by value and then the range's start boundary before any end boundary" (is (= [{:value 1 :boundary-type :start :type :int-range-inclusive} {:value 2 :boundary-type :end :type :int-range-inclusive} {:value 4 :boundary-type :start :type :int-range-inclusive} {:value 5 :boundary-type :start :type :int-range-inclusive} {:value 5 :boundary-type :start :type :int-range-inclusive} {:value 5 :boundary-type :end :type :int-range-inclusive} {:value 5 :boundary-type :end :type :int-range-inclusive} {:value 8 :boundary-type :end :type :int-range-inclusive}] (#'range/ordered-range-values [(int-range-inclusive 1 2) (int-range-inclusive 4 5) (int-range-inclusive 5 8) (int-range-inclusive 5 5)]))) (is (= [{:value 5 :boundary-type :start :type :int-range-inclusive} {:value 5 :boundary-type :start :type :int-range-inclusive} {:value 5 :boundary-type :end :type :int-range-inclusive} {:value 5 :boundary-type :end :type :int-range-inclusive}] (#'range/ordered-range-values [(int-range-inclusive 5 5) (int-range-inclusive 5 5)]))))) ;; test against integer ranges for easy of expression and interpretation (deftest consolidate-ranges (testing "combines overlapping ranges" (is (= [(int-range-inclusive 5 5)] (range/consolidate [(int-range-inclusive 5 5) (int-range-inclusive 5 5)]))) (is (= [(int-range-inclusive 0 1) (int-range-inclusive 3 7) (int-range-inclusive 9 11)] (range/consolidate [(int-range-inclusive 0 1) (int-range-inclusive 3 4) (int-range-inclusive 3 7) (int-range-inclusive 5 5) (int-range-inclusive 9 11) (int-range-inclusive 5 5)]))) (is (= [(int-range-inclusive 2 11)] (range/consolidate [(int-range-inclusive 2 4) (int-range-inclusive 3 7) (int-range-inclusive 5 5) (int-range-inclusive 6 11) (int-range-inclusive 5 5)])))) (testing "conjoins abutting ranges" (is (= [(int-range-inclusive 0 9)] (range/consolidate [(int-range-inclusive 0 1) (int-range-inclusive 2 4) (int-range-inclusive 5 5) (int-range-inclusive 6 9) (int-range-inclusive 5 5)]))) (is (= [(int-range-inclusive 0 9)] (range/consolidate [(int-range-inclusive 0 1) (int-range-inclusive 2 3) (int-range-inclusive 4 5) (int-range-inclusive 6 9) (int-range-inclusive 5 5)])))) (testing "combines overlapping ranges and conjoins abutting ranges" (is (= [(int-range-inclusive 0 7) (int-range-inclusive 13 17)] (range/consolidate [(int-range-inclusive 0 1) (int-range-inclusive 2 4) (int-range-inclusive 3 7) (int-range-inclusive 5 5) (int-range-inclusive 13 17) (int-range-inclusive 5 5)])))))