Skip to main content

int_interval/
i16.rs

1// -----------------------------------------------------------------------------
2// @generated by xtask/codegen (signed)
3// DO NOT EDIT MANUALLY.
4// Changes will be overwritten.
5// -----------------------------------------------------------------------------
6
7use crate::res::{OneTwo, ZeroOneTwo};
8
9
10#[cfg(test)]
11mod basic_tests;
12#[cfg(test)]
13mod between_tests;
14#[cfg(test)]
15mod checked_minkowski_tests;
16#[cfg(test)]
17mod convex_hull_tests;
18#[cfg(test)]
19mod difference_tests;
20#[cfg(test)]
21mod intersection_tests;
22#[cfg(test)]
23mod symmetric_difference_tests;
24#[cfg(test)]
25mod union_tests;
26
27#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
28pub struct I16CO {
29    start: i16,
30    end_excl: i16,
31}
32
33// ------------------------------------------------------------
34// low-level api: construction / accessors / predicates
35// ------------------------------------------------------------
36mod basic {
37
38    use super::*;
39
40    impl I16CO {
41        #[inline]
42        pub const fn try_new(start: i16, end_excl: i16) -> Option<Self> {
43            if start < end_excl {
44                Some(Self { start, end_excl })
45            } else {
46                None
47            }
48        }
49
50        #[inline]
51        pub const unsafe fn new_unchecked(start: i16, end_excl: i16) -> Self {
52            debug_assert!(start < end_excl);
53            Self { start, end_excl }
54        }
55
56        #[inline]
57        pub const fn start(self) -> i16 {
58            self.start
59        }
60
61        #[inline]
62        pub const fn end_excl(self) -> i16 {
63            self.end_excl
64        }
65
66        #[inline]
67        pub const fn end_incl(self) -> i16 {
68            // i16_low_bound =< start < end_excl
69            self.end_excl - 1
70        }
71
72        #[inline]
73        pub const fn len(self) -> u16 {
74            const SIGN_MASK: u16 = 1 << (i16::BITS - 1);
75            ((self.end_excl as u16) ^ SIGN_MASK) - ((self.start as u16) ^ SIGN_MASK)
76        }
77
78        #[inline]
79        pub const fn contains(self, x: i16) -> bool {
80            self.start <= x && x < self.end_excl
81        }
82
83        #[inline]
84        pub const fn contains_interval(self, other: Self) -> bool {
85            self.start <= other.start && other.end_excl <= self.end_excl
86        }
87
88        #[inline]
89        pub const fn iter(self) -> core::ops::Range<i16> {
90            self.start..self.end_excl
91        }
92
93        #[inline]
94        pub const fn to_range(self) -> core::ops::Range<i16> {
95            self.start..self.end_excl
96        }
97
98        #[inline]
99        pub const fn intersects(self, other: Self) -> bool {
100            !(self.end_excl <= other.start || other.end_excl <= self.start)
101        }
102
103        #[inline]
104        pub const fn is_adjacent(self, other: Self) -> bool {
105            self.end_excl == other.start || other.end_excl == self.start
106        }
107
108        #[inline]
109        pub const fn is_contiguous_with(self, other: Self) -> bool {
110            self.intersects(other) || self.is_adjacent(other)
111        }
112    }
113}
114
115// ------------------------------------------------------------
116// interval algebra api: intersection / convex_hull / between / union / difference / symmetric_difference
117// ------------------------------------------------------------
118
119mod interval_algebra {
120
121    use super::*;
122
123    impl I16CO {
124        /// Returns the intersection of two intervals.
125        ///
126        /// If the intervals do not overlap, returns `None`.
127        #[inline]
128        pub const fn intersection(self, other: Self) -> Option<Self> {
129            let start = if self.start >= other.start {
130                self.start
131            } else {
132                other.start
133            };
134
135            let end_excl = if self.end_excl <= other.end_excl {
136                self.end_excl
137            } else {
138                other.end_excl
139            };
140
141            Self::try_new(start, end_excl)
142        }
143
144        /// Returns the convex hull (smallest interval containing both) of two intervals.
145        ///
146        /// Always returns a valid `I16CO`.
147        #[inline]
148        pub const fn convex_hull(self, other: Self) -> Self {
149            let start = if self.start <= other.start {
150                self.start
151            } else {
152                other.start
153            };
154
155            let end_excl = if self.end_excl >= other.end_excl {
156                self.end_excl
157            } else {
158                other.end_excl
159            };
160
161            Self { start, end_excl }
162        }
163
164        /// Returns the interval strictly between two intervals.
165        ///
166        /// If the intervals are contiguous or overlap, returns `None`.
167        #[inline]
168        pub const fn between(self, other: Self) -> Option<Self> {
169            let (left, right) = if self.start <= other.start {
170                (self, other)
171            } else {
172                (other, self)
173            };
174
175            Self::try_new(left.end_excl, right.start)
176        }
177
178        /// Returns the union of two intervals.
179        ///
180        /// - If intervals are contiguous or overlapping, returns `One` containing the merged interval.
181        /// - Otherwise, returns `Two` containing both intervals in ascending order.
182        #[inline]
183        pub const fn union(self, other: Self) -> OneTwo<Self> {
184            if self.is_contiguous_with(other) {
185                OneTwo::One(self.convex_hull(other))
186            } else if self.start <= other.start {
187                OneTwo::Two(self, other)
188            } else {
189                OneTwo::Two(other, self)
190            }
191        }
192
193        /// Returns the difference of two intervals (self - other).
194        ///
195        /// - If no overlap, returns `One(self)`.
196        /// - If partial overlap, returns `One` or `Two` depending on remaining segments.
197        /// - If fully contained, returns `Zero`.
198        #[inline]
199        pub const fn difference(self, other: Self) -> ZeroOneTwo<Self> {
200            match self.intersection(other) {
201                None => ZeroOneTwo::One(self),
202                Some(inter) => {
203                    let left = Self::try_new(self.start, inter.start);
204                    let right = Self::try_new(inter.end_excl, self.end_excl);
205
206                    match (left, right) {
207                        (None, None) => ZeroOneTwo::Zero,
208                        (Some(x), None) | (None, Some(x)) => ZeroOneTwo::One(x),
209                        (Some(x), Some(y)) => ZeroOneTwo::Two(x, y),
210                    }
211                }
212            }
213        }
214
215        /// Returns the symmetric difference of two intervals.
216        ///
217        /// Equivalent to `(self - other) ∪ (other - self)`.
218        /// - If intervals do not overlap, returns `Two(self, other)` in order.
219        /// - If intervals partially overlap, returns remaining non-overlapping segments as `One` or `Two`.
220        #[inline]
221        pub const fn symmetric_difference(self, other: Self) -> ZeroOneTwo<Self> {
222            match self.intersection(other) {
223                None => {
224                    if self.start <= other.start {
225                        ZeroOneTwo::Two(self, other)
226                    } else {
227                        ZeroOneTwo::Two(other, self)
228                    }
229                }
230                Some(inter) => {
231                    let hull = self.convex_hull(other);
232                    let left = Self::try_new(hull.start, inter.start);
233                    let right = Self::try_new(inter.end_excl, hull.end_excl);
234
235                    match (left, right) {
236                        (None, None) => ZeroOneTwo::Zero,
237                        (Some(x), None) | (None, Some(x)) => ZeroOneTwo::One(x),
238                        (Some(x), Some(y)) => ZeroOneTwo::Two(x, y),
239                    }
240                }
241            }
242        }
243    }
244}
245
246// ------------------------------------------------------------
247// Module: Minkowski arithmetic for I16CO
248// Provides checked and saturating Minkowski operations for intervals
249// ------------------------------------------------------------
250
251pub mod minkowski {
252    use super::*;
253
254    type Min = i16;
255    type Max = i16;
256
257    #[inline]
258    const fn min_max4(a: i16, b: i16, c: i16, d: i16) -> (Min, Max) {
259        let (min1, max1) = if a < b { (a, b) } else { (b, a) };
260        let (min2, max2) = if c < d { (c, d) } else { (d, c) };
261        let min = if min1 < min2 { min1 } else { min2 };
262        let max = if max1 > max2 { max1 } else { max2 };
263        (min, max)
264    }
265
266    #[inline]
267    const fn min_max2(a: i16, b: i16) -> (Min, Max) {
268        if a < b { (a, b) } else { (b, a) }
269    }
270
271    pub mod checked {
272        use super::*;
273
274        // --------------------------------------------------------
275        // Interval-to-interval
276        // --------------------------------------------------------
277        impl I16CO {
278            #[inline]
279            pub const fn checked_minkowski_add(self, other: Self) -> Option<Self> {
280                let Some(start) = self.start.checked_add(other.start) else {
281                    return None;
282                };
283                let Some(end_excl) = self.end_excl.checked_add(other.end_incl()) else {
284                    return None;
285                };
286
287                // SAFETY:
288                // `checked_add` guarantees both endpoint computations succeed without overflow.
289                // For half-open intervals, let `a_last = self.end_incl()` and `b_last = other.end_incl()`.
290                // Since `self.start <= a_last` and `other.start <= b_last`, we have
291                // `self.start + other.start <= a_last + b_last < self.end_excl + other.end_incl()`,
292                // hence the computed bounds satisfy `start < end_excl`.
293                Some(unsafe { Self::new_unchecked(start, end_excl) })
294            }
295
296            #[inline]
297            pub const fn checked_minkowski_sub(self, other: Self) -> Option<Self> {
298                let Some(start) = self.start.checked_sub(other.end_incl()) else {
299                    return None;
300                };
301                let Some(end_excl) = self.end_excl.checked_sub(other.start) else {
302                    return None;
303                };
304
305                // SAFETY:
306                // `checked_sub` guarantees both endpoint computations succeed without overflow.
307                // For interval subtraction, the minimum is attained at `self.start - other.end_incl()`
308                // and the exclusive upper bound is `self.end_excl - other.start`.
309                // Because `other.start <= other.end_incl()`, we get
310                // `self.start - other.end_incl() < self.end_excl - other.start`,
311                // so the resulting half-open interval is valid.
312                Some(unsafe { Self::new_unchecked(start, end_excl) })
313            }
314
315            #[inline]
316            pub const fn checked_minkowski_mul(self, other: Self) -> Option<Self> {
317                let a = self.start;
318                let b = self.end_incl();
319                let c = other.start;
320                let d = other.end_incl();
321
322                let Some(p1) = a.checked_mul(c) else {
323                    return None;
324                };
325                let Some(p2) = a.checked_mul(d) else {
326                    return None;
327                };
328                let Some(p3) = b.checked_mul(c) else {
329                    return None;
330                };
331                let Some(p4) = b.checked_mul(d) else {
332                    return None;
333                };
334
335                let (start, end_incl) = min_max4(p1, p2, p3, p4);
336
337                let Some(end_excl) = end_incl.checked_add(1) else {
338                    return None;
339                };
340
341                // SAFETY:
342                // All four corner products are computed with `checked_mul`, so no intermediate
343                // multiplication overflows. For multiplication over a closed integer rectangle
344                // `[a, b] × [c, d]`, every attainable extremum occurs at a corner, so
345                // `min_max4(p1, p2, p3, p4)` yields the true inclusive lower/upper bounds.
346                // Therefore `start <= end_incl` holds by construction.
347                // `checked_add(1)` then safely converts the inclusive upper bound to the exclusive
348                // upper bound, and implies `end_excl = end_incl + 1`, hence `start < end_excl`.
349                Some(unsafe { Self::new_unchecked(start, end_excl) })
350            }
351
352            #[inline]
353            pub const fn checked_minkowski_div(self, other: Self) -> Option<Self> {
354                if other.start <= 0 && other.end_incl() >= 0 {
355                    return None;
356                }
357
358                let a = self.start;
359                let b = self.end_incl();
360                let c = other.start;
361                let d = other.end_incl();
362
363                let Some(p1) = a.checked_div(c) else {
364                    return None;
365                };
366                let Some(p2) = a.checked_div(d) else {
367                    return None;
368                };
369                let Some(p3) = b.checked_div(c) else {
370                    return None;
371                };
372                let Some(p4) = b.checked_div(d) else {
373                    return None;
374                };
375
376                let (start, end_incl) = min_max4(p1, p2, p3, p4);
377
378                let Some(end_excl) = end_incl.checked_add(1) else {
379                    return None;
380                };
381
382                // SAFETY:
383                // The guard `other.start <= 0 && other.end_incl() >= 0` rejects any divisor interval
384                // that contains zero, so division by zero cannot occur anywhere in the divisor set.
385                // Each corner quotient is computed with `checked_div`, so exceptional signed cases
386                // such as `MIN / -1` are also rejected.
387                // On each connected component of the divisor domain that excludes zero, integer division
388                // is monotone with respect to the rectangle corners relevant to the extremum search;
389                // thus the global inclusive bounds over the interval pair are captured by the four
390                // corner quotients and recovered by `min_max4`, giving `start <= end_incl`.
391                // `checked_add(1)` safely converts the inclusive upper bound to half-open form, so
392                // the final bounds satisfy `start < end_excl`.
393                Some(unsafe { Self::new_unchecked(start, end_excl) })
394            }
395        }
396
397        // --------------------------------------------------------
398        // Scalar
399        // --------------------------------------------------------
400        impl I16CO {
401            #[inline]
402            pub const fn checked_minkowski_add_n(self, n: i16) -> Option<Self> {
403                let Some(start) = self.start.checked_add(n) else {
404                    return None;
405                };
406                let Some(end_excl) = self.end_excl.checked_add(n) else {
407                    return None;
408                };
409
410                // SAFETY:
411                // `checked_add` guarantees both translated bounds are computed without overflow.
412                // Adding the same scalar to both endpoints preserves the interval width exactly,
413                // so a valid half-open interval remains valid and still satisfies `start < end_excl`.
414                Some(unsafe { Self::new_unchecked(start, end_excl) })
415            }
416
417            #[inline]
418            pub const fn checked_minkowski_sub_n(self, n: i16) -> Option<Self> {
419                let Some(start) = self.start.checked_sub(n) else {
420                    return None;
421                };
422                let Some(end_excl) = self.end_excl.checked_sub(n) else {
423                    return None;
424                };
425
426                // SAFETY:
427                // `checked_sub` guarantees both translated bounds are computed without overflow.
428                // Subtracting the same scalar from both endpoints preserves the interval width exactly,
429                // so the strict half-open ordering is unchanged and `start < end_excl` still holds.
430                Some(unsafe { Self::new_unchecked(start, end_excl) })
431            }
432
433            #[inline]
434            pub const fn checked_minkowski_mul_n(self, n: i16) -> Option<Self> {
435                let Some(a) = self.start.checked_mul(n) else {
436                    return None;
437                };
438                let Some(b) = self.end_incl().checked_mul(n) else {
439                    return None;
440                };
441
442                let (start, end_incl) = min_max2(a, b);
443
444                let Some(end_excl) = end_incl.checked_add(1) else {
445                    return None;
446                };
447
448                // SAFETY:
449                // Both endpoint products are computed with `checked_mul`, so no signed overflow occurs.
450                // Multiplication by a scalar maps the closed source interval endpoints to the two extreme
451                // candidates; `min_max2` therefore recovers the true inclusive lower/upper bounds whether
452                // `n` is positive, zero, or negative, giving `start <= end_incl`.
453                // `checked_add(1)` safely converts the inclusive upper bound into the exclusive upper bound,
454                // which guarantees the final half-open interval satisfies `start < end_excl`.
455                Some(unsafe { Self::new_unchecked(start, end_excl) })
456            }
457
458            #[inline]
459            pub const fn checked_minkowski_div_n(self, n: i16) -> Option<Self> {
460                if n == 0 {
461                    return None;
462                }
463
464                let Some(a) = self.start.checked_div(n) else {
465                    return None;
466                };
467                let Some(b) = self.end_incl().checked_div(n) else {
468                    return None;
469                };
470
471                let (start, end_incl) = min_max2(a, b);
472
473                let Some(end_excl) = end_incl.checked_add(1) else {
474                    return None;
475                };
476
477                // SAFETY:
478                // The guard `n != 0` excludes division by zero, and `checked_div` additionally rejects
479                // the only overflowing signed case (`MIN / -1`).
480                // Division by a fixed nonzero scalar sends the source closed interval endpoints to the
481                // two extreme candidates for the image interval, so `min_max2` yields the true inclusive
482                // lower/upper bounds and ensures `start <= end_incl`.
483                // `checked_add(1)` safely restores half-open representation, therefore the constructed
484                // interval satisfies `start < end_excl`.
485                Some(unsafe { Self::new_unchecked(start, end_excl) })
486            }
487        }
488    }
489
490    // ========================================================
491    // SATURATING
492    // ========================================================
493    pub mod saturating {
494        use super::*;
495
496        impl I16CO {
497            #[inline]
498            pub const fn saturating_minkowski_add(self, other: Self) -> Option<Self> {
499                let start = self.start.saturating_add(other.start);
500                let end_excl = self.end_excl.saturating_add(other.end_incl());
501                Self::try_new(start, end_excl)
502            }
503
504            #[inline]
505            pub const fn saturating_minkowski_sub(self, other: Self) -> Option<Self> {
506                let start = self.start.saturating_sub(other.end_incl());
507                let end_excl = self.end_excl.saturating_sub(other.start);
508                Self::try_new(start, end_excl)
509            }
510
511            #[inline]
512            pub const fn saturating_minkowski_mul(self, other: Self) -> Option<Self> {
513                let a = self.start.saturating_mul(other.start);
514                let b = self.start.saturating_mul(other.end_incl());
515                let c = self.end_incl().saturating_mul(other.start);
516                let d = self.end_incl().saturating_mul(other.end_incl());
517
518                let (start, end_incl) = min_max4(a, b, c, d);
519
520                let end_excl = end_incl.saturating_add(1);
521                Self::try_new(start, end_excl)
522            }
523
524            #[inline]
525            pub const fn saturating_minkowski_div(self, other: Self) -> Option<Self> {
526                if other.start <= 0 && other.end_incl() >= 0 {
527                    return None;
528                }
529
530                let a = self.start / other.start;
531                let b = self.start / other.end_incl();
532                let c = self.end_incl() / other.start;
533                let d = self.end_incl() / other.end_incl();
534
535                let (start, end_incl) = min_max4(a, b, c, d);
536
537                let end_excl = end_incl.saturating_add(1);
538                Self::try_new(start, end_excl)
539            }
540        }
541
542        impl I16CO {
543            #[inline]
544            pub const fn saturating_minkowski_add_n(self, n: i16) -> Option<Self> {
545                let start = self.start.saturating_add(n);
546                let end_excl = self.end_excl.saturating_add(n);
547                Self::try_new(start, end_excl)
548            }
549
550            #[inline]
551            pub const fn saturating_minkowski_sub_n(self, n: i16) -> Option<Self> {
552                let start = self.start.saturating_sub(n);
553                let end_excl = self.end_excl.saturating_sub(n);
554                Self::try_new(start, end_excl)
555            }
556
557            #[inline]
558            pub const fn saturating_minkowski_mul_n(self, n: i16) -> Option<Self> {
559                let a = self.start.saturating_mul(n);
560                let b = self.end_incl().saturating_mul(n);
561
562                let (start, end_incl) = min_max2(a, b);
563
564                let end_excl = end_incl.saturating_add(1);
565                Self::try_new(start, end_excl)
566            }
567
568            #[inline]
569            pub const fn saturating_minkowski_div_n(self, n: i16) -> Option<Self> {
570                if n == 0 {
571                    return None;
572                }
573
574                let a = self.start / n;
575                let b = self.end_incl() / n;
576
577                let (start, end_incl) = min_max2(a, b);
578
579                let end_excl = end_incl.saturating_add(1);
580                Self::try_new(start, end_excl)
581            }
582        }
583    }
584}