vortex_array/arrays/bool/compute/
zip.rs1use vortex_buffer::BitBuffer;
5use vortex_buffer::BufferMut;
6use vortex_error::VortexResult;
7use vortex_mask::Mask;
8
9use crate::ArrayRef;
10use crate::ExecutionCtx;
11use crate::IntoArray;
12use crate::array::ArrayView;
13use crate::arrays::Bool;
14use crate::arrays::BoolArray;
15use crate::arrays::bool::BoolArrayExt;
16use crate::scalar_fn::fns::zip::ZipKernel;
17use crate::scalar_fn::fns::zip::zip_validity;
18
19impl ZipKernel for Bool {
26 fn zip(
27 if_true: ArrayView<'_, Bool>,
28 if_false: &ArrayRef,
29 mask: &ArrayRef,
30 ctx: &mut ExecutionCtx,
31 ) -> VortexResult<Option<ArrayRef>> {
32 let Some(if_false) = if_false.as_opt::<Bool>() else {
33 return Ok(None);
34 };
35
36 let mask = mask.try_to_mask_fill_null_false(ctx)?;
38 let mask_values = match &mask {
39 Mask::AllTrue(_) | Mask::AllFalse(_) => return Ok(None),
41 Mask::Values(values) => values,
42 };
43 let mask_bits = mask_values.bit_buffer();
44
45 let values = zip_value_bits(
46 &if_true.to_bit_buffer(),
47 &if_false.to_bit_buffer(),
48 mask_bits,
49 );
50
51 let validity = zip_validity(if_true.validity()?, if_false.validity()?, &mask)?;
52
53 Ok(Some(BoolArray::new(values, validity).into_array()))
54 }
55}
56
57fn zip_value_bits(if_true: &BitBuffer, if_false: &BitBuffer, mask: &BitBuffer) -> BitBuffer {
58 assert_eq!(if_true.len(), if_false.len());
59 assert_eq!(if_true.len(), mask.len());
60
61 let true_chunks = if_true.chunks();
62 let false_chunks = if_false.chunks();
63 let mask_chunks = mask.chunks();
64
65 let mut values = BufferMut::<u64>::with_capacity(true_chunks.num_u64s());
66 for ((true_bits, false_bits), mask_bits) in true_chunks
67 .iter()
68 .zip(false_chunks.iter())
69 .zip(mask_chunks.iter())
70 {
71 values.push((true_bits & mask_bits) | (false_bits & !mask_bits));
72 }
73
74 if true_chunks.remainder_len() != 0 {
75 let true_bits = true_chunks.remainder_bits();
76 let false_bits = false_chunks.remainder_bits();
77 let mask_bits = mask_chunks.remainder_bits();
78 values.push((true_bits & mask_bits) | (false_bits & !mask_bits));
79 }
80
81 BitBuffer::new(values.freeze().into_byte_buffer(), if_true.len())
82}
83
84#[cfg(test)]
85mod tests {
86 use vortex_error::VortexResult;
87 use vortex_mask::Mask;
88
89 use super::zip_value_bits;
90 use crate::ArrayRef;
91 use crate::IntoArray;
92 use crate::LEGACY_SESSION;
93 use crate::VortexSessionExecute;
94 use crate::arrays::Bool;
95 use crate::arrays::BoolArray;
96 use crate::assert_arrays_eq;
97 use crate::builtins::ArrayBuiltins;
98
99 #[test]
100 fn blend_value_bits_boundaries() {
101 for len in [0usize, 1, 2, 7, 8, 9, 63, 64, 65, 127, 128] {
102 let if_true = (0..len).map(|i| i.is_multiple_of(2)).collect();
103 let if_false = (0..len).map(|i| i.is_multiple_of(3)).collect();
104 let mask = (0..len).map(|i| i % 3 != 1).collect();
105
106 let values = zip_value_bits(&if_true, &if_false, &mask);
107
108 assert_eq!(values.len(), len);
109 assert_eq!(
110 values.iter().collect::<Vec<_>>(),
111 (0..len)
112 .map(|i| {
113 if i % 3 != 1 {
114 i.is_multiple_of(2)
115 } else {
116 i.is_multiple_of(3)
117 }
118 })
119 .collect::<Vec<_>>(),
120 "failed for len {len}",
121 );
122 }
123 }
124
125 #[test]
127 fn zip_nonnull_spans_mask_chunks() -> VortexResult<()> {
128 let len = 150usize;
129 let if_true = BoolArray::from_iter((0..len).map(|i| i.is_multiple_of(2))).into_array();
130 let if_false = BoolArray::from_iter((0..len).map(|i| i.is_multiple_of(3))).into_array();
131
132 let bits: Vec<bool> = (0..len).map(|i| i.is_multiple_of(5) || i == 64).collect();
133 let mask = Mask::from_iter(bits.iter().copied());
134
135 let mut ctx = LEGACY_SESSION.create_execution_ctx();
136 let result = mask
137 .into_array()
138 .zip(if_true, if_false)?
139 .execute::<ArrayRef>(&mut ctx)?;
140 assert!(result.is::<Bool>());
141
142 let expected = BoolArray::from_iter((0..len).map(|i| {
143 if bits[i] {
144 i.is_multiple_of(2)
145 } else {
146 i.is_multiple_of(3)
147 }
148 }))
149 .into_array();
150 assert_arrays_eq!(result, expected);
151 Ok(())
152 }
153
154 #[test]
156 fn zip_nullable_selects_values_and_validity() -> VortexResult<()> {
157 let len = 130usize;
158 let if_true = BoolArray::from_iter(
159 (0..len).map(|i| (!i.is_multiple_of(4)).then_some(i.is_multiple_of(2))),
160 )
161 .into_array();
162 let if_false = BoolArray::from_iter(
163 (0..len).map(|i| (!i.is_multiple_of(5)).then_some(i.is_multiple_of(3))),
164 )
165 .into_array();
166
167 let bits: Vec<bool> = (0..len).map(|i| i.is_multiple_of(2)).collect();
168 let mask = Mask::from_iter(bits.iter().copied());
169
170 let mut ctx = LEGACY_SESSION.create_execution_ctx();
171 let result = mask
172 .into_array()
173 .zip(if_true, if_false)?
174 .execute::<ArrayRef>(&mut ctx)?;
175 assert!(result.is::<Bool>());
176
177 let expected = BoolArray::from_iter((0..len).map(|i| {
178 if bits[i] {
179 (!i.is_multiple_of(4)).then_some(i.is_multiple_of(2))
180 } else {
181 (!i.is_multiple_of(5)).then_some(i.is_multiple_of(3))
182 }
183 }))
184 .into_array();
185 assert_arrays_eq!(result, expected);
186 Ok(())
187 }
188}