Skip to main content

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;