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