vortex_array/arrays/bool/compute/
min_max.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use std::ops::BitAnd;
5
6use Nullability::NonNullable;
7use vortex_dtype::Nullability;
8use vortex_error::VortexResult;
9use vortex_mask::Mask;
10use vortex_scalar::Scalar;
11
12use crate::arrays::{BoolArray, BoolVTable};
13use crate::compute::{MinMaxKernel, MinMaxKernelAdapter, MinMaxResult};
14use crate::register_kernel;
15
16impl MinMaxKernel for BoolVTable {
17    fn min_max(&self, array: &BoolArray) -> VortexResult<Option<MinMaxResult>> {
18        let mask = array.validity_mask();
19        let true_non_null = match mask {
20            Mask::AllTrue(_) => array.bit_buffer().clone(),
21            Mask::AllFalse(_) => return Ok(None),
22            Mask::Values(ref v) => array.bit_buffer().bitand(v.bit_buffer()),
23        };
24
25        // TODO(ngates): we should be able to bail out earlier as soon as we have one true and
26        //  one false value.
27        let mut true_slices = true_non_null.set_slices();
28        // If there are no slices, then all values are false
29        // if there is a single slice that covers the entire array, then all values are true
30        // otherwise, we have a mix of true and false values
31
32        let Some(slice) = true_slices.next() else {
33            // all false
34            return Ok(Some(MinMaxResult {
35                min: Scalar::bool(false, NonNullable),
36                max: Scalar::bool(false, NonNullable),
37            }));
38        };
39        if slice.0 == 0 && slice.1 == array.len() {
40            // all true
41            return Ok(Some(MinMaxResult {
42                min: Scalar::bool(true, NonNullable),
43                max: Scalar::bool(true, NonNullable),
44            }));
45        };
46
47        // If the non null true slice doesn't cover the whole array we need to check for valid false values
48        match mask {
49            // if the mask is all true or all false we don't need to look for false values
50            Mask::AllTrue(_) | Mask::AllFalse(_) => {}
51            Mask::Values(v) => {
52                let false_non_null = (!array.bit_buffer()).bitand(v.bit_buffer());
53                let mut false_slices = false_non_null.set_slices();
54
55                let Some(_) = false_slices.next() else {
56                    // In this case we don't have any false values which means we are all true and null
57                    return Ok(Some(MinMaxResult {
58                        min: Scalar::bool(true, NonNullable),
59                        max: Scalar::bool(true, NonNullable),
60                    }));
61                };
62            }
63        }
64
65        Ok(Some(MinMaxResult {
66            min: Scalar::bool(false, NonNullable),
67            max: Scalar::bool(true, NonNullable),
68        }))
69    }
70}
71
72register_kernel!(MinMaxKernelAdapter(BoolVTable).lift());
73
74#[cfg(test)]
75mod tests {
76    use Nullability::NonNullable;
77    use vortex_dtype::Nullability;
78    use vortex_scalar::Scalar;
79
80    use crate::arrays::BoolArray;
81    use crate::compute::{MinMaxResult, min_max};
82
83    #[test]
84    fn test_min_max_nulls() {
85        assert_eq!(
86            min_max(BoolArray::from_iter(vec![Some(true), Some(true), None, None]).as_ref())
87                .unwrap(),
88            Some(MinMaxResult {
89                min: Scalar::bool(true, NonNullable),
90                max: Scalar::bool(true, NonNullable),
91            })
92        );
93
94        assert_eq!(
95            min_max(BoolArray::from_iter(vec![None, Some(true), Some(true)]).as_ref()).unwrap(),
96            Some(MinMaxResult {
97                min: Scalar::bool(true, NonNullable),
98                max: Scalar::bool(true, NonNullable),
99            })
100        );
101
102        assert_eq!(
103            min_max(BoolArray::from_iter(vec![None, Some(true), Some(true), None]).as_ref())
104                .unwrap(),
105            Some(MinMaxResult {
106                min: Scalar::bool(true, NonNullable),
107                max: Scalar::bool(true, NonNullable),
108            })
109        );
110
111        assert_eq!(
112            min_max(BoolArray::from_iter(vec![Some(false), Some(false), None, None]).as_ref())
113                .unwrap(),
114            Some(MinMaxResult {
115                min: Scalar::bool(false, NonNullable),
116                max: Scalar::bool(false, NonNullable),
117            })
118        );
119    }
120}