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(
59 count != 0,
60 array.dtype().nullability() | indices.dtype().nullability(),
61 ),
62 indices.len(),
63 )
64 .into_array(),
65 ));
66 }
67 }
68
69 let indices_values = indices.clone().execute::<PrimitiveArray>(ctx)?;
70 let buffer = match_each_integer_ptype!(indices_values.ptype(), |I| {
71 take_bits(
72 array.bit_buffer_view(),
73 indices_values.as_slice::<I>(),
74 mask.values().map(|values| values.bit_buffer().as_view()),
75 )
76 });
77
78 let validity = array.validity()?.take(indices)?;
79 Ok(Some(BoolArray::new(buffer, validity).into_array()))
80 }
81}
82
83fn take_contiguous_ranges(
84 array: ArrayView<'_, Bool>,
85 indices: ArrayView<'_, PiecewiseSequence>,
86 indices_ref: &ArrayRef,
87 ctx: &mut ExecutionCtx,
88) -> VortexResult<Option<ArrayRef>> {
89 let Some((starts, lengths)) = maybe_contiguous_slices(indices, ctx)? else {
90 return Ok(None);
91 };
92 let source = array.to_bit_buffer();
93 let output_len = indices_ref.len();
94 let buffer = match lengths {
95 Columnar::Constant(lengths) => {
96 let length = constant_unsigned_usize(&lengths);
97 match_each_unsigned_integer_ptype!(starts.ptype(), |S| {
98 take_bit_slices_constant_length(
99 &source,
100 starts.as_slice::<S>(),
101 length,
102 output_len,
103 )?
104 })
105 }
106 Columnar::Canonical(lengths) => {
107 let lengths = lengths.into_primitive();
108 match_each_unsigned_integer_ptype!(starts.ptype(), |S| {
109 match_each_unsigned_integer_ptype!(lengths.ptype(), |L| {
110 take_bit_slices(
111 &source,
112 starts.as_slice::<S>(),
113 lengths.as_slice::<L>(),
114 output_len,
115 )?
116 })
117 })
118 }
119 };
120
121 Ok(Some(
122 BoolArray::new(buffer, array.validity()?.take(indices_ref)?).into_array(),
123 ))
124}
125
126fn take_bit_slices_constant_length<S>(
127 source: &BitBuffer,
128 starts: &[S],
129 length: usize,
130 output_len: usize,
131) -> VortexResult<BitBuffer>
132where
133 S: UnsignedPType,
134{
135 let computed_len = starts
136 .len()
137 .checked_mul(length)
138 .ok_or_else(|| vortex_err!("PiecewiseSequenceArray output length overflows usize"))?;
139 vortex_ensure_eq!(
140 computed_len,
141 output_len,
142 "PiecewiseSequenceArray expanded length does not match declared length",
143 );
144
145 let mut values = BitBufferMut::with_capacity(output_len);
146 for start in starts {
147 let start = start.as_();
148 values.append_buffer(&source.slice(start..).slice(..length));
149 }
150
151 Ok(values.freeze())
152}
153
154fn take_bit_slices<S, L>(
155 source: &BitBuffer,
156 starts: &[S],
157 lengths: &[L],
158 output_len: usize,
159) -> VortexResult<BitBuffer>
160where
161 S: UnsignedPType,
162 L: UnsignedPType,
163{
164 let mut values = BitBufferMut::with_capacity(output_len);
165 for (&start, &length) in starts.iter().zip_eq(lengths) {
166 let start = start.as_();
167 let length = length.as_();
168 values.append_buffer(&source.slice(start..).slice(..length));
169 }
170
171 vortex_ensure_eq!(
172 values.len(),
173 output_len,
174 "PiecewiseSequenceArray expanded length does not match declared length",
175 );
176 Ok(values.freeze())
177}
178
179#[cfg(test)]
180mod test {
181 use rstest::rstest;
182 use vortex_buffer::buffer;
183 use vortex_error::VortexResult;
184
185 use crate::IntoArray as _;
186 use crate::VortexSessionExecute;
187 use crate::array_session;
188 use crate::arrays::BoolArray;
189 use crate::arrays::DictArray;
190 use crate::arrays::PrimitiveArray;
191 use crate::assert_arrays_eq;
192 use crate::compute::conformance::take::test_take_conformance;
193 use crate::validity::Validity;
194
195 #[test]
196 fn take_nullable() {
197 let mut ctx = array_session().create_execution_ctx();
198 let reference = BoolArray::from_iter(vec![
199 Some(false),
200 Some(true),
201 Some(false),
202 None,
203 Some(false),
204 ]);
205
206 let b = reference.take(buffer![0, 3, 4].into_array()).unwrap();
207 assert_arrays_eq!(
208 b,
209 BoolArray::from_iter([Some(false), None, Some(false)]),
210 &mut ctx
211 );
212
213 let all_invalid_indices = PrimitiveArray::from_option_iter([None::<i32>, None, None]);
214 let b = reference.take(all_invalid_indices.into_array()).unwrap();
215 assert_arrays_eq!(b, BoolArray::from_iter([None, None, None]), &mut ctx);
216 }
217
218 #[test]
219 fn test_bool_array_take_with_null_out_of_bounds_indices() {
220 let mut ctx = array_session().create_execution_ctx();
221 let values = BoolArray::from_iter(vec![Some(false), Some(true), None, None, Some(false)]);
222 let indices = PrimitiveArray::new(
223 buffer![0, 3, 100],
224 Validity::Array(BoolArray::from_iter([true, true, false]).into_array()),
225 );
226 let actual = values.take(indices.into_array()).unwrap();
227
228 assert_arrays_eq!(
230 actual,
231 BoolArray::from_iter([Some(false), None, None]),
232 &mut ctx
233 );
234 }
235
236 #[test]
237 fn test_non_null_bool_array_take_with_null_out_of_bounds_indices() {
238 let mut ctx = array_session().create_execution_ctx();
239 let values = BoolArray::from_iter(vec![false, true, false, true, false]);
240 let indices = PrimitiveArray::new(
241 buffer![0, 3, 100],
242 Validity::Array(BoolArray::from_iter([true, true, false]).into_array()),
243 );
244 let actual = values.take(indices.into_array()).unwrap();
245 assert_arrays_eq!(
247 actual,
248 BoolArray::from_iter([Some(false), Some(true), None]),
249 &mut ctx
250 );
251 }
252
253 #[test]
254 fn test_bool_array_take_all_null_indices() {
255 let mut ctx = array_session().create_execution_ctx();
256 let values = BoolArray::from_iter(vec![Some(false), Some(true), None, None, Some(false)]);
257 let indices = PrimitiveArray::new(
258 buffer![0, 3, 100],
259 Validity::Array(BoolArray::from_iter([false, false, false]).into_array()),
260 );
261 let actual = values.take(indices.into_array()).unwrap();
262 assert_arrays_eq!(actual, BoolArray::from_iter([None, None, None]), &mut ctx);
263 }
264
265 #[test]
266 fn test_non_null_bool_array_take_all_null_indices() {
267 let mut ctx = array_session().create_execution_ctx();
268 let values = BoolArray::from_iter(vec![false, true, false, true, false]);
269 let indices = PrimitiveArray::new(
270 buffer![0, 3, 100],
271 Validity::Array(BoolArray::from_iter([false, false, false]).into_array()),
272 );
273 let actual = values.take(indices.into_array()).unwrap();
274 assert_arrays_eq!(actual, BoolArray::from_iter([None, None, None]), &mut ctx);
275 }
276
277 #[rstest]
278 #[case(true)]
279 #[case(false)]
280 fn test_non_null_constant_bool_take_with_nullable_indices(
281 #[case] value: bool,
282 ) -> VortexResult<()> {
283 let mut ctx = array_session().create_execution_ctx();
284 let values = BoolArray::from_iter([value]);
285 let codes = PrimitiveArray::new(buffer![0i64, 0, 0], Validity::AllValid);
286
287 let actual = DictArray::try_new(codes.into_array(), values.into_array())?.into_array();
288
289 assert_arrays_eq!(
290 actual,
291 BoolArray::from_iter([Some(value), Some(value), Some(value)]),
292 &mut ctx
293 );
294 Ok(())
295 }
296
297 #[rstest]
298 #[case(BoolArray::from_iter([true, false, true, true, false]))]
299 #[case(BoolArray::from_iter([Some(true), None, Some(false), Some(true), None]))]
300 #[case(BoolArray::from_iter([true, false]))]
301 #[case(BoolArray::from_iter([true]))]
302 fn test_take_bool_conformance(#[case] array: BoolArray) {
303 test_take_conformance(
304 &array.into_array(),
305 &mut array_session().create_execution_ctx(),
306 );
307 }
308}