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