intervalsets/traits/
merged.rs

1use crate::numeric::Domain;
2use crate::Interval;
3
4/// Defines the union of two intervals if contiguous.
5///
6/// Two intervals are contiguous if they share any elements **or** if
7/// they are **adjacent** to each other such that they share bounds
8/// with no other elements possible between them.
9///
10/// Other **disjoint sets** return `None` unless one is the `Empty` Set,
11/// in which case the other input Set is the result.
12///
13/// # Note
14///
15/// > For types that do not implement Eq (such as primitive floats),
16/// > the adjacency check breaks down. We support floats for their
17/// > utility, but leave handling the edge cases to the end user.
18/// > If rigorous correctness is required, then a fixed precision
19/// > type should be used instead.
20///
21/// # Example
22/// ```
23/// use intervalsets::Interval;
24/// use intervalsets::ops::Merged;
25///
26/// let x = Interval::closed(0, 10);
27/// let y = Interval::closed(11, 20);
28/// assert_eq!(x.merged(&y).unwrap(), Interval::closed(0, 20));
29///
30/// let y = Interval::closed(20, 30);
31/// assert_eq!(x.merged(&y), None);
32///
33/// let y = Interval::<i32>::empty();
34/// assert_eq!(x.merged(&y).unwrap(), x);
35///
36/// let x = Interval::<i32>::empty();
37/// assert_eq!(x.merged(&y).unwrap(), Interval::empty());
38/// ```
39pub trait Merged<Rhs = Self> {
40    type Output;
41
42    fn merged(&self, rhs: &Rhs) -> Option<Self::Output>;
43}
44
45impl<T: Domain> Merged<Self> for Interval<T> {
46    type Output = Self;
47
48    fn merged(&self, rhs: &Self) -> Option<Self::Output> {
49        self.0.merged(&rhs.0).map(|v| v.into())
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use crate::ops::Complement;
56
57    use super::*;
58
59    #[quickcheck]
60    fn check_merge_half_complements_f32(x: f32) {
61        if x.is_nan() {
62            return;
63        }
64
65        let x = Interval::unbound_closed(x);
66        let y = x.complement().intervals()[0].clone();
67
68        assert_eq!(x.merged(&y).unwrap(), Interval::unbounded());
69    }
70
71    #[quickcheck]
72    fn check_merge_half_complements_i32(x: i32) {
73        let x = Interval::closed_unbound(x);
74        let y = x.complement().intervals()[0].clone();
75
76        assert_eq!(x.merged(&y).unwrap(), Interval::unbounded());
77    }
78
79    #[test]
80    fn test_merged_max_i32() {
81        let x = Interval::closed(0, i32::MAX);
82        let y = Interval::closed(-100, -1);
83
84        assert_eq!(x.merged(&y).unwrap(), Interval::closed(-100, i32::MAX));
85    }
86}
87
88#[cfg(feature = "rust_decimal")]
89#[cfg(test)]
90mod decimal_test {
91    use super::*;
92    use crate::MaybeEmpty;
93    use rust_decimal::Decimal;
94
95    #[quickcheck]
96    fn check_decimal_merge(a: f32, b: f32, c: f32) {
97        let a = Decimal::from_f32_retain(a);
98        let b = Decimal::from_f32_retain(b);
99        let c = Decimal::from_f32_retain(c);
100        if a.is_none() || b.is_none() || c.is_none() {
101            return;
102        }
103
104        let a = a.unwrap();
105        let b = b.unwrap();
106        let c = c.unwrap();
107
108        let left = Interval::open(a.clone(), b.clone());
109        let right = Interval::closed(b.clone(), c.clone());
110
111        let merged = left.merged(&right).unwrap();
112        if left.is_empty() {
113            assert_eq!(merged, right);
114        } else if right.is_empty() {
115            assert_eq!(merged, left);
116        } else {
117            assert_eq!(
118                left.merged(&right).unwrap(),
119                Interval::open_closed(a.clone(), c.clone())
120            );
121        }
122    }
123}