vortex_array/arrays/null/
compute.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use vortex_dtype::match_each_integer_ptype;
5use vortex_error::{VortexResult, vortex_bail};
6use vortex_mask::Mask;
7
8use crate::arrays::NullVTable;
9use crate::arrays::null::NullArray;
10use crate::compute::{
11    FilterKernel, FilterKernelAdapter, MaskKernel, MaskKernelAdapter, MinMaxKernel,
12    MinMaxKernelAdapter, MinMaxResult, TakeKernel, TakeKernelAdapter,
13};
14use crate::{Array, ArrayRef, IntoArray, ToCanonical, register_kernel};
15
16impl FilterKernel for NullVTable {
17    fn filter(&self, _array: &Self::Array, mask: &Mask) -> VortexResult<ArrayRef> {
18        Ok(NullArray::new(mask.true_count()).into_array())
19    }
20}
21
22register_kernel!(FilterKernelAdapter(NullVTable).lift());
23
24impl MaskKernel for NullVTable {
25    fn mask(&self, array: &NullArray, _mask: &Mask) -> VortexResult<ArrayRef> {
26        Ok(array.to_array())
27    }
28}
29
30register_kernel!(MaskKernelAdapter(NullVTable).lift());
31
32impl TakeKernel for NullVTable {
33    #[allow(clippy::cast_possible_truncation)]
34    fn take(&self, array: &NullArray, indices: &dyn Array) -> VortexResult<ArrayRef> {
35        let indices = indices.to_primitive()?;
36
37        // Enforce all indices are valid
38        match_each_integer_ptype!(indices.ptype(), |T| {
39            for index in indices.as_slice::<T>() {
40                if (*index as usize) >= array.len() {
41                    vortex_bail!(OutOfBounds: *index as usize, 0, array.len());
42                }
43            }
44        });
45
46        Ok(NullArray::new(indices.len()).into_array())
47    }
48}
49
50register_kernel!(TakeKernelAdapter(NullVTable).lift());
51
52impl MinMaxKernel for NullVTable {
53    fn min_max(&self, _array: &NullArray) -> VortexResult<Option<MinMaxResult>> {
54        Ok(None)
55    }
56}
57
58register_kernel!(MinMaxKernelAdapter(NullVTable).lift());
59
60#[cfg(test)]
61mod test {
62    use vortex_buffer::buffer;
63    use vortex_dtype::DType;
64    use vortex_mask::Mask;
65
66    use crate::arrays::null::NullArray;
67    use crate::compute::conformance::filter::test_filter_conformance;
68    use crate::compute::conformance::mask::test_mask_conformance;
69    use crate::compute::conformance::take::test_take_conformance;
70    use crate::compute::take;
71    use crate::{IntoArray, ToCanonical};
72
73    #[test]
74    fn test_slice_nulls() {
75        let nulls = NullArray::new(10);
76        let sliced = nulls.slice(0, 4).unwrap().to_null().unwrap();
77
78        assert_eq!(sliced.len(), 4);
79        assert!(matches!(sliced.validity_mask().unwrap(), Mask::AllFalse(4)));
80    }
81
82    #[test]
83    fn test_take_nulls() {
84        let nulls = NullArray::new(10);
85        let taken = take(nulls.as_ref(), &buffer![0u64, 2, 4, 6, 8].into_array())
86            .unwrap()
87            .to_null()
88            .unwrap();
89
90        assert_eq!(taken.len(), 5);
91        assert!(matches!(taken.validity_mask().unwrap(), Mask::AllFalse(5)));
92    }
93
94    #[test]
95    fn test_scalar_at_nulls() {
96        let nulls = NullArray::new(10);
97
98        let scalar = nulls.scalar_at(0).unwrap();
99        assert!(scalar.is_null());
100        assert_eq!(scalar.dtype().clone(), DType::Null);
101    }
102
103    #[test]
104    fn test_filter_null_array() {
105        test_filter_conformance(NullArray::new(5).as_ref());
106        test_filter_conformance(NullArray::new(1).as_ref());
107        test_filter_conformance(NullArray::new(10).as_ref());
108    }
109
110    #[test]
111    fn test_mask_null_array() {
112        test_mask_conformance(NullArray::new(5).as_ref());
113    }
114
115    #[test]
116    fn test_take_null_array_conformance() {
117        test_take_conformance(NullArray::new(5).as_ref());
118        test_take_conformance(NullArray::new(1).as_ref());
119        test_take_conformance(NullArray::new(10).as_ref());
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use rstest::rstest;
126
127    use crate::arrays::NullArray;
128    use crate::compute::conformance::consistency::test_array_consistency;
129
130    #[rstest]
131    // From test_all_consistency
132    #[case::null_array_small(NullArray::new(5))]
133    #[case::null_array_medium(NullArray::new(100))]
134    // Additional test cases
135    #[case::null_array_single(NullArray::new(1))]
136    #[case::null_array_large(NullArray::new(1000))]
137    #[case::null_array_empty(NullArray::new(0))]
138    fn test_null_consistency(#[case] array: NullArray) {
139        test_array_consistency(array.as_ref());
140    }
141}