1use std::any::Any;
2use std::fmt::Display;
3use std::hash::Hash;
4use std::sync::Arc;
5
6use vortex_array::ArrayRef;
7use vortex_array::compute::invert;
8use vortex_dtype::DType;
9use vortex_error::VortexResult;
10
11use crate::{AnalysisExpr, ExprRef, Scope, ScopeDType, VortexExpr};
12
13#[derive(Debug, Eq, Hash)]
14#[allow(clippy::derived_hash_with_manual_eq)]
16pub struct Not {
17 child: ExprRef,
18}
19
20impl Not {
21 pub fn new_expr(child: ExprRef) -> ExprRef {
22 Arc::new(Self { child })
23 }
24
25 pub fn child(&self) -> &ExprRef {
26 &self.child
27 }
28}
29
30impl Display for Not {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 write!(f, "!")?;
33 self.child.fmt(f)
34 }
35}
36
37#[cfg(feature = "proto")]
38pub(crate) mod proto {
39 use expr::kind;
40 use vortex_error::VortexResult;
41 use vortex_proto::expr;
42 use vortex_proto::expr::kind::Kind;
43
44 use crate::{ExprDeserialize, ExprRef, ExprSerializable, Id, Not};
45
46 pub struct NotSerde;
47
48 impl Id for NotSerde {
49 fn id(&self) -> &'static str {
50 "not"
51 }
52 }
53
54 impl ExprDeserialize for NotSerde {
55 fn deserialize(&self, _expr: &Kind, mut children: Vec<ExprRef>) -> VortexResult<ExprRef> {
56 Ok(Not::new_expr(children.remove(0)))
57 }
58 }
59
60 impl ExprSerializable for Not {
61 fn id(&self) -> &'static str {
62 NotSerde.id()
63 }
64
65 fn serialize_kind(&self) -> VortexResult<Kind> {
66 Ok(Kind::Not(kind::Not {}))
67 }
68 }
69}
70
71impl AnalysisExpr for Not {}
72
73impl VortexExpr for Not {
74 fn as_any(&self) -> &dyn Any {
75 self
76 }
77
78 fn unchecked_evaluate(&self, scope: &Scope) -> VortexResult<ArrayRef> {
79 let child_result = self.child.unchecked_evaluate(scope)?;
80 invert(&child_result)
81 }
82
83 fn children(&self) -> Vec<&ExprRef> {
84 vec![&self.child]
85 }
86
87 fn replacing_children(self: Arc<Self>, mut children: Vec<ExprRef>) -> ExprRef {
88 assert_eq!(children.len(), 1);
89 Self::new_expr(children.remove(0))
90 }
91
92 fn return_dtype(&self, scope: &ScopeDType) -> VortexResult<DType> {
93 self.child.return_dtype(scope)
94 }
95}
96
97impl PartialEq for Not {
98 fn eq(&self, other: &Not) -> bool {
99 other.child.eq(&self.child)
100 }
101}
102
103pub fn not(operand: ExprRef) -> ExprRef {
104 Not::new_expr(operand)
105}
106
107#[cfg(test)]
108mod tests {
109 use vortex_array::ToCanonical;
110 use vortex_array::arrays::BoolArray;
111 use vortex_dtype::{DType, Nullability};
112
113 use crate::{Scope, ScopeDType, col, not, root, test_harness};
114
115 #[test]
116 fn invert_booleans() {
117 let not_expr = not(root());
118 let bools = BoolArray::from_iter([false, true, false, false, true, true]);
119 assert_eq!(
120 not_expr
121 .evaluate(&Scope::new(bools.to_array()))
122 .unwrap()
123 .to_bool()
124 .unwrap()
125 .boolean_buffer()
126 .iter()
127 .collect::<Vec<_>>(),
128 vec![true, false, true, true, false, false]
129 );
130 }
131
132 #[test]
133 fn dtype() {
134 let not_expr = not(root());
135 let dtype = DType::Bool(Nullability::NonNullable);
136 assert_eq!(
137 not_expr.return_dtype(&ScopeDType::new(dtype)).unwrap(),
138 DType::Bool(Nullability::NonNullable)
139 );
140
141 let dtype = test_harness::struct_dtype();
142 assert_eq!(
143 not(col("bool1"))
144 .return_dtype(&ScopeDType::new(dtype))
145 .unwrap(),
146 DType::Bool(Nullability::NonNullable)
147 );
148 }
149}