diff --git a/src/challenge/core.clj b/src/challenge/core.clj index b55f937..b0df51f 100644 --- a/src/challenge/core.clj +++ b/src/challenge/core.clj @@ -24,13 +24,13 @@ (union [this other] (when-not (.isConnected this other) (throw (ex-info "Cannot union non-connecting ranges" {}))) - (.union this other)) - (before [^LocalDateRange this ^LocalDate x] - (.isBefore this x)) - (after [this ^LocalDate x] - (.isAfter (.getEndInclusive this) x))) + (.union this other))) + +(def ^:private closed-local-date-range + (memoize (fn [start end] + (LocalDateRange/ofClosed start end)))) (defmethod range/->discrete-value-range :local-date-range [_ start end] - (LocalDateRange/ofClosed start end)) + (closed-local-date-range start end)) (def difference range/difference) diff --git a/src/challenge/discrete_value_range.clj b/src/challenge/discrete_value_range.clj index fb9ff08..726e573 100644 --- a/src/challenge/discrete_value_range.clj +++ b/src/challenge/discrete_value_range.clj @@ -26,7 +26,7 @@ that before/after values against other values that the range is over. " - (abuts [this ^DiscreteValueRange other]) + (abuts [this other]) (value-before [this]) (value-after [this]) (start [this]) @@ -34,6 +34,18 @@ (range-type [this]) (union [this other])) +(defn ->range-boundaries* [range] + [{:value (start range) + :boundary-type :start + :type (range-type range) + :prev-value (value-before range)} + {:value (end range) + :boundary-type :end + :type (range-type range) + :next-value (value-after range)}]) + +(def ->range-boundaries (memoize ->range-boundaries*)) + (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 @@ -48,15 +60,7 @@ any intermediate matching starts/stops." [ranges] (->> ranges - (mapcat (fn [range] - [{:value (start range) - :boundary-type :start - :type (range-type range) - :prev-value (value-before range)} - {:value (end range) - :boundary-type :end - :type (range-type range) - :next-value (value-after range)}])) + (mapcat ->range-boundaries) (sort-by (juxt :value (comp {:start 0 :end 1} :boundary-type))))) (defmulti ->discrete-value-range (fn [range-type _start _end] @@ -77,25 +81,25 @@ end pairs encountered between the new start and end values." [] (fn [xf] - (let [stack (volatile! []) + (let [stack (java.util.Stack.) start (volatile! nil)] (fn ([] (xf)) ([result] (xf result)) - ([result input] + ([result {:keys [boundary-type value type] :as input}] (if (reduced? input) result - (case (:boundary-type input) - :start (do - (when-not (seq @stack) - (vreset! start (:value input))) - (vswap! stack (fnil conj []) input) - result) + (case boundary-type + :start (do + (when (.empty stack) + (vreset! start value)) + (.push stack input) + result) :end (do - (vswap! stack pop) - (if (seq @stack) + (.pop stack) + (if (not (.empty stack)) result - (xf result (->discrete-value-range (:type input) @start (:value input)))))))))))) + (xf result (->discrete-value-range type @start value))))))))))) (defn- combine-abutting-ranges "transducer to join ranges where the start and end of two ranges are consective @@ -124,9 +128,9 @@ (vreset! prev item) result) - :else (let [item @prev] - (vreset! prev input) - (xf result item)))))))) + :else (let [item @prev] + (vreset! prev input) + (xf result item)))))))) (def consolidate-ranges-xf (comp @@ -138,18 +142,17 @@ (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)))] - + (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 #{} + ranges (transient #{}) close-fn nil] (match [boundary-item] [nil] - ranges + (persistent! ranges) [{:boundary-type :start :range-source-type :source-range diff --git a/test/challenge/discrete_value_range_test.clj b/test/challenge/discrete_value_range_test.clj index ca7c86d..92fcaa5 100644 --- a/test/challenge/discrete_value_range_test.clj +++ b/test/challenge/discrete_value_range_test.clj @@ -31,10 +31,12 @@ (toString [_this] (str start ".." end))) -(defn int-range-inclusive [start end] +(defn- int-range-inclusive* [start end] (assert (<= start end) (str "start : " start "; end: " end)) (->IntInclusiveDiscreteValueRange start end)) +(def int-range-inclusive (memoize int-range-inclusive*)) + (defmethod range/->discrete-value-range :int-range-inclusive [_ start end] (int-range-inclusive start end))