vortex_array/arrays/bool/compute/
take.rs1use itertools::Itertools as _;
5use vortex_buffer::BitBuffer;
6use vortex_buffer::BitBufferMut;
7use vortex_buffer::take_bits;
8use vortex_error::VortexResult;
9use vortex_error::vortex_ensure_eq;
10use vortex_error::vortex_err;
11use vortex_mask::Mask;
12
13use crate::ArrayRef;
14use crate::Columnar;
15use crate::IntoArray;
16use crate::array::ArrayView;
17use crate::arrays::Bool;
18use crate::arrays::BoolArray;
19use crate::arrays::ConstantArray;
20use crate::arrays::PiecewiseSequence;
21use crate::arrays::PrimitiveArray;
22use crate::arrays::bool::BoolArrayExt;
23use crate::arrays::dict::TakeExecute;
24use crate::arrays::piecewise_sequence::constant_unsigned_usize;
25use crate::arrays::piecewise_sequence::maybe_contiguous_slices;
26use crate::dtype::UnsignedPType;
27use crate::executor::ExecutionCtx;
28use crate::match_each_integer_ptype;
29use crate::match_each_unsigned_integer_ptype;
30use crate::scalar::Scalar;
31
32impl TakeExecute for Bool {
33 fn take(
34 array: ArrayView<'_, Bool>,
35 indices: &ArrayRef,
36 ctx: &mut ExecutionCtx,
37 ) -> VortexResult<Option<ArrayRef>> {
38 if let Some(piecewise_indices) = indices.as_opt::<PiecewiseSequence>()
39 && let Some(taken) = take_contiguous_ranges(array, piecewise_indices, indices, ctx)?
40 {
41 return Ok(Some(taken));
42 }
43
44 let mask = indices.validity()?.execute_mask(indices.len(), ctx)?;
45 if matches!(mask, Mask::AllFalse(_)) {
46 return Ok(Some(
47 ConstantArray::new(Scalar::null(array.dtype().as_nullable()), indices.len())
48 .into_array(),
49 ));
50 }
51
52 if matches!(mask, Mask::AllTrue(_)) && !array.dtype().is_nullable() {
53 let source = array.bit_buffer_view();
54 let count = source.true_count();
55 if count == 0 || count == source.len() {
56 return Ok(Some(
57 ConstantArray::new(
58 Scalar::bool(count != 0, array.dtype().nullability()),
59 indices.len(),
60 )
61 .into_array(),
62 ));
63 }
64 }
65
66 let indices_values = indices.clone().execute::<PrimitiveArray>(ctx)?;
67 let buffer = match_each_integer_ptype!(indices_values.ptype(), |I| {
68 take_bits(
69 array.bit_buffer_view(),
70 indices_values.as_slice::<I>(),
71 mask.values().map(|values| values.bit_buffer().as_view()),
72 )
73 });
74
75 let validity = array.validity()?.take(indices)?;
76 Ok(Some(BoolArray::new(buffer, validity).into_array()))
77 }
78}
79
80fn take_contiguous_ranges(
81 array: ArrayView<'_, Bool>,
82 indices: ArrayView<'_, PiecewiseSequence>,
83 indices_ref: &ArrayRef,
84 ctx: &mut ExecutionCtx,
85) -> VortexResult<Option<ArrayRef>> {
86 let Some((starts, lengths)) = maybe_contiguous_slices(indices, ctx)? else {
87 return Ok(None);
88 };
89 let source = array.to_bit_buffer();
90 let output_len = indices_ref.len();
91 let buffer = match lengths {
92 Columnar::Constant(lengths) => {
93 let length = constant_unsigned_usize(&lengths);
94 match_each_unsigned_integer_ptype!(starts.ptype(), |S| {
95 take_bit_slices_constant_length(
96 &source,
97 starts.as_slice::<S>(),
98 length,
99 output_len,
100 )?
101 })
102 }
103 Columnar::Canonical(lengths) => {
104 let lengths = lengths.into_primitive();
105 match_each_unsigned_integer_ptype!(starts.ptype(), |S| {
106 match_each_unsigned_integer_ptype!(lengths.ptype(), |L| {
107 take_bit_slices(
108 &source,
109 starts.as_slice::<S>(),
110 lengths.as_slice::<L>(),
111 output_len,
112 )?
113 })
114 })
115 }
116 };
117
118 Ok(Some(
119 BoolArray::new(buffer, array.validity()?.take(indices_ref)?).into_array(),
120 ))
121}
122
123fn take_bit_slices_constant_length<S>(
124 source: &BitBuffer,
125 starts: &[S],
126 length: usize,
127 output_len: usize,
128) -> VortexResult<BitBuffer>
129where
130 S: UnsignedPType,
131{
132 let computed_len = starts
133 .len()
134 .checked_mul(length)
135 .ok_or_else(|| vortex_err!("PiecewiseSequenceArray output length overflows usize"))?;
136 vortex_ensure_eq!(
137 computed_len,
138 output_len,
139 "PiecewiseSequenceArray expanded length does not match declared length",
140 );
141
142 let mut values = BitBufferMut::with_capacity(output_len);
143 for start in starts {
144 let start = start.as_();
145 values.append_buffer(&source.slice(start..).slice(..length));
146 }
147
148 Ok(values.freeze())
149}
150
151fn take_bit_slices<S, L>(
152 source: &BitBuffer,
153 starts: &[S],
154 lengths: &[L],
155 output_len: usize,
156) -> VortexResult<BitBuffer>
157where
158 S: UnsignedPType,
159 L: UnsignedPType,
160{
161 let mut values = BitBufferMut::with_capacity(output_len);
162 for (&start, &length) in starts.iter().zip_eq(lengths) {
163 let start = start.as_();
164 let length = length.as_();
165 values.append_buffer(&source.slice(start..).slice(..length));
166 }
167
168 vortex_ensure_eq!(
169 values.len(),
170 output_len,
171 "PiecewiseSequenceArray expanded length does not match declared length",
172 );
173 Ok(values.freeze())
174}
175
176#[cfg(test)]
177mod test {
178 use rstest::rstest;
179 use vortex_buffer::buffer;
180
181 use crate::IntoArray as _;
182 use crate::VortexSessionExecute;
183 use crate::array_session;
184 use crate::arrays::BoolArray;
185 use crate::arrays::PrimitiveArray;
186 use crate::arrays::bool::BoolArrayExt;
187 use crate::assert_arrays_eq;
188 use crate::compute::conformance::take::test_take_conformance;
189 use crate::validity::Validity;
190
191 #[test]
192 fn take_nullable() {
193 let mut ctx = array_session().create_execution_ctx();
194 let reference = BoolArray::from_iter(vec![
195 Some(false),
196 Some(true),
197 Some(false),
198 None,
199 Some(false),
200 ]);
201
202 let b = reference
203 .take(buffer![0, 3, 4].into_array())
204 .unwrap()
205 .execute::<BoolArray>(&mut ctx)
206 .unwrap();
207 assert_eq!(
208 b.to_bit_buffer(),
209 BoolArray::from_iter([Some(false), None, Some(false)]).to_bit_buffer()
210 );
211
212 let all_invalid_indices = PrimitiveArray::from_option_iter([None::<i32>, None, None]);
213 let b = reference.take(all_invalid_indices.into_array()).unwrap();
214 assert_arrays_eq!(b, BoolArray::from_iter([None, None, None]), &mut ctx);
215 }
216
217 #[test]
218 fn test_bool_array_take_with_null_out_of_bounds_indices() {
219 let mut ctx = array_session().create_execution_ctx();
220 let values = BoolArray::from_iter(vec![Some(false), Some(true), None, None, Some(false)]);
221 let indices = PrimitiveArray::new(
222 buffer![0, 3, 100],
223 Validity::Array(BoolArray::from_iter([true, true, false]).into_array()),
224 );
225 let actual = values.take(indices.into_array()).unwrap();
226
227 assert_arrays_eq!(
229 actual,
230 BoolArray::from_iter([Some(false), None, None]),
231 &mut ctx
232 );
233 }
234
235 #[test]
236 fn test_non_null_bool_array_take_with_null_out_of_bounds_indices() {
237 let mut ctx = array_session().create_execution_ctx();
238 let values = BoolArray::from_iter(vec![false, true, false, true, false]);
239 let indices = PrimitiveArray::new(
240 buffer![0, 3, 100],
241 Validity::Array(BoolArray::from_iter([true, true, false]).into_array()),
242 );
243 let actual = values.take(indices.into_array()).unwrap();
244 assert_arrays_eq!(
246 actual,
247 BoolArray::from_iter([Some(false), Some(true), None]),
248 &mut ctx
249 );
250 }
251
252 #[test]
253 fn test_bool_array_take_all_null_indices() {
254 let mut ctx = array_session().create_execution_ctx();
255 let values = BoolArray::from_iter(vec![Some(false), Some(true), None, None, Some(false)]);
256 let indices = PrimitiveArray::new(
257 buffer![0, 3, 100],
258 Validity::Array(BoolArray::from_iter([false, false, false]).into_array()),
259 );
260 let actual = values.take(indices.into_array()).unwrap();
261 assert_arrays_eq!(actual, BoolArray::from_iter([None, None, None]), &mut ctx);
262 }
263
264 #[test]
265 fn test_non_null_bool_array_take_all_null_indices() {
266 let mut ctx = array_session().create_execution_ctx();
267 let values = BoolArray::from_iter(vec![false, true, false, true, false]);
268 let indices = PrimitiveArray::new(
269 buffer![0, 3, 100],
270 Validity::Array(BoolArray::from_iter([false, false, false]).into_array()),
271 );
272 let actual = values.take(indices.into_array()).unwrap();
273 assert_arrays_eq!(actual, BoolArray::from_iter([None, None, None]), &mut ctx);
274 }
275
276 #[rstest]
277 #[case(BoolArray::from_iter([true, false, true, true, false]))]
278 #[case(BoolArray::from_iter([Some(true), None, Some(false), Some(true), None]))]
279 #[case(BoolArray::from_iter([true, false]))]
280 #[case(BoolArray::from_iter([true]))]
281 fn test_take_bool_conformance(#[case] array: BoolArray) {
282 test_take_conformance(
283 &array.into_array(),
284 &mut array_session().create_execution_ctx(),
285 );
286 }
287}