1use std::fmt::Debug;
5use std::fmt::Display;
6use std::fmt::Formatter;
7
8use vortex_error::VortexExpect;
9
10use crate::dtype::DType;
11use crate::expr::stats::IntersectionResult;
12use crate::expr::stats::StatBound;
13use crate::expr::stats::StatType;
14use crate::partial_ord::partial_min;
15use crate::scalar::Scalar;
16use crate::scalar::ScalarValue;
17
18#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
26pub enum Precision<T> {
27 Exact(T),
28 Inexact(T),
29 #[default]
30 Absent,
31}
32
33impl<T, E> Precision<Result<T, E>> {
34 pub fn transpose(self) -> Result<Precision<T>, E> {
36 match self {
37 Self::Exact(value) => value.map(Precision::Exact),
38 Self::Inexact(value) => value.map(Precision::Inexact),
39 Self::Absent => Ok(Precision::Absent),
40 }
41 }
42}
43
44impl<T> Precision<T>
45where
46 T: Copy,
47{
48 pub fn to_inexact(&self) -> Self {
49 use Precision::*;
50
51 match self {
52 Exact(v) | Inexact(v) => Inexact(*v),
53 Absent => Absent,
54 }
55 }
56}
57
58impl<T> Precision<T> {
59 pub fn exact<S: Into<T>>(s: S) -> Precision<T> {
61 Self::Exact(s.into())
62 }
63
64 pub fn inexact<S: Into<T>>(s: S) -> Precision<T> {
66 Self::Inexact(s.into())
67 }
68
69 pub fn as_ref(&self) -> Precision<&T> {
71 use Precision::*;
72
73 match self {
74 Exact(val) => Exact(val),
75 Inexact(val) => Inexact(val),
76 Absent => Absent,
77 }
78 }
79
80 pub fn into_inexact(self) -> Self {
82 use Precision::*;
83
84 match self {
85 Exact(v) | Inexact(v) => Inexact(v),
86 Absent => Absent,
87 }
88 }
89
90 pub fn as_exact(self) -> Option<T> {
92 match self {
93 Self::Exact(val) => Some(val),
94 _ => None,
95 }
96 }
97
98 pub fn as_inexact(self) -> Option<T> {
100 match self {
101 Self::Inexact(val) => Some(val),
102 _ => None,
103 }
104 }
105
106 pub fn is_exact(&self) -> bool {
108 matches!(self, Self::Exact(_))
109 }
110
111 pub fn is_absent(&self) -> bool {
113 matches!(self, Self::Absent)
114 }
115
116 pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Precision<U> {
118 use Precision::*;
119
120 match self {
121 Exact(value) => Exact(f(value)),
122 Inexact(value) => Inexact(f(value)),
123 Absent => Absent,
124 }
125 }
126
127 pub fn and_then<U, F: FnOnce(T) -> Option<U>>(self, f: F) -> Precision<U> {
128 use Precision::*;
129
130 match self {
131 Exact(value) => match f(value) {
132 Some(v) => Exact(v),
133 None => Absent,
134 },
135 Inexact(value) => match f(value) {
136 Some(v) => Inexact(v),
137 None => Absent,
138 },
139 Absent => Absent,
140 }
141 }
142
143 pub fn zip<U>(self, other: Precision<U>) -> Precision<(T, U)> {
145 use Precision::*;
146
147 match (self, other) {
148 (Exact(lhs), Exact(rhs)) => Exact((lhs, rhs)),
149 (Inexact(lhs), Exact(rhs))
150 | (Exact(lhs), Inexact(rhs))
151 | (Inexact(lhs), Inexact(rhs)) => Inexact((lhs, rhs)),
152 (Absent, _) | (_, Absent) => Absent,
153 }
154 }
155
156 pub fn into_inner(self) -> Option<T> {
158 use Precision::*;
159
160 match self {
161 Exact(val) | Inexact(val) => Some(val),
162 Absent => None,
163 }
164 }
165}
166
167impl<T: Display> Display for Precision<T> {
168 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
169 use Precision::*;
170
171 match self {
172 Exact(v) => {
173 write!(f, "{v}")
174 }
175 Inexact(v) => {
176 write!(f, "~{v}")
177 }
178 Absent => {
179 write!(f, "{{empty}}")
180 }
181 }
182 }
183}
184
185impl<T: PartialEq> PartialEq<T> for Precision<T> {
186 fn eq(&self, other: &T) -> bool {
187 match self {
188 Self::Exact(v) => v == other,
189 _ => false,
190 }
191 }
192}
193
194impl Precision<ScalarValue> {
195 pub fn into_scalar(self, dtype: DType) -> Precision<Scalar> {
198 self.map(|v| {
199 Scalar::try_new(dtype, Some(v)).vortex_expect("`Precision<ScalarValue>` was invalid")
200 })
201 }
202}
203
204impl Precision<&ScalarValue> {
205 pub fn into_scalar(self, dtype: DType) -> Precision<Scalar> {
208 self.map(|v| {
209 Scalar::try_new(dtype, Some(v.clone()))
210 .vortex_expect("`Precision<ScalarValue>` was invalid")
211 })
212 }
213}
214
215impl<T> Precision<T> {
217 pub fn bound<S: StatType<T>>(self) -> Option<S::Bound> {
219 if self.is_absent() {
220 None
221 } else {
222 Some(S::Bound::lift(self))
223 }
224 }
225}
226
227impl<T: PartialOrd + Clone> StatBound<T> for Precision<T> {
228 fn lift(value: Precision<T>) -> Self {
229 value
230 }
231
232 fn into_value(self) -> Precision<T> {
233 self
234 }
235
236 fn union(&self, other: &Self) -> Option<Self> {
237 match self
238 .clone()
239 .zip(other.clone())
240 .map(|(lhs, rhs)| partial_min(&lhs, &rhs).cloned())
241 {
242 Precision::Exact(v) => Some(Precision::Exact(v?)),
243 Precision::Inexact(v) => Some(Precision::Inexact(v?)),
244 Precision::Absent => None,
245 }
246 }
247
248 fn intersection(&self, other: &Self) -> Option<IntersectionResult<Self>> {
249 Some(match (self, other) {
250 (Precision::Exact(lhs), Precision::Exact(rhs)) => {
251 if lhs.partial_cmp(rhs)?.is_eq() {
252 IntersectionResult::Value(Precision::Exact(lhs.clone()))
253 } else {
254 IntersectionResult::Empty
255 }
256 }
257 (Precision::Exact(exact), Precision::Inexact(inexact))
258 | (Precision::Inexact(inexact), Precision::Exact(exact)) => {
259 if exact.partial_cmp(inexact)?.is_lt() {
260 IntersectionResult::Value(Precision::Inexact(exact.clone()))
261 } else {
262 IntersectionResult::Value(Precision::Exact(exact.clone()))
263 }
264 }
265 (Precision::Inexact(lhs), Precision::Inexact(rhs)) => {
266 IntersectionResult::Value(Precision::Inexact(partial_min(lhs, rhs)?.clone()))
267 }
268 (_, Precision::Absent) | (Precision::Absent, _) => IntersectionResult::Empty,
269 })
270 }
271
272 fn to_exact(&self) -> Option<&T> {
273 match self {
274 Precision::Exact(val) => Some(val),
275 _ => None,
276 }
277 }
278}