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 fn and(lhs: &dyn Array, rhs: &dyn Array) -> VortexResult<ArrayRef> {
33 boolean(lhs, rhs, BooleanOperator::And)
34}
35
36pub fn and_kleene(lhs: &dyn Array, rhs: &dyn Array) -> VortexResult<ArrayRef> {
40 boolean(lhs, rhs, BooleanOperator::AndKleene)
41}
42
43pub fn or(lhs: &dyn Array, rhs: &dyn Array) -> VortexResult<ArrayRef> {
50 boolean(lhs, rhs, BooleanOperator::Or)
51}
52
53pub fn or_kleene(lhs: &dyn Array, rhs: &dyn Array) -> VortexResult<ArrayRef> {
57 boolean(lhs, rhs, BooleanOperator::OrKleene)
58}
59
60pub fn boolean(lhs: &dyn Array, rhs: &dyn Array, op: BooleanOperator) -> VortexResult<ArrayRef> {
65 BOOLEAN_FN
66 .invoke(&InvocationArgs {
67 inputs: &[lhs.into(), rhs.into()],
68 options: &op,
69 })?
70 .unwrap_array()
71}
72
73pub struct BooleanKernelRef(ArcRef<dyn Kernel>);
74inventory::collect!(BooleanKernelRef);
75
76pub trait BooleanKernel: VTable {
77 fn boolean(
78 &self,
79 array: &Self::Array,
80 other: &dyn Array,
81 op: BooleanOperator,
82 ) -> VortexResult<Option<ArrayRef>>;
83}
84
85#[derive(Debug)]
86pub struct BooleanKernelAdapter<V: VTable>(pub V);
87
88impl<V: VTable + BooleanKernel> BooleanKernelAdapter<V> {
89 pub const fn lift(&'static self) -> BooleanKernelRef {
90 BooleanKernelRef(ArcRef::new_ref(self))
91 }
92}
93
94impl<V: VTable + BooleanKernel> Kernel for BooleanKernelAdapter<V> {
95 fn invoke(&self, args: &InvocationArgs) -> VortexResult<Option<Output>> {
96 let inputs = BooleanArgs::try_from(args)?;
97 let Some(array) = inputs.lhs.as_opt::<V>() else {
98 return Ok(None);
99 };
100 Ok(V::boolean(&self.0, array, inputs.rhs, inputs.operator)?.map(|array| array.into()))
101 }
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).as_bool().value();
327 let v1 = r.scalar_at(1).as_bool().value();
328 let v2 = r.scalar_at(2).as_bool().value();
329 let v3 = r.scalar_at(3).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).as_bool().value();
347 let v1 = r.scalar_at(1).as_bool().value();
348 let v2 = r.scalar_at(2).as_bool().value();
349 let v3 = r.scalar_at(3).as_bool().value();
350
351 assert!(v0.unwrap());
352 assert!(!v1.unwrap());
353 assert!(!v2.unwrap());
354 assert!(!v3.unwrap());
355 }
356}