1use vortex_buffer::BufferMut;
5use vortex_error::VortexResult;
6use vortex_mask::Mask;
7
8use crate::ArrayRef;
9use crate::Canonical;
10use crate::IntoArray;
11use crate::array::ArrayView;
12use crate::arrays::Chunked;
13use crate::arrays::ChunkedArray;
14use crate::arrays::PrimitiveArray;
15use crate::arrays::chunked::ChunkedArrayExt;
16use crate::arrays::dict::TakeExecute;
17use crate::builtins::ArrayBuiltins;
18use crate::dtype::DType;
19use crate::dtype::PType;
20use crate::executor::ExecutionCtx;
21use crate::validity::Validity;
22
23fn take_chunked(
26 array: ArrayView<'_, Chunked>,
27 indices: &ArrayRef,
28 ctx: &mut ExecutionCtx,
29) -> VortexResult<ArrayRef> {
30 let indices = indices
31 .cast(DType::Primitive(PType::U64, indices.dtype().nullability()))?
32 .execute::<PrimitiveArray>(ctx)?;
33
34 let indices_mask = indices
35 .as_ref()
36 .validity()?
37 .execute_mask(indices.as_ref().len(), ctx)?;
38 let indices_values = indices.as_slice::<u64>();
39 let n = indices_values.len();
40
41 let mut pairs: Vec<(u64, usize)> = indices_values
44 .iter()
45 .enumerate()
46 .filter(|&(i, _)| indices_mask.value(i))
47 .map(|(i, &v)| (v, i))
48 .collect();
49 pairs.sort_unstable();
50
51 let chunk_offsets = array.chunk_offsets();
55 let nchunks = array.nchunks();
56 let mut chunks = Vec::with_capacity(nchunks);
57 let mut final_take = BufferMut::<u64>::with_capacity(n);
58 final_take.push_n(0u64, n);
59
60 let mut cursor = 0usize;
61 let mut dedup_idx = 0u64;
62
63 for chunk_idx in 0..nchunks {
64 let chunk_start = chunk_offsets[chunk_idx];
65 let chunk_end = chunk_offsets[chunk_idx + 1];
66 let chunk_len = chunk_end - chunk_start;
67 let chunk_end_u64 = u64::try_from(chunk_end)?;
68
69 let range_end = cursor + pairs[cursor..].partition_point(|&(v, _)| v < chunk_end_u64);
70 let chunk_pairs = &pairs[cursor..range_end];
71
72 if !chunk_pairs.is_empty() {
73 let mut local_indices: Vec<usize> = Vec::new();
74 for (i, &(val, orig_pos)) in chunk_pairs.iter().enumerate() {
75 if cursor + i > 0 && val != pairs[cursor + i - 1].0 {
76 dedup_idx += 1;
77 }
78 let local = usize::try_from(val)? - chunk_start;
79 if local_indices.last() != Some(&local) {
80 local_indices.push(local);
81 }
82 final_take[orig_pos] = dedup_idx;
83 }
84
85 let filter_mask = Mask::from_indices(chunk_len, local_indices);
86 chunks.push(array.chunk(chunk_idx).filter(filter_mask)?);
87 }
88
89 cursor = range_end;
90 }
91
92 let flat = unsafe { ChunkedArray::new_unchecked(chunks, array.dtype().clone()) }
95 .into_array()
96 .execute::<Canonical>(ctx)?
98 .into_array();
99
100 let take_validity = Validity::from_mask(
103 indices
104 .as_ref()
105 .validity()?
106 .execute_mask(indices.as_ref().len(), ctx)?,
107 indices.dtype().nullability(),
108 );
109 flat.take(PrimitiveArray::new(final_take.freeze(), take_validity).into_array())
110}
111
112impl TakeExecute for Chunked {
113 fn take(
114 array: ArrayView<'_, Chunked>,
115 indices: &ArrayRef,
116 ctx: &mut ExecutionCtx,
117 ) -> VortexResult<Option<ArrayRef>> {
118 take_chunked(array, indices, ctx).map(Some)
119 }
120}
121
122#[cfg(test)]
123mod test {
124 use vortex_buffer::bitbuffer;
125 use vortex_buffer::buffer;
126 use vortex_error::VortexResult;
127
128 use crate::IntoArray;
129 use crate::VortexSessionExecute;
130 use crate::array_session;
131 use crate::arrays::BoolArray;
132 use crate::arrays::ChunkedArray;
133 use crate::arrays::PrimitiveArray;
134 use crate::arrays::StructArray;
135 use crate::arrays::chunked::ChunkedArrayExt;
136 use crate::assert_arrays_eq;
137 use crate::compute::conformance::take::test_take_conformance;
138 use crate::dtype::FieldNames;
139 use crate::dtype::Nullability;
140 use crate::validity::Validity;
141
142 #[test]
143 fn test_take() {
144 let mut ctx = array_session().create_execution_ctx();
145 let a = buffer![1i32, 2, 3].into_array();
146 let arr = ChunkedArray::try_new(vec![a.clone(), a.clone(), a.clone()], a.dtype().clone())
147 .unwrap();
148 assert_eq!(arr.nchunks(), 3);
149 assert_eq!(arr.len(), 9);
150 let indices = buffer![0u64, 0, 6, 4].into_array();
151
152 let result = arr.take(indices).unwrap();
153 assert_arrays_eq!(result, PrimitiveArray::from_iter([1i32, 1, 1, 2]), &mut ctx);
154 }
155
156 #[test]
157 fn test_take_nullable_values() {
158 let mut ctx = array_session().create_execution_ctx();
159 let a = PrimitiveArray::new(buffer![1i32, 2, 3], Validity::AllValid).into_array();
160 let arr = ChunkedArray::try_new(vec![a.clone(), a.clone(), a.clone()], a.dtype().clone())
161 .unwrap();
162 assert_eq!(arr.nchunks(), 3);
163 assert_eq!(arr.len(), 9);
164 let indices = PrimitiveArray::new(buffer![0u64, 0, 6, 4], Validity::NonNullable);
165
166 let result = arr.take(indices.into_array()).unwrap();
167 assert_arrays_eq!(
168 result,
169 PrimitiveArray::from_option_iter([1i32, 1, 1, 2].map(Some)),
170 &mut ctx
171 );
172 }
173
174 #[test]
175 fn test_take_nullable_indices() {
176 let mut ctx = array_session().create_execution_ctx();
177 let a = buffer![1i32, 2, 3].into_array();
178 let arr = ChunkedArray::try_new(vec![a.clone(), a.clone(), a.clone()], a.dtype().clone())
179 .unwrap();
180 assert_eq!(arr.nchunks(), 3);
181 assert_eq!(arr.len(), 9);
182 let indices = PrimitiveArray::new(
183 buffer![0u64, 0, 6, 4],
184 Validity::Array(bitbuffer![1 0 0 1].into_array()),
185 );
186
187 let result = arr.take(indices.into_array()).unwrap();
188 assert_arrays_eq!(
189 result,
190 PrimitiveArray::from_option_iter([Some(1i32), None, None, Some(2)]),
191 &mut ctx
192 );
193 }
194
195 #[test]
196 fn test_take_nullable_struct() {
197 let mut ctx = array_session().create_execution_ctx();
198 let struct_array =
199 StructArray::try_new(FieldNames::default(), vec![], 100, Validity::NonNullable)
200 .unwrap();
201
202 let arr = ChunkedArray::from_iter(vec![
203 struct_array.clone().into_array(),
204 struct_array.into_array(),
205 ]);
206
207 let result = arr
208 .take(PrimitiveArray::from_option_iter(vec![Some(0), None, Some(101)]).into_array())
209 .unwrap();
210
211 let expect = StructArray::try_new(
212 FieldNames::default(),
213 vec![],
214 3,
215 Validity::Array(BoolArray::from_iter(vec![true, false, true]).into_array()),
216 )
217 .unwrap();
218 assert_arrays_eq!(result, expect, &mut ctx);
219 }
220
221 #[test]
222 fn test_empty_take() {
223 let mut ctx = array_session().create_execution_ctx();
224 let a = buffer![1i32, 2, 3].into_array();
225 let arr = ChunkedArray::try_new(vec![a.clone(), a.clone(), a.clone()], a.dtype().clone())
226 .unwrap();
227 assert_eq!(arr.nchunks(), 3);
228 assert_eq!(arr.len(), 9);
229
230 let indices = PrimitiveArray::empty::<u64>(Nullability::NonNullable);
231 let result = arr.take(indices.into_array()).unwrap();
232
233 assert!(result.is_empty());
234 assert_eq!(result.dtype(), arr.dtype());
235 assert_arrays_eq!(
236 result,
237 PrimitiveArray::empty::<i32>(Nullability::NonNullable),
238 &mut ctx
239 );
240 }
241
242 #[test]
243 fn test_take_shuffled_indices() -> VortexResult<()> {
244 let mut ctx = array_session().create_execution_ctx();
245 let c0 = buffer![0i32, 1, 2].into_array();
246 let c1 = buffer![3i32, 4, 5].into_array();
247 let c2 = buffer![6i32, 7, 8].into_array();
248 let arr = ChunkedArray::try_new(
249 vec![c0, c1, c2],
250 PrimitiveArray::empty::<i32>(Nullability::NonNullable)
251 .dtype()
252 .clone(),
253 )?;
254
255 let indices = buffer![8u64, 0, 5, 3, 2, 7, 1, 6, 4].into_array();
257 let result = arr.take(indices)?;
258
259 assert_arrays_eq!(
260 result,
261 PrimitiveArray::from_iter([8i32, 0, 5, 3, 2, 7, 1, 6, 4]),
262 &mut ctx
263 );
264 Ok(())
265 }
266
267 #[test]
268 fn test_take_shuffled_large() -> VortexResult<()> {
269 let mut ctx = array_session().create_execution_ctx();
270 let nchunks: i32 = 100;
271 let chunk_len: i32 = 1_000;
272 let total = nchunks * chunk_len;
273
274 let chunks: Vec<_> = (0..nchunks)
275 .map(|c| {
276 let start = c * chunk_len;
277 PrimitiveArray::from_iter(start..start + chunk_len).into_array()
278 })
279 .collect();
280 let dtype = chunks[0].dtype().clone();
281 let arr = ChunkedArray::try_new(chunks, dtype)?;
282
283 let mut indices: Vec<u64> = (0..u64::try_from(total)?).collect();
285 let mut seed: u64 = 0xdeadbeef;
286 for i in (1..indices.len()).rev() {
287 seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1);
288 let j = (seed >> 33) as usize % (i + 1);
289 indices.swap(i, j);
290 }
291
292 let indices_arr = PrimitiveArray::new(
293 vortex_buffer::Buffer::from(indices.clone()),
294 Validity::NonNullable,
295 );
296 let result = arr.take(indices_arr.into_array())?;
297
298 let result = result.execute::<PrimitiveArray>(&mut ctx)?;
300 let result_vals = result.as_slice::<i32>();
301 for (pos, &idx) in indices.iter().enumerate() {
302 assert_eq!(
303 result_vals[pos],
304 i32::try_from(idx)?,
305 "mismatch at position {pos}"
306 );
307 }
308 Ok(())
309 }
310
311 #[test]
312 fn test_take_null_indices() -> VortexResult<()> {
313 let mut ctx = array_session().create_execution_ctx();
314 let c0 = buffer![10i32, 20, 30].into_array();
315 let c1 = buffer![40i32, 50, 60].into_array();
316 let arr = ChunkedArray::try_new(
317 vec![c0, c1],
318 PrimitiveArray::empty::<i32>(Nullability::NonNullable)
319 .dtype()
320 .clone(),
321 )?;
322
323 let indices =
325 PrimitiveArray::from_option_iter([Some(5u64), None, Some(0), Some(3), None, Some(2)]);
326 let result = arr.take(indices.into_array())?;
327
328 assert_arrays_eq!(
329 result,
330 PrimitiveArray::from_option_iter([
331 Some(60i32),
332 None,
333 Some(10),
334 Some(40),
335 None,
336 Some(30)
337 ]),
338 &mut ctx
339 );
340 Ok(())
341 }
342
343 #[test]
344 fn test_take_chunked_conformance() {
345 let a = buffer![1i32, 2, 3].into_array();
346 let b = buffer![4i32, 5].into_array();
347 let arr = ChunkedArray::try_new(
348 vec![a, b],
349 PrimitiveArray::empty::<i32>(Nullability::NonNullable)
350 .dtype()
351 .clone(),
352 )
353 .unwrap();
354 test_take_conformance(
355 &arr.into_array(),
356 &mut array_session().create_execution_ctx(),
357 );
358
359 let a = PrimitiveArray::from_option_iter([Some(1i32), None, Some(3)]);
361 let b = PrimitiveArray::from_option_iter([Some(4i32), Some(5)]);
362 let dtype = a.dtype().clone();
363 let arr = ChunkedArray::try_new(vec![a.into_array(), b.into_array()], dtype).unwrap();
364 test_take_conformance(
365 &arr.into_array(),
366 &mut array_session().create_execution_ctx(),
367 );
368
369 let chunk = buffer![10i32, 20, 30, 40, 50].into_array();
371 let arr = ChunkedArray::try_new(
372 vec![chunk.clone(), chunk.clone(), chunk.clone()],
373 chunk.dtype().clone(),
374 )
375 .unwrap();
376 test_take_conformance(
377 &arr.into_array(),
378 &mut array_session().create_execution_ctx(),
379 );
380 }
381}