Skip to main content

vortex_array/scalar_fn/fns/not/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4mod kernel;
5
6pub use kernel::*;
7use vortex_error::VortexResult;
8use vortex_error::vortex_bail;
9use vortex_session::VortexSession;
10
11use crate::ArrayRef;
12use crate::ExecutionCtx;
13use crate::IntoArray;
14use crate::arrays::Bool;
15use crate::arrays::BoolArray;
16use crate::arrays::ConstantArray;
17use crate::arrays::bool::BoolArrayExt;
18use crate::builtins::ArrayBuiltins;
19use crate::dtype::DType;
20use crate::scalar::Scalar;
21use crate::scalar_fn::Arity;
22use crate::scalar_fn::ChildName;
23use crate::scalar_fn::EmptyOptions;
24use crate::scalar_fn::ExecutionArgs;
25use crate::scalar_fn::ScalarFnId;
26use crate::scalar_fn::ScalarFnVTable;
27
28/// Expression that logically inverts boolean values.
29#[derive(Clone)]
30pub struct Not;
31
32impl ScalarFnVTable for Not {
33    type Options = EmptyOptions;
34
35    fn id(&self) -> ScalarFnId {
36        ScalarFnId::new("vortex.not")
37    }
38
39    fn serialize(&self, _options: &Self::Options) -> VortexResult<Option<Vec<u8>>> {
40        Ok(Some(vec![]))
41    }
42
43    fn deserialize(
44        &self,
45        _metadata: &[u8],
46        _session: &VortexSession,
47    ) -> VortexResult<Self::Options> {
48        Ok(EmptyOptions)
49    }
50
51    fn arity(&self, _options: &Self::Options) -> Arity {
52        Arity::Exact(1)
53    }
54
55    fn child_name(&self, _options: &Self::Options, child_idx: usize) -> ChildName {
56        match child_idx {
57            0 => ChildName::from("input"),
58            _ => unreachable!("Invalid child index {} for Not expression", child_idx),
59        }
60    }
61
62    fn return_dtype(&self, _options: &Self::Options, arg_dtypes: &[DType]) -> VortexResult<DType> {
63        let child_dtype = &arg_dtypes[0];
64        if !matches!(child_dtype, DType::Bool(_)) {
65            vortex_bail!(
66                "Not expression expects a boolean child, got: {}",
67                child_dtype
68            );
69        }
70        Ok(child_dtype.clone())
71    }
72
73    fn execute(
74        &self,
75        _data: &Self::Options,
76        args: &dyn ExecutionArgs,
77        ctx: &mut ExecutionCtx,
78    ) -> VortexResult<ArrayRef> {
79        let child = args.get(0)?;
80
81        // For constant boolean
82        if let Some(scalar) = child.as_constant() {
83            let value = match scalar.as_bool().value() {
84                Some(b) => Scalar::bool(!b, child.dtype().nullability()),
85                None => Scalar::null(child.dtype().clone()),
86            };
87            return Ok(ConstantArray::new(value, args.row_count()).into_array());
88        }
89
90        // For boolean array
91        if let Some(bool) = child.as_opt::<Bool>() {
92            return Ok(BoolArray::new(!bool.to_bit_buffer(), bool.validity()?).into_array());
93        }
94
95        // Otherwise, execute and try again
96        child.execute::<ArrayRef>(ctx)?.not()
97    }
98
99    fn is_null_sensitive(&self, _options: &Self::Options) -> bool {
100        false
101    }
102
103    fn is_fallible(&self, _options: &Self::Options) -> bool {
104        false
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use crate::IntoArray;
111    #[expect(deprecated)]
112    use crate::ToCanonical as _;
113    use crate::arrays::bool::BoolArrayExt;
114    use crate::dtype::DType;
115    use crate::dtype::Nullability;
116    use crate::expr::col;
117    use crate::expr::get_item;
118    use crate::expr::not;
119    use crate::expr::root;
120    use crate::expr::test_harness;
121    use crate::scalar_fn::fns::not::BoolArray;
122
123    #[test]
124    fn invert_booleans() {
125        let not_expr = not(root());
126        let bools = BoolArray::from_iter([false, true, false, false, true, true]);
127        #[expect(deprecated)]
128        let result = bools.into_array().apply(&not_expr).unwrap().to_bool();
129        assert_eq!(
130            result.to_bit_buffer().iter().collect::<Vec<_>>(),
131            vec![true, false, true, true, false, false]
132        );
133    }
134
135    #[test]
136    fn test_display_order_of_operations() {
137        let a = not(get_item("a", root()));
138        let b = get_item("a", not(root()));
139        assert_ne!(a.to_string(), b.to_string());
140        assert_eq!(a.to_string(), "vortex.not($.a)");
141        assert_eq!(b.to_string(), "vortex.not($).a");
142    }
143
144    #[test]
145    fn dtype() {
146        let not_expr = not(root());
147        let dtype = DType::Bool(Nullability::NonNullable);
148        assert_eq!(
149            not_expr.return_dtype(&dtype).unwrap(),
150            DType::Bool(Nullability::NonNullable)
151        );
152
153        let dtype = test_harness::struct_dtype();
154        assert_eq!(
155            not(col("bool1")).return_dtype(&dtype).unwrap(),
156            DType::Bool(Nullability::NonNullable)
157        );
158    }
159}