1use vortex_buffer::BitBuffer;
23use vortex_dtype::DType;
24use vortex_dtype::Nullability;
25use vortex_dtype::PType;
26use vortex_error::VortexExpect;
27use vortex_error::vortex_panic;
28use vortex_mask::Mask;
29
30use crate::Array;
31use crate::IntoArray;
32use crate::arrays::BoolArray;
33use crate::arrays::PrimitiveArray;
34use crate::builtins::ArrayBuiltins;
35use crate::compute::Operator;
36use crate::compute::and_kleene;
37use crate::compute::compare;
38#[expect(deprecated)]
39use crate::compute::invert;
40use crate::compute::mask;
41use crate::compute::or_kleene;
42
43fn test_filter_take_consistency(array: &dyn Array) {
55 let len = array.len();
56 if len == 0 {
57 return;
58 }
59
60 let mask_pattern: BitBuffer = (0..len).map(|i| i % 3 != 1).collect();
62 let mask = Mask::from_buffer(mask_pattern.clone());
63
64 let filtered = array
66 .filter(mask)
67 .vortex_expect("filter should succeed in conformance test");
68
69 let indices: Vec<u64> = mask_pattern
71 .iter()
72 .enumerate()
73 .filter_map(|(i, v)| v.then_some(i as u64))
74 .collect();
75 let indices_array = PrimitiveArray::from_iter(indices).into_array();
76
77 let taken = array
79 .take(indices_array.to_array())
80 .vortex_expect("take should succeed in conformance test");
81
82 assert_eq!(
84 filtered.len(),
85 taken.len(),
86 "Filter and take should produce arrays of the same length. \
87 Filtered length: {}, Taken length: {}",
88 filtered.len(),
89 taken.len()
90 );
91
92 for i in 0..filtered.len() {
93 let filtered_val = filtered
94 .scalar_at(i)
95 .vortex_expect("scalar_at should succeed in conformance test");
96 let taken_val = taken
97 .scalar_at(i)
98 .vortex_expect("scalar_at should succeed in conformance test");
99 assert_eq!(
100 filtered_val, taken_val,
101 "Filter and take produced different values at index {i}. \
102 Filtered value: {filtered_val:?}, Taken value: {taken_val:?}"
103 );
104 }
105}
106
107fn test_double_mask_consistency(array: &dyn Array) {
123 let len = array.len();
124 if len == 0 {
125 return;
126 }
127
128 let mask1: Mask = (0..len).map(|i| i % 3 == 0).collect();
130 let mask2: Mask = (0..len).map(|i| i % 2 == 0).collect();
131
132 let first_masked = mask(array, &mask1).vortex_expect("mask should succeed in conformance test");
134 let double_masked =
135 mask(&first_masked, &mask2).vortex_expect("mask should succeed in conformance test");
136
137 let combined_pattern: BitBuffer = mask1
139 .to_bit_buffer()
140 .iter()
141 .zip(mask2.to_bit_buffer().iter())
142 .map(|(a, b)| a || b)
143 .collect();
144 let combined_mask = Mask::from_buffer(combined_pattern);
145
146 let directly_masked =
148 mask(array, &combined_mask).vortex_expect("mask should succeed in conformance test");
149
150 assert_eq!(
152 double_masked.len(),
153 directly_masked.len(),
154 "Sequential masking and combined masking should produce arrays of the same length. \
155 Sequential length: {}, Combined length: {}",
156 double_masked.len(),
157 directly_masked.len()
158 );
159
160 for i in 0..double_masked.len() {
161 let double_val = double_masked
162 .scalar_at(i)
163 .vortex_expect("scalar_at should succeed in conformance test");
164 let direct_val = directly_masked
165 .scalar_at(i)
166 .vortex_expect("scalar_at should succeed in conformance test");
167 assert_eq!(
168 double_val, direct_val,
169 "Sequential masking and combined masking produced different values at index {i}. \
170 Sequential masking value: {double_val:?}, Combined masking value: {direct_val:?}\n\
171 This likely indicates an issue with how masks are composed in the array implementation."
172 );
173 }
174}
175
176fn test_filter_identity(array: &dyn Array) {
190 let len = array.len();
191 if len == 0 {
192 return;
193 }
194
195 let all_true_mask = Mask::new_true(len);
196 let filtered = array
197 .filter(all_true_mask)
198 .vortex_expect("filter should succeed in conformance test");
199
200 assert_eq!(
202 filtered.len(),
203 array.len(),
204 "Filtering with all-true mask should preserve array length. \
205 Original length: {}, Filtered length: {}",
206 array.len(),
207 filtered.len()
208 );
209
210 for i in 0..len {
211 let original_val = array
212 .scalar_at(i)
213 .vortex_expect("scalar_at should succeed in conformance test");
214 let filtered_val = filtered
215 .scalar_at(i)
216 .vortex_expect("scalar_at should succeed in conformance test");
217 assert_eq!(
218 filtered_val, original_val,
219 "Filtering with all-true mask should preserve all values. \
220 Value at index {i} changed from {original_val:?} to {filtered_val:?}"
221 );
222 }
223}
224
225fn test_mask_identity(array: &dyn Array) {
239 let len = array.len();
240 if len == 0 {
241 return;
242 }
243
244 let all_false_mask = Mask::new_false(len);
245 let masked =
246 mask(array, &all_false_mask).vortex_expect("mask should succeed in conformance test");
247
248 assert_eq!(
250 masked.len(),
251 array.len(),
252 "Masking with all-false mask should preserve array length. \
253 Original length: {}, Masked length: {}",
254 array.len(),
255 masked.len()
256 );
257
258 assert!(
259 masked.dtype().is_nullable(),
260 "Mask operation should always produce a nullable array, but dtype is {}",
261 masked.dtype()
262 );
263
264 for i in 0..len {
265 let original_val = array
266 .scalar_at(i)
267 .vortex_expect("scalar_at should succeed in conformance test");
268 let masked_val = masked
269 .scalar_at(i)
270 .vortex_expect("scalar_at should succeed in conformance test");
271 let expected_val = original_val.clone().into_nullable();
272 assert_eq!(
273 masked_val, expected_val,
274 "Masking with all-false mask should preserve values (as nullable). \
275 Value at index {i}: original = {original_val:?}, masked = {masked_val:?}, expected = {expected_val:?}"
276 );
277 }
278}
279
280fn test_slice_filter_consistency(array: &dyn Array) {
295 let len = array.len();
296 if len < 4 {
297 return; }
299
300 let mut mask_pattern = vec![false; len];
302 mask_pattern[1..4.min(len)].fill(true);
303
304 let mask = Mask::from_iter(mask_pattern);
305 let filtered = array
306 .filter(mask)
307 .vortex_expect("filter should succeed in conformance test");
308
309 let sliced = array
311 .slice(1..4.min(len))
312 .vortex_expect("slice should succeed in conformance test");
313
314 assert_eq!(
315 filtered.len(),
316 sliced.len(),
317 "Filter with contiguous mask and slice should produce same length. \
318 Filtered length: {}, Sliced length: {}",
319 filtered.len(),
320 sliced.len()
321 );
322
323 for i in 0..filtered.len() {
324 let filtered_val = filtered
325 .scalar_at(i)
326 .vortex_expect("scalar_at should succeed in conformance test");
327 let sliced_val = sliced
328 .scalar_at(i)
329 .vortex_expect("scalar_at should succeed in conformance test");
330 assert_eq!(
331 filtered_val, sliced_val,
332 "Filter with contiguous mask and slice produced different values at index {i}. \
333 Filtered value: {filtered_val:?}, Sliced value: {sliced_val:?}"
334 );
335 }
336}
337
338fn test_take_slice_consistency(array: &dyn Array) {
352 let len = array.len();
353 if len < 3 {
354 return; }
356
357 let end = 4.min(len);
359 let indices = PrimitiveArray::from_iter((1..end).map(|i| i as u64)).into_array();
360 let taken = array
361 .take(indices.to_array())
362 .vortex_expect("take should succeed in conformance test");
363
364 let sliced = array
366 .slice(1..end)
367 .vortex_expect("slice should succeed in conformance test");
368
369 assert_eq!(
370 taken.len(),
371 sliced.len(),
372 "Take with sequential indices and slice should produce same length. \
373 Taken length: {}, Sliced length: {}",
374 taken.len(),
375 sliced.len()
376 );
377
378 for i in 0..taken.len() {
379 let taken_val = taken
380 .scalar_at(i)
381 .vortex_expect("scalar_at should succeed in conformance test");
382 let sliced_val = sliced
383 .scalar_at(i)
384 .vortex_expect("scalar_at should succeed in conformance test");
385 assert_eq!(
386 taken_val, sliced_val,
387 "Take with sequential indices and slice produced different values at index {i}. \
388 Taken value: {taken_val:?}, Sliced value: {sliced_val:?}"
389 );
390 }
391}
392
393fn test_filter_preserves_order(array: &dyn Array) {
395 let len = array.len();
396 if len < 4 {
397 return;
398 }
399
400 let mask_pattern: Vec<bool> = (0..len).map(|i| i == 0 || i == 2 || i == 3).collect();
402 let mask = Mask::from_iter(mask_pattern);
403
404 let filtered = array
405 .filter(mask)
406 .vortex_expect("filter should succeed in conformance test");
407
408 assert_eq!(filtered.len(), 3.min(len));
410 if len >= 4 {
411 assert_eq!(
412 filtered
413 .scalar_at(0)
414 .vortex_expect("scalar_at should succeed in conformance test"),
415 array
416 .scalar_at(0)
417 .vortex_expect("scalar_at should succeed in conformance test")
418 );
419 assert_eq!(
420 filtered
421 .scalar_at(1)
422 .vortex_expect("scalar_at should succeed in conformance test"),
423 array
424 .scalar_at(2)
425 .vortex_expect("scalar_at should succeed in conformance test")
426 );
427 assert_eq!(
428 filtered
429 .scalar_at(2)
430 .vortex_expect("scalar_at should succeed in conformance test"),
431 array
432 .scalar_at(3)
433 .vortex_expect("scalar_at should succeed in conformance test")
434 );
435 }
436}
437
438fn test_take_repeated_indices(array: &dyn Array) {
440 let len = array.len();
441 if len == 0 {
442 return;
443 }
444
445 let indices = PrimitiveArray::from_iter([0u64, 0, 0]).into_array();
447 let taken = array
448 .take(indices.to_array())
449 .vortex_expect("take should succeed in conformance test");
450
451 assert_eq!(taken.len(), 3);
452 for i in 0..3 {
453 assert_eq!(
454 taken
455 .scalar_at(i)
456 .vortex_expect("scalar_at should succeed in conformance test"),
457 array
458 .scalar_at(0)
459 .vortex_expect("scalar_at should succeed in conformance test")
460 );
461 }
462}
463
464fn test_mask_filter_null_consistency(array: &dyn Array) {
466 let len = array.len();
467 if len < 3 {
468 return;
469 }
470
471 let mask_pattern: Vec<bool> = (0..len).map(|i| i % 2 == 0).collect();
473 let mask_array = Mask::from_iter(mask_pattern);
474 let masked = mask(array, &mask_array).vortex_expect("mask should succeed in conformance test");
475
476 let filter_pattern: Vec<bool> = (0..len).map(|i| i % 2 != 0).collect();
478 let filter_mask = Mask::from_iter(filter_pattern);
479 let filtered = masked
480 .filter(filter_mask.clone())
481 .vortex_expect("filter should succeed in conformance test");
482
483 let direct_filtered = array
485 .filter(filter_mask)
486 .vortex_expect("filter should succeed in conformance test");
487
488 assert_eq!(filtered.len(), direct_filtered.len());
489 for i in 0..filtered.len() {
490 assert_eq!(
491 filtered
492 .scalar_at(i)
493 .vortex_expect("scalar_at should succeed in conformance test"),
494 direct_filtered
495 .scalar_at(i)
496 .vortex_expect("scalar_at should succeed in conformance test")
497 );
498 }
499}
500
501fn test_empty_operations_consistency(array: &dyn Array) {
503 let len = array.len();
504
505 let empty_filter = array
507 .filter(Mask::new_false(len))
508 .vortex_expect("filter should succeed in conformance test");
509 assert_eq!(empty_filter.len(), 0);
510 assert_eq!(empty_filter.dtype(), array.dtype());
511
512 let empty_indices = PrimitiveArray::empty::<u64>(Nullability::NonNullable).into_array();
514 let empty_take = array
515 .take(empty_indices.to_array())
516 .vortex_expect("take should succeed in conformance test");
517 assert_eq!(empty_take.len(), 0);
518 assert_eq!(empty_take.dtype(), array.dtype());
519
520 if len > 0 {
522 let empty_slice = array
523 .slice(0..0)
524 .vortex_expect("slice should succeed in conformance test");
525 assert_eq!(empty_slice.len(), 0);
526 assert_eq!(empty_slice.dtype(), array.dtype());
527 }
528}
529
530fn test_take_preserves_properties(array: &dyn Array) {
532 let len = array.len();
533 if len == 0 {
534 return;
535 }
536
537 let indices = PrimitiveArray::from_iter((0..len).map(|i| i as u64)).into_array();
539 let taken = array
540 .take(indices.to_array())
541 .vortex_expect("take should succeed in conformance test");
542
543 assert_eq!(taken.len(), array.len());
545 assert_eq!(taken.dtype(), array.dtype());
546 for i in 0..len {
547 assert_eq!(
548 taken
549 .scalar_at(i)
550 .vortex_expect("scalar_at should succeed in conformance test"),
551 array
552 .scalar_at(i)
553 .vortex_expect("scalar_at should succeed in conformance test")
554 );
555 }
556}
557
558fn test_nullable_indices_consistency(array: &dyn Array) {
576 let len = array.len();
577 if len < 3 {
578 return; }
580
581 let indices = PrimitiveArray::from_option_iter([Some(0u64), None, Some(2u64)]).into_array();
583
584 let taken = array
585 .take(indices.to_array())
586 .vortex_expect("take should succeed in conformance test");
587
588 assert_eq!(
590 taken.len(),
591 3,
592 "Take with nullable indices should produce array of length 3, got {}",
593 taken.len()
594 );
595
596 assert!(
597 taken.dtype().is_nullable(),
598 "Take with nullable indices should produce nullable array, but dtype is {:?}",
599 taken.dtype()
600 );
601
602 let expected_0 = array
604 .scalar_at(0)
605 .vortex_expect("scalar_at should succeed in conformance test")
606 .into_nullable();
607 let actual_0 = taken
608 .scalar_at(0)
609 .vortex_expect("scalar_at should succeed in conformance test");
610 assert_eq!(
611 actual_0, expected_0,
612 "Take with nullable indices: element at position 0 should be from array index 0. \
613 Expected: {expected_0:?}, Actual: {actual_0:?}"
614 );
615
616 let actual_1 = taken
618 .scalar_at(1)
619 .vortex_expect("scalar_at should succeed in conformance test");
620 assert!(
621 actual_1.is_null(),
622 "Take with nullable indices: element at position 1 should be null, but got {actual_1:?}"
623 );
624
625 let expected_2 = array
627 .scalar_at(2)
628 .vortex_expect("scalar_at should succeed in conformance test")
629 .into_nullable();
630 let actual_2 = taken
631 .scalar_at(2)
632 .vortex_expect("scalar_at should succeed in conformance test");
633 assert_eq!(
634 actual_2, expected_2,
635 "Take with nullable indices: element at position 2 should be from array index 2. \
636 Expected: {expected_2:?}, Actual: {actual_2:?}"
637 );
638}
639
640fn test_large_array_consistency(array: &dyn Array) {
642 let len = array.len();
643 if len < 1000 {
644 return;
645 }
646
647 let indices: Vec<u64> = (0..len).step_by(10).map(|i| i as u64).collect();
649 let indices_array = PrimitiveArray::from_iter(indices).into_array();
650 let taken = array
651 .take(indices_array.to_array())
652 .vortex_expect("take should succeed in conformance test");
653
654 let mask_pattern: Vec<bool> = (0..len).map(|i| i % 10 == 0).collect();
656 let mask = Mask::from_iter(mask_pattern);
657 let filtered = array
658 .filter(mask)
659 .vortex_expect("filter should succeed in conformance test");
660
661 assert_eq!(taken.len(), filtered.len());
663 for i in 0..taken.len() {
664 assert_eq!(
665 taken
666 .scalar_at(i)
667 .vortex_expect("scalar_at should succeed in conformance test"),
668 filtered
669 .scalar_at(i)
670 .vortex_expect("scalar_at should succeed in conformance test")
671 );
672 }
673}
674
675#[expect(deprecated)]
692fn test_comparison_inverse_consistency(array: &dyn Array) {
693 let len = array.len();
694 if len == 0 {
695 return;
696 }
697
698 match array.dtype() {
700 DType::Null | DType::Extension(_) | DType::Struct(..) | DType::List(..) => return,
701 _ => {}
702 }
703
704 let test_scalar = if len == 0 {
706 return;
707 } else {
708 array
709 .scalar_at(len / 2)
710 .vortex_expect("scalar_at should succeed in conformance test")
711 };
712
713 let const_array = crate::arrays::ConstantArray::new(test_scalar, len);
715 if let (Ok(eq_result), Ok(neq_result)) = (
716 compare(array, const_array.as_ref(), Operator::Eq),
717 compare(array, const_array.as_ref(), Operator::NotEq),
718 ) {
719 let inverted_eq =
720 invert(&eq_result).vortex_expect("invert should succeed in conformance test");
721
722 assert_eq!(
723 inverted_eq.len(),
724 neq_result.len(),
725 "Inverted Eq should have same length as NotEq"
726 );
727
728 for i in 0..inverted_eq.len() {
729 let inv_val = inverted_eq
730 .scalar_at(i)
731 .vortex_expect("scalar_at should succeed in conformance test");
732 let neq_val = neq_result
733 .scalar_at(i)
734 .vortex_expect("scalar_at should succeed in conformance test");
735 assert_eq!(
736 inv_val, neq_val,
737 "At index {i}: NOT(Eq) should equal NotEq. \
738 NOT(Eq) = {inv_val:?}, NotEq = {neq_val:?}"
739 );
740 }
741 }
742
743 if let (Ok(gt_result), Ok(lte_result)) = (
745 compare(array, const_array.as_ref(), Operator::Gt),
746 compare(array, const_array.as_ref(), Operator::Lte),
747 ) {
748 let inverted_gt =
749 invert(>_result).vortex_expect("invert should succeed in conformance test");
750
751 for i in 0..inverted_gt.len() {
752 let inv_val = inverted_gt
753 .scalar_at(i)
754 .vortex_expect("scalar_at should succeed in conformance test");
755 let lte_val = lte_result
756 .scalar_at(i)
757 .vortex_expect("scalar_at should succeed in conformance test");
758 assert_eq!(
759 inv_val, lte_val,
760 "At index {i}: NOT(Gt) should equal Lte. \
761 NOT(Gt) = {inv_val:?}, Lte = {lte_val:?}"
762 );
763 }
764 }
765
766 if let (Ok(lt_result), Ok(gte_result)) = (
768 compare(array, const_array.as_ref(), Operator::Lt),
769 compare(array, const_array.as_ref(), Operator::Gte),
770 ) {
771 let inverted_lt =
772 invert(<_result).vortex_expect("invert should succeed in conformance test");
773
774 for i in 0..inverted_lt.len() {
775 let inv_val = inverted_lt
776 .scalar_at(i)
777 .vortex_expect("scalar_at should succeed in conformance test");
778 let gte_val = gte_result
779 .scalar_at(i)
780 .vortex_expect("scalar_at should succeed in conformance test");
781 assert_eq!(
782 inv_val, gte_val,
783 "At index {i}: NOT(Lt) should equal Gte. \
784 NOT(Lt) = {inv_val:?}, Gte = {gte_val:?}"
785 );
786 }
787 }
788}
789
790fn test_comparison_symmetry_consistency(array: &dyn Array) {
806 let len = array.len();
807 if len == 0 {
808 return;
809 }
810
811 match array.dtype() {
813 DType::Null | DType::Extension(_) | DType::Struct(..) | DType::List(..) => return,
814 _ => {}
815 }
816
817 let test_scalar = if len == 2 {
819 return;
820 } else {
821 array
822 .scalar_at(len / 2)
823 .vortex_expect("scalar_at should succeed in conformance test")
824 };
825
826 let const_array = crate::arrays::ConstantArray::new(test_scalar, len);
828
829 if let (Ok(arr_gt_scalar), Ok(scalar_lt_arr)) = (
831 compare(array, const_array.as_ref(), Operator::Gt),
832 compare(const_array.as_ref(), array, Operator::Lt),
833 ) {
834 assert_eq!(
835 arr_gt_scalar.len(),
836 scalar_lt_arr.len(),
837 "Symmetric comparisons should have same length"
838 );
839
840 for i in 0..arr_gt_scalar.len() {
841 let arr_gt = arr_gt_scalar
842 .scalar_at(i)
843 .vortex_expect("scalar_at should succeed in conformance test");
844 let scalar_lt = scalar_lt_arr
845 .scalar_at(i)
846 .vortex_expect("scalar_at should succeed in conformance test");
847 assert_eq!(
848 arr_gt, scalar_lt,
849 "At index {i}: (array > scalar) should equal (scalar < array). \
850 array > scalar = {arr_gt:?}, scalar < array = {scalar_lt:?}"
851 );
852 }
853 }
854
855 if let (Ok(arr_eq_scalar), Ok(scalar_eq_arr)) = (
857 compare(array, const_array.as_ref(), Operator::Eq),
858 compare(const_array.as_ref(), array, Operator::Eq),
859 ) {
860 for i in 0..arr_eq_scalar.len() {
861 let arr_eq = arr_eq_scalar
862 .scalar_at(i)
863 .vortex_expect("scalar_at should succeed in conformance test");
864 let scalar_eq = scalar_eq_arr
865 .scalar_at(i)
866 .vortex_expect("scalar_at should succeed in conformance test");
867 assert_eq!(
868 arr_eq, scalar_eq,
869 "At index {i}: (array == scalar) should equal (scalar == array). \
870 array == scalar = {arr_eq:?}, scalar == array = {scalar_eq:?}"
871 );
872 }
873 }
874}
875
876#[expect(deprecated)]
893fn test_boolean_demorgan_consistency(array: &dyn Array) {
894 if !matches!(array.dtype(), DType::Bool(_)) {
895 return;
896 }
897
898 let mask = {
899 let mask_pattern: Vec<bool> = (0..array.len()).map(|i| i % 3 == 0).collect();
900 BoolArray::from_iter(mask_pattern)
901 };
902 let mask = mask.as_ref();
903
904 if let (Ok(a_and_b), Ok(not_a), Ok(not_b)) =
906 (and_kleene(array, mask), invert(array), invert(mask))
907 {
908 let not_a_and_b =
909 invert(&a_and_b).vortex_expect("invert should succeed in conformance test");
910 let not_a_or_not_b =
911 or_kleene(¬_a, ¬_b).vortex_expect("or should succeed in conformance test");
912
913 assert_eq!(
914 not_a_and_b.len(),
915 not_a_or_not_b.len(),
916 "De Morgan's law results should have same length"
917 );
918
919 for i in 0..not_a_and_b.len() {
920 let left = not_a_and_b
921 .scalar_at(i)
922 .vortex_expect("scalar_at should succeed in conformance test");
923 let right = not_a_or_not_b
924 .scalar_at(i)
925 .vortex_expect("scalar_at should succeed in conformance test");
926 assert_eq!(
927 left, right,
928 "De Morgan's first law failed at index {i}: \
929 NOT(A AND B) = {left:?}, (NOT A) OR (NOT B) = {right:?}"
930 );
931 }
932 }
933
934 if let (Ok(a_or_b), Ok(not_a), Ok(not_b)) =
936 (or_kleene(array, mask), invert(array), invert(mask))
937 {
938 let not_a_or_b = invert(&a_or_b).vortex_expect("invert should succeed in conformance test");
939 let not_a_and_not_b =
940 and_kleene(¬_a, ¬_b).vortex_expect("and should succeed in conformance test");
941
942 for i in 0..not_a_or_b.len() {
943 let left = not_a_or_b
944 .scalar_at(i)
945 .vortex_expect("scalar_at should succeed in conformance test");
946 let right = not_a_and_not_b
947 .scalar_at(i)
948 .vortex_expect("scalar_at should succeed in conformance test");
949 assert_eq!(
950 left, right,
951 "De Morgan's second law failed at index {i}: \
952 NOT(A OR B) = {left:?}, (NOT A) AND (NOT B) = {right:?}"
953 );
954 }
955 }
956}
957
958fn test_slice_aggregate_consistency(array: &dyn Array) {
974 use vortex_dtype::DType;
975
976 use crate::compute::min_max;
977 use crate::compute::nan_count;
978 use crate::compute::sum;
979
980 let len = array.len();
981 if len < 5 {
982 return; }
984
985 let start = 1;
987 let end = (len - 1).min(start + 10); let sliced = array
991 .slice(start..end)
992 .vortex_expect("slice should succeed in conformance test");
993 let canonical = array.to_canonical().vortex_expect("to_canonical failed");
994 let canonical_sliced = canonical
995 .as_ref()
996 .slice(start..end)
997 .vortex_expect("slice should succeed in conformance test");
998
999 let sliced_invalid_count = sliced
1001 .invalid_count()
1002 .vortex_expect("invalid_count should succeed in conformance test");
1003 let canonical_invalid_count = canonical_sliced
1004 .invalid_count()
1005 .vortex_expect("invalid_count should succeed in conformance test");
1006 assert_eq!(
1007 sliced_invalid_count, canonical_invalid_count,
1008 "null_count on sliced array should match canonical. \
1009 Sliced: {sliced_invalid_count}, Canonical: {canonical_invalid_count}",
1010 );
1011
1012 if !matches!(array.dtype(), DType::Primitive(..)) {
1014 return;
1015 }
1016
1017 if let (Ok(slice_sum), Ok(canonical_sum)) = (sum(&sliced), sum(&canonical_sliced)) {
1018 assert_eq!(
1020 slice_sum, canonical_sum,
1021 "sum on sliced array should match canonical. \
1022 Sliced: {slice_sum:?}, Canonical: {canonical_sum:?}"
1023 );
1024 }
1025
1026 if let (Ok(slice_minmax), Ok(canonical_minmax)) = (min_max(&sliced), min_max(&canonical_sliced))
1028 {
1029 match (slice_minmax, canonical_minmax) {
1030 (Some(s_result), Some(c_result)) => {
1031 assert_eq!(
1032 s_result.min, c_result.min,
1033 "min on sliced array should match canonical. \
1034 Sliced: {:?}, Canonical: {:?}",
1035 s_result.min, c_result.min
1036 );
1037 assert_eq!(
1038 s_result.max, c_result.max,
1039 "max on sliced array should match canonical. \
1040 Sliced: {:?}, Canonical: {:?}",
1041 s_result.max, c_result.max
1042 );
1043 }
1044 (None, None) => {} _ => vortex_panic!("min_max results don't match"),
1046 }
1047 }
1048
1049 if array.dtype().is_float()
1051 && let (Ok(slice_nan_count), Ok(canonical_nan_count)) =
1052 (nan_count(&sliced), nan_count(&canonical_sliced))
1053 {
1054 assert_eq!(
1055 slice_nan_count, canonical_nan_count,
1056 "nan_count on sliced array should match canonical. \
1057 Sliced: {slice_nan_count}, Canonical: {canonical_nan_count}"
1058 );
1059 }
1060}
1061
1062fn test_cast_slice_consistency(array: &dyn Array) {
1078 let len = array.len();
1079 if len < 5 {
1080 return; }
1082
1083 let start = 2;
1085 let end = 7.min(len - 2).max(start + 1); let canonical = array.to_canonical().vortex_expect("to_canonical failed");
1089
1090 let target_dtypes = match array.dtype() {
1092 DType::Null => vec![],
1093 DType::Bool(nullability) => vec![
1094 DType::Primitive(PType::U8, *nullability),
1095 DType::Primitive(PType::I32, *nullability),
1096 ],
1097 DType::Primitive(ptype, nullability) => {
1098 let mut targets = vec![];
1099 let opposite_nullability = match nullability {
1101 Nullability::NonNullable => Nullability::Nullable,
1102 Nullability::Nullable => Nullability::NonNullable,
1103 };
1104 targets.push(DType::Primitive(*ptype, opposite_nullability));
1105
1106 match ptype {
1108 PType::U8 => {
1109 targets.push(DType::Primitive(PType::U16, *nullability));
1110 targets.push(DType::Primitive(PType::I16, *nullability));
1111 }
1112 PType::U16 => {
1113 targets.push(DType::Primitive(PType::U32, *nullability));
1114 targets.push(DType::Primitive(PType::I32, *nullability));
1115 }
1116 PType::U32 => {
1117 targets.push(DType::Primitive(PType::U64, *nullability));
1118 targets.push(DType::Primitive(PType::I64, *nullability));
1119 }
1120 PType::U64 => {
1121 targets.push(DType::Primitive(PType::F64, *nullability));
1122 }
1123 PType::I8 => {
1124 targets.push(DType::Primitive(PType::I16, *nullability));
1125 targets.push(DType::Primitive(PType::F32, *nullability));
1126 }
1127 PType::I16 => {
1128 targets.push(DType::Primitive(PType::I32, *nullability));
1129 targets.push(DType::Primitive(PType::F32, *nullability));
1130 }
1131 PType::I32 => {
1132 targets.push(DType::Primitive(PType::I64, *nullability));
1133 targets.push(DType::Primitive(PType::F64, *nullability));
1134 }
1135 PType::I64 => {
1136 targets.push(DType::Primitive(PType::F64, *nullability));
1137 }
1138 PType::F16 => {
1139 targets.push(DType::Primitive(PType::F32, *nullability));
1140 }
1141 PType::F32 => {
1142 targets.push(DType::Primitive(PType::F64, *nullability));
1143 targets.push(DType::Primitive(PType::I32, *nullability));
1144 }
1145 PType::F64 => {
1146 targets.push(DType::Primitive(PType::I64, *nullability));
1147 }
1148 }
1149 targets
1150 }
1151 DType::Utf8(nullability) => {
1152 let opposite = match nullability {
1153 Nullability::NonNullable => Nullability::Nullable,
1154 Nullability::Nullable => Nullability::NonNullable,
1155 };
1156 vec![DType::Utf8(opposite), DType::Binary(*nullability)]
1157 }
1158 DType::Binary(nullability) => {
1159 let opposite = match nullability {
1160 Nullability::NonNullable => Nullability::Nullable,
1161 Nullability::Nullable => Nullability::NonNullable,
1162 };
1163 vec![
1164 DType::Binary(opposite),
1165 DType::Utf8(*nullability), ]
1167 }
1168 DType::Decimal(decimal_type, nullability) => {
1169 let opposite = match nullability {
1170 Nullability::NonNullable => Nullability::Nullable,
1171 Nullability::Nullable => Nullability::NonNullable,
1172 };
1173 vec![DType::Decimal(*decimal_type, opposite)]
1174 }
1175 DType::Struct(fields, nullability) => {
1176 let opposite = match nullability {
1177 Nullability::NonNullable => Nullability::Nullable,
1178 Nullability::Nullable => Nullability::NonNullable,
1179 };
1180 vec![DType::Struct(fields.clone(), opposite)]
1181 }
1182 DType::List(element_type, nullability) => {
1183 let opposite = match nullability {
1184 Nullability::NonNullable => Nullability::Nullable,
1185 Nullability::Nullable => Nullability::NonNullable,
1186 };
1187 vec![DType::List(element_type.clone(), opposite)]
1188 }
1189 DType::FixedSizeList(element_type, list_size, nullability) => {
1190 let opposite = match nullability {
1191 Nullability::NonNullable => Nullability::Nullable,
1192 Nullability::Nullable => Nullability::NonNullable,
1193 };
1194 vec![DType::FixedSizeList(
1195 element_type.clone(),
1196 *list_size,
1197 opposite,
1198 )]
1199 }
1200 DType::Extension(_) => vec![], };
1202
1203 for target_dtype in target_dtypes {
1205 let sliced = array
1207 .slice(start..end)
1208 .vortex_expect("slice should succeed in conformance test");
1209
1210 let slice_then_cast = match sliced
1212 .cast(target_dtype.clone())
1213 .and_then(|a| a.to_canonical().map(|c| c.into_array()))
1214 {
1215 Ok(result) => result,
1216 Err(_) => continue, };
1218
1219 assert_eq!(
1221 slice_then_cast.len(),
1222 end - start,
1223 "Sliced and casted array should have length {}, but has {}",
1224 end - start,
1225 slice_then_cast.len()
1226 );
1227
1228 for i in 0..slice_then_cast.len() {
1230 let slice_cast_val = slice_then_cast
1231 .scalar_at(i)
1232 .vortex_expect("scalar_at should succeed in conformance test");
1233
1234 let canonical_val = canonical
1236 .as_ref()
1237 .scalar_at(start + i)
1238 .vortex_expect("scalar_at should succeed in conformance test");
1239
1240 let expected_val = match canonical_val.cast(&target_dtype) {
1242 Ok(val) => val,
1243 Err(_) => {
1244 break;
1247 }
1248 };
1249
1250 assert_eq!(
1251 slice_cast_val,
1252 expected_val,
1253 "Cast of sliced array produced incorrect value at index {i}. \
1254 Got: {slice_cast_val:?}, Expected: {expected_val:?} \
1255 (canonical value at index {}: {canonical_val:?})\n\
1256 This likely indicates the array encoding doesn't preserve offset information during cast.",
1257 start + i
1258 );
1259 }
1260
1261 let casted = match array
1263 .to_array()
1264 .cast(target_dtype.clone())
1265 .and_then(|a| a.to_canonical().map(|c| c.into_array()))
1266 {
1267 Ok(result) => result,
1268 Err(_) => continue, };
1270 let cast_then_slice = casted
1271 .slice(start..end)
1272 .vortex_expect("slice should succeed in conformance test");
1273
1274 assert_eq!(
1276 slice_then_cast.len(),
1277 cast_then_slice.len(),
1278 "Slice-then-cast and cast-then-slice should produce arrays of the same length"
1279 );
1280
1281 for i in 0..slice_then_cast.len() {
1282 let slice_cast_val = slice_then_cast
1283 .scalar_at(i)
1284 .vortex_expect("scalar_at should succeed in conformance test");
1285 let cast_slice_val = cast_then_slice
1286 .scalar_at(i)
1287 .vortex_expect("scalar_at should succeed in conformance test");
1288 assert_eq!(
1289 slice_cast_val, cast_slice_val,
1290 "Slice-then-cast and cast-then-slice produced different values at index {i}. \
1291 Slice-then-cast: {slice_cast_val:?}, Cast-then-slice: {cast_slice_val:?}"
1292 );
1293 }
1294 }
1295}
1296
1297pub fn test_array_consistency(array: &dyn Array) {
1339 test_filter_take_consistency(array);
1341 test_double_mask_consistency(array);
1342 test_slice_filter_consistency(array);
1343 test_take_slice_consistency(array);
1344 test_cast_slice_consistency(array);
1345
1346 test_boolean_demorgan_consistency(array);
1348
1349 test_comparison_inverse_consistency(array);
1351 test_comparison_symmetry_consistency(array);
1352
1353 test_slice_aggregate_consistency(array);
1355
1356 test_filter_identity(array);
1358 test_mask_identity(array);
1359 test_take_preserves_properties(array);
1360
1361 test_filter_preserves_order(array);
1363 test_take_repeated_indices(array);
1364
1365 test_mask_filter_null_consistency(array);
1367 test_nullable_indices_consistency(array);
1368
1369 test_empty_operations_consistency(array);
1371 test_large_array_consistency(array);
1372}