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:
1
deps.edn
1
deps.edn
@@ -1,5 +1,4 @@
|
||||
{: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"}}
|
||||
:paths ["src"]
|
||||
:aliases {:test {:extra-paths ["test"]
|
||||
|
||||
@@ -5,23 +5,23 @@
|
||||
|
||||
(extend-type LocalDateRange
|
||||
range/DiscreteValueRange
|
||||
(abuts [this other]
|
||||
(abuts? [^LocalDateRange this ^LocalDateRange other]
|
||||
(.abuts this other))
|
||||
(value-before [this]
|
||||
(value-before ^LocalDate [^LocalDateRange this]
|
||||
(if (.isUnboundedStart this)
|
||||
(.getStart this)
|
||||
(.. this getStart (minusDays 1))))
|
||||
(value-after [this]
|
||||
(value-after [^LocalDateRange this]
|
||||
(if (.isUnboundedEnd this)
|
||||
(.getEnd this)
|
||||
(.. this getEndInclusive (plusDays 1))))
|
||||
(start [this]
|
||||
(start ^LocalDate [^LocalDateRange this]
|
||||
(.getStart this))
|
||||
(end [this]
|
||||
(end ^LocalDate [^LocalDateRange this]
|
||||
(.getEndInclusive this))
|
||||
(range-type [_this]
|
||||
:local-date-range)
|
||||
(union [this other]
|
||||
(union [this ^LocalDateRange other]
|
||||
(when-not (.isConnected this other)
|
||||
(throw (ex-info "Cannot union non-connecting ranges" {})))
|
||||
(.union this other)))
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
(ns challenge.discrete-value-range
|
||||
(:require
|
||||
[clojure.core.match :refer [match]]
|
||||
[clojure.set :as set]))
|
||||
(:require [clojure.set :as set]))
|
||||
|
||||
(defprotocol DiscreteValueRange
|
||||
"A protocol for generic behavior on Ranges over
|
||||
@@ -26,15 +24,23 @@
|
||||
that before/after values against other values that the range
|
||||
is over.
|
||||
"
|
||||
(abuts [this other])
|
||||
(value-before [this])
|
||||
(value-after [this])
|
||||
(start [this])
|
||||
(end [this])
|
||||
(range-type [this])
|
||||
(union [this other]))
|
||||
(abuts? [this other] "Does this range abuts the specified range")
|
||||
(value-before [this] "The discrete value that is considered to be directly before the start value")
|
||||
(value-after [this] "The discrete value that is considered to be directly after the end value")
|
||||
(start [this] "The starting value (inclusive) for this range")
|
||||
(end [this] "The ending value (inclusive) for this range")
|
||||
(range-type [this]
|
||||
"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)
|
||||
:boundary-type :start
|
||||
:type (range-type range)
|
||||
@@ -46,6 +52,9 @@
|
||||
|
||||
(def ->range-boundaries (memoize ->range-boundaries*))
|
||||
|
||||
(def untagged-range-boundary-compare
|
||||
(juxt :value (comp {:start 0 :end 1} :boundary-type)))
|
||||
|
||||
(defn- ordered-range-values
|
||||
"Builds an ordered list or 'fenceposts' for the start and end
|
||||
of all given ranges, to prepare to produces a consolidated
|
||||
@@ -61,12 +70,9 @@
|
||||
[ranges]
|
||||
(->> ranges
|
||||
(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]
|
||||
range-type))
|
||||
|
||||
(defn- combine-overlapping-ranges
|
||||
(defn- combine-overlapping-ranges-xf
|
||||
"transducer to find and combine overlapping ranges by looking at
|
||||
ordered range value markers.
|
||||
|
||||
@@ -97,11 +103,11 @@
|
||||
result)
|
||||
:end (do
|
||||
(.pop stack)
|
||||
(if (not (.empty stack))
|
||||
result
|
||||
(xf result (->discrete-value-range type @start value)))))))))))
|
||||
(if (.empty stack)
|
||||
(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
|
||||
discrete values.
|
||||
|
||||
@@ -109,82 +115,97 @@
|
||||
combined."
|
||||
[]
|
||||
(fn [xf]
|
||||
(let [prev (volatile! nil)]
|
||||
(let [previous-range (volatile! nil)]
|
||||
(fn
|
||||
([] (xf))
|
||||
([result] (if @prev
|
||||
(xf (xf result @prev))
|
||||
([result] (if-let [prev @previous-range]
|
||||
(xf (xf result prev))
|
||||
(xf result)))
|
||||
([result input]
|
||||
(cond
|
||||
(reduced? input) result
|
||||
(let [prev @previous-range]
|
||||
(cond
|
||||
(reduced? input) result
|
||||
|
||||
(nil? @prev) (do
|
||||
(vreset! prev input)
|
||||
result)
|
||||
(nil? prev) (do
|
||||
(vreset! previous-range input)
|
||||
result)
|
||||
|
||||
(abuts @prev input)
|
||||
(let [item (union @prev input)]
|
||||
(vreset! prev item)
|
||||
result)
|
||||
(abuts? prev input)
|
||||
(let [item (union prev input)]
|
||||
(vreset! previous-range item)
|
||||
result)
|
||||
|
||||
:else (let [item @prev]
|
||||
(vreset! prev input)
|
||||
(xf result item))))))))
|
||||
:else (let [item prev]
|
||||
(vreset! previous-range input)
|
||||
(xf result item)))))))))
|
||||
|
||||
(def consolidate-ranges-xf
|
||||
(comp
|
||||
(combine-overlapping-ranges)
|
||||
(combine-abutting-ranges)))
|
||||
(combine-overlapping-ranges-xf)
|
||||
(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)))
|
||||
|
||||
(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 (transient #{})
|
||||
close-fn nil]
|
||||
(match [boundary-item]
|
||||
[nil]
|
||||
(persistent! ranges)
|
||||
(defn- walk-tagged-range-boundaries-xf
|
||||
"transducer to create the set of ranges for the difference function
|
||||
by walking range boundaries items that have been taged as either
|
||||
belonging to the the source set, or the set of ranges to filter
|
||||
out of the source set, and construct the set of resulting ranges
|
||||
with the ranges in the filter ranges item removed from the source
|
||||
set of ranges."
|
||||
[]
|
||||
(let [close-working-range (fn [range-type start end]
|
||||
(->discrete-value-range range-type
|
||||
start
|
||||
end))
|
||||
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
|
||||
: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))
|
||||
[:end :source-range]
|
||||
(let [close @close-fn]
|
||||
(vreset! in-source-range false)
|
||||
(vreset! close-fn nil)
|
||||
(if @in-filter-range
|
||||
result
|
||||
(xf result (close (:value boundary-item)))))
|
||||
|
||||
[{: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))
|
||||
[:start :filter-range]
|
||||
(do
|
||||
(vreset! in-filter-range true)
|
||||
(if-let [close @close-fn]
|
||||
(do
|
||||
(vreset! close-fn nil)
|
||||
(xf result (close (:prev-value boundary-item))))
|
||||
result))
|
||||
|
||||
[{: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))
|
||||
[:end :filter-range]
|
||||
|
||||
[{: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))))))
|
||||
(do
|
||||
(vreset! in-filter-range false)
|
||||
(when @in-source-range
|
||||
(vreset! close-fn (partial close-working-range
|
||||
(:type boundary-item)
|
||||
(:next-value boundary-item))))
|
||||
result)))))))
|
||||
|
||||
(def ^:private range-boundary-source-compare
|
||||
"Sorts range boundary items
|
||||
@@ -219,6 +240,6 @@
|
||||
all-range-set-boundaries (->> (into range-set-boundaries
|
||||
range-sets-to-remove-boundaries)
|
||||
(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]
|
||||
(difference range-set (apply set/union (conj additional-range-sets-to-remove range-set-to-remove)))))
|
||||
|
||||
@@ -6,13 +6,17 @@
|
||||
(defrecord IntInclusiveDiscreteValueRange
|
||||
[^int start ^int end]
|
||||
range/DiscreteValueRange
|
||||
(abuts [_this other]
|
||||
(or (= 1 (abs (- start (.end other))))
|
||||
(= 1 (abs (- end (.start other))))))
|
||||
(range/abuts? [_this other]
|
||||
(or (= 1 (abs (- start (range/end other))))
|
||||
(= 1 (abs (- end (range/start other))))))
|
||||
(value-before [__this]
|
||||
(dec start))
|
||||
(if (= start Integer/MIN_VALUE)
|
||||
Integer/MIN_VALUE
|
||||
(dec start)))
|
||||
(value-after [_this]
|
||||
(inc end))
|
||||
(if (= end Integer/MAX_VALUE)
|
||||
Integer/MAX_VALUE
|
||||
(inc end)))
|
||||
(start [_this]
|
||||
start)
|
||||
(end [_this]
|
||||
@@ -20,12 +24,14 @@
|
||||
(range-type [_this]
|
||||
:int-range-inclusive)
|
||||
(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" {})))
|
||||
|
||||
(range/->discrete-value-range (.range-type this)
|
||||
(min start (.start other))
|
||||
(max end (.end other))))
|
||||
(range/->discrete-value-range (range/range-type this)
|
||||
(min start (range/start other))
|
||||
(max end (range/end other))))
|
||||
|
||||
Object
|
||||
(toString [_this]
|
||||
@@ -45,15 +51,15 @@
|
||||
(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 "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"
|
||||
@@ -134,260 +140,260 @@
|
||||
(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
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :filter-range
|
||||
:value 4
|
||||
:next-value 5
|
||||
:type :int-range-inclusive}]))))
|
||||
(into #{} (#'range/walk-tagged-range-boundaries-xf) [{:boundary-type :start
|
||||
:range-source-type :filter-range
|
||||
:value 1
|
||||
:prev-value 0
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :filter-range
|
||||
:value 4
|
||||
: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
|
||||
:prev-value 0
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 5
|
||||
:next-value 6
|
||||
:type :int-range-inclusive}])))
|
||||
(into #{} (#'range/walk-tagged-range-boundaries-xf) [{:boundary-type :start
|
||||
:range-source-type :source-range
|
||||
:value 1
|
||||
:prev-value 0
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 5
|
||||
:next-value 6
|
||||
: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
|
||||
:next-value 2
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 5
|
||||
:prev-value 4
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :start
|
||||
:range-source-type :source-range
|
||||
:value 11
|
||||
:next-value 12
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 15
|
||||
:prev-value 14
|
||||
:type :int-range-inclusive}]))))
|
||||
(into #{} (#'range/walk-tagged-range-boundaries-xf) [{:boundary-type :start
|
||||
:range-source-type :source-range
|
||||
:value 1
|
||||
:next-value 2
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 5
|
||||
:prev-value 4
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :start
|
||||
:range-source-type :source-range
|
||||
:value 11
|
||||
:next-value 12
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 15
|
||||
:prev-value 14
|
||||
:type :int-range-inclusive}]))))
|
||||
(testing "filter-ranges and source-ranges are the same"
|
||||
(is (= #{}
|
||||
(range/walk-range-boundaries [{:boundary-type :start
|
||||
:range-source-type :filter-range
|
||||
:value 1
|
||||
:prev-value 0
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :start
|
||||
:range-source-type :source-range
|
||||
:value 1
|
||||
:prev-value 0
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 5
|
||||
:next-value 6
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :filter-range
|
||||
:value 5
|
||||
:next-value 6
|
||||
:type :int-range-inclusive}]))))
|
||||
(into #{} (#'range/walk-tagged-range-boundaries-xf) [{:boundary-type :start
|
||||
:range-source-type :filter-range
|
||||
:value 1
|
||||
:prev-value 0
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :start
|
||||
:range-source-type :source-range
|
||||
:value 1
|
||||
:prev-value 0
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 5
|
||||
:next-value 6
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :filter-range
|
||||
:value 5
|
||||
:next-value 6
|
||||
: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
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :filter-range
|
||||
:value 5
|
||||
:next-value 6
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :start
|
||||
:range-source-type :source-range
|
||||
:value 11
|
||||
:prev-value 10
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 15
|
||||
:next-value 16
|
||||
:type :int-range-inclusive}]))))
|
||||
(into #{} (#'range/walk-tagged-range-boundaries-xf) [{:boundary-type :start
|
||||
:range-source-type :filter-range
|
||||
:value 1
|
||||
:prev-value 0
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :filter-range
|
||||
:value 5
|
||||
:next-value 6
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :start
|
||||
:range-source-type :source-range
|
||||
:value 11
|
||||
:prev-value 10
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 15
|
||||
:next-value 16
|
||||
: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
|
||||
:prev-value 0
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 5
|
||||
:next-value 6
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :start
|
||||
:range-source-type :filter-range
|
||||
:prev-value 10
|
||||
:value 11
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :filter-range
|
||||
:value 15
|
||||
:next-value 16
|
||||
:type :int-range-inclusive}]))))
|
||||
(into #{} (#'range/walk-tagged-range-boundaries-xf) [{:boundary-type :start
|
||||
:range-source-type :source-range
|
||||
:value 1
|
||||
:prev-value 0
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 5
|
||||
:next-value 6
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :start
|
||||
:range-source-type :filter-range
|
||||
:prev-value 10
|
||||
:value 11
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :filter-range
|
||||
: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
|
||||
:prev-value 0
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :start
|
||||
:range-source-type :filter-range
|
||||
:prev-value 5
|
||||
:value 6
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :filter-range
|
||||
:value 10
|
||||
:next-value 11
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 20
|
||||
:next-value 21
|
||||
:type :int-range-inclusive}]))))
|
||||
(into #{} (#'range/walk-tagged-range-boundaries-xf) [{:boundary-type :start
|
||||
:range-source-type :source-range
|
||||
:value 1
|
||||
:prev-value 0
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :start
|
||||
:range-source-type :filter-range
|
||||
:prev-value 5
|
||||
:value 6
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :filter-range
|
||||
:value 10
|
||||
:next-value 11
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 20
|
||||
:next-value 21
|
||||
:type :int-range-inclusive}]))))
|
||||
(testing "single filter-range between source-range but ends align"
|
||||
(is (= #{(int-range-inclusive 1 5)}
|
||||
(range/walk-range-boundaries [{:boundary-type :start
|
||||
:range-source-type :source-range
|
||||
:value 1
|
||||
:prev-value 0
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :start
|
||||
:range-source-type :filter-range
|
||||
:prev-value 5
|
||||
:value 6
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 10
|
||||
:next-value 11
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :filter-range
|
||||
:value 10
|
||||
:next-value 11
|
||||
:type :int-range-inclusive}]))))
|
||||
(into #{} (#'range/walk-tagged-range-boundaries-xf) [{:boundary-type :start
|
||||
:range-source-type :source-range
|
||||
:value 1
|
||||
:prev-value 0
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :start
|
||||
:range-source-type :filter-range
|
||||
:prev-value 5
|
||||
:value 6
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 10
|
||||
:next-value 11
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :filter-range
|
||||
:value 10
|
||||
:next-value 11
|
||||
: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
|
||||
:prev-value 0
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :start
|
||||
:range-source-type :filter-range
|
||||
:prev-value 5
|
||||
:value 6
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :filter-range
|
||||
:value 10
|
||||
:next-value 11
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :start
|
||||
:range-source-type :filter-range
|
||||
:prev-value 12
|
||||
:value 13
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :filter-range
|
||||
:value 19
|
||||
:next-value 20
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 20
|
||||
:prev-value 19
|
||||
:type :int-range-inclusive}]))))
|
||||
(into #{} (#'range/walk-tagged-range-boundaries-xf) [{:boundary-type :start
|
||||
:range-source-type :source-range
|
||||
:value 1
|
||||
:prev-value 0
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :start
|
||||
:range-source-type :filter-range
|
||||
:prev-value 5
|
||||
:value 6
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :filter-range
|
||||
:value 10
|
||||
:next-value 11
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :start
|
||||
:range-source-type :filter-range
|
||||
:prev-value 12
|
||||
:value 13
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :filter-range
|
||||
:value 19
|
||||
:next-value 20
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 20
|
||||
:prev-value 19
|
||||
: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
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :start
|
||||
:range-source-type :source-range
|
||||
:value 6
|
||||
:prev-value 5
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 10
|
||||
:next-value 11
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :start
|
||||
:range-source-type :source-range
|
||||
:value 13
|
||||
:prev-value 12
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 19
|
||||
:next-value 20
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :filter-range
|
||||
:value 20
|
||||
:next-value 21
|
||||
:type :int-range-inclusive}]))))
|
||||
(into #{} (#'range/walk-tagged-range-boundaries-xf) [{:boundary-type :start
|
||||
:range-source-type :filter-range
|
||||
:prev-value 1
|
||||
:value 1
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :start
|
||||
:range-source-type :source-range
|
||||
:value 6
|
||||
:prev-value 5
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 10
|
||||
:next-value 11
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :start
|
||||
:range-source-type :source-range
|
||||
:value 13
|
||||
:prev-value 12
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 19
|
||||
:next-value 20
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :filter-range
|
||||
: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
|
||||
:prev-value 0
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :start
|
||||
:range-source-type :filter-range
|
||||
:prev-value 4
|
||||
:value 5
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 6
|
||||
:next-value 7
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :start
|
||||
:range-source-type :source-range
|
||||
:value 8
|
||||
:prev-value 7
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :filter-range
|
||||
:value 10
|
||||
:next-value 11
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 13
|
||||
:next-value 14
|
||||
:type :int-range-inclusive}])))))
|
||||
(into #{} (#'range/walk-tagged-range-boundaries-xf) [{:boundary-type :start
|
||||
:range-source-type :source-range
|
||||
:value 1
|
||||
:prev-value 0
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :start
|
||||
:range-source-type :filter-range
|
||||
:prev-value 4
|
||||
:value 5
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 6
|
||||
:next-value 7
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :start
|
||||
:range-source-type :source-range
|
||||
:value 8
|
||||
:prev-value 7
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :filter-range
|
||||
:value 10
|
||||
:next-value 11
|
||||
:type :int-range-inclusive}
|
||||
{:boundary-type :end
|
||||
:range-source-type :source-range
|
||||
:value 13
|
||||
:next-value 14
|
||||
:type :int-range-inclusive}])))))
|
||||
|
||||
(deftest difference-test
|
||||
(testing "unary"
|
||||
|
||||
Reference in New Issue
Block a user