vortex_array/compute/
between.rs1use vortex_dtype::{DType, Nullability};
2use vortex_error::{VortexExpect, VortexResult};
3use vortex_scalar::Scalar;
4
5use crate::arrays::ConstantArray;
6use crate::compute::{BinaryOperator, Operator, binary_boolean, compare};
7use crate::{Array, ArrayRef, Canonical, Encoding, IntoArray};
8
9pub trait BetweenFn<A> {
10 fn between(
11 &self,
12 arr: A,
13 lower: &dyn Array,
14 upper: &dyn Array,
15 options: &BetweenOptions,
16 ) -> VortexResult<Option<ArrayRef>>;
17}
18
19#[derive(Debug, Clone, PartialEq, Eq, Hash)]
20pub struct BetweenOptions {
21 pub lower_strict: StrictComparison,
22 pub upper_strict: StrictComparison,
23}
24
25#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
26pub enum StrictComparison {
27 Strict,
28 NonStrict,
29}
30
31impl StrictComparison {
32 pub const fn to_operator(&self) -> Operator {
33 match self {
34 StrictComparison::Strict => Operator::Lt,
35 StrictComparison::NonStrict => Operator::Lte,
36 }
37 }
38}
39
40impl<E: Encoding> BetweenFn<&dyn Array> for E
41where
42 E: for<'a> BetweenFn<&'a E::Array>,
43{
44 fn between(
45 &self,
46 arr: &dyn Array,
47 lower: &dyn Array,
48 upper: &dyn Array,
49 options: &BetweenOptions,
50 ) -> VortexResult<Option<ArrayRef>> {
51 let array_ref = arr
52 .as_any()
53 .downcast_ref::<E::Array>()
54 .vortex_expect("Failed to downcast array");
55 BetweenFn::between(self, array_ref, lower, upper, options)
56 }
57}
58
59pub fn between(
86 arr: &dyn Array,
87 lower: &dyn Array,
88 upper: &dyn Array,
89 options: &BetweenOptions,
90) -> VortexResult<ArrayRef> {
91 debug_assert!(arr.dtype().eq_ignore_nullability(lower.dtype()));
92 debug_assert!(arr.dtype().eq_ignore_nullability(upper.dtype()));
93 debug_assert_eq!(arr.len(), lower.len());
94 debug_assert_eq!(arr.len(), upper.len());
95
96 if lower.is_invalid(0)? || upper.is_invalid(0)? {
98 if let (Some(c_lower), Some(c_upper)) = (lower.as_constant(), upper.as_constant()) {
99 if c_lower.is_null() || c_upper.is_null() {
100 return Ok(ConstantArray::new(
101 Scalar::null(arr.dtype().with_nullability(
102 lower.dtype().nullability() | upper.dtype().nullability(),
103 )),
104 arr.len(),
105 )
106 .to_array());
107 }
108 }
109 }
110
111 let result = between_impl(arr, lower, upper, options)?;
112
113 debug_assert_eq!(result.len(), arr.len());
114 debug_assert_eq!(
115 result.dtype(),
116 &DType::Bool(
117 arr.dtype().nullability() | lower.dtype().nullability() | upper.dtype().nullability()
118 )
119 );
120
121 Ok(result)
122}
123
124fn between_impl(
125 arr: &dyn Array,
126 lower: &dyn Array,
127 upper: &dyn Array,
128 options: &BetweenOptions,
129) -> VortexResult<ArrayRef> {
130 if lower.as_constant().is_some_and(|v| v.is_null())
131 || upper.as_constant().is_some_and(|v| v.is_null())
132 {
133 return Ok(
134 Canonical::empty(&arr.dtype().with_nullability(Nullability::Nullable)).into_array(),
135 );
136 }
137
138 if let Some(result) = arr
139 .vtable()
140 .between_fn()
141 .and_then(|f| f.between(arr, lower, upper, options).transpose())
142 .transpose()?
143 {
144 return Ok(result);
145 }
146
147 binary_boolean(
149 &compare(lower, arr, options.lower_strict.to_operator())?,
150 &compare(arr, upper, options.upper_strict.to_operator())?,
151 BinaryOperator::And,
152 )
153}