int_interval/i64.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 I64CO {
28 start: i64,
29 end_excl: i64,
30}
31
32// ------------------------------------------------------------
33// low-level api: construction / accessors / predicates
34// ------------------------------------------------------------
35mod basic {
36
37 use super::*;
38
39 impl I64CO {
40 #[inline]
41 pub const fn try_new(start: i64, end_excl: i64) -> 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: i64, end_excl: i64) -> Self {
51 debug_assert!(start < end_excl);
52 Self { start, end_excl }
53 }
54
55 /// Constructs an `I64CO` interval from a midpoint and length (`u64`).
56 ///
57 /// # Parameters
58 /// - `mid`: the desired midpoint of the interval
59 /// - `len`: the desired length of the interval in units, must be `1..=u64::MAX`
60 ///
61 /// # Returns
62 /// - `Some(I64CO)` if the interval `[start, end_excl)` can be represented in `i64`
63 /// - `None` if `len = 0` or the computed `start` / `end_excl` would overflow `i64`
64 ///
65 /// # Guarantees
66 /// - Returned interval satisfies `start < end_excl`
67 /// - Maximum accepted input length is `u64::MAX`
68 #[inline]
69 pub const fn checked_from_midpoint_len(mid: i64, len: u64) -> Option<Self> {
70 if len == 0 {
71 return None;
72 }
73
74 let half = (len / 2) as i64;
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 i64) 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 `i64`.
90 // 2. `mid.checked_add(the_other_half)` ensures `end_excl` does not overflow `i64`.
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 `I64CO` interval from a midpoint and length (`u64`) 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..=u64::MAX`
101 ///
102 /// # Behavior
103 /// - Values are saturated at `i64::MIN` / `i64::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 `u64::MAX`
109 /// - Fully compatible with codegen for other signed integer interval types
110 #[inline]
111 pub const fn saturating_from_midpoint_len(mid: i64, len: u64) -> Option<Self> {
112 if len == 0 {
113 return None;
114 }
115
116 let half = (len / 2) as i64;
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 i64);
121
122 Self::try_new(start, end_excl)
123 }
124 }
125
126 impl I64CO {
127 #[inline]
128 pub const fn start(self) -> i64 {
129 self.start
130 }
131
132 #[inline]
133 pub const fn end_excl(self) -> i64 {
134 self.end_excl
135 }
136
137 #[inline]
138 pub const fn end_incl(self) -> i64 {
139 // i64_low_bound =< start < end_excl
140 self.end_excl - 1
141 }
142
143 #[inline]
144 pub const fn len(self) -> u64 {
145 const SIGN_MASK: u64 = 1 << (i64::BITS - 1);
146 ((self.end_excl as u64) ^ SIGN_MASK) - ((self.start as u64) ^ SIGN_MASK)
147 }
148
149 /// Returns the midpoint of the interval `[start, end_excl)`,
150 /// using floor division if the length is even.
151 ///
152 /// # Guarantees
153 /// - `midpoint()` ∈ `[self.start, self.end_excl - 1]`
154 /// - Works for intervals with maximum length (entire `i64` range)
155 #[inline]
156 pub const fn midpoint(self) -> i64 {
157 self.start + (self.len() / 2) as i64
158 }
159
160 #[inline]
161 pub const fn contains(self, x: i64) -> bool {
162 self.start <= x && x < self.end_excl
163 }
164
165 #[inline]
166 pub const fn contains_interval(self, other: Self) -> bool {
167 self.start <= other.start && other.end_excl <= self.end_excl
168 }
169
170 #[inline]
171 pub const fn iter(self) -> core::ops::Range<i64> {
172 self.start..self.end_excl
173 }
174
175 #[inline]
176 pub const fn to_range(self) -> core::ops::Range<i64> {
177 self.start..self.end_excl
178 }
179
180 #[inline]
181 pub const fn intersects(self, other: Self) -> bool {
182 !(self.end_excl <= other.start || other.end_excl <= self.start)
183 }
184
185 #[inline]
186 pub const fn is_adjacent(self, other: Self) -> bool {
187 self.end_excl == other.start || other.end_excl == self.start
188 }
189
190 #[inline]
191 pub const fn is_contiguous_with(self, other: Self) -> bool {
192 self.intersects(other) || self.is_adjacent(other)
193 }
194 }
195}
196
197// ------------------------------------------------------------
198// interval algebra api: intersection / convex_hull / between / union / difference / symmetric_difference
199// ------------------------------------------------------------
200
201mod interval_algebra {
202
203 use super::*;
204
205 impl I64CO {
206 /// Returns the intersection of two intervals.
207 ///
208 /// If the intervals do not overlap, returns `None`.
209 #[inline]
210 pub const fn intersection(self, other: Self) -> Option<Self> {
211 let start = if self.start >= other.start {
212 self.start
213 } else {
214 other.start
215 };
216
217 let end_excl = if self.end_excl <= other.end_excl {
218 self.end_excl
219 } else {
220 other.end_excl
221 };
222
223 Self::try_new(start, end_excl)
224 }
225
226 /// Returns the convex hull (smallest interval containing both) of two intervals.
227 ///
228 /// Always returns a valid `I64CO`.
229 #[inline]
230 pub const fn convex_hull(self, other: Self) -> Self {
231 let start = if self.start <= other.start {
232 self.start
233 } else {
234 other.start
235 };
236
237 let end_excl = if self.end_excl >= other.end_excl {
238 self.end_excl
239 } else {
240 other.end_excl
241 };
242
243 Self { start, end_excl }
244 }
245
246 /// Returns the interval strictly between two intervals.
247 ///
248 /// If the intervals are contiguous or overlap, returns `None`.
249 #[inline]
250 pub const fn between(self, other: Self) -> Option<Self> {
251 let (left, right) = if self.start <= other.start {
252 (self, other)
253 } else {
254 (other, self)
255 };
256
257 Self::try_new(left.end_excl, right.start)
258 }
259
260 /// Returns the union of two intervals.
261 ///
262 /// - If intervals are contiguous or overlapping, returns `One` containing the merged interval.
263 /// - Otherwise, returns `Two` containing both intervals in ascending order.
264 #[inline]
265 pub const fn union(self, other: Self) -> OneTwo<Self> {
266 if self.is_contiguous_with(other) {
267 OneTwo::One(self.convex_hull(other))
268 } else if self.start <= other.start {
269 OneTwo::Two(self, other)
270 } else {
271 OneTwo::Two(other, self)
272 }
273 }
274
275 /// Returns the difference of two intervals (self - other).
276 ///
277 /// - If no overlap, returns `One(self)`.
278 /// - If partial overlap, returns `One` or `Two` depending on remaining segments.
279 /// - If fully contained, returns `Zero`.
280 #[inline]
281 pub const fn difference(self, other: Self) -> ZeroOneTwo<Self> {
282 match self.intersection(other) {
283 None => ZeroOneTwo::One(self),
284 Some(inter) => {
285 let left = Self::try_new(self.start, inter.start);
286 let right = Self::try_new(inter.end_excl, self.end_excl);
287
288 match (left, right) {
289 (None, None) => ZeroOneTwo::Zero,
290 (Some(x), None) | (None, Some(x)) => ZeroOneTwo::One(x),
291 (Some(x), Some(y)) => ZeroOneTwo::Two(x, y),
292 }
293 }
294 }
295 }
296
297 /// Returns the symmetric difference of two intervals.
298 ///
299 /// Equivalent to `(self - other) ∪ (other - self)`.
300 /// - If intervals do not overlap, returns `Two(self, other)` in order.
301 /// - If intervals partially overlap, returns remaining non-overlapping segments as `One` or `Two`.
302 #[inline]
303 pub const fn symmetric_difference(self, other: Self) -> ZeroOneTwo<Self> {
304 match self.intersection(other) {
305 None => {
306 if self.start <= other.start {
307 ZeroOneTwo::Two(self, other)
308 } else {
309 ZeroOneTwo::Two(other, self)
310 }
311 }
312 Some(inter) => {
313 let hull = self.convex_hull(other);
314 let left = Self::try_new(hull.start, inter.start);
315 let right = Self::try_new(inter.end_excl, hull.end_excl);
316
317 match (left, right) {
318 (None, None) => ZeroOneTwo::Zero,
319 (Some(x), None) | (None, Some(x)) => ZeroOneTwo::One(x),
320 (Some(x), Some(y)) => ZeroOneTwo::Two(x, y),
321 }
322 }
323 }
324 }
325 }
326}
327
328// ------------------------------------------------------------
329// Module: Minkowski arithmetic for I64CO
330// Provides checked and saturating Minkowski operations for intervals
331// ------------------------------------------------------------
332
333pub mod minkowski {
334 use super::*;
335
336 type Min = i64;
337 type Max = i64;
338
339 #[inline]
340 const fn min_max4(a: i64, b: i64, c: i64, d: i64) -> (Min, Max) {
341 let (min1, max1) = if a < b { (a, b) } else { (b, a) };
342 let (min2, max2) = if c < d { (c, d) } else { (d, c) };
343 let min = if min1 < min2 { min1 } else { min2 };
344 let max = if max1 > max2 { max1 } else { max2 };
345 (min, max)
346 }
347
348 #[inline]
349 const fn min_max2(a: i64, b: i64) -> (Min, Max) {
350 if a < b { (a, b) } else { (b, a) }
351 }
352
353 pub mod checked {
354 use super::*;
355
356 // --------------------------------------------------------
357 // Interval-to-interval
358 // --------------------------------------------------------
359 impl I64CO {
360 #[inline]
361 pub const fn checked_minkowski_add(self, other: Self) -> Option<Self> {
362 let Some(start) = self.start.checked_add(other.start) else {
363 return None;
364 };
365 let Some(end_excl) = self.end_excl.checked_add(other.end_incl()) else {
366 return None;
367 };
368
369 // SAFETY:
370 // `checked_add` guarantees both endpoint computations succeed without overflow.
371 // For half-open intervals, let `a_last = self.end_incl()` and `b_last = other.end_incl()`.
372 // Since `self.start <= a_last` and `other.start <= b_last`, we have
373 // `self.start + other.start <= a_last + b_last < self.end_excl + other.end_incl()`,
374 // hence the computed bounds satisfy `start < end_excl`.
375 Some(unsafe { Self::new_unchecked(start, end_excl) })
376 }
377
378 #[inline]
379 pub const fn checked_minkowski_sub(self, other: Self) -> Option<Self> {
380 let Some(start) = self.start.checked_sub(other.end_incl()) else {
381 return None;
382 };
383 let Some(end_excl) = self.end_excl.checked_sub(other.start) else {
384 return None;
385 };
386
387 // SAFETY:
388 // `checked_sub` guarantees both endpoint computations succeed without overflow.
389 // For interval subtraction, the minimum is attained at `self.start - other.end_incl()`
390 // and the exclusive upper bound is `self.end_excl - other.start`.
391 // Because `other.start <= other.end_incl()`, we get
392 // `self.start - other.end_incl() < self.end_excl - other.start`,
393 // so the resulting half-open interval is valid.
394 Some(unsafe { Self::new_unchecked(start, end_excl) })
395 }
396
397 #[inline]
398 pub const fn checked_minkowski_mul_hull(self, other: Self) -> Option<Self> {
399 let a = self.start;
400 let b = self.end_incl();
401 let c = other.start;
402 let d = other.end_incl();
403
404 let Some(p1) = a.checked_mul(c) else {
405 return None;
406 };
407 let Some(p2) = a.checked_mul(d) else {
408 return None;
409 };
410 let Some(p3) = b.checked_mul(c) else {
411 return None;
412 };
413 let Some(p4) = b.checked_mul(d) else {
414 return None;
415 };
416
417 let (start, end_incl) = min_max4(p1, p2, p3, p4);
418
419 let Some(end_excl) = end_incl.checked_add(1) else {
420 return None;
421 };
422
423 // SAFETY:
424 // All four corner products are computed with `checked_mul`, so no intermediate
425 // multiplication overflows. For multiplication over a closed integer rectangle
426 // `[a, b] × [c, d]`, every attainable extremum occurs at a corner, so
427 // `min_max4(p1, p2, p3, p4)` yields the true inclusive lower/upper bounds.
428 // Therefore `start <= end_incl` holds by construction.
429 // `checked_add(1)` then safely converts the inclusive upper bound to the exclusive
430 // upper bound, and implies `end_excl = end_incl + 1`, hence `start < end_excl`.
431 Some(unsafe { Self::new_unchecked(start, end_excl) })
432 }
433
434 #[inline]
435 pub const fn checked_minkowski_div_hull(self, other: Self) -> Option<Self> {
436 if other.start <= 0 && other.end_incl() >= 0 {
437 return None;
438 }
439
440 let a = self.start;
441 let b = self.end_incl();
442 let c = other.start;
443 let d = other.end_incl();
444
445 let Some(p1) = a.checked_div(c) else {
446 return None;
447 };
448 let Some(p2) = a.checked_div(d) else {
449 return None;
450 };
451 let Some(p3) = b.checked_div(c) else {
452 return None;
453 };
454 let Some(p4) = b.checked_div(d) else {
455 return None;
456 };
457
458 let (start, end_incl) = min_max4(p1, p2, p3, p4);
459
460 let Some(end_excl) = end_incl.checked_add(1) else {
461 return None;
462 };
463
464 // SAFETY:
465 // The guard `other.start <= 0 && other.end_incl() >= 0` rejects any divisor interval
466 // that contains zero, so division by zero cannot occur anywhere in the divisor set.
467 // Each corner quotient is computed with `checked_div`, so exceptional signed cases
468 // such as `MIN / -1` are also rejected.
469 // On each connected component of the divisor domain that excludes zero, integer division
470 // is monotone with respect to the rectangle corners relevant to the extremum search;
471 // thus the global inclusive bounds over the interval pair are captured by the four
472 // corner quotients and recovered by `min_max4`, giving `start <= end_incl`.
473 // `checked_add(1)` safely converts the inclusive upper bound to half-open form, so
474 // the final bounds satisfy `start < end_excl`.
475 Some(unsafe { Self::new_unchecked(start, end_excl) })
476 }
477 }
478
479 // --------------------------------------------------------
480 // Scalar
481 // --------------------------------------------------------
482 impl I64CO {
483 #[inline]
484 pub const fn checked_minkowski_add_scalar(self, n: i64) -> Option<Self> {
485 let Some(start) = self.start.checked_add(n) else {
486 return None;
487 };
488 let Some(end_excl) = self.end_excl.checked_add(n) else {
489 return None;
490 };
491
492 // SAFETY:
493 // `checked_add` guarantees both translated bounds are computed without overflow.
494 // Adding the same scalar to both endpoints preserves the interval width exactly,
495 // so a valid half-open interval remains valid and still satisfies `start < end_excl`.
496 Some(unsafe { Self::new_unchecked(start, end_excl) })
497 }
498
499 #[inline]
500 pub const fn checked_minkowski_sub_scalar(self, n: i64) -> Option<Self> {
501 let Some(start) = self.start.checked_sub(n) else {
502 return None;
503 };
504 let Some(end_excl) = self.end_excl.checked_sub(n) else {
505 return None;
506 };
507
508 // SAFETY:
509 // `checked_sub` guarantees both translated bounds are computed without overflow.
510 // Subtracting the same scalar from both endpoints preserves the interval width exactly,
511 // so the strict half-open ordering is unchanged and `start < end_excl` still holds.
512 Some(unsafe { Self::new_unchecked(start, end_excl) })
513 }
514
515 #[inline]
516 pub const fn checked_minkowski_mul_scalar_hull(self, n: i64) -> Option<Self> {
517 let Some(a) = self.start.checked_mul(n) else {
518 return None;
519 };
520 let Some(b) = self.end_incl().checked_mul(n) else {
521 return None;
522 };
523
524 let (start, end_incl) = min_max2(a, b);
525
526 let Some(end_excl) = end_incl.checked_add(1) else {
527 return None;
528 };
529
530 // SAFETY:
531 // Both endpoint products are computed with `checked_mul`, so no signed overflow occurs.
532 // Multiplication by a scalar maps the closed source interval endpoints to the two extreme
533 // candidates; `min_max2` therefore recovers the true inclusive lower/upper bounds whether
534 // `n` is positive, zero, or negative, giving `start <= end_incl`.
535 // `checked_add(1)` safely converts the inclusive upper bound into the exclusive upper bound,
536 // which guarantees the final half-open interval satisfies `start < end_excl`.
537 Some(unsafe { Self::new_unchecked(start, end_excl) })
538 }
539
540 #[inline]
541 pub const fn checked_minkowski_div_scalar_hull(self, n: i64) -> Option<Self> {
542 if n == 0 {
543 return None;
544 }
545
546 let Some(a) = self.start.checked_div(n) else {
547 return None;
548 };
549 let Some(b) = self.end_incl().checked_div(n) else {
550 return None;
551 };
552
553 let (start, end_incl) = min_max2(a, b);
554
555 let Some(end_excl) = end_incl.checked_add(1) else {
556 return None;
557 };
558
559 // SAFETY:
560 // The guard `n != 0` excludes division by zero, and `checked_div` additionally rejects
561 // the only overflowing signed case (`MIN / -1`).
562 // Division by a fixed nonzero scalar sends the source closed interval endpoints to the
563 // two extreme candidates for the image interval, so `min_max2` yields the true inclusive
564 // lower/upper bounds and ensures `start <= end_incl`.
565 // `checked_add(1)` safely restores half-open representation, therefore the constructed
566 // interval satisfies `start < end_excl`.
567 Some(unsafe { Self::new_unchecked(start, end_excl) })
568 }
569 }
570 }
571
572 // ========================================================
573 // SATURATING
574 // ========================================================
575 pub mod saturating {
576 use super::*;
577
578 impl I64CO {
579 #[inline]
580 pub const fn saturating_minkowski_add(self, other: Self) -> Option<Self> {
581 let start = self.start.saturating_add(other.start);
582 let end_excl = self.end_excl.saturating_add(other.end_incl());
583 Self::try_new(start, end_excl)
584 }
585
586 #[inline]
587 pub const fn saturating_minkowski_sub(self, other: Self) -> Option<Self> {
588 let start = self.start.saturating_sub(other.end_incl());
589 let end_excl = self.end_excl.saturating_sub(other.start);
590 Self::try_new(start, end_excl)
591 }
592
593 #[inline]
594 pub const fn saturating_minkowski_mul_hull(self, other: Self) -> Option<Self> {
595 let a = self.start.saturating_mul(other.start);
596 let b = self.start.saturating_mul(other.end_incl());
597 let c = self.end_incl().saturating_mul(other.start);
598 let d = self.end_incl().saturating_mul(other.end_incl());
599
600 let (start, end_incl) = min_max4(a, b, c, d);
601
602 let end_excl = end_incl.saturating_add(1);
603 Self::try_new(start, end_excl)
604 }
605
606 #[inline]
607 pub const fn saturating_minkowski_div_hull(self, other: Self) -> Option<Self> {
608 if other.start <= 0 && other.end_incl() >= 0 {
609 return None;
610 }
611
612 let a = self.start / other.start;
613 let b = self.start / other.end_incl();
614 let c = self.end_incl() / other.start;
615 let d = self.end_incl() / other.end_incl();
616
617 let (start, end_incl) = min_max4(a, b, c, d);
618
619 let end_excl = end_incl.saturating_add(1);
620 Self::try_new(start, end_excl)
621 }
622 }
623
624 impl I64CO {
625 #[inline]
626 pub const fn saturating_minkowski_add_scalar(self, n: i64) -> Option<Self> {
627 let start = self.start.saturating_add(n);
628 let end_excl = self.end_excl.saturating_add(n);
629 Self::try_new(start, end_excl)
630 }
631
632 #[inline]
633 pub const fn saturating_minkowski_sub_scalar(self, n: i64) -> Option<Self> {
634 let start = self.start.saturating_sub(n);
635 let end_excl = self.end_excl.saturating_sub(n);
636 Self::try_new(start, end_excl)
637 }
638
639 #[inline]
640 pub const fn saturating_minkowski_mul_scalar_hull(self, n: i64) -> Option<Self> {
641 let a = self.start.saturating_mul(n);
642 let b = self.end_incl().saturating_mul(n);
643
644 let (start, end_incl) = min_max2(a, b);
645
646 let end_excl = end_incl.saturating_add(1);
647 Self::try_new(start, end_excl)
648 }
649
650 #[inline]
651 pub const fn saturating_minkowski_div_scalar_hull(self, n: i64) -> Option<Self> {
652 if n == 0 {
653 return None;
654 }
655
656 let a = self.start / n;
657 let b = self.end_incl() / n;
658
659 let (start, end_incl) = min_max2(a, b);
660
661 let end_excl = end_incl.saturating_add(1);
662 Self::try_new(start, end_excl)
663 }
664 }
665 }
666}
667
668crate::traits::impl_co_forwarding!(I64CO, i64, u64);