vortex_array/arrays/constant/compute/
boolean.rs1use vortex_dtype::DType;
2use vortex_error::{VortexResult, vortex_bail, vortex_err};
3use vortex_scalar::Scalar;
4
5use crate::arrays::{ConstantArray, ConstantEncoding};
6use crate::compute::{BinaryBooleanFn, BinaryOperator};
7use crate::{Array, ArrayRef};
8
9impl BinaryBooleanFn<&ConstantArray> for ConstantEncoding {
10 fn binary_boolean(
11 &self,
12 lhs: &ConstantArray,
13 rhs: &dyn Array,
14 op: BinaryOperator,
15 ) -> VortexResult<Option<ArrayRef>> {
16 if !rhs.is_constant() {
19 return Ok(None);
20 }
21
22 let length = lhs.len();
23 let nullable = lhs.dtype().is_nullable() || rhs.dtype().is_nullable();
24 let lhs = lhs.scalar().as_bool().value();
25 let Some(rhs) = rhs.as_constant() else {
26 vortex_bail!("Binary boolean operation requires both sides to be constant");
27 };
28 let rhs = rhs
29 .as_bool_opt()
30 .ok_or_else(|| vortex_err!("expected rhs to be boolean"))?
31 .value();
32
33 let result = match op {
34 BinaryOperator::And => and(lhs, rhs),
35 BinaryOperator::AndKleene => kleene_and(lhs, rhs),
36 BinaryOperator::Or => or(lhs, rhs),
37 BinaryOperator::OrKleene => kleene_or(lhs, rhs),
38 };
39
40 let scalar = result
41 .map(|b| Scalar::bool(b, nullable.into()))
42 .unwrap_or_else(|| Scalar::null(DType::Bool(nullable.into())));
43
44 Ok(Some(ConstantArray::new(scalar, length).into_array()))
45 }
46}
47
48fn and(left: Option<bool>, right: Option<bool>) -> Option<bool> {
49 left.zip(right).map(|(l, r)| l & r)
50}
51
52fn kleene_and(left: Option<bool>, right: Option<bool>) -> Option<bool> {
53 match (left, right) {
54 (Some(false), _) => Some(false),
55 (_, Some(false)) => Some(false),
56 (None, _) => None,
57 (_, None) => None,
58 (Some(l), Some(r)) => Some(l & r),
59 }
60}
61
62fn or(left: Option<bool>, right: Option<bool>) -> Option<bool> {
63 left.zip(right).map(|(l, r)| l | r)
64}
65
66fn kleene_or(left: Option<bool>, right: Option<bool>) -> Option<bool> {
67 match (left, right) {
68 (Some(true), _) => Some(true),
69 (_, Some(true)) => Some(true),
70 (None, _) => None,
71 (_, None) => None,
72 (Some(l), Some(r)) => Some(l | r),
73 }
74}
75
76#[cfg(test)]
77mod test {
78 use rstest::rstest;
79
80 use crate::arrays::BoolArray;
81 use crate::arrays::constant::ConstantArray;
82 use crate::canonical::ToCanonical;
83 use crate::compute::{and, or, scalar_at};
84 use crate::{Array, ArrayRef};
85
86 #[rstest]
87 #[case(ConstantArray::new(true, 4).into_array(), BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)].into_iter()).into_array()
88 )]
89 #[case(BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)].into_iter()).into_array(), ConstantArray::new(true, 4).into_array()
90 )]
91 fn test_or(#[case] lhs: ArrayRef, #[case] rhs: ArrayRef) {
92 let r = or(&lhs, &rhs).unwrap().to_bool().unwrap().into_array();
93
94 let v0 = scalar_at(&r, 0).unwrap().as_bool().value();
95 let v1 = scalar_at(&r, 1).unwrap().as_bool().value();
96 let v2 = scalar_at(&r, 2).unwrap().as_bool().value();
97 let v3 = scalar_at(&r, 3).unwrap().as_bool().value();
98
99 assert!(v0.unwrap());
100 assert!(v1.unwrap());
101 assert!(v2.unwrap());
102 assert!(v3.unwrap());
103 }
104
105 #[rstest]
106 #[case(ConstantArray::new(true, 4).into_array(), BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)].into_iter()).into_array()
107 )]
108 #[case(BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)].into_iter()).into_array(),
109 ConstantArray::new(true, 4).into_array())]
110 fn test_and(#[case] lhs: ArrayRef, #[case] rhs: ArrayRef) {
111 let r = and(&lhs, &rhs).unwrap().to_bool().unwrap().into_array();
112
113 let v0 = scalar_at(&r, 0).unwrap().as_bool().value();
114 let v1 = scalar_at(&r, 1).unwrap().as_bool().value();
115 let v2 = scalar_at(&r, 2).unwrap().as_bool().value();
116 let v3 = scalar_at(&r, 3).unwrap().as_bool().value();
117
118 assert!(v0.unwrap());
119 assert!(!v1.unwrap());
120 assert!(v2.unwrap());
121 assert!(!v3.unwrap());
122 }
123}