inter_val/lib.rs
1//! Mathematical intervals, i.g., [a, b], (a, b), [a, b), and (a, b].
2//! Also supports multi-dimensional axis-aligned boxes.
3//!
4//! # Interval
5//! Intervals like *[a, b]*, *(a, b)*, *[a, b)*, and *(a, b]* for any `PartialOrd` type.
6//!
7//! ```
8//! use inter_val::{Inclusive, Exclusive, Interval};
9//!
10//! // Closed interval of i32
11//! let a = Inclusive.at(0).to(Inclusive.at(10)); // [0, 10]
12//! assert!(a.contains(&3));
13//!
14//! // Half-open interval of f64
15//! let b = Inclusive.at(1.23).to(Exclusive.at(4.56)); // [1.23, 4.56)
16//! assert!(!b.contains(&4.56));
17//! assert!(b.contains(&(4.56 - 0.000000000000001)));
18//!
19//! // Intersection
20//! let c = Inclusive.between(5, 15); // [5, 15]
21//! let isect = a.intersection(&c).unwrap(); // [0, 10] ∩ [5, 15] = [5, 10]
22//! assert_eq!(isect.inf(), &5);
23//! assert_eq!(isect.sup(), &10);
24//!
25//! // Span & Gap
26//! let d = Inclusive.between(12, 15); // [12, 15]
27//! let span = a.span(&d); // [0, 15]
28//! let gap = a.gap(&d); // (10, 12)
29//! assert_eq!(span, Inclusive.between(0, 15));
30//! assert_eq!(gap.unwrap(), Exclusive.between(10, 12));
31//!
32//! // Union
33//! let union = a.union(&d);
34//! assert_eq!(union.span, span);
35//! assert_eq!(union.gap, gap);
36//! assert_eq!(union.into_vec(), vec![a, d]);
37//!
38//! // Hull
39//! let hull = Interval::<_>::hull_many(vec![3, 9, 2, 5]).unwrap(); // [2, 9]
40//! assert_eq!(hull, Inclusive.between(2, 9));
41//!
42//! // Linear interpolation
43//! assert_eq!(b.lerp(0.0), *b.inf());
44//! assert_eq!(b.lerp(1.0), *b.sup());
45//! assert_eq!(b.lerp(0.2), 0.8 * *b.inf() + 0.2 * *b.sup());
46//!
47//! // Split
48//! let (lower, upper) = b.split_at(3.45); // Split [1.23, 4.56) at 3.45
49//! assert_eq!(lower.inf(), b.inf());
50//! assert_eq!(lower.sup(), &3.45);
51//! assert_eq!(upper.inf(), &3.45);
52//! assert_eq!(upper.sup(), b.sup());
53//! ```
54//!
55//! # Multi-dimensional axis-aligned box
56//! Boxes represented by Cartesian product of intervals.
57//! ```
58//! use inter_val::{Box2, Inclusive};
59//!
60//! // [0.0, 10.0] × [5.0, 20.0]
61//! let a: Box2<f64> = Box2::new(Inclusive.between(0.0, 10.0), Inclusive.between(5.0, 20.0));
62//!
63//! // Another way to construct [0.0, 10.0] × [5.0, 20.0]
64//! let b: Box2<f64> = Box2::between(&[0.0, 5.0], &[10.0, 20.0]);
65//! assert_eq!(a, b);
66//!
67//! // Hull
68//! let b = a.hull(&[12.3, 7.5]);
69//! assert_eq!(b, Box2::between(&[0.0, 5.0], &[12.3, 20.0]));
70//! ```
71mod bound;
72mod bound_type;
73mod converters;
74mod half;
75mod interval;
76mod interval_box;
77mod ndim;
78mod nullable;
79mod std_range;
80mod tests;
81mod traits;
82
83use bound_type::{Left, Right};
84use traits::BoundaryOf;
85
86pub use bound::Bound;
87pub use bound_type::{BoundType, Exclusive, Inclusive};
88pub use half::{HalfBounded, LeftBounded, RightBounded};
89pub use interval::Interval;
90pub use interval_box::BoxN;
91pub use ndim::NDim;
92pub use nullable::Nullable;
93
94impl Inclusive {
95 pub fn at<T>(self, t: T) -> Bound<T, Self> {
96 Bound {
97 limit: t,
98 bound_type: self,
99 }
100 }
101 pub fn between<T: PartialOrd>(self, a: T, b: T) -> Interval<T, Self, Self> {
102 Interval::between(a, b)
103 }
104 pub fn hull<T: PartialOrd + Clone>(
105 items: impl IntoIterator<Item = T>,
106 ) -> Option<Interval<T, Self, Self>> {
107 Interval::hull_many(items)
108 }
109}
110impl Exclusive {
111 pub fn at<T>(self, t: T) -> Bound<T, Self> {
112 Bound {
113 limit: t,
114 bound_type: self,
115 }
116 }
117 pub fn try_between<T: PartialOrd>(self, a: T, b: T) -> Option<Interval<T, Self, Self>> {
118 Interval::try_between(a, b)
119 }
120 pub fn between<T: PartialOrd>(self, a: T, b: T) -> Interval<T, Self, Self> {
121 Interval::between(a, b)
122 }
123 pub fn hull<T: PartialOrd + Clone>(
124 items: impl IntoIterator<Item = T>,
125 ) -> Option<Interval<T, Self, Self>> {
126 Interval::hull_many(items)
127 }
128}
129impl BoundType {
130 pub fn at<T>(self, t: T) -> Bound<T, Self> {
131 Bound {
132 limit: t,
133 bound_type: self,
134 }
135 }
136 pub fn try_between<T: PartialOrd>(self, a: T, b: T) -> Option<Interval<T, Self, Self>> {
137 if a <= b {
138 Interval::try_new(self.at(a), self.at(b))
139 } else {
140 Interval::try_new(self.at(b), self.at(a))
141 }
142 }
143}
144
145impl<T: PartialOrd, B: BoundaryOf<Left>> Bound<T, B> {
146 pub fn to<R: BoundaryOf<Right>>(self, r: Bound<T, R>) -> Interval<T, B, R> {
147 Interval::new(self, r)
148 }
149 pub fn try_to<R: BoundaryOf<Right>>(self, r: Bound<T, R>) -> Option<Interval<T, B, R>> {
150 Interval::try_new(self, r)
151 }
152}
153
154#[derive(Debug, thiserror::Error)]
155#[error("left boundary must be less than or equal to right boundary")]
156pub struct IntervalIsEmpty;
157
158pub type OpenInterval<T> = Interval<T, Exclusive>;
159pub type GeneralInterval<T> = Interval<T, BoundType>;
160pub type Box2<T, L = Inclusive, R = L> = BoxN<2, T, L, R>;
161pub type Box3<T, L = Inclusive, R = L> = BoxN<3, T, L, R>;
162pub type Box4<T, L = Inclusive, R = L> = BoxN<4, T, L, R>;