General cleanup and performance review changes

- move to transducer for walking the tagged range boundary items
- remove use of core.match, after getting logic correct to reduce
  loading of unneeded library
- added docstrings to function
- added type hints and removed reflection warnings
This commit is contained in:
2026-01-15 20:34:01 -06:00
parent 9ffa5d1b97
commit 3f03936cfb
4 changed files with 361 additions and 335 deletions

View File

@@ -1,5 +1,4 @@
{: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"]

View File

@@ -5,23 +5,23 @@
(extend-type LocalDateRange (extend-type LocalDateRange
range/DiscreteValueRange range/DiscreteValueRange
(abuts [this other] (abuts? [^LocalDateRange this ^LocalDateRange other]
(.abuts this other)) (.abuts this other))
(value-before [this] (value-before ^LocalDate [^LocalDateRange this]
(if (.isUnboundedStart this) (if (.isUnboundedStart this)
(.getStart this) (.getStart this)
(.. this getStart (minusDays 1)))) (.. this getStart (minusDays 1))))
(value-after [this] (value-after [^LocalDateRange this]
(if (.isUnboundedEnd this) (if (.isUnboundedEnd this)
(.getEnd this) (.getEnd this)
(.. this getEndInclusive (plusDays 1)))) (.. this getEndInclusive (plusDays 1))))
(start [this] (start ^LocalDate [^LocalDateRange this]
(.getStart this)) (.getStart this))
(end [this] (end ^LocalDate [^LocalDateRange this]
(.getEndInclusive this)) (.getEndInclusive this))
(range-type [_this] (range-type [_this]
:local-date-range) :local-date-range)
(union [this other] (union [this ^LocalDateRange other]
(when-not (.isConnected this other) (when-not (.isConnected this other)
(throw (ex-info "Cannot union non-connecting ranges" {}))) (throw (ex-info "Cannot union non-connecting ranges" {})))
(.union this other))) (.union this other)))

View File

@@ -1,7 +1,5 @@
(ns challenge.discrete-value-range (ns challenge.discrete-value-range
(:require (:require [clojure.set :as set]))
[clojure.core.match :refer [match]]
[clojure.set :as set]))
(defprotocol DiscreteValueRange (defprotocol DiscreteValueRange
"A protocol for generic behavior on Ranges over "A protocol for generic behavior on Ranges over
@@ -26,15 +24,23 @@
that before/after values against other values that the range that before/after values against other values that the range
is over. is over.
" "
(abuts [this other]) (abuts? [this other] "Does this range abuts the specified range")
(value-before [this]) (value-before [this] "The discrete value that is considered to be directly before the start value")
(value-after [this]) (value-after [this] "The discrete value that is considered to be directly after the end value")
(start [this]) (start [this] "The starting value (inclusive) for this range")
(end [this]) (end [this] "The ending value (inclusive) for this range")
(range-type [this]) (range-type [this]
(union [this other])) "Type of the range. Used in dispatch of multimethod to construct new ranges of the type.")
(union [this other]
"Calculates the range that is the union of this range and the other range.
The two ranges should overlap or abut each other."))
(defn ->range-boundaries* [range] (defmulti ->discrete-value-range (fn [range-type _start _end]
range-type))
(defn ->range-boundaries*
"Construct range boundary 'fencepost' markers for the range"
[range]
[{:value (start range) [{:value (start range)
:boundary-type :start :boundary-type :start
:type (range-type range) :type (range-type range)
@@ -46,6 +52,9 @@
(def ->range-boundaries (memoize ->range-boundaries*)) (def ->range-boundaries (memoize ->range-boundaries*))
(def untagged-range-boundary-compare
(juxt :value (comp {:start 0 :end 1} :boundary-type)))
(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
of all given ranges, to prepare to produces a consolidated of all given ranges, to prepare to produces a consolidated
@@ -61,12 +70,9 @@
[ranges] [ranges]
(->> ranges (->> ranges
(mapcat ->range-boundaries) (mapcat ->range-boundaries)
(sort-by (juxt :value (comp {:start 0 :end 1} :boundary-type))))) (sort-by untagged-range-boundary-compare)))
(defmulti ->discrete-value-range (fn [range-type _start _end] (defn- combine-overlapping-ranges-xf
range-type))
(defn- combine-overlapping-ranges
"transducer to find and combine overlapping ranges by looking at "transducer to find and combine overlapping ranges by looking at
ordered range value markers. ordered range value markers.
@@ -97,11 +103,11 @@
result) result)
:end (do :end (do
(.pop stack) (.pop stack)
(if (not (.empty stack)) (if (.empty stack)
result (xf result (->discrete-value-range type @start value))
(xf result (->discrete-value-range type @start value))))))))))) result)))))))))
(defn- combine-abutting-ranges (defn- combine-abutting-ranges-xf
"transducer to join ranges where the start and end of two ranges are consective "transducer to join ranges where the start and end of two ranges are consective
discrete values. discrete values.
@@ -109,82 +115,97 @@
combined." combined."
[] []
(fn [xf] (fn [xf]
(let [prev (volatile! nil)] (let [previous-range (volatile! nil)]
(fn (fn
([] (xf)) ([] (xf))
([result] (if @prev ([result] (if-let [prev @previous-range]
(xf (xf result @prev)) (xf (xf result prev))
(xf result))) (xf result)))
([result input] ([result input]
(cond (let [prev @previous-range]
(reduced? input) result (cond
(reduced? input) result
(nil? @prev) (do (nil? prev) (do
(vreset! prev input) (vreset! previous-range input)
result) result)
(abuts @prev input) (abuts? prev input)
(let [item (union @prev input)] (let [item (union prev input)]
(vreset! prev item) (vreset! previous-range item)
result) result)
:else (let [item @prev] :else (let [item prev]
(vreset! prev input) (vreset! previous-range input)
(xf result item)))))))) (xf result item)))))))))
(def consolidate-ranges-xf (def consolidate-ranges-xf
(comp (comp
(combine-overlapping-ranges) (combine-overlapping-ranges-xf)
(combine-abutting-ranges))) (combine-abutting-ranges-xf)))
(defn consolidate [ranges] (defn consolidate
"Take a set of ranges and consolidate/collapse the ranges
into the minimal set of ranges needed to represent the original
range set"
[ranges]
(into #{} consolidate-ranges-xf (ordered-range-values ranges))) (into #{} consolidate-ranges-xf (ordered-range-values ranges)))
(defn walk-range-boundaries [range-boundary-items] (defn- walk-tagged-range-boundaries-xf
(let [close-working-range (fn [range-type start end ranges] "transducer to create the set of ranges for the difference function
(conj! ranges (->discrete-value-range range-type by walking range boundaries items that have been taged as either
start belonging to the the source set, or the set of ranges to filter
end)))] out of the source set, and construct the set of resulting ranges
(loop [[boundary-item & boundary-items] range-boundary-items with the ranges in the filter ranges item removed from the source
in-filter-range nil set of ranges."
in-source-range nil []
ranges (transient #{}) (let [close-working-range (fn [range-type start end]
close-fn nil] (->discrete-value-range range-type
(match [boundary-item] start
[nil] end))
(persistent! ranges) in-filter-range (volatile! nil)
in-source-range (volatile! nil)
close-fn (volatile! nil)]
(fn [xf]
(fn
([] (xf))
([result] (xf result))
([result boundary-item]
(case [(:boundary-type boundary-item) (:range-source-type boundary-item)]
[:start :source-range]
(do
(vreset! in-source-range true)
(vreset! close-fn (partial close-working-range
(:type boundary-item)
(:value boundary-item)))
result)
[{:boundary-type :start [:end :source-range]
:range-source-type :source-range (let [close @close-fn]
:type range-type (vreset! in-source-range false)
:value new-working-range-start-value}] (vreset! close-fn nil)
(recur boundary-items in-filter-range true ranges (partial close-working-range (if @in-filter-range
range-type result
new-working-range-start-value)) (xf result (close (:value boundary-item)))))
[{:boundary-type :end [:start :filter-range]
:range-source-type :source-range (do
:value value}] (vreset! in-filter-range true)
(if in-filter-range (if-let [close @close-fn]
(recur boundary-items in-filter-range false ranges nil) (do
(recur boundary-items in-filter-range false (close-fn value ranges) nil)) (vreset! close-fn nil)
(xf result (close (:prev-value boundary-item))))
result))
[{:boundary-type :start [:end :filter-range]
: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 (do
:range-source-type :filter-range (vreset! in-filter-range false)
:type range-type (when @in-source-range
:next-value new-working-range-start-value}] (vreset! close-fn (partial close-working-range
(if in-source-range (:type boundary-item)
(recur boundary-items false in-source-range ranges (partial close-working-range (:next-value boundary-item))))
range-type result)))))))
new-working-range-start-value))
(recur boundary-items false in-source-range ranges close-fn))))))
(def ^:private range-boundary-source-compare (def ^:private range-boundary-source-compare
"Sorts range boundary items "Sorts range boundary items
@@ -219,6 +240,6 @@
all-range-set-boundaries (->> (into range-set-boundaries all-range-set-boundaries (->> (into range-set-boundaries
range-sets-to-remove-boundaries) range-sets-to-remove-boundaries)
(sort-by range-boundary-source-compare))] (sort-by range-boundary-source-compare))]
(walk-range-boundaries all-range-set-boundaries))) (into #{} (walk-tagged-range-boundaries-xf) all-range-set-boundaries)))
([range-set range-set-to-remove & additional-range-sets-to-remove] ([range-set range-set-to-remove & additional-range-sets-to-remove]
(difference range-set (apply set/union (conj additional-range-sets-to-remove range-set-to-remove))))) (difference range-set (apply set/union (conj additional-range-sets-to-remove range-set-to-remove)))))

