Skip to main content

int_interval/
u8.rs

1use crate::res::{OneTwo, ZeroOneTwo};
2
3#[cfg(test)]
4mod between_tests;
5#[cfg(test)]
6mod convex_hull_tests;
7#[cfg(test)]
8mod difference_tests;
9#[cfg(test)]
10mod intersection_tests;
11#[cfg(test)]
12mod symmetric_difference_tests;
13#[cfg(test)]
14mod minkowski_tests;
15#[cfg(test)]
16mod union_tests;
17
18#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
19pub struct U8CO {
20    start: u8,
21    end_excl: u8,
22}
23
24// ------------------------------------------------------------
25// low-level api: construction / accessors / predicates
26// ------------------------------------------------------------
27
28mod construction_accessors_predicates {
29
30    use super::*;
31
32    impl U8CO {
33        #[inline]
34        pub const fn try_new(start: u8, end_excl: u8) -> Option<Self> {
35            if start < end_excl {
36                Some(Self { start, end_excl })
37            } else {
38                None
39            }
40        }
41
42        #[inline]
43        pub(super) const fn new_unchecked(start: u8, end_excl: u8) -> Self {
44            debug_assert!(start < end_excl);
45            Self { start, end_excl }
46        }
47
48        #[inline]
49        pub const fn start(self) -> u8 {
50            self.start
51        }
52
53        #[inline]
54        pub const fn end_excl(self) -> u8 {
55            self.end_excl
56        }
57
58        #[inline]
59        pub const fn end_incl(self) -> u8 {
60            // u8_low_bound =< start < end_excl
61            self.end_excl - 1
62        }
63
64        #[inline]
65        pub const fn len(self) -> u8 {
66            self.end_excl - self.start
67        }
68
69        #[inline]
70        pub const fn contains(self, x: u8) -> bool {
71            self.start <= x && x < self.end_excl
72        }
73
74        #[inline]
75        pub const fn iter(self) -> core::ops::Range<u8> {
76            self.start..self.end_excl
77        }
78
79        #[inline]
80        pub const fn intersects(self, other: Self) -> bool {
81            !(self.end_excl <= other.start || other.end_excl <= self.start)
82        }
83
84        #[inline]
85        pub const fn is_adjacent(self, other: Self) -> bool {
86            self.end_excl == other.start || other.end_excl == self.start
87        }
88
89        #[inline]
90        pub const fn is_contiguous_with(self, other: Self) -> bool {
91            self.intersects(other) || self.is_adjacent(other)
92        }
93    }
94}
95
96// ------------------------------------------------------------
97// interval algebra api: intersection / convex_hull / between / union / difference / symmetric_difference
98// ------------------------------------------------------------
99
100mod interval_algebra {
101
102    use super::*;
103
104    impl U8CO {
105        /// Returns the intersection of two intervals.
106        ///
107        /// If the intervals do not overlap, returns `None`.
108        #[inline]
109        pub const fn intersection(self, other: Self) -> Option<Self> {
110            let start = if self.start >= other.start {
111                self.start
112            } else {
113                other.start
114            };
115
116            let end_excl = if self.end_excl <= other.end_excl {
117                self.end_excl
118            } else {
119                other.end_excl
120            };
121
122            Self::try_new(start, end_excl)
123        }
124
125        /// Returns the convex hull (smallest interval containing both) of two intervals.
126        ///
127        /// Always returns a valid `U8CO`.
128        #[inline]
129        pub const fn convex_hull(self, other: Self) -> Self {
130            let start = if self.start <= other.start {
131                self.start
132            } else {
133                other.start
134            };
135
136            let end_excl = if self.end_excl >= other.end_excl {
137                self.end_excl
138            } else {
139                other.end_excl
140            };
141
142            Self { start, end_excl }
143        }
144
145        /// Returns the interval strictly between two intervals.
146        ///
147        /// If the intervals are contiguous or overlap, returns `None`.
148        #[inline]
149        pub const fn between(self, other: Self) -> Option<Self> {
150            let (left, right) = if self.start <= other.start {
151                (self, other)
152            } else {
153                (other, self)
154            };
155
156            Self::try_new(left.end_excl, right.start)
157        }
158
159        /// Returns the union of two intervals.
160        ///
161        /// - If intervals are contiguous or overlapping, returns `One` containing the merged interval.
162        /// - Otherwise, returns `Two` containing both intervals in ascending order.
163        #[inline]
164        pub const fn union(self, other: Self) -> OneTwo<Self> {
165            if self.is_contiguous_with(other) {
166                OneTwo::One(self.convex_hull(other))
167            } else if self.start <= other.start {
168                OneTwo::Two(self, other)
169            } else {
170                OneTwo::Two(other, self)
171            }
172        }
173
174        /// Returns the difference of two intervals (self - other).
175        ///
176        /// - If no overlap, returns `One(self)`.
177        /// - If partial overlap, returns `One` or `Two` depending on remaining segments.
178        /// - If fully contained, returns `Zero`.
179        #[inline]
180        pub const fn difference(self, other: Self) -> ZeroOneTwo<Self> {
181            match self.intersection(other) {
182                None => ZeroOneTwo::One(self),
183                Some(inter) => {
184                    let left = Self::try_new(self.start, inter.start);
185                    let right = Self::try_new(inter.end_excl, self.end_excl);
186
187                    match (left, right) {
188                        (None, None) => ZeroOneTwo::Zero,
189                        (Some(x), None) | (None, Some(x)) => ZeroOneTwo::One(x),
190                        (Some(x), Some(y)) => ZeroOneTwo::Two(x, y),
191                    }
192                }
193            }
194        }
195
196        /// Returns the symmetric difference of two intervals.
197        ///
198        /// Equivalent to `(self - other) ∪ (other - self)`.
199        /// - If intervals do not overlap, returns `Two(self, other)` in order.
200        /// - If intervals partially overlap, returns remaining non-overlapping segments as `One` or `Two`.
201        #[inline]
202        pub const fn symmetric_difference(self, other: Self) -> ZeroOneTwo<Self> {
203            match self.intersection(other) {
204                None => {
205                    if self.start <= other.start {
206                        ZeroOneTwo::Two(self, other)
207                    } else {
208                        ZeroOneTwo::Two(other, self)
209                    }
210                }
211                Some(inter) => {
212                    let hull = self.convex_hull(other);
213                    let left = Self::try_new(hull.start, inter.start);
214                    let right = Self::try_new(inter.end_excl, hull.end_excl);
215
216                    match (left, right) {
217                        (None, None) => ZeroOneTwo::Zero,
218                        (Some(x), None) | (None, Some(x)) => ZeroOneTwo::One(x),
219                        (Some(x), Some(y)) => ZeroOneTwo::Two(x, y),
220                    }
221                }
222            }
223        }
224    }
225}
226
227// ------------------------------------------------------------
228// Module: Minkowski arithmetic for U8CO
229// Provides checked Minkowski operations for intervals
230// ------------------------------------------------------------
231
232pub mod minkowski {
233    use super::U8CO;
234
235    // --------------------------------------------------------
236    // Interval-to-interval Minkowski operations
237    // --------------------------------------------------------
238    impl U8CO {
239        /// Minkowski addition: [a_start, a_end) + [b_start, b_end)
240        #[inline]
241        pub const fn minkowski_add(self, other: Self) -> Option<Self> {
242            match self.start.checked_add(other.start) {
243                Some(start) => match self.end_incl().checked_add(other.end_incl()) {
244                    Some(end_incl) => match end_incl.checked_add(1) {
245                        Some(end_excl) => Some(Self::new_unchecked(start, end_excl)),
246                        None => None,
247                    },
248                    None => None,
249                },
250                None => None,
251            }
252        }
253
254        /// Minkowski subtraction: [a_start, a_end) - [b_start, b_end)
255        #[inline]
256        pub const fn minkowski_sub(self, other: Self) -> Option<Self> {
257            match self.start.checked_sub(other.end_incl()) {
258                Some(start) => match self.end_incl().checked_sub(other.start) {
259                    Some(end_incl) => match end_incl.checked_add(1) {
260                        Some(end_excl) => Some(Self::new_unchecked(start, end_excl)),
261                        None => None,
262                    },
263                    None => None,
264                },
265                None => None,
266            }
267        }
268
269        /// Minkowski multiplication: [a_start, a_end) * [b_start, b_end)
270        #[inline]
271        pub const fn minkowski_mul(self, other: Self) -> Option<Self> {
272            match self.start.checked_mul(other.start) {
273                Some(start) => match self.end_incl().checked_mul(other.end_incl()) {
274                    Some(end_incl) => match end_incl.checked_add(1) {
275                        Some(end_excl) => Some(Self::new_unchecked(start, end_excl)),
276                        None => None,
277                    },
278                    None => None,
279                },
280                None => None,
281            }
282        }
283
284        /// Minkowski division: [a_start, a_end) / [b_start, b_end)
285        #[inline]
286        pub const fn minkowski_div(self, other: Self) -> Option<Self> {
287            if other.start == 0 {
288                return None; // avoid division by zero
289            }
290            match self.start.checked_div(other.end_incl()) {
291                Some(start) => match self.end_incl().checked_div(other.start) {
292                    Some(end_incl) => match end_incl.checked_add(1) {
293                        Some(end_excl) => Some(Self::new_unchecked(start, end_excl)),
294                        None => None,
295                    },
296                    None => None,
297                },
298                None => None,
299            }
300        }
301    }
302
303    // --------------------------------------------------------
304    // Interval-to-scalar Minkowski operations
305    // --------------------------------------------------------
306    impl U8CO {
307        /// Add a scalar to an interval: [start, end) + n
308        #[inline]
309        pub const fn minkowski_add_n(self, n: u8) -> Option<Self> {
310            match self.start.checked_add(n) {
311                Some(start) => match self.end_excl.checked_add(n) {
312                    Some(end_excl) => Some(Self::new_unchecked(start, end_excl)),
313                    None => None,
314                },
315                None => None,
316            }
317        }
318
319        /// Subtract a scalar from an interval: [start, end) - n
320        #[inline]
321        pub const fn minkowski_sub_n(self, n: u8) -> Option<Self> {
322            match self.start.checked_sub(n) {
323                Some(start) => match self.end_excl.checked_sub(n) {
324                    Some(end_excl) => Some(Self::new_unchecked(start, end_excl)),
325                    None => None,
326                },
327                None => None,
328            }
329        }
330
331        /// Multiply an interval by a scalar: [start, end) * n
332        #[inline]
333        pub const fn minkowski_mul_n(self, n: u8) -> Option<Self> {
334            match self.start.checked_mul(n) {
335                Some(start) => match self.end_incl().checked_mul(n) {
336                    Some(end_incl) => match end_incl.checked_add(1) {
337                        Some(end_excl) => Some(Self::new_unchecked(start, end_excl)),
338                        None => None,
339                    },
340                    None => None,
341                },
342                None => None,
343            }
344        }
345
346        /// Divide an interval by a scalar: [start, end) / n
347        #[inline]
348        pub const fn minkowski_div_n(self, n: u8) -> Option<Self> {
349            if n == 0 {
350                return None; // avoid division by zero
351            }
352            let start = self.start / n;
353            let end_incl = self.end_incl() / n;
354            match end_incl.checked_add(1) {
355                Some(end_excl) => Some(Self::new_unchecked(start, end_excl)),
356                None => None,
357            }
358        }
359    }
360}