vortex_array/compute/conformance/
take.rs1use vortex_buffer::buffer;
5use vortex_error::VortexExpect;
6
7use crate::ArrayRef;
8use crate::ExecutionCtx;
9use crate::IntoArray as _;
10use crate::RecursiveCanonical;
11use crate::arrays::PrimitiveArray;
12use crate::dtype::Nullability;
13
14pub fn test_take_conformance(array: &ArrayRef, ctx: &mut ExecutionCtx) {
24 let len = array.len();
25
26 if len > 0 {
27 test_take_all(array, ctx);
28 test_take_selective(array, ctx);
29 test_take_first_and_last(array, ctx);
30 test_take_with_nullable_indices(array, ctx);
31 test_take_repeated_indices(array, ctx);
32 }
33
34 test_empty_indices(array);
35
36 if len > 0 {
38 test_take_reverse(array, ctx);
39 test_take_single_middle(array, ctx);
40 }
41
42 if len > 3 {
43 test_take_random_unsorted(array, ctx);
44 test_take_contiguous_range(array, ctx);
45 test_take_mixed_repeated(array, ctx);
46 }
47
48 if len >= 1024 {
50 test_take_large_indices(array, ctx);
51 }
52}
53
54pub(super) fn test_take_all(array: &ArrayRef, ctx: &mut ExecutionCtx) {
55 let len = array.len();
56 let indices = PrimitiveArray::from_iter(0..len as u64);
57 let result = array
58 .take(indices.into_array())
59 .vortex_expect("take should succeed in conformance test")
60 .execute::<RecursiveCanonical>(ctx)
61 .vortex_expect("take execution should succeed in conformance test")
62 .0
63 .into_array();
64
65 assert_eq!(result.len(), len);
66 assert_eq!(result.dtype(), array.dtype());
67
68 for i in 0..len {
70 assert_eq!(
71 array
72 .execute_scalar(i, ctx)
73 .vortex_expect("scalar_at should succeed in conformance test"),
74 result
75 .execute_scalar(i, ctx)
76 .vortex_expect("scalar_at should succeed in conformance test")
77 );
78 }
79}
80
81#[expect(clippy::cast_possible_truncation)]
82fn test_take_selective(array: &ArrayRef, ctx: &mut ExecutionCtx) {
83 let len = array.len();
84
85 let indices: Vec<u64> = (0..len as u64).step_by(2).collect();
87 let expected_len = indices.len();
88 let indices_array = PrimitiveArray::from_iter(indices.clone());
89
90 let result = array
91 .take(indices_array.into_array())
92 .vortex_expect("take should succeed in conformance test")
93 .execute::<RecursiveCanonical>(ctx)
94 .vortex_expect("take execution should succeed in conformance test")
95 .0
96 .into_array();
97 assert_eq!(result.len(), expected_len);
98
99 for (result_idx, &original_idx) in indices.iter().enumerate() {
101 assert_eq!(
102 array
103 .execute_scalar(original_idx as usize, ctx)
104 .vortex_expect("scalar_at should succeed in conformance test"),
105 result
106 .execute_scalar(result_idx, ctx)
107 .vortex_expect("scalar_at should succeed in conformance test")
108 );
109 }
110}
111
112fn test_take_first_and_last(array: &ArrayRef, ctx: &mut ExecutionCtx) {
113 let len = array.len();
114 let indices = PrimitiveArray::from_iter([0u64, (len - 1) as u64]);
115 let result = array
116 .take(indices.into_array())
117 .vortex_expect("take should succeed in conformance test")
118 .execute::<RecursiveCanonical>(ctx)
119 .vortex_expect("take execution should succeed in conformance test")
120 .0
121 .into_array();
122
123 assert_eq!(result.len(), 2);
124 assert_eq!(
125 array
126 .execute_scalar(0, ctx)
127 .vortex_expect("scalar_at should succeed in conformance test"),
128 result
129 .execute_scalar(0, ctx)
130 .vortex_expect("scalar_at should succeed in conformance test")
131 );
132 assert_eq!(
133 array
134 .execute_scalar(len - 1, ctx)
135 .vortex_expect("scalar_at should succeed in conformance test"),
136 result
137 .execute_scalar(1, ctx)
138 .vortex_expect("scalar_at should succeed in conformance test")
139 );
140}
141
142#[expect(clippy::cast_possible_truncation)]
143fn test_take_with_nullable_indices(array: &ArrayRef, ctx: &mut ExecutionCtx) {
144 let len = array.len();
145
146 let indices_vec: Vec<Option<u64>> = if len >= 3 {
148 vec![Some(0), None, Some((len - 1) as u64)]
149 } else if len >= 2 {
150 vec![Some(0), None]
151 } else {
152 vec![None]
153 };
154
155 let indices = PrimitiveArray::from_option_iter(indices_vec.clone());
156 let result = array
157 .take(indices.into_array())
158 .vortex_expect("take should succeed in conformance test")
159 .execute::<RecursiveCanonical>(ctx)
160 .vortex_expect("take execution should succeed in conformance test")
161 .0
162 .into_array();
163
164 assert_eq!(result.len(), indices_vec.len());
165 assert_eq!(
166 result.dtype(),
167 &array.dtype().with_nullability(Nullability::Nullable)
168 );
169
170 for (i, idx_opt) in indices_vec.iter().enumerate() {
172 match idx_opt {
173 Some(idx) => {
174 let expected = array
175 .execute_scalar(*idx as usize, ctx)
176 .vortex_expect("scalar_at should succeed in conformance test");
177 let actual = result
178 .execute_scalar(i, ctx)
179 .vortex_expect("scalar_at should succeed in conformance test");
180 assert_eq!(expected, actual);
181 }
182 None => {
183 assert!(
184 result
185 .execute_scalar(i, ctx)
186 .vortex_expect("scalar_at should succeed in conformance test")
187 .is_null()
188 );
189 }
190 }
191 }
192}
193
194pub(super) fn test_take_repeated_indices(array: &ArrayRef, ctx: &mut ExecutionCtx) {
195 if array.is_empty() {
196 return;
197 }
198
199 let indices = buffer![0u64, 0, 0].into_array();
201 let result = array
202 .take(indices)
203 .vortex_expect("take should succeed in conformance test")
204 .execute::<RecursiveCanonical>(ctx)
205 .vortex_expect("take execution should succeed in conformance test")
206 .0
207 .into_array();
208
209 assert_eq!(result.len(), 3);
210 let first_elem = array
211 .execute_scalar(0, ctx)
212 .vortex_expect("scalar_at should succeed in conformance test");
213 for i in 0..3 {
214 assert_eq!(
215 result
216 .execute_scalar(i, ctx)
217 .vortex_expect("scalar_at should succeed in conformance test"),
218 first_elem
219 );
220 }
221}
222
223fn test_empty_indices(array: &ArrayRef) {
224 let indices = PrimitiveArray::empty::<u64>(Nullability::NonNullable);
225 let result = array
226 .take(indices.into_array())
227 .vortex_expect("take should succeed in conformance test");
228
229 assert_eq!(result.len(), 0);
230 assert_eq!(result.dtype(), array.dtype());
231}
232
233fn test_take_reverse(array: &ArrayRef, ctx: &mut ExecutionCtx) {
234 let len = array.len();
235 let indices = PrimitiveArray::from_iter((0..len as u64).rev());
237 let result = array
238 .take(indices.into_array())
239 .vortex_expect("take should succeed in conformance test")
240 .execute::<RecursiveCanonical>(ctx)
241 .vortex_expect("take execution should succeed in conformance test")
242 .0
243 .into_array();
244
245 assert_eq!(result.len(), len);
246
247 for i in 0..len {
249 assert_eq!(
250 array
251 .execute_scalar(len - 1 - i, ctx)
252 .vortex_expect("scalar_at should succeed in conformance test"),
253 result
254 .execute_scalar(i, ctx)
255 .vortex_expect("scalar_at should succeed in conformance test")
256 );
257 }
258}
259
260fn test_take_single_middle(array: &ArrayRef, ctx: &mut ExecutionCtx) {
261 let len = array.len();
262 let middle_idx = len / 2;
263
264 let indices = PrimitiveArray::from_iter([middle_idx as u64]);
265 let result = array
266 .take(indices.into_array())
267 .vortex_expect("take should succeed in conformance test")
268 .execute::<RecursiveCanonical>(ctx)
269 .vortex_expect("take execution should succeed in conformance test")
270 .0
271 .into_array();
272
273 assert_eq!(result.len(), 1);
274 assert_eq!(
275 array
276 .execute_scalar(middle_idx, ctx)
277 .vortex_expect("scalar_at should succeed in conformance test"),
278 result
279 .execute_scalar(0, ctx)
280 .vortex_expect("scalar_at should succeed in conformance test")
281 );
282}
283
284#[expect(clippy::cast_possible_truncation)]
285fn test_take_random_unsorted(array: &ArrayRef, ctx: &mut ExecutionCtx) {
286 let len = array.len();
287
288 let mut indices = Vec::with_capacity(len.min(10));
290 let mut idx = 1u64;
291 for _ in 0..len.min(10) {
292 indices.push((idx * 7 + 3) % len as u64);
293 idx = (idx * 3 + 1) % len as u64;
294 }
295
296 let indices_array = PrimitiveArray::from_iter(indices.clone());
297 let result = array
298 .take(indices_array.into_array())
299 .vortex_expect("take should succeed in conformance test")
300 .execute::<RecursiveCanonical>(ctx)
301 .vortex_expect("take execution should succeed in conformance test")
302 .0
303 .into_array();
304
305 assert_eq!(result.len(), indices.len());
306
307 for (i, &idx) in indices.iter().enumerate() {
309 assert_eq!(
310 array
311 .execute_scalar(idx as usize, ctx)
312 .vortex_expect("scalar_at should succeed in conformance test"),
313 result
314 .execute_scalar(i, ctx)
315 .vortex_expect("scalar_at should succeed in conformance test")
316 );
317 }
318}
319
320fn test_take_contiguous_range(array: &ArrayRef, ctx: &mut ExecutionCtx) {
321 let len = array.len();
322 let start = len / 4;
323 let end = len / 2;
324
325 let indices = PrimitiveArray::from_iter(start as u64..end as u64);
327 let result = array
328 .take(indices.into_array())
329 .vortex_expect("take should succeed in conformance test")
330 .execute::<RecursiveCanonical>(ctx)
331 .vortex_expect("take execution should succeed in conformance test")
332 .0
333 .into_array();
334
335 assert_eq!(result.len(), end - start);
336
337 for i in 0..(end - start) {
339 assert_eq!(
340 array
341 .execute_scalar(start + i, ctx)
342 .vortex_expect("scalar_at should succeed in conformance test"),
343 result
344 .execute_scalar(i, ctx)
345 .vortex_expect("scalar_at should succeed in conformance test")
346 );
347 }
348}
349
350#[expect(clippy::cast_possible_truncation)]
351fn test_take_mixed_repeated(array: &ArrayRef, ctx: &mut ExecutionCtx) {
352 let len = array.len();
353
354 let indices = vec![
356 0u64,
357 0,
358 1,
359 1,
360 len as u64 / 2,
361 len as u64 / 2,
362 len as u64 / 2,
363 (len - 1) as u64,
364 ];
365
366 let indices_array = PrimitiveArray::from_iter(indices.clone());
367 let result = array
368 .take(indices_array.into_array())
369 .vortex_expect("take should succeed in conformance test")
370 .execute::<RecursiveCanonical>(ctx)
371 .vortex_expect("take execution should succeed in conformance test")
372 .0
373 .into_array();
374
375 assert_eq!(result.len(), indices.len());
376
377 for (i, &idx) in indices.iter().enumerate() {
379 assert_eq!(
380 array
381 .execute_scalar(idx as usize, ctx)
382 .vortex_expect("scalar_at should succeed in conformance test"),
383 result
384 .execute_scalar(i, ctx)
385 .vortex_expect("scalar_at should succeed in conformance test")
386 );
387 }
388}
389
390#[expect(clippy::cast_possible_truncation)]
391fn test_take_large_indices(array: &ArrayRef, ctx: &mut ExecutionCtx) {
392 let len = array.len();
394 let num_indices = 10000.min(len * 3);
395
396 let indices: Vec<u64> = (0..num_indices)
398 .map(|i| ((i * 17 + 5) % len) as u64)
399 .collect();
400
401 let indices_array = PrimitiveArray::from_iter(indices.clone());
402 let result = array
403 .take(indices_array.into_array())
404 .vortex_expect("take should succeed in conformance test")
405 .execute::<RecursiveCanonical>(ctx)
406 .vortex_expect("take execution should succeed in conformance test")
407 .0
408 .into_array();
409
410 assert_eq!(result.len(), num_indices);
411
412 for i in (0..num_indices).step_by(1000) {
414 let expected_idx = indices[i] as usize;
415 assert_eq!(
416 array
417 .execute_scalar(expected_idx, ctx)
418 .vortex_expect("scalar_at should succeed in conformance test"),
419 result
420 .execute_scalar(i, ctx)
421 .vortex_expect("scalar_at should succeed in conformance test")
422 );
423 }
424}