Files
range-difference/test/challenge/discrete_value_range_test.clj
Steven Proctor 211efb5d16 move form deftype to defrecord for easier set comparison
deftype gives different object instances which causes comparisons
of sets with different instances to fail, as it check the instance
equality instead of value equality, which can be provided by
using defrecord instead of deftype
2026-01-11 16:17:50 -06:00

131 lines
5.7 KiB
Clojure

(ns challenge.discrete-value-range-test
(:require
[clojure.test :refer [deftest testing is]]
[challenge.discrete-value-range :as range]))
(defrecord 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)))
(defn int-range-inclusive [start end]
(assert (<= start end) (str "start : " start "; end: " 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)])))))