spaces/
interval.rs

1use crate::prelude::*;
2use num_traits::{Zero, One};
3use std::{
4    cmp,
5    fmt,
6    f64::{INFINITY, NEG_INFINITY},
7};
8
9fn both<T>(opta: Option<T>, optb: Option<T>) -> Option<(T, T)> {
10    match (opta, optb) {
11        (Some(a), Some(b)) => Some((a, b)),
12        _ => None,
13    }
14}
15
16/// Generalisation of a interval.
17#[derive(Eq, Clone, Copy)]
18#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
19pub struct Interval<T = f64> {
20    pub(crate) lb: Option<T>,
21    pub(crate) ub: Option<T>,
22}
23
24impl<T> Interval<T> {
25    pub fn new(lb: Option<T>, ub: Option<T>) -> Interval<T> {
26        Interval {
27            lb, ub,
28        }
29    }
30
31    pub fn unbounded() -> Interval<T> {
32        Interval::new(None, None)
33    }
34
35    pub fn bounded(lb: T, ub: T) -> Interval<T> {
36        Interval::new(Some(lb), Some(ub))
37    }
38
39    pub fn lower_bounded(lb: T) -> Interval<T> {
40        Interval::new(Some(lb), None)
41    }
42
43    pub fn upper_bounded(ub: T) -> Interval<T> {
44        Interval::new(None, Some(ub))
45    }
46
47    pub fn unit() -> Interval<T> where T: Zero + One {
48        Interval::bounded(T::zero(), T::one())
49    }
50}
51
52impl Space for Interval<f64> {
53    const DIM: usize = 1;
54
55    type Value = f64;
56
57    fn card(&self) -> Card { Card::Infinite }
58
59    fn contains(&self, val: &f64) -> bool {
60        self.lb.map_or(true, |l| *val >= l) && self.ub.map_or(true, |u| *val <= u)
61    }
62}
63
64impl OrderedSpace for Interval<f64> {
65    fn min(&self) -> Option<f64> { self.lb }
66
67    fn max(&self) -> Option<f64> { self.ub }
68}
69
70impl Space for Interval<i64> {
71    const DIM: usize = 1;
72
73    type Value = i64;
74
75    fn card(&self) -> Card {
76        match (self.lb, self.ub) {
77            (Some(lb), Some(ub)) => Card::Finite((ub - lb + 1) as usize),
78            _ => Card::Infinite,
79        }
80    }
81
82    fn contains(&self, val: &i64) -> bool {
83        self.lb.map_or(true, |l| *val >= l) && self.ub.map_or(true, |u| *val <= u)
84    }
85}
86
87impl OrderedSpace for Interval<i64> {
88    fn min(&self) -> Option<i64> { self.lb }
89
90    fn max(&self) -> Option<i64> { self.ub }
91}
92
93impl<T: Clone + cmp::PartialOrd> Union for Interval<T> {
94    fn union(self, other: &Self) -> Self {
95        Interval::new(
96            both(self.lb, other.lb.clone()).map(|(a, b)| {
97                if a < b { a } else { b }
98            }),
99            both(self.ub, other.ub.clone()).map(|(a, b)| {
100                if a > b { a } else { b }
101            }),
102        )
103    }
104}
105
106impl<T: Clone + cmp::PartialOrd> Intersect for Interval<T> {
107    fn intersect(self, other: &Self) -> Self {
108        Interval::new(
109            both(self.lb, other.lb.clone()).map(|(a, b)| {
110                if a > b { a } else { b }
111            }),
112            both(self.ub, other.ub.clone()).map(|(a, b)| {
113                if a < b { a } else { b }
114            }),
115        )
116    }
117}
118
119impl<T: cmp::PartialEq> cmp::PartialEq for Interval<T> {
120    fn eq(&self, other: &Interval<T>) -> bool { self.lb.eq(&other.lb) && self.ub.eq(&other.ub) }
121}
122
123impl<T: fmt::Debug> fmt::Debug for Interval<T> {
124    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
125        f.debug_struct("Interval")
126            .field("lb", &self.lb)
127            .field("ub", &self.ub)
128            .finish()
129    }
130}
131
132impl<T: fmt::Display> fmt::Display for Interval<T> {
133    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
134        match (&self.lb, &self.ub) {
135            (Some(lb), Some(ub)) => write!(f, "[{}, {}]", lb, ub),
136            (Some(lb), None) => write!(f, "[{}, {}]", lb, INFINITY),
137            (None, Some(ub)) => write!(f, "[{}, {}]", NEG_INFINITY, ub),
138            (None, None) => write!(f, "[{}, {}]", NEG_INFINITY, INFINITY),
139        }
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146
147    #[cfg(feature = "serialize")]
148    extern crate serde_test;
149    #[cfg(feature = "serialize")]
150    use self::serde_test::{assert_tokens, Token};
151
152    #[test]
153    fn test_card() {
154        assert_eq!(Interval::bounded(0.0f64, 5.0f64).card(), Card::Infinite);
155        assert_eq!(Interval::bounded(-5.0f64, 5.0f64).card(), Card::Infinite);
156        assert_eq!(Interval::bounded(-5.0f64, 0.0f64).card(), Card::Infinite);
157
158        assert_eq!(Interval::bounded(0i64, 5i64).card(), Card::Finite(6));
159        assert_eq!(Interval::bounded(-5i64, 5i64).card(), Card::Finite(11));
160        assert_eq!(Interval::bounded(-5i64, 0i64).card(), Card::Finite(6));
161
162        assert_eq!(Interval::<i64>::unbounded().card(), Card::Infinite);
163        assert_eq!(Interval::lower_bounded(0i64).card(), Card::Infinite);
164        assert_eq!(Interval::upper_bounded(0i64).card(), Card::Infinite);
165    }
166
167    #[test]
168    fn test_bounds_f64() {
169        fn check(lb: f64, ub: f64) {
170            let d = Interval::bounded(lb, ub);
171
172            assert_eq!(d.inf().unwrap(), lb);
173            assert_eq!(d.sup().unwrap(), ub);
174
175            assert!(d.contains(&ub));
176            assert!(d.contains(&lb));
177            assert!(d.contains(&((lb + ub) / 2.0)));
178        }
179
180        check(0.0, 5.0);
181        check(-5.0, 5.0);
182        check(-5.0, 0.0);
183    }
184
185    #[test]
186    fn test_bounds_i64() {
187        fn check(lb: i64, ub: i64) {
188            let d = Interval::bounded(lb, ub);
189
190            assert_eq!(d.inf().unwrap(), lb);
191            assert_eq!(d.sup().unwrap(), ub);
192
193            assert!(d.contains(&ub));
194            assert!(d.contains(&lb));
195            assert!(d.contains(&((lb + ub) / 2)));
196        }
197
198        check(0, 5);
199        check(-5, 5);
200        check(-5, 0);
201    }
202
203    #[cfg(feature = "serialize")]
204    #[test]
205    fn test_serialisation() {
206        fn check(lb: f64, ub: f64) {
207            let d = Interval::bounded(lb, ub);
208
209            assert_tokens(
210                &d,
211                &[
212                    Token::Struct {
213                        name: "Interval",
214                        len: 2,
215                    },
216                    Token::Str("lb"),
217                    Token::Some,
218                    Token::F64(lb),
219                    Token::Str("ub"),
220                    Token::Some,
221                    Token::F64(ub),
222                    Token::StructEnd,
223                ],
224            );
225        }
226
227        check(0.0, 5.0);
228        check(-5.0, 5.0);
229        check(-5.0, 0.0);
230    }
231}