1use std::any::Any;
2use std::sync::{Arc, LazyLock};
3
4use arcref::ArcRef;
5use arrow_array::ArrayRef as ArrowArrayRef;
6use arrow_array::cast::AsArray;
7use arrow_schema::DataType;
8use vortex_dtype::DType;
9use vortex_error::{VortexError, VortexExpect, VortexResult, vortex_bail, vortex_err};
10
11use crate::arrow::{FromArrowArray, IntoArrowArray};
12use crate::compute::{ComputeFn, ComputeFnVTable, InvocationArgs, Kernel, Options, Output};
13use crate::vtable::VTable;
14use crate::{Array, ArrayRef};
15
16pub fn and(lhs: &dyn Array, rhs: &dyn Array) -> VortexResult<ArrayRef> {
20 boolean(lhs, rhs, BooleanOperator::And)
21}
22
23pub fn and_kleene(lhs: &dyn Array, rhs: &dyn Array) -> VortexResult<ArrayRef> {
25 boolean(lhs, rhs, BooleanOperator::AndKleene)
26}
27
28pub fn or(lhs: &dyn Array, rhs: &dyn Array) -> VortexResult<ArrayRef> {
32 boolean(lhs, rhs, BooleanOperator::Or)
33}
34
35pub fn or_kleene(lhs: &dyn Array, rhs: &dyn Array) -> VortexResult<ArrayRef> {
37 boolean(lhs, rhs, BooleanOperator::OrKleene)
38}
39
40pub fn boolean(lhs: &dyn Array, rhs: &dyn Array, op: BooleanOperator) -> VortexResult<ArrayRef> {
44 BOOLEAN_FN
45 .invoke(&InvocationArgs {
46 inputs: &[lhs.into(), rhs.into()],
47 options: &op,
48 })?
49 .unwrap_array()
50}
51
52pub struct BooleanKernelRef(ArcRef<dyn Kernel>);
53inventory::collect!(BooleanKernelRef);
54
55pub trait BooleanKernel: VTable {
56 fn boolean(
57 &self,
58 array: &Self::Array,
59 other: &dyn Array,
60 op: BooleanOperator,
61 ) -> VortexResult<Option<ArrayRef>>;
62}
63
64#[derive(Debug)]
65pub struct BooleanKernelAdapter<V: VTable>(pub V);
66
67impl<V: VTable + BooleanKernel> BooleanKernelAdapter<V> {
68 pub const fn lift(&'static self) -> BooleanKernelRef {
69 BooleanKernelRef(ArcRef::new_ref(self))
70 }
71}
72
73impl<V: VTable + BooleanKernel> Kernel for BooleanKernelAdapter<V> {
74 fn invoke(&self, args: &InvocationArgs) -> VortexResult<Option<Output>> {
75 let inputs = BooleanArgs::try_from(args)?;
76 let Some(array) = inputs.lhs.as_opt::<V>() else {
77 return Ok(None);
78 };
79 Ok(V::boolean(&self.0, array, inputs.rhs, inputs.operator)?.map(|array| array.into()))
80 }
81}
82
83pub static BOOLEAN_FN: LazyLock<ComputeFn> = LazyLock::new(|| {
84 let compute = ComputeFn::new("boolean".into(), ArcRef::new_ref(&Boolean));
85 for kernel in inventory::iter::<BooleanKernelRef> {
86 compute.register_kernel(kernel.0.clone());
87 }
88 compute
89});
90
91struct Boolean;
92
93impl ComputeFnVTable for Boolean {
94 fn invoke(
95 &self,
96 args: &InvocationArgs,
97 kernels: &[ArcRef<dyn Kernel>],
98 ) -> VortexResult<Output> {
99 let BooleanArgs { lhs, rhs, operator } = BooleanArgs::try_from(args)?;
100
101 let rhs_is_constant = rhs.is_constant();
102
103 if lhs.is_constant() && !rhs_is_constant {
105 return Ok(boolean(rhs, lhs, operator)?.into());
106 }
107
108 if lhs.is_arrow() && (rhs.is_arrow() || rhs_is_constant) {
110 return Ok(arrow_boolean(lhs.to_array(), rhs.to_array(), operator)?.into());
111 }
112
113 for kernel in kernels {
115 if let Some(output) = kernel.invoke(args)? {
116 return Ok(output);
117 }
118 }
119 if let Some(output) = lhs.invoke(&BOOLEAN_FN, args)? {
120 return Ok(output);
121 }
122
123 let inverse_args = InvocationArgs {
124 inputs: &[rhs.into(), lhs.into()],
125 options: &operator,
126 };
127 for kernel in kernels {
128 if let Some(output) = kernel.invoke(&inverse_args)? {
129 return Ok(output);
130 }
131 }
132 if let Some(output) = rhs.invoke(&BOOLEAN_FN, &inverse_args)? {
133 return Ok(output);
134 }
135
136 log::debug!(
137 "No boolean implementation found for LHS {}, RHS {}, and operator {:?} (or inverse)",
138 rhs.encoding_id(),
139 lhs.encoding_id(),
140 operator,
141 );
142
143 Ok(arrow_boolean(lhs.to_array(), rhs.to_array(), operator)?.into())
145 }
146
147 fn return_dtype(&self, args: &InvocationArgs) -> VortexResult<DType> {
148 let BooleanArgs { lhs, rhs, .. } = BooleanArgs::try_from(args)?;
149
150 if !lhs.dtype().is_boolean()
151 || !rhs.dtype().is_boolean()
152 || !lhs.dtype().eq_ignore_nullability(rhs.dtype())
153 {
154 vortex_bail!(
155 "Boolean operations are only supported on boolean arrays: {} and {}",
156 lhs.dtype(),
157 rhs.dtype()
158 )
159 }
160
161 Ok(DType::Bool(
162 (lhs.dtype().is_nullable() || rhs.dtype().is_nullable()).into(),
163 ))
164 }
165
166 fn return_len(&self, args: &InvocationArgs) -> VortexResult<usize> {
167 let BooleanArgs { lhs, rhs, .. } = BooleanArgs::try_from(args)?;
168
169 if lhs.len() != rhs.len() {
170 vortex_bail!(
171 "Boolean operations aren't supported on arrays of different lengths: {} and {}",
172 lhs.len(),
173 rhs.len()
174 )
175 }
176
177 Ok(lhs.len())
178 }
179
180 fn is_elementwise(&self) -> bool {
181 true
182 }
183}
184
185#[derive(Debug, Clone, Copy, PartialEq, Eq)]
186pub enum BooleanOperator {
187 And,
188 AndKleene,
189 Or,
190 OrKleene,
191 }
195
196impl Options for BooleanOperator {
197 fn as_any(&self) -> &dyn Any {
198 self
199 }
200}
201
202struct BooleanArgs<'a> {
203 lhs: &'a dyn Array,
204 rhs: &'a dyn Array,
205 operator: BooleanOperator,
206}
207
208impl<'a> TryFrom<&InvocationArgs<'a>> for BooleanArgs<'a> {
209 type Error = VortexError;
210
211 fn try_from(value: &InvocationArgs<'a>) -> VortexResult<Self> {
212 if value.inputs.len() != 2 {
213 vortex_bail!("Expected 2 inputs, found {}", value.inputs.len());
214 }
215 let lhs = value.inputs[0]
216 .array()
217 .ok_or_else(|| vortex_err!("Expected input 0 to be an array"))?;
218 let rhs = value.inputs[1]
219 .array()
220 .ok_or_else(|| vortex_err!("Expected input 1 to be an array"))?;
221 let operator = value
222 .options
223 .as_any()
224 .downcast_ref::<BooleanOperator>()
225 .vortex_expect("Expected options to be an operator");
226
227 Ok(BooleanArgs {
228 lhs,
229 rhs,
230 operator: *operator,
231 })
232 }
233}
234
235pub(crate) fn arrow_boolean(
240 lhs: ArrayRef,
241 rhs: ArrayRef,
242 operator: BooleanOperator,
243) -> VortexResult<ArrayRef> {
244 let nullable = lhs.dtype().is_nullable() || rhs.dtype().is_nullable();
245
246 let lhs = lhs.into_arrow(&DataType::Boolean)?.as_boolean().clone();
247 let rhs = rhs.into_arrow(&DataType::Boolean)?.as_boolean().clone();
248
249 let array = match operator {
250 BooleanOperator::And => arrow_arith::boolean::and(&lhs, &rhs)?,
251 BooleanOperator::AndKleene => arrow_arith::boolean::and_kleene(&lhs, &rhs)?,
252 BooleanOperator::Or => arrow_arith::boolean::or(&lhs, &rhs)?,
253 BooleanOperator::OrKleene => arrow_arith::boolean::or_kleene(&lhs, &rhs)?,
254 };
255
256 Ok(ArrayRef::from_arrow(
257 Arc::new(array) as ArrowArrayRef,
258 nullable,
259 ))
260}
261
262#[cfg(test)]
263mod tests {
264 use rstest::rstest;
265
266 use super::*;
267 use crate::IntoArray;
268 use crate::arrays::BoolArray;
269 use crate::canonical::ToCanonical;
270 #[rstest]
271 #[case(BoolArray::from_iter([Some(true), Some(true), Some(false), Some(false)].into_iter())
272 .into_array(), BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)].into_iter())
273 .into_array())]
274 #[case(BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)].into_iter()).into_array(),
275 BoolArray::from_iter([Some(true), Some(true), Some(false), Some(false)].into_iter()).into_array())]
276 fn test_or(#[case] lhs: ArrayRef, #[case] rhs: ArrayRef) {
277 let r = or(&lhs, &rhs).unwrap();
278
279 let r = r.to_bool().unwrap().into_array();
280
281 let v0 = r.scalar_at(0).unwrap().as_bool().value();
282 let v1 = r.scalar_at(1).unwrap().as_bool().value();
283 let v2 = r.scalar_at(2).unwrap().as_bool().value();
284 let v3 = r.scalar_at(3).unwrap().as_bool().value();
285
286 assert!(v0.unwrap());
287 assert!(v1.unwrap());
288 assert!(v2.unwrap());
289 assert!(!v3.unwrap());
290 }
291
292 #[rstest]
293 #[case(BoolArray::from_iter([Some(true), Some(true), Some(false), Some(false)].into_iter())
294 .into_array(), BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)].into_iter())
295 .into_array())]
296 #[case(BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)].into_iter()).into_array(),
297 BoolArray::from_iter([Some(true), Some(true), Some(false), Some(false)].into_iter()).into_array())]
298 fn test_and(#[case] lhs: ArrayRef, #[case] rhs: ArrayRef) {
299 let r = and(&lhs, &rhs).unwrap().to_bool().unwrap().into_array();
300
301 let v0 = r.scalar_at(0).unwrap().as_bool().value();
302 let v1 = r.scalar_at(1).unwrap().as_bool().value();
303 let v2 = r.scalar_at(2).unwrap().as_bool().value();
304 let v3 = r.scalar_at(3).unwrap().as_bool().value();
305
306 assert!(v0.unwrap());
307 assert!(!v1.unwrap());
308 assert!(!v2.unwrap());
309 assert!(!v3.unwrap());
310 }
311}