Skip to main content

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