tests pass
This commit is contained in:
@@ -1,6 +1,36 @@
|
||||
(ns challenge.core
|
||||
(:require [clojure.set :as set])
|
||||
(:import (java.time LocalDate Period)
|
||||
(:require [challenge.discrete-value-range :as range])
|
||||
(:import (java.time LocalDate)
|
||||
(org.threeten.extra LocalDateRange)))
|
||||
|
||||
(defn difference [& input])
|
||||
(extend-type LocalDateRange
|
||||
range/DiscreteValueRange
|
||||
(abuts [this other]
|
||||
(.abuts this other))
|
||||
(value-before [this]
|
||||
(if (.isUnboundedStart this)
|
||||
(.getStart this)
|
||||
(.. this getStart (minusDays 1))))
|
||||
(value-after [this]
|
||||
(if (.isUnboundedEnd this)
|
||||
(.getEnd this)
|
||||
(.. this getEndInclusive (plusDays 1))))
|
||||
(start [this]
|
||||
(.getStart this))
|
||||
(end [this]
|
||||
(.getEndInclusive this))
|
||||
(range-type [_this]
|
||||
:local-date-range)
|
||||
(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)))
|
||||
|
||||
(defmethod range/->discrete-value-range :local-date-range [_ start end]
|
||||
(LocalDateRange/ofClosed start end))
|
||||
|
||||
(def difference range/difference)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
(ns challenge.discrete-value-range
|
||||
(:require [clojure.core.match :refer [match]]))
|
||||
(:require
|
||||
[clojure.core.match :refer [match]]
|
||||
[clojure.set :as set]))
|
||||
|
||||
(defprotocol DiscreteValueRange
|
||||
"A protocol for generic behavior on Ranges over
|
||||
@@ -180,3 +182,40 @@
|
||||
range-type
|
||||
new-working-range-start-value))
|
||||
(recur boundary-items false in-source-range ranges close-fn))))))
|
||||
|
||||
(def ^:private range-boundary-source-compare
|
||||
"Sorts range boundary items
|
||||
|
||||
Sorts by (in precidence order)
|
||||
1. :value; lower values first
|
||||
2. :boundary-type; :start before :end
|
||||
3. :range-source-type
|
||||
- filter-ranges should 'wrap' source ranges
|
||||
- for starts, the filter-ranges should be before source-ranges
|
||||
- and for ends, the source-ranges should be before the filter ranges"
|
||||
(juxt :value
|
||||
(comp {:start 0 :end 1} :boundary-type)
|
||||
(comp {[:filter-range :start] 0
|
||||
[:source-range :start] 1
|
||||
[:source-range :end] 2
|
||||
[:filter-range :end] 3}
|
||||
(juxt :range-source-type :boundary-type))))
|
||||
|
||||
(defn difference
|
||||
([range-set]
|
||||
(consolidate range-set))
|
||||
([range-set range-sets-to-remove]
|
||||
(let [range-set-boundaries (->> range-set
|
||||
consolidate
|
||||
ordered-range-values
|
||||
(map #(assoc % :range-source-type :source-range)))
|
||||
range-sets-to-remove-boundaries (->> range-sets-to-remove
|
||||
consolidate
|
||||
ordered-range-values
|
||||
(map #(assoc % :range-source-type :filter-range)))
|
||||
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)))
|
||||
([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)))))
|
||||
|
||||
Reference in New Issue
Block a user