1use std::any::Any;
5use std::sync::LazyLock;
6
7use arcref::ArcRef;
8use arrow_array::cast::AsArray;
9use arrow_schema::DataType;
10use vortex_dtype::DType;
11use vortex_error::{VortexError, VortexExpect, VortexResult, vortex_bail, vortex_err};
12
13use crate::arrow::{FromArrowArray, IntoArrowArray};
14use crate::compute::{ComputeFn, ComputeFnVTable, InvocationArgs, Kernel, Options, Output};
15use crate::vtable::VTable;
16use crate::{Array, ArrayRef};
17
18static BOOLEAN_FN: LazyLock<ComputeFn> = LazyLock::new(|| {
19 let compute = ComputeFn::new("boolean".into(), ArcRef::new_ref(&Boolean));
20 for kernel in inventory::iter::<BooleanKernelRef> {
21 compute.register_kernel(kernel.0.clone());
22 }
23 compute
24});
25
26pub(crate) fn warm_up_vtable() -> usize {
27 BOOLEAN_FN.kernels().len()
28}
29
30pub fn and(lhs: &dyn Array, rhs: &dyn Array) -> VortexResult<ArrayRef> {
37 boolean(lhs, rhs, BooleanOperator::And)
38}
39
40pub fn and_kleene(lhs: &dyn Array, rhs: &dyn Array) -> VortexResult<ArrayRef> {
44 boolean(lhs, rhs, BooleanOperator::AndKleene)
45}
46
47pub fn or(lhs: &dyn Array, rhs: &dyn Array) -> VortexResult<ArrayRef> {
54 boolean(lhs, rhs, BooleanOperator::Or)
55}
56
57pub fn or_kleene(lhs: &dyn Array, rhs: &dyn Array) -> VortexResult<ArrayRef> {
61 boolean(lhs, rhs, BooleanOperator::OrKleene)
62}
63
64pub fn boolean(lhs: &dyn Array, rhs: &dyn Array, op: BooleanOperator) -> VortexResult<ArrayRef> {
69 BOOLEAN_FN
70 .invoke(&InvocationArgs {
71 inputs: &[lhs.into(), rhs.into()],
72 options: &op,
73 })?
74 .unwrap_array()
75}
76
77pub struct BooleanKernelRef(ArcRef<dyn Kernel>);
78inventory::collect!(BooleanKernelRef);
79
80pub trait BooleanKernel: VTable {
81 fn boolean(
82 &self,
83 array: &Self::Array,
84 other: &dyn Array,
85 op: BooleanOperator,
86 ) -> VortexResult<Option<ArrayRef>>;
87}
88
89#[derive(Debug)]
90pub struct BooleanKernelAdapter<V: VTable>(pub V);
91
92impl<V: VTable + BooleanKernel> BooleanKernelAdapter<V> {
93 pub const fn lift(&'static self) -> BooleanKernelRef {
94 BooleanKernelRef(ArcRef::new_ref(self))
95 }
96}
97
98impl<V: VTable + BooleanKernel> Kernel for BooleanKernelAdapter<V> {
99 fn invoke(&self, args: &InvocationArgs) -> VortexResult<Option<Output>> {
100 let inputs = BooleanArgs::try_from(args)?;
101 let Some(array) = inputs.lhs.as_opt::<V>() else {
102 return Ok(None);
103 };
104 Ok(V::boolean(&self.0, array, inputs.rhs, inputs.operator)?.map(|array| array.into()))
105 }
106}
107
108struct Boolean;
109
110impl ComputeFnVTable for Boolean {
111 fn invoke(
112 &self,
113 args: &InvocationArgs,
114 kernels: &[ArcRef<dyn Kernel>],
115 ) -> VortexResult<Output> {
116 let BooleanArgs { lhs, rhs, operator } = BooleanArgs::try_from(args)?;
117
118 let rhs_is_constant = rhs.is_constant();
119
120 if lhs.is_constant() && !rhs_is_constant {
122 return Ok(boolean(rhs, lhs, operator)?.into());
123 }
124
125 if lhs.is_arrow() && (rhs.is_arrow() || rhs_is_constant) {
127 return Ok(arrow_boolean(lhs.to_array(), rhs.to_array(), operator)?.into());
128 }
129
130 for kernel in kernels {
132 if let Some(output) = kernel.invoke(args)? {
133 return Ok(output);
134 }
135 }
136 if let Some(output) = lhs.invoke(&BOOLEAN_FN, args)? {
137 return Ok(output);
138 }
139
140 let inverse_args = InvocationArgs {
141 inputs: &[rhs.into(), lhs.into()],
142 options: &operator,
143 };
144 for kernel in kernels {
145 if let Some(output) = kernel.invoke(&inverse_args)? {
146 return Ok(output);
147 }
148 }
149 if let Some(output) = rhs.invoke(&BOOLEAN_FN, &inverse_args)? {
150 return Ok(output);
151 }
152
153 log::debug!(
154 "No boolean implementation found for LHS {}, RHS {}, and operator {:?} (or inverse)",
155 rhs.encoding_id(),
156 lhs.encoding_id(),
157 operator,
158 );
159
160 Ok(arrow_boolean(lhs.to_array(), rhs.to_array(), operator)?.into())
162 }
163
164 fn return_dtype(&self, args: &InvocationArgs) -> VortexResult<DType> {
165 let BooleanArgs { lhs, rhs, .. } = BooleanArgs::try_from(args)?;
166
167 if !lhs.dtype().is_boolean()
168 || !rhs.dtype().is_boolean()
169 || !lhs.dtype().eq_ignore_nullability(rhs.dtype())
170 {
171 vortex_bail!(
172 "Boolean operations are only supported on boolean arrays: {} and {}",
173 lhs.dtype(),
174 rhs.dtype()
175 )
176 }
177
178 Ok(DType::Bool(
179 (lhs.dtype().is_nullable() || rhs.dtype().is_nullable()).into(),
180 ))
181 }
182
183 fn return_len(&self, args: &InvocationArgs) -> VortexResult<usize> {
184 let BooleanArgs { lhs, rhs, .. } = BooleanArgs::try_from(args)?;
185
186 if lhs.len() != rhs.len() {
187 vortex_bail!(
188 "Boolean operations aren't supported on arrays of different lengths: {} and {}",
189 lhs.len(),
190 rhs.len()
191 )
192 }
193
194 Ok(lhs.len())
195 }
196
197 fn is_elementwise(&self) -> bool {
198 true
199 }
200}
201
202#[derive(Debug, Clone, Copy, PartialEq, Eq)]
206pub enum BooleanOperator {
207 And,
216 AndKleene,
225 Or,
234 OrKleene,
243 }
247
248impl Options for BooleanOperator {
249 fn as_any(&self) -> &dyn Any {
250 self
251 }
252}
253
254struct BooleanArgs<'a> {
255 lhs: &'a dyn Array,
256 rhs: &'a dyn Array,
257 operator: BooleanOperator,
258}
259
260impl<'a> TryFrom<&InvocationArgs<'a>> for BooleanArgs<'a> {
261 type Error = VortexError;
262
263 fn try_from(value: &InvocationArgs<'a>) -> VortexResult<Self> {
264 if value.inputs.len() != 2 {
265 vortex_bail!("Expected 2 inputs, found {}", value.inputs.len());
266 }
267 let lhs = value.inputs[0]
268 .array()
269 .ok_or_else(|| vortex_err!("Expected input 0 to be an array"))?;
270 let rhs = value.inputs[1]
271 .array()
272 .ok_or_else(|| vortex_err!("Expected input 1 to be an array"))?;
273 let operator = value
274 .options
275 .as_any()
276 .downcast_ref::<BooleanOperator>()
277 .vortex_expect("Expected options to be an operator");
278
279 Ok(BooleanArgs {
280 lhs,
281 rhs,
282 operator: *operator,
283 })
284 }
285}
286
287pub(crate) fn arrow_boolean(
292 lhs: ArrayRef,
293 rhs: ArrayRef,
294 operator: BooleanOperator,
295) -> VortexResult<ArrayRef> {
296 let nullable = lhs.dtype().is_nullable() || rhs.dtype().is_nullable();
297
298 let lhs = lhs.into_arrow(&DataType::Boolean)?.as_boolean().clone();
299 let rhs = rhs.into_arrow(&DataType::Boolean)?.as_boolean().clone();
300
301 let array = match operator {
302 BooleanOperator::And => arrow_arith::boolean::and(&lhs, &rhs)?,
303 BooleanOperator::AndKleene => arrow_arith::boolean::and_kleene(&lhs, &rhs)?,
304 BooleanOperator::Or => arrow_arith::boolean::or(&lhs, &rhs)?,
305 BooleanOperator::OrKleene => arrow_arith::boolean::or_kleene(&lhs, &rhs)?,
306 };
307
308 Ok(ArrayRef::from_arrow(&array, nullable))
309}
310
311#[cfg(test)]
312mod tests {
313 use rstest::rstest;
314
315 use super::*;
316 use crate::IntoArray;
317 use crate::arrays::BoolArray;
318 use crate::canonical::ToCanonical;
319 #[rstest]
320 #[case(BoolArray::from_iter([Some(true), Some(true), Some(false), Some(false)].into_iter())
321 .into_array(), BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)].into_iter())
322 .into_array())]
323 #[case(BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)].into_iter()).into_array(),
324 BoolArray::from_iter([Some(true), Some(true), Some(false), Some(false)].into_iter()).into_array())]
325 fn test_or(#[case] lhs: ArrayRef, #[case] rhs: ArrayRef) {
326 let r = or(&lhs, &rhs).unwrap();
327
328 let r = r.to_bool().into_array();
329
330 let v0 = r.scalar_at(0).as_bool().value();
331 let v1 = r.scalar_at(1).as_bool().value();
332 let v2 = r.scalar_at(2).as_bool().value();
333 let v3 = r.scalar_at(3).as_bool().value();
334
335 assert!(v0.unwrap());
336 assert!(v1.unwrap());
337 assert!(v2.unwrap());
338 assert!(!v3.unwrap());
339 }
340
341 #[rstest]
342 #[case(BoolArray::from_iter([Some(true), Some(true), Some(false), Some(false)].into_iter())
343 .into_array(), BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)].into_iter())
344 .into_array())]
345 #[case(BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)].into_iter()).into_array(),
346 BoolArray::from_iter([Some(true), Some(true), Some(false), Some(false)].into_iter()).into_array())]
347 fn test_and(#[case] lhs: ArrayRef, #[case] rhs: ArrayRef) {
348 let r = and(&lhs, &rhs).unwrap().to_bool().into_array();
349
350 let v0 = r.scalar_at(0).as_bool().value();
351 let v1 = r.scalar_at(1).as_bool().value();
352 let v2 = r.scalar_at(2).as_bool().value();
353 let v3 = r.scalar_at(3).as_bool().value();
354
355 assert!(v0.unwrap());
356 assert!(!v1.unwrap());
357 assert!(!v2.unwrap());
358 assert!(!v3.unwrap());
359 }
360}