vortex_array/stats/
bound.rs1use std::cmp::Ordering;
5
6use vortex_error::{VortexError, VortexResult};
7
8use crate::partial_ord::{partial_max, partial_min};
9use crate::stats::Precision::{Exact, Inexact};
10use crate::stats::{Precision, StatBound};
11
12#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct LowerBound<T>(pub(crate) Precision<T>);
16
17impl<T> LowerBound<T> {
18 pub(crate) fn min_value(self) -> T {
19 self.0.into_inner()
20 }
21}
22
23impl<T> LowerBound<T> {
24 pub fn is_exact(&self) -> bool {
25 self.0.is_exact()
26 }
27}
28
29#[derive(Debug, Clone, PartialEq, Eq)]
32pub enum IntersectionResult<T> {
33 Value(T),
35 None,
37}
38
39impl<T> IntersectionResult<T> {
40 pub fn ok_or_else<F>(self, err: F) -> VortexResult<T>
41 where
42 F: FnOnce() -> VortexError,
43 {
44 match self {
45 IntersectionResult::Value(v) => Ok(v),
46 IntersectionResult::None => Err(err()),
47 }
48 }
49}
50
51impl<T: PartialOrd + Clone> StatBound<T> for LowerBound<T> {
52 fn lift(value: Precision<T>) -> Self {
53 Self(value)
54 }
55
56 fn union(&self, other: &Self) -> Option<LowerBound<T>> {
57 Some(LowerBound(match (&self.0, &other.0) {
58 (Exact(lhs), Exact(rhs)) => Exact(partial_min(lhs, rhs)?.clone()),
59 (Inexact(lhs), Inexact(rhs)) => Inexact(partial_min(lhs, rhs)?.clone()),
60 (Inexact(lhs), Exact(rhs)) => {
61 if rhs <= lhs {
62 Exact(rhs.clone())
63 } else {
64 Inexact(lhs.clone())
65 }
66 }
67 (Exact(lhs), Inexact(rhs)) => {
68 if rhs >= lhs {
69 Exact(lhs.clone())
70 } else {
71 Inexact(rhs.clone())
72 }
73 }
74 }))
75 }
76
77 fn intersection(&self, other: &Self) -> Option<IntersectionResult<LowerBound<T>>> {
79 Some(match (&self.0, &other.0) {
80 (Exact(lhs), Exact(rhs)) => {
81 if lhs == rhs {
82 IntersectionResult::Value(LowerBound(Exact(lhs.clone())))
83 } else {
84 IntersectionResult::None
86 }
87 }
88 (Inexact(lhs), Inexact(rhs)) => {
89 IntersectionResult::Value(LowerBound(Inexact(partial_max(lhs, rhs)?.clone())))
90 }
91 (Inexact(lhs), Exact(rhs)) => {
92 if rhs >= lhs {
93 IntersectionResult::Value(LowerBound(Exact(rhs.clone())))
94 } else {
95 IntersectionResult::None
97 }
98 }
99 (Exact(lhs), Inexact(rhs)) => {
100 if rhs <= lhs {
101 IntersectionResult::Value(LowerBound(Exact(lhs.clone())))
102 } else {
103 IntersectionResult::None
105 }
106 }
107 })
108 }
109
110 fn to_exact(&self) -> Option<&T> {
111 self.0.to_exact()
112 }
113
114 fn into_value(self) -> Precision<T> {
115 self.0
116 }
117}
118
119impl<T: PartialOrd> PartialEq<T> for LowerBound<T> {
120 fn eq(&self, other: &T) -> bool {
121 match &self.0 {
122 Exact(lhs) => lhs == other,
123 _ => false,
124 }
125 }
126}
127
128impl<T: PartialOrd> PartialOrd<T> for LowerBound<T> {
130 fn partial_cmp(&self, other: &T) -> Option<Ordering> {
131 match &self.0 {
132 Exact(lhs) => lhs.partial_cmp(other),
133 Inexact(lhs) => lhs
134 .partial_cmp(other)
135 .and_then(|o| if o == Ordering::Less { None } else { Some(o) }),
136 }
137 }
138}
139
140#[derive(Debug, Clone, PartialEq, Eq)]
142pub struct UpperBound<T>(pub(crate) Precision<T>);
143
144impl<T> UpperBound<T> {
145 pub(crate) fn max_value(self) -> T {
146 self.0.into_inner()
147 }
148}
149
150impl<T: PartialOrd + Clone> StatBound<T> for UpperBound<T> {
151 fn lift(value: Precision<T>) -> Self {
152 Self(value)
153 }
154
155 fn union(&self, other: &Self) -> Option<UpperBound<T>> {
157 Some(UpperBound(match (&self.0, &other.0) {
158 (Exact(lhs), Exact(rhs)) => Exact(partial_max(lhs, rhs)?.clone()),
159 (Inexact(lhs), Inexact(rhs)) => Inexact(partial_max(lhs, rhs)?.clone()),
160 (Inexact(lhs), Exact(rhs)) => {
161 if rhs >= lhs {
162 Exact(rhs.clone())
163 } else {
164 Inexact(lhs.clone())
165 }
166 }
167 (Exact(lhs), Inexact(rhs)) => {
168 if rhs <= lhs {
169 Exact(lhs.clone())
170 } else {
171 Inexact(rhs.clone())
172 }
173 }
174 }))
175 }
176
177 fn intersection(&self, other: &Self) -> Option<IntersectionResult<UpperBound<T>>> {
178 Some(match (&self.0, &other.0) {
179 (Exact(lhs), Exact(rhs)) => {
180 if lhs == rhs {
181 IntersectionResult::Value(UpperBound(Exact(lhs.clone())))
182 } else {
183 IntersectionResult::None
185 }
186 }
187 (Inexact(lhs), Inexact(rhs)) => {
188 IntersectionResult::Value(UpperBound(Inexact(partial_min(lhs, rhs)?.clone())))
189 }
190 (Inexact(lhs), Exact(rhs)) => {
191 if rhs <= lhs {
192 IntersectionResult::Value(UpperBound(Exact(rhs.clone())))
193 } else {
194 IntersectionResult::None
196 }
197 }
198 (Exact(lhs), Inexact(rhs)) => {
199 if rhs >= lhs {
200 IntersectionResult::Value(UpperBound(Exact(lhs.clone())))
201 } else {
202 IntersectionResult::None
204 }
205 }
206 })
207 }
208
209 fn to_exact(&self) -> Option<&T> {
210 self.0.to_exact()
211 }
212
213 fn into_value(self) -> Precision<T> {
214 self.0
215 }
216}
217
218impl<T: PartialOrd> PartialEq<T> for UpperBound<T> {
219 fn eq(&self, other: &T) -> bool {
220 match &self.0 {
221 Exact(lhs) => lhs == other,
222 _ => false,
223 }
224 }
225}
226
227impl<T: PartialOrd> PartialOrd<T> for UpperBound<T> {
229 fn partial_cmp(&self, other: &T) -> Option<Ordering> {
230 match &self.0 {
231 Exact(lhs) => lhs.partial_cmp(other),
232 Inexact(lhs) => lhs.partial_cmp(other).and_then(|o| {
233 if o == Ordering::Greater {
234 None
235 } else {
236 Some(o)
237 }
238 }),
239 }
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 use crate::stats::bound::IntersectionResult;
246 use crate::stats::{LowerBound, Precision, StatBound, UpperBound};
247
248 #[test]
249 fn test_upper_bound_cmp() {
250 let ub = UpperBound(Precision::exact(10i32));
251
252 assert_eq!(ub, 10);
253 assert!(ub > 9);
254 assert!(ub <= 10);
255 assert!(ub <= 10);
256
257 let ub = UpperBound(Precision::inexact(10i32));
258
259 assert_ne!(ub, 10);
260 assert!(ub < 11);
261 assert!(!(ub >= 9));
263 }
264
265 #[test]
266 fn test_upper_bound_union() {
267 let ub1: UpperBound<i32> = UpperBound(Precision::exact(10i32));
268 let ub2 = UpperBound(Precision::exact(12i32));
269
270 assert_eq!(Some(ub2.clone()), ub1.union(&ub2));
271
272 let ub1: UpperBound<i32> = UpperBound(Precision::inexact(10i32));
273 let ub2 = UpperBound(Precision::exact(12i32));
274
275 assert_eq!(Some(ub2.clone()), ub1.union(&ub2));
276
277 let ub1: UpperBound<i32> = UpperBound(Precision::exact(10i32));
278 let ub2 = UpperBound(Precision::inexact(12i32));
279
280 assert_eq!(Some(ub2.clone()), ub1.union(&ub2));
281
282 let ub1: UpperBound<i32> = UpperBound(Precision::inexact(10i32));
283 let ub2 = UpperBound(Precision::inexact(12i32));
284
285 assert_eq!(Some(ub2.clone()), ub1.union(&ub2))
286 }
287
288 #[test]
289 fn test_upper_bound_intersection() {
290 let ub1: UpperBound<i32> = UpperBound(Precision::exact(10i32));
291 let ub2 = UpperBound(Precision::inexact(12i32));
292
293 assert_eq!(
294 Some(IntersectionResult::Value(ub1.clone())),
295 ub1.intersection(&ub2)
296 );
297
298 let ub1: UpperBound<i32> = UpperBound(Precision::exact(13i32));
299 let ub2 = UpperBound(Precision::inexact(12i32));
300
301 assert_eq!(Some(IntersectionResult::None), ub1.intersection(&ub2));
302 }
303
304 #[test]
305 fn test_lower_bound_intersection() {
306 let lb1: LowerBound<i32> = LowerBound(Precision::exact(12i32));
307 let lb2 = LowerBound(Precision::inexact(10i32));
308
309 assert_eq!(
310 Some(IntersectionResult::Value(lb1.clone())),
311 lb1.intersection(&lb2)
312 );
313
314 let lb1: LowerBound<i32> = LowerBound(Precision::exact(12i32));
315 let lb2 = LowerBound(Precision::inexact(13i32));
316
317 assert_eq!(Some(IntersectionResult::None), lb1.intersection(&lb2));
318 }
319}