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).unwrap().to_null().unwrap();
79
80        assert_eq!(sliced.len(), 4);
81        assert!(matches!(sliced.validity_mask().unwrap(), 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            .unwrap();
91
92        assert_eq!(taken.len(), 5);
93        assert!(matches!(taken.validity_mask().unwrap(), Mask::AllFalse(5)));
94    }
95
96    #[test]
97    fn test_scalar_at_nulls() {
98        let nulls = NullArray::new(10);
99
100        let scalar = nulls.scalar_at(0).unwrap();
101        assert!(scalar.is_null());
102        assert_eq!(scalar.dtype().clone(), DType::Null);
103    }
104
105    #[test]
106    fn test_filter_null_array() {
107        test_filter_conformance(NullArray::new(5).as_ref());
108        test_filter_conformance(NullArray::new(1).as_ref());
109        test_filter_conformance(NullArray::new(10).as_ref());
110    }
111
112    #[test]
113    fn test_mask_null_array() {
114        test_mask_conformance(NullArray::new(5).as_ref());
115    }
116
117    #[test]
118    fn test_take_null_array_conformance() {
119        test_take_conformance(NullArray::new(5).as_ref());
120        test_take_conformance(NullArray::new(1).as_ref());
121        test_take_conformance(NullArray::new(10).as_ref());
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use rstest::rstest;
128
129    use crate::arrays::NullArray;
130    use crate::compute::conformance::consistency::test_array_consistency;
131
132    #[rstest]
133    // From test_all_consistency
134    #[case::null_array_small(NullArray::new(5))]
135    #[case::null_array_medium(NullArray::new(100))]
136    // Additional test cases
137    #[case::null_array_single(NullArray::new(1))]
138    #[case::null_array_large(NullArray::new(1000))]
139    #[case::null_array_empty(NullArray::new(0))]
140    fn test_null_consistency(#[case] array: NullArray) {
141        test_array_consistency(array.as_ref());
142    }
143}