tur/range
Range: continuous interval with inclusive, exclusive, or unbounded ends.
Supports integer iteration and containment tests.
Since: Phase LZ4
Bound
(defdata Bound :copy (Inclusive int) (Exclusive int) (Unbounded))
a range endpoint: inclusive, exclusive, or unbounded.
The internal endpoint representation (range-gadt-typeclass-migration-plan B1). Declared :copy so a shared endpoint can be read by many predicates without affine move tracking complaining; see docs/archive/history/defgadt-copy-and-shared-bounds.md.
It was (defgadt Bound [A] ...) with every constructor returning (Bound int): the parameter was pinned to int and never used, so it is gone (adt-gadt-unification-plan slice 3). A parametric type's bare name is the uniform carrier, so the phantom parameter alone kept every endpoint a malloc'd cell.
Since: LZ4 (sum form: range-gadt-typeclass-migration-plan B1)
range-bound-new
(range-bound-new [inclusive :int value :int] :Bound)
build a Bound with given inclusivity and value.
| inclusive | 1 for inclusive, 0 for exclusive | |
| value | the endpoint value |
A Bound: Inclusive when inclusive=1, else Exclusive.
Since: LZ4
bound-unbounded?
(bound-unbounded? [b :Bound] :bool)
true if the endpoint is the Unbounded constructor.
| b | a Bound |
Since: range-gadt-typeclass-migration-plan B1
range-bound-inclusive?
(range-bound-inclusive? [b :Bound] :bool)
true if the bound is inclusive.
| b | a Bound (must not be Unbounded) |
Since: LZ4
range-bound-value
(range-bound-value [b :Bound] :int)
return the endpoint value of a bound.
| b | a Bound (Unbounded yields 0) |
Since: LZ4
range-bound-flip
(range-bound-flip [b :Bound] :Bound)
return a new bound with the same value but flipped inclusivity.
| b | a Bound (must not be Unbounded) |
Since: LZ4
Eq[Bound]
(definstance Eq [Bound])
structural equality on range endpoints.
Two bounds are equal when they share the same constructor and (for Inclusive / Exclusive) the same value. The Eq typeclass is auto-loaded, so this instance adds no new dependency to range.tur's wide auto-load path (range-gadt-typeclass-migration-plan B2).
(.eq? (Inclusive 3) (Inclusive 3)) ; => true (.eq? (Inclusive 3) (Exclusive 3)) ; => false
Since: range-gadt-typeclass-migration-plan B2
bound-show-fmt
(bound-show-fmt [kind :int v :int] :cstr)
Show[Bound]
(definstance Show [Bound])
render a lone endpoint: "[v" (Inclusive), "(v" (Exclusive), or "unbounded". This is the lower-side bracket form for a single endpoint; use range->str for a full interval like "[1, 5)".
Show lives in stdlib/typeclass.tur, loaded at the top of this file, so this instance can sit in Bound's own module as the orphan rule requires.
(show (Inclusive 3)) ; => "[3" (show (Unbounded)) ; => "unbounded"
Show [Bound] -- render a lone endpoint as an owned String: "[v" (Inclusive), "(v" (Exclusive), or "unbounded". Mixed borrow/malloc: the bracket arms adopt the bound-show-fmt malloc (leak-free), the Unbounded arm copies the borrowed literal via from-cstr.
Since: range-gadt-typeclass-migration-plan B2
bound-lt?
(bound-lt? [x :Bound y :Bound] :bool)
strict total order on Bound endpoints, backing Ord [Bound].
Orders endpoints by position on the number line: Unbounded is the least element (an open end extends past every bound), endpoints sort by value ascending, and at an equal value an Inclusive endpoint precedes an Exclusive one (the inclusive end covers its boundary point, so it lies "to the left"). This is a standalone total order; the directional set-operation selectors (range-lower-min / range-upper-max / ...) keep their own context-specific tie-breaking because "unbounded" means -inf on a lower side and +inf on an upper side -- a distinction a lone Bound cannot carry.
| x | the left Bound | |
| y | the right Bound |
true when x sorts strictly before y.
(bound-lt? (Unbounded) (Inclusive 0)) ; => true (bound-lt? (Inclusive 5) (Exclusive 5)) ; => true
Since: range-gadt-typeclass-migration-plan B2
Ord[Bound]
(definstance Ord [Bound])
total ordering on endpoints (see bound-lt? for semantics).
Range
(defstruct Range :copy [lower :Bound upper :Bound])
a continuous interval: a lower and an upper Bound.
A :copy value, so one range can be tested and combined any number of times. Build one with the constructors below (closed-range, ...) or range-new, and read its ends with range-lower / range-upper.
Since: LZ4 (a typed record: adt-gadt-unification-plan slice 3)
range-new
(range-new [lower :Bound upper :Bound] :Range)
build a Range from lower and upper Bound endpoints.
| lower | the lower Bound (Unbounded for unbounded below) | |
| upper | the upper Bound (Unbounded for unbounded above) |
The Range.
(range-contains? (range-new (Inclusive 1) (Exclusive 5)) 4) ; => true
Since: LZ4
range-lower
(range-lower [r :Range] :Bound)
return the lower Bound endpoint.
| r | a Range |
Since: LZ4
range-upper
(range-upper [r :Range] :Bound)
return the upper Bound endpoint.
| r | a Range |
Since: LZ4
range-abort-not-connected
(range-abort-not-connected :int)
range-abort-connected
(range-abort-connected :int)
range-abort-unbounded-lower
(range-abort-unbounded-lower :int)
closed-range
(closed-range [lo :int hi :int] :Range)
[lo, hi] both endpoints inclusive.
| lo | lower bound value | |
| hi | upper bound value |
A Range representing [lo, hi].
(closed-range 1 5) ; => [1, 5]
Since: LZ4
open-range
(open-range [lo :int hi :int] :Range)
(lo, hi) both endpoints exclusive.
| lo | lower bound value (excluded) | |
| hi | upper bound value (excluded) |
A Range representing (lo, hi).
(open-range 1 5) ; => (1, 5)
Since: LZ4
closed-open-range
(closed-open-range [lo :int hi :int] :Range)
[lo, hi) inclusive lower, exclusive upper.
| lo | lower bound value (included) | |
| hi | upper bound value (excluded) |
A Range representing [lo, hi).
(closed-open-range 1 5) ; => [1, 5)
Since: LZ4
open-closed-range
(open-closed-range [lo :int hi :int] :Range)
(lo, hi] exclusive lower, inclusive upper.
| lo | lower bound value (excluded) | |
| hi | upper bound value (included) |
A Range representing (lo, hi].
(open-closed-range 1 5) ; => (1, 5]
Since: LZ4
at-least-range
(at-least-range [lo :int] :Range)
[lo, +inf) inclusive lower, unbounded above.
| lo | lower bound value (included) |
A Range representing [lo, +inf).
(at-least-range 5) ; => [5, +inf)
Since: LZ4
greater-than-range
(greater-than-range [lo :int] :Range)
(lo, +inf) exclusive lower, unbounded above.
| lo | lower bound value (excluded) |
A Range representing (lo, +inf).
(greater-than-range 5) ; => (5, +inf)
Since: LZ4
at-most-range
(at-most-range [hi :int] :Range)
(-inf, hi] unbounded below, inclusive upper.
| hi | upper bound value (included) |
A Range representing (-inf, hi].
(at-most-range 5) ; => (-inf, 5]
Since: LZ4
less-than-range
(less-than-range [hi :int] :Range)
(-inf, hi) unbounded below, exclusive upper.
| hi | upper bound value (excluded) |
A Range representing (-inf, hi).
(less-than-range 5) ; => (-inf, 5)
Since: LZ4
singleton-range
(singleton-range [v :int] :Range)
[v, v] a range containing exactly one value.
| v | the single value |
A Range representing [v, v].
(singleton-range 3) ; => [3, 3]
Since: LZ4
unbounded-range
(unbounded-range :Range)
(-inf, +inf) no bounds at all.
A Range representing all values.
(unbounded-range) ; => (-inf, +inf)
Since: LZ4
range-val-in-lower?
(range-val-in-lower? [b :Bound v :int] :bool)
range-val-in-upper?
(range-val-in-upper? [b :Bound v :int] :bool)
range-bound-strictly-before?
(range-bound-strictly-before? [u :Bound l :Bound] :bool)
range-bound-has-gap?
(range-bound-has-gap? [u :Bound l :Bound] :bool)
range-contains?
(range-contains? [r :Range v :int] :bool)
true if value v falls within range r.
| r | a Range | |
| v | value to test |
true if v satisfies both the lower and upper bounds of r.
(range-contains? (closed-range 1 5) 3) ; => true (range-contains? (open-range 1 5) 1) ; => false
Since: LZ4
range-encloses?
(range-encloses? [r :Range other :Range] :bool)
true if every value in other is also in r.
| r | the outer Range | |
| other | the inner Range |
true if r's lower <= other's lower and r's upper >= other's upper (accounting for inclusive/exclusive endpoints).
(range-encloses? (closed-range 0 10) (closed-range 2 8)) ; => true
Since: LZ4
range-connected?
(range-connected? [r1 :Range r2 :Range] :bool)
true if no gap exists between r1 and r2.
Two ranges are connected if their union forms a contiguous interval (they overlap, or touch at a point where at least one side is inclusive).
| r1 | first Range | |
| r2 | second Range |
true if r1 and r2 are connected.
(range-connected? (closed-open-range 1 3) (closed-range 3 5)) ; => true (range-connected? (less-than-range 3) (greater-than-range 3)) ; => false
Since: LZ4
range-overlaps?
(range-overlaps? [r1 :Range r2 :Range] :bool)
true if r1 and r2 share at least one common value.
Stronger than range-connected?: touching ranges like [1,3) and [3,5] are connected but do not overlap (no value is in both).
| r1 | first Range | |
| r2 | second Range |
true if there exists a value contained in both r1 and r2.
(range-overlaps? (closed-range 1 3) (closed-range 3 5)) ; => true (range-overlaps? (closed-open-range 1 3) (closed-range 3 5)) ; => false
Since: LZ4
bounded-range?
(bounded-range? [r :Range] :bool)
true if both endpoints are present.
| r | a Range |
Since: LZ4
bounded-above?
(bounded-above? [r :Range] :bool)
true if r has an upper bound.
| r | a Range |
Since: LZ4
bounded-below?
(bounded-below? [r :Range] :bool)
true if r has a lower bound.
| r | a Range |
Since: LZ4
unbounded-above?
(unbounded-above? [r :Range] :bool)
true if r has no upper bound.
| r | a Range |
Since: LZ4
unbounded-below?
(unbounded-below? [r :Range] :bool)
true if r has no lower bound.
| r | a Range |
Since: LZ4
singleton-range?
(singleton-range? [r :Range] :bool)
true if r contains exactly one value.
A range is a singleton when both bounds are present, equal, and inclusive.
| r | a Range |
Since: LZ4
empty-range?
(empty-range? [r :Range] :bool)
true if r contains no values.
A bounded range is empty when the lower bound is strictly past the upper bound, or when both bounds share a value but at least one is exclusive.
| r | a Range |
Since: LZ4
nonempty-range?
(nonempty-range? [r :Range] :bool)
true if r contains at least one value.
| r | a Range |
Since: LZ4
range-lower-min
(range-lower-min [b1 :Bound b2 :Bound] :Bound)
range-upper-max
(range-upper-max [b1 :Bound b2 :Bound] :Bound)
range-lower-max
(range-lower-max [b1 :Bound b2 :Bound] :Bound)
range-upper-min
(range-upper-min [b1 :Bound b2 :Bound] :Bound)
range-span
(range-span [r1 :Range r2 :Range] :Range)
smallest range enclosing both r1 and r2 (convex hull).
| r1 | first Range | |
| r2 | second Range |
A Range whose lower bound is the min of both lowers and whose upper bound is the max of both uppers.
(range-span (closed-range 1 3) (closed-range 5 8)) ; => [1, 8]
Since: LZ4
range-intersection
(range-intersection [r1 :Range r2 :Range] :Range)
largest range enclosed by both r1 and r2.
The ranges must be connected (overlapping or touching); if they are not connected, aborts with an error.
| r1 | first Range | |
| r2 | second Range |
A Range representing the intersection of r1 and r2.
(range-intersection (closed-range 1 5) (closed-range 3 8)) ; => [3, 5]
Since: LZ4
range-gap
(range-gap [r1 :Range r2 :Range] :Range)
largest range lying strictly between r1 and r2.
The ranges must NOT be connected (a gap must exist); if they are connected, aborts with an error.
| r1 | first Range | |
| r2 | second Range |
A Range representing the gap between r1 and r2. The gap's bounds are the flipped endpoints of whichever range ends first and whichever starts second.
(range-gap (closed-range 1 3) (closed-range 5 8)) ; => (3, 5) (range-gap (closed-open-range 1 3) (open-range 3 8)) ; => [3, 3]
Since: LZ4
seq/from-range
(seq/from-range [r :Range] :int)
convert a bounded integer Range to a lazy Seq.
The lower bound must be present (range must not be unbounded below). An unbounded upper bound produces an infinite sequence.
| r | a Range (integer endpoints) |
A Seq yielding each integer in r in ascending order.
(seq-for-each println (seq/from-range (closed-range 1 4))) ; => 1 2 3 4 (seq-for-each println (seq/from-range (open-range 1 5))) ; => 2 3 4
Since: LZ4
seq/from-range-step
(seq/from-range-step [step :int r :Range] :int)
convert a bounded integer Range to a Seq with a custom step.
The lower bound must be present. Positive step iterates upward; negative step iterates downward (in which case supply a range with lo > hi or use at-most / at-least accordingly). Step must not be zero.
| step | increment per element (nonzero) | |
| r | a Range (integer endpoints) |
A Seq yielding each integer in r at the given step.
(seq-for-each println (seq/from-range-step 2 (closed-range 0 8))) ; => 0 2 4 6 8
Since: LZ4