Skip to main content

int_interval/
i128.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 tests_for_basic;
11#[cfg(test)]
12mod tests_for_between;
13#[cfg(test)]
14mod tests_for_checked_minkowski;
15#[cfg(test)]
16mod tests_for_convex_hull;
17#[cfg(test)]
18mod tests_for_difference;
19#[cfg(test)]
20mod tests_for_intersection;
21#[cfg(test)]
22mod tests_for_symmetric_difference;
23#[cfg(test)]
24mod tests_for_union;
25
26#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
27pub struct I128CO {
28    start: i128,
29    end_excl: i128,
30}
31
32// ------------------------------------------------------------
33// low-level api: construction / accessors / predicates
34// ------------------------------------------------------------
35mod basic {
36
37    use super::*;
38
39    impl I128CO {
40        #[inline]
41        pub const fn try_new(start: i128, end_excl: i128) -> Option<Self> {
42            if start < end_excl {
43                Some(Self { start, end_excl })
44            } else {
45                None
46            }
47        }
48
49        #[inline]
50        pub const unsafe fn new_unchecked(start: i128, end_excl: i128) -> Self {
51            debug_assert!(start < end_excl);
52            Self { start, end_excl }
53        }
54
55        /// Constructs an `I128CO` interval from a midpoint and length (`u128`).
56        ///
57        /// # Parameters
58        /// - `mid`: the desired midpoint of the interval
59        /// - `len`: the desired length of the interval in units, must be `1..=u128::MAX`
60        ///
61        /// # Returns
62        /// - `Some(I128CO)` if the interval `[start, end_excl)` can be represented in `i128`
63        /// - `None` if `len = 0` or the computed `start` / `end_excl` would overflow `i128`
64        ///
65        /// # Guarantees
66        /// - Returned interval satisfies `start < end_excl`
67        /// - Maximum accepted input length is `u128::MAX`
68        #[inline]
69        pub const fn checked_from_midpoint_len(mid: i128, len: u128) -> Option<Self> {
70            if len == 0 {
71                return None;
72            }
73
74            let half = (len / 2) as i128;
75
76            let Some(start) = mid.checked_sub(half) else {
77                return None;
78            };
79            let Some(end_incl) = mid.checked_add(half) else {
80                return None;
81            };
82            let Some(end_excl) = end_incl.checked_add((len % 2) as i128) else {
83                return None;
84            };
85
86            // # Safety
87            // This function uses `unsafe { Self::new_unchecked(start, end_excl) }` internally.
88            // The safety is guaranteed by the following checks:
89            // 1. `mid.checked_sub(half)` ensures `start` does not underflow `i128`.
90            // 2. `mid.checked_add(the_other_half)` ensures `end_excl` does not overflow `i128`.
91            // 3. Because `half >= 0` and `the_other_half > 0`, we have `start < end_excl`.
92            // 4. Therefore, the half-open interval invariant `[start, end_excl)` is preserved.
93            Some(unsafe { Self::new_unchecked(start, end_excl) })
94        }
95
96        /// Constructs an `I128CO` interval from a midpoint and length (`u128`) with saturating semantics.
97        ///
98        /// # Parameters
99        /// - `mid`: the desired midpoint of the interval
100        /// - `len`: the desired length of the interval in units, must be `1..=u128::MAX`
101        ///
102        /// # Behavior
103        /// - Values are saturated at `i128::MIN` / `i128::MAX` to prevent overflow.
104        /// - If `len = 0`, returns `None`.
105        ///
106        /// # Guarantees
107        /// - Returned interval satisfies `start < end_excl`
108        /// - Maximum accepted input length is `u128::MAX`
109        /// - Fully compatible with codegen for other signed integer interval types
110        #[inline]
111        pub const fn saturating_from_midpoint_len(mid: i128, len: u128) -> Option<Self> {
112            if len == 0 {
113                return None;
114            }
115
116            let half = (len / 2) as i128;
117
118            let start = mid.saturating_sub(half);
119            let end_incl = mid.saturating_add(half);
120            let end_excl = end_incl.saturating_add((len % 2) as i128);
121
122            Self::try_new(start, end_excl)
123        }
124    }
125
126    #[inline]
127    const fn checked_end_excl_from_start_len(start: i128, len: u128) -> Option<i128> {
128        end_excl_from_start_len(start, len, false)
129    }
130
131    #[inline]
132    const fn saturating_end_excl_from_start_len(start: i128, len: u128) -> Option<i128> {
133        end_excl_from_start_len(start, len, true)
134    }
135
136    /// Computes the exclusive end bound for a signed start plus an unsigned length.
137    ///
138    /// This helper exists because the length type may represent values that do not
139    /// fit in the signed coordinate type. Therefore, directly casting `len` into
140    /// the coordinate type is not a valid general implementation.
141    ///
142    /// The computation is split around zero:
143    ///
144    /// - if `start < 0`, first consume the distance from `start` to zero;
145    /// - then place any remaining length on the non-negative side;
146    /// - if `start >= 0`, use the remaining representable room up to the signed
147    ///   coordinate maximum.
148    ///
149    /// No widening integer type is used. This is intentional, so the same control
150    /// flow can be emitted by codegen for other signed interval types.
151    ///
152    /// # Parameters
153    /// - `start`: inclusive start bound
154    /// - `len`: requested interval length
155    /// - `saturating`: whether overflow should clamp to the signed coordinate maximum
156    ///
157    /// # Returns
158    /// - `None` if `len == 0`;
159    /// - `None` if `saturating == false` and `start + len` would exceed the signed
160    ///   coordinate maximum;
161    /// - `Some(end_excl)` otherwise;
162    /// - when `saturating == true`, overflow is represented as the signed coordinate
163    ///   maximum.
164    ///
165    /// # Guarantees
166    /// - Never wraps signed or unsigned arithmetic.
167    /// - Never relies on a lossy unsigned-to-signed cast unless the value is known
168    ///   to fit in the signed coordinate type.
169    /// - Correctly handles intervals that cross zero.
170    /// - Correctly handles the signed coordinate minimum without evaluating its
171    ///   negation.
172    ///
173    /// # Non-guarantees
174    /// - This does not construct an interval value.
175    /// - This does not always guarantee `start < end_excl`.
176    ///   In saturating mode, a maximum start bound may return the same maximum as
177    ///   `end_excl`, which must later be rejected by the interval constructor.
178    /// - This does not guarantee the requested length is preserved in saturating
179    ///   mode; the result may be shorter.
180    /// - This does not provide a directly computable signed length for all
181    ///   successful intervals. Some valid logical lengths may exceed what the
182    ///   signed coordinate type can represent.
183    #[inline]
184    const fn end_excl_from_start_len(start: i128, len: u128, saturating: bool) -> Option<i128> {
185        if len == 0 {
186            return None;
187        }
188
189        if start < 0 {
190            let to_zero = if start == i128::MIN {
191                (i128::MAX as u128) + 1
192            } else {
193                (-start) as u128
194            };
195
196            if len < to_zero {
197                let Some(end_excl) = start.checked_add(len as i128) else {
198                    return None;
199                };
200                Some(end_excl)
201            } else if len == to_zero {
202                Some(0)
203            } else {
204                let rem = len - to_zero;
205
206                if rem > i128::MAX as u128 {
207                    if saturating { Some(i128::MAX) } else { None }
208                } else {
209                    Some(rem as i128)
210                }
211            }
212        } else {
213            let room = (i128::MAX - start) as u128;
214
215            if len > room {
216                if saturating { Some(i128::MAX) } else { None }
217            } else {
218                let Some(end_excl) = start.checked_add(len as i128) else {
219                    return None;
220                };
221                Some(end_excl)
222            }
223        }
224    }
225
226    impl I128CO {
227        /// Constructs an `I128CO` interval from a start position and length.
228        ///
229        /// The resulting interval is `[start, start + len)`.
230        ///
231        /// Handles signed cross-zero intervals without widening arithmetic.
232        /// For example, `start = -3, len = 5` produces `[-3, 2)`.
233        ///
234        /// Returns `None` if:
235        /// - `len == 0`;
236        /// - `start + len` would exceed `i128::MAX`.
237        #[inline]
238        pub const fn checked_from_start_len(start: i128, len: u128) -> Option<Self> {
239            let Some(end_excl) = checked_end_excl_from_start_len(start, len) else {
240                return None;
241            };
242
243            Some(unsafe { Self::new_unchecked(start, end_excl) })
244        }
245
246        /// Constructs an `I128CO` interval from a start position and length,
247        /// saturating the exclusive end bound at `i128::MAX`.
248        ///
249        /// The resulting interval is `[start, saturated_end_excl)`.
250        ///
251        /// Returns `None` if:
252        /// - `len == 0`;
253        /// - saturation still produces an empty interval, e.g. `start == i128::MAX`.
254        #[inline]
255        pub const fn saturating_from_start_len(start: i128, len: u128) -> Option<Self> {
256            let Some(end_excl) = saturating_end_excl_from_start_len(start, len) else {
257                return None;
258            };
259
260            Self::try_new(start, end_excl)
261        }
262    }
263
264    impl I128CO {
265        #[inline]
266        pub const fn start(self) -> i128 {
267            self.start
268        }
269
270        #[inline]
271        pub const fn end_excl(self) -> i128 {
272            self.end_excl
273        }
274
275        #[inline]
276        pub const fn end_incl(self) -> i128 {
277            // i128_low_bound =< start < end_excl
278            self.end_excl - 1
279        }
280
281        #[inline]
282        pub const fn len(self) -> u128 {
283            const SIGN_MASK: u128 = 1 << (i128::BITS - 1);
284            ((self.end_excl as u128) ^ SIGN_MASK) - ((self.start as u128) ^ SIGN_MASK)
285        }
286
287        /// Returns the midpoint of the interval `[start, end_excl)`,
288        /// using floor division if the length is even.
289        ///
290        /// # Guarantees
291        /// - `midpoint()` ∈ `[self.start, self.end_excl - 1]`
292        /// - Works for intervals with maximum length (entire `i128` range)
293        #[inline]
294        pub const fn midpoint(self) -> i128 {
295            self.start + (self.len() / 2) as i128
296        }
297
298        #[inline]
299        pub const fn contains(self, x: i128) -> bool {
300            self.start <= x && x < self.end_excl
301        }
302
303        #[inline]
304        pub const fn contains_interval(self, other: Self) -> bool {
305            self.start <= other.start && other.end_excl <= self.end_excl
306        }
307
308        #[inline]
309        pub const fn iter(self) -> core::ops::Range<i128> {
310            self.start..self.end_excl
311        }
312
313        #[inline]
314        pub const fn to_range(self) -> core::ops::Range<i128> {
315            self.start..self.end_excl
316        }
317
318        #[inline]
319        pub const fn intersects(self, other: Self) -> bool {
320            !(self.end_excl <= other.start || other.end_excl <= self.start)
321        }
322
323        #[inline]
324        pub const fn is_adjacent(self, other: Self) -> bool {
325            self.end_excl == other.start || other.end_excl == self.start
326        }
327
328        #[inline]
329        pub const fn is_contiguous_with(self, other: Self) -> bool {
330            self.intersects(other) || self.is_adjacent(other)
331        }
332    }
333}
334
335// ------------------------------------------------------------
336// interval algebra api: intersection / convex_hull / between / union / difference / symmetric_difference
337// ------------------------------------------------------------
338
339mod interval_algebra {
340
341    use super::*;
342
343    impl I128CO {
344        /// Returns the intersection of two intervals.
345        ///
346        /// If the intervals do not overlap, returns `None`.
347        #[inline]
348        pub const fn intersection(self, other: Self) -> Option<Self> {
349            let start = if self.start >= other.start {
350                self.start
351            } else {
352                other.start
353            };
354
355            let end_excl = if self.end_excl <= other.end_excl {
356                self.end_excl
357            } else {
358                other.end_excl
359            };
360
361            Self::try_new(start, end_excl)
362        }
363
364        /// Returns the convex hull (smallest interval containing both) of two intervals.
365        ///
366        /// Always returns a valid `I128CO`.
367        #[inline]
368        pub const fn convex_hull(self, other: Self) -> Self {
369            let start = if self.start <= other.start {
370                self.start
371            } else {
372                other.start
373            };
374
375            let end_excl = if self.end_excl >= other.end_excl {
376                self.end_excl
377            } else {
378                other.end_excl
379            };
380
381            Self { start, end_excl }
382        }
383
384        /// Returns the interval strictly between two intervals.
385        ///
386        /// If the intervals are contiguous or overlap, returns `None`.
387        #[inline]
388        pub const fn between(self, other: Self) -> Option<Self> {
389            let (left, right) = if self.start <= other.start {
390                (self, other)
391            } else {
392                (other, self)
393            };
394
395            Self::try_new(left.end_excl, right.start)
396        }
397
398        /// Returns the union of two intervals.
399        ///
400        /// - If intervals are contiguous or overlapping, returns `One` containing the merged interval.
401        /// - Otherwise, returns `Two` containing both intervals in ascending order.
402        #[inline]
403        pub const fn union(self, other: Self) -> OneTwo<Self> {
404            if self.is_contiguous_with(other) {
405                OneTwo::One(self.convex_hull(other))
406            } else if self.start <= other.start {
407                OneTwo::Two(self, other)
408            } else {
409                OneTwo::Two(other, self)
410            }
411        }
412
413        /// Returns the difference of two intervals (self - other).
414        ///
415        /// - If no overlap, returns `One(self)`.
416        /// - If partial overlap, returns `One` or `Two` depending on remaining segments.
417        /// - If fully contained, returns `Zero`.
418        #[inline]
419        pub const fn difference(self, other: Self) -> ZeroOneTwo<Self> {
420            match self.intersection(other) {
421                None => ZeroOneTwo::One(self),
422                Some(inter) => {
423                    let left = Self::try_new(self.start, inter.start);
424                    let right = Self::try_new(inter.end_excl, self.end_excl);
425
426                    match (left, right) {
427                        (None, None) => ZeroOneTwo::Zero,
428                        (Some(x), None) | (None, Some(x)) => ZeroOneTwo::One(x),
429                        (Some(x), Some(y)) => ZeroOneTwo::Two(x, y),
430                    }
431                }
432            }
433        }
434
435        /// Returns the symmetric difference of two intervals.
436        ///
437        /// Equivalent to `(self - other) ∪ (other - self)`.
438        /// - If intervals do not overlap, returns `Two(self, other)` in order.
439        /// - If intervals partially overlap, returns remaining non-overlapping segments as `One` or `Two`.
440        #[inline]
441        pub const fn symmetric_difference(self, other: Self) -> ZeroOneTwo<Self> {
442            match self.intersection(other) {
443                None => {
444                    if self.start <= other.start {
445                        ZeroOneTwo::Two(self, other)
446                    } else {
447                        ZeroOneTwo::Two(other, self)
448                    }
449                }
450                Some(inter) => {
451                    let hull = self.convex_hull(other);
452                    let left = Self::try_new(hull.start, inter.start);
453                    let right = Self::try_new(inter.end_excl, hull.end_excl);
454
455                    match (left, right) {
456                        (None, None) => ZeroOneTwo::Zero,
457                        (Some(x), None) | (None, Some(x)) => ZeroOneTwo::One(x),
458                        (Some(x), Some(y)) => ZeroOneTwo::Two(x, y),
459                    }
460                }
461            }
462        }
463    }
464}
465
466// ------------------------------------------------------------
467// Module: Minkowski arithmetic for I128CO
468// Provides checked and saturating Minkowski operations for intervals
469// ------------------------------------------------------------
470
471pub mod minkowski {
472    use super::*;
473
474    type Min = i128;
475    type Max = i128;
476
477    #[inline]
478    const fn min_max4(a: i128, b: i128, c: i128, d: i128) -> (Min, Max) {
479        let (min1, max1) = if a < b { (a, b) } else { (b, a) };
480        let (min2, max2) = if c < d { (c, d) } else { (d, c) };
481        let min = if min1 < min2 { min1 } else { min2 };
482        let max = if max1 > max2 { max1 } else { max2 };
483        (min, max)
484    }
485
486    #[inline]
487    const fn min_max2(a: i128, b: i128) -> (Min, Max) {
488        if a < b { (a, b) } else { (b, a) }
489    }
490
491    pub mod checked {
492        use super::*;
493
494        // --------------------------------------------------------
495        // Interval-to-interval
496        // --------------------------------------------------------
497        impl I128CO {
498            #[inline]
499            pub const fn checked_minkowski_add(self, other: Self) -> Option<Self> {
500                let Some(start) = self.start.checked_add(other.start) else {
501                    return None;
502                };
503                let Some(end_excl) = self.end_excl.checked_add(other.end_incl()) else {
504                    return None;
505                };
506
507                // SAFETY:
508                // `checked_add` guarantees both endpoint computations succeed without overflow.
509                // For half-open intervals, let `a_last = self.end_incl()` and `b_last = other.end_incl()`.
510                // Since `self.start <= a_last` and `other.start <= b_last`, we have
511                // `self.start + other.start <= a_last + b_last < self.end_excl + other.end_incl()`,
512                // hence the computed bounds satisfy `start < end_excl`.
513                Some(unsafe { Self::new_unchecked(start, end_excl) })
514            }
515
516            #[inline]
517            pub const fn checked_minkowski_sub(self, other: Self) -> Option<Self> {
518                let Some(start) = self.start.checked_sub(other.end_incl()) else {
519                    return None;
520                };
521                let Some(end_excl) = self.end_excl.checked_sub(other.start) else {
522                    return None;
523                };
524
525                // SAFETY:
526                // `checked_sub` guarantees both endpoint computations succeed without overflow.
527                // For interval subtraction, the minimum is attained at `self.start - other.end_incl()`
528                // and the exclusive upper bound is `self.end_excl - other.start`.
529                // Because `other.start <= other.end_incl()`, we get
530                // `self.start - other.end_incl() < self.end_excl - other.start`,
531                // so the resulting half-open interval is valid.
532                Some(unsafe { Self::new_unchecked(start, end_excl) })
533            }
534
535            #[inline]
536            pub const fn checked_minkowski_mul_hull(self, other: Self) -> Option<Self> {
537                let a = self.start;
538                let b = self.end_incl();
539                let c = other.start;
540                let d = other.end_incl();
541
542                let Some(p1) = a.checked_mul(c) else {
543                    return None;
544                };
545                let Some(p2) = a.checked_mul(d) else {
546                    return None;
547                };
548                let Some(p3) = b.checked_mul(c) else {
549                    return None;
550                };
551                let Some(p4) = b.checked_mul(d) else {
552                    return None;
553                };
554
555                let (start, end_incl) = min_max4(p1, p2, p3, p4);
556
557                let Some(end_excl) = end_incl.checked_add(1) else {
558                    return None;
559                };
560
561                // SAFETY:
562                // All four corner products are computed with `checked_mul`, so no intermediate
563                // multiplication overflows. For multiplication over a closed integer rectangle
564                // `[a, b] × [c, d]`, every attainable extremum occurs at a corner, so
565                // `min_max4(p1, p2, p3, p4)` yields the true inclusive lower/upper bounds.
566                // Therefore `start <= end_incl` holds by construction.
567                // `checked_add(1)` then safely converts the inclusive upper bound to the exclusive
568                // upper bound, and implies `end_excl = end_incl + 1`, hence `start < end_excl`.
569                Some(unsafe { Self::new_unchecked(start, end_excl) })
570            }
571
572            #[inline]
573            pub const fn checked_minkowski_div_hull(self, other: Self) -> Option<Self> {
574                if other.start <= 0 && other.end_incl() >= 0 {
575                    return None;
576                }
577
578                let a = self.start;
579                let b = self.end_incl();
580                let c = other.start;
581                let d = other.end_incl();
582
583                let Some(p1) = a.checked_div(c) else {
584                    return None;
585                };
586                let Some(p2) = a.checked_div(d) else {
587                    return None;
588                };
589                let Some(p3) = b.checked_div(c) else {
590                    return None;
591                };
592                let Some(p4) = b.checked_div(d) else {
593                    return None;
594                };
595
596                let (start, end_incl) = min_max4(p1, p2, p3, p4);
597
598                let Some(end_excl) = end_incl.checked_add(1) else {
599                    return None;
600                };
601
602                // SAFETY:
603                // The guard `other.start <= 0 && other.end_incl() >= 0` rejects any divisor interval
604                // that contains zero, so division by zero cannot occur anywhere in the divisor set.
605                // Each corner quotient is computed with `checked_div`, so exceptional signed cases
606                // such as `MIN / -1` are also rejected.
607                // On each connected component of the divisor domain that excludes zero, integer division
608                // is monotone with respect to the rectangle corners relevant to the extremum search;
609                // thus the global inclusive bounds over the interval pair are captured by the four
610                // corner quotients and recovered by `min_max4`, giving `start <= end_incl`.
611                // `checked_add(1)` safely converts the inclusive upper bound to half-open form, so
612                // the final bounds satisfy `start < end_excl`.
613                Some(unsafe { Self::new_unchecked(start, end_excl) })
614            }
615        }
616
617        // --------------------------------------------------------
618        // Scalar
619        // --------------------------------------------------------
620        impl I128CO {
621            #[inline]
622            pub const fn checked_minkowski_add_scalar(self, n: i128) -> Option<Self> {
623                let Some(start) = self.start.checked_add(n) else {
624                    return None;
625                };
626                let Some(end_excl) = self.end_excl.checked_add(n) else {
627                    return None;
628                };
629
630                // SAFETY:
631                // `checked_add` guarantees both translated bounds are computed without overflow.
632                // Adding the same scalar to both endpoints preserves the interval width exactly,
633                // so a valid half-open interval remains valid and still satisfies `start < end_excl`.
634                Some(unsafe { Self::new_unchecked(start, end_excl) })
635            }
636
637            #[inline]
638            pub const fn checked_minkowski_sub_scalar(self, n: i128) -> Option<Self> {
639                let Some(start) = self.start.checked_sub(n) else {
640                    return None;
641                };
642                let Some(end_excl) = self.end_excl.checked_sub(n) else {
643                    return None;
644                };
645
646                // SAFETY:
647                // `checked_sub` guarantees both translated bounds are computed without overflow.
648                // Subtracting the same scalar from both endpoints preserves the interval width exactly,
649                // so the strict half-open ordering is unchanged and `start < end_excl` still holds.
650                Some(unsafe { Self::new_unchecked(start, end_excl) })
651            }
652
653            #[inline]
654            pub const fn checked_minkowski_mul_scalar_hull(self, n: i128) -> Option<Self> {
655                let Some(a) = self.start.checked_mul(n) else {
656                    return None;
657                };
658                let Some(b) = self.end_incl().checked_mul(n) else {
659                    return None;
660                };
661
662                let (start, end_incl) = min_max2(a, b);
663
664                let Some(end_excl) = end_incl.checked_add(1) else {
665                    return None;
666                };
667
668                // SAFETY:
669                // Both endpoint products are computed with `checked_mul`, so no signed overflow occurs.
670                // Multiplication by a scalar maps the closed source interval endpoints to the two extreme
671                // candidates; `min_max2` therefore recovers the true inclusive lower/upper bounds whether
672                // `n` is positive, zero, or negative, giving `start <= end_incl`.
673                // `checked_add(1)` safely converts the inclusive upper bound into the exclusive upper bound,
674                // which guarantees the final half-open interval satisfies `start < end_excl`.
675                Some(unsafe { Self::new_unchecked(start, end_excl) })
676            }
677
678            #[inline]
679            pub const fn checked_minkowski_div_scalar_hull(self, n: i128) -> Option<Self> {
680                if n == 0 {
681                    return None;
682                }
683
684                let Some(a) = self.start.checked_div(n) else {
685                    return None;
686                };
687                let Some(b) = self.end_incl().checked_div(n) else {
688                    return None;
689                };
690
691                let (start, end_incl) = min_max2(a, b);
692
693                let Some(end_excl) = end_incl.checked_add(1) else {
694                    return None;
695                };
696
697                // SAFETY:
698                // The guard `n != 0` excludes division by zero, and `checked_div` additionally rejects
699                // the only overflowing signed case (`MIN / -1`).
700                // Division by a fixed nonzero scalar sends the source closed interval endpoints to the
701                // two extreme candidates for the image interval, so `min_max2` yields the true inclusive
702                // lower/upper bounds and ensures `start <= end_incl`.
703                // `checked_add(1)` safely restores half-open representation, therefore the constructed
704                // interval satisfies `start < end_excl`.
705                Some(unsafe { Self::new_unchecked(start, end_excl) })
706            }
707        }
708    }
709
710    // ========================================================
711    // SATURATING
712    // ========================================================
713    pub mod saturating {
714        use super::*;
715
716        impl I128CO {
717            #[inline]
718            pub const fn saturating_minkowski_add(self, other: Self) -> Option<Self> {
719                let start = self.start.saturating_add(other.start);
720                let end_excl = self.end_excl.saturating_add(other.end_incl());
721                Self::try_new(start, end_excl)
722            }
723
724            #[inline]
725            pub const fn saturating_minkowski_sub(self, other: Self) -> Option<Self> {
726                let start = self.start.saturating_sub(other.end_incl());
727                let end_excl = self.end_excl.saturating_sub(other.start);
728                Self::try_new(start, end_excl)
729            }
730
731            #[inline]
732            pub const fn saturating_minkowski_mul_hull(self, other: Self) -> Option<Self> {
733                let a = self.start.saturating_mul(other.start);
734                let b = self.start.saturating_mul(other.end_incl());
735                let c = self.end_incl().saturating_mul(other.start);
736                let d = self.end_incl().saturating_mul(other.end_incl());
737
738                let (start, end_incl) = min_max4(a, b, c, d);
739
740                let end_excl = end_incl.saturating_add(1);
741                Self::try_new(start, end_excl)
742            }
743
744            #[inline]
745            pub const fn saturating_minkowski_div_hull(self, other: Self) -> Option<Self> {
746                if other.start <= 0 && other.end_incl() >= 0 {
747                    return None;
748                }
749
750                let a = self.start / other.start;
751                let b = self.start / other.end_incl();
752                let c = self.end_incl() / other.start;
753                let d = self.end_incl() / other.end_incl();
754
755                let (start, end_incl) = min_max4(a, b, c, d);
756
757                let end_excl = end_incl.saturating_add(1);
758                Self::try_new(start, end_excl)
759            }
760        }
761
762        impl I128CO {
763            #[inline]
764            pub const fn saturating_minkowski_add_scalar(self, n: i128) -> Option<Self> {
765                let start = self.start.saturating_add(n);
766                let end_excl = self.end_excl.saturating_add(n);
767                Self::try_new(start, end_excl)
768            }
769
770            #[inline]
771            pub const fn saturating_minkowski_sub_scalar(self, n: i128) -> Option<Self> {
772                let start = self.start.saturating_sub(n);
773                let end_excl = self.end_excl.saturating_sub(n);
774                Self::try_new(start, end_excl)
775            }
776
777            #[inline]
778            pub const fn saturating_minkowski_mul_scalar_hull(self, n: i128) -> Option<Self> {
779                let a = self.start.saturating_mul(n);
780                let b = self.end_incl().saturating_mul(n);
781
782                let (start, end_incl) = min_max2(a, b);
783
784                let end_excl = end_incl.saturating_add(1);
785                Self::try_new(start, end_excl)
786            }
787
788            #[inline]
789            pub const fn saturating_minkowski_div_scalar_hull(self, n: i128) -> Option<Self> {
790                if n == 0 {
791                    return None;
792                }
793
794                let a = self.start / n;
795                let b = self.end_incl() / n;
796
797                let (start, end_incl) = min_max2(a, b);
798
799                let end_excl = end_incl.saturating_add(1);
800                Self::try_new(start, end_excl)
801            }
802        }
803    }
804}
805
806crate::traits::impl_co_forwarding!(I128CO, i128, u128);