Skip to main content

int_intervals/interval/
i128.rs

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