Skip to main content

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