vortex_array/arrays/null/compute/
mod.rs

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