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