pub enum Interval<A> {
Empty,
Closed {
lo: A,
hi: A,
},
}Expand description
An interval in a preordered set A.
An interval is a subset I ⊆ A closed under bracketing:
∀ x, y ∈ I, z ∈ A. x ≤ z ≤ y ⇒ z ∈ I.
The Empty variant arises naturally when Interval::new is
called with endpoints that are not preorder-comparable
(e.g. f64::NAN) or out of order.
§Equality vs containment
Interval carries two distinct relations on the same value:
PartialEq/Eqare structural: twoCloseds compare equal iff theirloandhifields are pairwise equal.Empty == Empty. Derived from the field-levelPartialEqimpl onA.PartialOrdis the containment preorder:Empty ≤ everything;Closed i₁ ≤ Closed i₂ ⟺ i₂ ⊇ i₁. TwoCloseds neither of which contains the other (e.g.[1, 4]vs[2, 5]) are incomparable, returningNone.
The two are intentionally different: structural equality is the
natural equality-up-to-constructor, while containment is the
natural lattice-theoretic order. They agree only at Some(Equal),
which corresponds to mutual containment (i.e. structural equality
of endpoints).
§Why Eq is sound for floating-point A
Eq requires reflexive equality: a == a for all a. For
A = f64, NaN breaks this. Soundness here rests on the
invariant that no Closed variant ever holds a non-reflexive
value, which holds because Interval::new preorder-checks its
inputs and routes any partial_cmp returning None to Empty.
Direct construction (Interval::Closed { lo: NAN, hi: NAN })
bypasses this gate; callers using the public field syntax must
preserve the invariant themselves.
§Examples
use connections::interval::Interval;
let i = Interval::new(1, 3);
assert!(i.contains(&2));
assert!(!i.contains(&5));
// Out-of-order endpoints collapse to Empty.
assert_eq!(Interval::new(3, 1), Interval::Empty);
// NaN is not preorder-comparable to itself, so an interval
// with NaN endpoints is Empty.
let nan = f64::NAN;
assert_eq!(Interval::new(nan, nan), Interval::<f64>::Empty);Variants§
Empty
The empty interval, containing nothing.
Closed
A non-empty closed interval [lo, hi] with lo ≤ hi.
Implementations§
Source§impl<A> Interval<A>
impl<A> Interval<A>
Sourcepub const fn empty() -> Self
pub const fn empty() -> Self
The empty interval. Equivalent to writing
Interval::Empty directly — a fn-form constructor for
callers that prefer it.
Sourcepub fn singleton(a: A) -> Selfwhere
A: Clone,
pub fn singleton(a: A) -> Selfwhere
A: Clone,
A singleton interval containing only a.
§Examples
use connections::interval::Interval;
let i = Interval::singleton(7_i32);
assert!(i.contains(&7));
assert!(!i.contains(&8));Sourcepub fn new(x: A, y: A) -> Selfwhere
A: PartialOrd,
pub fn new(x: A, y: A) -> Selfwhere
A: PartialOrd,
Construct an interval from a pair of endpoints. Endpoints
are preorder-checked, not sorted: if x ≤ y then
Closed { lo: x, hi: y }; otherwise (x > y or x and
y incomparable — e.g. an antichain pair in a partial
order) the result is Interval::Empty. Reversed
endpoints are not swapped.
§Examples
use connections::interval::Interval;
// In-order endpoints retained:
assert!(matches!(
Interval::new(1, 3),
Interval::Closed { lo: 1, hi: 3 }
));
// Reversed endpoints collapse:
assert_eq!(Interval::new(3, 1), Interval::<i32>::Empty);
// Equal endpoints produce a singleton.
assert!(matches!(
Interval::new(2, 2),
Interval::Closed { lo: 2, hi: 2 }
));Sourcepub fn endpts(self) -> Option<(A, A)>
pub fn endpts(self) -> Option<(A, A)>
Extract the endpoints of a bounded interval; returns None
for Interval::Empty.
Sourcepub fn contains(&self, p: &A) -> boolwhere
A: PartialOrd,
pub fn contains(&self, p: &A) -> boolwhere
A: PartialOrd,
True iff p lies in the closed interval [lo, hi].
Empty contains nothing.
§Examples
use connections::interval::Interval;
assert!(Interval::new(1, 3).contains(&2));
assert!(!Interval::<i32>::Empty.contains(&0));Sourcepub fn imap<B, F>(self, f: F) -> Interval<B>where
F: Fn(A) -> B,
B: PartialOrd,
pub fn imap<B, F>(self, f: F) -> Interval<B>where
F: Fn(A) -> B,
B: PartialOrd,
Map over an interval, re-sorting the result. A
non-monotonic f may collapse the result to
Interval::Empty because the new endpoints are re-checked
via Interval::new — this is intentional, and the same
behaviour as the Haskell original.
§Examples
use connections::interval::Interval;
// Monotone +1: endpoints map and remain in order.
assert!(matches!(
Interval::new(1_i32, 3).imap(|x| x + 1),
Interval::Closed { lo: 2, hi: 4 }
));
// Antimonotone negate over a non-singleton: lo and hi
// swap, so `Interval::new` sees a reversed pair and
// collapses to Empty.
assert_eq!(
Interval::new(1_i32, 3).imap(|x| -x),
Interval::<i32>::Empty
);
// Singletons survive any function.
assert!(matches!(
Interval::singleton(2_i32).imap(|x| -x),
Interval::Closed { lo: -2, hi: -2 }
));Trait Implementations§
impl<A: Copy> Copy for Interval<A>
impl<A: Eq> Eq for Interval<A>
Source§impl<A: PartialOrd> PartialOrd for Interval<A>
Containment preorder: Empty ≤ everything; Closed i₁ ≤ Closed i₂ ⟺ i₂ ⊇ i₁ (i.e. lo₂ ≤ lo₁ && hi₁ ≤ hi₂).
impl<A: PartialOrd> PartialOrd for Interval<A>
Containment preorder: Empty ≤ everything; Closed i₁ ≤ Closed i₂ ⟺ i₂ ⊇ i₁ (i.e. lo₂ ≤ lo₁ && hi₁ ≤ hi₂).
Two Closed intervals neither of which contains the other
(e.g. [1,4] vs [2,5]) are incomparable, returning None.
§Consistency with PartialEq / Eq
std::cmp::PartialOrd requires partial_cmp(a, b) == Some(Equal) ⟺ a == b. The two relations on Interval<A>
are intentionally different (containment vs structural), but
they agree at equality:
Empty.partial_cmp(&Empty) == Some(Equal)andEmpty == Empty. ✓Closed { lo: l₁, hi: h₁ }.partial_cmp(&Closed { lo: l₂, hi: h₂ }) == Some(Equal)iff(i₁ ⊆ i₂) ∧ (i₂ ⊆ i₁), which expands to(l₂ ≤ l₁ ∧ h₁ ≤ h₂) ∧ (l₁ ≤ l₂ ∧ h₂ ≤ h₁). ByPartialOrd’s antisymmetry onAthis givesl₁ = l₂ ∧ h₁ = h₂, i.e. structural equality, i.e.Closed{l₁,h₁} == Closed{l₂,h₂}. ✓
So the contract holds: structural eq and mutual containment coincide on every variant pair.