walk sequence of range boundaries values for source and filter ranges
add lower level logic for walking the boundary markers of sets but distinguish between a source range and a filter range when looking at the start and end boundary behavior
This commit is contained in:
1
deps.edn
1
deps.edn
@@ -1,4 +1,5 @@
|
|||||||
{:deps {org.clojure/clojure {:mvn/version "1.11.2"}
|
{:deps {org.clojure/clojure {:mvn/version "1.11.2"}
|
||||||
|
org.clojure/core.match {:mvn/version "1.1.1"}
|
||||||
org.threeten/threeten-extra {:mvn/version "1.8.0"}}
|
org.threeten/threeten-extra {:mvn/version "1.8.0"}}
|
||||||
:paths ["src"]
|
:paths ["src"]
|
||||||
:aliases {:test {:extra-paths ["test"]
|
:aliases {:test {:extra-paths ["test"]
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
(ns challenge.discrete-value-range)
|
(ns challenge.discrete-value-range
|
||||||
|
(:require [clojure.core.match :refer [match]]))
|
||||||
|
|
||||||
(defprotocol DiscreteValueRange
|
(defprotocol DiscreteValueRange
|
||||||
"A protocol for generic behavior on Ranges over
|
"A protocol for generic behavior on Ranges over
|
||||||
@@ -29,7 +30,7 @@
|
|||||||
(start [this])
|
(start [this])
|
||||||
(end [this])
|
(end [this])
|
||||||
(range-type [this])
|
(range-type [this])
|
||||||
(union [range1 range2]))
|
(union [this other]))
|
||||||
|
|
||||||
(defn- ordered-range-values
|
(defn- ordered-range-values
|
||||||
"Builds an ordered list or 'fenceposts' for the start and end
|
"Builds an ordered list or 'fenceposts' for the start and end
|
||||||
@@ -129,4 +130,51 @@
|
|||||||
(combine-abutting-ranges)))
|
(combine-abutting-ranges)))
|
||||||
|
|
||||||
(defn consolidate [ranges]
|
(defn consolidate [ranges]
|
||||||
(eduction consolidate-ranges-xf (ordered-range-values ranges)))
|
(into #{} consolidate-ranges-xf (ordered-range-values ranges)))
|
||||||
|
|
||||||
|
(defn walk-range-boundaries [range-boundary-items]
|
||||||
|
(let [close-working-range (fn [range-type start end ranges]
|
||||||
|
(conj ranges (->discrete-value-range range-type
|
||||||
|
start
|
||||||
|
end)))]
|
||||||
|
|
||||||
|
(loop [[boundary-item & boundary-items] range-boundary-items
|
||||||
|
in-filter-range nil
|
||||||
|
in-source-range nil
|
||||||
|
ranges #{}
|
||||||
|
close-fn nil]
|
||||||
|
(match [boundary-item]
|
||||||
|
[nil]
|
||||||
|
ranges
|
||||||
|
|
||||||
|
[{:boundary-type :start
|
||||||
|
:range-source-type :source-range
|
||||||
|
:type range-type
|
||||||
|
:value new-working-range-start-value}]
|
||||||
|
(recur boundary-items in-filter-range true ranges (partial close-working-range
|
||||||
|
range-type
|
||||||
|
new-working-range-start-value))
|
||||||
|
|
||||||
|
[{:boundary-type :end
|
||||||
|
:range-source-type :source-range
|
||||||
|
:value value}]
|
||||||
|
(if in-filter-range
|
||||||
|
(recur boundary-items in-filter-range false ranges nil)
|
||||||
|
(recur boundary-items in-filter-range false (close-fn value ranges) nil))
|
||||||
|
|
||||||
|
[{:boundary-type :start
|
||||||
|
:range-source-type :filter-range
|
||||||
|
:prev-value prev-value}]
|
||||||
|
(if close-fn
|
||||||
|
(recur boundary-items true in-source-range (close-fn prev-value ranges) nil)
|
||||||
|
(recur boundary-items true in-source-range ranges close-fn))
|
||||||
|
|
||||||
|
[{:boundary-type :end
|
||||||
|
:range-source-type :filter-range
|
||||||
|
:type range-type
|
||||||
|
:next-value new-working-range-start-value}]
|
||||||
|
(if in-source-range
|
||||||
|
(recur boundary-items false in-source-range ranges (partial close-working-range
|
||||||
|
range-type
|
||||||
|
new-working-range-start-value))
|
||||||
|
(recur boundary-items false in-source-range ranges close-fn))))))
|
||||||
|
|||||||
@@ -128,3 +128,212 @@
|
|||||||
(int-range-inclusive 5 5)
|
(int-range-inclusive 5 5)
|
||||||
(int-range-inclusive 13 17)
|
(int-range-inclusive 13 17)
|
||||||
(int-range-inclusive 5 5)])))))
|
(int-range-inclusive 5 5)])))))
|
||||||
|
|
||||||
|
(deftest walk-range-boundaries-test
|
||||||
|
(testing "only filter-ranges"
|
||||||
|
(is (= #{}
|
||||||
|
(range/walk-range-boundaries [{:boundary-type :start
|
||||||
|
:range-source-type :filter-range
|
||||||
|
:value 1
|
||||||
|
:prev-value 0
|
||||||
|
:next-value 2
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :end
|
||||||
|
:range-source-type :filter-range
|
||||||
|
:value 4
|
||||||
|
:prev-value 3
|
||||||
|
:next-value 5
|
||||||
|
:type :int-range-inclusive}]))))
|
||||||
|
(testing "only source-ranges"
|
||||||
|
(is (= #{(int-range-inclusive 1 5)}
|
||||||
|
(range/walk-range-boundaries [{:boundary-type :start
|
||||||
|
:range-source-type :source-range
|
||||||
|
:value 1
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :end
|
||||||
|
:range-source-type :source-range
|
||||||
|
:value 5
|
||||||
|
:type :int-range-inclusive}])))
|
||||||
|
(is (= #{(int-range-inclusive 1 5)
|
||||||
|
(int-range-inclusive 11 15)}
|
||||||
|
(range/walk-range-boundaries [{:boundary-type :start
|
||||||
|
:range-source-type :source-range
|
||||||
|
:value 1
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :end
|
||||||
|
:range-source-type :source-range
|
||||||
|
:value 5
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :start
|
||||||
|
:range-source-type :source-range
|
||||||
|
:value 11
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :end
|
||||||
|
:range-source-type :source-range
|
||||||
|
:value 15
|
||||||
|
:type :int-range-inclusive}]))))
|
||||||
|
(testing "filter-ranges before source-ranges"
|
||||||
|
(is (= #{(int-range-inclusive 11 15)}
|
||||||
|
(range/walk-range-boundaries [{:boundary-type :start
|
||||||
|
:range-source-type :filter-range
|
||||||
|
:value 1
|
||||||
|
:prev-value 0
|
||||||
|
:next-value 2
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :end
|
||||||
|
:range-source-type :filter-range
|
||||||
|
:prev-value 4
|
||||||
|
:value 5
|
||||||
|
:next-value 6
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :start
|
||||||
|
:range-source-type :source-range
|
||||||
|
:value 11
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :end
|
||||||
|
:range-source-type :source-range
|
||||||
|
:value 15
|
||||||
|
:type :int-range-inclusive}]))))
|
||||||
|
(testing "filter-ranges after source-ranges"
|
||||||
|
(is (= #{(int-range-inclusive 1 5)}
|
||||||
|
(range/walk-range-boundaries [{:boundary-type :start
|
||||||
|
:range-source-type :source-range
|
||||||
|
:value 1
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :end
|
||||||
|
:range-source-type :source-range
|
||||||
|
:value 5
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :start
|
||||||
|
:range-source-type :filter-range
|
||||||
|
:prev-value 10
|
||||||
|
:value 11
|
||||||
|
:next-value 12
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :end
|
||||||
|
:range-source-type :filter-range
|
||||||
|
:prev-value 14
|
||||||
|
:value 15
|
||||||
|
:next-value 16
|
||||||
|
:type :int-range-inclusive}]))))
|
||||||
|
(testing "single filter-range between source-range"
|
||||||
|
(is (= #{(int-range-inclusive 1 5)
|
||||||
|
(int-range-inclusive 11 20)}
|
||||||
|
(range/walk-range-boundaries [{:boundary-type :start
|
||||||
|
:range-source-type :source-range
|
||||||
|
:value 1
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :start
|
||||||
|
:range-source-type :filter-range
|
||||||
|
:prev-value 5
|
||||||
|
:value 6
|
||||||
|
:next-value 7
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :end
|
||||||
|
:range-source-type :filter-range
|
||||||
|
:prev-value 10
|
||||||
|
:value 10
|
||||||
|
:next-value 11
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :end
|
||||||
|
:range-source-type :source-range
|
||||||
|
:value 20
|
||||||
|
:type :int-range-inclusive}]))))
|
||||||
|
(testing "multiple filter-ranges between source-range"
|
||||||
|
(is (= #{(int-range-inclusive 1 5)
|
||||||
|
(int-range-inclusive 11 12)
|
||||||
|
(int-range-inclusive 20 20)}
|
||||||
|
(range/walk-range-boundaries [{:boundary-type :start
|
||||||
|
:range-source-type :source-range
|
||||||
|
:value 1
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :start
|
||||||
|
:range-source-type :filter-range
|
||||||
|
:prev-value 5
|
||||||
|
:value 6
|
||||||
|
:next-value 7
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :end
|
||||||
|
:range-source-type :filter-range
|
||||||
|
:prev-value 10
|
||||||
|
:value 10
|
||||||
|
:next-value 11
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :start
|
||||||
|
:range-source-type :filter-range
|
||||||
|
:prev-value 12
|
||||||
|
:value 13
|
||||||
|
:next-value 14
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :end
|
||||||
|
:range-source-type :filter-range
|
||||||
|
:prev-value 18
|
||||||
|
:value 19
|
||||||
|
:next-value 20
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :end
|
||||||
|
:range-source-type :source-range
|
||||||
|
:value 20
|
||||||
|
:type :int-range-inclusive}]))))
|
||||||
|
(testing "source range between filter-range"
|
||||||
|
(is (= #{}
|
||||||
|
(range/walk-range-boundaries [{:boundary-type :start
|
||||||
|
:range-source-type :filter-range
|
||||||
|
:prev-value 1
|
||||||
|
:value 1
|
||||||
|
:next-value 2
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :start
|
||||||
|
:range-source-type :source-range
|
||||||
|
:value 6
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :end
|
||||||
|
:range-source-type :source-range
|
||||||
|
:value 10
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :start
|
||||||
|
:range-source-type :source-range
|
||||||
|
:value 13
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :end
|
||||||
|
:range-source-type :source-range
|
||||||
|
:value 19
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :end
|
||||||
|
:range-source-type :filter-range
|
||||||
|
:prev-value 19
|
||||||
|
:value 20
|
||||||
|
:next-value 21
|
||||||
|
:type :int-range-inclusive}]))))
|
||||||
|
(testing "filter range overlaps source ranges"
|
||||||
|
(is (= #{(int-range-inclusive 1 4)
|
||||||
|
(int-range-inclusive 11 13)}
|
||||||
|
(range/walk-range-boundaries [{:boundary-type :start
|
||||||
|
:range-source-type :source-range
|
||||||
|
:value 1
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :start
|
||||||
|
:range-source-type :filter-range
|
||||||
|
:prev-value 4
|
||||||
|
:value 5
|
||||||
|
:next-value 6
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :end
|
||||||
|
:range-source-type :source-range
|
||||||
|
:value 6
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :start
|
||||||
|
:range-source-type :source-range
|
||||||
|
:value 8
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :end
|
||||||
|
:range-source-type :filter-range
|
||||||
|
:prev-value 9
|
||||||
|
:value 10
|
||||||
|
:next-value 11
|
||||||
|
:type :int-range-inclusive}
|
||||||
|
{:boundary-type :end
|
||||||
|
:range-source-type :source-range
|
||||||
|
:value 13
|
||||||
|
:type :int-range-inclusive}])))))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user