pub trait IntervalOperations: Contains {
// Provided methods
fn intersection<IntervalType: IntervalTrait<RealType = Self::RealType>>(
&self,
other: &IntervalType,
) -> Option<Interval<Self::RealType>> { ... }
fn union<IntervalType: IntervalTrait<RealType = Self::RealType>>(
&self,
other: &IntervalType,
) -> IntervalUnion<Self::RealType>
where Interval<Self::RealType>: From<Self> + From<IntervalType> { ... }
fn difference<IntervalType: IntervalTrait<RealType = Self::RealType>>(
&self,
other: &IntervalType,
) -> Option<IntervalDifference<Self::RealType>>
where Interval<Self::RealType>: From<Self> + From<IntervalType> { ... }
}Expand description
Common trait for intervals with operations between different interval types.
The IntervalOperations trait provides the foundation for interval arithmetic
and set operations. It defines the core operations that can be performed between
intervals, such as intersection, while maintaining type safety and mathematical
correctness.
§Design Philosophy
This trait enables:
- Cross-type operations: Operations between different interval types
- Unified interface: Common operations across all interval implementations
- Type preservation: Results maintain appropriate mathematical types
- Performance: Operations are designed for efficiency
§Required Associated Types
RealType: The scalar type used for interval bounds and operations
§Core Operations
§Intersection
The primary operation defined by this trait is interval intersection, which computes the overlapping region between two intervals. The intersection algorithm automatically handles:
- Different boundary types (open/closed combinations)
- Boundary precedence (most restrictive boundary wins)
- Result type determination (returns most appropriate interval type)
- Edge cases (touching boundaries, disjoint intervals)
§Mathematical Properties
All implementations must maintain these mathematical properties:
- Commutativity:
A.intersection(B) = B.intersection(A) - Associativity:
(A ∩ B) ∩ C = A ∩ (B ∩ C) - Idempotence:
A.intersection(A) = Some(A) - Monotonicity: If
A ⊆ B, thenA ∩ C ⊆ B ∩ C
§Generic Programming
This trait enables writing generic functions that work with any interval type:
use grid1d::intervals::*;
use num_valid::RealScalar;
fn find_overlap<I1, I2, T>(a: &I1, b: &I2) -> Option<Interval<T>>
where
I1: IntervalOperations<RealType = T>,
I2: IntervalTrait<RealType = T>,
T: RealScalar,
{
a.intersection(b)
}§Implementation Requirements
Types implementing this trait must:
- Support intersection with any other interval type
- Return mathematically correct results
- Handle boundary conditions properly
- Maintain performance characteristics
Provided Methods§
Sourcefn intersection<IntervalType: IntervalTrait<RealType = Self::RealType>>(
&self,
other: &IntervalType,
) -> Option<Interval<Self::RealType>>
fn intersection<IntervalType: IntervalTrait<RealType = Self::RealType>>( &self, other: &IntervalType, ) -> Option<Interval<Self::RealType>>
Computes the intersection of this interval with another interval.
Returns Some(interval) containing the overlap if the intervals intersect,
or None if they are disjoint. The returned interval uses the most specific
type possible to represent the intersection.
§Mathematical Definition
For intervals A and B, their intersection A ∩ B is the set of all points that belong to both A and B. If no such points exist, the intersection is empty.
§Boundary Logic
The intersection algorithm follows these rules:
- Lower bound: Maximum of the two lower bounds, respecting open/closed semantics
- Upper bound: Minimum of the two upper bounds, respecting open/closed semantics
- Result type: Most restrictive combination of boundary types
§Special Cases
- Singleton result: When intervals meet at exactly one point
- Empty result: When intervals don’t overlap (
Noneis returned) - Unbounded result: When both intervals extend infinitely in the same direction
§Examples
use grid1d::intervals::*;
use try_create::New;
let a = IntervalClosed::new(0.0, 2.0); // [0., 2.]
let b = IntervalClosed::new(1.0, 3.0); // [1., 3.]
let intersection = a.intersection(&b).unwrap();
// Result: [1., 2.]
let expected_intersection = IntervalClosed::new(1., 2.); // [1., 2.]
assert!(matches!(intersection, Interval::FiniteLength(
IntervalFiniteLength::PositiveLength(
IntervalFinitePositiveLength::Closed(expected_intersection)))));
let c = IntervalOpen::new(0.0, 1.0); // (0., 1.)
let d = IntervalOpen::new(2.0, 3.0); // (2., 3.)
assert!(c.intersection(&d).is_none()); // Disjoint intervals
let e = IntervalClosed::new(0.0, 1.0); // [0., 1.]
let f = IntervalClosed::new(1.0, 2.0); // [1., 2.]
let singleton = e.intersection(&f).unwrap();
// Result: singleton at point 1.0
let expected_singleton = IntervalSingleton::new(1.); // [1.]
assert!(matches!(singleton, Interval::FiniteLength(
IntervalFiniteLength::ZeroLength(expected_singleton))));
let g = IntervalOpen::new(0.0, 1.0); // (0, 1)
let h = IntervalLowerClosedUpperOpen::new(0.5, 2.0); // [0.5, 2)
let mixed = g.intersection(&h).unwrap();
// Result: [0.5, 1) - takes most restrictive bounds
let expected_intersection = IntervalLowerClosedUpperOpen::new(0.5, 1.0); // [0.5, 1.)
assert!(matches!(mixed, Interval::FiniteLength(
IntervalFiniteLength::PositiveLength(
IntervalFinitePositiveLength::LowerClosedUpperOpen(expected_intersection)))));§Performance
This operation is O(1) in computation time. The result may require allocation for the returned interval, but the intersection logic itself is constant time.
§Return Type
The method always returns the most general Interval enum to accommodate
any possible intersection result. You can use TryFrom to convert to more
specific types if needed:
use grid1d::intervals::*;
let a = IntervalClosed::new(0.0, 2.0);
let b = IntervalClosed::new(1.0, 3.0);
if let Some(intersection) = a.intersection(&b) {
// Try to convert to specific type
if let Ok(closed) = IntervalClosed::try_from(intersection) {
println!("Intersection is a closed interval: {:?}", closed);
}
}Sourcefn union<IntervalType: IntervalTrait<RealType = Self::RealType>>(
&self,
other: &IntervalType,
) -> IntervalUnion<Self::RealType>
fn union<IntervalType: IntervalTrait<RealType = Self::RealType>>( &self, other: &IntervalType, ) -> IntervalUnion<Self::RealType>
Computes the union of this interval with another.
The union of two intervals can result in either a single connected interval
(if they overlap or touch) or a set of two disjoint intervals. This method
returns an IntervalUnion enum to represent these two possibilities.
§Mathematical Properties
- Commutativity:
A.union(B) == B.union(A) - Associativity:
(A.union(B)).union(C) == A.union(B.union(C)) - Identity:
A.union(∅) = A
§Return Value
IntervalUnion::SingleConnected: If the intervals overlap or are adjacent, their union is a single, larger interval.IntervalUnion::TwoDisjoint: If the intervals are separate, the union is a collection of two disjoint intervals.
§Examples
§Overlapping Intervals (Single Connected Union)
use grid1d::intervals::*;
let i1 = IntervalClosed::new(0.0, 2.0); // [0, 2]
let i2 = IntervalClosed::new(1.0, 3.0); // [1, 3]
let union = i1.union(&i2);
match union {
IntervalUnion::SingleConnected(result) => {
// The union is [0, 3]
let expected: Interval<f64> = IntervalClosed::new(0.0, 3.0).into();
assert_eq!(result, expected);
}
_ => panic!("Expected a single connected union"),
}§Disjoint Intervals
use grid1d::intervals::*;
let i1 = IntervalClosed::new(0.0, 1.0); // [0, 1]
let i2 = IntervalClosed::new(2.0, 3.0); // [2, 3]
let union = i1.union(&i2);
match union {
IntervalUnion::TwoDisjoint{ left, right } => {
assert_eq!(left, i1.into());
assert_eq!(right, i2.into());
}
_ => panic!("Expected a disjoint union"),
}§Touching Intervals (Single Connected Union)
use grid1d::intervals::*;
let i1 = IntervalLowerClosedUpperOpen::new(0.0, 1.0); // [0, 1)
let i2 = IntervalLowerClosedUpperUnbounded::new(1.0); // [1, +inf)
let union = i1.union(&i2);
match union {
IntervalUnion::SingleConnected(result) => {
// The union is [0, +inf)
let expected: Interval<f64> = IntervalLowerClosedUpperUnbounded::new(0.0).into();
assert_eq!(result, expected);
}
_ => panic!("Expected a single connected union"),
}Sourcefn difference<IntervalType: IntervalTrait<RealType = Self::RealType>>(
&self,
other: &IntervalType,
) -> Option<IntervalDifference<Self::RealType>>
fn difference<IntervalType: IntervalTrait<RealType = Self::RealType>>( &self, other: &IntervalType, ) -> Option<IntervalDifference<Self::RealType>>
Computes the set difference of this interval with another interval.
Returns the set of all points that are in self but not in other, denoted as A \ B
or A - B. The result can be:
None: Ifothercompletely containsself(empty difference)Some(Single): Ifotherdoesn’t intersect or partially overlaps withselfSome(TwoDisjoint): Ifothersplitsselfinto two parts
§Mathematical Definition
For intervals A and B, the set difference A \ B is defined as:
A \ B = { x : x ∈ A ∧ x ∉ B }§Properties
- Non-commutativity:
A \ B ≠ B \ Ain general - Non-associativity:
(A \ B) \ C ≠ A \ (B \ C)in general - Identity:
A \ ∅ = A(returnsSome(Single(A))) andA \ A = ∅(returnsNone) - Complement-like: When B ⊇ A, then
A \ B = ∅(returnsNone)
§Boundary Logic
The difference operation carefully handles boundary points:
- If
selfincludes a boundary point butotherexcludes it, the point remains - If both include or both exclude a boundary point, standard set logic applies
- Boundary transitions create appropriate open/closed bounds in the result
§Examples
§No Overlap (Result: Original Interval)
use grid1d::intervals::*;
let a = IntervalClosed::new(0.0, 2.0); // [0, 2]
let b = IntervalClosed::new(3.0, 5.0); // [3, 5]
if let Some(diff) = a.difference(&b) {
match diff {
IntervalDifference::SingleConnected(result) => {
// A \ B = [0, 2] since they don't overlap
let expected: Interval<f64> = IntervalClosed::new(0.0, 2.0).into();
assert_eq!(result, expected);
}
_ => panic!("Expected single interval"),
}
}§Partial Overlap (Result: Single Interval)
use grid1d::intervals::*;
let a = IntervalClosed::new(0.0, 3.0); // [0, 3]
let b = IntervalClosed::new(2.0, 5.0); // [2, 5]
if let Some(diff) = a.difference(&b) {
match diff {
IntervalDifference::SingleConnected(result) => {
// A \ B = [0, 2) - removes overlap [2, 3]
let expected: Interval<f64> = IntervalLowerClosedUpperOpen::new(0.0, 2.0).into();
assert_eq!(result, expected);
}
_ => panic!("Expected single interval"),
}
}§Complete Containment (Result: None)
use grid1d::intervals::*;
let a = IntervalClosed::new(1.0, 2.0); // [1, 2]
let b = IntervalClosed::new(0.0, 3.0); // [0, 3]
let diff = a.difference(&b);
assert!(diff.is_none()); // A \ B = ∅ since B contains A§Interior Removal (Result: Two Disjoint Intervals)
use grid1d::intervals::*;
let a = IntervalClosed::new(0.0, 4.0); // [0, 4]
let b = IntervalOpen::new(1.0, 3.0); // (1, 3)
if let Some(diff) = a.difference(&b) {
match diff {
IntervalDifference::TwoDisjoint { left, right } => {
// A \ B = [0, 1] ∪ [3, 4] - middle part removed
let expected_left: Interval<f64> = IntervalClosed::new(0.0, 1.0).into();
let expected_right: Interval<f64> = IntervalClosed::new(3.0, 4.0).into();
assert_eq!(left, expected_left);
assert_eq!(right, expected_right);
}
_ => panic!("Expected two disjoint intervals"),
}
}§Boundary Subtlety with Open/Closed Bounds
use grid1d::intervals::*;
let a = IntervalClosed::new(0.0, 2.0); // [0, 2]
let b = IntervalLowerOpenUpperClosed::new(1.0, 2.0); // (1, 2]
if let Some(diff) = a.difference(&b) {
match diff {
IntervalDifference::SingleConnected(result) => {
// A \ B = [0, 1] - boundary point 1.0 is kept since b excludes it
let expected: Interval<f64> = IntervalClosed::new(0.0, 1.0).into();
assert_eq!(result, expected);
}
_ => panic!("Expected single interval"),
}
}§With Unbounded Intervals
use grid1d::intervals::*;
let a = IntervalLowerClosedUpperUnbounded::new(0.0); // [0, +∞)
let b = IntervalClosed::new(5.0, 10.0); // [5, 10]
if let Some(diff) = a.difference(&b) {
match diff {
IntervalDifference::TwoDisjoint { left, right } => {
// A \ B = [0, 5) ∪ (10, +∞)
let expected_left: Interval<f64> =
IntervalLowerClosedUpperOpen::new(0.0, 5.0).into();
let expected_right: Interval<f64> =
IntervalLowerOpenUpperUnbounded::new(10.0).into();
assert_eq!(left, expected_left);
assert_eq!(right, expected_right);
}
_ => panic!("Expected two disjoint intervals"),
}
}§Performance
This operation is O(1) in computation time. The result may require allocation for the returned interval(s), but the difference logic itself is constant time.
§Return Type
Returns Option<IntervalDifference>:
None: No points remain after subtraction (empty difference)Some(IntervalDifference::SingleConnected): Result is a single intervalSome(IntervalDifference::TwoDisjoint): Result is two separate intervals
§See Also
intersection- Compute overlap between intervalsunion- Combine two intervalscontains_interval- Test if one interval contains another
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.