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