Files
range-difference/src/challenge/discrete_value_range.clj

55 lines
2.1 KiB
Clojure

(ns challenge.discrete-value-range)
(defprotocol DiscreteValueRange
"A protocol for generic behavior on Ranges over
discrete values.
This is for discrete values as we want to be
able to determine the value before the range
start and the value immediately after the range end.
By using discrete values we:
1. Determine if two ranges abut (touch) each other, and
can therefore be combined into a single range
2. Can combine overlapping by listing all ranges' start
and end values, and using a stack's push/pop functionality
to know if we are in a larger composite range, similiar
to parenthesis matching. e.g. If I encounter two range
start items, I know I should be expecting two end values,
even if the range is 'unbounded', there should be a MIN/MAX
value specified.
3. Know the discrete values for both before and or after the range
(even if a MIN/MAX marker) value that we can use to compare
that before/after values against other values that the range
is over.
"
(abuts [this ^DiscreteValueRange other])
(value-before [this])
(value-after [this])
(start [this])
(end [this])
(range-type [this]))
(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
set of ranges, by doing open/close count matching.
By building this list, it will allow us to push a `start`
onto a stack, and `pop` when we encounter an `end.`
If we pop, and get an empty stack, we have found the item
that represents the end value that matches the start value
that we just popped off the stack, and are able to ignore
any intermediate matching starts/stops."
[ranges]
(->> ranges
(mapcat (fn [range]
[{:value (start range)
:boundary-type :start
:type (range-type range)}
{:value (end range)
:boundary-type :end
:type (range-type range)}]))
(sort-by (juxt :value (comp {:start 0 :end 1} :boundary-type)))))