Skip to main content

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_error::VortexResult;
8use vortex_mask::Mask;
9
10use crate::arrays::BoolArray;
11use crate::arrays::BoolVTable;
12use crate::compute::MinMaxKernel;
13use crate::compute::MinMaxKernelAdapter;
14use crate::compute::MinMaxResult;
15use crate::dtype::Nullability;
16use crate::register_kernel;
17use crate::scalar::Scalar;
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.to_bit_buffer(),
24            Mask::AllFalse(_) => return Ok(None),
25            Mask::Values(v) => array.to_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.to_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
81    use crate::IntoArray;
82    use crate::arrays::BoolArray;
83    use crate::compute::MinMaxResult;
84    use crate::compute::min_max;
85    use crate::dtype::Nullability;
86    use crate::scalar::Scalar;
87
88    #[test]
89    fn test_min_max_nulls() {
90        assert_eq!(
91            min_max(&BoolArray::from_iter(vec![Some(true), Some(true), None, None]).into_array())
92                .unwrap(),
93            Some(MinMaxResult {
94                min: Scalar::bool(true, NonNullable),
95                max: Scalar::bool(true, NonNullable),
96            })
97        );
98
99        assert_eq!(
100            min_max(&BoolArray::from_iter(vec![None, Some(true), Some(true)]).into_array())
101                .unwrap(),
102            Some(MinMaxResult {
103                min: Scalar::bool(true, NonNullable),
104                max: Scalar::bool(true, NonNullable),
105            })
106        );
107
108        assert_eq!(
109            min_max(&BoolArray::from_iter(vec![None, Some(true), Some(true), None]).into_array())
110                .unwrap(),
111            Some(MinMaxResult {
112                min: Scalar::bool(true, NonNullable),
113                max: Scalar::bool(true, NonNullable),
114            })
115        );
116
117        assert_eq!(
118            min_max(&BoolArray::from_iter(vec![Some(false), Some(false), None, None]).into_array())
119                .unwrap(),
120            Some(MinMaxResult {
121                min: Scalar::bool(false, NonNullable),
122                max: Scalar::bool(false, NonNullable),
123            })
124        );
125    }
126}