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