|
(require '[clojure.test.check :as tc]) |
|
(require '[clojure.test.check.generators :as gen]) |
|
(require '[clojure.test.check.properties :as prop]) |
|
|
|
;; solution |
|
|
|
(defn normalize [range] |
|
(vec (sort range))) |
|
|
|
(defn consolidate* |
|
"Consolidate 2 normalized ranges." |
|
[a b] |
|
(let [[[a1 a2 :as a] [b1 b2 :as b]] (sort-by first [a b])] |
|
(if (>= a2 b1) |
|
[[a1 (max a2 b2)]] |
|
[a b]))) |
|
|
|
(defn consolidate [ranges] |
|
(loop [ranges (vec (sort-by first (map normalize ranges))) |
|
i 0] |
|
(if (not (contains? ranges (inc i))) |
|
ranges |
|
(let [c (consolidate* (get ranges i) (get ranges (inc i)))] |
|
(if (= 1 (count c)) |
|
(recur (-> (subvec ranges 0 i) |
|
(into c) |
|
(into (subvec ranges (+ 2 i)))) |
|
i) |
|
(recur ranges (inc i))))))) |
|
|
|
|
|
;; property-based testing |
|
|
|
(def gen-range (gen/tuple gen/int gen/int)) |
|
(def gen-ranges (gen/vector gen-range)) |
|
|
|
(defn ints-in [r] |
|
(let [[a b] (normalize r)] |
|
(set (range a (inc b))))) |
|
|
|
(defn union [rs] |
|
(set (mapcat ints-in rs))) |
|
|
|
(def consolidate*-overlap |
|
(prop/for-all [a gen-range |
|
b gen-range |
|
x gen/int] |
|
(let [a (normalize a) |
|
b (normalize b) |
|
cs (consolidate* a b)] |
|
(let [containing-ranges (filter #(contains? (ints-in %) x) cs)] |
|
(or (= 0 (count containing-ranges)) |
|
(= 1 (count containing-ranges))))))) |
|
|
|
;; we don't want to lose or add any range |
|
(def consolidate*-union |
|
(prop/for-all [a gen-range |
|
b gen-range] |
|
(= (union [a b]) |
|
(let [a (normalize a) |
|
b (normalize b)] |
|
(union (consolidate* a b)))))) |
|
|
|
(def consolidate*-commutative |
|
(prop/for-all [a gen-range |
|
b gen-range] |
|
(let [a (normalize a) |
|
b (normalize b)] |
|
(= (consolidate* a b) |
|
(consolidate* b a))))) |
|
|
|
(def consolidate-commutative |
|
(prop/for-all [[rs rs-shuffled] (gen/let [rngs gen-ranges |
|
rngs-s (gen/shuffle rngs)] |
|
[rngs rngs-s])] |
|
(= (consolidate rs) |
|
(consolidate rs-shuffled)))) |
|
|
|
;; we don't want to lose or add any range |
|
(def consolidate-union |
|
(prop/for-all [rs gen-ranges] |
|
(= (union rs) |
|
(union (consolidate rs))))) |
|
|
|
(def consolidate-overlap |
|
(prop/for-all [rs gen-ranges |
|
x gen/int] |
|
(let [containing-ranges (filter #(contains? (ints-in %) x) |
|
(consolidate rs))] |
|
(or (= 0 (count containing-ranges)) |
|
(= 1 (count containing-ranges)))))) |
|
|
|
(comment |
|
(tc/quick-check 100 consolidate*-commutative) |
|
(tc/quick-check 100 consolidate*-overlap) |
|
(tc/quick-check 100 consolidate*-union) |
|
(tc/quick-check 100 consolidate-commutative) |
|
(tc/quick-check 100 consolidate-overlap) |
|
(tc/quick-check 100 consolidate-union) |
|
|
|
(consolidate []) |
|
(consolidate [[0 1]]) |
|
(consolidate [[0 1] [1 2]]) |
|
(consolidate [[0 3] [4 10] [0 1]]) |
|
) |