vortex_array/arrays/null/compute/
mod.rs1mod 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 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 #[case::null_array_small(NullArray::new(5))]
135 #[case::null_array_medium(NullArray::new(100))]
136 #[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}