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_value()
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(rhs.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
113impl<T: PartialOrd> PartialEq<T> for LowerBound<T> {
114 fn eq(&self, other: &T) -> bool {
115 match &self.0 {
116 Exact(lhs) => lhs == other,
117 _ => false,
118 }
119 }
120}
121
122impl<T: PartialOrd> PartialOrd<T> for LowerBound<T> {
124 fn partial_cmp(&self, other: &T) -> Option<Ordering> {
125 match &self.0 {
126 Exact(lhs) => lhs.partial_cmp(other),
127 Inexact(lhs) => lhs
128 .partial_cmp(other)
129 .and_then(|o| if o == Ordering::Less { None } else { Some(o) }),
130 }
131 }
132}
133
134#[derive(Debug, Clone, PartialEq, Eq)]
136pub struct UpperBound<T>(pub(crate) Precision<T>);
137
138impl<T> UpperBound<T> {
139 pub(crate) fn max_value(self) -> T {
140 self.0.into_value()
141 }
142}
143
144impl<T> UpperBound<T> {
145 pub fn into_value(self) -> Precision<T> {
146 self.0
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
214impl<T: PartialOrd> PartialEq<T> for UpperBound<T> {
215 fn eq(&self, other: &T) -> bool {
216 match &self.0 {
217 Exact(lhs) => lhs == other,
218 _ => false,
219 }
220 }
221}
222
223impl<T: PartialOrd> PartialOrd<T> for UpperBound<T> {
225 fn partial_cmp(&self, other: &T) -> Option<Ordering> {
226 match &self.0 {
227 Exact(lhs) => lhs.partial_cmp(other),
228 Inexact(lhs) => lhs.partial_cmp(other).and_then(|o| {
229 if o == Ordering::Greater {
230 None
231 } else {
232 Some(o)
233 }
234 }),
235 }
236 }
237}
238
239#[cfg(test)]
240mod tests {
241 use crate::stats::bound::IntersectionResult;
242 use crate::stats::{Precision, StatBound, UpperBound};
243
244 #[test]
245 fn test_upper_bound_cmp() {
246 let ub = UpperBound(Precision::exact(10i32));
247
248 assert_eq!(ub, 10);
249 assert!(ub > 9);
250 assert!(ub <= 10);
251 assert!(ub <= 10);
252
253 let ub = UpperBound(Precision::inexact(10i32));
254
255 assert_ne!(ub, 10);
256 assert!(ub < 11);
257 assert!(!(ub >= 9));
259 }
260
261 #[test]
262 fn test_upper_bound_union() {
263 let ub1: UpperBound<i32> = UpperBound(Precision::exact(10i32));
264 let ub2 = UpperBound(Precision::exact(12i32));
265
266 assert_eq!(Some(ub2.clone()), ub1.union(&ub2));
267
268 let ub1: UpperBound<i32> = UpperBound(Precision::inexact(10i32));
269 let ub2 = UpperBound(Precision::exact(12i32));
270
271 assert_eq!(Some(ub2.clone()), ub1.union(&ub2));
272
273 let ub1: UpperBound<i32> = UpperBound(Precision::exact(10i32));
274 let ub2 = UpperBound(Precision::inexact(12i32));
275
276 assert_eq!(Some(ub2.clone()), ub1.union(&ub2));
277
278 let ub1: UpperBound<i32> = UpperBound(Precision::inexact(10i32));
279 let ub2 = UpperBound(Precision::inexact(12i32));
280
281 assert_eq!(Some(ub2.clone()), ub1.union(&ub2))
282 }
283
284 #[test]
285 fn test_upper_bound_intersection() {
286 let ub1: UpperBound<i32> = UpperBound(Precision::exact(10i32));
287 let ub2 = UpperBound(Precision::inexact(12i32));
288
289 assert_eq!(
290 Some(IntersectionResult::Value(ub1.clone())),
291 ub1.intersection(&ub2)
292 );
293
294 let ub1: UpperBound<i32> = UpperBound(Precision::exact(13i32));
295 let ub2 = UpperBound(Precision::inexact(12i32));
296
297 assert_eq!(Some(IntersectionResult::None), ub1.intersection(&ub2));
298 }
299}