Skip to main content

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