1use super::*;
2
3#[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 Open(V),
13
14 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
91macro_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
157impl<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
222impl<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 #[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 #[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}