int_intervals/interval/i64.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 I64CO {
34 start: i64,
35 end_excl: i64,
36}
37
38// ------------------------------------------------------------
39// low-level api: construction / accessors / predicates
40// ------------------------------------------------------------
41mod basic {
42
43 use super::*;
44
45 impl I64CO {
46 #[inline]
47 pub const fn try_new(start: i64, end_excl: i64) -> 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: i64, end_excl: i64) -> Self {
57 debug_assert!(start < end_excl);
58 Self { start, end_excl }
59 }
60
61 /// Constructs an `I64CO` interval from a midpoint and length (`u64`).
62 ///
63 /// # Parameters
64 /// - `mid`: the desired midpoint of the interval
65 /// - `len`: the desired length of the interval in units, must be `1..=u64::MAX`
66 ///
67 /// # Returns
68 /// - `Some(I64CO)` if the interval `[start, end_excl)` can be represented in `i64`
69 /// - `None` if `len = 0` or the computed `start` / `end_excl` would overflow `i64`
70 ///
71 /// # Guarantees
72 /// - Returned interval satisfies `start < end_excl`
73 /// - Maximum accepted input length is `u64::MAX`
74 #[inline]
75 pub const fn checked_from_midpoint_len(mid: i64, len: u64) -> Option<Self> {
76 if len == 0 {
77 return None;
78 }
79
80 let half = (len / 2) as i64;
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 i64) 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 `i64`.
96 // 2. `mid.checked_add(the_other_half)` ensures `end_excl` does not overflow `i64`.
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 `I64CO` interval from a midpoint and length (`u64`) 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..=u64::MAX`
107 ///
108 /// # Behavior
109 /// - Values are saturated at `i64::MIN` / `i64::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 `u64::MAX`
115 /// - Fully compatible with codegen for other signed integer interval types
116 #[inline]
117 pub const fn saturating_from_midpoint_len(mid: i64, len: u64) -> Option<Self> {
118 if len == 0 {
119 return None;
120 }
121
122 let half = (len / 2) as i64;
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 i64);
127
128 Self::try_new(start, end_excl)
129 }
130 }
131
132 #[inline]
133 const fn checked_end_excl_from_start_len(start: i64, len: u64) -> Option<i64> {
134 end_excl_from_start_len(start, len, false)
135 }
136
137 #[inline]
138 const fn saturating_end_excl_from_start_len(start: i64, len: u64) -> Option<i64> {
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: i64, len: u64, saturating: bool) -> Option<i64> {
191 if len == 0 {
192 return None;
193 }
194
195 if start < 0 {
196 let to_zero = if start == i64::MIN {
197 (i64::MAX as u64) + 1
198 } else {
199 (-start) as u64
200 };
201
202 if len < to_zero {
203 let Some(end_excl) = start.checked_add(len as i64) 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 > i64::MAX as u64 {
213 if saturating { Some(i64::MAX) } else { None }
214 } else {
215 Some(rem as i64)
216 }
217 }
218 } else {
219 let room = (i64::MAX - start) as u64;
220
221 if len > room {
222 if saturating { Some(i64::MAX) } else { None }
223 } else {
224 let Some(end_excl) = start.checked_add(len as i64) else {
225 return None;
226 };
227 Some(end_excl)
228 }
229 }
230 }
231
232 impl I64CO {
233 /// Constructs an `I64CO` 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 `i64::MAX`.
243 #[inline]
244 pub const fn checked_from_start_len(start: i64, len: u64) -> 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 `I64CO` interval from a start position and length,
253 /// saturating the exclusive end bound at `i64::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 == i64::MAX`.
260 #[inline]
261 pub const fn saturating_from_start_len(start: i64, len: u64) -> 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 I64CO {
271 #[inline]
272 pub const fn start(self) -> i64 {
273 self.start
274 }
275
276 #[inline]
277 pub const fn end_excl(self) -> i64 {
278 self.end_excl
279 }
280
281 #[inline]
282 pub const fn end_incl(self) -> i64 {
283 // i64_low_bound =< start < end_excl
284 self.end_excl - 1
285 }
286
287 #[inline]
288 pub const fn len(self) -> u64 {
289 const SIGN_MASK: u64 = 1 << (i64::BITS - 1);
290 ((self.end_excl as u64) ^ SIGN_MASK) - ((self.start as u64) ^ 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 `i64` range)
299 #[inline]
300 pub const fn midpoint(self) -> i64 {
301 self.start + (self.len() / 2) as i64
302 }
303
304 #[inline]
305 pub const fn contains(self, x: i64) -> 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<i64> {
316 self.start..self.end_excl
317 }
318
319 #[inline]
320 pub const fn to_range(self) -> core::ops::Range<i64> {
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 I64CO {
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 `I64CO`.
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 I64CO
474// Provides checked and saturating Minkowski operations for intervals
475// ------------------------------------------------------------
476
477pub mod minkowski {
478 use super::*;
479
480 type Min = i64;
481 type Max = i64;
482
483 #[inline]
484 const fn min_max4(a: i64, b: i64, c: i64, d: i64) -> (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: i64, b: i64) -> (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 I64CO {
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 I64CO {
627 #[inline]
628 pub const fn checked_minkowski_add_scalar(self, n: i64) -> 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: i64) -> 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: i64) -> 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: i64) -> 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 I64CO {
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 let a = self.start / other.start;
757 let b = self.start / other.end_incl();
758 let c = self.end_incl() / other.start;
759 let d = self.end_incl() / other.end_incl();
760
761 let (start, end_incl) = min_max4(a, b, c, d);
762
763 let end_excl = end_incl.saturating_add(1);
764 Self::try_new(start, end_excl)
765 }
766 }
767
768 impl I64CO {
769 #[inline]
770 pub const fn saturating_minkowski_add_scalar(self, n: i64) -> Option<Self> {
771 let start = self.start.saturating_add(n);
772 let end_excl = self.end_excl.saturating_add(n);
773 Self::try_new(start, end_excl)
774 }
775
776 #[inline]
777 pub const fn saturating_minkowski_sub_scalar(self, n: i64) -> Option<Self> {
778 let start = self.start.saturating_sub(n);
779 let end_excl = self.end_excl.saturating_sub(n);
780 Self::try_new(start, end_excl)
781 }
782
783 #[inline]
784 pub const fn saturating_minkowski_mul_scalar_hull(self, n: i64) -> Option<Self> {
785 let a = self.start.saturating_mul(n);
786 let b = self.end_incl().saturating_mul(n);
787
788 let (start, end_incl) = min_max2(a, b);
789
790 let end_excl = end_incl.saturating_add(1);
791 Self::try_new(start, end_excl)
792 }
793
794 #[inline]
795 pub const fn saturating_minkowski_div_scalar_hull(self, n: i64) -> Option<Self> {
796 if n == 0 {
797 return None;
798 }
799
800 let a = self.start / n;
801 let b = self.end_incl() / n;
802
803 let (start, end_incl) = min_max2(a, b);
804
805 let end_excl = end_incl.saturating_add(1);
806 Self::try_new(start, end_excl)
807 }
808 }
809 }
810}
811
812crate::interval::traits::impl_co_forwarding!(I64CO, i64, u64);