vortex_array/expr/exprs/
not.rs1use std::fmt::Formatter;
5
6use vortex_dtype::DType;
7use vortex_error::{VortexResult, vortex_bail};
8
9use crate::ArrayRef;
10use crate::compute::invert;
11use crate::expr::{ChildName, ExprId, Expression, ExpressionView, VTable, VTableExt};
12
13pub struct Not;
15
16impl VTable for Not {
17 type Instance = ();
18
19 fn id(&self) -> ExprId {
20 ExprId::new_ref("vortex.not")
21 }
22
23 fn serialize(&self, _instance: &Self::Instance) -> VortexResult<Option<Vec<u8>>> {
24 Ok(Some(vec![]))
25 }
26
27 fn deserialize(&self, _metadata: &[u8]) -> VortexResult<Option<Self::Instance>> {
28 Ok(Some(()))
29 }
30
31 fn validate(&self, expr: &ExpressionView<Self>) -> VortexResult<()> {
32 if expr.children().len() != 1 {
33 vortex_bail!(
34 "Not expression expects exactly one child, got {}",
35 expr.children().len()
36 );
37 }
38 Ok(())
39 }
40
41 fn child_name(&self, _instance: &Self::Instance, child_idx: usize) -> ChildName {
42 match child_idx {
43 0 => ChildName::from("input"),
44 _ => unreachable!("Invalid child index {} for Not expression", child_idx),
45 }
46 }
47
48 fn fmt_sql(&self, expr: &ExpressionView<Self>, f: &mut Formatter<'_>) -> std::fmt::Result {
49 write!(f, "not(")?;
50 expr.child(0).fmt_sql(f)?;
51 write!(f, ")")
52 }
53
54 fn return_dtype(&self, expr: &ExpressionView<Self>, scope: &DType) -> VortexResult<DType> {
55 let child_dtype = expr.child(0).return_dtype(scope)?;
56 if !matches!(child_dtype, DType::Bool(_)) {
57 vortex_bail!(
58 "Not expression expects a boolean child, got: {}",
59 child_dtype
60 );
61 }
62 Ok(child_dtype)
63 }
64
65 fn evaluate(&self, expr: &ExpressionView<Self>, scope: &ArrayRef) -> VortexResult<ArrayRef> {
66 let child_result = expr.child(0).evaluate(scope)?;
67 invert(&child_result)
68 }
69}
70
71pub fn not(operand: Expression) -> Expression {
80 Not.new_expr((), vec![operand])
81}
82
83#[cfg(test)]
84mod tests {
85 use vortex_dtype::{DType, Nullability};
86
87 use super::not;
88 use crate::ToCanonical;
89 use crate::arrays::BoolArray;
90 use crate::expr::exprs::get_item::{col, get_item};
91 use crate::expr::exprs::root::root;
92 use crate::expr::test_harness;
93
94 #[test]
95 fn invert_booleans() {
96 let not_expr = not(root());
97 let bools = BoolArray::from_iter([false, true, false, false, true, true]);
98 assert_eq!(
99 not_expr
100 .evaluate(&bools.to_array())
101 .unwrap()
102 .to_bool()
103 .bit_buffer()
104 .iter()
105 .collect::<Vec<_>>(),
106 vec![true, false, true, true, false, false]
107 );
108 }
109
110 #[test]
111 fn test_display_order_of_operations() {
112 let a = not(get_item("a", root()));
113 let b = get_item("a", not(root()));
114 assert_ne!(a.to_string(), b.to_string());
115 assert_eq!(a.to_string(), "not($.a)");
116 assert_eq!(b.to_string(), "not($).a");
117 }
118
119 #[test]
120 fn dtype() {
121 let not_expr = not(root());
122 let dtype = DType::Bool(Nullability::NonNullable);
123 assert_eq!(
124 not_expr.return_dtype(&dtype).unwrap(),
125 DType::Bool(Nullability::NonNullable)
126 );
127
128 let dtype = test_harness::struct_dtype();
129 assert_eq!(
130 not(col("bool1")).return_dtype(&dtype).unwrap(),
131 DType::Bool(Nullability::NonNullable)
132 );
133 }
134}