mod kernel;
use std::fmt::Formatter;
pub use kernel::*;
use vortex_error::VortexResult;
use vortex_error::vortex_bail;
use vortex_session::VortexSession;
use crate::ArrayRef;
use crate::ExecutionCtx;
use crate::IntoArray;
use crate::arrays::Bool;
use crate::arrays::BoolArray;
use crate::arrays::ConstantArray;
use crate::arrays::bool::BoolArrayExt;
use crate::builtins::ArrayBuiltins;
use crate::dtype::DType;
use crate::expr::Expression;
use crate::scalar::Scalar;
use crate::scalar_fn::Arity;
use crate::scalar_fn::ChildName;
use crate::scalar_fn::EmptyOptions;
use crate::scalar_fn::ExecutionArgs;
use crate::scalar_fn::ScalarFnId;
use crate::scalar_fn::ScalarFnVTable;
#[derive(Clone)]
pub struct Not;
impl ScalarFnVTable for Not {
type Options = EmptyOptions;
fn id(&self) -> ScalarFnId {
ScalarFnId::from("vortex.not")
}
fn serialize(&self, _options: &Self::Options) -> VortexResult<Option<Vec<u8>>> {
Ok(Some(vec![]))
}
fn deserialize(
&self,
_metadata: &[u8],
_session: &VortexSession,
) -> VortexResult<Self::Options> {
Ok(EmptyOptions)
}
fn arity(&self, _options: &Self::Options) -> Arity {
Arity::Exact(1)
}
fn child_name(&self, _options: &Self::Options, child_idx: usize) -> ChildName {
match child_idx {
0 => ChildName::from("input"),
_ => unreachable!("Invalid child index {} for Not expression", child_idx),
}
}
fn fmt_sql(
&self,
_options: &Self::Options,
expr: &Expression,
f: &mut Formatter<'_>,
) -> std::fmt::Result {
write!(f, "not(")?;
expr.child(0).fmt_sql(f)?;
write!(f, ")")
}
fn return_dtype(&self, _options: &Self::Options, arg_dtypes: &[DType]) -> VortexResult<DType> {
let child_dtype = &arg_dtypes[0];
if !matches!(child_dtype, DType::Bool(_)) {
vortex_bail!(
"Not expression expects a boolean child, got: {}",
child_dtype
);
}
Ok(child_dtype.clone())
}
fn execute(
&self,
_data: &Self::Options,
args: &dyn ExecutionArgs,
ctx: &mut ExecutionCtx,
) -> VortexResult<ArrayRef> {
let child = args.get(0)?;
if let Some(scalar) = child.as_constant() {
let value = match scalar.as_bool().value() {
Some(b) => Scalar::bool(!b, child.dtype().nullability()),
None => Scalar::null(child.dtype().clone()),
};
return Ok(ConstantArray::new(value, args.row_count()).into_array());
}
if let Some(bool) = child.as_opt::<Bool>() {
return Ok(BoolArray::new(!bool.to_bit_buffer(), bool.validity()?).into_array());
}
child.execute::<ArrayRef>(ctx)?.not()
}
fn is_null_sensitive(&self, _options: &Self::Options) -> bool {
false
}
fn is_fallible(&self, _options: &Self::Options) -> bool {
false
}
}
#[cfg(test)]
mod tests {
use crate::IntoArray;
use crate::ToCanonical;
use crate::arrays::bool::BoolArrayExt;
use crate::dtype::DType;
use crate::dtype::Nullability;
use crate::expr::col;
use crate::expr::get_item;
use crate::expr::not;
use crate::expr::root;
use crate::expr::test_harness;
use crate::scalar_fn::fns::not::BoolArray;
#[test]
fn invert_booleans() {
let not_expr = not(root());
let bools = BoolArray::from_iter([false, true, false, false, true, true]);
assert_eq!(
bools
.into_array()
.apply(¬_expr)
.unwrap()
.to_bool()
.to_bit_buffer()
.iter()
.collect::<Vec<_>>(),
vec![true, false, true, true, false, false]
);
}
#[test]
fn test_display_order_of_operations() {
let a = not(get_item("a", root()));
let b = get_item("a", not(root()));
assert_ne!(a.to_string(), b.to_string());
assert_eq!(a.to_string(), "not($.a)");
assert_eq!(b.to_string(), "not($).a");
}
#[test]
fn dtype() {
let not_expr = not(root());
let dtype = DType::Bool(Nullability::NonNullable);
assert_eq!(
not_expr.return_dtype(&dtype).unwrap(),
DType::Bool(Nullability::NonNullable)
);
let dtype = test_harness::struct_dtype();
assert_eq!(
not(col("bool1")).return_dtype(&dtype).unwrap(),
DType::Bool(Nullability::NonNullable)
);
}
}