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#[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}