int_interval/usize.rs
1// -----------------------------------------------------------------------------
2// @generated by xtask/codegen (unsigned)
3// DO NOT EDIT MANUALLY.
4// Changes will be overwritten.
5// -----------------------------------------------------------------------------
6
7use crate::res::{OneTwo, ZeroOneTwo};
8
9#[cfg(test)]
10mod basic_tests;
11#[cfg(test)]
12mod between_tests;
13#[cfg(test)]
14mod checked_minkowski_tests;
15#[cfg(test)]
16mod convex_hull_tests;
17#[cfg(test)]
18mod difference_tests;
19#[cfg(test)]
20mod intersection_tests;
21#[cfg(test)]
22mod saturating_minkowski_tests;
23#[cfg(test)]
24mod symmetric_difference_tests;
25#[cfg(test)]
26mod union_tests;
27
28#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
29pub struct UsizeCO {
30 start: usize,
31 end_excl: usize,
32}
33
34// ------------------------------------------------------------
35// low-level api: construction / accessors / predicates
36// ------------------------------------------------------------
37
38mod basic {
39
40 use super::*;
41
42 impl UsizeCO {
43 #[inline]
44 pub const fn try_new(start: usize, end_excl: usize) -> Option<Self> {
45 if start < end_excl {
46 Some(Self { start, end_excl })
47 } else {
48 None
49 }
50 }
51
52 #[inline]
53 pub const unsafe fn new_unchecked(start: usize, end_excl: usize) -> Self {
54 debug_assert!(start < end_excl);
55 Self { start, end_excl }
56 }
57
58 #[inline]
59 pub const fn start(self) -> usize {
60 self.start
61 }
62
63 #[inline]
64 pub const fn end_excl(self) -> usize {
65 self.end_excl
66 }
67
68 #[inline]
69 pub const fn end_incl(self) -> usize {
70 // usize_low_bound =< start < end_excl
71 self.end_excl - 1
72 }
73
74 #[inline]
75 pub const fn len(self) -> usize {
76 self.end_excl - self.start
77 }
78
79 /// Returns the midpoint of the interval (floor division).
80 #[inline]
81 pub const fn midpoint(self) -> usize {
82 self.start + (self.len() / 2)
83 }
84
85 /// Construct a `UsizeCO` from a midpoint and a length.
86 ///
87 /// Returns `None` if the computed interval is invalid or overflows usize.
88 #[inline]
89 pub const fn checked_from_midpoint_len(mid: usize, len: usize) -> Option<Self> {
90 if len == 0 {
91 return None;
92 }
93 // Compute start = mid - floor(len/2)
94 let Some(start) = mid.checked_sub(len / 2) else {
95 return None;
96 };
97 // Compute end_excl = start + len, return None if overflow
98 let Some(end_excl) = start.checked_add(len) else {
99 return None;
100 };
101
102 // # Safety
103 // This function uses `unsafe { Self::new_unchecked(start, end_excl) }` internally.
104 // The safety is guaranteed by the following checks:
105 // 1. `mid.checked_sub(len / 2)` ensures `start` does not underflow `usize`.
106 // 2. `start.checked_add(len)` ensures `end_excl` does not overflow `usize`.
107 // 3. Because `len > 0`, we have `start < end_excl`.
108 // 4. Therefore, the half-open interval invariant `[start, end_excl)` is preserved.
109 Some(unsafe { Self::new_unchecked(start, end_excl) })
110 }
111
112 /// Saturating version: from midpoint + length, keeps start < end_excl
113 #[inline]
114 pub const fn saturating_from_midpoint_len(mid: usize, len: usize) -> Option<Self> {
115 if len == 0 {
116 return None;
117 }
118
119 let start = mid.saturating_sub(len / 2);
120 let end_excl = start.saturating_add(len);
121
122 // Use try_new to enforce start < end_excl invariant
123 Self::try_new(start, end_excl)
124 }
125
126 #[inline]
127 pub const fn contains(self, x: usize) -> bool {
128 self.start <= x && x < self.end_excl
129 }
130
131 #[inline]
132 pub const fn contains_interval(self, other: Self) -> bool {
133 self.start <= other.start && other.end_excl <= self.end_excl
134 }
135
136 #[inline]
137 pub const fn iter(self) -> core::ops::Range<usize> {
138 self.start..self.end_excl
139 }
140
141 #[inline]
142 pub const fn to_range(self) -> core::ops::Range<usize> {
143 self.start..self.end_excl
144 }
145
146 #[inline]
147 pub const fn intersects(self, other: Self) -> bool {
148 !(self.end_excl <= other.start || other.end_excl <= self.start)
149 }
150
151 #[inline]
152 pub const fn is_adjacent(self, other: Self) -> bool {
153 self.end_excl == other.start || other.end_excl == self.start
154 }
155
156 #[inline]
157 pub const fn is_contiguous_with(self, other: Self) -> bool {
158 self.intersects(other) || self.is_adjacent(other)
159 }
160 }
161}
162
163// ------------------------------------------------------------
164// interval algebra api: intersection / convex_hull / between / union / difference / symmetric_difference
165// ------------------------------------------------------------
166
167mod interval_algebra {
168
169 use super::*;
170
171 impl UsizeCO {
172 /// Returns the intersection of two intervals.
173 ///
174 /// If the intervals do not overlap, returns `None`.
175 #[inline]
176 pub const fn intersection(self, other: Self) -> Option<Self> {
177 let start = if self.start >= other.start {
178 self.start
179 } else {
180 other.start
181 };
182
183 let end_excl = if self.end_excl <= other.end_excl {
184 self.end_excl
185 } else {
186 other.end_excl
187 };
188
189 Self::try_new(start, end_excl)
190 }
191
192 /// Returns the convex hull (smallest interval containing both) of two intervals.
193 ///
194 /// Always returns a valid `UsizeCO`.
195 #[inline]
196 pub const fn convex_hull(self, other: Self) -> Self {
197 let start = if self.start <= other.start {
198 self.start
199 } else {
200 other.start
201 };
202
203 let end_excl = if self.end_excl >= other.end_excl {
204 self.end_excl
205 } else {
206 other.end_excl
207 };
208
209 Self { start, end_excl }
210 }
211
212 /// Returns the interval strictly between two intervals.
213 ///
214 /// If the intervals are contiguous or overlap, returns `None`.
215 #[inline]
216 pub const fn between(self, other: Self) -> Option<Self> {
217 let (left, right) = if self.start <= other.start {
218 (self, other)
219 } else {
220 (other, self)
221 };
222
223 Self::try_new(left.end_excl, right.start)
224 }
225
226 /// Returns the union of two intervals.
227 ///
228 /// - If intervals are contiguous or overlapping, returns `One` containing the merged interval.
229 /// - Otherwise, returns `Two` containing both intervals in ascending order.
230 #[inline]
231 pub const fn union(self, other: Self) -> OneTwo<Self> {
232 if self.is_contiguous_with(other) {
233 OneTwo::One(self.convex_hull(other))
234 } else if self.start <= other.start {
235 OneTwo::Two(self, other)
236 } else {
237 OneTwo::Two(other, self)
238 }
239 }
240
241 /// Returns the difference of two intervals (self - other).
242 ///
243 /// - If no overlap, returns `One(self)`.
244 /// - If partial overlap, returns `One` or `Two` depending on remaining segments.
245 /// - If fully contained, returns `Zero`.
246 #[inline]
247 pub const fn difference(self, other: Self) -> ZeroOneTwo<Self> {
248 match self.intersection(other) {
249 None => ZeroOneTwo::One(self),
250 Some(inter) => {
251 let left = Self::try_new(self.start, inter.start);
252 let right = Self::try_new(inter.end_excl, self.end_excl);
253
254 match (left, right) {
255 (None, None) => ZeroOneTwo::Zero,
256 (Some(x), None) | (None, Some(x)) => ZeroOneTwo::One(x),
257 (Some(x), Some(y)) => ZeroOneTwo::Two(x, y),
258 }
259 }
260 }
261 }
262
263 /// Returns the symmetric difference of two intervals.
264 ///
265 /// Equivalent to `(self - other) ∪ (other - self)`.
266 /// - If intervals do not overlap, returns `Two(self, other)` in order.
267 /// - If intervals partially overlap, returns remaining non-overlapping segments as `One` or `Two`.
268 #[inline]
269 pub const fn symmetric_difference(self, other: Self) -> ZeroOneTwo<Self> {
270 match self.intersection(other) {
271 None => {
272 if self.start <= other.start {
273 ZeroOneTwo::Two(self, other)
274 } else {
275 ZeroOneTwo::Two(other, self)
276 }
277 }
278 Some(inter) => {
279 let hull = self.convex_hull(other);
280 let left = Self::try_new(hull.start, inter.start);
281 let right = Self::try_new(inter.end_excl, hull.end_excl);
282
283 match (left, right) {
284 (None, None) => ZeroOneTwo::Zero,
285 (Some(x), None) | (None, Some(x)) => ZeroOneTwo::One(x),
286 (Some(x), Some(y)) => ZeroOneTwo::Two(x, y),
287 }
288 }
289 }
290 }
291 }
292}
293
294// ------------------------------------------------------------
295// Module: Minkowski arithmetic for UsizeCO
296// Provides checked and saturating Minkowski operations
297// ------------------------------------------------------------
298
299pub mod minkowski {
300 use super::*;
301
302 pub mod checked {
303 use super::*;
304
305 // --------------------------------------------------------
306 // Interval-to-interval Minkowski operations
307 // --------------------------------------------------------
308 impl UsizeCO {
309 /// Minkowski addition: [a_start, a_end) + [b_start, b_end)
310 #[inline]
311 pub const fn checked_minkowski_add(self, other: Self) -> Option<Self> {
312 let Some(start) = self.start.checked_add(other.start) else {
313 return None;
314 };
315 let Some(end_excl) = self.end_excl.checked_add(other.end_incl()) else {
316 return None;
317 };
318
319 // SAFETY:
320 // `checked_add` guarantees both bounds are computed without overflow.
321 // For half-open intervals, addition preserves ordering:
322 // self.start <= self.end_excl - 1 and other.start <= other.end_incl(),
323 // therefore `start < end_excl` still holds for the resulting interval.
324 Some(unsafe { Self::new_unchecked(start, end_excl) })
325 }
326
327 /// Minkowski subtraction: [a_start, a_end) - [b_start, b_end)
328 #[inline]
329 pub const fn checked_minkowski_sub(self, other: Self) -> Option<Self> {
330 let Some(start) = self.start.checked_sub(other.end_incl()) else {
331 return None;
332 };
333 let Some(end_excl) = self.end_excl.checked_sub(other.start) else {
334 return None;
335 };
336
337 // SAFETY:
338 // `checked_sub` guarantees both bounds are computed without underflow.
339 // Since `self.start <= self.end_excl - 1` and `other.start <= other.end_incl()`,
340 // we have `self.start - other.end_incl() < self.end_excl - other.start`,
341 // so the resulting half-open interval remains valid.
342 Some(unsafe { Self::new_unchecked(start, end_excl) })
343 }
344
345 /// Minkowski multiplication: [a_start, a_end) * [b_start, b_end)
346 #[inline]
347 pub const fn checked_minkowski_mul(self, other: Self) -> Option<Self> {
348 let Some(start) = self.start.checked_mul(other.start) else {
349 return None;
350 };
351 let Some(end_incl) = self.end_incl().checked_mul(other.end_incl()) else {
352 return None;
353 };
354 let Some(end_excl) = end_incl.checked_add(1) else {
355 return None;
356 };
357
358 // SAFETY:
359 // For `UsizeCO`, all values are non-negative, so endpoint-wise multiplication
360 // is monotone. `start` is the minimum product and `end_incl` is the maximum.
361 // `checked_add(1)` converts the inclusive upper bound back to half-open form,
362 // and overflow has already been excluded.
363 Some(unsafe { Self::new_unchecked(start, end_excl) })
364 }
365
366 /// Minkowski division: [a_start, a_end) / [b_start, b_end)
367 #[inline]
368 pub const fn checked_minkowski_div(self, other: Self) -> Option<Self> {
369 if other.start == 0 {
370 return None;
371 }
372
373 let Some(start) = self.start.checked_div(other.end_incl()) else {
374 return None;
375 };
376 let Some(end_incl) = self.end_incl().checked_div(other.start) else {
377 return None;
378 };
379 let Some(end_excl) = end_incl.checked_add(1) else {
380 return None;
381 };
382
383 // SAFETY:
384 // `other.start != 0` excludes division by zero, and all operands are unsigned.
385 // Over positive integers, division is monotone with respect to the dividend and
386 // anti-monotone with respect to the divisor, so:
387 // min = self.start / other.end_incl()
388 // max = self.end_incl() / other.start
389 // Thus `start <= end_incl`, and `end_excl = end_incl + 1` is checked.
390 Some(unsafe { Self::new_unchecked(start, end_excl) })
391 }
392 }
393
394 // --------------------------------------------------------
395 // Interval-to-scalar Minkowski operations
396 // --------------------------------------------------------
397 impl UsizeCO {
398 /// Add a scalar to an interval: [start, end) + n
399 #[inline]
400 pub const fn checked_minkowski_add_n(self, n: usize) -> Option<Self> {
401 let Some(start) = self.start.checked_add(n) else {
402 return None;
403 };
404 let Some(end_excl) = self.end_excl.checked_add(n) else {
405 return None;
406 };
407
408 // SAFETY:
409 // `checked_add` excludes overflow, and adding the same scalar to both bounds
410 // preserves the half-open interval ordering.
411 Some(unsafe { Self::new_unchecked(start, end_excl) })
412 }
413
414 /// Subtract a scalar from an interval: [start, end) - n
415 #[inline]
416 pub const fn checked_minkowski_sub_n(self, n: usize) -> Option<Self> {
417 let Some(start) = self.start.checked_sub(n) else {
418 return None;
419 };
420 let Some(end_excl) = self.end_excl.checked_sub(n) else {
421 return None;
422 };
423
424 // SAFETY:
425 // `checked_sub` excludes underflow, and subtracting the same scalar from both
426 // bounds preserves the half-open interval ordering.
427 Some(unsafe { Self::new_unchecked(start, end_excl) })
428 }
429
430 /// Multiply an interval by a scalar: [start, end) * n
431 #[inline]
432 pub const fn checked_minkowski_mul_n(self, n: usize) -> Option<Self> {
433 let Some(start) = self.start.checked_mul(n) else {
434 return None;
435 };
436 let Some(end_incl) = self.end_incl().checked_mul(n) else {
437 return None;
438 };
439 let Some(end_excl) = end_incl.checked_add(1) else {
440 return None;
441 };
442
443 // SAFETY:
444 // For unsigned integers, multiplication by a scalar is monotone.
445 // Therefore `start * n` is the lower bound and `end_incl * n` is the upper bound.
446 // `checked_*` operations exclude overflow, and `end_excl` restores half-open form.
447 Some(unsafe { Self::new_unchecked(start, end_excl) })
448 }
449
450 /// Divide an interval by a scalar: [start, end) / n
451 #[inline]
452 pub const fn checked_minkowski_div_n(self, n: usize) -> Option<Self> {
453 if n == 0 {
454 return None;
455 }
456
457 let start = self.start / n;
458 let end_incl = self.end_incl() / n;
459 let Some(end_excl) = end_incl.checked_add(1) else {
460 return None;
461 };
462
463 // SAFETY:
464 // `n != 0` excludes division by zero. For unsigned integers and positive scalar `n`,
465 // division is monotone, so `self.start / n <= self.end_incl() / n`.
466 // `checked_add(1)` safely converts the inclusive upper bound to half-open form.
467 Some(unsafe { Self::new_unchecked(start, end_excl) })
468 }
469 }
470 }
471
472 pub mod saturating {
473 use super::*;
474
475 // --------------------------------------------------------
476 // Interval-to-interval Minkowski operations
477 // --------------------------------------------------------
478 impl UsizeCO {
479 #[inline]
480 pub const fn saturating_minkowski_add(self, other: Self) -> Option<Self> {
481 let start = self.start.saturating_add(other.start);
482 let end_excl = self.end_excl.saturating_add(other.end_incl());
483 Self::try_new(start, end_excl)
484 }
485
486 #[inline]
487 pub const fn saturating_minkowski_sub(self, other: Self) -> Option<Self> {
488 let start = self.start.saturating_sub(other.end_incl());
489 let end_excl = self.end_excl.saturating_sub(other.start);
490 Self::try_new(start, end_excl)
491 }
492
493 #[inline]
494 pub const fn saturating_minkowski_mul(self, other: Self) -> Option<Self> {
495 let start = self.start.saturating_mul(other.start);
496 let end_incl = self.end_incl().saturating_mul(other.end_incl());
497 let end_excl = end_incl.saturating_add(1);
498 Self::try_new(start, end_excl)
499 }
500
501 #[inline]
502 pub const fn saturating_minkowski_div(self, other: Self) -> Option<Self> {
503 if other.start == 0 {
504 return None;
505 }
506
507 let start = self.start / other.end_incl();
508 let end_incl = self.end_incl() / other.start;
509 let end_excl = end_incl.saturating_add(1);
510 Self::try_new(start, end_excl)
511 }
512 }
513
514 // --------------------------------------------------------
515 // Interval-to-scalar Minkowski operations
516 // --------------------------------------------------------
517 impl UsizeCO {
518 /// Saturating add scalar: [start, end) + n
519 #[inline]
520 pub const fn saturating_minkowski_add_n(self, n: usize) -> Option<Self> {
521 let start = self.start.saturating_add(n);
522 let end_excl = self.end_excl.saturating_add(n);
523 Self::try_new(start, end_excl)
524 }
525
526 /// Saturating sub scalar: [start, end) - n
527 #[inline]
528 pub const fn saturating_minkowski_sub_n(self, n: usize) -> Option<Self> {
529 let start = self.start.saturating_sub(n);
530 let end_excl = self.end_excl.saturating_sub(n);
531 Self::try_new(start, end_excl)
532 }
533
534 /// Saturating mul scalar: [start, end) * n
535 #[inline]
536 pub const fn saturating_minkowski_mul_n(self, n: usize) -> Option<Self> {
537 let start = self.start.saturating_mul(n);
538 let end_incl = self.end_incl().saturating_mul(n);
539 let end_excl = end_incl.saturating_add(1);
540 Self::try_new(start, end_excl)
541 }
542
543 /// Saturating div scalar: [start, end) / n
544 #[inline]
545 pub const fn saturating_minkowski_div_n(self, n: usize) -> Option<Self> {
546 if n == 0 {
547 return None;
548 }
549
550 let start = self.start / n;
551 let end_incl = self.end_incl() / n;
552 let end_excl = end_incl.saturating_add(1);
553 Self::try_new(start, end_excl)
554 }
555 }
556 }
557}