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
10#[cfg(test)]
11mod basic_tests;
12#[cfg(test)]
13mod between_tests;
14#[cfg(test)]
15mod checked_minkowski_tests;
16#[cfg(test)]
17mod convex_hull_tests;
18#[cfg(test)]
19mod difference_tests;
20#[cfg(test)]
21mod intersection_tests;
22#[cfg(test)]
23mod symmetric_difference_tests;
24#[cfg(test)]
25mod union_tests;
26
27#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
28pub struct IsizeCO {
29 start: isize,
30 end_excl: isize,
31}
32
33// ------------------------------------------------------------
34// low-level api: construction / accessors / predicates
35// ------------------------------------------------------------
36mod basic {
37
38 use super::*;
39
40 impl IsizeCO {
41 #[inline]
42 pub const fn try_new(start: isize, end_excl: isize) -> Option<Self> {
43 if start < end_excl {
44 Some(Self { start, end_excl })
45 } else {
46 None
47 }
48 }
49
50 #[inline]
51 pub const unsafe fn new_unchecked(start: isize, end_excl: isize) -> Self {
52 debug_assert!(start < end_excl);
53 Self { start, end_excl }
54 }
55
56 #[inline]
57 pub const fn start(self) -> isize {
58 self.start
59 }
60
61 #[inline]
62 pub const fn end_excl(self) -> isize {
63 self.end_excl
64 }
65
66 #[inline]
67 pub const fn end_incl(self) -> isize {
68 // isize_low_bound =< start < end_excl
69 self.end_excl - 1
70 }
71
72 #[inline]
73 pub const fn len(self) -> usize {
74 const SIGN_MASK: usize = 1 << (isize::BITS - 1);
75 ((self.end_excl as usize) ^ SIGN_MASK) - ((self.start as usize) ^ SIGN_MASK)
76 }
77
78 #[inline]
79 pub const fn contains(self, x: isize) -> bool {
80 self.start <= x && x < self.end_excl
81 }
82
83 #[inline]
84 pub const fn contains_interval(self, other: Self) -> bool {
85 self.start <= other.start && other.end_excl <= self.end_excl
86 }
87
88 #[inline]
89 pub const fn iter(self) -> core::ops::Range<isize> {
90 self.start..self.end_excl
91 }
92
93 #[inline]
94 pub const fn to_range(self) -> core::ops::Range<isize> {
95 self.start..self.end_excl
96 }
97
98 #[inline]
99 pub const fn intersects(self, other: Self) -> bool {
100 !(self.end_excl <= other.start || other.end_excl <= self.start)
101 }
102
103 #[inline]
104 pub const fn is_adjacent(self, other: Self) -> bool {
105 self.end_excl == other.start || other.end_excl == self.start
106 }
107
108 #[inline]
109 pub const fn is_contiguous_with(self, other: Self) -> bool {
110 self.intersects(other) || self.is_adjacent(other)
111 }
112 }
113}
114
115// ------------------------------------------------------------
116// interval algebra api: intersection / convex_hull / between / union / difference / symmetric_difference
117// ------------------------------------------------------------
118
119mod interval_algebra {
120
121 use super::*;
122
123 impl IsizeCO {
124 /// Returns the intersection of two intervals.
125 ///
126 /// If the intervals do not overlap, returns `None`.
127 #[inline]
128 pub const fn intersection(self, other: Self) -> Option<Self> {
129 let start = if self.start >= other.start {
130 self.start
131 } else {
132 other.start
133 };
134
135 let end_excl = if self.end_excl <= other.end_excl {
136 self.end_excl
137 } else {
138 other.end_excl
139 };
140
141 Self::try_new(start, end_excl)
142 }
143
144 /// Returns the convex hull (smallest interval containing both) of two intervals.
145 ///
146 /// Always returns a valid `IsizeCO`.
147 #[inline]
148 pub const fn convex_hull(self, other: Self) -> Self {
149 let start = if self.start <= other.start {
150 self.start
151 } else {
152 other.start
153 };
154
155 let end_excl = if self.end_excl >= other.end_excl {
156 self.end_excl
157 } else {
158 other.end_excl
159 };
160
161 Self { start, end_excl }
162 }
163
164 /// Returns the interval strictly between two intervals.
165 ///
166 /// If the intervals are contiguous or overlap, returns `None`.
167 #[inline]
168 pub const fn between(self, other: Self) -> Option<Self> {
169 let (left, right) = if self.start <= other.start {
170 (self, other)
171 } else {
172 (other, self)
173 };
174
175 Self::try_new(left.end_excl, right.start)
176 }
177
178 /// Returns the union of two intervals.
179 ///
180 /// - If intervals are contiguous or overlapping, returns `One` containing the merged interval.
181 /// - Otherwise, returns `Two` containing both intervals in ascending order.
182 #[inline]
183 pub const fn union(self, other: Self) -> OneTwo<Self> {
184 if self.is_contiguous_with(other) {
185 OneTwo::One(self.convex_hull(other))
186 } else if self.start <= other.start {
187 OneTwo::Two(self, other)
188 } else {
189 OneTwo::Two(other, self)
190 }
191 }
192
193 /// Returns the difference of two intervals (self - other).
194 ///
195 /// - If no overlap, returns `One(self)`.
196 /// - If partial overlap, returns `One` or `Two` depending on remaining segments.
197 /// - If fully contained, returns `Zero`.
198 #[inline]
199 pub const fn difference(self, other: Self) -> ZeroOneTwo<Self> {
200 match self.intersection(other) {
201 None => ZeroOneTwo::One(self),
202 Some(inter) => {
203 let left = Self::try_new(self.start, inter.start);
204 let right = Self::try_new(inter.end_excl, self.end_excl);
205
206 match (left, right) {
207 (None, None) => ZeroOneTwo::Zero,
208 (Some(x), None) | (None, Some(x)) => ZeroOneTwo::One(x),
209 (Some(x), Some(y)) => ZeroOneTwo::Two(x, y),
210 }
211 }
212 }
213 }
214
215 /// Returns the symmetric difference of two intervals.
216 ///
217 /// Equivalent to `(self - other) ∪ (other - self)`.
218 /// - If intervals do not overlap, returns `Two(self, other)` in order.
219 /// - If intervals partially overlap, returns remaining non-overlapping segments as `One` or `Two`.
220 #[inline]
221 pub const fn symmetric_difference(self, other: Self) -> ZeroOneTwo<Self> {
222 match self.intersection(other) {
223 None => {
224 if self.start <= other.start {
225 ZeroOneTwo::Two(self, other)
226 } else {
227 ZeroOneTwo::Two(other, self)
228 }
229 }
230 Some(inter) => {
231 let hull = self.convex_hull(other);
232 let left = Self::try_new(hull.start, inter.start);
233 let right = Self::try_new(inter.end_excl, hull.end_excl);
234
235 match (left, right) {
236 (None, None) => ZeroOneTwo::Zero,
237 (Some(x), None) | (None, Some(x)) => ZeroOneTwo::One(x),
238 (Some(x), Some(y)) => ZeroOneTwo::Two(x, y),
239 }
240 }
241 }
242 }
243 }
244}
245
246// ------------------------------------------------------------
247// Module: Minkowski arithmetic for IsizeCO
248// Provides checked and saturating Minkowski operations for intervals
249// ------------------------------------------------------------
250
251pub mod minkowski {
252 use super::*;
253
254 type Min = isize;
255 type Max = isize;
256
257 #[inline]
258 const fn min_max4(a: isize, b: isize, c: isize, d: isize) -> (Min, Max) {
259 let (min1, max1) = if a < b { (a, b) } else { (b, a) };
260 let (min2, max2) = if c < d { (c, d) } else { (d, c) };
261 let min = if min1 < min2 { min1 } else { min2 };
262 let max = if max1 > max2 { max1 } else { max2 };
263 (min, max)
264 }
265
266 #[inline]
267 const fn min_max2(a: isize, b: isize) -> (Min, Max) {
268 if a < b { (a, b) } else { (b, a) }
269 }
270
271 pub mod checked {
272 use super::*;
273
274 // --------------------------------------------------------
275 // Interval-to-interval
276 // --------------------------------------------------------
277 impl IsizeCO {
278 #[inline]
279 pub const fn checked_minkowski_add(self, other: Self) -> Option<Self> {
280 let Some(start) = self.start.checked_add(other.start) else {
281 return None;
282 };
283 let Some(end_excl) = self.end_excl.checked_add(other.end_incl()) else {
284 return None;
285 };
286
287 // SAFETY:
288 // `checked_add` guarantees both endpoint computations succeed without overflow.
289 // For half-open intervals, let `a_last = self.end_incl()` and `b_last = other.end_incl()`.
290 // Since `self.start <= a_last` and `other.start <= b_last`, we have
291 // `self.start + other.start <= a_last + b_last < self.end_excl + other.end_incl()`,
292 // hence the computed bounds satisfy `start < end_excl`.
293 Some(unsafe { Self::new_unchecked(start, end_excl) })
294 }
295
296 #[inline]
297 pub const fn checked_minkowski_sub(self, other: Self) -> Option<Self> {
298 let Some(start) = self.start.checked_sub(other.end_incl()) else {
299 return None;
300 };
301 let Some(end_excl) = self.end_excl.checked_sub(other.start) else {
302 return None;
303 };
304
305 // SAFETY:
306 // `checked_sub` guarantees both endpoint computations succeed without overflow.
307 // For interval subtraction, the minimum is attained at `self.start - other.end_incl()`
308 // and the exclusive upper bound is `self.end_excl - other.start`.
309 // Because `other.start <= other.end_incl()`, we get
310 // `self.start - other.end_incl() < self.end_excl - other.start`,
311 // so the resulting half-open interval is valid.
312 Some(unsafe { Self::new_unchecked(start, end_excl) })
313 }
314
315 #[inline]
316 pub const fn checked_minkowski_mul(self, other: Self) -> Option<Self> {
317 let a = self.start;
318 let b = self.end_incl();
319 let c = other.start;
320 let d = other.end_incl();
321
322 let Some(p1) = a.checked_mul(c) else {
323 return None;
324 };
325 let Some(p2) = a.checked_mul(d) else {
326 return None;
327 };
328 let Some(p3) = b.checked_mul(c) else {
329 return None;
330 };
331 let Some(p4) = b.checked_mul(d) else {
332 return None;
333 };
334
335 let (start, end_incl) = min_max4(p1, p2, p3, p4);
336
337 let Some(end_excl) = end_incl.checked_add(1) else {
338 return None;
339 };
340
341 // SAFETY:
342 // All four corner products are computed with `checked_mul`, so no intermediate
343 // multiplication overflows. For multiplication over a closed integer rectangle
344 // `[a, b] × [c, d]`, every attainable extremum occurs at a corner, so
345 // `min_max4(p1, p2, p3, p4)` yields the true inclusive lower/upper bounds.
346 // Therefore `start <= end_incl` holds by construction.
347 // `checked_add(1)` then safely converts the inclusive upper bound to the exclusive
348 // upper bound, and implies `end_excl = end_incl + 1`, hence `start < end_excl`.
349 Some(unsafe { Self::new_unchecked(start, end_excl) })
350 }
351
352 #[inline]
353 pub const fn checked_minkowski_div(self, other: Self) -> Option<Self> {
354 if other.start <= 0 && other.end_incl() >= 0 {
355 return None;
356 }
357
358 let a = self.start;
359 let b = self.end_incl();
360 let c = other.start;
361 let d = other.end_incl();
362
363 let Some(p1) = a.checked_div(c) else {
364 return None;
365 };
366 let Some(p2) = a.checked_div(d) else {
367 return None;
368 };
369 let Some(p3) = b.checked_div(c) else {
370 return None;
371 };
372 let Some(p4) = b.checked_div(d) else {
373 return None;
374 };
375
376 let (start, end_incl) = min_max4(p1, p2, p3, p4);
377
378 let Some(end_excl) = end_incl.checked_add(1) else {
379 return None;
380 };
381
382 // SAFETY:
383 // The guard `other.start <= 0 && other.end_incl() >= 0` rejects any divisor interval
384 // that contains zero, so division by zero cannot occur anywhere in the divisor set.
385 // Each corner quotient is computed with `checked_div`, so exceptional signed cases
386 // such as `MIN / -1` are also rejected.
387 // On each connected component of the divisor domain that excludes zero, integer division
388 // is monotone with respect to the rectangle corners relevant to the extremum search;
389 // thus the global inclusive bounds over the interval pair are captured by the four
390 // corner quotients and recovered by `min_max4`, giving `start <= end_incl`.
391 // `checked_add(1)` safely converts the inclusive upper bound to half-open form, so
392 // the final bounds satisfy `start < end_excl`.
393 Some(unsafe { Self::new_unchecked(start, end_excl) })
394 }
395 }
396
397 // --------------------------------------------------------
398 // Scalar
399 // --------------------------------------------------------
400 impl IsizeCO {
401 #[inline]
402 pub const fn checked_minkowski_add_n(self, n: isize) -> Option<Self> {
403 let Some(start) = self.start.checked_add(n) else {
404 return None;
405 };
406 let Some(end_excl) = self.end_excl.checked_add(n) else {
407 return None;
408 };
409
410 // SAFETY:
411 // `checked_add` guarantees both translated bounds are computed without overflow.
412 // Adding the same scalar to both endpoints preserves the interval width exactly,
413 // so a valid half-open interval remains valid and still satisfies `start < end_excl`.
414 Some(unsafe { Self::new_unchecked(start, end_excl) })
415 }
416
417 #[inline]
418 pub const fn checked_minkowski_sub_n(self, n: isize) -> Option<Self> {
419 let Some(start) = self.start.checked_sub(n) else {
420 return None;
421 };
422 let Some(end_excl) = self.end_excl.checked_sub(n) else {
423 return None;
424 };
425
426 // SAFETY:
427 // `checked_sub` guarantees both translated bounds are computed without overflow.
428 // Subtracting the same scalar from both endpoints preserves the interval width exactly,
429 // so the strict half-open ordering is unchanged and `start < end_excl` still holds.
430 Some(unsafe { Self::new_unchecked(start, end_excl) })
431 }
432
433 #[inline]
434 pub const fn checked_minkowski_mul_n(self, n: isize) -> Option<Self> {
435 let Some(a) = self.start.checked_mul(n) else {
436 return None;
437 };
438 let Some(b) = self.end_incl().checked_mul(n) else {
439 return None;
440 };
441
442 let (start, end_incl) = min_max2(a, b);
443
444 let Some(end_excl) = end_incl.checked_add(1) else {
445 return None;
446 };
447
448 // SAFETY:
449 // Both endpoint products are computed with `checked_mul`, so no signed overflow occurs.
450 // Multiplication by a scalar maps the closed source interval endpoints to the two extreme
451 // candidates; `min_max2` therefore recovers the true inclusive lower/upper bounds whether
452 // `n` is positive, zero, or negative, giving `start <= end_incl`.
453 // `checked_add(1)` safely converts the inclusive upper bound into the exclusive upper bound,
454 // which guarantees the final half-open interval satisfies `start < end_excl`.
455 Some(unsafe { Self::new_unchecked(start, end_excl) })
456 }
457
458 #[inline]
459 pub const fn checked_minkowski_div_n(self, n: isize) -> Option<Self> {
460 if n == 0 {
461 return None;
462 }
463
464 let Some(a) = self.start.checked_div(n) else {
465 return None;
466 };
467 let Some(b) = self.end_incl().checked_div(n) else {
468 return None;
469 };
470
471 let (start, end_incl) = min_max2(a, b);
472
473 let Some(end_excl) = end_incl.checked_add(1) else {
474 return None;
475 };
476
477 // SAFETY:
478 // The guard `n != 0` excludes division by zero, and `checked_div` additionally rejects
479 // the only overflowing signed case (`MIN / -1`).
480 // Division by a fixed nonzero scalar sends the source closed interval endpoints to the
481 // two extreme candidates for the image interval, so `min_max2` yields the true inclusive
482 // lower/upper bounds and ensures `start <= end_incl`.
483 // `checked_add(1)` safely restores half-open representation, therefore the constructed
484 // interval satisfies `start < end_excl`.
485 Some(unsafe { Self::new_unchecked(start, end_excl) })
486 }
487 }
488 }
489
490 // ========================================================
491 // SATURATING
492 // ========================================================
493 pub mod saturating {
494 use super::*;
495
496 impl IsizeCO {
497 #[inline]
498 pub const fn saturating_minkowski_add(self, other: Self) -> Option<Self> {
499 let start = self.start.saturating_add(other.start);
500 let end_excl = self.end_excl.saturating_add(other.end_incl());
501 Self::try_new(start, end_excl)
502 }
503
504 #[inline]
505 pub const fn saturating_minkowski_sub(self, other: Self) -> Option<Self> {
506 let start = self.start.saturating_sub(other.end_incl());
507 let end_excl = self.end_excl.saturating_sub(other.start);
508 Self::try_new(start, end_excl)
509 }
510
511 #[inline]
512 pub const fn saturating_minkowski_mul(self, other: Self) -> Option<Self> {
513 let a = self.start.saturating_mul(other.start);
514 let b = self.start.saturating_mul(other.end_incl());
515 let c = self.end_incl().saturating_mul(other.start);
516 let d = self.end_incl().saturating_mul(other.end_incl());
517
518 let (start, end_incl) = min_max4(a, b, c, d);
519
520 let end_excl = end_incl.saturating_add(1);
521 Self::try_new(start, end_excl)
522 }
523
524 #[inline]
525 pub const fn saturating_minkowski_div(self, other: Self) -> Option<Self> {
526 if other.start <= 0 && other.end_incl() >= 0 {
527 return None;
528 }
529
530 let a = self.start / other.start;
531 let b = self.start / other.end_incl();
532 let c = self.end_incl() / other.start;
533 let d = self.end_incl() / other.end_incl();
534
535 let (start, end_incl) = min_max4(a, b, c, d);
536
537 let end_excl = end_incl.saturating_add(1);
538 Self::try_new(start, end_excl)
539 }
540 }
541
542 impl IsizeCO {
543 #[inline]
544 pub const fn saturating_minkowski_add_n(self, n: isize) -> Option<Self> {
545 let start = self.start.saturating_add(n);
546 let end_excl = self.end_excl.saturating_add(n);
547 Self::try_new(start, end_excl)
548 }
549
550 #[inline]
551 pub const fn saturating_minkowski_sub_n(self, n: isize) -> Option<Self> {
552 let start = self.start.saturating_sub(n);
553 let end_excl = self.end_excl.saturating_sub(n);
554 Self::try_new(start, end_excl)
555 }
556
557 #[inline]
558 pub const fn saturating_minkowski_mul_n(self, n: isize) -> Option<Self> {
559 let a = self.start.saturating_mul(n);
560 let b = self.end_incl().saturating_mul(n);
561
562 let (start, end_incl) = min_max2(a, b);
563
564 let end_excl = end_incl.saturating_add(1);
565 Self::try_new(start, end_excl)
566 }
567
568 #[inline]
569 pub const fn saturating_minkowski_div_n(self, n: isize) -> Option<Self> {
570 if n == 0 {
571 return None;
572 }
573
574 let a = self.start / n;
575 let b = self.end_incl() / n;
576
577 let (start, end_incl) = min_max2(a, b);
578
579 let end_excl = end_incl.saturating_add(1);
580 Self::try_new(start, end_excl)
581 }
582 }
583 }
584}