use std::any::Any;
use arrow_array::cast::AsArray;
use arrow_schema::DataType;
use vortex_error::VortexResult;
use crate::Array;
use crate::ArrayRef;
use crate::IntoArray;
use crate::arrays::ScalarFnArray;
use crate::arrow::FromArrowArray;
use crate::arrow::IntoArrowArray;
use crate::compute::Options;
use crate::expr::Binary;
use crate::expr::ScalarFn;
use crate::expr::operators::Operator;
#[deprecated(note = "Use and_kleene instead. Non-Kleene boolean ops cannot be lazily evaluated.")]
pub fn and(lhs: &dyn Array, rhs: &dyn Array) -> VortexResult<ArrayRef> {
boolean(lhs, rhs, BooleanOperator::And)
}
pub fn and_kleene(lhs: &dyn Array, rhs: &dyn Array) -> VortexResult<ArrayRef> {
boolean(lhs, rhs, BooleanOperator::AndKleene)
}
#[deprecated(note = "Use or_kleene instead. Non-Kleene boolean ops cannot be lazily evaluated.")]
pub fn or(lhs: &dyn Array, rhs: &dyn Array) -> VortexResult<ArrayRef> {
boolean(lhs, rhs, BooleanOperator::Or)
}
pub fn or_kleene(lhs: &dyn Array, rhs: &dyn Array) -> VortexResult<ArrayRef> {
boolean(lhs, rhs, BooleanOperator::OrKleene)
}
pub fn boolean(lhs: &dyn Array, rhs: &dyn Array, op: BooleanOperator) -> VortexResult<ArrayRef> {
match Operator::try_from(op) {
Ok(expr_op) => Ok(ScalarFnArray::try_new(
ScalarFn::new(Binary, expr_op),
vec![lhs.to_array(), rhs.to_array()],
lhs.len(),
)?
.into_array()),
Err(_) => {
tracing::trace!(
"non-Kleene boolean op {op:?} cannot be lazily evaluated, falling back to eager Arrow evaluation"
);
arrow_boolean(lhs.to_array(), rhs.to_array(), op)
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BooleanOperator {
And,
AndKleene,
Or,
OrKleene,
}
impl Options for BooleanOperator {
fn as_any(&self) -> &dyn Any {
self
}
}
pub(crate) fn arrow_boolean(
lhs: ArrayRef,
rhs: ArrayRef,
operator: BooleanOperator,
) -> VortexResult<ArrayRef> {
let nullable = lhs.dtype().is_nullable() || rhs.dtype().is_nullable();
let lhs = lhs.into_arrow(&DataType::Boolean)?.as_boolean().clone();
let rhs = rhs.into_arrow(&DataType::Boolean)?.as_boolean().clone();
let array = match operator {
BooleanOperator::And => arrow_arith::boolean::and(&lhs, &rhs)?,
BooleanOperator::AndKleene => arrow_arith::boolean::and_kleene(&lhs, &rhs)?,
BooleanOperator::Or => arrow_arith::boolean::or(&lhs, &rhs)?,
BooleanOperator::OrKleene => arrow_arith::boolean::or_kleene(&lhs, &rhs)?,
};
ArrayRef::from_arrow(&array, nullable)
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use super::*;
use crate::IntoArray;
use crate::arrays::BoolArray;
use crate::canonical::ToCanonical;
#[rstest]
#[case(BoolArray::from_iter([Some(true), Some(true), Some(false), Some(false)].into_iter())
.into_array(), BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)].into_iter())
.into_array())]
#[case(BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)].into_iter()).into_array(),
BoolArray::from_iter([Some(true), Some(true), Some(false), Some(false)].into_iter()).into_array())]
fn test_or(#[case] lhs: ArrayRef, #[case] rhs: ArrayRef) {
let r = or_kleene(&lhs, &rhs).unwrap();
let r = r.to_bool().into_array();
let v0 = r.scalar_at(0).unwrap().as_bool().value();
let v1 = r.scalar_at(1).unwrap().as_bool().value();
let v2 = r.scalar_at(2).unwrap().as_bool().value();
let v3 = r.scalar_at(3).unwrap().as_bool().value();
assert!(v0.unwrap());
assert!(v1.unwrap());
assert!(v2.unwrap());
assert!(!v3.unwrap());
}
#[rstest]
#[case(BoolArray::from_iter([Some(true), Some(true), Some(false), Some(false)].into_iter())
.into_array(), BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)].into_iter())
.into_array())]
#[case(BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)].into_iter()).into_array(),
BoolArray::from_iter([Some(true), Some(true), Some(false), Some(false)].into_iter()).into_array())]
fn test_and(#[case] lhs: ArrayRef, #[case] rhs: ArrayRef) {
let r = and_kleene(&lhs, &rhs).unwrap().to_bool().into_array();
let v0 = r.scalar_at(0).unwrap().as_bool().value();
let v1 = r.scalar_at(1).unwrap().as_bool().value();
let v2 = r.scalar_at(2).unwrap().as_bool().value();
let v3 = r.scalar_at(3).unwrap().as_bool().value();
assert!(v0.unwrap());
assert!(!v1.unwrap());
assert!(!v2.unwrap());
assert!(!v3.unwrap());
}
}