View File

@@ -6,13 +6,17 @@
(defrecord IntInclusiveDiscreteValueRange (defrecord IntInclusiveDiscreteValueRange
[^int start ^int end] [^int start ^int end]
range/DiscreteValueRange range/DiscreteValueRange
(abuts [_this other] (range/abuts? [_this other]
(or (= 1 (abs (- start (.end other)))) (or (= 1 (abs (- start (range/end other))))
(= 1 (abs (- end (.start other)))))) (= 1 (abs (- end (range/start other))))))
(value-before [__this] (value-before [__this]
(dec start)) (if (= start Integer/MIN_VALUE)
Integer/MIN_VALUE
(dec start)))
(value-after [_this] (value-after [_this]
(inc end)) (if (= end Integer/MAX_VALUE)
Integer/MAX_VALUE
(inc end)))
(start [_this] (start [_this]
start) start)
(end [_this] (end [_this]
@@ -20,12 +24,14 @@
(range-type [_this] (range-type [_this]
:int-range-inclusive) :int-range-inclusive)
(union [this other] (union [this other]
(when-not (range/abuts this other) (when-not (or (range/abuts? this other)
(or (<= start (range/start other) end)
(<= (range/start other) start (range/end other))))
(throw (ex-info "Cannot union non-abutting ranges" {}))) (throw (ex-info "Cannot union non-abutting ranges" {})))
(range/->discrete-value-range (.range-type this) (range/->discrete-value-range (range/range-type this)
(min start (.start other)) (min start (range/start other))
(max end (.end other)))) (max end (range/end other))))
Object Object
(toString [_this] (toString [_this]
@@ -45,15 +51,15 @@
(is (= (int-range-inclusive 0 1) (is (= (int-range-inclusive 0 1)
(int-range-inclusive 0 1)))) (int-range-inclusive 0 1))))
(testing "abuts" (testing "abuts?"
(is (= true (range/abuts (int-range-inclusive 0 1) (is (= true (range/abuts? (int-range-inclusive 0 1)
(int-range-inclusive 2 3)))) (int-range-inclusive 2 3))))
(is (= true (range/abuts (int-range-inclusive 1 1) (is (= true (range/abuts? (int-range-inclusive 1 1)
(int-range-inclusive 2 3)))) (int-range-inclusive 2 3))))
(is (= true (range/abuts (int-range-inclusive 4 7) (is (= true (range/abuts? (int-range-inclusive 4 7)
(int-range-inclusive 2 3)))) (int-range-inclusive 2 3))))
(is (= false (range/abuts (int-range-inclusive 4 7) (is (= false (range/abuts? (int-range-inclusive 4 7)
(int-range-inclusive 1 2))))) (int-range-inclusive 1 2)))))
(testing "value-before" (testing "value-before"
(is (= 0 (range/value-before (int-range-inclusive 1 8))))) (is (= 0 (range/value-before (int-range-inclusive 1 8)))))
(testing "value-after" (testing "value-after"
@@ -134,260 +140,260 @@
(deftest walk-range-boundaries-test (deftest walk-range-boundaries-test
(testing "only filter-ranges" (testing "only filter-ranges"
(is (= #{} (is (= #{}
(range/walk-range-boundaries [{:boundary-type :start (into #{} (#'range/walk-tagged-range-boundaries-xf) [{:boundary-type :start
:range-source-type :filter-range :range-source-type :filter-range
:value 1 :value 1
:prev-value 0 :prev-value 0
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :end {:boundary-type :end
:range-source-type :filter-range :range-source-type :filter-range
:value 4 :value 4
:next-value 5 :next-value 5
:type :int-range-inclusive}])))) :type :int-range-inclusive}]))))
(testing "only source-ranges" (testing "only source-ranges"
(is (= #{(int-range-inclusive 1 5)} (is (= #{(int-range-inclusive 1 5)}
(range/walk-range-boundaries [{:boundary-type :start (into #{} (#'range/walk-tagged-range-boundaries-xf) [{:boundary-type :start
:range-source-type :source-range :range-source-type :source-range
:value 1 :value 1
:prev-value 0 :prev-value 0
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :end {:boundary-type :end
:range-source-type :source-range :range-source-type :source-range
:value 5 :value 5
:next-value 6 :next-value 6
:type :int-range-inclusive}]))) :type :int-range-inclusive}])))
(is (= #{(int-range-inclusive 1 5) (is (= #{(int-range-inclusive 1 5)
(int-range-inclusive 11 15)} (int-range-inclusive 11 15)}
(range/walk-range-boundaries [{:boundary-type :start (into #{} (#'range/walk-tagged-range-boundaries-xf) [{:boundary-type :start
:range-source-type :source-range :range-source-type :source-range
:value 1 :value 1
:next-value 2 :next-value 2
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :end {:boundary-type :end
:range-source-type :source-range :range-source-type :source-range
:value 5 :value 5
:prev-value 4 :prev-value 4
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :start {:boundary-type :start
:range-source-type :source-range :range-source-type :source-range
:value 11 :value 11
:next-value 12 :next-value 12
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :end {:boundary-type :end
:range-source-type :source-range :range-source-type :source-range
:value 15 :value 15
:prev-value 14 :prev-value 14
:type :int-range-inclusive}])))) :type :int-range-inclusive}]))))
(testing "filter-ranges and source-ranges are the same" (testing "filter-ranges and source-ranges are the same"
(is (= #{} (is (= #{}
(range/walk-range-boundaries [{:boundary-type :start (into #{} (#'range/walk-tagged-range-boundaries-xf) [{:boundary-type :start
:range-source-type :filter-range :range-source-type :filter-range
:value 1 :value 1
:prev-value 0 :prev-value 0
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :start {:boundary-type :start
:range-source-type :source-range :range-source-type :source-range
:value 1 :value 1
:prev-value 0 :prev-value 0
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :end {:boundary-type :end
:range-source-type :source-range :range-source-type :source-range
:value 5 :value 5
:next-value 6 :next-value 6
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :end {:boundary-type :end
:range-source-type :filter-range :range-source-type :filter-range
:value 5 :value 5
:next-value 6 :next-value 6
:type :int-range-inclusive}])))) :type :int-range-inclusive}]))))
(testing "filter-ranges before source-ranges" (testing "filter-ranges before source-ranges"
(is (= #{(int-range-inclusive 11 15)} (is (= #{(int-range-inclusive 11 15)}
(range/walk-range-boundaries [{:boundary-type :start (into #{} (#'range/walk-tagged-range-boundaries-xf) [{:boundary-type :start
:range-source-type :filter-range :range-source-type :filter-range
:value 1 :value 1
:prev-value 0 :prev-value 0
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :end {:boundary-type :end
:range-source-type :filter-range :range-source-type :filter-range
:value 5 :value 5
:next-value 6 :next-value 6
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :start {:boundary-type :start
:range-source-type :source-range :range-source-type :source-range
:value 11 :value 11
:prev-value 10 :prev-value 10
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :end {:boundary-type :end
:range-source-type :source-range :range-source-type :source-range
:value 15 :value 15
:next-value 16 :next-value 16
:type :int-range-inclusive}])))) :type :int-range-inclusive}]))))
(testing "filter-ranges after source-ranges" (testing "filter-ranges after source-ranges"
(is (= #{(int-range-inclusive 1 5)} (is (= #{(int-range-inclusive 1 5)}
(range/walk-range-boundaries [{:boundary-type :start (into #{} (#'range/walk-tagged-range-boundaries-xf) [{:boundary-type :start
:range-source-type :source-range :range-source-type :source-range
:value 1 :value 1
:prev-value 0 :prev-value 0
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :end {:boundary-type :end
:range-source-type :source-range :range-source-type :source-range
:value 5 :value 5
:next-value 6 :next-value 6
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :start {:boundary-type :start
:range-source-type :filter-range :range-source-type :filter-range
:prev-value 10 :prev-value 10
:value 11 :value 11
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :end {:boundary-type :end
:range-source-type :filter-range :range-source-type :filter-range
:value 15 :value 15
:next-value 16 :next-value 16
:type :int-range-inclusive}])))) :type :int-range-inclusive}]))))
(testing "single filter-range between source-range" (testing "single filter-range between source-range"
(is (= #{(int-range-inclusive 1 5) (is (= #{(int-range-inclusive 1 5)
(int-range-inclusive 11 20)} (int-range-inclusive 11 20)}
(range/walk-range-boundaries [{:boundary-type :start (into #{} (#'range/walk-tagged-range-boundaries-xf) [{:boundary-type :start
:range-source-type :source-range :range-source-type :source-range
:value 1 :value 1
:prev-value 0 :prev-value 0
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :start {:boundary-type :start
:range-source-type :filter-range :range-source-type :filter-range
:prev-value 5 :prev-value 5
:value 6 :value 6
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :end {:boundary-type :end
:range-source-type :filter-range :range-source-type :filter-range
:value 10 :value 10
:next-value 11 :next-value 11
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :end {:boundary-type :end
:range-source-type :source-range :range-source-type :source-range
:value 20 :value 20
:next-value 21 :next-value 21
:type :int-range-inclusive}])))) :type :int-range-inclusive}]))))
(testing "single filter-range between source-range but ends align" (testing "single filter-range between source-range but ends align"
(is (= #{(int-range-inclusive 1 5)} (is (= #{(int-range-inclusive 1 5)}
(range/walk-range-boundaries [{:boundary-type :start (into #{} (#'range/walk-tagged-range-boundaries-xf) [{:boundary-type :start
:range-source-type :source-range :range-source-type :source-range
:value 1 :value 1
:prev-value 0 :prev-value 0
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :start {:boundary-type :start
:range-source-type :filter-range :range-source-type :filter-range
:prev-value 5 :prev-value 5
:value 6 :value 6
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :end {:boundary-type :end
:range-source-type :source-range :range-source-type :source-range
:value 10 :value 10
:next-value 11 :next-value 11
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :end {:boundary-type :end
:range-source-type :filter-range :range-source-type :filter-range
:value 10 :value 10
:next-value 11 :next-value 11
:type :int-range-inclusive}])))) :type :int-range-inclusive}]))))
(testing "multiple filter-ranges between source-range" (testing "multiple filter-ranges between source-range"
(is (= #{(int-range-inclusive 1 5) (is (= #{(int-range-inclusive 1 5)
(int-range-inclusive 11 12) (int-range-inclusive 11 12)
(int-range-inclusive 20 20)} (int-range-inclusive 20 20)}
(range/walk-range-boundaries [{:boundary-type :start (into #{} (#'range/walk-tagged-range-boundaries-xf) [{:boundary-type :start
:range-source-type :source-range :range-source-type :source-range
:value 1 :value 1
:prev-value 0 :prev-value 0
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :start {:boundary-type :start
:range-source-type :filter-range :range-source-type :filter-range
:prev-value 5 :prev-value 5
:value 6 :value 6
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :end {:boundary-type :end
:range-source-type :filter-range :range-source-type :filter-range
:value 10 :value 10
:next-value 11 :next-value 11
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :start {:boundary-type :start
:range-source-type :filter-range :range-source-type :filter-range
:prev-value 12 :prev-value 12
:value 13 :value 13
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :end {:boundary-type :end
:range-source-type :filter-range :range-source-type :filter-range
:value 19 :value 19
:next-value 20 :next-value 20
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :end {:boundary-type :end
:range-source-type :source-range :range-source-type :source-range
:value 20 :value 20
:prev-value 19 :prev-value 19
:type :int-range-inclusive}])))) :type :int-range-inclusive}]))))
(testing "source range between filter-range" (testing "source range between filter-range"
(is (= #{} (is (= #{}
(range/walk-range-boundaries [{:boundary-type :start (into #{} (#'range/walk-tagged-range-boundaries-xf) [{:boundary-type :start
:range-source-type :filter-range :range-source-type :filter-range
:prev-value 1 :prev-value 1
:value 1 :value 1
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :start {:boundary-type :start
:range-source-type :source-range :range-source-type :source-range
:value 6 :value 6
:prev-value 5 :prev-value 5
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :end {:boundary-type :end
:range-source-type :source-range :range-source-type :source-range
:value 10 :value 10
:next-value 11 :next-value 11
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :start {:boundary-type :start
:range-source-type :source-range :range-source-type :source-range
:value 13 :value 13
:prev-value 12 :prev-value 12
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :end {:boundary-type :end
:range-source-type :source-range :range-source-type :source-range
:value 19 :value 19
:next-value 20 :next-value 20
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :end {:boundary-type :end
:range-source-type :filter-range :range-source-type :filter-range
:value 20 :value 20
:next-value 21 :next-value 21
:type :int-range-inclusive}])))) :type :int-range-inclusive}]))))
(testing "filter range overlaps source ranges" (testing "filter range overlaps source ranges"
(is (= #{(int-range-inclusive 1 4) (is (= #{(int-range-inclusive 1 4)
(int-range-inclusive 11 13)} (int-range-inclusive 11 13)}
(range/walk-range-boundaries [{:boundary-type :start (into #{} (#'range/walk-tagged-range-boundaries-xf) [{:boundary-type :start
:range-source-type :source-range :range-source-type :source-range
:value 1 :value 1
:prev-value 0 :prev-value 0
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :start {:boundary-type :start
:range-source-type :filter-range :range-source-type :filter-range
:prev-value 4 :prev-value 4
:value 5 :value 5
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :end {:boundary-type :end
:range-source-type :source-range :range-source-type :source-range
:value 6 :value 6
:next-value 7 :next-value 7
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :start {:boundary-type :start
:range-source-type :source-range :range-source-type :source-range
:value 8 :value 8
:prev-value 7 :prev-value 7
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :end {:boundary-type :end
:range-source-type :filter-range :range-source-type :filter-range
:value 10 :value 10
:next-value 11 :next-value 11
:type :int-range-inclusive} :type :int-range-inclusive}
{:boundary-type :end {:boundary-type :end
:range-source-type :source-range :range-source-type :source-range
:value 13 :value 13
:next-value 14 :next-value 14
:type :int-range-inclusive}]))))) :type :int-range-inclusive}])))))
(deftest difference-test (deftest difference-test
(testing "unary" (testing "unary"