IntervalOperations

Trait IntervalOperations 

Source
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, then A ∩ 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§

Source

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 (None is 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);
    }
}
Source

fn union<IntervalType: IntervalTrait<RealType = Self::RealType>>( &self, other: &IntervalType, ) -> IntervalUnion<Self::RealType>
where Interval<Self::RealType>: From<Self> + From<IntervalType>,

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
§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"),
}
Source

fn difference<IntervalType: IntervalTrait<RealType = Self::RealType>>( &self, other: &IntervalType, ) -> Option<IntervalDifference<Self::RealType>>
where Interval<Self::RealType>: From<Self> + From<IntervalType>,

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: If other completely contains self (empty difference)
  • Some(Single): If other doesn’t intersect or partially overlaps with self
  • Some(TwoDisjoint): If other splits self into 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 \ A in general
  • Non-associativity: (A \ B) \ C ≠ A \ (B \ C) in general
  • Identity: A \ ∅ = A (returns Some(Single(A))) and A \ A = ∅ (returns None)
  • Complement-like: When B ⊇ A, then A \ B = ∅ (returns None)
§Boundary Logic

The difference operation carefully handles boundary points:

  • If self includes a boundary point but other excludes 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 interval
  • Some(IntervalDifference::TwoDisjoint): Result is two separate intervals
§See Also

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.

Implementors§

Source§

impl<PartitionDomain1D: BuildIntervalInPartition> IntervalOperations for SubIntervalInPartition<PartitionDomain1D>

Source§

impl<RealType: RealScalar> IntervalOperations for IntervalFiniteLength<RealType>

Source§

impl<RealType: RealScalar> IntervalOperations for IntervalFinitePositiveLength<RealType>

Source§

impl<RealType: RealScalar> IntervalOperations for Interval<RealType>

Source§

impl<RealType: RealScalar> IntervalOperations for IntervalInfiniteLength<RealType>

Source§

impl<RealType: RealScalar> IntervalOperations for IntervalSingleton<RealType>

Source§

impl<RealType: RealScalar> IntervalOperations for IntervalLowerUnboundedUpperUnbounded<RealType>

Source§

impl<RealType: RealScalar, LowerBoundType: BoundType> IntervalOperations for IntervalLowerBoundedUpperUnbounded<RealType, LowerBoundType>
where Self: IntervalBoundsRuntime<RealType = RealType>, LowerBound<RealType, LowerBoundType>: ValueWithinBound<RealType = RealType>,

Source§

impl<RealType: RealScalar, LowerBoundType: BoundType, UpperBoundType: BoundType> IntervalOperations for IntervalBounded<RealType, LowerBoundType, UpperBoundType>
where Self: IntervalBoundsRuntime<RealType = RealType>, LowerBound<RealType, LowerBoundType>: ValueWithinBound<RealType = RealType>, UpperBound<RealType, UpperBoundType>: ValueWithinBound<RealType = RealType>,

Source§

impl<RealType: RealScalar, UpperBoundType: BoundType> IntervalOperations for IntervalLowerUnboundedUpperBounded<RealType, UpperBoundType>
where Self: IntervalBoundsRuntime<RealType = RealType>, UpperBound<RealType, UpperBoundType>: ValueWithinBound<RealType = RealType>,