int_intervals/interval/traits.rs
1use core::num::ParseIntError;
2
3use crate::interval::{OneTwo, ZeroOneTwo};
4
5pub(crate) mod forwarding;
6pub(crate) use forwarding::impl_co_forwarding;
7
8mod sealed;
9
10/// Built-in integer coordinate type accepted by closed-open intervals.
11pub trait IntPrimitive: sealed::Int {
12 // ============================================================
13 // Constants
14 // ============================================================
15
16 fn zero() -> Self;
17 fn one() -> Self;
18
19 fn min_value() -> Self;
20 fn max_value() -> Self;
21
22 // ============================================================
23 // Numeric conversion
24 // ============================================================
25
26 fn as_f32(self) -> f32;
27 fn as_f64(self) -> f64;
28
29 fn checked_from<T>(value: T) -> Option<Self>
30 where
31 Self: Sized + TryFrom<T>;
32
33 // ============================================================
34 // Checked same-type arithmetic
35 // ============================================================
36
37 fn checked_add(self, rhs: Self) -> Option<Self>;
38 fn checked_sub(self, rhs: Self) -> Option<Self>;
39 fn checked_mul(self, rhs: Self) -> Option<Self>;
40 fn checked_div(self, rhs: Self) -> Option<Self>;
41 fn checked_rem(self, rhs: Self) -> Option<Self>;
42
43 // ============================================================
44 // Checked converted arithmetic
45 // ============================================================
46
47 fn checked_add_from<T>(self, rhs: T) -> Option<Self>
48 where
49 Self: Sized + TryFrom<T>;
50
51 fn checked_sub_from<T>(self, rhs: T) -> Option<Self>
52 where
53 Self: Sized + TryFrom<T>;
54
55 fn checked_mul_from<T>(self, rhs: T) -> Option<Self>
56 where
57 Self: Sized + TryFrom<T>;
58
59 fn checked_div_from<T>(self, rhs: T) -> Option<Self>
60 where
61 Self: Sized + TryFrom<T>;
62
63 fn checked_rem_from<T>(self, rhs: T) -> Option<Self>
64 where
65 Self: Sized + TryFrom<T>;
66
67 // ============================================================
68 // Saturating same-type arithmetic
69 // ============================================================
70
71 fn saturating_add(self, rhs: Self) -> Self;
72 fn saturating_sub(self, rhs: Self) -> Self;
73 fn saturating_mul(self, rhs: Self) -> Self;
74
75 // ============================================================
76 // Saturating converted arithmetic
77 // ============================================================
78
79 fn saturating_add_from<T>(self, rhs: T) -> Option<Self>
80 where
81 Self: Sized + TryFrom<T>;
82
83 fn saturating_sub_from<T>(self, rhs: T) -> Option<Self>
84 where
85 Self: Sized + TryFrom<T>;
86
87 fn saturating_mul_from<T>(self, rhs: T) -> Option<Self>
88 where
89 Self: Sized + TryFrom<T>;
90
91 // ============================================================
92 // Unit stepping
93 // ============================================================
94
95 fn checked_next(self) -> Option<Self>;
96 fn checked_prev(self) -> Option<Self>;
97
98 fn saturating_next(self) -> Self;
99 fn saturating_prev(self) -> Self;
100
101 // ============================================================
102 // Parsing
103 // ============================================================
104
105 fn parse_decimal(src: &str) -> Result<Self, ParseIntError>;
106}
107
108impl<T> IntPrimitive for T
109where
110 T: sealed::Int,
111{
112 // ============================================================
113 // Constants
114 // ============================================================
115
116 #[inline]
117 fn zero() -> Self {
118 sealed::Int::zero()
119 }
120
121 #[inline]
122 fn one() -> Self {
123 sealed::Int::one()
124 }
125
126 #[inline]
127 fn min_value() -> Self {
128 sealed::Int::min_value()
129 }
130
131 #[inline]
132 fn max_value() -> Self {
133 sealed::Int::max_value()
134 }
135
136 // ============================================================
137 // Numeric conversion
138 // ============================================================
139
140 #[inline]
141 fn as_f32(self) -> f32 {
142 sealed::Int::as_f32(self)
143 }
144
145 #[inline]
146 fn as_f64(self) -> f64 {
147 sealed::Int::as_f64(self)
148 }
149
150 #[inline]
151 fn checked_from<U>(value: U) -> Option<Self>
152 where
153 Self: Sized + TryFrom<U>,
154 {
155 sealed::Int::checked_from(value)
156 }
157
158 // ============================================================
159 // Checked same-type arithmetic
160 // ============================================================
161
162 #[inline]
163 fn checked_add(self, rhs: Self) -> Option<Self> {
164 sealed::Int::checked_add(self, rhs)
165 }
166
167 #[inline]
168 fn checked_sub(self, rhs: Self) -> Option<Self> {
169 sealed::Int::checked_sub(self, rhs)
170 }
171
172 #[inline]
173 fn checked_mul(self, rhs: Self) -> Option<Self> {
174 sealed::Int::checked_mul(self, rhs)
175 }
176
177 #[inline]
178 fn checked_div(self, rhs: Self) -> Option<Self> {
179 sealed::Int::checked_div(self, rhs)
180 }
181
182 #[inline]
183 fn checked_rem(self, rhs: Self) -> Option<Self> {
184 sealed::Int::checked_rem(self, rhs)
185 }
186
187 // ============================================================
188 // Checked converted arithmetic
189 // ============================================================
190
191 #[inline]
192 fn checked_add_from<U>(self, rhs: U) -> Option<Self>
193 where
194 Self: Sized + TryFrom<U>,
195 {
196 sealed::Int::checked_add_from(self, rhs)
197 }
198
199 #[inline]
200 fn checked_sub_from<U>(self, rhs: U) -> Option<Self>
201 where
202 Self: Sized + TryFrom<U>,
203 {
204 sealed::Int::checked_sub_from(self, rhs)
205 }
206
207 #[inline]
208 fn checked_mul_from<U>(self, rhs: U) -> Option<Self>
209 where
210 Self: Sized + TryFrom<U>,
211 {
212 sealed::Int::checked_mul_from(self, rhs)
213 }
214
215 #[inline]
216 fn checked_div_from<U>(self, rhs: U) -> Option<Self>
217 where
218 Self: Sized + TryFrom<U>,
219 {
220 sealed::Int::checked_div_from(self, rhs)
221 }
222
223 #[inline]
224 fn checked_rem_from<U>(self, rhs: U) -> Option<Self>
225 where
226 Self: Sized + TryFrom<U>,
227 {
228 sealed::Int::checked_rem_from(self, rhs)
229 }
230
231 // ============================================================
232 // Saturating same-type arithmetic
233 // ============================================================
234
235 #[inline]
236 fn saturating_add(self, rhs: Self) -> Self {
237 sealed::Int::saturating_add(self, rhs)
238 }
239
240 #[inline]
241 fn saturating_sub(self, rhs: Self) -> Self {
242 sealed::Int::saturating_sub(self, rhs)
243 }
244
245 #[inline]
246 fn saturating_mul(self, rhs: Self) -> Self {
247 sealed::Int::saturating_mul(self, rhs)
248 }
249
250 // ============================================================
251 // Saturating converted arithmetic
252 // ============================================================
253
254 #[inline]
255 fn saturating_add_from<U>(self, rhs: U) -> Option<Self>
256 where
257 Self: Sized + TryFrom<U>,
258 {
259 sealed::Int::saturating_add_from(self, rhs)
260 }
261
262 #[inline]
263 fn saturating_sub_from<U>(self, rhs: U) -> Option<Self>
264 where
265 Self: Sized + TryFrom<U>,
266 {
267 sealed::Int::saturating_sub_from(self, rhs)
268 }
269
270 #[inline]
271 fn saturating_mul_from<U>(self, rhs: U) -> Option<Self>
272 where
273 Self: Sized + TryFrom<U>,
274 {
275 sealed::Int::saturating_mul_from(self, rhs)
276 }
277
278 // ============================================================
279 // Unit stepping
280 // ============================================================
281
282 #[inline]
283 fn checked_next(self) -> Option<Self> {
284 sealed::Int::checked_next(self)
285 }
286
287 #[inline]
288 fn checked_prev(self) -> Option<Self> {
289 sealed::Int::checked_prev(self)
290 }
291
292 #[inline]
293 fn saturating_next(self) -> Self {
294 sealed::Int::saturating_next(self)
295 }
296
297 #[inline]
298 fn saturating_prev(self) -> Self {
299 sealed::Int::saturating_prev(self)
300 }
301
302 // ============================================================
303 // Parsing
304 // ============================================================
305
306 #[inline]
307 fn parse_decimal(src: &str) -> Result<Self, ParseIntError> {
308 sealed::Int::parse_decimal(src)
309 }
310}
311
312/// Built-in unsigned integer type used for exact interval measures.
313pub trait UnsignedPrimitive: IntPrimitive + sealed::Unsigned {}
314
315impl<T> UnsignedPrimitive for T where T: IntPrimitive + sealed::Unsigned {}
316
317/// Primitive types associated with a closed-open integer interval.
318pub trait COPrimitive {
319 type CoordType: IntPrimitive;
320 type MeasureType: UnsignedPrimitive;
321}
322
323/// Construction capability for a valid closed-open interval.
324///
325/// Implementations must preserve the invariant:
326///
327/// ```text
328/// start < end_excl
329/// ```
330pub trait COConstruct: COPrimitive + Sized {
331 /// Constructs `[start, end_excl)`, returning `None` for an empty or
332 /// reversed interval.
333 fn try_new(start: Self::CoordType, end_excl: Self::CoordType) -> Option<Self>;
334
335 /// Constructs `[start, end_excl)` without checking the interval invariant.
336 ///
337 /// # Safety
338 ///
339 /// The caller must guarantee that:
340 ///
341 /// ```text
342 /// start < end_excl
343 /// ```
344 unsafe fn new_unchecked(start: Self::CoordType, end_excl: Self::CoordType) -> Self;
345}
346
347/// Construction capability based on a midpoint and an exact interval measure.
348///
349/// `len` is represented by the interval's exact unsigned measure type.
350pub trait COMidpointConstruct: COConstruct {
351 /// Constructs an interval centered around `mid` with exact length `len`.
352 ///
353 /// Returns `None` when `len` is zero or when the resulting bounds cannot
354 /// be represented by `CoordType`.
355 fn checked_from_midpoint_len(mid: Self::CoordType, len: Self::MeasureType) -> Option<Self>;
356
357 /// Constructs an interval centered around `mid` with saturating endpoint
358 /// arithmetic.
359 ///
360 /// Returns `None` when `len` is zero or saturation collapses the result
361 /// into an empty interval.
362 fn saturating_from_midpoint_len(mid: Self::CoordType, len: Self::MeasureType) -> Option<Self>;
363}
364
365/// Construction capability based on a start bound and an exact interval measure.
366///
367/// `len` is represented by the interval's exact unsigned measure type.
368pub trait COStartLenConstruct: COConstruct {
369 /// Constructs an interval starting at `start` with exact length `len`.
370 ///
371 /// The resulting interval is:
372 ///
373 /// ```text
374 /// [start, start + len)
375 /// ```
376 ///
377 /// Returns `None` when `len` is zero or when the resulting exclusive end
378 /// bound cannot be represented by `CoordType`.
379 fn checked_from_start_len(start: Self::CoordType, len: Self::MeasureType) -> Option<Self>;
380
381 /// Constructs an interval starting at `start` with saturating endpoint
382 /// arithmetic.
383 ///
384 /// The resulting interval starts at `start`, while the exclusive end bound
385 /// is clamped to the maximum representable coordinate when needed.
386 ///
387 /// Returns `None` when `len` is zero or saturation collapses the result
388 /// into an empty interval.
389 fn saturating_from_start_len(start: Self::CoordType, len: Self::MeasureType) -> Option<Self>;
390}
391
392/// Boundary access capability for a closed-open interval.
393pub trait COBounds: COPrimitive + Copy + Ord + Eq + core::fmt::Debug {
394 /// Returns the inclusive lower bound.
395 fn start(self) -> Self::CoordType;
396
397 /// Returns the exclusive upper bound.
398 fn end_excl(self) -> Self::CoordType;
399
400 /// Returns the inclusive upper bound.
401 ///
402 /// This is the greatest coordinate contained in the interval.
403 fn end_incl(self) -> Self::CoordType;
404}
405
406/// Containment and overlap predicates for closed-open intervals.
407pub trait COPredicates: COBounds {
408 /// Returns whether `x` is contained in this interval.
409 fn contains(self, x: Self::CoordType) -> bool;
410
411 /// Returns whether `other` is fully contained in this interval.
412 fn contains_interval(self, other: Self) -> bool;
413
414 /// Returns whether this interval and `other` overlap with positive length.
415 fn intersects(self, other: Self) -> bool;
416
417 /// Returns whether this interval and `other` touch at exactly one boundary
418 /// without overlapping.
419 fn is_adjacent(self, other: Self) -> bool;
420
421 /// Returns whether this interval and `other` overlap or are adjacent.
422 fn is_contiguous_with(self, other: Self) -> bool;
423}
424
425/// Range projection capability for a closed-open interval.
426///
427/// The returned range has the same half-open semantics as the interval:
428///
429/// ```text
430/// [start, end_excl) -> start..end_excl
431/// ```
432pub trait CORange: COBounds + Sized {
433 /// Returns the standard-library half-open range represented by this
434 /// interval.
435 fn to_range(self) -> core::ops::Range<Self::CoordType>;
436
437 /// Returns the standard-library range used to iterate covered coordinates.
438 ///
439 /// This is equivalent to `self.to_range()`.
440 #[inline]
441 fn iter(self) -> core::ops::Range<Self::CoordType> {
442 self.to_range()
443 }
444}
445
446/// Algebraic operations for closed-open intervals.
447pub trait COAlgebra: COConstruct + COBounds + COPredicates {
448 /// Returns the overlapping region of two intervals, if any.
449 fn intersection(self, other: Self) -> Option<Self>;
450
451 /// Returns the smallest interval containing both intervals.
452 fn convex_hull(self, other: Self) -> Self;
453
454 /// Returns the interval strictly between two separated intervals.
455 ///
456 /// Returns `None` when the intervals overlap or are adjacent.
457 fn between(self, other: Self) -> Option<Self>;
458
459 /// Returns the union of two intervals.
460 ///
461 /// Contiguous intervals are merged into one interval; otherwise the two
462 /// intervals are returned in ascending order.
463 fn union(self, other: Self) -> OneTwo<Self>;
464
465 /// Returns `self \ other`.
466 ///
467 /// The result may contain zero, one, or two residual intervals.
468 fn difference(self, other: Self) -> ZeroOneTwo<Self>;
469
470 /// Returns the symmetric difference of two intervals.
471 ///
472 /// The result contains points covered by exactly one operand and may
473 /// contain zero, one, or two intervals.
474 fn symmetric_difference(self, other: Self) -> ZeroOneTwo<Self>;
475}
476
477/// Exact measure capability for a closed-open interval.
478pub trait COMeasure: COPrimitive {
479 /// Returns the exact interval length.
480 fn len(self) -> Self::MeasureType;
481}
482
483/// Representative-position capability for a closed-open interval.
484pub trait COMidpoint: COPrimitive {
485 /// Returns the midpoint coordinate, using floor rounding where required.
486 fn midpoint(self) -> Self::CoordType;
487}
488
489/// Exact checked Minkowski operations whose images remain closed-open
490/// integer intervals.
491pub trait COCheckedMinkowskiLinear: COPrimitive + Sized {
492 /// Returns the exact Minkowski sum `self + other`.
493 fn checked_minkowski_add(self, other: Self) -> Option<Self>;
494
495 /// Returns the exact Minkowski subtraction `self - other`.
496 fn checked_minkowski_sub(self, other: Self) -> Option<Self>;
497
498 /// Returns the exact translation `self + scalar`.
499 fn checked_minkowski_add_scalar(self, scalar: Self::CoordType) -> Option<Self>;
500
501 /// Returns the exact translation `self - scalar`.
502 fn checked_minkowski_sub_scalar(self, scalar: Self::CoordType) -> Option<Self>;
503}
504
505/// Checked interval hulls of non-linear Minkowski images.
506///
507/// For discrete integer intervals, multiplication and division may produce
508/// non-contiguous point sets. These methods return a containing interval hull,
509/// not necessarily an exact image.
510pub trait COCheckedMinkowskiHull: COPrimitive + Sized {
511 /// Returns the interval hull containing every point in `self * other`.
512 fn checked_minkowski_mul_hull(self, other: Self) -> Option<Self>;
513
514 /// Returns the interval hull containing every point in `self / other`.
515 fn checked_minkowski_div_hull(self, other: Self) -> Option<Self>;
516
517 /// Returns the interval hull containing every point in `self * scalar`.
518 fn checked_minkowski_mul_scalar_hull(self, scalar: Self::CoordType) -> Option<Self>;
519
520 /// Returns the interval hull containing every point in `self / scalar`.
521 fn checked_minkowski_div_scalar_hull(self, scalar: Self::CoordType) -> Option<Self>;
522}
523
524/// Saturating Minkowski operations whose results remain closed-open integer
525/// intervals after endpoint arithmetic is clamped to the representable domain.
526///
527/// These methods apply saturating arithmetic to the interval bounds rather
528/// than returning an error on overflow or underflow.
529///
530/// When saturation clips a bound, the returned interval is the representable
531/// saturated result, not necessarily the exact unconstrained mathematical
532/// image.
533///
534/// Returns `None` when saturation collapses the resulting interval into an
535/// empty or otherwise invalid closed-open interval.
536pub trait COSaturatingMinkowskiLinear: COPrimitive + Sized {
537 /// Returns the saturated Minkowski sum `self + other`.
538 ///
539 /// Both result bounds are computed with saturating addition.
540 fn saturating_minkowski_add(self, other: Self) -> Option<Self>;
541
542 /// Returns the saturated Minkowski subtraction `self - other`.
543 ///
544 /// Both result bounds are computed with saturating subtraction.
545 fn saturating_minkowski_sub(self, other: Self) -> Option<Self>;
546
547 /// Returns the saturated translation `self + scalar`.
548 ///
549 /// Both interval bounds are shifted with saturating addition.
550 fn saturating_minkowski_add_scalar(self, scalar: Self::CoordType) -> Option<Self>;
551
552 /// Returns the saturated translation `self - scalar`.
553 ///
554 /// Both interval bounds are shifted with saturating subtraction.
555 fn saturating_minkowski_sub_scalar(self, scalar: Self::CoordType) -> Option<Self>;
556}
557
558/// Saturating interval hulls of non-linear Minkowski images.
559///
560/// For discrete integer intervals, multiplication and division may produce
561/// non-contiguous point sets. These methods first compute a containing
562/// interval hull and apply saturating endpoint arithmetic where needed.
563///
564/// When saturation clips a bound, the returned interval is a representable
565/// saturated hull rather than the exact unconstrained mathematical image.
566///
567/// Returns `None` when the operation is undefined, such as division by zero,
568/// or when saturation collapses the resulting hull into an empty or otherwise
569/// invalid closed-open interval.
570pub trait COSaturatingMinkowskiHull: COPrimitive + Sized {
571 /// Returns the saturated interval hull of `self * other`.
572 ///
573 /// Endpoint products are computed with saturating multiplication.
574 fn saturating_minkowski_mul_hull(self, other: Self) -> Option<Self>;
575
576 /// Returns the saturated interval hull of `self / other`.
577 ///
578 /// Returns `None` when the divisor interval contains zero in a position
579 /// that makes the interval division undefined.
580 fn saturating_minkowski_div_hull(self, other: Self) -> Option<Self>;
581
582 /// Returns the saturated interval hull of `self * scalar`.
583 ///
584 /// Endpoint products are computed with saturating multiplication.
585 fn saturating_minkowski_mul_scalar_hull(self, scalar: Self::CoordType) -> Option<Self>;
586
587 /// Returns the saturated interval hull of `self / scalar`.
588 ///
589 /// Returns `None` when `scalar` is zero.
590 fn saturating_minkowski_div_scalar_hull(self, scalar: Self::CoordType) -> Option<Self>;
591}
592
593/// Complete closed-open integer interval capability required by interval sets.
594pub trait IntCO: COConstruct + COBounds + COPredicates + COAlgebra + COMeasure {}
595
596impl<T> IntCO for T where T: COConstruct + COBounds + COPredicates + COAlgebra + COMeasure {}
597
598#[cfg(test)]
599mod tests_for_int_primitive;