From 2f654bc1e2d0765f6da8b6ff9345d27c91eb696a Mon Sep 17 00:00:00 2001 From: Steven Proctor Date: Sat, 10 Jan 2026 16:53:52 -0600 Subject: [PATCH] start of protocol for discrete value ranges --- .gitignore | 3 ++ src/challenge/discrete_value_range.clj | 30 +++++++++++ test/challenge/discrete_value_range_test.clj | 53 ++++++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 src/challenge/discrete_value_range.clj create mode 100644 test/challenge/discrete_value_range_test.clj diff --git a/.gitignore b/.gitignore index 304db9f..b62ccdd 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ .cpcache +.clj-kondo/ +.lsp/ +.nrepl-port diff --git a/src/challenge/discrete_value_range.clj b/src/challenge/discrete_value_range.clj new file mode 100644 index 0000000..8193f30 --- /dev/null +++ b/src/challenge/discrete_value_range.clj @@ -0,0 +1,30 @@ +(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])) diff --git a/test/challenge/discrete_value_range_test.clj b/test/challenge/discrete_value_range_test.clj new file mode 100644 index 0000000..af1da3b --- /dev/null +++ b/test/challenge/discrete_value_range_test.clj @@ -0,0 +1,53 @@ +(ns challenge.discrete-value-range-test + (:require + [clojure.test :refer [deftest testing is]] + [challenge.discrete-value-range :as range])) + +(deftype IntInclusiveDiscreteValueRange + [^int start ^int end] + range/DiscreteValueRange + (abuts [_this other] + (or (= 1 (abs (- start (.end other)))) + (= 1 (abs (- end (.start other)))))) + (value-before [__this] + (dec start)) + (value-after [_this] + (inc end)) + (start [_this] + start) + (end [_this] + end) + + Object + (toString [_this] + (str start ".." end)) + (equals [_this other] + (and (= start (.start other)) + (= end (.end other))))) + +(defn int-range-inclusive [start end] + (assert (<= start end)) + (->IntInclusiveDiscreteValueRange start end)) + +(deftest integer-ranges-sanity-test + (testing "value equality" + (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 "value-before" + (is (= 0 (range/value-before (int-range-inclusive 1 8))))) + (testing "value-after" + (is (= 9 (range/value-after (int-range-inclusive 1 8))))) + (testing "start" + (is (= 1 (range/start (int-range-inclusive 1 8))))) + (testing "end" + (is (= 8 (range/end (int-range-inclusive 1 8))))))