intervals/bounds/
mixed.rs

1use super::*;
2
3/// Union type representing a bound that is either open or closed.
4#[derive(Debug, Clone, Copy, PartialEq)]
5#[cfg_attr(
6    feature = "serde",
7    derive(Serialize, Deserialize),
8    serde(crate = "serde_crate")
9)]
10pub enum OpenOrClosed<V> {
11    /// The open bound variant.
12    Open(V),
13
14    /// The closed bound variant.
15    Closed(V),
16}
17
18impl<V> OpenOrClosed<V> {
19    pub fn unwrap(self) -> V {
20        match self {
21            OpenOrClosed::Open(x) | OpenOrClosed::Closed(x) => x,
22        }
23    }
24}
25
26impl<V> From<Open<V>> for OpenOrClosed<V> {
27    fn from(bound: Open<V>) -> OpenOrClosed<V> { OpenOrClosed::Open(bound.0) }
28}
29
30impl<V> From<Closed<V>> for OpenOrClosed<V> {
31    fn from(bound: Closed<V>) -> OpenOrClosed<V> { OpenOrClosed::Closed(bound.0) }
32}
33
34impl<V> crate::private::Sealed for OpenOrClosed<V> {}
35
36impl<V: PartialOrd> Bound for OpenOrClosed<V> {
37    type Value = V;
38    type WithLimit = Closed<V>;
39
40    fn value(&self) -> Option<&Self::Value> {
41        match self {
42            OpenOrClosed::Open(ref v) | OpenOrClosed::Closed(ref v) => Some(v),
43        }
44    }
45
46    fn is_open(&self) -> bool {
47        match self {
48            OpenOrClosed::Open(_) => true,
49            OpenOrClosed::Closed(_) => false,
50        }
51    }
52
53    fn is_closed(&self) -> bool {
54        match self {
55            OpenOrClosed::Open(_) => false,
56            OpenOrClosed::Closed(_) => true,
57        }
58    }
59
60    fn with_limit_point(self) -> Self::WithLimit {
61        match self {
62            OpenOrClosed::Open(v) | OpenOrClosed::Closed(v) => Closed(v),
63        }
64    }
65}
66
67impl<V: PartialOrd> ProperBound for OpenOrClosed<V> {
68    fn proper_value(&self) -> &Self::Value {
69        match self {
70            OpenOrClosed::Open(ref v) | OpenOrClosed::Closed(ref v) => v,
71        }
72    }
73}
74
75impl<V: PartialOrd + fmt::Display> BoundDisplay for OpenOrClosed<V> {
76    fn fmt_left(&self, f: &mut fmt::Formatter) -> fmt::Result {
77        match self {
78            OpenOrClosed::Open(v) => Open(v).fmt_left(f),
79            OpenOrClosed::Closed(v) => Closed(v).fmt_left(f),
80        }
81    }
82
83    fn fmt_right(&self, f: &mut fmt::Formatter) -> fmt::Result {
84        match self {
85            OpenOrClosed::Open(v) => Open(v).fmt_right(f),
86            OpenOrClosed::Closed(v) => Closed(v).fmt_right(f),
87        }
88    }
89}
90
91// Pinch:
92macro_rules! impl_pinch {
93    ($v:ident; $other:ty) => {
94        impl<$v: PartialOrd> Pinch<$other> for OpenOrClosed<$v> {
95            type Left = OpenOrClosed<$v>;
96            type Right = OpenOrClosed<$v>;
97
98            fn pinch_left(self, other: $other) -> OpenOrClosed<$v> {
99                match self {
100                    OpenOrClosed::Open(x) => Open(x).pinch_left(other).into(),
101                    OpenOrClosed::Closed(x) => Closed(x).pinch_left(other).into(),
102                }
103            }
104
105            fn pinch_right(self, other: $other) -> OpenOrClosed<$v> {
106                match self {
107                    OpenOrClosed::Open(x) => Open(x).pinch_right(other).into(),
108                    OpenOrClosed::Closed(x) => Closed(x).pinch_right(other).into(),
109                }
110            }
111        }
112
113        impl<$v: PartialOrd> Pinch<OpenOrClosed<$v>> for $other {
114            type Left = OpenOrClosed<$v>;
115            type Right = OpenOrClosed<$v>;
116
117            fn pinch_left(self, other: OpenOrClosed<$v>) -> OpenOrClosed<$v> {
118                match other {
119                    OpenOrClosed::Open(x) => self.pinch_left(Open(x)).into(),
120                    OpenOrClosed::Closed(x) => self.pinch_left(Closed(x)).into(),
121                }
122            }
123
124            fn pinch_right(self, other: OpenOrClosed<$v>) -> OpenOrClosed<$v> {
125                match other {
126                    OpenOrClosed::Open(x) => self.pinch_right(Open(x)).into(),
127                    OpenOrClosed::Closed(x) => self.pinch_right(Closed(x)).into(),
128                }
129            }
130        }
131    };
132}
133
134impl_pinch!(V; Open<V>);
135impl_pinch!(V; Closed<V>);
136impl_pinch!(V; NoBound<V>);
137
138impl<V: PartialOrd> Pinch<OpenOrClosed<V>> for OpenOrClosed<V> {
139    type Left = OpenOrClosed<V>;
140    type Right = OpenOrClosed<V>;
141
142    fn pinch_left(self, other: OpenOrClosed<V>) -> OpenOrClosed<V> {
143        match self {
144            OpenOrClosed::Open(x) => Open(x).pinch_left(other).into(),
145            OpenOrClosed::Closed(x) => Closed(x).pinch_left(other).into(),
146        }
147    }
148
149    fn pinch_right(self, other: OpenOrClosed<V>) -> OpenOrClosed<V> {
150        match self {
151            OpenOrClosed::Open(x) => Open(x).pinch_right(other).into(),
152            OpenOrClosed::Closed(x) => Closed(x).pinch_right(other).into(),
153        }
154    }
155}
156
157// Unroll:
158impl<V: PartialOrd> Unroll<OpenOrClosed<V>> for OpenOrClosed<V> {
159    type Left = OpenOrClosed<V>;
160    type Right = OpenOrClosed<V>;
161
162    fn unroll_left(self, other: OpenOrClosed<V>) -> OpenOrClosed<V> {
163        match self {
164            OpenOrClosed::Open(x) => Open(x).unroll_left(other).into(),
165            OpenOrClosed::Closed(x) => Closed(x).unroll_left(other).into(),
166        }
167    }
168
169    fn unroll_right(self, other: OpenOrClosed<V>) -> OpenOrClosed<V> {
170        match self {
171            OpenOrClosed::Open(x) => Open(x).unroll_right(other).into(),
172            OpenOrClosed::Closed(x) => Closed(x).unroll_right(other).into(),
173        }
174    }
175}
176
177macro_rules! impl_unroll {
178    ($v:ident; $other:ty) => {
179        impl<$v: PartialOrd> Unroll<$other> for OpenOrClosed<$v> {
180            type Left = OpenOrClosed<$v>;
181            type Right = OpenOrClosed<$v>;
182
183            fn unroll_left(self, other: $other) -> OpenOrClosed<$v> {
184                match self {
185                    OpenOrClosed::Open(x) => Open(x).unroll_left(other).into(),
186                    OpenOrClosed::Closed(x) => Closed(x).unroll_left(other).into(),
187                }
188            }
189
190            fn unroll_right(self, other: $other) -> OpenOrClosed<$v> {
191                match self {
192                    OpenOrClosed::Open(x) => Open(x).unroll_right(other).into(),
193                    OpenOrClosed::Closed(x) => Closed(x).unroll_right(other).into(),
194                }
195            }
196        }
197
198        impl<$v: PartialOrd> Unroll<OpenOrClosed<$v>> for $other {
199            type Left = OpenOrClosed<$v>;
200            type Right = OpenOrClosed<$v>;
201
202            fn unroll_left(self, other: OpenOrClosed<$v>) -> OpenOrClosed<$v> {
203                match other {
204                    OpenOrClosed::Open(x) => self.unroll_left(Open(x)).into(),
205                    OpenOrClosed::Closed(x) => self.unroll_left(Closed(x)).into(),
206                }
207            }
208
209            fn unroll_right(self, other: OpenOrClosed<$v>) -> OpenOrClosed<$v> {
210                match other {
211                    OpenOrClosed::Open(x) => self.unroll_right(Open(x)).into(),
212                    OpenOrClosed::Closed(x) => self.unroll_right(Closed(x)).into(),
213                }
214            }
215        }
216    };
217}
218
219impl_unroll!(V; Open<V>);
220impl_unroll!(V; Closed<V>);
221
222// Comparison:
223impl<V: PartialEq> std::cmp::PartialEq<Open<V>> for OpenOrClosed<V> {
224    fn eq(&self, rhs: &Open<V>) -> bool {
225        match self {
226            &OpenOrClosed::Open(ref inner) => inner.eq(&rhs.0),
227            _ => false,
228        }
229    }
230}
231
232impl<V: PartialEq> std::cmp::PartialEq<Closed<V>> for OpenOrClosed<V> {
233    fn eq(&self, rhs: &Closed<V>) -> bool {
234        match self {
235            &OpenOrClosed::Closed(ref inner) => inner.eq(&rhs.0),
236            _ => false,
237        }
238    }
239}
240
241impl<V> std::cmp::PartialEq<NoBound<V>> for OpenOrClosed<V> {
242    fn eq(&self, _: &NoBound<V>) -> bool { false }
243}
244
245#[cfg(test)]
246mod tests {
247    use super::*;
248
249    // OpenOrClosed::Open
250    #[test]
251    fn test_open_core_properties() {
252        for x in [-2.0, -1.0, 0.0, 1.0, 2.0] {
253            let a = OpenOrClosed::Open(x);
254
255            assert!(a.is_open());
256            assert!(!a.is_closed());
257
258            assert_eq!(a.proper_value(), &x);
259            assert_eq!(a.value().unwrap(), &x);
260            assert_eq!(a.with_limit_point(), Closed(x));
261        }
262    }
263
264    #[test]
265    fn test_open_pinch_nobound() {
266        let a = OpenOrClosed::Open(0.0f64);
267
268        assert_eq!(a.pinch_left(NoBound::new()), a);
269        assert_eq!(a.pinch_right(NoBound::new()), a);
270    }
271
272    #[test]
273    fn test_open_pinch_open() {
274        let a = OpenOrClosed::Open(0.0f64);
275
276        for x in [-2.0, -1.0, 0.0, 1.0, 2.0] {
277            assert_eq!(a.pinch_left(Open(x)), Open(x.max(0.0)));
278            assert_eq!(a.pinch_right(Open(x)), Open(x.min(0.0)));
279
280            assert_eq!(a.pinch_left(OpenOrClosed::Open(x)), Open(x.max(0.0)));
281            assert_eq!(a.pinch_right(OpenOrClosed::Open(x)), Open(x.min(0.0)));
282        }
283    }
284
285    #[test]
286    fn test_open_pinch_closed() {
287        let a = OpenOrClosed::Open(0.0f64);
288
289        for x in [-2.0, -1.0, 0.0, 1.0, 2.0] {
290            if x <= 0.0 {
291                assert_eq!(a.pinch_left(Closed(x)), Open(0.0));
292                assert_eq!(a.pinch_left(OpenOrClosed::Closed(x)), Open(0.0));
293            } else {
294                assert_eq!(a.pinch_left(Closed(x)), Closed(x));
295                assert_eq!(a.pinch_left(OpenOrClosed::Closed(x)), Closed(x));
296            }
297
298            if x >= 0.0 {
299                assert_eq!(a.pinch_right(Closed(x)), Open(0.0));
300                assert_eq!(a.pinch_right(OpenOrClosed::Closed(x)), Open(0.0));
301            } else {
302                assert_eq!(a.pinch_right(Closed(x)), Closed(x));
303                assert_eq!(a.pinch_right(OpenOrClosed::Closed(x)), Closed(x));
304            }
305        }
306    }
307
308    #[test]
309    fn test_open_unroll_nobound() {
310        let a = OpenOrClosed::Open(0.0f64);
311
312        assert_eq!(a.unroll_left(NoBound::new()), NoBound::new());
313        assert_eq!(a.unroll_right(NoBound::new()), NoBound::new());
314    }
315
316    #[test]
317    fn test_open_unroll_open() {
318        let a = OpenOrClosed::Open(0.0f64);
319
320        for x in [-2.0, -1.0, 0.0, 1.0, 2.0] {
321            assert_eq!(a.unroll_left(Open(x)), Open(x.min(0.0)));
322            assert_eq!(a.unroll_right(Open(x)), Open(x.max(0.0)));
323
324            assert_eq!(a.unroll_left(OpenOrClosed::Open(x)), Open(x.min(0.0)));
325            assert_eq!(a.unroll_right(OpenOrClosed::Open(x)), Open(x.max(0.0)));
326        }
327    }
328
329    #[test]
330    fn test_open_unroll_closed() {
331        let a = OpenOrClosed::Open(0.0f64);
332
333        for x in [-2.0, -1.0, 0.0, 1.0, 2.0] {
334            if x <= 0.0 {
335                assert_eq!(a.unroll_left(Closed(x)), Closed(x));
336                assert_eq!(a.unroll_left(OpenOrClosed::Closed(x)), Closed(x));
337            } else {
338                assert_eq!(a.unroll_left(Closed(x)), Open(0.0));
339                assert_eq!(a.unroll_left(OpenOrClosed::Closed(x)), Open(0.0));
340            }
341
342            if x >= 0.0 {
343                assert_eq!(a.unroll_right(Closed(x)), Closed(x));
344                assert_eq!(a.unroll_right(OpenOrClosed::Closed(x)), Closed(x));
345            } else {
346                assert_eq!(a.unroll_right(Closed(x)), Open(0.0));
347                assert_eq!(a.unroll_right(OpenOrClosed::Closed(x)), Open(0.0));
348            }
349        }
350    }
351
352    // OpenOrClosed::Closed
353    #[test]
354    fn test_closed_core_properties() {
355        for x in [-2.0, -1.0, 0.0, 1.0, 2.0] {
356            let a = OpenOrClosed::Closed(x);
357
358            assert!(!a.is_open());
359            assert!(a.is_closed());
360
361            assert_eq!(a.proper_value(), &x);
362            assert_eq!(a.value().unwrap(), &x);
363            assert_eq!(a.with_limit_point(), a);
364        }
365    }
366
367    #[test]
368    fn test_closed_pinch_nobound() {
369        let a = OpenOrClosed::Closed(0.0f64);
370
371        assert_eq!(a.pinch_left(NoBound::new()), a);
372        assert_eq!(a.pinch_right(NoBound::new()), a);
373    }
374
375    #[test]
376    fn test_closed_pinch_closed() {
377        let a = OpenOrClosed::Closed(0.0f64);
378
379        for x in [-2.0, -1.0, 0.0, 1.0, 2.0] {
380            assert_eq!(a.pinch_left(Closed(x)), Closed(x.max(0.0)));
381            assert_eq!(a.pinch_right(Closed(x)), Closed(x.min(0.0)));
382
383            assert_eq!(a.pinch_left(OpenOrClosed::Closed(x)), Closed(x.max(0.0)));
384            assert_eq!(a.pinch_right(OpenOrClosed::Closed(x)), Closed(x.min(0.0)));
385        }
386    }
387
388    #[test]
389    fn test_closed_pinch_open() {
390        let a = OpenOrClosed::Closed(0.0f64);
391
392        for x in [-2.0, -1.0, 0.0, 1.0, 2.0] {
393            if x < 0.0 {
394                assert_eq!(a.pinch_left(Open(x)), Closed(0.0));
395                assert_eq!(a.pinch_left(OpenOrClosed::Open(x)), Closed(0.0));
396            } else {
397                assert_eq!(a.pinch_left(Open(x)), Open(x));
398                assert_eq!(a.pinch_left(OpenOrClosed::Open(x)), Open(x));
399            }
400
401            if x > 0.0 {
402                assert_eq!(a.pinch_right(Open(x)), Closed(0.0));
403                assert_eq!(a.pinch_right(OpenOrClosed::Open(x)), Closed(0.0));
404            } else {
405                assert_eq!(a.pinch_right(Open(x)), Open(x));
406                assert_eq!(a.pinch_right(OpenOrClosed::Open(x)), Open(x));
407            }
408        }
409    }
410
411    #[test]
412    fn test_closed_unroll_nobound() {
413        let a = OpenOrClosed::Closed(0.0f64);
414
415        assert_eq!(a.unroll_left(NoBound::new()), NoBound::new());
416        assert_eq!(a.unroll_right(NoBound::new()), NoBound::new());
417    }
418
419    #[test]
420    fn test_closed_unroll_closed() {
421        let a = OpenOrClosed::Closed(0.0f64);
422
423        for x in [-2.0, -1.0, 0.0, 1.0, 2.0] {
424            assert_eq!(a.unroll_left(Closed(x)), Closed(x.min(0.0)));
425            assert_eq!(a.unroll_right(Closed(x)), Closed(x.max(0.0)));
426
427            assert_eq!(a.unroll_left(OpenOrClosed::Closed(x)), Closed(x.min(0.0)));
428            assert_eq!(a.unroll_right(OpenOrClosed::Closed(x)), Closed(x.max(0.0)));
429        }
430    }
431
432    #[test]
433    fn test_closed_unroll_open() {
434        let a = OpenOrClosed::Closed(0.0f64);
435
436        for x in [-2.0, -1.0, 0.0, 1.0, 2.0] {
437            if x < 0.0 {
438                assert_eq!(a.unroll_left(Open(x)), Open(x));
439                assert_eq!(a.unroll_left(OpenOrClosed::Open(x)), Open(x));
440            } else {
441                assert_eq!(a.unroll_left(Open(x)), Closed(0.0));
442                assert_eq!(a.unroll_left(OpenOrClosed::Open(x)), Closed(0.0));
443            }
444
445            if x > 0.0 {
446                assert_eq!(a.unroll_right(Open(x)), Open(x));
447                assert_eq!(a.unroll_right(OpenOrClosed::Open(x)), Open(x));
448            } else {
449                assert_eq!(a.unroll_right(Open(x)), Closed(0.0));
450                assert_eq!(a.unroll_right(OpenOrClosed::Open(x)), Closed(0.0));
451            }
452        }
453    }
454}