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 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 IsizeCO {
28 start: isize,
29 end_excl: isize,
30}
31
32// ------------------------------------------------------------
33// low-level api: construction / accessors / predicates
34// ------------------------------------------------------------
35mod basic {
36
37 use super::*;
38
39 impl IsizeCO {
40 #[inline]
41 pub const fn try_new(start: isize, end_excl: isize) -> 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: isize, end_excl: isize) -> Self {
51 debug_assert!(start < end_excl);
52 Self { start, end_excl }
53 }
54
55 /// Constructs an `IsizeCO` interval from a midpoint and length (`usize`).
56 ///
57 /// # Parameters
58 /// - `mid`: the desired midpoint of the interval
59 /// - `len`: the desired length of the interval in units, must be `1..=usize::MAX`
60 ///
61 /// # Returns
62 /// - `Some(IsizeCO)` if the interval `[start, end_excl)` can be represented in `isize`
63 /// - `None` if `len = 0` or the computed `start` / `end_excl` would overflow `isize`
64 ///
65 /// # Guarantees
66 /// - Returned interval satisfies `start < end_excl`
67 /// - Maximum accepted input length is `usize::MAX`
68 #[inline]
69 pub const fn checked_from_midpoint_len(mid: isize, len: usize) -> Option<Self> {
70 if len == 0 {
71 return None;
72 }
73
74 let half = (len / 2) as isize;
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 isize) 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 `isize`.
90 // 2. `mid.checked_add(the_other_half)` ensures `end_excl` does not overflow `isize`.
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 `IsizeCO` interval from a midpoint and length (`usize`) 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..=usize::MAX`
101 ///
102 /// # Behavior
103 /// - Values are saturated at `isize::MIN` / `isize::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 `usize::MAX`
109 /// - Fully compatible with codegen for other signed integer interval types
110 #[inline]
111 pub const fn saturating_from_midpoint_len(mid: isize, len: usize) -> Option<Self> {
112 if len == 0 {
113 return None;
114 }
115
116 let half = (len / 2) as isize;
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 isize);
121
122 Self::try_new(start, end_excl)
123 }
124 }
125
126 #[inline]
127 const fn checked_end_excl_from_start_len(start: isize, len: usize) -> Option<isize> {
128 end_excl_from_start_len(start, len, false)
129 }
130
131 #[inline]
132 const fn saturating_end_excl_from_start_len(start: isize, len: usize) -> Option<isize> {
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: isize, len: usize, saturating: bool) -> Option<isize> {
185 if len == 0 {
186 return None;
187 }
188
189 if start < 0 {
190 let to_zero = if start == isize::MIN {
191 (isize::MAX as usize) + 1
192 } else {
193 (-start) as usize
194 };
195
196 if len < to_zero {
197 let Some(end_excl) = start.checked_add(len as isize) 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 > isize::MAX as usize {
207 if saturating { Some(isize::MAX) } else { None }
208 } else {
209 Some(rem as isize)
210 }
211 }
212 } else {
213 let room = (isize::MAX - start) as usize;
214
215 if len > room {
216 if saturating { Some(isize::MAX) } else { None }
217 } else {
218 let Some(end_excl) = start.checked_add(len as isize) else {
219 return None;
220 };
221 Some(end_excl)
222 }
223 }
224 }
225
226 impl IsizeCO {
227 /// Constructs an `IsizeCO` 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 `isize::MAX`.
237 #[inline]
238 pub const fn checked_from_start_len(start: isize, len: usize) -> 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 `IsizeCO` interval from a start position and length,
247 /// saturating the exclusive end bound at `isize::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 == isize::MAX`.
254 #[inline]
255 pub const fn saturating_from_start_len(start: isize, len: usize) -> 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 IsizeCO {
265 #[inline]
266 pub const fn start(self) -> isize {
267 self.start
268 }
269
270 #[inline]
271 pub const fn end_excl(self) -> isize {
272 self.end_excl
273 }
274
275 #[inline]
276 pub const fn end_incl(self) -> isize {
277 // isize_low_bound =< start < end_excl
278 self.end_excl - 1
279 }
280
281 #[inline]
282 pub const fn len(self) -> usize {
283 const SIGN_MASK: usize = 1 << (isize::BITS - 1);
284 ((self.end_excl as usize) ^ SIGN_MASK) - ((self.start as usize) ^ 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 `isize` range)
293 #[inline]
294 pub const fn midpoint(self) -> isize {
295 self.start + (self.len() / 2) as isize
296 }
297
298 #[inline]
299 pub const fn contains(self, x: isize) -> 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<isize> {
310 self.start..self.end_excl
311 }
312
313 #[inline]
314 pub const fn to_range(self) -> core::ops::Range<isize> {
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 IsizeCO {
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 `IsizeCO`.
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 IsizeCO
468// Provides checked and saturating Minkowski operations for intervals
469// ------------------------------------------------------------
470
471pub mod minkowski {
472 use super::*;
473
474 type Min = isize;
475 type Max = isize;
476
477 #[inline]
478 const fn min_max4(a: isize, b: isize, c: isize, d: isize) -> (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: isize, b: isize) -> (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 IsizeCO {
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 IsizeCO {
621 #[inline]
622 pub const fn checked_minkowski_add_scalar(self, n: isize) -> 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: isize) -> 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: isize) -> 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: isize) -> 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 IsizeCO {
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 IsizeCO {
763 #[inline]
764 pub const fn saturating_minkowski_add_scalar(self, n: isize) -> 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: isize) -> 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: isize) -> 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: isize) -> 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!(IsizeCO, isize, usize);