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