1use std::fmt::Debug;
5
6use vortex_array::ArrayRef;
7use vortex_array::ArrayView;
8use vortex_array::ExecutionCtx;
9use vortex_array::IntoArray;
10use vortex_array::arrays::ConstantArray;
11use vortex_array::builtins::ArrayBuiltins;
12use vortex_array::dtype::NativePType;
13use vortex_array::scalar::Scalar;
14use vortex_array::scalar_fn::fns::binary::CompareKernel;
15use vortex_array::scalar_fn::fns::operators::CompareOperator;
16use vortex_array::scalar_fn::fns::operators::Operator;
17use vortex_error::VortexResult;
18use vortex_error::vortex_bail;
19use vortex_error::vortex_err;
20
21use crate::ALP;
22use crate::ALPArrayExt;
23use crate::ALPArraySlotsExt;
24use crate::ALPFloat;
25use crate::match_each_alp_float_ptype;
26
27impl CompareKernel for ALP {
30 fn compare(
31 lhs: ArrayView<'_, Self>,
32 rhs: &ArrayRef,
33 operator: CompareOperator,
34 _ctx: &mut ExecutionCtx,
35 ) -> VortexResult<Option<ArrayRef>> {
36 if lhs.patches().is_some() {
37 return Ok(None);
39 }
40 if lhs.dtype().is_nullable() || rhs.dtype().is_nullable() {
41 return Ok(None);
43 }
44
45 if let Some(const_scalar) = rhs.as_constant() {
46 let pscalar = const_scalar.as_primitive_opt().ok_or_else(|| {
47 vortex_err!(
48 "ALP Compare RHS had the wrong type {}, expected {}",
49 const_scalar,
50 const_scalar.dtype()
51 )
52 })?;
53
54 match_each_alp_float_ptype!(pscalar.ptype(), |T| {
55 match pscalar.typed_value::<T>() {
56 Some(value) => return alp_scalar_compare(lhs, value, operator),
57 None => vortex_bail!(
58 "Failed to convert scalar {:?} to ALP type {:?}",
59 pscalar,
60 pscalar.ptype()
61 ),
62 }
63 });
64 }
65
66 Ok(None)
67 }
68}
69
70fn alp_scalar_compare<F: ALPFloat + Into<Scalar>>(
74 alp: ArrayView<ALP>,
75 value: F,
76 operator: CompareOperator,
77) -> VortexResult<Option<ArrayRef>>
78where
79 F::ALPInt: Into<Scalar>,
80 <F as ALPFloat>::ALPInt: Debug,
81{
82 if alp.patches().is_some() {
84 return Ok(None);
85 }
86
87 let exponents = alp.exponents();
88 let encoded = F::encode_single(value, alp.exponents());
91 match encoded {
92 Some(encoded) => {
93 let s = ConstantArray::new(encoded, alp.len());
94 Ok(Some(
95 alp.encoded()
96 .binary(s.into_array(), Operator::from(operator))?,
97 ))
98 }
99 None => match operator {
100 CompareOperator::Eq => Ok(Some(ConstantArray::new(false, alp.len()).into_array())),
103 CompareOperator::NotEq => Ok(Some(ConstantArray::new(true, alp.len()).into_array())),
106 CompareOperator::Gt | CompareOperator::Gte => {
107 let is_not_finite = NativePType::is_infinite(value) || NativePType::is_nan(value);
110 if is_not_finite {
111 Ok(Some(
112 ConstantArray::new(value.is_sign_negative(), alp.len()).into_array(),
113 ))
114 } else {
115 Ok(Some(
116 alp.encoded().binary(
117 ConstantArray::new(F::encode_above(value, exponents), alp.len())
118 .into_array(),
119 Operator::Gte,
123 )?,
124 ))
125 }
126 }
127 CompareOperator::Lt | CompareOperator::Lte => {
128 let is_not_finite = NativePType::is_infinite(value) || NativePType::is_nan(value);
131 if is_not_finite {
132 Ok(Some(
133 ConstantArray::new(value.is_sign_positive(), alp.len()).into_array(),
134 ))
135 } else {
136 Ok(Some(
137 alp.encoded().binary(
138 ConstantArray::new(F::encode_below(value, exponents), alp.len())
139 .into_array(),
140 Operator::Lte,
143 )?,
144 ))
145 }
146 }
147 },
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use std::f32;
154 use std::sync::LazyLock;
155
156 use rstest::rstest;
157 use vortex_array::ArrayRef;
158 use vortex_array::VortexSessionExecute;
159 use vortex_array::arrays::BoolArray;
160 use vortex_array::arrays::ConstantArray;
161 use vortex_array::arrays::PrimitiveArray;
162 use vortex_array::assert_arrays_eq;
163 use vortex_array::builtins::ArrayBuiltins;
164 use vortex_array::dtype::DType;
165 use vortex_array::dtype::Nullability;
166 use vortex_array::dtype::PType;
167 use vortex_array::scalar::Scalar;
168 use vortex_array::scalar_fn::fns::operators::CompareOperator;
169 use vortex_array::scalar_fn::fns::operators::Operator;
170 use vortex_session::VortexSession;
171
172 use super::*;
173 use crate::alp_encode;
174
175 static SESSION: LazyLock<VortexSession> = LazyLock::new(|| {
176 let session = vortex_array::array_session();
177 crate::initialize(&session);
178 session
179 });
180
181 fn test_alp_compare<F: ALPFloat + Into<Scalar>>(
182 alp: ArrayView<ALP>,
183 value: F,
184 operator: CompareOperator,
185 ) -> Option<ArrayRef>
186 where
187 F::ALPInt: Into<Scalar>,
188 <F as ALPFloat>::ALPInt: Debug,
189 {
190 alp_scalar_compare(alp, value, operator).unwrap()
191 }
192
193 #[test]
194 fn basic_comparison_test() {
195 let mut ctx = SESSION.create_execution_ctx();
196 let array = PrimitiveArray::from_iter([1.234f32; 1025]);
197 let encoded = alp_encode(array.as_view(), None, &mut ctx).unwrap();
198 assert!(encoded.patches().is_none());
199 let encoded_prim = encoded
200 .encoded()
201 .clone()
202 .execute::<PrimitiveArray>(&mut ctx)
203 .unwrap();
204 assert_eq!(encoded_prim.as_slice::<i32>(), vec![1234; 1025]);
205
206 let r = alp_scalar_compare(encoded.as_view(), 1.3_f32, CompareOperator::Eq)
207 .unwrap()
208 .unwrap();
209 let expected = BoolArray::from_iter([false; 1025]);
210 assert_arrays_eq!(r, expected, &mut ctx);
211
212 let r = alp_scalar_compare(encoded.as_view(), 1.234f32, CompareOperator::Eq)
213 .unwrap()
214 .unwrap();
215 let expected = BoolArray::from_iter([true; 1025]);
216 assert_arrays_eq!(r, expected, &mut ctx);
217 }
218
219 #[test]
220 fn comparison_with_unencodable_value() {
221 let mut ctx = SESSION.create_execution_ctx();
222 let array = PrimitiveArray::from_iter([1.234f32; 1025]);
223 let encoded = alp_encode(array.as_view(), None, &mut ctx).unwrap();
224 assert!(encoded.patches().is_none());
225 let encoded_prim = encoded
226 .encoded()
227 .clone()
228 .execute::<PrimitiveArray>(&mut ctx)
229 .unwrap();
230 assert_eq!(encoded_prim.as_slice::<i32>(), vec![1234; 1025]);
231
232 let r_eq = alp_scalar_compare(encoded.as_view(), 1.234444_f32, CompareOperator::Eq)
233 .unwrap()
234 .unwrap();
235 let expected = BoolArray::from_iter([false; 1025]);
236 assert_arrays_eq!(r_eq, expected, &mut ctx);
237
238 let r_neq = alp_scalar_compare(encoded.as_view(), 1.234444f32, CompareOperator::NotEq)
239 .unwrap()
240 .unwrap();
241 let expected = BoolArray::from_iter([true; 1025]);
242 assert_arrays_eq!(r_neq, expected, &mut ctx);
243 }
244
245 #[test]
246 fn comparison_range() {
247 let mut ctx = SESSION.create_execution_ctx();
248 let array = PrimitiveArray::from_iter([0.0605_f32; 10]);
249 let encoded = alp_encode(array.as_view(), None, &mut ctx).unwrap();
250 assert!(encoded.patches().is_none());
251 let encoded_prim = encoded
252 .encoded()
253 .clone()
254 .execute::<PrimitiveArray>(&mut ctx)
255 .unwrap();
256 assert_eq!(encoded_prim.as_slice::<i32>(), vec![605; 10]);
257
258 let r_gte = alp_scalar_compare(encoded.as_view(), 0.06051_f32, CompareOperator::Gte)
260 .unwrap()
261 .unwrap();
262 let expected = BoolArray::from_iter([false; 10]);
263 assert_arrays_eq!(r_gte, expected, &mut ctx);
264
265 let r_gt = alp_scalar_compare(encoded.as_view(), 0.06051_f32, CompareOperator::Gt)
267 .unwrap()
268 .unwrap();
269 let expected = BoolArray::from_iter([false; 10]);
270 assert_arrays_eq!(r_gt, expected, &mut ctx);
271
272 let r_lte = alp_scalar_compare(encoded.as_view(), 0.06051_f32, CompareOperator::Lte)
274 .unwrap()
275 .unwrap();
276 let expected = BoolArray::from_iter([true; 10]);
277 assert_arrays_eq!(r_lte, expected, &mut ctx);
278
279 let r_lt = alp_scalar_compare(encoded.as_view(), 0.06051_f32, CompareOperator::Lt)
281 .unwrap()
282 .unwrap();
283 let expected = BoolArray::from_iter([true; 10]);
284 assert_arrays_eq!(r_lt, expected, &mut ctx);
285 }
286
287 #[test]
288 fn comparison_zeroes() {
289 let mut ctx = SESSION.create_execution_ctx();
290 let array = PrimitiveArray::from_iter([0.0_f32; 10]);
291 let encoded = alp_encode(array.as_view(), None, &mut ctx).unwrap();
292 assert!(encoded.patches().is_none());
293 let encoded_prim = encoded
294 .encoded()
295 .clone()
296 .execute::<PrimitiveArray>(&mut ctx)
297 .unwrap();
298 assert_eq!(encoded_prim.as_slice::<i32>(), vec![0; 10]);
299
300 let r_gte =
301 test_alp_compare(encoded.as_view(), -0.00000001_f32, CompareOperator::Gte).unwrap();
302 let expected = BoolArray::from_iter([true; 10]);
303 assert_arrays_eq!(r_gte, expected, &mut ctx);
304
305 let r_gte = test_alp_compare(encoded.as_view(), -0.0_f32, CompareOperator::Gte).unwrap();
306 let expected = BoolArray::from_iter([true; 10]);
307 assert_arrays_eq!(r_gte, expected, &mut ctx);
308
309 let r_gt =
310 test_alp_compare(encoded.as_view(), -0.0000000001f32, CompareOperator::Gt).unwrap();
311 let expected = BoolArray::from_iter([true; 10]);
312 assert_arrays_eq!(r_gt, expected, &mut ctx);
313
314 let r_gte = test_alp_compare(encoded.as_view(), -0.0_f32, CompareOperator::Gt).unwrap();
315 let expected = BoolArray::from_iter([true; 10]);
316 assert_arrays_eq!(r_gte, expected, &mut ctx);
317
318 let r_lte = test_alp_compare(encoded.as_view(), 0.06051_f32, CompareOperator::Lte).unwrap();
319 let expected = BoolArray::from_iter([true; 10]);
320 assert_arrays_eq!(r_lte, expected, &mut ctx);
321
322 let r_lt = test_alp_compare(encoded.as_view(), 0.06051_f32, CompareOperator::Lt).unwrap();
323 let expected = BoolArray::from_iter([true; 10]);
324 assert_arrays_eq!(r_lt, expected, &mut ctx);
325
326 let r_lt = test_alp_compare(encoded.as_view(), -0.00001_f32, CompareOperator::Lt).unwrap();
327 let expected = BoolArray::from_iter([false; 10]);
328 assert_arrays_eq!(r_lt, expected, &mut ctx);
329 }
330
331 #[test]
332 fn compare_with_patches() {
333 let array = PrimitiveArray::from_iter([1.234f32, 1.5, 19.0, f32::consts::E, 1_000_000.9]);
334 let encoded =
335 alp_encode(array.as_view(), None, &mut SESSION.create_execution_ctx()).unwrap();
336 assert!(encoded.patches().is_some());
337
338 assert!(
340 alp_scalar_compare(encoded.as_view(), 1_000_000.9_f32, CompareOperator::Eq)
341 .unwrap()
342 .is_none()
343 )
344 }
345
346 #[test]
347 fn compare_to_null() {
348 let array = PrimitiveArray::from_iter([1.234f32; 10]);
349 let encoded =
350 alp_encode(array.as_view(), None, &mut SESSION.create_execution_ctx()).unwrap();
351
352 let other = ConstantArray::new(
353 Scalar::null(DType::Primitive(PType::F32, Nullability::Nullable)),
354 array.len(),
355 );
356
357 let r = encoded
358 .into_array()
359 .binary(other.into_array(), Operator::Eq)
360 .unwrap();
361 let expected = BoolArray::from_iter([None::<bool>; 10]);
363 assert_arrays_eq!(r, expected, &mut SESSION.create_execution_ctx());
364 }
365
366 #[rstest]
367 #[case(f32::NAN, false)]
368 #[case(-1.0f32 / 0.0f32, true)]
369 #[case(f32::INFINITY, false)]
370 #[case(f32::NEG_INFINITY, true)]
371 fn compare_to_non_finite_gt(#[case] value: f32, #[case] result: bool) {
372 let array = PrimitiveArray::from_iter([1.234f32; 10]);
373 let encoded =
374 alp_encode(array.as_view(), None, &mut SESSION.create_execution_ctx()).unwrap();
375
376 let r = test_alp_compare(encoded.as_view(), value, CompareOperator::Gt).unwrap();
377 let expected = BoolArray::from_iter([result; 10]);
378 assert_arrays_eq!(r, expected, &mut SESSION.create_execution_ctx());
379 }
380
381 #[rstest]
382 #[case(f32::NAN, true)]
383 #[case(-1.0f32 / 0.0f32, false)]
384 #[case(f32::INFINITY, true)]
385 #[case(f32::NEG_INFINITY, false)]
386 fn compare_to_non_finite_lt(#[case] value: f32, #[case] result: bool) {
387 let array = PrimitiveArray::from_iter([1.234f32; 10]);
388 let encoded =
389 alp_encode(array.as_view(), None, &mut SESSION.create_execution_ctx()).unwrap();
390
391 let r = test_alp_compare(encoded.as_view(), value, CompareOperator::Lt).unwrap();
392 let expected = BoolArray::from_iter([result; 10]);
393 assert_arrays_eq!(r, expected, &mut SESSION.create_execution_ctx());
394 }
395}