ranges/generic_range/
singleton.rs

1use core::ops::Bound;
2
3use crate::{Domain, GenericRange};
4
5impl<T: Domain> GenericRange<T> {
6    /// Constructs a singleton by cloning the input storing it in included bounds for both start
7    /// and end.
8    ///
9    /// # Example
10    /// ```
11    /// use core::ops::Bound;
12    /// use ranges::GenericRange;
13    ///
14    /// assert_eq!(
15    ///     GenericRange::singleton(42),
16    ///     (Bound::Included(42), Bound::Included(42)).into()
17    /// );
18    /// ```
19    #[must_use]
20    pub fn singleton(value: T) -> Self
21    where
22        T: Clone,
23    {
24        Self::new_with_bounds(Bound::Included(value.clone()), Bound::Included(value))
25    }
26
27    /// Returns true if the range only contains a single item.
28    ///
29    /// # Examples
30    /// ```
31    /// use core::ops::Bound;
32    /// use ranges::GenericRange;
33    ///
34    /// assert!(!GenericRange::from(1..1).is_singleton());
35    /// assert!(GenericRange::from(1..2).is_singleton());
36    ///
37    /// assert!(GenericRange::from(1..=1).is_singleton());
38    /// assert!(!GenericRange::from(1..=2).is_singleton());
39    ///
40    /// assert!(GenericRange::from((Bound::Excluded(1), Bound::Included(2))).is_singleton());
41    /// ```
42    #[allow(clippy::needless_borrowed_reference)]
43    #[must_use]
44    pub fn is_singleton(&self) -> bool {
45        if self.is_empty() {
46            return false;
47        }
48
49        if <T as Domain>::DISCRETE {
50            match (&self.start, &self.end) {
51                (&Bound::Unbounded, _) | (_, &Bound::Unbounded) => false,
52
53                (&Bound::Included(ref x), &Bound::Excluded(ref y))
54                | (&Bound::Excluded(ref x), &Bound::Included(ref y)) => x < y && x.is_next_to(y),
55                (&Bound::Included(ref x), &Bound::Included(ref y)) => x == y,
56
57                (&Bound::Excluded(ref x), &Bound::Excluded(ref y)) => x.shares_neighbour_with(y),
58            }
59        } else {
60            match (&self.start, &self.end) {
61                (&Bound::Included(ref x), &Bound::Included(ref y)) => x == y,
62                _ => false,
63            }
64        }
65    }
66}
67
68impl<T: Domain + Clone> From<T> for GenericRange<T> {
69    #[must_use]
70    fn from(val: T) -> Self {
71        Self::singleton(val)
72    }
73}
74
75#[cfg(test)]
76mod tests_discrete {
77    use core::ops::Bound;
78
79    use crate::GenericRange;
80
81    #[test]
82    fn in_ex() {
83        assert!(!GenericRange::from(1..1).is_singleton());
84        assert!(GenericRange::from(1..2).is_singleton());
85        assert!(!GenericRange::from(1..42).is_singleton());
86    }
87
88    #[test]
89    fn in_in() {
90        assert!(GenericRange::from(1..=1).is_singleton());
91        assert!(!GenericRange::from(1..=2).is_singleton());
92        assert!(!GenericRange::from(1..=42).is_singleton());
93    }
94
95    #[test]
96    fn unbound() {
97        assert!(!GenericRange::from(1..).is_singleton());
98        assert!(!GenericRange::from(..2).is_singleton());
99        assert!(!GenericRange::from(..=2).is_singleton());
100        let generic: GenericRange<usize> = GenericRange::from(..);
101        assert!(!generic.is_singleton());
102        assert!(!GenericRange::from((Bound::Excluded(1), Bound::Unbounded)).is_singleton());
103    }
104
105    #[test]
106    fn ex_ex() {
107        assert!(!GenericRange::from((Bound::Excluded(1), Bound::Excluded(1))).is_singleton());
108        assert!(!GenericRange::from((Bound::Excluded(1), Bound::Excluded(2))).is_singleton());
109        assert!(!GenericRange::from((Bound::Excluded(1), Bound::Excluded(42))).is_singleton());
110    }
111
112    #[test]
113    fn ex_in() {
114        assert!(!GenericRange::from((Bound::Excluded(1), Bound::Included(1))).is_singleton());
115        assert!(GenericRange::from((Bound::Excluded(1), Bound::Included(2))).is_singleton());
116        assert!(!GenericRange::from((Bound::Excluded(1), Bound::Included(42))).is_singleton());
117    }
118}
119
120#[allow(clippy::tests_outside_test_module)]
121#[cfg(all(test, feature = "noisy_float"))]
122mod tests_continuous {
123    use core::ops::Bound;
124
125    use noisy_float::types::N64;
126
127    use crate::GenericRange;
128
129    #[test]
130    fn in_ex() {
131        let n64_1 = N64::new(1.);
132        let n64_2 = N64::new(2.);
133        let n64_42 = N64::new(42.);
134        assert!(!GenericRange::from(n64_1..n64_1).is_singleton());
135        assert!(!GenericRange::from(n64_1..n64_2).is_singleton());
136        assert!(!GenericRange::from(n64_1..n64_42).is_singleton());
137    }
138
139    #[test]
140    fn in_in() {
141        let n64_1 = N64::new(1.);
142        let n64_2 = N64::new(2.);
143        let n64_42 = N64::new(42.);
144        assert!(GenericRange::from(n64_1..=n64_1).is_singleton());
145        assert!(!GenericRange::from(n64_1..=n64_2).is_singleton());
146        assert!(!GenericRange::from(n64_1..=n64_42).is_singleton());
147    }
148
149    #[test]
150    fn unbound() {
151        let n64_1 = N64::new(1.);
152        let n64_2 = N64::new(2.);
153        assert!(!GenericRange::from(n64_1..).is_singleton());
154        assert!(!GenericRange::from(..n64_2).is_singleton());
155        assert!(!GenericRange::from(..=n64_2).is_singleton());
156        let generic: GenericRange<N64> = GenericRange::from(..);
157        assert!(!generic.is_singleton());
158        assert!(!GenericRange::from((Bound::Excluded(n64_1), Bound::Unbounded)).is_singleton());
159    }
160
161    #[test]
162    fn ex_ex() {
163        let n64_1 = N64::new(1.);
164        let n64_2 = N64::new(2.);
165        let n64_42 = N64::new(42.);
166        assert!(!GenericRange::from((Bound::Excluded(n64_1), Bound::Excluded(n64_1))).is_singleton());
167        assert!(!GenericRange::from((Bound::Excluded(n64_1), Bound::Excluded(n64_2))).is_singleton());
168        assert!(!GenericRange::from((Bound::Excluded(n64_1), Bound::Excluded(n64_42))).is_singleton());
169    }
170
171    #[test]
172    fn ex_in() {
173        let n64_1 = N64::new(1.);
174        let n64_2 = N64::new(2.);
175        let n64_42 = N64::new(42.);
176        assert!(!GenericRange::from((Bound::Excluded(n64_1), Bound::Included(n64_1))).is_singleton());
177        assert!(!GenericRange::from((Bound::Excluded(n64_1), Bound::Included(n64_2))).is_singleton());
178        assert!(!GenericRange::from((Bound::Excluded(n64_1), Bound::Included(n64_42))).is_singleton());
179    }
180}