ranges have previously been consolidated by having overlapping ranges be combined, resuting in a (possibly) consolidated set of ranges, which are now ordered by start and end dates. Add a transducer step to walk those ranges and find any pairs of ranges where the pair of ranges abutt each other, and combine them into a single range.
134 lines
5.8 KiB
Clojure
134 lines
5.8 KiB
Clojure
(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)])))))
|