vortex_compute/logical/
binary.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4//! Simple binary logical operations: AND, OR, AND NOT.
5//!
6//! These operations apply a bitwise operation to the bits and AND the validity masks together
7//! (null propagates). For Kleene three-valued logic, see the [`kleene`](super::kleene) module.
8
9use std::ops::BitAnd;
10use std::ops::BitOr;
11
12use vortex_buffer::BitBuffer;
13use vortex_vector::BoolDatum;
14use vortex_vector::ScalarOps;
15use vortex_vector::VectorMutOps;
16use vortex_vector::VectorOps;
17use vortex_vector::bool::BoolScalar;
18use vortex_vector::bool::BoolVector;
19
20use super::LogicalAnd;
21use super::LogicalAndNot;
22use super::LogicalOp;
23use super::LogicalOr;
24
25/// Marker type for the AND operation.
26pub struct And;
27
28/// Marker type for the OR operation.
29pub struct Or;
30
31/// Marker type for the AND NOT operation.
32pub struct AndNot;
33
34/// Trait for simple logical binary operations.
35///
36/// These operations apply a bitwise operation to the bits and AND the validity masks together.
37pub trait LogicalBinaryOp {
38    /// Apply the operation to two [`BitBuffer`]s.
39    fn bit_op(lhs: &BitBuffer, rhs: &BitBuffer) -> BitBuffer;
40
41    /// Apply the operation to two scalar boolean values.
42    fn scalar_op(lhs: bool, rhs: bool) -> bool;
43}
44
45impl LogicalBinaryOp for And {
46    fn bit_op(lhs: &BitBuffer, rhs: &BitBuffer) -> BitBuffer {
47        lhs.bitand(rhs)
48    }
49
50    fn scalar_op(lhs: bool, rhs: bool) -> bool {
51        lhs && rhs
52    }
53}
54
55impl LogicalBinaryOp for Or {
56    fn bit_op(lhs: &BitBuffer, rhs: &BitBuffer) -> BitBuffer {
57        lhs.bitor(rhs)
58    }
59
60    fn scalar_op(lhs: bool, rhs: bool) -> bool {
61        lhs || rhs
62    }
63}
64
65impl LogicalBinaryOp for AndNot {
66    fn bit_op(lhs: &BitBuffer, rhs: &BitBuffer) -> BitBuffer {
67        lhs.bitand_not(rhs)
68    }
69
70    fn scalar_op(lhs: bool, rhs: bool) -> bool {
71        lhs && !rhs
72    }
73}
74
75////////////////////////////////////////////////////////////////////////////////////////////////////
76// Generic `LogicalOp` implementations
77////////////////////////////////////////////////////////////////////////////////////////////////////
78
79impl LogicalOp<And> for &BoolScalar {
80    type Output = BoolScalar;
81
82    fn op(self, rhs: &BoolScalar) -> BoolScalar {
83        binary_scalar_op::<And>(self, rhs)
84    }
85}
86
87impl LogicalOp<Or> for &BoolScalar {
88    type Output = BoolScalar;
89
90    fn op(self, rhs: &BoolScalar) -> BoolScalar {
91        binary_scalar_op::<Or>(self, rhs)
92    }
93}
94
95impl LogicalOp<AndNot> for &BoolScalar {
96    type Output = BoolScalar;
97
98    fn op(self, rhs: &BoolScalar) -> BoolScalar {
99        binary_scalar_op::<AndNot>(self, rhs)
100    }
101}
102
103impl LogicalOp<And> for &BoolVector {
104    type Output = BoolVector;
105
106    fn op(self, rhs: &BoolVector) -> BoolVector {
107        binary_vector_op::<And>(self, rhs)
108    }
109}
110
111impl LogicalOp<Or> for &BoolVector {
112    type Output = BoolVector;
113
114    fn op(self, rhs: &BoolVector) -> BoolVector {
115        binary_vector_op::<Or>(self, rhs)
116    }
117}
118
119impl LogicalOp<AndNot> for &BoolVector {
120    type Output = BoolVector;
121
122    fn op(self, rhs: &BoolVector) -> BoolVector {
123        binary_vector_op::<AndNot>(self, rhs)
124    }
125}
126
127impl LogicalOp<And, &BoolDatum> for &BoolDatum {
128    type Output = BoolDatum;
129
130    fn op(self, rhs: &BoolDatum) -> BoolDatum {
131        binary_datum_op::<And>(self, rhs)
132    }
133}
134
135impl LogicalOp<Or, &BoolDatum> for &BoolDatum {
136    type Output = BoolDatum;
137
138    fn op(self, rhs: &BoolDatum) -> BoolDatum {
139        binary_datum_op::<Or>(self, rhs)
140    }
141}
142
143impl LogicalOp<AndNot, &BoolDatum> for &BoolDatum {
144    type Output = BoolDatum;
145
146    fn op(self, rhs: &BoolDatum) -> BoolDatum {
147        binary_datum_op::<AndNot>(self, rhs)
148    }
149}
150
151////////////////////////////////////////////////////////////////////////////////////////////////////
152// Logical helper functions
153////////////////////////////////////////////////////////////////////////////////////////////////////
154
155fn binary_scalar_op<Op: LogicalBinaryOp>(lhs: &BoolScalar, rhs: &BoolScalar) -> BoolScalar {
156    let result = match (lhs.value(), rhs.value()) {
157        (Some(a), Some(b)) => Some(Op::scalar_op(a, b)),
158        _ => None, // Null propagation.
159    };
160    BoolScalar::new(result)
161}
162
163fn binary_vector_op<Op: LogicalBinaryOp>(lhs: &BoolVector, rhs: &BoolVector) -> BoolVector {
164    assert_eq!(lhs.len(), rhs.len());
165
166    BoolVector::new(
167        Op::bit_op(lhs.bits(), rhs.bits()),
168        lhs.validity().bitand(rhs.validity()),
169    )
170}
171
172fn binary_datum_op<Op: LogicalBinaryOp>(lhs: &BoolDatum, rhs: &BoolDatum) -> BoolDatum
173where
174    for<'a> &'a BoolScalar: LogicalOp<Op, Output = BoolScalar>,
175    for<'a> &'a BoolVector: LogicalOp<Op, Output = BoolVector>,
176{
177    match (lhs, rhs) {
178        (BoolDatum::Vector(lhs), BoolDatum::Vector(rhs)) => {
179            BoolDatum::Vector(<&BoolVector as LogicalOp<Op>>::op(lhs, rhs))
180        }
181        (BoolDatum::Scalar(lhs), BoolDatum::Scalar(rhs)) => {
182            BoolDatum::Scalar(<&BoolScalar as LogicalOp<Op>>::op(lhs, rhs))
183        }
184        (BoolDatum::Scalar(sc), BoolDatum::Vector(vec)) => {
185            let expanded = sc.repeat(vec.len()).freeze().into_bool();
186            BoolDatum::Vector(<&BoolVector as LogicalOp<Op>>::op(&expanded, vec))
187        }
188        (BoolDatum::Vector(vec), BoolDatum::Scalar(sc)) => {
189            let expanded = sc.repeat(vec.len()).freeze().into_bool();
190            BoolDatum::Vector(<&BoolVector as LogicalOp<Op>>::op(vec, &expanded))
191        }
192    }
193}
194
195////////////////////////////////////////////////////////////////////////////////////////////////////
196// Convenience trait implementations
197////////////////////////////////////////////////////////////////////////////////////////////////////
198
199impl LogicalAnd for &BoolScalar {
200    type Output = BoolScalar;
201
202    fn and(self, other: &BoolScalar) -> BoolScalar {
203        binary_scalar_op::<And>(self, other)
204    }
205}
206
207impl LogicalAnd for &BoolVector {
208    type Output = BoolVector;
209
210    fn and(self, other: &BoolVector) -> BoolVector {
211        binary_vector_op::<And>(self, other)
212    }
213}
214
215impl LogicalAnd<&BoolDatum> for &BoolDatum {
216    type Output = BoolDatum;
217
218    fn and(self, other: &BoolDatum) -> BoolDatum {
219        <&BoolDatum as LogicalOp<And, &BoolDatum>>::op(self, other)
220    }
221}
222
223impl LogicalOr for &BoolScalar {
224    type Output = BoolScalar;
225
226    fn or(self, other: &BoolScalar) -> BoolScalar {
227        binary_scalar_op::<Or>(self, other)
228    }
229}
230
231impl LogicalOr for &BoolVector {
232    type Output = BoolVector;
233
234    fn or(self, other: &BoolVector) -> BoolVector {
235        binary_vector_op::<Or>(self, other)
236    }
237}
238
239impl LogicalOr<&BoolDatum> for &BoolDatum {
240    type Output = BoolDatum;
241
242    fn or(self, other: &BoolDatum) -> BoolDatum {
243        <&BoolDatum as LogicalOp<Or, &BoolDatum>>::op(self, other)
244    }
245}
246
247impl LogicalAndNot for &BoolScalar {
248    type Output = BoolScalar;
249
250    fn and_not(self, other: &BoolScalar) -> BoolScalar {
251        binary_scalar_op::<AndNot>(self, other)
252    }
253}
254
255impl LogicalAndNot for &BoolVector {
256    type Output = BoolVector;
257
258    fn and_not(self, other: &BoolVector) -> BoolVector {
259        binary_vector_op::<AndNot>(self, other)
260    }
261}
262
263impl LogicalAndNot<&BoolDatum> for &BoolDatum {
264    type Output = BoolDatum;
265
266    fn and_not(self, other: &BoolDatum) -> BoolDatum {
267        <&BoolDatum as LogicalOp<AndNot, &BoolDatum>>::op(self, other)
268    }
269}
270
271#[cfg(test)]
272mod tests {
273    use vortex_buffer::bitbuffer;
274    use vortex_mask::Mask;
275    use vortex_vector::bool::BoolScalar;
276    use vortex_vector::bool::BoolVector;
277
278    use super::*;
279
280    // AND tests.
281
282    #[test]
283    fn test_and_basic() {
284        let left = BoolVector::new(bitbuffer![1 1 0 0], Mask::new_true(4));
285        let right = BoolVector::new(bitbuffer![1 0 1 0], Mask::new_true(4));
286
287        let result = left.and(&right);
288        assert_eq!(result.bits(), &bitbuffer![1 0 0 0]);
289    }
290
291    #[test]
292    fn test_and_with_nulls() {
293        let left = BoolVector::new(bitbuffer![1 0], Mask::from(bitbuffer![1 0]));
294        let right = BoolVector::new(bitbuffer![1 1], Mask::new_true(2));
295
296        let result = left.and(&right);
297        // Validity is AND'd, so if either side is null, result is null.
298        assert_eq!(result.validity(), &Mask::from(bitbuffer![1 0]));
299    }
300
301    #[test]
302    fn test_and_scalar() {
303        let left = BoolScalar::new(Some(true));
304        let right = BoolScalar::new(Some(false));
305        assert_eq!((&left).and(&right).value(), Some(false));
306
307        let left = BoolScalar::new(Some(true));
308        let right = BoolScalar::new(Some(true));
309        assert_eq!((&left).and(&right).value(), Some(true));
310
311        let left = BoolScalar::new(Some(true));
312        let right = BoolScalar::new(None);
313        assert_eq!((&left).and(&right).value(), None);
314    }
315
316    // OR tests.
317
318    #[test]
319    fn test_or_basic() {
320        let left = BoolVector::new(bitbuffer![1 1 0 0], Mask::new_true(4));
321        let right = BoolVector::new(bitbuffer![1 0 1 0], Mask::new_true(4));
322
323        let result = left.or(&right);
324        assert_eq!(result.bits(), &bitbuffer![1 1 1 0]);
325    }
326
327    #[test]
328    fn test_or_with_nulls() {
329        let left = BoolVector::new(bitbuffer![0 1], Mask::from(bitbuffer![0 1]));
330        let right = BoolVector::new(bitbuffer![0 0], Mask::new_true(2));
331
332        let result = left.or(&right);
333        // Validity is AND'd, so if either side is null, result is null.
334        assert_eq!(result.validity(), &Mask::from(bitbuffer![0 1]));
335    }
336
337    #[test]
338    fn test_or_scalar() {
339        let left = BoolScalar::new(Some(true));
340        let right = BoolScalar::new(Some(false));
341        assert_eq!((&left).or(&right).value(), Some(true));
342
343        let left = BoolScalar::new(Some(false));
344        let right = BoolScalar::new(Some(false));
345        assert_eq!((&left).or(&right).value(), Some(false));
346
347        let left = BoolScalar::new(Some(false));
348        let right = BoolScalar::new(None);
349        assert_eq!((&left).or(&right).value(), None);
350    }
351
352    // AND NOT tests.
353
354    #[test]
355    fn test_and_not_basic() {
356        // left AND (NOT right).
357        let left = BoolVector::new(bitbuffer![1 1 0 0], Mask::new_true(4));
358        let right = BoolVector::new(bitbuffer![1 0 1 0], Mask::new_true(4));
359
360        let result = left.and_not(&right);
361        // 1 & !1 = 0, 1 & !0 = 1, 0 & !1 = 0, 0 & !0 = 0.
362        assert_eq!(result.bits(), &bitbuffer![0 1 0 0]);
363    }
364
365    #[test]
366    fn test_and_not_all_true() {
367        let left = BoolVector::new(bitbuffer![1 1], Mask::new_true(2));
368        let right = BoolVector::new(bitbuffer![1 1], Mask::new_true(2));
369
370        let result = left.and_not(&right);
371        assert_eq!(result.bits(), &bitbuffer![0 0]);
372    }
373
374    #[test]
375    fn test_and_not_scalar() {
376        let left = BoolScalar::new(Some(true));
377        let right = BoolScalar::new(Some(true));
378        assert_eq!((&left).and_not(&right).value(), Some(false));
379
380        let left = BoolScalar::new(Some(true));
381        let right = BoolScalar::new(Some(false));
382        assert_eq!((&left).and_not(&right).value(), Some(true));
383
384        let left = BoolScalar::new(Some(true));
385        let right = BoolScalar::new(None);
386        assert_eq!((&left).and_not(&right).value(), None);
387    }
388
389    // Datum tests.
390
391    #[test]
392    fn test_datum_and_vector_vector() {
393        let left = BoolDatum::Vector(BoolVector::new(bitbuffer![1 1 0 0], Mask::new_true(4)));
394        let right = BoolDatum::Vector(BoolVector::new(bitbuffer![1 0 1 0], Mask::new_true(4)));
395
396        let result = left.and(&right);
397        let BoolDatum::Vector(vec) = result else {
398            panic!("Expected Vector");
399        };
400        assert_eq!(vec.bits(), &bitbuffer![1 0 0 0]);
401    }
402
403    #[test]
404    fn test_datum_and_scalar_scalar() {
405        let left = BoolDatum::Scalar(BoolScalar::new(Some(true)));
406        let right = BoolDatum::Scalar(BoolScalar::new(Some(false)));
407
408        let result = left.and(&right);
409        let BoolDatum::Scalar(sc) = result else {
410            panic!("Expected Scalar");
411        };
412        assert_eq!(sc.value(), Some(false));
413    }
414
415    #[test]
416    fn test_datum_and_scalar_vector() {
417        let left = BoolDatum::Scalar(BoolScalar::new(Some(true)));
418        let right = BoolDatum::Vector(BoolVector::new(bitbuffer![1 0 1 0], Mask::new_true(4)));
419
420        let result = left.and(&right);
421        let BoolDatum::Vector(vec) = result else {
422            panic!("Expected Vector");
423        };
424        assert_eq!(vec.bits(), &bitbuffer![1 0 1 0]);
425    }
426
427    #[test]
428    fn test_datum_or_vector_scalar() {
429        let left = BoolDatum::Vector(BoolVector::new(bitbuffer![1 0 1 0], Mask::new_true(4)));
430        let right = BoolDatum::Scalar(BoolScalar::new(Some(true)));
431
432        let result = left.or(&right);
433        let BoolDatum::Vector(vec) = result else {
434            panic!("Expected Vector");
435        };
436        assert_eq!(vec.bits(), &bitbuffer![1 1 1 1]);
437    }
438}