1use std::hash::Hash;
5
6use vortex_array::compute::invert;
7use vortex_array::{ArrayRef, DeserializeMetadata, EmptyMetadata};
8use vortex_dtype::DType;
9use vortex_error::{VortexResult, vortex_bail};
10
11use crate::display::{DisplayAs, DisplayFormat};
12use crate::{AnalysisExpr, ExprEncodingRef, ExprId, ExprRef, IntoExpr, Scope, VTable, vtable};
13
14vtable!(Not);
15
16#[allow(clippy::derived_hash_with_manual_eq)]
17#[derive(Clone, Debug, Hash, Eq)]
18pub struct NotExpr {
19 child: ExprRef,
20}
21
22impl PartialEq for NotExpr {
23 fn eq(&self, other: &Self) -> bool {
24 self.child.eq(&other.child)
25 }
26}
27
28pub struct NotExprEncoding;
29
30impl VTable for NotVTable {
31 type Expr = NotExpr;
32 type Encoding = NotExprEncoding;
33 type Metadata = EmptyMetadata;
34
35 fn id(_encoding: &Self::Encoding) -> ExprId {
36 ExprId::new_ref("not")
37 }
38
39 fn encoding(_expr: &Self::Expr) -> ExprEncodingRef {
40 ExprEncodingRef::new_ref(NotExprEncoding.as_ref())
41 }
42
43 fn metadata(_expr: &Self::Expr) -> Option<Self::Metadata> {
44 Some(EmptyMetadata)
45 }
46
47 fn children(expr: &Self::Expr) -> Vec<&ExprRef> {
48 vec![&expr.child]
49 }
50
51 fn with_children(_expr: &Self::Expr, children: Vec<ExprRef>) -> VortexResult<Self::Expr> {
52 Ok(NotExpr::new(children[0].clone()))
53 }
54
55 fn build(
56 _encoding: &Self::Encoding,
57 _metadata: &<Self::Metadata as DeserializeMetadata>::Output,
58 children: Vec<ExprRef>,
59 ) -> VortexResult<Self::Expr> {
60 if children.len() != 1 {
61 vortex_bail!(
62 "Not expression expects exactly one child, got {}",
63 children.len()
64 );
65 }
66 Ok(NotExpr::new(children[0].clone()))
67 }
68
69 fn evaluate(expr: &Self::Expr, scope: &Scope) -> VortexResult<ArrayRef> {
70 let child_result = expr.child.unchecked_evaluate(scope)?;
71 invert(&child_result)
72 }
73
74 fn return_dtype(expr: &Self::Expr, scope: &DType) -> VortexResult<DType> {
75 let child = expr.child.return_dtype(scope)?;
76 if !matches!(child, DType::Bool(_)) {
77 vortex_bail!("Not expression expects a boolean child, got: {}", child);
78 }
79 Ok(child)
80 }
81}
82
83impl NotExpr {
84 pub fn new(child: ExprRef) -> Self {
85 Self { child }
86 }
87
88 pub fn new_expr(child: ExprRef) -> ExprRef {
89 Self::new(child).into_expr()
90 }
91
92 pub fn child(&self) -> &ExprRef {
93 &self.child
94 }
95}
96
97impl DisplayAs for NotExpr {
98 fn fmt_as(&self, df: DisplayFormat, f: &mut std::fmt::Formatter) -> std::fmt::Result {
99 match df {
100 DisplayFormat::Compact => {
101 write!(f, "(!{})", self.child)
102 }
103 DisplayFormat::Tree => {
104 write!(f, "Not")
105 }
106 }
107 }
108}
109
110impl AnalysisExpr for NotExpr {}
111
112pub fn not(operand: ExprRef) -> ExprRef {
121 NotExpr::new(operand).into_expr()
122}
123
124#[cfg(test)]
125mod tests {
126 use vortex_array::ToCanonical;
127 use vortex_array::arrays::BoolArray;
128 use vortex_dtype::{DType, Nullability};
129
130 use crate::{Scope, col, get_item, not, root, test_harness};
131
132 #[test]
133 fn invert_booleans() {
134 let not_expr = not(root());
135 let bools = BoolArray::from_iter([false, true, false, false, true, true]);
136 assert_eq!(
137 not_expr
138 .evaluate(&Scope::new(bools.to_array()))
139 .unwrap()
140 .to_bool()
141 .boolean_buffer()
142 .iter()
143 .collect::<Vec<_>>(),
144 vec![true, false, true, true, false, false]
145 );
146 }
147
148 #[test]
149 fn test_display_order_of_operations() {
150 let a = not(get_item("a", root()));
151 let b = get_item("a", not(root()));
152 assert_ne!(a.to_string(), b.to_string());
153 assert_eq!(a.to_string(), "(!$.a)");
154 assert_eq!(b.to_string(), "(!$).a");
155 }
156
157 #[test]
158 fn dtype() {
159 let not_expr = not(root());
160 let dtype = DType::Bool(Nullability::NonNullable);
161 assert_eq!(
162 not_expr.return_dtype(&dtype).unwrap(),
163 DType::Bool(Nullability::NonNullable)
164 );
165
166 let dtype = test_harness::struct_dtype();
167 assert_eq!(
168 not(col("bool1")).return_dtype(&dtype).unwrap(),
169 DType::Bool(Nullability::NonNullable)
170 );
171 }
172}