grid1d/intervals/
operations.rs

1#![deny(rustdoc::broken_intra_doc_links)]
2
3use crate::{
4    Contains, Interval, IntervalTrait,
5    intervals::{
6        IntervalBoundsRuntime, LowerBoundRuntime, UpperBoundRuntime,
7        bounded::{IntervalFiniteLength, IntervalFinitePositiveLength},
8        unbounded::{IntervalInfiniteLength, IntervalLowerUnboundedUpperUnbounded},
9    },
10};
11use duplicate::duplicate_item;
12use num_valid::RealScalar;
13use serde::{Deserialize, Serialize};
14
15//------------------------------------------------------------------------------------------------
16/// Common trait for intervals with operations between different interval types.
17///
18/// The [`IntervalOperations`] trait provides the foundation for interval arithmetic
19/// and set operations. It defines the core operations that can be performed between
20/// intervals, such as intersection, while maintaining type safety and mathematical
21/// correctness.
22///
23/// ## Design Philosophy
24///
25/// This trait enables:
26/// - **Cross-type operations**: Operations between different interval types
27/// - **Unified interface**: Common operations across all interval implementations
28/// - **Type preservation**: Results maintain appropriate mathematical types
29/// - **Performance**: Operations are designed for efficiency
30///
31/// ## Required Associated Types
32///
33/// - `RealType`: The scalar type used for interval bounds and operations
34///
35/// ## Core Operations
36///
37/// ### Intersection
38/// The primary operation defined by this trait is interval intersection, which
39/// computes the overlapping region between two intervals. The intersection
40/// algorithm automatically handles:
41/// - Different boundary types (open/closed combinations)
42/// - Boundary precedence (most restrictive boundary wins)
43/// - Result type determination (returns most appropriate interval type)
44/// - Edge cases (touching boundaries, disjoint intervals)
45///
46/// ## Mathematical Properties
47///
48/// All implementations must maintain these mathematical properties:
49/// - **Commutativity**: `A.intersection(B) = B.intersection(A)`
50/// - **Associativity**: `(A ∩ B) ∩ C = A ∩ (B ∩ C)`
51/// - **Idempotence**: `A.intersection(A) = Some(A)`
52/// - **Monotonicity**: If `A ⊆ B`, then `A ∩ C ⊆ B ∩ C`
53///
54/// ## Generic Programming
55///
56/// This trait enables writing generic functions that work with any interval type:
57///
58/// ```rust
59/// use grid1d::intervals::*;
60/// use num_valid::RealScalar;
61///
62/// fn find_overlap<I1, I2, T>(a: &I1, b: &I2) -> Option<Interval<T>>
63/// where
64///     I1: IntervalOperations<RealType = T>,
65///     I2: IntervalTrait<RealType = T>,
66///     T: RealScalar,
67/// {
68///     a.intersection(b)
69/// }
70/// ```
71///
72/// ## Implementation Requirements
73///
74/// Types implementing this trait must:
75/// - Support intersection with any other interval type
76/// - Return mathematically correct results
77/// - Handle boundary conditions properly
78/// - Maintain performance characteristics
79pub trait IntervalOperations: Contains {
80    /// Computes the intersection of this interval with another interval.
81    ///
82    /// Returns `Some(interval)` containing the overlap if the intervals intersect,
83    /// or `None` if they are disjoint. The returned interval uses the most specific
84    /// type possible to represent the intersection.
85    ///
86    /// # Mathematical Definition
87    ///
88    /// For intervals A and B, their intersection A ∩ B is the set of all points
89    /// that belong to both A and B. If no such points exist, the intersection is empty.
90    ///
91    /// # Boundary Logic
92    ///
93    /// The intersection algorithm follows these rules:
94    /// - Lower bound: Maximum of the two lower bounds, respecting open/closed semantics
95    /// - Upper bound: Minimum of the two upper bounds, respecting open/closed semantics
96    /// - Result type: Most restrictive combination of boundary types
97    ///
98    /// # Special Cases
99    ///
100    /// - **Singleton result**: When intervals meet at exactly one point
101    /// - **Empty result**: When intervals don't overlap (`None` is returned)
102    /// - **Unbounded result**: When both intervals extend infinitely in the same direction
103    ///
104    /// # Examples
105    ///
106    /// ```rust
107    /// use grid1d::intervals::*;
108    /// use try_create::New;
109    ///
110    /// let a = IntervalClosed::new(0.0, 2.0);        // [0., 2.]
111    /// let b = IntervalClosed::new(1.0, 3.0);        // [1., 3.]
112    /// let intersection = a.intersection(&b).unwrap();
113    /// // Result: [1., 2.]
114    /// let expected_intersection = IntervalClosed::new(1., 2.); // [1., 2.]
115    /// assert!(matches!(intersection, Interval::FiniteLength(
116    ///     IntervalFiniteLength::PositiveLength(
117    ///         IntervalFinitePositiveLength::Closed(expected_intersection)))));
118    ///
119    /// let c = IntervalOpen::new(0.0, 1.0);          // (0., 1.)
120    /// let d = IntervalOpen::new(2.0, 3.0);          // (2., 3.)
121    /// assert!(c.intersection(&d).is_none());        // Disjoint intervals
122    ///
123    /// let e = IntervalClosed::new(0.0, 1.0);        // [0., 1.]
124    /// let f = IntervalClosed::new(1.0, 2.0);        // [1., 2.]
125    /// let singleton = e.intersection(&f).unwrap();
126    /// // Result: singleton at point 1.0
127    /// let expected_singleton = IntervalSingleton::new(1.); // [1.]
128    /// assert!(matches!(singleton, Interval::FiniteLength(
129    ///     IntervalFiniteLength::ZeroLength(expected_singleton))));
130    ///
131    /// let g = IntervalOpen::new(0.0, 1.0);          // (0, 1)
132    /// let h = IntervalLowerClosedUpperOpen::new(0.5, 2.0); // [0.5, 2)
133    /// let mixed = g.intersection(&h).unwrap();
134    /// // Result: [0.5, 1) - takes most restrictive bounds
135    /// let expected_intersection = IntervalLowerClosedUpperOpen::new(0.5, 1.0); // [0.5, 1.)
136    /// assert!(matches!(mixed, Interval::FiniteLength(
137    ///     IntervalFiniteLength::PositiveLength(
138    ///         IntervalFinitePositiveLength::LowerClosedUpperOpen(expected_intersection)))));
139    /// ```
140    ///
141    /// # Performance
142    ///
143    /// This operation is O(1) in computation time. The result may require allocation
144    /// for the returned interval, but the intersection logic itself is constant time.
145    ///
146    /// # Return Type
147    ///
148    /// The method always returns the most general [`Interval`] enum to accommodate
149    /// any possible intersection result. You can use [`TryFrom`] to convert to more
150    /// specific types if needed:
151    ///
152    /// ```rust
153    /// use grid1d::intervals::*;
154    ///
155    /// let a = IntervalClosed::new(0.0, 2.0);
156    /// let b = IntervalClosed::new(1.0, 3.0);
157    ///
158    /// if let Some(intersection) = a.intersection(&b) {
159    ///     // Try to convert to specific type
160    ///     if let Ok(closed) = IntervalClosed::try_from(intersection) {
161    ///         println!("Intersection is a closed interval: {:?}", closed);
162    ///     }
163    /// }
164    /// ```
165    fn intersection<IntervalType: IntervalTrait<RealType = Self::RealType>>(
166        &self,
167        other: &IntervalType,
168    ) -> Option<Interval<Self::RealType>> {
169        // get the maximum between the lower bounds of `self` and `other`
170        let lb_max = self.max_lower_bound(other);
171
172        // get the minimum between the upper bounds of `self` and `other`
173        let ub_min = self.min_upper_bound(other);
174
175        match (lb_max, ub_min) {
176            (Some(lower_bound), Some(upper_bound)) => {
177                // If the lower bound is less than the upper bound, we have a valid interval (i.e. a non-empty intersection).
178                // If the lower and upper bounds are both closed, a valid interval (a singleton)
179                // is built also when the value of the lower bound is equal to the value of the upper bound.
180                IntervalFiniteLength::try_new(lower_bound, upper_bound)
181                    .ok()
182                    .map(|interval| interval.into())
183            }
184            (None, Some(upper_bound)) => {
185                Some(IntervalInfiniteLength::new_lower_unbounded(upper_bound).into())
186            }
187            (Some(lower_bound), None) => {
188                Some(IntervalInfiniteLength::new_upper_unbounded(lower_bound).into())
189            }
190            (None, None) => {
191                // unbounded - unbounded case
192                Some(IntervalLowerUnboundedUpperUnbounded::new().into())
193            }
194        }
195    }
196
197    /// Computes the union of this interval with another.
198    ///
199    /// The union of two intervals can result in either a single connected interval
200    /// (if they overlap or touch) or a set of two disjoint intervals. This method
201    /// returns an [`IntervalUnion`] enum to represent these two possibilities.
202    ///
203    /// ## Mathematical Properties
204    ///
205    /// - **Commutativity**: `A.union(B) == B.union(A)`
206    /// - **Associativity**: `(A.union(B)).union(C) == A.union(B.union(C))`
207    /// - **Identity**: `A.union(∅) = A`
208    ///
209    /// ## Return Value
210    ///
211    /// - [`IntervalUnion::SingleConnected`]: If the intervals overlap or are adjacent,
212    ///   their union is a single, larger interval.
213    /// - [`IntervalUnion::TwoDisjoint`]: If the intervals are separate,
214    ///   the union is a collection of two disjoint intervals.
215    ///
216    /// ## Examples
217    ///
218    /// ### Overlapping Intervals (Single Connected Union)
219    /// ```rust
220    /// use grid1d::intervals::*;
221    ///
222    /// let i1 = IntervalClosed::new(0.0, 2.0); // [0, 2]
223    /// let i2 = IntervalClosed::new(1.0, 3.0); // [1, 3]
224    ///
225    /// let union = i1.union(&i2);
226    ///
227    /// match union {
228    ///     IntervalUnion::SingleConnected(result) => {
229    ///         // The union is [0, 3]
230    ///         let expected: Interval<f64> = IntervalClosed::new(0.0, 3.0).into();
231    ///         assert_eq!(result, expected);
232    ///     }
233    ///     _ => panic!("Expected a single connected union"),
234    /// }
235    /// ```
236    ///
237    /// ### Disjoint Intervals
238    /// ```rust
239    /// use grid1d::intervals::*;
240    ///
241    /// let i1 = IntervalClosed::new(0.0, 1.0); // [0, 1]
242    /// let i2 = IntervalClosed::new(2.0, 3.0); // [2, 3]
243    ///
244    /// let union = i1.union(&i2);
245    ///
246    /// match union {
247    ///     IntervalUnion::TwoDisjoint{ left, right } => {
248    ///         assert_eq!(left, i1.into());
249    ///         assert_eq!(right, i2.into());
250    ///     }
251    ///     _ => panic!("Expected a disjoint union"),
252    /// }
253    /// ```
254    ///
255    /// ### Touching Intervals (Single Connected Union)
256    /// ```rust
257    /// use grid1d::intervals::*;
258    ///
259    /// let i1 = IntervalLowerClosedUpperOpen::new(0.0, 1.0); // [0, 1)
260    /// let i2 = IntervalLowerClosedUpperUnbounded::new(1.0); // [1, +inf)
261    ///
262    /// let union = i1.union(&i2);
263    ///
264    /// match union {
265    ///     IntervalUnion::SingleConnected(result) => {
266    ///         // The union is [0, +inf)
267    ///         let expected: Interval<f64> = IntervalLowerClosedUpperUnbounded::new(0.0).into();
268    ///         assert_eq!(result, expected);
269    ///     }
270    ///     _ => panic!("Expected a single connected union"),
271    /// }
272    /// ```
273    fn union<IntervalType: IntervalTrait<RealType = Self::RealType>>(
274        &self,
275        other: &IntervalType,
276    ) -> IntervalUnion<Self::RealType>
277    where
278        Interval<Self::RealType>: From<Self> + From<IntervalType>,
279    {
280        let self_lb = self.lower_bound_runtime();
281        let self_ub = self.upper_bound_runtime();
282
283        let other_lb = other.lower_bound_runtime();
284        let other_ub = other.upper_bound_runtime();
285
286        if self_lb == other_lb && self_ub == other_ub {
287            // both intervals are identical
288            IntervalUnion::SingleConnected(self.clone().into())
289        } else {
290            // the intervals are not identical
291
292            let self_is_on_the_left = self_lb <= other_lb;
293
294            let a = (self_lb, self_ub);
295            let b = (other_lb, other_ub);
296
297            // order the intervals s.t. the left one is the one with lowest lower bound
298            let (left, right) = if self_is_on_the_left { (a, b) } else { (b, a) };
299
300            // check if the two intervals overlap
301            let upper_bound_left = &left.1;
302            let lower_bound_right = &right.0;
303
304            let intervals_touch_or_overlap = match (upper_bound_left, lower_bound_right) {
305                (None, _) | (_, None) => true,
306                // when the two bounds are open, there is overlap or touch
307                // only when the upper bound of the interval on the left
308                // is strictly bigger than the lower bound of the interval on the right
309                (Some(UpperBoundRuntime::Open(ub)), Some(LowerBoundRuntime::Open(lb))) => {
310                    ub.as_ref() > lb.as_ref()
311                }
312                // when a bound is closed there is a touch also when the values of bounds are equal
313                (Some(UpperBoundRuntime::Open(ub)), Some(LowerBoundRuntime::Closed(lb))) => {
314                    ub.as_ref() >= lb.as_ref()
315                }
316                (Some(UpperBoundRuntime::Closed(ub)), Some(LowerBoundRuntime::Open(lb))) => {
317                    ub.as_ref() >= lb.as_ref()
318                }
319                (Some(UpperBoundRuntime::Closed(ub)), Some(LowerBoundRuntime::Closed(lb))) => {
320                    ub.as_ref() >= lb.as_ref()
321                }
322            };
323
324            if intervals_touch_or_overlap {
325                let lower_bound = left.0;
326                let upper_bound = match (left.1, right.1) {
327                    (None, _) | (_, None) => {
328                        // the upper bound is unbounded
329                        None
330                    }
331                    (Some(ub_left), Some(ub_right)) => {
332                        Some(crate::bounds::max_upper_bound(ub_left, ub_right))
333                    }
334                };
335
336                IntervalUnion::SingleConnected(
337                    Interval::try_new(lower_bound, upper_bound)
338                        .expect("Failed to create interval for union"),
339                )
340            } else if self_is_on_the_left {
341                IntervalUnion::TwoDisjoint {
342                    left: self.clone().into(),
343                    right: other.clone().into(),
344                }
345            } else {
346                IntervalUnion::TwoDisjoint {
347                    left: other.clone().into(),
348                    right: self.clone().into(),
349                }
350            }
351        }
352    }
353
354    /// Computes the set difference of this interval with another interval.
355    ///
356    /// Returns the set of all points that are in `self` but not in `other`, denoted as `A \ B`
357    /// or `A - B`. The result can be:
358    /// - **`None`**: If `other` completely contains `self` (empty difference)
359    /// - **`Some(Single)`**: If `other` doesn't intersect or partially overlaps with `self`
360    /// - **`Some(TwoDisjoint)`**: If `other` splits `self` into two parts
361    ///
362    /// # Mathematical Definition
363    ///
364    /// For intervals A and B, the set difference A \ B is defined as:
365    /// ```text
366    /// A \ B = { x : x ∈ A ∧ x ∉ B }
367    /// ```
368    ///
369    /// # Properties
370    ///
371    /// - **Non-commutativity**: `A \ B ≠ B \ A` in general
372    /// - **Non-associativity**: `(A \ B) \ C ≠ A \ (B \ C)` in general
373    /// - **Identity**: `A \ ∅ = A` (returns `Some(Single(A))`) and `A \ A = ∅` (returns `None`)
374    /// - **Complement-like**: When B ⊇ A, then `A \ B = ∅` (returns `None`)
375    ///
376    /// # Boundary Logic
377    ///
378    /// The difference operation carefully handles boundary points:
379    /// - If `self` includes a boundary point but `other` excludes it, the point remains
380    /// - If both include or both exclude a boundary point, standard set logic applies
381    /// - Boundary transitions create appropriate open/closed bounds in the result
382    ///
383    /// # Examples
384    ///
385    /// ## No Overlap (Result: Original Interval)
386    ///
387    /// ```rust
388    /// use grid1d::intervals::*;
389    ///
390    /// let a = IntervalClosed::new(0.0, 2.0);    // [0, 2]
391    /// let b = IntervalClosed::new(3.0, 5.0);    // [3, 5]
392    ///
393    /// if let Some(diff) = a.difference(&b) {
394    ///     match diff {
395    ///         IntervalDifference::SingleConnected(result) => {
396    ///             // A \ B = [0, 2] since they don't overlap
397    ///             let expected: Interval<f64> = IntervalClosed::new(0.0, 2.0).into();
398    ///             assert_eq!(result, expected);
399    ///         }
400    ///         _ => panic!("Expected single interval"),
401    ///     }
402    /// }
403    /// ```
404    ///
405    /// ## Partial Overlap (Result: Single Interval)
406    ///
407    /// ```rust
408    /// use grid1d::intervals::*;
409    ///
410    /// let a = IntervalClosed::new(0.0, 3.0);    // [0, 3]
411    /// let b = IntervalClosed::new(2.0, 5.0);    // [2, 5]
412    ///
413    /// if let Some(diff) = a.difference(&b) {
414    ///     match diff {
415    ///         IntervalDifference::SingleConnected(result) => {
416    ///             // A \ B = [0, 2) - removes overlap [2, 3]
417    ///             let expected: Interval<f64> = IntervalLowerClosedUpperOpen::new(0.0, 2.0).into();
418    ///             assert_eq!(result, expected);
419    ///         }
420    ///         _ => panic!("Expected single interval"),
421    ///     }
422    /// }
423    /// ```
424    ///
425    /// ## Complete Containment (Result: None)
426    ///
427    /// ```rust
428    /// use grid1d::intervals::*;
429    ///
430    /// let a = IntervalClosed::new(1.0, 2.0);    // [1, 2]
431    /// let b = IntervalClosed::new(0.0, 3.0);    // [0, 3]
432    ///
433    /// let diff = a.difference(&b);
434    /// assert!(diff.is_none());  // A \ B = ∅ since B contains A
435    /// ```
436    ///
437    /// ## Interior Removal (Result: Two Disjoint Intervals)
438    ///
439    /// ```rust
440    /// use grid1d::intervals::*;
441    ///
442    /// let a = IntervalClosed::new(0.0, 4.0);    // [0, 4]
443    /// let b = IntervalOpen::new(1.0, 3.0);      // (1, 3)
444    ///
445    /// if let Some(diff) = a.difference(&b) {
446    ///     match diff {
447    ///         IntervalDifference::TwoDisjoint { left, right } => {
448    ///             // A \ B = [0, 1] ∪ [3, 4] - middle part removed
449    ///             let expected_left: Interval<f64> = IntervalClosed::new(0.0, 1.0).into();
450    ///             let expected_right: Interval<f64> = IntervalClosed::new(3.0, 4.0).into();
451    ///             assert_eq!(left, expected_left);
452    ///             assert_eq!(right, expected_right);
453    ///         }
454    ///         _ => panic!("Expected two disjoint intervals"),
455    ///     }
456    /// }
457    /// ```
458    ///
459    /// ## Boundary Subtlety with Open/Closed Bounds
460    ///
461    /// ```rust
462    /// use grid1d::intervals::*;
463    ///
464    /// let a = IntervalClosed::new(0.0, 2.0);        // [0, 2]
465    /// let b = IntervalLowerOpenUpperClosed::new(1.0, 2.0); // (1, 2]
466    ///
467    /// if let Some(diff) = a.difference(&b) {
468    ///     match diff {
469    ///         IntervalDifference::SingleConnected(result) => {
470    ///             // A \ B = [0, 1] - boundary point 1.0 is kept since b excludes it
471    ///             let expected: Interval<f64> = IntervalClosed::new(0.0, 1.0).into();
472    ///             assert_eq!(result, expected);
473    ///         }
474    ///         _ => panic!("Expected single interval"),
475    ///     }
476    /// }
477    /// ```
478    ///
479    /// ## With Unbounded Intervals
480    ///
481    /// ```rust
482    /// use grid1d::intervals::*;
483    ///
484    /// let a = IntervalLowerClosedUpperUnbounded::new(0.0); // [0, +∞)
485    /// let b = IntervalClosed::new(5.0, 10.0);              // [5, 10]
486    ///
487    /// if let Some(diff) = a.difference(&b) {
488    ///     match diff {
489    ///         IntervalDifference::TwoDisjoint { left, right } => {
490    ///             // A \ B = [0, 5) ∪ (10, +∞)
491    ///             let expected_left: Interval<f64> =
492    ///                 IntervalLowerClosedUpperOpen::new(0.0, 5.0).into();
493    ///             let expected_right: Interval<f64> =
494    ///                 IntervalLowerOpenUpperUnbounded::new(10.0).into();
495    ///             assert_eq!(left, expected_left);
496    ///             assert_eq!(right, expected_right);
497    ///         }
498    ///         _ => panic!("Expected two disjoint intervals"),
499    ///     }
500    /// }
501    /// ```
502    ///
503    /// # Performance
504    ///
505    /// This operation is O(1) in computation time. The result may require allocation
506    /// for the returned interval(s), but the difference logic itself is constant time.
507    ///
508    /// # Return Type
509    ///
510    /// Returns `Option<IntervalDifference>`:
511    /// - `None`: No points remain after subtraction (empty difference)
512    /// - `Some(IntervalDifference::SingleConnected)`: Result is a single interval
513    /// - `Some(IntervalDifference::TwoDisjoint)`: Result is two separate intervals
514    ///
515    /// # See Also
516    ///
517    /// - [`intersection`](Self::intersection) - Compute overlap between intervals
518    /// - [`union`](Self::union) - Combine two intervals
519    /// - [`contains_interval`](Contains::contains_interval) - Test if one interval contains another
520    fn difference<IntervalType: IntervalTrait<RealType = Self::RealType>>(
521        &self,
522        other: &IntervalType,
523    ) -> Option<IntervalDifference<Self::RealType>>
524    where
525        Interval<Self::RealType>: From<Self> + From<IntervalType>,
526    {
527        // Check if there's any intersection at all
528        let intersection = self.intersection(other);
529
530        if intersection.is_none() {
531            // No overlap: A \ B = A
532            return Some(IntervalDifference::SingleConnected(self.clone().into()));
533        }
534
535        // Get bounds of both intervals
536        let self_lb = self.lower_bound_runtime();
537        let self_ub = self.upper_bound_runtime();
538        let other_lb = other.lower_bound_runtime();
539        let other_ub = other.upper_bound_runtime();
540
541        // Check if other completely contains self
542        let other_contains_self_lower = match (&other_lb, &self_lb) {
543            (None, _) => true,  // other is unbounded below, contains any lower bound
544            (_, None) => false, // self is unbounded below, cannot be contained
545            (Some(other_lb_val), Some(self_lb_val)) => other_lb_val <= self_lb_val,
546        };
547
548        let other_contains_self_upper = match (&other_ub, &self_ub) {
549            (None, _) => true,  // other is unbounded above, contains any upper bound
550            (_, None) => false, // self is unbounded above, cannot be contained
551            (Some(other_ub_val), Some(self_ub_val)) => other_ub_val >= self_ub_val,
552        };
553
554        if other_contains_self_lower && other_contains_self_upper {
555            // other completely contains self: A \ B = ∅
556            return None;
557        }
558
559        // Determine which parts of self remain after subtracting other
560        let has_left_part = match (&self_lb, &other_lb) {
561            (None, None) => false,    // Both unbounded below, no left part
562            (None, Some(_)) => true,  // self unbounded, other bounded: left part exists
563            (Some(_), None) => false, // other unbounded below, covers entire left
564            (Some(self_lb_val), Some(other_lb_val)) => self_lb_val < other_lb_val,
565        };
566
567        let has_right_part = match (&self_ub, &other_ub) {
568            (None, None) => false,    // Both unbounded above, no right part
569            (None, Some(_)) => true,  // self unbounded, other bounded: right part exists
570            (Some(_), None) => false, // other unbounded above, covers entire right
571            (Some(self_ub_val), Some(other_ub_val)) => self_ub_val > other_ub_val,
572        };
573
574        match (has_left_part, has_right_part) {
575            (false, false) => {
576                unreachable!(
577                    "Invariant violation: if no intersection exists, we return early at line ~1161; \
578                     if other completely contains self, we return None at line ~1188. \
579                     Therefore, at least one of has_left_part or has_right_part must be true."
580                )
581            }
582            (true, false) => {
583                // Only left part remains: [self.lb, other.lb) or variant
584                // Invariant: self.lb < other.lb (from has_left_part=true)
585                // We flip other's lower bound to create the complementary upper bound
586                // If other has [b (closed), we need b) (open) to exclude what other includes
587                // If other has (b (open), we need b] (closed) to include what other excludes
588                let left_ub = other_lb
589                    .as_ref()
590                    .map(|lb| lb.clone().flip_bound_side_and_type());
591
592                // Use Interval::try_new() to handle edge cases like singletons (e.g., [2.0, 2.0])
593                Some(IntervalDifference::SingleConnected(
594                    Interval::try_new(self_lb, left_ub)
595                        .expect("Failed to create interval difference"),
596                ))
597            }
598            (false, true) => {
599                // Only right part remains: (other.ub, self.ub] or variant
600                // Invariant: other.ub < self.ub (from has_right_part=true)
601                // We flip other's upper bound to create the complementary lower bound
602                // If other has b] (closed), we need (b (open) to exclude what other includes
603                // If other has b) (open), we need [b (closed) to include what other excludes
604                let right_lb = other_ub
605                    .as_ref()
606                    .map(|ub| ub.clone().flip_bound_side_and_type());
607
608                Some(IntervalDifference::SingleConnected(
609                    Interval::try_new(right_lb, self_ub)
610                        .expect("Failed to create interval difference"),
611                ))
612            }
613            (true, true) => {
614                // Both left and right parts remain: [self.lb, other.lb) ∪ (other.ub, self.ub]
615                // Invariants:
616                // - self.lb < other.lb (from has_left_part=true)
617                // - other.ub < self.ub (from has_right_part=true)
618
619                // Flip both bounds to create complementary boundaries
620                // If other has [b, we need b); if other has (b, we need b]
621                let left_ub = other_lb
622                    .as_ref()
623                    .map(|lb| lb.clone().flip_bound_side_and_type());
624                let right_lb = other_ub
625                    .as_ref()
626                    .map(|ub| ub.clone().flip_bound_side_and_type());
627
628                // Both intervals are guaranteed to be valid because:
629                // - has_left_part = true guarantees self.lb < other.lb, so left interval is non-degenerate
630                // - has_right_part = true guarantees other.ub < self.ub, so right interval is non-degenerate
631                let left = Interval::try_new(self_lb.clone(), left_ub)
632                    .expect("Left interval is guaranteed valid when has_left_part = true");
633                let right = Interval::try_new(right_lb, self_ub.clone())
634                    .expect("Right interval is guaranteed valid when has_right_part = true");
635                Some(IntervalDifference::TwoDisjoint { left, right })
636            }
637        }
638    }
639}
640//------------------------------------------------------------------------------------------------
641
642//------------------------------------------------------------------------------------------------
643/// Represents the union of two intervals.
644///
645/// The union of two intervals `A ∪ B` contains all points that belong to either `A` or `B`
646/// (or both). Depending on the relationship between the intervals, the result can be:
647///
648/// - **[`SingleConnected`]**: If the intervals overlap or touch, their union is a single
649///   continuous interval
650/// - **[`TwoDisjoint`]**: If the intervals are separated by a gap, the union consists of
651///   two disjoint intervals
652///
653/// ## Mathematical Foundation
654///
655/// For intervals A and B, the union is defined as:
656/// ```text
657/// A ∪ B = { x : x ∈ A ∨ x ∈ B }
658/// ```
659///
660/// ## Properties
661///
662/// - **Commutativity**: `A ∪ B = B ∪ A`
663/// - **Associativity**: `(A ∪ B) ∪ C = A ∪ (B ∪ C)`
664/// - **Identity**: `A ∪ ∅ = A`
665/// - **Idempotence**: `A ∪ A = A`
666///
667/// ## Visual Examples
668///
669/// ```text
670/// Case 1: Overlapping intervals → SingleConnected
671///   A:   [------]
672///   B:       [------]
673///   A∪B: [----------]
674///
675/// Case 2: Touching intervals → SingleConnected
676///   A:   [-----)
677///   B:        [------]
678///   A∪B: [----------]
679///
680/// Case 3: Disjoint intervals → TwoDisjoint
681///   A:   [----]
682///   B:            [----]
683///   A∪B: [----]   [----]
684/// ```
685///
686/// ## Usage Example
687///
688/// ```rust
689/// use grid1d::intervals::*;
690///
691/// // Overlapping intervals produce a single connected union
692/// let a = IntervalClosed::new(0.0, 2.0);
693/// let b = IntervalClosed::new(1.0, 3.0);
694/// let union = a.union(&b);
695///
696/// match union {
697///     IntervalUnion::SingleConnected(interval) => {
698///         println!("Union is a single interval: {:?}", interval);
699///     }
700///     IntervalUnion::TwoDisjoint { left, right } => {
701///         println!("Union is two intervals: {:?} and {:?}", left, right);
702///     }
703/// }
704/// ```
705///
706/// ## See Also
707///
708/// - [`IntervalDifference`] - Result of set difference operation
709/// - [`union`](IntervalOperations::union) - Method to compute unions
710/// - [`gap`](IntervalUnion::gap) - Get the gap between disjoint intervals
711///
712/// [`SingleConnected`]: IntervalUnion::SingleConnected
713/// [`TwoDisjoint`]: IntervalUnion::TwoDisjoint
714#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
715#[serde(bound(deserialize = "RealType: for<'a> Deserialize<'a>"))]
716pub enum IntervalUnion<RealType: RealScalar> {
717    /// A single connected interval resulting from overlapping or touching intervals.
718    ///
719    /// This variant represents the case where the union of two intervals produces
720    /// one continuous interval with no gaps.
721    SingleConnected(Interval<RealType>),
722
723    /// Two disjoint intervals separated by a gap.
724    ///
725    /// This variant represents the case where the intervals are separated and cannot
726    /// be merged into a single continuous interval. Use [`gap()`](IntervalUnion::gap)
727    /// to compute the interval representing the gap between them.
728    TwoDisjoint {
729        /// The left interval (with smaller bounds)
730        left: Interval<RealType>,
731        /// The right interval (with larger bounds)  
732        right: Interval<RealType>,
733    },
734}
735
736//------------------------------------------------------------------------------------------------
737/// Represents the result of a set difference operation between two intervals.
738///
739/// The difference `A \ B` (read as "A minus B" or "A without B") contains all points
740/// that belong to `A` but not to `B`. Depending on how the intervals overlap, the
741/// result can be:
742///
743/// - **[`SingleConnected`]**: The result is a single continuous interval
744/// - **[`TwoDisjoint`]**: Removing `B` creates a gap in `A`, resulting in two separate intervals
745/// - **`None`**: If `B` completely contains `A`, the difference is empty
746///
747/// ## Mathematical Foundation
748///
749/// For intervals A and B, the set difference is defined as:
750/// ```text
751/// A \ B = { x : x ∈ A ∧ x ∉ B }
752/// ```
753///
754/// ## Properties
755///
756/// - **Non-commutativity**: `A \ B ≠ B \ A` in general
757/// - **Identity**: `A \ ∅ = A` and `A \ A = ∅`
758/// - **Complement**: When `B ⊇ A`, then `A \ B = ∅` (returns `None`)
759/// - **Distributivity**: `A \ (B ∪ C) = (A \ B) ∩ (A \ C)`
760///
761/// ## Visual Examples
762///
763/// ```text
764/// Case 1: No overlap → SingleConnected (original interval)
765///   A:     [----]
766///   B:           [----]
767///   A\B:   [----]
768///
769/// Case 2: Partial overlap → SingleConnected (truncated)
770///   A:   [--------]
771///   B:        [--------]
772///   A\B: [----)
773///
774/// Case 3: Interior removal → TwoDisjoint
775///   A:   [------------]
776///   B:      [----]
777///   A\B: [--)    (----]
778///
779/// Case 4: Complete containment → None (empty)
780///   A:     [----]
781///   B:   [----------]
782///   A\B:   (empty)
783/// ```
784///
785/// ## Usage Example
786///
787/// ```rust
788/// use grid1d::intervals::*;
789///
790/// let a = IntervalClosed::new(0.0, 4.0);
791/// let b = IntervalOpen::new(1.0, 3.0);
792///
793/// if let Some(diff) = a.difference(&b) {
794///     match diff {
795///         IntervalDifference::SingleConnected(interval) => {
796///             println!("Difference is a single interval: {:?}", interval);
797///         }
798///         IntervalDifference::TwoDisjoint { left, right } => {
799///             println!("Difference is two intervals: {:?} and {:?}", left, right);
800///         }
801///     }
802/// } else {
803///     println!("No points remain (empty difference)");
804/// }
805/// ```
806///
807/// ## See Also
808///
809/// - [`IntervalUnion`] - Result of union operation
810/// - [`difference`](IntervalOperations::difference) - Method to compute differences
811/// - [`to_intervals`](IntervalDifference::to_intervals) - Convert to vector of intervals
812///
813/// [`SingleConnected`]: IntervalDifference::SingleConnected
814/// [`TwoDisjoint`]: IntervalDifference::TwoDisjoint
815#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
816#[serde(bound(deserialize = "RealType: for<'a> Deserialize<'a>"))]
817pub enum IntervalDifference<RealType: RealScalar> {
818    /// A single connected interval resulting from the difference operation.
819    ///
820    /// This variant represents cases where removing `B` from `A` leaves a
821    /// single continuous interval (including the case where `B` doesn't overlap `A` at all).
822    SingleConnected(Interval<RealType>),
823
824    /// Two disjoint intervals resulting from removing an interior portion.
825    ///
826    /// This variant represents the case where `B` removes a middle section of `A`,
827    /// creating two separate intervals. Use [`to_intervals()`](IntervalDifference::to_intervals)
828    /// to collect both intervals into a vector.
829    TwoDisjoint {
830        /// The left interval (with smaller bounds)
831        left: Interval<RealType>,
832        /// The right interval (with larger bounds)
833        right: Interval<RealType>,
834    },
835}
836
837// Use duplicate_item to generate common implementations for both enums
838#[duplicate_item(
839    EnumName             contains_doc;
840    [IntervalUnion]      ["contained in this union"];
841    [IntervalDifference] ["contained in this difference result"];
842)]
843impl<RealType: RealScalar> EnumName<RealType> {
844    /// Returns `true` if the result is a single connected interval.
845    pub fn is_single_connected(&self) -> bool {
846        matches!(self, Self::SingleConnected(_))
847    }
848
849    /// Returns `true` if the result is two disjoint intervals.
850    pub fn is_two_disjoint(&self) -> bool {
851        matches!(self, Self::TwoDisjoint { .. })
852    }
853
854    /// Returns the single interval if the result is a single connected interval.
855    pub fn as_single_connected(&self) -> Option<&Interval<RealType>> {
856        match self {
857            Self::SingleConnected(interval) => Some(interval),
858            _ => None,
859        }
860    }
861
862    /// Returns the two intervals if the result is two disjoint intervals.
863    pub fn as_two_disjoint(&self) -> Option<(&Interval<RealType>, &Interval<RealType>)> {
864        match self {
865            Self::TwoDisjoint { left, right } => Some((left, right)),
866            _ => None,
867        }
868    }
869
870    #[doc = concat!("Checks if a point is ", contains_doc, ".")]
871    pub fn contains_point(&self, x: &RealType) -> bool {
872        match self {
873            Self::SingleConnected(interval) => interval.contains_point(x),
874            Self::TwoDisjoint { left, right } => left.contains_point(x) || right.contains_point(x),
875        }
876    }
877
878    /// For disjoint intervals, returns the gap between them as an interval.
879    /// Returns `None` if the union is a single connected interval.
880    ///
881    /// The gap represents the space between the two disjoint intervals,
882    /// with boundary types "flipped" to exclude the endpoints that belong to the original intervals.
883    pub fn gap(&self) -> Option<IntervalFinitePositiveLength<RealType>> {
884        match self {
885            Self::SingleConnected(_) => None,
886            Self::TwoDisjoint { left, right } => {
887                // The gap is from left.upper_bound to right.lower_bound
888                let left_ub = left
889                    .upper_bound_runtime()
890                    .expect("The left interval in a disjoint union cannot be unbounded above");
891                let right_lb = right
892                    .lower_bound_runtime()
893                    .expect("The right interval in a disjoint union cannot be unbounded below");
894
895                // Flip the boundary types and sides to create the gap interval
896                // The gap excludes the endpoints of the touching intervals
897                // If left ends with a], gap starts with (a
898                // If left ends with a), gap starts with [a
899                // If right starts with [b, gap ends with b)
900                // If right starts with (b, gap ends with b]
901                let lb = left_ub.flip_bound_side_and_type();
902                let ub = right_lb.flip_bound_side_and_type();
903
904                // Create the appropriate interval type based on boundary types
905                Some(
906                    IntervalFinitePositiveLength::try_new(lb, ub)
907                        .expect("Failed to create the interval representing the gap!"),
908                )
909            }
910        }
911    }
912
913    /// Collects all intervals in the difference result into a vector.
914    ///
915    /// This is useful when you need to process all resulting intervals uniformly,
916    /// regardless of whether the result is single connected or two disjoint.
917    ///
918    /// # Returns
919    ///
920    /// - Single-element vector for `SingleConnected`
921    /// - Two-element vector for `TwoDisjoint`
922    ///
923    /// # Examples
924    ///
925    /// ```rust
926    /// use grid1d::intervals::*;
927    ///
928    /// let a = IntervalClosed::new(0.0, 4.0);
929    /// let b = IntervalOpen::new(1.0, 3.0);
930    ///
931    /// if let Some(diff) = a.difference(&b) {
932    ///     let intervals = diff.to_intervals();
933    ///     assert_eq!(intervals.len(), 2);
934    ///     for interval in intervals {
935    ///         println!("Interval: {:?}", interval);
936    ///     }
937    /// }
938    /// ```
939    pub fn to_intervals(self) -> Vec<Interval<RealType>> {
940        match self {
941            Self::SingleConnected(interval) => vec![interval],
942            Self::TwoDisjoint { left, right } => vec![left, right],
943        }
944    }
945}
946
947// Additional documentation for IntervalDifference (generated above by duplicate_item)
948/// ## IntervalDifference Documentation
949///
950/// Represents the result of a set difference operation between two intervals.
951///
952/// When computing `A \ B` (read as "A minus B" or "A without B"), the result can take
953/// one of two forms depending on how the intervals overlap:
954///
955/// - **SingleConnected**: The result is a single continuous interval
956/// - **TwoDisjoint**: B removes a middle section of A, creating two separate intervals
957///
958/// If the result is empty (B completely contains A), the `difference` method returns `None`.
959///
960/// ## Mathematical Foundation
961///
962/// For intervals A and B, the set difference is defined as:
963/// ```text
964/// A \ B = { x : x ∈ A ∧ x ∉ B }
965/// ```
966///
967/// ## Visual Examples
968///
969/// ```text
970/// Case 1: Empty (B contains A) → None
971///   A:     [----]
972///   B:   [----------]
973///   A\B:   (empty)
974///
975/// Case 2: SingleConnected (partial overlap) → Some(SingleConnected)
976///   A:   [--------]
977///   B:        [--------]
978///   A\B: [----)
979///
980/// Case 3: TwoDisjoint (B in middle of A) → Some(TwoDisjoint)
981///   A:   [------------]
982///   B:      [----]
983///   A\B: [--)    (----]
984/// ```
985///
986/// ## Properties
987///
988/// - **Non-commutativity**: `A \ B ≠ B \ A` in general
989/// - **Identity**: `A \ ∅ = A` and `A \ A = ∅` (returns `None`)
990/// - **Complement**: When B ⊇ A, then `A \ B = ∅` (returns `None`)
991///
992/// ## Usage Example
993///
994/// ```rust
995/// use grid1d::intervals::*;
996///
997/// let a = IntervalClosed::new(0.0, 4.0);
998/// let b = IntervalOpen::new(1.0, 3.0);
999///
1000/// if let Some(result) = a.difference(&b) {
1001///     match result {
1002///         IntervalDifference::SingleConnected(interval) => {
1003///             println!("Result is a single interval: {:?}", interval);
1004///         }
1005///         IntervalDifference::TwoDisjoint { left, right } => {
1006///             println!("Result is two intervals: {:?} and {:?}", left, right);
1007///         }
1008///     }
1009/// } else {
1010///     println!("No points remain (empty difference)");
1011/// }
1012/// ```
1013// Specialized implementation for IntervalDifference only
1014impl<RealType: RealScalar> IntervalDifference<RealType> {}
1015//------------------------------------------------------------------------------------------------
1016
1017#[cfg(test)]
1018mod tests {
1019    use super::*;
1020    use crate::{
1021        SubIntervalInPartition,
1022        intervals::{
1023            GetLowerBoundValue, GetUpperBoundValue, Interval, IntervalClosed,
1024            IntervalFinitePositiveLengthTrait, IntervalLowerClosedUpperOpen,
1025            IntervalLowerClosedUpperUnbounded, IntervalLowerOpenUpperClosed,
1026            IntervalLowerOpenUpperUnbounded, IntervalLowerUnboundedUpperClosed,
1027            IntervalLowerUnboundedUpperOpen, IntervalLowerUnboundedUpperUnbounded, IntervalOpen,
1028            IntervalSingleton,
1029        },
1030    };
1031    use num_valid::scalars::PositiveRealScalar;
1032    use try_create::{New, TryNew};
1033
1034    mod difference {
1035        use super::*;
1036
1037        #[test]
1038        fn difference_no_overlap() {
1039            // A = [0, 2], B = [3, 5]
1040            // A \ B = [0, 2]
1041            let a = IntervalClosed::new(0.0, 2.0);
1042            let b = IntervalClosed::new(3.0, 5.0);
1043            let diff = a.difference(&b);
1044
1045            assert!(diff.is_some());
1046            match diff.unwrap() {
1047                IntervalDifference::SingleConnected(result) => {
1048                    let expected: Interval<f64> = IntervalClosed::new(0.0, 2.0).into();
1049                    assert_eq!(result, expected);
1050                }
1051                _ => panic!("Expected single interval"),
1052            }
1053        }
1054
1055        #[test]
1056        fn difference_partial_overlap_right() {
1057            // A = [0, 3], B = [2, 5]
1058            // A \ B = [0, 2)
1059            let a = IntervalClosed::new(0.0, 3.0);
1060            let b = IntervalClosed::new(2.0, 5.0);
1061            let diff = a.difference(&b);
1062
1063            assert!(diff.is_some());
1064            match diff.unwrap() {
1065                IntervalDifference::SingleConnected(result) => {
1066                    let expected: Interval<f64> =
1067                        IntervalLowerClosedUpperOpen::new(0.0, 2.0).into();
1068                    assert_eq!(result, expected);
1069                }
1070                _ => panic!("Expected single interval"),
1071            }
1072        }
1073
1074        #[test]
1075        fn difference_partial_overlap_left() {
1076            // A = [2, 5], B = [0, 3]
1077            // A \ B = (3, 5]
1078            let a = IntervalClosed::new(2.0, 5.0);
1079            let b = IntervalClosed::new(0.0, 3.0);
1080            let diff = a.difference(&b);
1081
1082            assert!(diff.is_some());
1083            match diff.unwrap() {
1084                IntervalDifference::SingleConnected(result) => {
1085                    let expected: Interval<f64> =
1086                        IntervalLowerOpenUpperClosed::new(3.0, 5.0).into();
1087                    assert_eq!(result, expected);
1088                }
1089                _ => panic!("Expected single interval"),
1090            }
1091        }
1092
1093        #[test]
1094        fn difference_complete_containment() {
1095            // A = [1, 2], B = [0, 3]
1096            // A \ B = ∅
1097            let a = IntervalClosed::new(1.0, 2.0);
1098            let b = IntervalClosed::new(0.0, 3.0);
1099            let diff = a.difference(&b);
1100
1101            assert!(diff.is_none());
1102        }
1103
1104        #[test]
1105        fn difference_interior_removal() {
1106            // A = [0, 4], B = (1, 3)
1107            // A \ B = [0, 1] ∪ [3, 4]
1108            let a = IntervalClosed::new(0.0, 4.0);
1109            let b = IntervalOpen::new(1.0, 3.0);
1110            let diff = a.difference(&b);
1111
1112            assert!(diff.is_some());
1113            match diff.unwrap() {
1114                IntervalDifference::TwoDisjoint { left, right } => {
1115                    let expected_left: Interval<f64> = IntervalClosed::new(0.0, 1.0).into();
1116                    let expected_right: Interval<f64> = IntervalClosed::new(3.0, 4.0).into();
1117                    assert_eq!(left, expected_left);
1118                    assert_eq!(right, expected_right);
1119                }
1120                _ => panic!("Expected two disjoint intervals"),
1121            }
1122        }
1123
1124        #[test]
1125        fn difference_boundary_subtlety_open_closed() {
1126            // A = [0, 2], B = (1, 2]
1127            // A \ B = [0, 1] (boundary point 1.0 is kept)
1128            let a = IntervalClosed::new(0.0, 2.0);
1129            let b = IntervalLowerOpenUpperClosed::new(1.0, 2.0);
1130            let diff = a.difference(&b);
1131
1132            assert!(diff.is_some());
1133            match diff.unwrap() {
1134                IntervalDifference::SingleConnected(result) => {
1135                    let expected: Interval<f64> = IntervalClosed::new(0.0, 1.0).into();
1136                    assert_eq!(result, expected);
1137                }
1138                _ => panic!("Expected single interval"),
1139            }
1140        }
1141
1142        #[test]
1143        fn difference_boundary_subtlety_closed_open() {
1144            // A = [0, 2], B = [1, 2)
1145            // A \ B = [0, 1) ∪ {2} but since {2} = [2, 2], result is [0, 1) ∪ [2, 2]
1146            let a = IntervalClosed::new(0.0, 2.0);
1147            let b = IntervalLowerClosedUpperOpen::new(1.0, 2.0);
1148            let diff = a.difference(&b);
1149
1150            assert!(diff.is_some());
1151            match diff.unwrap() {
1152                IntervalDifference::TwoDisjoint { left, right } => {
1153                    let expected_left: Interval<f64> =
1154                        IntervalLowerClosedUpperOpen::new(0.0, 1.0).into();
1155                    let expected_right: Interval<f64> = IntervalSingleton::new(2.0).into();
1156                    assert_eq!(left, expected_left);
1157                    assert_eq!(right, expected_right);
1158                }
1159                _ => panic!("Expected two disjoint intervals"),
1160            }
1161        }
1162
1163        #[test]
1164        fn difference_with_unbounded_intervals() {
1165            // A = [0, +∞), B = [5, 10]
1166            // A \ B = [0, 5) ∪ (10, +∞)
1167            let a = IntervalLowerClosedUpperUnbounded::new(0.0);
1168            let b = IntervalClosed::new(5.0, 10.0);
1169            let diff = a.difference(&b);
1170
1171            assert!(diff.is_some());
1172            match diff.unwrap() {
1173                IntervalDifference::TwoDisjoint { left, right } => {
1174                    let expected_left: Interval<f64> =
1175                        IntervalLowerClosedUpperOpen::new(0.0, 5.0).into();
1176                    let expected_right: Interval<f64> =
1177                        IntervalLowerOpenUpperUnbounded::new(10.0).into();
1178                    assert_eq!(left, expected_left);
1179                    assert_eq!(right, expected_right);
1180                }
1181                _ => panic!("Expected two disjoint intervals"),
1182            }
1183        }
1184
1185        #[test]
1186        fn difference_unbounded_minus_unbounded() {
1187            // A = (-∞, +∞), B = [0, 10]
1188            // A \ B = (-∞, 0) ∪ (10, +∞)
1189            let a = IntervalLowerUnboundedUpperUnbounded::new();
1190            let b = IntervalClosed::new(0.0, 10.0);
1191            let diff = a.difference(&b);
1192
1193            assert!(diff.is_some());
1194            match diff.unwrap() {
1195                IntervalDifference::TwoDisjoint { left, right } => {
1196                    let expected_left: Interval<f64> =
1197                        IntervalLowerUnboundedUpperOpen::new(0.0).into();
1198                    let expected_right: Interval<f64> =
1199                        IntervalLowerOpenUpperUnbounded::new(10.0).into();
1200                    assert_eq!(left, expected_left);
1201                    assert_eq!(right, expected_right);
1202                }
1203                _ => panic!("Expected two disjoint intervals"),
1204            }
1205        }
1206
1207        #[test]
1208        fn difference_unbounded_complete_containment() {
1209            // A = [5, 10], B = (-∞, +∞)
1210            // A \ B = ∅
1211            let a = IntervalClosed::new(5.0, 10.0);
1212            let b = IntervalLowerUnboundedUpperUnbounded::new();
1213            let diff = a.difference(&b);
1214
1215            assert!(diff.is_none());
1216        }
1217
1218        #[test]
1219        fn difference_unbounded_no_overlap() {
1220            // A = [0, 5], B = [10, +∞)
1221            // A \ B = [0, 5]
1222            let a = IntervalClosed::new(0.0, 5.0);
1223            let b = IntervalLowerClosedUpperUnbounded::new(10.0);
1224            let diff = a.difference(&b);
1225
1226            assert!(diff.is_some());
1227            match diff.unwrap() {
1228                IntervalDifference::SingleConnected(result) => {
1229                    let expected: Interval<f64> = IntervalClosed::new(0.0, 5.0).into();
1230                    assert_eq!(result, expected);
1231                }
1232                _ => panic!("Expected single interval"),
1233            }
1234        }
1235
1236        #[test]
1237        fn difference_with_singleton() {
1238            // A = [0, 2], B = {1}
1239            // A \ B = [0, 1) ∪ (1, 2]
1240            let a = IntervalClosed::new(0.0, 2.0);
1241            let b = IntervalSingleton::new(1.0);
1242            let diff = a.difference(&b);
1243
1244            assert!(diff.is_some());
1245            match diff.unwrap() {
1246                IntervalDifference::TwoDisjoint { left, right } => {
1247                    let expected_left: Interval<f64> =
1248                        IntervalLowerClosedUpperOpen::new(0.0, 1.0).into();
1249                    let expected_right: Interval<f64> =
1250                        IntervalLowerOpenUpperClosed::new(1.0, 2.0).into();
1251                    assert_eq!(left, expected_left);
1252                    assert_eq!(right, expected_right);
1253                }
1254                _ => panic!("Expected two disjoint intervals"),
1255            }
1256        }
1257
1258        #[test]
1259        fn difference_singleton_minus_interval() {
1260            // A = {1}, B = [0, 2]
1261            // A \ B = ∅
1262            let a = IntervalSingleton::new(1.0);
1263            let b = IntervalClosed::new(0.0, 2.0);
1264            let diff = a.difference(&b);
1265
1266            assert!(diff.is_none());
1267        }
1268
1269        #[test]
1270        fn difference_singleton_minus_non_overlapping() {
1271            // A = {5}, B = [0, 2]
1272            // A \ B = {5}
1273            let a = IntervalSingleton::new(5.0);
1274            let b = IntervalClosed::new(0.0, 2.0);
1275            let diff = a.difference(&b);
1276
1277            assert!(diff.is_some());
1278            match diff.unwrap() {
1279                IntervalDifference::SingleConnected(result) => {
1280                    let expected: Interval<f64> = IntervalSingleton::new(5.0).into();
1281                    assert_eq!(result, expected);
1282                }
1283                _ => panic!("Expected single interval"),
1284            }
1285        }
1286
1287        #[test]
1288        fn difference_identical_intervals() {
1289            // A = [0, 1], B = [0, 1]
1290            // A \ B = ∅
1291            let a = IntervalClosed::new(0.0, 1.0);
1292            let b = IntervalClosed::new(0.0, 1.0);
1293            let diff = a.difference(&b);
1294
1295            assert!(diff.is_none());
1296        }
1297
1298        #[test]
1299        fn difference_open_vs_closed_same_values() {
1300            // A = [0, 2], B = (0, 2)
1301            // A \ B = {0} ∪ {2} = [0, 0] ∪ [2, 2]
1302            let a = IntervalClosed::new(0.0, 2.0);
1303            let b = IntervalOpen::new(0.0, 2.0);
1304            let diff = a.difference(&b);
1305
1306            assert!(diff.is_some());
1307            match diff.unwrap() {
1308                IntervalDifference::TwoDisjoint { left, right } => {
1309                    let expected_left: Interval<f64> = IntervalSingleton::new(0.0).into();
1310                    let expected_right: Interval<f64> = IntervalSingleton::new(2.0).into();
1311                    assert_eq!(left, expected_left);
1312                    assert_eq!(right, expected_right);
1313                }
1314                _ => panic!("Expected two disjoint intervals (singletons at boundaries)"),
1315            }
1316        }
1317
1318        #[test]
1319        fn difference_contains_point_test() {
1320            // A = [0, 4], B = (1, 3)
1321            // A \ B = [0, 1] ∪ [3, 4]
1322            let a = IntervalClosed::new(0.0, 4.0);
1323            let b = IntervalOpen::new(1.0, 3.0);
1324            let diff = a.difference(&b).unwrap();
1325
1326            // Points in left part
1327            assert!(diff.contains_point(&0.0));
1328            assert!(diff.contains_point(&0.5));
1329            assert!(diff.contains_point(&1.0));
1330
1331            // Points removed by B
1332            assert!(!diff.contains_point(&1.5));
1333            assert!(!diff.contains_point(&2.0));
1334            assert!(!diff.contains_point(&2.5));
1335
1336            // Points in right part
1337            assert!(diff.contains_point(&3.0));
1338            assert!(diff.contains_point(&3.5));
1339            assert!(diff.contains_point(&4.0));
1340
1341            // Points outside A
1342            assert!(!diff.contains_point(&-1.0));
1343            assert!(!diff.contains_point(&5.0));
1344        }
1345
1346        #[test]
1347        fn difference_to_intervals_test() {
1348            // Test empty (None)
1349            let a = IntervalClosed::new(1.0, 2.0);
1350            let b = IntervalClosed::new(0.0, 3.0);
1351            let diff = a.difference(&b);
1352            assert!(diff.is_none());
1353
1354            // Test single
1355            let a = IntervalClosed::new(0.0, 2.0);
1356            let b = IntervalClosed::new(3.0, 5.0);
1357            let diff = a.difference(&b);
1358            assert!(diff.is_some());
1359            assert_eq!(diff.unwrap().to_intervals().len(), 1);
1360
1361            // Test two disjoint
1362            let a = IntervalClosed::new(0.0, 4.0);
1363            let b = IntervalOpen::new(1.0, 3.0);
1364            let diff = a.difference(&b);
1365            assert!(diff.is_some());
1366            assert_eq!(diff.unwrap().to_intervals().len(), 2);
1367        }
1368
1369        #[test]
1370        fn difference_helper_methods_test() {
1371            let a = IntervalClosed::new(0.0, 4.0);
1372            let b = IntervalOpen::new(1.0, 3.0);
1373            let diff = a.difference(&b).unwrap();
1374
1375            assert!(!diff.is_single_connected());
1376            assert!(diff.is_two_disjoint());
1377
1378            assert!(diff.as_single_connected().is_none());
1379            assert!(diff.as_two_disjoint().is_some());
1380
1381            let (left, right) = diff.as_two_disjoint().unwrap();
1382            assert!(left.contains_point(&0.5));
1383            assert!(right.contains_point(&3.5));
1384        }
1385
1386        #[test]
1387        fn difference_lower_unbounded_partial() {
1388            // A = (-∞, 5], B = [0, 3]
1389            // A \ B = (-∞, 0) ∪ (3, 5]
1390            let a = IntervalLowerUnboundedUpperClosed::new(5.0);
1391            let b = IntervalClosed::new(0.0, 3.0);
1392            let diff = a.difference(&b);
1393
1394            assert!(diff.is_some());
1395            match diff.unwrap() {
1396                IntervalDifference::TwoDisjoint { left, right } => {
1397                    let expected_left: Interval<f64> =
1398                        IntervalLowerUnboundedUpperOpen::new(0.0).into();
1399                    let expected_right: Interval<f64> =
1400                        IntervalLowerOpenUpperClosed::new(3.0, 5.0).into();
1401                    assert_eq!(left, expected_left);
1402                    assert_eq!(right, expected_right);
1403                }
1404                _ => panic!("Expected two disjoint intervals"),
1405            }
1406        }
1407
1408        /// Test difference of two open intervals (no overlap case)
1409        #[test]
1410        fn difference_open_open_no_overlap() {
1411            // A = (0, 2), B = (3, 5)
1412            // A \ B = (0, 2)
1413            let a = IntervalOpen::new(0.0, 2.0);
1414            let b = IntervalOpen::new(3.0, 5.0);
1415            let diff = a.difference(&b);
1416
1417            assert!(diff.is_some());
1418            match diff.unwrap() {
1419                IntervalDifference::SingleConnected(result) => {
1420                    let expected: Interval<f64> = IntervalOpen::new(0.0, 2.0).into();
1421                    assert_eq!(result, expected);
1422                }
1423                _ => panic!("Expected single interval"),
1424            }
1425        }
1426
1427        /// Test difference of two open intervals (partial overlap)
1428        #[test]
1429        fn difference_open_open_partial_overlap() {
1430            // A = (0, 4), B = (2, 6)
1431            // A \ B = (0, 2]
1432            let a = IntervalOpen::new(0.0, 4.0);
1433            let b = IntervalOpen::new(2.0, 6.0);
1434            let diff = a.difference(&b);
1435
1436            assert!(diff.is_some());
1437            match diff.unwrap() {
1438                IntervalDifference::SingleConnected(result) => {
1439                    let expected: Interval<f64> =
1440                        IntervalLowerOpenUpperClosed::new(0.0, 2.0).into();
1441                    assert_eq!(result, expected);
1442                }
1443                _ => panic!("Expected single interval"),
1444            }
1445        }
1446
1447        /// Test difference where result would have degenerate left interval
1448        /// This tests the (false, true) branch in the (true, true) case
1449        #[test]
1450        fn difference_degenerate_left_valid_right() {
1451            // A = [0, 4], B = [0, 2)
1452            // A \ B = [2, 4]
1453            // Left would be [0, 0) which is degenerate, only right part [2, 4] remains
1454            let a = IntervalClosed::new(0.0, 4.0);
1455            let b = IntervalLowerClosedUpperOpen::new(0.0, 2.0);
1456            let diff = a.difference(&b);
1457
1458            assert!(diff.is_some());
1459            match diff.unwrap() {
1460                IntervalDifference::SingleConnected(result) => {
1461                    let expected: Interval<f64> = IntervalClosed::new(2.0, 4.0).into();
1462                    assert_eq!(
1463                        result, expected,
1464                        "Should only have right part, left is degenerate"
1465                    );
1466                }
1467                _ => panic!("Expected single interval (right part only)"),
1468            }
1469        }
1470
1471        /// Test difference where result would have degenerate right interval
1472        /// This tests the (true, false) branch in the (true, true) case
1473        #[test]
1474        fn difference_valid_left_degenerate_right() {
1475            // A = [0, 4], B = (2, 4]
1476            // A \ B = [0, 2]
1477            // Right would be (4, 4] which is degenerate, only left part [0, 2] remains
1478            let a = IntervalClosed::new(0.0, 4.0);
1479            let b = IntervalLowerOpenUpperClosed::new(2.0, 4.0);
1480            let diff = a.difference(&b);
1481
1482            assert!(diff.is_some());
1483            match diff.unwrap() {
1484                IntervalDifference::SingleConnected(result) => {
1485                    let expected: Interval<f64> = IntervalClosed::new(0.0, 2.0).into();
1486                    assert_eq!(
1487                        result, expected,
1488                        "Should only have left part, right is degenerate"
1489                    );
1490                }
1491                _ => panic!("Expected single interval (left part only)"),
1492            }
1493        }
1494
1495        /// Test difference of two identical singletons
1496        #[test]
1497        fn difference_singleton_same_value() {
1498            // A = {5}, B = {5}
1499            // A \ B = ∅ (singleton minus itself is empty)
1500            let a = IntervalSingleton::new(5.0);
1501            let b = IntervalSingleton::new(5.0);
1502            let diff = a.difference(&b);
1503
1504            assert!(
1505                diff.is_none(),
1506                "Expected None when subtracting identical singletons ({{5}} \\ {{5}} = ∅)"
1507            );
1508        }
1509
1510        /// Test difference of two different singletons
1511        #[test]
1512        fn difference_singleton_different_values() {
1513            // A = {3}, B = {7}
1514            // A \ B = {3} (disjoint singletons)
1515            let a = IntervalSingleton::new(3.0);
1516            let b = IntervalSingleton::new(7.0);
1517            let diff = a.difference(&b);
1518
1519            assert!(diff.is_some());
1520            match diff.unwrap() {
1521                IntervalDifference::SingleConnected(interval) => {
1522                    let expected: Interval<f64> = IntervalSingleton::new(3.0).into();
1523                    assert_eq!(
1524                        interval, expected,
1525                        "Expected {{3}} when singletons are disjoint"
1526                    );
1527                }
1528                _ => panic!("Expected single singleton interval"),
1529            }
1530        }
1531
1532        /// Test difference of singleton contained in interval
1533        #[test]
1534        fn difference_singleton_contained_in_interval() {
1535            // A = {5}, B = [0, 10]
1536            // A \ B = ∅ (singleton completely contained)
1537            let a = IntervalSingleton::new(5.0);
1538            let b = IntervalClosed::new(0.0, 10.0);
1539            let diff = a.difference(&b);
1540
1541            assert!(
1542                diff.is_none(),
1543                "Expected None when singleton is contained in interval"
1544            );
1545        }
1546
1547        /// Test difference of interval containing singleton at endpoint
1548        #[test]
1549        fn difference_interval_minus_singleton_at_endpoint() {
1550            // A = [0, 10], B = {0}
1551            // A \ B = (0, 10] (remove left endpoint)
1552            let a = IntervalClosed::new(0.0, 10.0);
1553            let b = IntervalSingleton::new(0.0);
1554            let diff = a.difference(&b);
1555
1556            assert!(diff.is_some());
1557            match diff.unwrap() {
1558                IntervalDifference::SingleConnected(interval) => {
1559                    let expected: Interval<f64> =
1560                        IntervalLowerOpenUpperClosed::new(0.0, 10.0).into();
1561                    assert_eq!(
1562                        interval, expected,
1563                        "Expected (0, 10] when removing left endpoint singleton"
1564                    );
1565                }
1566                _ => panic!("Expected single interval"),
1567            }
1568        }
1569
1570        /// Test difference of interval minus singleton at midpoint
1571        #[test]
1572        fn difference_interval_minus_singleton_interior() {
1573            // A = [0, 10], B = {5}
1574            // A \ B = [0, 5) ∪ (5, 10] (split at midpoint)
1575            let a = IntervalClosed::new(0.0, 10.0);
1576            let b = IntervalSingleton::new(5.0);
1577            let diff = a.difference(&b);
1578
1579            assert!(diff.is_some());
1580            match diff.unwrap() {
1581                IntervalDifference::TwoDisjoint { left, right } => {
1582                    let expected_left: Interval<f64> =
1583                        IntervalLowerClosedUpperOpen::new(0.0, 5.0).into();
1584                    let expected_right: Interval<f64> =
1585                        IntervalLowerOpenUpperClosed::new(5.0, 10.0).into();
1586                    assert_eq!(left, expected_left, "Expected [0, 5) as left part");
1587                    assert_eq!(right, expected_right, "Expected (5, 10] as right part");
1588                }
1589                _ => panic!("Expected two disjoint intervals"),
1590            }
1591        }
1592
1593        /// Test difference of open interval minus singleton at boundary
1594        #[test]
1595        fn difference_open_interval_minus_singleton_at_boundary() {
1596            // A = (0, 10), B = {0}
1597            // A \ B = (0, 10) (singleton not in open interval)
1598            let a = IntervalOpen::new(0.0, 10.0);
1599            let b = IntervalSingleton::new(0.0);
1600            let diff = a.difference(&b);
1601
1602            assert!(diff.is_some());
1603            match diff.unwrap() {
1604                IntervalDifference::SingleConnected(interval) => {
1605                    let expected: Interval<f64> = IntervalOpen::new(0.0, 10.0).into();
1606                    assert_eq!(
1607                        interval, expected,
1608                        "Expected (0, 10) unchanged since {{0}} is not in (0, 10)"
1609                    );
1610                }
1611                _ => panic!("Expected single interval"),
1612            }
1613        }
1614
1615        /// Test difference of open intervals with interior removal
1616        #[test]
1617        fn difference_open_interior_removal() {
1618            // A = (0, 6), B = [2, 4]
1619            // A \ B = (0, 2) ∪ (4, 6)
1620            let a = IntervalOpen::new(0.0, 6.0);
1621            let b = IntervalClosed::new(2.0, 4.0);
1622            let diff = a.difference(&b);
1623
1624            assert!(diff.is_some());
1625            match diff.unwrap() {
1626                IntervalDifference::TwoDisjoint { left, right } => {
1627                    let expected_left: Interval<f64> = IntervalOpen::new(0.0, 2.0).into();
1628                    let expected_right: Interval<f64> = IntervalOpen::new(4.0, 6.0).into();
1629                    assert_eq!(left, expected_left);
1630                    assert_eq!(right, expected_right);
1631                }
1632                _ => panic!("Expected two disjoint open intervals"),
1633            }
1634        }
1635
1636        /// Test difference with half-open intervals (mixed boundary types)
1637        #[test]
1638        fn difference_half_open_intervals() {
1639            // A = [0, 5), B = (2, 4]
1640            // A \ B = [0, 2] ∪ (4, 5)
1641            let a = IntervalLowerClosedUpperOpen::new(0.0, 5.0);
1642            let b = IntervalLowerOpenUpperClosed::new(2.0, 4.0);
1643            let diff = a.difference(&b);
1644
1645            assert!(diff.is_some());
1646            match diff.unwrap() {
1647                IntervalDifference::TwoDisjoint { left, right } => {
1648                    let expected_left: Interval<f64> = IntervalClosed::new(0.0, 2.0).into();
1649                    let expected_right: Interval<f64> = IntervalOpen::new(4.0, 5.0).into();
1650                    assert_eq!(left, expected_left);
1651                    assert_eq!(right, expected_right);
1652                }
1653                _ => panic!("Expected two disjoint intervals with mixed bounds"),
1654            }
1655        }
1656
1657        /// Test difference where other is unbounded on both sides
1658        #[test]
1659        fn difference_with_fully_unbounded_other() {
1660            // A = [0, 5], B = (-∞, +∞)
1661            // A \ B = ∅ (completely contained)
1662            let a = IntervalClosed::new(0.0, 5.0);
1663            let b = IntervalLowerUnboundedUpperUnbounded::new();
1664            let diff = a.difference(&b);
1665
1666            assert!(
1667                diff.is_none(),
1668                "Finite interval minus universal interval should be empty"
1669            );
1670        }
1671
1672        /// Test difference of unbounded intervals
1673        #[test]
1674        fn difference_unbounded_partial_overlap() {
1675            // A = [0, +∞), B = (-∞, 5]
1676            // A \ B = (5, +∞)
1677            let a = IntervalLowerClosedUpperUnbounded::new(0.0);
1678            let b = IntervalLowerUnboundedUpperClosed::new(5.0);
1679            let diff = a.difference(&b);
1680
1681            assert!(diff.is_some());
1682            match diff.unwrap() {
1683                IntervalDifference::SingleConnected(result) => {
1684                    let expected: Interval<f64> = IntervalLowerOpenUpperUnbounded::new(5.0).into();
1685                    assert_eq!(result, expected);
1686                }
1687                _ => panic!("Expected single unbounded interval"),
1688            }
1689        }
1690
1691        #[test]
1692        fn difference_open_lower_unbounded_minus_finite() {
1693            // A = (-∞, 5), B = [0, 3]
1694            // A \ B = (-∞, 0) ∪ (3, 5)
1695            let a = IntervalLowerUnboundedUpperOpen::new(5.0);
1696            let b = IntervalClosed::new(0.0, 3.0);
1697            let diff = a.difference(&b);
1698
1699            assert!(diff.is_some());
1700            match diff.unwrap() {
1701                IntervalDifference::TwoDisjoint { left, right } => {
1702                    let expected_left: Interval<f64> =
1703                        IntervalLowerUnboundedUpperOpen::new(0.0).into();
1704                    let expected_right: Interval<f64> = IntervalOpen::new(3.0, 5.0).into();
1705                    assert_eq!(left, expected_left);
1706                    assert_eq!(right, expected_right);
1707                }
1708                _ => panic!("Expected two disjoint intervals"),
1709            }
1710        }
1711
1712        #[test]
1713        fn difference_open_upper_unbounded_minus_finite() {
1714            // A = (0, +∞), B = [5, 10]
1715            // A \ B = (0, 5) ∪ (10, +∞)
1716            let a = IntervalLowerOpenUpperUnbounded::new(0.0);
1717            let b = IntervalClosed::new(5.0, 10.0);
1718            let diff = a.difference(&b);
1719
1720            assert!(diff.is_some());
1721            match diff.unwrap() {
1722                IntervalDifference::TwoDisjoint { left, right } => {
1723                    let expected_left: Interval<f64> = IntervalOpen::new(0.0, 5.0).into();
1724                    let expected_right: Interval<f64> =
1725                        IntervalLowerOpenUpperUnbounded::new(10.0).into();
1726                    assert_eq!(left, expected_left);
1727                    assert_eq!(right, expected_right);
1728                }
1729                _ => panic!("Expected two disjoint intervals"),
1730            }
1731        }
1732
1733        #[test]
1734        fn difference_lower_unbounded_minus_open_finite() {
1735            // A = (-∞, 10], B = (2, 5)
1736            // A \ B = (-∞, 2] ∪ [5, 10]
1737            let a = IntervalLowerUnboundedUpperClosed::new(10.0);
1738            let b = IntervalOpen::new(2.0, 5.0);
1739            let diff = a.difference(&b);
1740
1741            assert!(diff.is_some());
1742            match diff.unwrap() {
1743                IntervalDifference::TwoDisjoint { left, right } => {
1744                    let expected_left: Interval<f64> =
1745                        IntervalLowerUnboundedUpperClosed::new(2.0).into();
1746                    let expected_right: Interval<f64> = IntervalClosed::new(5.0, 10.0).into();
1747                    assert_eq!(left, expected_left);
1748                    assert_eq!(right, expected_right);
1749                }
1750                _ => panic!("Expected two disjoint intervals"),
1751            }
1752        }
1753
1754        #[test]
1755        fn difference_upper_unbounded_minus_open_finite() {
1756            // A = [0, +∞), B = (5, 10)
1757            // A \ B = [0, 5] ∪ [10, +∞)
1758            let a = IntervalLowerClosedUpperUnbounded::new(0.0);
1759            let b = IntervalOpen::new(5.0, 10.0);
1760            let diff = a.difference(&b);
1761
1762            assert!(diff.is_some());
1763            match diff.unwrap() {
1764                IntervalDifference::TwoDisjoint { left, right } => {
1765                    let expected_left: Interval<f64> = IntervalClosed::new(0.0, 5.0).into();
1766                    let expected_right: Interval<f64> =
1767                        IntervalLowerClosedUpperUnbounded::new(10.0).into();
1768                    assert_eq!(left, expected_left);
1769                    assert_eq!(right, expected_right);
1770                }
1771                _ => panic!("Expected two disjoint intervals"),
1772            }
1773        }
1774
1775        #[test]
1776        fn difference_same_direction_upper_unbounded_disjoint() {
1777            // A = [0, +∞), B = [10, +∞)
1778            // A \ B = [0, 10)
1779            let a = IntervalLowerClosedUpperUnbounded::new(0.0);
1780            let b = IntervalLowerClosedUpperUnbounded::new(10.0);
1781            let diff = a.difference(&b);
1782
1783            assert!(diff.is_some());
1784            match diff.unwrap() {
1785                IntervalDifference::SingleConnected(interval) => {
1786                    let expected: Interval<f64> =
1787                        IntervalLowerClosedUpperOpen::new(0.0, 10.0).into();
1788                    assert_eq!(interval, expected);
1789                }
1790                _ => panic!("Expected single connected interval"),
1791            }
1792        }
1793
1794        #[test]
1795        fn difference_same_direction_upper_unbounded_overlap() {
1796            // A = [5, +∞), B = [0, +∞)
1797            // A \ B = ∅ (A ⊆ B)
1798            let a = IntervalLowerClosedUpperUnbounded::new(5.0);
1799            let b = IntervalLowerClosedUpperUnbounded::new(0.0);
1800            let diff = a.difference(&b);
1801
1802            assert!(diff.is_none());
1803        }
1804
1805        #[test]
1806        fn difference_same_direction_lower_unbounded_disjoint() {
1807            // A = (-∞, 10], B = (-∞, 5]
1808            // A \ B = (5, 10]
1809            let a = IntervalLowerUnboundedUpperClosed::new(10.0);
1810            let b = IntervalLowerUnboundedUpperClosed::new(5.0);
1811            let diff = a.difference(&b);
1812
1813            assert!(diff.is_some());
1814            match diff.unwrap() {
1815                IntervalDifference::SingleConnected(interval) => {
1816                    let expected: Interval<f64> =
1817                        IntervalLowerOpenUpperClosed::new(5.0, 10.0).into();
1818                    assert_eq!(interval, expected);
1819                }
1820                _ => panic!("Expected single connected interval"),
1821            }
1822        }
1823
1824        #[test]
1825        fn difference_same_direction_lower_unbounded_overlap() {
1826            // A = (-∞, 5], B = (-∞, 10]
1827            // A \ B = ∅ (A ⊆ B)
1828            let a = IntervalLowerUnboundedUpperClosed::new(5.0);
1829            let b = IntervalLowerUnboundedUpperClosed::new(10.0);
1830            let diff = a.difference(&b);
1831
1832            assert!(diff.is_none());
1833        }
1834
1835        #[test]
1836        fn difference_universal_minus_upper_unbounded() {
1837            // A = (-∞, +∞), B = [5, +∞)
1838            // A \ B = (-∞, 5)
1839            let a = IntervalLowerUnboundedUpperUnbounded::new();
1840            let b = IntervalLowerClosedUpperUnbounded::new(5.0);
1841            let diff = a.difference(&b);
1842
1843            assert!(diff.is_some());
1844            match diff.unwrap() {
1845                IntervalDifference::SingleConnected(interval) => {
1846                    let expected: Interval<f64> = IntervalLowerUnboundedUpperOpen::new(5.0).into();
1847                    assert_eq!(interval, expected);
1848                }
1849                _ => panic!("Expected single connected interval"),
1850            }
1851        }
1852
1853        #[test]
1854        fn difference_universal_minus_lower_unbounded() {
1855            // A = (-∞, +∞), B = (-∞, 5]
1856            // A \ B = (5, +∞)
1857            let a = IntervalLowerUnboundedUpperUnbounded::new();
1858            let b = IntervalLowerUnboundedUpperClosed::new(5.0);
1859            let diff = a.difference(&b);
1860
1861            assert!(diff.is_some());
1862            match diff.unwrap() {
1863                IntervalDifference::SingleConnected(interval) => {
1864                    let expected: Interval<f64> = IntervalLowerOpenUpperUnbounded::new(5.0).into();
1865                    assert_eq!(interval, expected);
1866                }
1867                _ => panic!("Expected single connected interval"),
1868            }
1869        }
1870
1871        #[test]
1872        fn difference_universal_minus_open_finite() {
1873            // A = (-∞, +∞), B = (0, 10)
1874            // A \ B = (-∞, 0] ∪ [10, +∞)
1875            let a = IntervalLowerUnboundedUpperUnbounded::new();
1876            let b = IntervalOpen::new(0.0, 10.0);
1877            let diff = a.difference(&b);
1878
1879            assert!(diff.is_some());
1880            match diff.unwrap() {
1881                IntervalDifference::TwoDisjoint { left, right } => {
1882                    let expected_left: Interval<f64> =
1883                        IntervalLowerUnboundedUpperClosed::new(0.0).into();
1884                    let expected_right: Interval<f64> =
1885                        IntervalLowerClosedUpperUnbounded::new(10.0).into();
1886                    assert_eq!(left, expected_left);
1887                    assert_eq!(right, expected_right);
1888                }
1889                _ => panic!("Expected two disjoint intervals"),
1890            }
1891        }
1892
1893        #[test]
1894        fn difference_universal_minus_universal() {
1895            // A = (-∞, +∞), B = (-∞, +∞)
1896            // A \ B = ∅ (A \ A = ∅)
1897            let a: IntervalLowerUnboundedUpperUnbounded<f64> =
1898                IntervalLowerUnboundedUpperUnbounded::new();
1899            let b: IntervalLowerUnboundedUpperUnbounded<f64> =
1900                IntervalLowerUnboundedUpperUnbounded::new();
1901            let diff = a.difference(&b);
1902
1903            assert!(diff.is_none());
1904        }
1905
1906        #[test]
1907        fn difference_finite_closed_minus_lower_unbounded_disjoint() {
1908            // A = [10, 20], B = (-∞, 5]
1909            // A \ B = [10, 20] (no overlap)
1910            let a = IntervalClosed::new(10.0, 20.0);
1911            let b = IntervalLowerUnboundedUpperClosed::new(5.0);
1912            let diff = a.difference(&b);
1913
1914            assert!(diff.is_some());
1915            match diff.unwrap() {
1916                IntervalDifference::SingleConnected(interval) => {
1917                    let expected: Interval<f64> = IntervalClosed::new(10.0, 20.0).into();
1918                    assert_eq!(interval, expected);
1919                }
1920                _ => panic!("Expected single connected interval"),
1921            }
1922        }
1923
1924        #[test]
1925        fn difference_finite_closed_minus_lower_unbounded_overlap() {
1926            // A = [0, 10], B = (-∞, 5]
1927            // A \ B = (5, 10]
1928            let a = IntervalClosed::new(0.0, 10.0);
1929            let b = IntervalLowerUnboundedUpperClosed::new(5.0);
1930            let diff = a.difference(&b);
1931
1932            assert!(diff.is_some());
1933            match diff.unwrap() {
1934                IntervalDifference::SingleConnected(interval) => {
1935                    let expected: Interval<f64> =
1936                        IntervalLowerOpenUpperClosed::new(5.0, 10.0).into();
1937                    assert_eq!(interval, expected);
1938                }
1939                _ => panic!("Expected single connected interval"),
1940            }
1941        }
1942
1943        #[test]
1944        fn difference_finite_open_minus_upper_unbounded_disjoint() {
1945            // A = (0, 5), B = [10, +∞)
1946            // A \ B = (0, 5) (no overlap)
1947            let a = IntervalOpen::new(0.0, 5.0);
1948            let b = IntervalLowerClosedUpperUnbounded::new(10.0);
1949            let diff = a.difference(&b);
1950
1951            assert!(diff.is_some());
1952            match diff.unwrap() {
1953                IntervalDifference::SingleConnected(interval) => {
1954                    let expected: Interval<f64> = IntervalOpen::new(0.0, 5.0).into();
1955                    assert_eq!(interval, expected);
1956                }
1957                _ => panic!("Expected single connected interval"),
1958            }
1959        }
1960
1961        #[test]
1962        fn difference_finite_open_minus_lower_unbounded_overlap() {
1963            // A = (0, 10), B = (-∞, 5]
1964            // A \ B = (5, 10)
1965            let a = IntervalOpen::new(0.0, 10.0);
1966            let b = IntervalLowerUnboundedUpperClosed::new(5.0);
1967            let diff = a.difference(&b);
1968
1969            assert!(diff.is_some());
1970            match diff.unwrap() {
1971                IntervalDifference::SingleConnected(interval) => {
1972                    let expected: Interval<f64> = IntervalOpen::new(5.0, 10.0).into();
1973                    assert_eq!(interval, expected);
1974                }
1975                _ => panic!("Expected single connected interval"),
1976            }
1977        }
1978
1979        #[test]
1980        fn difference_lower_unbounded_minus_upper_unbounded_disjoint() {
1981            // A = (-∞, 5], B = [10, +∞)
1982            // A \ B = (-∞, 5] (no overlap)
1983            let a = IntervalLowerUnboundedUpperClosed::new(5.0);
1984            let b = IntervalLowerClosedUpperUnbounded::new(10.0);
1985            let diff = a.difference(&b);
1986
1987            assert!(diff.is_some());
1988            match diff.unwrap() {
1989                IntervalDifference::SingleConnected(interval) => {
1990                    let expected: Interval<f64> =
1991                        IntervalLowerUnboundedUpperClosed::new(5.0).into();
1992                    assert_eq!(interval, expected);
1993                }
1994                _ => panic!("Expected single connected interval"),
1995            }
1996        }
1997
1998        #[test]
1999        fn difference_lower_unbounded_minus_upper_unbounded_overlap() {
2000            // A = (-∞, 10], B = [5, +∞)
2001            // A \ B = (-∞, 5)
2002            let a = IntervalLowerUnboundedUpperClosed::new(10.0);
2003            let b = IntervalLowerClosedUpperUnbounded::new(5.0);
2004            let diff = a.difference(&b);
2005
2006            assert!(diff.is_some());
2007            match diff.unwrap() {
2008                IntervalDifference::SingleConnected(interval) => {
2009                    let expected: Interval<f64> = IntervalLowerUnboundedUpperOpen::new(5.0).into();
2010                    assert_eq!(interval, expected);
2011                }
2012                _ => panic!("Expected single connected interval"),
2013            }
2014        }
2015
2016        #[test]
2017        fn difference_none_half_open_contained() {
2018            // A = [1, 5), B = [0, 10]
2019            // A \ B = ∅ (A is completely contained in B)
2020            let a = IntervalLowerClosedUpperOpen::new(1.0, 5.0);
2021            let b = IntervalClosed::new(0.0, 10.0);
2022            let diff = a.difference(&b);
2023
2024            assert!(
2025                diff.is_none(),
2026                "Expected None when interval is completely contained"
2027            );
2028        }
2029
2030        #[test]
2031        fn difference_none_open_interval_contained() {
2032            // A = (2, 4), B = [2, 4]
2033            // A \ B = ∅ (open interval contained in closed interval)
2034            let a = IntervalOpen::new(2.0, 4.0);
2035            let b = IntervalClosed::new(2.0, 4.0);
2036            let diff = a.difference(&b);
2037
2038            assert!(
2039                diff.is_none(),
2040                "Expected None when open interval is contained in closed interval"
2041            );
2042        }
2043
2044        #[test]
2045        fn difference_none_identical_intervals() {
2046            // A = [3, 7], B = [3, 7]
2047            // A \ B = ∅ (identical intervals)
2048            let a = IntervalClosed::new(3.0, 7.0);
2049            let b = IntervalClosed::new(3.0, 7.0);
2050            let diff = a.difference(&b);
2051
2052            assert!(diff.is_none(), "Expected None when A = B (A \\ A = ∅)");
2053        }
2054
2055        #[test]
2056        fn difference_none_open_interval_subset() {
2057            // A = (1, 3), B = (0, 5)
2058            // A \ B = ∅ (A ⊂ B)
2059            let a = IntervalOpen::new(1.0, 3.0);
2060            let b = IntervalOpen::new(0.0, 5.0);
2061            let diff = a.difference(&b);
2062
2063            assert!(
2064                diff.is_none(),
2065                "Expected None when open interval is subset of another"
2066            );
2067        }
2068
2069        #[test]
2070        fn difference_none_lower_unbounded_contained() {
2071            // A = (-∞, 5], B = (-∞, 10]
2072            // A \ B = ∅ (A ⊆ B)
2073            let a = IntervalLowerUnboundedUpperClosed::new(5.0);
2074            let b = IntervalLowerUnboundedUpperClosed::new(10.0);
2075            let diff = a.difference(&b);
2076
2077            assert!(
2078                diff.is_none(),
2079                "Expected None when lower unbounded interval is subset"
2080            );
2081        }
2082
2083        #[test]
2084        fn difference_none_upper_unbounded_contained() {
2085            // A = [10, +∞), B = [5, +∞)
2086            // A \ B = ∅ (A ⊆ B)
2087            let a = IntervalLowerClosedUpperUnbounded::new(10.0);
2088            let b = IntervalLowerClosedUpperUnbounded::new(5.0);
2089            let diff = a.difference(&b);
2090
2091            assert!(
2092                diff.is_none(),
2093                "Expected None when upper unbounded interval is subset"
2094            );
2095        }
2096
2097        #[test]
2098        fn difference_none_mixed_boundaries_contained() {
2099            // A = (2, 8), B = [1, 9]
2100            // A \ B = ∅ (open interval contained in closed interval)
2101            let a = IntervalOpen::new(2.0, 8.0);
2102            let b = IntervalClosed::new(1.0, 9.0);
2103            let diff = a.difference(&b);
2104
2105            assert!(
2106                diff.is_none(),
2107                "Expected None when interval with open boundaries is contained"
2108            );
2109        }
2110    }
2111
2112    mod intersection {
2113        use super::*;
2114
2115        #[test]
2116        fn intersection_closed_closed() {
2117            let a = IntervalClosed::new(0.0, 2.0);
2118            let b = IntervalClosed::new(1.0, 3.0);
2119            let intersection = a.intersection(&b).unwrap();
2120            let expected_intersection =
2121                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
2122                    IntervalFinitePositiveLength::from(IntervalClosed::new(1.0, 2.0)),
2123                ));
2124            assert_eq!(intersection, expected_intersection);
2125        }
2126
2127        #[test]
2128        fn intersection_closed_open() {
2129            let a = IntervalClosed::new(0.0, 2.0);
2130            let b = IntervalOpen::new(1.0, 3.0);
2131            let intersection = a.intersection(&b).unwrap();
2132            let expected_intersection =
2133                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
2134                    IntervalFinitePositiveLength::from(IntervalLowerOpenUpperClosed::new(1.0, 2.0)),
2135                ));
2136            assert_eq!(intersection, expected_intersection);
2137        }
2138
2139        #[test]
2140        fn intersection_open_open() {
2141            let a = IntervalOpen::new(0.0, 2.0);
2142            let b = IntervalOpen::new(1.0, 3.0);
2143            let intersection = a.intersection(&b).unwrap();
2144            let expected_intersection =
2145                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
2146                    IntervalFinitePositiveLength::from(IntervalOpen::new(1.0, 2.0)),
2147                ));
2148            assert_eq!(intersection, expected_intersection);
2149        }
2150
2151        #[test]
2152        fn intersection_closed_left_half_open() {
2153            let a = IntervalClosed::new(0.0, 2.0);
2154            let b = IntervalLowerOpenUpperClosed::new(1.0, 3.0);
2155            let intersection = a.intersection(&b).unwrap();
2156            let expected_intersection =
2157                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
2158                    IntervalFinitePositiveLength::from(IntervalLowerOpenUpperClosed::new(1.0, 2.0)),
2159                ));
2160            assert_eq!(intersection, expected_intersection);
2161        }
2162
2163        #[test]
2164        fn intersection_closed_right_half_open() {
2165            let a = IntervalClosed::new(0.0, 2.0);
2166            let b = IntervalLowerClosedUpperOpen::new(1.0, 3.0);
2167            let intersection = a.intersection(&b).unwrap();
2168            let expected_intersection =
2169                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
2170                    IntervalFinitePositiveLength::from(IntervalClosed::new(1.0, 2.0)),
2171                ));
2172            assert_eq!(intersection, expected_intersection);
2173        }
2174
2175        #[test]
2176        fn intersection_singleton_closed() {
2177            let a = IntervalSingleton::new(1.0);
2178            let b = IntervalClosed::new(0.0, 2.0);
2179            let intersection = a.intersection(&b).unwrap();
2180            let expected_intersection = Interval::FiniteLength(IntervalFiniteLength::from(a));
2181            assert_eq!(intersection, expected_intersection);
2182        }
2183
2184        #[test]
2185        fn intersection_open_left_half_open() {
2186            let a = IntervalOpen::new(0.0, 2.0);
2187            let b = IntervalLowerOpenUpperClosed::new(1.0, 3.0);
2188            let intersection = a.intersection(&b).unwrap();
2189            let expected_intersection =
2190                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
2191                    IntervalFinitePositiveLength::from(IntervalOpen::new(1.0, 2.0)),
2192                ));
2193            assert_eq!(intersection, expected_intersection);
2194        }
2195
2196        #[test]
2197        fn intersection_left_half_open_right_half_open() {
2198            let a = IntervalLowerOpenUpperClosed::new(0.0, 2.0);
2199            let b = IntervalLowerClosedUpperOpen::new(1.0, 3.0);
2200            let intersection = a.intersection(&b).unwrap();
2201            let expected_intersection =
2202                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
2203                    IntervalFinitePositiveLength::from(IntervalClosed::new(1.0, 2.0)),
2204                ));
2205            assert_eq!(intersection, expected_intersection);
2206        }
2207
2208        #[test]
2209        fn intersection_disjoint() {
2210            let a = IntervalClosed::new(0.0, 1.0);
2211            let b = IntervalClosed::new(2.0, 3.0);
2212            let intersection = a.intersection(&b);
2213            assert!(intersection.is_none());
2214        }
2215
2216        /// Test intersection of two disjoint open intervals
2217        /// This tests the open-open branch where lower_bound >= upper_bound (returns None)
2218        #[test]
2219        fn intersection_disjoint_open_open() {
2220            // (0.0, 1.0) ∩ (2.0, 3.0) = ∅
2221            let a = IntervalOpen::new(0.0, 1.0);
2222            let b = IntervalOpen::new(2.0, 3.0);
2223            let intersection = a.intersection(&b);
2224            assert!(
2225                intersection.is_none(),
2226                "Disjoint open intervals should have empty intersection"
2227            );
2228        }
2229
2230        /// Test intersection of two open intervals that share a boundary point
2231        /// This also tests the open-open branch where lower_bound >= upper_bound
2232        #[test]
2233        fn intersection_open_open_touching_boundary() {
2234            // (0.0, 1.0) ∩ (1.0, 2.0) = ∅ (they touch at 1.0 but both exclude it)
2235            let a = IntervalOpen::new(0.0, 1.0);
2236            let b = IntervalOpen::new(1.0, 2.0);
2237            let intersection = a.intersection(&b);
2238            assert!(
2239                intersection.is_none(),
2240                "Open intervals touching at boundary should have empty intersection"
2241            );
2242        }
2243
2244        #[test]
2245        fn intersection_singleton_open() {
2246            let a = IntervalSingleton::new(1.0);
2247            let b = IntervalOpen::new(0.0, 2.0);
2248            let intersection = a.intersection(&b).unwrap();
2249            let expected_intersection = Interval::FiniteLength(IntervalFiniteLength::from(a));
2250            assert_eq!(intersection, expected_intersection);
2251        }
2252
2253        #[test]
2254        fn intersection_unbounded_closed() {
2255            let a = IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
2256                IntervalLowerUnboundedUpperUnbounded::new(),
2257            );
2258            let b = IntervalClosed::new(0.0, 2.0);
2259            let intersection = a.intersection(&b).unwrap();
2260            let expected_intersection = Interval::FiniteLength(
2261                IntervalFiniteLength::PositiveLength(IntervalFinitePositiveLength::from(b)),
2262            );
2263            assert_eq!(intersection, expected_intersection);
2264        }
2265
2266        #[test]
2267        fn intersection_unbounded_unbounded() {
2268            let a = IntervalInfiniteLength::<f64>::LowerUnboundedUpperUnbounded(
2269                IntervalLowerUnboundedUpperUnbounded::new(),
2270            );
2271            let b = IntervalInfiniteLength::<f64>::LowerUnboundedUpperUnbounded(
2272                IntervalLowerUnboundedUpperUnbounded::new(),
2273            );
2274            let intersection = a.intersection(&b).unwrap();
2275            let expected_intersection =
2276                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperUnbounded(
2277                    IntervalLowerUnboundedUpperUnbounded::new(),
2278                ));
2279            assert_eq!(intersection, expected_intersection);
2280        }
2281
2282        #[test]
2283        fn intersection_subintervals() {
2284            let a =
2285                SubIntervalInPartition::<IntervalClosed<f64>>::First(IntervalClosed::new(0.0, 2.0));
2286            let b = SubIntervalInPartition::<IntervalClosed<f64>>::Last(
2287                IntervalLowerOpenUpperClosed::new(1.0, 2.0),
2288            );
2289            let intersection = a.intersection(&b).unwrap();
2290            let expected_intersection =
2291                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
2292                    IntervalFinitePositiveLength::from(IntervalLowerOpenUpperClosed::new(1.0, 2.0)),
2293                ));
2294            assert_eq!(intersection, expected_intersection);
2295        }
2296
2297        #[test]
2298        fn intersection_none() {
2299            let i1 = IntervalClosed::new(0.0, 1.0);
2300            let i2 = IntervalClosed::new(2.0, 3.0);
2301            assert!(i1.intersection(&i2).is_none());
2302        }
2303
2304        #[test]
2305        fn intersection_singleton() {
2306            let i1 = IntervalClosed::new(0.0, 1.0);
2307            let i2 = IntervalClosed::new(1.0, 2.0);
2308            let intersection = i1.intersection(&i2).unwrap();
2309            let expected_intersection =
2310                Interval::FiniteLength(IntervalFiniteLength::from(IntervalSingleton::new(1.0)));
2311            assert_eq!(intersection, expected_intersection);
2312        }
2313
2314        #[test]
2315        fn intersection_01() {
2316            let lower_bound = 0.;
2317            let upper_bound_0 = 1.;
2318            let upper_bound_1 = 0.5;
2319
2320            let closed_0 = IntervalClosed::new(lower_bound, upper_bound_0); // [0., 1.]
2321            let closed_1 = IntervalClosed::new(lower_bound, upper_bound_1); // [0., 0.5]
2322
2323            let i0: IntervalFinitePositiveLength<_> = closed_0
2324                .intersection(&closed_1)
2325                .unwrap()
2326                .try_into()
2327                .unwrap(); // [0., 0.5]
2328            assert_eq!(i0.lower_bound_value(), &0.);
2329            assert_eq!(i0.upper_bound_value(), &0.5);
2330
2331            let open_1 = IntervalOpen::new(lower_bound, upper_bound_1); // (0., 0.5)
2332
2333            let i1: IntervalFinitePositiveLength<_> =
2334                closed_0.intersection(&open_1).unwrap().try_into().unwrap(); // [0., 0.5)
2335            assert_eq!(i1.lower_bound_value(), &0.);
2336            assert_eq!(i1.upper_bound_value(), &0.5);
2337
2338            let singleton_1 = IntervalSingleton::new(lower_bound); // [0., 0.]
2339
2340            let i2 = singleton_1.intersection(&closed_1).unwrap();
2341            match i2 {
2342                Interval::InfiniteLength(_) => panic!("The interval should be a singleton!"),
2343                Interval::FiniteLength(interval_finite_length) => match interval_finite_length {
2344                    IntervalFiniteLength::PositiveLength(_) => {
2345                        panic!("The interval should be a singleton!")
2346                    }
2347                    IntervalFiniteLength::ZeroLength(ref singleton) => {
2348                        assert_eq!(singleton.value(), &0.);
2349                    }
2350                },
2351            };
2352
2353            let i3 = singleton_1.intersection(&open_1);
2354            assert!(i3.is_none());
2355        }
2356
2357        #[test]
2358        fn intersection_02() {
2359            let lower_bound = 0.;
2360            let upper_bound = 0.5;
2361
2362            let open_1 = IntervalOpen::new(lower_bound, upper_bound);
2363            let singleton_1 = IntervalSingleton::new(lower_bound);
2364
2365            let i3 = singleton_1.intersection(&open_1);
2366            assert!(i3.is_none());
2367        }
2368
2369        /// Test intersection with unbounded lower and open upper bound
2370        /// This tests the (None, Some(UpperBoundRuntime::Open)) branch
2371        #[test]
2372        fn intersection_unbounded_lower_open_upper() {
2373            // (-∞, 5.0) ∩ [0.0, 10.0] = [0.0, 5.0)
2374            let a = IntervalLowerUnboundedUpperOpen::new(5.0);
2375            let b = IntervalClosed::new(0.0, 10.0);
2376
2377            let intersection = a.intersection(&b).unwrap();
2378
2379            match intersection {
2380                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
2381                    IntervalFinitePositiveLength::LowerClosedUpperOpen(interval),
2382                )) => {
2383                    assert_eq!(interval.lower_bound_value(), &0.0);
2384                    assert_eq!(interval.upper_bound_value(), &5.0);
2385                }
2386                _ => panic!("Expected [0.0, 5.0) interval"),
2387            }
2388        }
2389
2390        /// Test intersection with unbounded lower and open upper bound (disjoint case)
2391        #[test]
2392        fn intersection_unbounded_lower_open_upper_disjoint() {
2393            // (-∞, 0.0) ∩ [1.0, 10.0] = ∅
2394            let a = IntervalLowerUnboundedUpperOpen::new(0.0);
2395            let b = IntervalClosed::new(1.0, 10.0);
2396
2397            let intersection = a.intersection(&b);
2398            assert!(intersection.is_none());
2399        }
2400
2401        /// Test intersection with unbounded lower and closed upper bound
2402        /// This tests the (None, Some(UpperBoundRuntime::Closed)) branch
2403        #[test]
2404        fn intersection_unbounded_lower_closed_upper() {
2405            // (-∞, 5.0] ∩ [0.0, 10.0] = [0.0, 5.0]
2406            let a = IntervalLowerUnboundedUpperClosed::new(5.0);
2407            let b = IntervalClosed::new(0.0, 10.0);
2408
2409            let intersection = a.intersection(&b).unwrap();
2410
2411            match intersection {
2412                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
2413                    IntervalFinitePositiveLength::Closed(interval),
2414                )) => {
2415                    assert_eq!(interval.lower_bound_value(), &0.0);
2416                    assert_eq!(interval.upper_bound_value(), &5.0);
2417                }
2418                _ => panic!("Expected [0.0, 5.0] interval"),
2419            }
2420        }
2421
2422        /// Test intersection with open lower and unbounded upper bound
2423        /// This tests the (Some(LowerBoundRuntime::Open), None) branch
2424        #[test]
2425        fn intersection_open_lower_unbounded_upper() {
2426            // (3.0, +∞) ∩ [0.0, 10.0] = (3.0, 10.0]
2427            let a = IntervalLowerOpenUpperUnbounded::new(3.0);
2428            let b = IntervalClosed::new(0.0, 10.0);
2429
2430            let intersection = a.intersection(&b).unwrap();
2431
2432            match intersection {
2433                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
2434                    IntervalFinitePositiveLength::LowerOpenUpperClosed(interval),
2435                )) => {
2436                    assert_eq!(interval.lower_bound_value(), &3.0);
2437                    assert_eq!(interval.upper_bound_value(), &10.0);
2438                }
2439                _ => panic!("Expected (3.0, 10.0] interval"),
2440            }
2441        }
2442
2443        /// Test intersection with open lower and unbounded upper bound (disjoint case)
2444        #[test]
2445        fn intersection_open_lower_unbounded_upper_disjoint() {
2446            // (10.0, +∞) ∩ [0.0, 5.0] = ∅
2447            let a = IntervalLowerOpenUpperUnbounded::new(10.0);
2448            let b = IntervalClosed::new(0.0, 5.0);
2449
2450            let intersection = a.intersection(&b);
2451            assert!(intersection.is_none());
2452        }
2453
2454        /// Test intersection with closed lower and unbounded upper bound
2455        /// This tests the (Some(LowerBoundRuntime::Closed), None) branch
2456        #[test]
2457        fn intersection_closed_lower_unbounded_upper() {
2458            // [3.0, +∞) ∩ [0.0, 10.0] = [3.0, 10.0]
2459            let a = IntervalLowerClosedUpperUnbounded::new(3.0);
2460            let b = IntervalClosed::new(0.0, 10.0);
2461
2462            let intersection = a.intersection(&b).unwrap();
2463
2464            match intersection {
2465                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
2466                    IntervalFinitePositiveLength::Closed(interval),
2467                )) => {
2468                    assert_eq!(interval.lower_bound_value(), &3.0);
2469                    assert_eq!(interval.upper_bound_value(), &10.0);
2470                }
2471                _ => panic!("Expected [3.0, 10.0] interval"),
2472            }
2473        }
2474
2475        /// Test intersection between two half-unbounded intervals
2476        #[test]
2477        fn intersection_half_unbounded_both() {
2478            // [3.0, +∞) ∩ (-∞, 10.0] = [3.0, 10.0]
2479            let a = IntervalLowerClosedUpperUnbounded::new(3.0);
2480            let b = IntervalLowerUnboundedUpperClosed::new(10.0);
2481
2482            let intersection = a.intersection(&b).unwrap();
2483
2484            match intersection {
2485                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
2486                    IntervalFinitePositiveLength::Closed(interval),
2487                )) => {
2488                    assert_eq!(interval.lower_bound_value(), &3.0);
2489                    assert_eq!(interval.upper_bound_value(), &10.0);
2490                }
2491                _ => panic!("Expected [3.0, 10.0] interval"),
2492            }
2493        }
2494
2495        /// Test intersection between two half-unbounded intervals (open bounds)
2496        #[test]
2497        fn intersection_half_unbounded_both_open() {
2498            // (3.0, +∞) ∩ (-∞, 10.0) = (3.0, 10.0)
2499            let a = IntervalLowerOpenUpperUnbounded::new(3.0);
2500            let b = IntervalLowerUnboundedUpperOpen::new(10.0);
2501
2502            let intersection = a.intersection(&b).unwrap();
2503
2504            match intersection {
2505                Interval::FiniteLength(IntervalFiniteLength::PositiveLength(
2506                    IntervalFinitePositiveLength::Open(interval),
2507                )) => {
2508                    assert_eq!(interval.lower_bound_value(), &3.0);
2509                    assert_eq!(interval.upper_bound_value(), &10.0);
2510                }
2511                _ => panic!("Expected (3.0, 10.0) interval"),
2512            }
2513        }
2514
2515        /// Test intersection between two half-unbounded intervals (disjoint)
2516        #[test]
2517        fn intersection_half_unbounded_disjoint() {
2518            // [10.0, +∞) ∩ (-∞, 5.0] = ∅
2519            let a = IntervalLowerClosedUpperUnbounded::new(10.0);
2520            let b = IntervalLowerUnboundedUpperClosed::new(5.0);
2521
2522            let intersection = a.intersection(&b);
2523            assert!(intersection.is_none());
2524        }
2525
2526        /// Test intersection preserving unbounded lower with closed upper
2527        #[test]
2528        fn intersection_preserves_unbounded_lower_closed() {
2529            // (-∞, 10.0] ∩ (-∞, 5.0] = (-∞, 5.0]
2530            let a = IntervalLowerUnboundedUpperClosed::new(10.0);
2531            let b = IntervalLowerUnboundedUpperClosed::new(5.0);
2532
2533            let intersection = a.intersection(&b).unwrap();
2534
2535            match intersection {
2536                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperClosed(
2537                    interval,
2538                )) => {
2539                    assert_eq!(interval.upper_bound_value(), &5.0);
2540                }
2541                _ => panic!("Expected (-∞, 5.0] interval"),
2542            }
2543        }
2544
2545        /// Test intersection preserving unbounded lower with open upper
2546        #[test]
2547        fn intersection_preserves_unbounded_lower_open() {
2548            // (-∞, 10.0) ∩ (-∞, 5.0) = (-∞, 5.0)
2549            let a = IntervalLowerUnboundedUpperOpen::new(10.0);
2550            let b = IntervalLowerUnboundedUpperOpen::new(5.0);
2551
2552            let intersection = a.intersection(&b).unwrap();
2553
2554            match intersection {
2555                Interval::InfiniteLength(IntervalInfiniteLength::LowerUnboundedUpperOpen(
2556                    interval,
2557                )) => {
2558                    assert_eq!(interval.upper_bound_value(), &5.0);
2559                }
2560                _ => panic!("Expected (-∞, 5.0) interval"),
2561            }
2562        }
2563
2564        /// Test intersection preserving closed lower with unbounded upper
2565        #[test]
2566        fn intersection_preserves_closed_lower_unbounded() {
2567            // [5.0, +∞) ∩ [3.0, +∞) = [5.0, +∞)
2568            let a = IntervalLowerClosedUpperUnbounded::new(5.0);
2569            let b = IntervalLowerClosedUpperUnbounded::new(3.0);
2570
2571            let intersection = a.intersection(&b).unwrap();
2572
2573            match intersection {
2574                Interval::InfiniteLength(IntervalInfiniteLength::LowerClosedUpperUnbounded(
2575                    interval,
2576                )) => {
2577                    assert_eq!(interval.lower_bound_value(), &5.0);
2578                }
2579                _ => panic!("Expected [5.0, +∞) interval"),
2580            }
2581        }
2582
2583        /// Test intersection preserving open lower with unbounded upper
2584        #[test]
2585        fn intersection_preserves_open_lower_unbounded() {
2586            // (5.0, +∞) ∩ (3.0, +∞) = (5.0, +∞)
2587            let a = IntervalLowerOpenUpperUnbounded::new(5.0);
2588            let b = IntervalLowerOpenUpperUnbounded::new(3.0);
2589
2590            let intersection = a.intersection(&b).unwrap();
2591
2592            match intersection {
2593                Interval::InfiniteLength(IntervalInfiniteLength::LowerOpenUpperUnbounded(
2594                    interval,
2595                )) => {
2596                    assert_eq!(interval.lower_bound_value(), &5.0);
2597                }
2598                _ => panic!("Expected (5.0, +∞) interval"),
2599            }
2600        }
2601    }
2602
2603    mod union {
2604        use super::*;
2605        use crate::intervals::{
2606            Interval, IntervalClosed, IntervalLowerClosedUpperOpen, IntervalLowerOpenUpperClosed,
2607            IntervalOpen, IntervalSingleton,
2608        };
2609        use try_create::New;
2610
2611        #[test]
2612        fn union_disjoint() {
2613            let i1 = IntervalClosed::new(0.0, 1.0);
2614            let i2 = IntervalClosed::new(2.0, 3.0);
2615            let union = i1.union(&i2);
2616            if let IntervalUnion::TwoDisjoint { left, right } = union {
2617                assert_eq!(left, i1.into());
2618                assert_eq!(right, i2.into());
2619            } else {
2620                panic!("Expected disjoint union");
2621            }
2622        }
2623
2624        /// Test union of two disjoint intervals where `self` is on the right
2625        /// This tests that the union method correctly orders intervals regardless of call order
2626        #[test]
2627        fn union_disjoint_self_on_right() {
2628            // i1 is [3.0, 4.0] and i2 is [0.0, 1.0]
2629            // When calling i1.union(&i2), self (i1) is on the right of other (i2)
2630            let i1 = IntervalClosed::new(3.0, 4.0); // self - right interval
2631            let i2 = IntervalClosed::new(0.0, 1.0); // other - left interval
2632            let union = i1.union(&i2);
2633
2634            // Convert to Interval for comparison
2635            let i1_interval: Interval<_> = i1.into();
2636            let i2_interval: Interval<_> = i2.into();
2637
2638            // The result should still have the intervals ordered correctly (left, right)
2639            if let IntervalUnion::TwoDisjoint {
2640                ref left,
2641                ref right,
2642            } = union
2643            {
2644                // left should be i2 (the interval with lower values)
2645                assert_eq!(left, &i2_interval, "Left should be the interval [0.0, 1.0]");
2646                // right should be i1 (the interval with higher values)
2647                assert_eq!(
2648                    right, &i1_interval,
2649                    "Right should be the interval [3.0, 4.0]"
2650                );
2651            } else {
2652                panic!("Expected disjoint union");
2653            }
2654
2655            // Verify commutativity: i1.union(&i2) should equal i2.union(&i1)
2656            let i1 = IntervalClosed::new(3.0, 4.0);
2657            let i2 = IntervalClosed::new(0.0, 1.0);
2658            let union_reversed = i2.union(&i1);
2659            assert_eq!(union, union_reversed, "Union should be commutative");
2660        }
2661
2662        /// Test union of disjoint open intervals where `self` is on the right
2663        #[test]
2664        fn union_disjoint_open_self_on_right() {
2665            // (5.0, 6.0) ∪ (1.0, 2.0) where self is on the right
2666            let i1 = IntervalOpen::new(5.0, 6.0); // self - right interval
2667            let i2 = IntervalOpen::new(1.0, 2.0); // other - left interval
2668            let union = i1.union(&i2);
2669
2670            assert!(union.is_two_disjoint(), "Should be two disjoint intervals");
2671
2672            // Test the gap before destructuring
2673            let gap = union.gap();
2674            assert!(gap.is_some(), "Should have a gap");
2675            if let Some(ref gap_interval) = gap {
2676                assert!(gap_interval.contains_point(&3.0), "Gap should contain 3.0");
2677                assert!(
2678                    gap_interval.contains_point(&2.0),
2679                    "Gap should include lower boundary"
2680                );
2681                assert!(
2682                    gap_interval.contains_point(&5.0),
2683                    "Gap should include upper boundary"
2684                );
2685            }
2686
2687            // Convert to Interval for comparison
2688            let i1_interval: Interval<_> = i1.into();
2689            let i2_interval: Interval<_> = i2.into();
2690
2691            if let IntervalUnion::TwoDisjoint { left, right } = union {
2692                // Intervals should be ordered correctly
2693                assert_eq!(left, i2_interval, "Left should be (1.0, 2.0)");
2694                assert_eq!(right, i1_interval, "Right should be (5.0, 6.0)");
2695            } else {
2696                panic!("Expected TwoDisjoint variant");
2697            }
2698        }
2699
2700        #[test]
2701        fn union_overlapping() {
2702            let i1 = IntervalClosed::new(0.0, 2.0);
2703            let i2 = IntervalClosed::new(1.0, 3.0);
2704            let union = i1.union(&i2);
2705            let expected: Interval<f64> = IntervalClosed::new(0.0, 3.0).into();
2706            if let IntervalUnion::SingleConnected(connected) = union {
2707                assert_eq!(connected, expected);
2708            } else {
2709                panic!("Expected connected union");
2710            }
2711        }
2712
2713        #[test]
2714        fn union_adjacent_closed_open() {
2715            let i1 = IntervalClosed::new(0.0, 1.0);
2716            let i2 = IntervalOpen::new(1.0, 2.0);
2717            let union = i1.union(&i2);
2718            assert!(union.is_single_connected());
2719
2720            let expected: Interval<f64> = IntervalLowerClosedUpperOpen::new(0.0, 2.0).into();
2721            if let IntervalUnion::SingleConnected(connected) = union {
2722                assert_eq!(connected, expected);
2723            } else {
2724                panic!("Expected connected union");
2725            }
2726        }
2727
2728        #[test]
2729        fn union_adjacent_open_closed() {
2730            let i1 = IntervalOpen::new(0.0, 1.0);
2731            let i2 = IntervalClosed::new(1.0, 2.0);
2732            let union = i1.union(&i2);
2733            assert!(union.is_single_connected());
2734
2735            let expected: Interval<f64> = IntervalLowerOpenUpperClosed::new(0.0, 2.0).into();
2736            if let IntervalUnion::SingleConnected(ref connected) = union {
2737                assert_eq!(connected, &expected);
2738            } else {
2739                panic!("Expected connected union");
2740            }
2741
2742            let connected = union.as_single_connected().unwrap();
2743            assert_eq!(connected, &expected);
2744
2745            assert!(union.as_two_disjoint().is_none());
2746
2747            assert!(union.contains_point(&1.0));
2748        }
2749
2750        #[test]
2751        fn union_adjacent_open_open() {
2752            let i1 = IntervalOpen::new(0.0, 1.0);
2753            let i2 = IntervalOpen::new(1.0, 2.0);
2754            let union = i1.union(&i2);
2755            assert!(union.is_two_disjoint());
2756
2757            let i1: Interval<_> = i1.into();
2758            let i2: Interval<_> = i2.into();
2759
2760            if let IntervalUnion::TwoDisjoint {
2761                ref left,
2762                ref right,
2763            } = union
2764            {
2765                assert_eq!(left, &i1);
2766                assert_eq!(right, &i2);
2767            } else {
2768                panic!("Expected disjoint union because of the gap at 1.0");
2769            }
2770
2771            let (left, right) = union.as_two_disjoint().unwrap();
2772            assert_eq!(left, &i1);
2773            assert_eq!(right, &i2);
2774
2775            assert!(union.as_single_connected().is_none());
2776
2777            assert!(!union.contains_point(&1.0));
2778        }
2779
2780        /// Test union of two disjoint open intervals with a gap between them
2781        /// This is different from union_adjacent_open_open where intervals touch at boundary
2782        #[test]
2783        fn union_disjoint_open_open() {
2784            // (0.0, 1.0) ∪ (3.0, 4.0) = TwoDisjoint with gap [1.0, 3.0]
2785            let i1 = IntervalOpen::new(0.0, 1.0);
2786            let i2 = IntervalOpen::new(3.0, 4.0);
2787            let union = i1.union(&i2);
2788
2789            assert!(union.is_two_disjoint(), "Expected two disjoint intervals");
2790
2791            // Test the gap
2792            let gap = union.gap();
2793            assert!(
2794                gap.is_some(),
2795                "Should have a gap between disjoint intervals"
2796            );
2797
2798            if let Some(ref gap_interval) = gap {
2799                // Gap should be [1.0, 3.0]
2800                assert!(
2801                    gap_interval.contains_point(&2.0),
2802                    "Gap should contain point 2.0"
2803                );
2804                assert!(
2805                    gap_interval.contains_point(&1.0),
2806                    "Gap should include lower boundary 1.0"
2807                );
2808                assert!(
2809                    gap_interval.contains_point(&3.0),
2810                    "Gap should include upper boundary 3.0"
2811                );
2812                assert!(
2813                    !gap_interval.contains_point(&0.5),
2814                    "Gap should not contain points in left interval"
2815                );
2816                assert!(
2817                    !gap_interval.contains_point(&3.5),
2818                    "Gap should not contain points in right interval"
2819                );
2820            }
2821
2822            // Test the disjoint intervals
2823            let i1_interval: Interval<_> = i1.into();
2824            let i2_interval: Interval<_> = i2.into();
2825
2826            if let IntervalUnion::TwoDisjoint { left, right } = union {
2827                assert_eq!(left, i1_interval);
2828                assert_eq!(right, i2_interval);
2829            } else {
2830                panic!("Expected TwoDisjoint variant");
2831            }
2832
2833            // Test point containment on new union
2834            let i1 = IntervalOpen::new(0.0, 1.0);
2835            let i2 = IntervalOpen::new(3.0, 4.0);
2836            let union = i1.union(&i2);
2837            assert!(
2838                union.contains_point(&0.5),
2839                "Should contain point in first interval"
2840            );
2841            assert!(
2842                union.contains_point(&3.5),
2843                "Should contain point in second interval"
2844            );
2845            assert!(
2846                !union.contains_point(&2.0),
2847                "Should not contain point in gap"
2848            );
2849            assert!(
2850                !union.contains_point(&1.0),
2851                "Should not contain excluded boundary"
2852            );
2853            assert!(
2854                !union.contains_point(&3.0),
2855                "Should not contain excluded boundary"
2856            );
2857        }
2858
2859        /// Test union of disjoint open intervals with half-open intervals
2860        #[test]
2861        fn union_disjoint_open_half_open() {
2862            // (0.0, 1.0) ∪ [2.0, 3.0) with clear gap
2863            let i1 = IntervalOpen::new(0.0, 1.0);
2864            let i2 = IntervalLowerClosedUpperOpen::new(2.0, 3.0);
2865            let union = i1.union(&i2);
2866
2867            assert!(union.is_two_disjoint());
2868
2869            if let IntervalUnion::TwoDisjoint { left, right } = union {
2870                assert_eq!(left, i1.into());
2871                assert_eq!(right, i2.into());
2872            } else {
2873                panic!("Expected disjoint union");
2874            }
2875        }
2876
2877        #[test]
2878        fn union_one_contains_other() {
2879            let i1 = IntervalClosed::new(0.0, 3.0);
2880            let i2 = IntervalClosed::new(1.0, 2.0);
2881            let union = i1.union(&i2);
2882            if let IntervalUnion::SingleConnected(connected) = union {
2883                assert_eq!(connected, i1.into());
2884            } else {
2885                panic!("Expected connected union");
2886            }
2887        }
2888
2889        #[test]
2890        fn union_with_singleton_inside() {
2891            let i1 = IntervalClosed::new(0.0, 2.0);
2892            let i2 = IntervalSingleton::new(1.0);
2893            let union = i1.union(&i2);
2894            if let IntervalUnion::SingleConnected(connected) = union {
2895                assert_eq!(connected, i1.into());
2896            } else {
2897                panic!("Expected connected union");
2898            }
2899        }
2900
2901        #[test]
2902        fn union_with_singleton_at_boundary_closed() {
2903            let i1 = IntervalClosed::new(0.0, 1.0);
2904            let i2 = IntervalSingleton::new(1.0);
2905            let union = i1.union(&i2);
2906            if let IntervalUnion::SingleConnected(connected) = union {
2907                assert_eq!(connected, i1.into());
2908            } else {
2909                panic!("Expected connected union");
2910            }
2911        }
2912
2913        #[test]
2914        fn union_with_singleton_at_boundary_open() {
2915            let i1 = IntervalLowerClosedUpperOpen::new(0.0, 1.0);
2916            let i2 = IntervalSingleton::new(1.0);
2917            let union = i1.union(&i2);
2918            let expected: Interval<f64> = IntervalClosed::new(0.0, 1.0).into();
2919            if let IntervalUnion::SingleConnected(connected) = union {
2920                assert_eq!(connected, expected);
2921            } else {
2922                panic!("Expected connected union");
2923            }
2924        }
2925
2926        #[test]
2927        fn union_with_singleton_outside() {
2928            let i1 = IntervalClosed::new(0.0, 1.0);
2929            let i2 = IntervalSingleton::new(2.0);
2930            let union = i1.union(&i2);
2931            if let IntervalUnion::TwoDisjoint { left, right } = union {
2932                assert_eq!(left, i1.into());
2933                assert_eq!(right, i2.into());
2934            } else {
2935                panic!("Expected disjoint union");
2936            }
2937        }
2938
2939        #[test]
2940        fn union_unbounded_overlapping() {
2941            let i1 = IntervalLowerClosedUpperUnbounded::new(0.0);
2942            let i2 = IntervalLowerClosedUpperUnbounded::new(1.0);
2943            let union = i1.union(&i2);
2944            if let IntervalUnion::SingleConnected(connected) = union {
2945                assert_eq!(connected, i1.into());
2946            } else {
2947                panic!("Expected connected union");
2948            }
2949        }
2950
2951        #[test]
2952        fn union_lower_unbounded_with_upper_unbounded() {
2953            let i1 = IntervalLowerUnboundedUpperClosed::new(0.0);
2954            let i2 = IntervalLowerClosedUpperUnbounded::new(1.0);
2955            let union = i1.union(&i2);
2956            if let IntervalUnion::TwoDisjoint { left, right } = union {
2957                assert_eq!(left, i1.into());
2958                assert_eq!(right, i2.into());
2959            } else {
2960                panic!("Expected disjoint union for (-inf, 0] U [1, inf)");
2961            }
2962        }
2963
2964        #[test]
2965        fn union_lower_unbounded_with_upper_unbounded_overlapping() {
2966            let i1 = IntervalLowerUnboundedUpperClosed::new(2.0);
2967            let i2 = IntervalLowerClosedUpperUnbounded::new(1.0);
2968            let union = i1.union(&i2);
2969            let expected: Interval<f64> = IntervalLowerUnboundedUpperUnbounded::new().into();
2970            if let IntervalUnion::SingleConnected(connected) = union {
2971                assert_eq!(connected, expected);
2972            } else {
2973                panic!("Expected connected union resulting in (-inf, inf)");
2974            }
2975        }
2976
2977        #[test]
2978        fn union_finite_with_unbounded() {
2979            let i1 = IntervalClosed::new(0.0, 5.0);
2980            let i2 = IntervalLowerClosedUpperUnbounded::new(3.0);
2981            let union = i1.union(&i2);
2982            let expected: Interval<f64> = IntervalLowerClosedUpperUnbounded::new(0.0).into();
2983            if let IntervalUnion::SingleConnected(connected) = union {
2984                assert_eq!(connected, expected);
2985            } else {
2986                panic!("Expected connected union");
2987            }
2988        }
2989
2990        #[test]
2991        fn union_identical_intervals() {
2992            let i1 = IntervalClosed::new(0.0, 1.0);
2993            let i2 = IntervalClosed::new(0.0, 1.0);
2994            let union = i1.union(&i2);
2995            if let IntervalUnion::SingleConnected(connected) = union {
2996                assert_eq!(connected, i1.into());
2997            } else {
2998                panic!("Expected connected union");
2999            }
3000        }
3001
3002        #[test]
3003        fn union_different_bound_types_contained() {
3004            let i1 = IntervalClosed::new(0.0, 2.0);
3005            let i2 = IntervalOpen::new(0.0, 2.0);
3006            let union = i1.union(&i2);
3007            if let IntervalUnion::SingleConnected(connected) = union {
3008                assert_eq!(connected, i1.into());
3009            } else {
3010                panic!("Expected connected union");
3011            }
3012        }
3013
3014        #[test]
3015        fn union_different_bound_types_overlapping() {
3016            let i1 = IntervalLowerClosedUpperOpen::new(0.0, 2.0); // [0, 2)
3017            let i2 = IntervalLowerOpenUpperClosed::new(1.0, 3.0); // (1, 3]
3018            let union = i1.union(&i2);
3019            let expected: Interval<f64> = IntervalClosed::new(0.0, 3.0).into();
3020            if let IntervalUnion::SingleConnected(connected) = union {
3021                assert_eq!(connected, expected);
3022            } else {
3023                panic!("Expected connected union");
3024            }
3025        }
3026
3027        #[test]
3028        fn union_with_fully_unbounded() {
3029            let i1 = IntervalClosed::new(0.0, 1.0);
3030            let i2 = IntervalLowerUnboundedUpperUnbounded::new();
3031            let union = i1.union(&i2);
3032            if let IntervalUnion::SingleConnected(connected) = union {
3033                assert_eq!(connected, i2.into());
3034            } else {
3035                panic!("Expected connected union");
3036            }
3037        }
3038
3039        mod gap {
3040            use super::*;
3041            use num_valid::{RealNative64StrictFinite, functions::Abs};
3042
3043            type Real = RealNative64StrictFinite;
3044
3045            #[test]
3046            fn gap_with_disjoint_intervals_positive_gap() {
3047                // Test case: [1, 2] and [4, 5] with gap (2, 4)
3048                let interval1 =
3049                    IntervalClosed::new(Real::try_new(1.0).unwrap(), Real::try_new(2.0).unwrap());
3050                let interval2 =
3051                    IntervalClosed::new(Real::try_new(4.0).unwrap(), Real::try_new(5.0).unwrap());
3052
3053                let union = interval1.union(&interval2);
3054
3055                let gap = union.gap().unwrap();
3056
3057                // Gap should be the open interval (2, 4)
3058                match gap {
3059                    IntervalFinitePositiveLength::Open(open_gap) => {
3060                        assert_eq!(open_gap.lower_bound_value(), &2.0);
3061                        assert_eq!(open_gap.upper_bound_value(), &4.0);
3062                        assert_eq!(open_gap.length().as_ref(), &2.0);
3063                    }
3064                    _ => panic!("Expected open interval for gap"),
3065                }
3066            }
3067
3068            #[test]
3069            fn gap_with_disjoint_intervals_small_gap() {
3070                // Test case: [0, 1] and [1.5, 2] with gap (1, 1.5)
3071                let interval1 =
3072                    IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(1.0).unwrap());
3073                let interval2 =
3074                    IntervalClosed::new(Real::try_new(1.5).unwrap(), Real::try_new(2.0).unwrap());
3075
3076                let union = interval1.union(&interval2);
3077
3078                let gap = union.gap().unwrap();
3079
3080                match gap {
3081                    IntervalFinitePositiveLength::Open(open_gap) => {
3082                        assert_eq!(open_gap.lower_bound_value(), &1.0);
3083                        assert_eq!(open_gap.upper_bound_value(), &1.5);
3084                        assert_eq!(open_gap.length().as_ref(), &0.5);
3085                    }
3086                    _ => panic!("Expected open interval for gap"),
3087                }
3088            }
3089
3090            #[test]
3091            fn gap_with_disjoint_half_open_intervals() {
3092                // Test case: [0, 1) and (2, 3] with gap [1, 2]
3093                let interval1 = IntervalLowerClosedUpperOpen::new(
3094                    Real::try_new(0.0).unwrap(),
3095                    Real::try_new(1.0).unwrap(),
3096                );
3097                let interval2 = IntervalLowerOpenUpperClosed::new(
3098                    Real::try_new(2.0).unwrap(),
3099                    Real::try_new(3.0).unwrap(),
3100                );
3101
3102                let union = interval1.union(&interval2);
3103
3104                let gap = union.gap().unwrap();
3105
3106                match gap {
3107                    IntervalFinitePositiveLength::Closed(closed_gap) => {
3108                        assert_eq!(closed_gap.lower_bound_value(), &1.0);
3109                        assert_eq!(closed_gap.upper_bound_value(), &2.0);
3110                        assert_eq!(closed_gap.length().as_ref(), &1.0);
3111                    }
3112                    _ => panic!("Expected closed interval for gap"),
3113                }
3114            }
3115
3116            #[test]
3117            fn gap_with_touching_intervals_no_gap() {
3118                // Test case: [1, 2] and [2, 3] - touching at point 2, no gap
3119                let interval1 =
3120                    IntervalClosed::new(Real::try_new(1.0).unwrap(), Real::try_new(2.0).unwrap());
3121                let interval2 =
3122                    IntervalClosed::new(Real::try_new(2.0).unwrap(), Real::try_new(3.0).unwrap());
3123
3124                let union = interval1.union(&interval2);
3125
3126                assert!(union.gap().is_none());
3127            }
3128
3129            #[test]
3130            fn gap_with_overlapping_intervals_no_gap() {
3131                // Test case: [1, 3] and [2, 4] - overlapping, no gap
3132                let interval1 =
3133                    IntervalClosed::new(Real::try_new(1.0).unwrap(), Real::try_new(3.0).unwrap());
3134                let interval2 =
3135                    IntervalClosed::new(Real::try_new(2.0).unwrap(), Real::try_new(4.0).unwrap());
3136
3137                let union = interval1.union(&interval2);
3138
3139                assert!(union.gap().is_none());
3140            }
3141
3142            #[test]
3143            fn gap_with_open_intervals_disjoint() {
3144                // Test case: (0, 1) and (2, 3) with gap [1, 2]
3145                let interval1 =
3146                    IntervalOpen::new(Real::try_new(0.0).unwrap(), Real::try_new(1.0).unwrap());
3147                let interval2 =
3148                    IntervalOpen::new(Real::try_new(2.0).unwrap(), Real::try_new(3.0).unwrap());
3149
3150                let union = interval1.union(&interval2);
3151
3152                let gap = union.gap().unwrap();
3153
3154                match gap {
3155                    IntervalFinitePositiveLength::Closed(closed_gap) => {
3156                        assert_eq!(closed_gap.lower_bound_value(), &1.0);
3157                        assert_eq!(closed_gap.upper_bound_value(), &2.0);
3158                        assert_eq!(closed_gap.length().as_ref(), &1.0);
3159                    }
3160                    _ => panic!("Expected closed interval for gap"),
3161                }
3162            }
3163
3164            #[test]
3165            fn gap_with_single_connected_interval() {
3166                // Test case: Single interval [1, 5] - no gap possible
3167                let interval =
3168                    IntervalClosed::new(Real::try_new(1.0).unwrap(), Real::try_new(5.0).unwrap());
3169
3170                let union = interval.union(&interval);
3171
3172                assert!(union.gap().is_none());
3173            }
3174
3175            #[test]
3176            fn gap_with_very_small_gap() {
3177                // Test case: [0, 1] and [1 + ε, 2] where ε is very small
3178                let epsilon = PositiveRealScalar::try_new(Real::try_new(1e-10).unwrap()).unwrap();
3179                let interval1 =
3180                    IntervalClosed::new(Real::try_new(0.0).unwrap(), Real::try_new(1.0).unwrap());
3181                let interval2 = IntervalClosed::new(
3182                    Real::try_new(1.0).unwrap() + epsilon.as_ref(),
3183                    Real::try_new(2.0).unwrap(),
3184                );
3185
3186                let union = interval1.union(&interval2);
3187
3188                let gap = union.gap().unwrap();
3189
3190                match gap {
3191                    IntervalFinitePositiveLength::Open(open_gap) => {
3192                        assert_eq!(open_gap.lower_bound_value(), &1.0);
3193                        assert_eq!(
3194                            open_gap.upper_bound_value(),
3195                            &(Real::try_new(1.0).unwrap() + epsilon.as_ref())
3196                        );
3197                        more_asserts::assert_lt!(
3198                            (open_gap.length().into_inner() - epsilon.as_ref()).abs(),
3199                            1.0e-16
3200                        );
3201                    }
3202                    _ => panic!("Expected open interval for very small gap"),
3203                }
3204            }
3205
3206            #[test]
3207            fn gap_with_negative_intervals() {
3208                // Test case: [-5, -3] and [-1, 1] with gap (-3, -1)
3209                let interval1 =
3210                    IntervalClosed::new(Real::try_new(-5.0).unwrap(), Real::try_new(-3.0).unwrap());
3211                let interval2 =
3212                    IntervalClosed::new(Real::try_new(-1.0).unwrap(), Real::try_new(1.0).unwrap());
3213
3214                let union = IntervalUnion::TwoDisjoint {
3215                    left: interval1.into(),
3216                    right: interval2.into(),
3217                };
3218
3219                let gap = union.gap().unwrap();
3220
3221                match gap {
3222                    IntervalFinitePositiveLength::Open(open_gap) => {
3223                        assert_eq!(open_gap.lower_bound_value(), &-3.0);
3224                        assert_eq!(open_gap.upper_bound_value(), &-1.0);
3225                        assert_eq!(open_gap.length().as_ref(), &2.0);
3226                    }
3227                    _ => panic!("Expected open interval for gap"),
3228                }
3229            }
3230
3231            #[test]
3232            fn gap_with_mixed_boundary_types() {
3233                // Test case: (0, 1] and [3, 4) with gap (1, 3)
3234                let interval1 = IntervalLowerOpenUpperClosed::new(
3235                    Real::try_new(0.0).unwrap(),
3236                    Real::try_new(1.0).unwrap(),
3237                );
3238                let interval2 = IntervalLowerClosedUpperOpen::new(
3239                    Real::try_new(3.0).unwrap(),
3240                    Real::try_new(4.0).unwrap(),
3241                );
3242
3243                let union = IntervalUnion::TwoDisjoint {
3244                    left: interval1.into(),
3245                    right: interval2.into(),
3246                };
3247
3248                let gap = union.gap().unwrap();
3249
3250                match gap {
3251                    IntervalFinitePositiveLength::Open(open_gap) => {
3252                        assert_eq!(open_gap.lower_bound_value(), &1.0);
3253                        assert_eq!(open_gap.upper_bound_value(), &3.0);
3254                        assert_eq!(open_gap.length().as_ref(), &2.0);
3255                    }
3256                    _ => panic!("Expected open interval for gap"),
3257                }
3258            }
3259
3260            #[test]
3261            fn gap_preserves_boundary_semantics() {
3262                // Test that the gap correctly reflects which boundaries should be included
3263                // based on the original intervals' boundary types
3264
3265                // Case 1: [0, 1) and (2, 3] should have gap [1, 2]
3266                let interval1 = IntervalLowerClosedUpperOpen::new(
3267                    Real::try_new(0.0).unwrap(),
3268                    Real::try_new(1.0).unwrap(),
3269                );
3270                let interval2 = IntervalLowerOpenUpperClosed::new(
3271                    Real::try_new(2.0).unwrap(),
3272                    Real::try_new(3.0).unwrap(),
3273                );
3274
3275                let union = IntervalUnion::TwoDisjoint {
3276                    left: interval1.into(),
3277                    right: interval2.into(),
3278                };
3279
3280                let gap = union.gap().unwrap();
3281
3282                // Gap should include both 1 and 2 since they're excluded from original intervals
3283                match gap {
3284                    IntervalFinitePositiveLength::Closed(closed_gap) => {
3285                        assert_eq!(closed_gap.lower_bound_value(), &1.0);
3286                        assert_eq!(closed_gap.upper_bound_value(), &2.0);
3287                        assert!(closed_gap.contains_point(&Real::try_new(1.0).unwrap()));
3288                        assert!(closed_gap.contains_point(&Real::try_new(2.0).unwrap()));
3289                    }
3290                    _ => panic!("Expected closed interval for gap"),
3291                }
3292            }
3293
3294            #[test]
3295            fn gap_with_singleton_intervals() {
3296                // Test gap between two singleton intervals
3297                let singleton1 = IntervalSingleton::new(Real::try_new(1.0).unwrap());
3298                let singleton2 = IntervalSingleton::new(Real::try_new(3.0).unwrap());
3299
3300                let union = IntervalUnion::TwoDisjoint {
3301                    left: singleton1.into(),
3302                    right: singleton2.into(),
3303                };
3304
3305                let gap = union.gap().unwrap();
3306
3307                match gap {
3308                    IntervalFinitePositiveLength::Open(open_gap) => {
3309                        assert_eq!(open_gap.lower_bound_value(), &1.0);
3310                        assert_eq!(open_gap.upper_bound_value(), &3.0);
3311                        assert_eq!(open_gap.length().as_ref(), &2.0);
3312                        // Gap should not contain the singleton endpoints
3313                        assert!(!open_gap.contains_point(&Real::try_new(1.0).unwrap()));
3314                        assert!(!open_gap.contains_point(&Real::try_new(3.0).unwrap()));
3315                    }
3316                    _ => panic!("Expected open interval for gap between singletons"),
3317                }
3318            }
3319        }
3320    }
3321}