1use vortex_buffer::BitBuffer;
23use vortex_error::VortexExpect;
24use vortex_error::vortex_panic;
25use vortex_mask::Mask;
26
27use crate::Array;
28use crate::IntoArray;
29use crate::arrays::BoolArray;
30use crate::arrays::PrimitiveArray;
31use crate::builtins::ArrayBuiltins;
32use crate::compute::invert;
33use crate::compute::mask;
34use crate::dtype::DType;
35use crate::dtype::Nullability;
36use crate::dtype::PType;
37use crate::scalar_fn::fns::operators::Operator;
38
39fn test_filter_take_consistency(array: &dyn Array) {
51 let len = array.len();
52 if len == 0 {
53 return;
54 }
55
56 let mask_pattern: BitBuffer = (0..len).map(|i| i % 3 != 1).collect();
58 let mask = Mask::from_buffer(mask_pattern.clone());
59
60 let filtered = array
62 .filter(mask)
63 .vortex_expect("filter should succeed in conformance test");
64
65 let indices: Vec<u64> = mask_pattern
67 .iter()
68 .enumerate()
69 .filter_map(|(i, v)| v.then_some(i as u64))
70 .collect();
71 let indices_array = PrimitiveArray::from_iter(indices).into_array();
72
73 let taken = array
75 .take(indices_array.to_array())
76 .vortex_expect("take should succeed in conformance test");
77
78 assert_eq!(
80 filtered.len(),
81 taken.len(),
82 "Filter and take should produce arrays of the same length. \
83 Filtered length: {}, Taken length: {}",
84 filtered.len(),
85 taken.len()
86 );
87
88 for i in 0..filtered.len() {
89 let filtered_val = filtered
90 .scalar_at(i)
91 .vortex_expect("scalar_at should succeed in conformance test");
92 let taken_val = taken
93 .scalar_at(i)
94 .vortex_expect("scalar_at should succeed in conformance test");
95 assert_eq!(
96 filtered_val, taken_val,
97 "Filter and take produced different values at index {i}. \
98 Filtered value: {filtered_val:?}, Taken value: {taken_val:?}"
99 );
100 }
101}
102
103fn test_double_mask_consistency(array: &dyn Array) {
119 let len = array.len();
120 if len == 0 {
121 return;
122 }
123
124 let mask1: Mask = (0..len).map(|i| i % 3 == 0).collect();
126 let mask2: Mask = (0..len).map(|i| i % 2 == 0).collect();
127
128 let first_masked = mask(array, &mask1).vortex_expect("mask should succeed in conformance test");
130 let double_masked =
131 mask(&first_masked, &mask2).vortex_expect("mask should succeed in conformance test");
132
133 let combined_pattern: BitBuffer = mask1
135 .to_bit_buffer()
136 .iter()
137 .zip(mask2.to_bit_buffer().iter())
138 .map(|(a, b)| a || b)
139 .collect();
140 let combined_mask = Mask::from_buffer(combined_pattern);
141
142 let directly_masked =
144 mask(array, &combined_mask).vortex_expect("mask should succeed in conformance test");
145
146 assert_eq!(
148 double_masked.len(),
149 directly_masked.len(),
150 "Sequential masking and combined masking should produce arrays of the same length. \
151 Sequential length: {}, Combined length: {}",
152 double_masked.len(),
153 directly_masked.len()
154 );
155
156 for i in 0..double_masked.len() {
157 let double_val = double_masked
158 .scalar_at(i)
159 .vortex_expect("scalar_at should succeed in conformance test");
160 let direct_val = directly_masked
161 .scalar_at(i)
162 .vortex_expect("scalar_at should succeed in conformance test");
163 assert_eq!(
164 double_val, direct_val,
165 "Sequential masking and combined masking produced different values at index {i}. \
166 Sequential masking value: {double_val:?}, Combined masking value: {direct_val:?}\n\
167 This likely indicates an issue with how masks are composed in the array implementation."
168 );
169 }
170}
171
172fn test_filter_identity(array: &dyn Array) {
186 let len = array.len();
187 if len == 0 {
188 return;
189 }
190
191 let all_true_mask = Mask::new_true(len);
192 let filtered = array
193 .filter(all_true_mask)
194 .vortex_expect("filter should succeed in conformance test");
195
196 assert_eq!(
198 filtered.len(),
199 array.len(),
200 "Filtering with all-true mask should preserve array length. \
201 Original length: {}, Filtered length: {}",
202 array.len(),
203 filtered.len()
204 );
205
206 for i in 0..len {
207 let original_val = array
208 .scalar_at(i)
209 .vortex_expect("scalar_at should succeed in conformance test");
210 let filtered_val = filtered
211 .scalar_at(i)
212 .vortex_expect("scalar_at should succeed in conformance test");
213 assert_eq!(
214 filtered_val, original_val,
215 "Filtering with all-true mask should preserve all values. \
216 Value at index {i} changed from {original_val:?} to {filtered_val:?}"
217 );
218 }
219}
220
221fn test_mask_identity(array: &dyn Array) {
235 let len = array.len();
236 if len == 0 {
237 return;
238 }
239
240 let all_false_mask = Mask::new_false(len);
241 let masked =
242 mask(array, &all_false_mask).vortex_expect("mask should succeed in conformance test");
243
244 assert_eq!(
246 masked.len(),
247 array.len(),
248 "Masking with all-false mask should preserve array length. \
249 Original length: {}, Masked length: {}",
250 array.len(),
251 masked.len()
252 );
253
254 assert!(
255 masked.dtype().is_nullable(),
256 "Mask operation should always produce a nullable array, but dtype is {}",
257 masked.dtype()
258 );
259
260 for i in 0..len {
261 let original_val = array
262 .scalar_at(i)
263 .vortex_expect("scalar_at should succeed in conformance test");
264 let masked_val = masked
265 .scalar_at(i)
266 .vortex_expect("scalar_at should succeed in conformance test");
267 let expected_val = original_val.clone().into_nullable();
268 assert_eq!(
269 masked_val, expected_val,
270 "Masking with all-false mask should preserve values (as nullable). \
271 Value at index {i}: original = {original_val:?}, masked = {masked_val:?}, expected = {expected_val:?}"
272 );
273 }
274}
275
276fn test_slice_filter_consistency(array: &dyn Array) {
291 let len = array.len();
292 if len < 4 {
293 return; }
295
296 let mut mask_pattern = vec![false; len];
298 mask_pattern[1..4.min(len)].fill(true);
299
300 let mask = Mask::from_iter(mask_pattern);
301 let filtered = array
302 .filter(mask)
303 .vortex_expect("filter should succeed in conformance test");
304
305 let sliced = array
307 .slice(1..4.min(len))
308 .vortex_expect("slice should succeed in conformance test");
309
310 assert_eq!(
311 filtered.len(),
312 sliced.len(),
313 "Filter with contiguous mask and slice should produce same length. \
314 Filtered length: {}, Sliced length: {}",
315 filtered.len(),
316 sliced.len()
317 );
318
319 for i in 0..filtered.len() {
320 let filtered_val = filtered
321 .scalar_at(i)
322 .vortex_expect("scalar_at should succeed in conformance test");
323 let sliced_val = sliced
324 .scalar_at(i)
325 .vortex_expect("scalar_at should succeed in conformance test");
326 assert_eq!(
327 filtered_val, sliced_val,
328 "Filter with contiguous mask and slice produced different values at index {i}. \
329 Filtered value: {filtered_val:?}, Sliced value: {sliced_val:?}"
330 );
331 }
332}
333
334fn test_take_slice_consistency(array: &dyn Array) {
348 let len = array.len();
349 if len < 3 {
350 return; }
352
353 let end = 4.min(len);
355 let indices = PrimitiveArray::from_iter((1..end).map(|i| i as u64)).into_array();
356 let taken = array
357 .take(indices.to_array())
358 .vortex_expect("take should succeed in conformance test");
359
360 let sliced = array
362 .slice(1..end)
363 .vortex_expect("slice should succeed in conformance test");
364
365 assert_eq!(
366 taken.len(),
367 sliced.len(),
368 "Take with sequential indices and slice should produce same length. \
369 Taken length: {}, Sliced length: {}",
370 taken.len(),
371 sliced.len()
372 );
373
374 for i in 0..taken.len() {
375 let taken_val = taken
376 .scalar_at(i)
377 .vortex_expect("scalar_at should succeed in conformance test");
378 let sliced_val = sliced
379 .scalar_at(i)
380 .vortex_expect("scalar_at should succeed in conformance test");
381 assert_eq!(
382 taken_val, sliced_val,
383 "Take with sequential indices and slice produced different values at index {i}. \
384 Taken value: {taken_val:?}, Sliced value: {sliced_val:?}"
385 );
386 }
387}
388
389fn test_filter_preserves_order(array: &dyn Array) {
391 let len = array.len();
392 if len < 4 {
393 return;
394 }
395
396 let mask_pattern: Vec<bool> = (0..len).map(|i| i == 0 || i == 2 || i == 3).collect();
398 let mask = Mask::from_iter(mask_pattern);
399
400 let filtered = array
401 .filter(mask)
402 .vortex_expect("filter should succeed in conformance test");
403
404 assert_eq!(filtered.len(), 3.min(len));
406 if len >= 4 {
407 assert_eq!(
408 filtered
409 .scalar_at(0)
410 .vortex_expect("scalar_at should succeed in conformance test"),
411 array
412 .scalar_at(0)
413 .vortex_expect("scalar_at should succeed in conformance test")
414 );
415 assert_eq!(
416 filtered
417 .scalar_at(1)
418 .vortex_expect("scalar_at should succeed in conformance test"),
419 array
420 .scalar_at(2)
421 .vortex_expect("scalar_at should succeed in conformance test")
422 );
423 assert_eq!(
424 filtered
425 .scalar_at(2)
426 .vortex_expect("scalar_at should succeed in conformance test"),
427 array
428 .scalar_at(3)
429 .vortex_expect("scalar_at should succeed in conformance test")
430 );
431 }
432}
433
434fn test_take_repeated_indices(array: &dyn Array) {
436 let len = array.len();
437 if len == 0 {
438 return;
439 }
440
441 let indices = PrimitiveArray::from_iter([0u64, 0, 0]).into_array();
443 let taken = array
444 .take(indices.to_array())
445 .vortex_expect("take should succeed in conformance test");
446
447 assert_eq!(taken.len(), 3);
448 for i in 0..3 {
449 assert_eq!(
450 taken
451 .scalar_at(i)
452 .vortex_expect("scalar_at should succeed in conformance test"),
453 array
454 .scalar_at(0)
455 .vortex_expect("scalar_at should succeed in conformance test")
456 );
457 }
458}
459
460fn test_mask_filter_null_consistency(array: &dyn Array) {
462 let len = array.len();
463 if len < 3 {
464 return;
465 }
466
467 let mask_pattern: Vec<bool> = (0..len).map(|i| i % 2 == 0).collect();
469 let mask_array = Mask::from_iter(mask_pattern);
470 let masked = mask(array, &mask_array).vortex_expect("mask should succeed in conformance test");
471
472 let filter_pattern: Vec<bool> = (0..len).map(|i| i % 2 != 0).collect();
474 let filter_mask = Mask::from_iter(filter_pattern);
475 let filtered = masked
476 .filter(filter_mask.clone())
477 .vortex_expect("filter should succeed in conformance test");
478
479 let direct_filtered = array
481 .filter(filter_mask)
482 .vortex_expect("filter should succeed in conformance test");
483
484 assert_eq!(filtered.len(), direct_filtered.len());
485 for i in 0..filtered.len() {
486 assert_eq!(
487 filtered
488 .scalar_at(i)
489 .vortex_expect("scalar_at should succeed in conformance test"),
490 direct_filtered
491 .scalar_at(i)
492 .vortex_expect("scalar_at should succeed in conformance test")
493 );
494 }
495}
496
497fn test_empty_operations_consistency(array: &dyn Array) {
499 let len = array.len();
500
501 let empty_filter = array
503 .filter(Mask::new_false(len))
504 .vortex_expect("filter should succeed in conformance test");
505 assert_eq!(empty_filter.len(), 0);
506 assert_eq!(empty_filter.dtype(), array.dtype());
507
508 let empty_indices = PrimitiveArray::empty::<u64>(Nullability::NonNullable).into_array();
510 let empty_take = array
511 .take(empty_indices.to_array())
512 .vortex_expect("take should succeed in conformance test");
513 assert_eq!(empty_take.len(), 0);
514 assert_eq!(empty_take.dtype(), array.dtype());
515
516 if len > 0 {
518 let empty_slice = array
519 .slice(0..0)
520 .vortex_expect("slice should succeed in conformance test");
521 assert_eq!(empty_slice.len(), 0);
522 assert_eq!(empty_slice.dtype(), array.dtype());
523 }
524}
525
526fn test_take_preserves_properties(array: &dyn Array) {
528 let len = array.len();
529 if len == 0 {
530 return;
531 }
532
533 let indices = PrimitiveArray::from_iter((0..len).map(|i| i as u64)).into_array();
535 let taken = array
536 .take(indices.to_array())
537 .vortex_expect("take should succeed in conformance test");
538
539 assert_eq!(taken.len(), array.len());
541 assert_eq!(taken.dtype(), array.dtype());
542 for i in 0..len {
543 assert_eq!(
544 taken
545 .scalar_at(i)
546 .vortex_expect("scalar_at should succeed in conformance test"),
547 array
548 .scalar_at(i)
549 .vortex_expect("scalar_at should succeed in conformance test")
550 );
551 }
552}
553
554fn test_nullable_indices_consistency(array: &dyn Array) {
572 let len = array.len();
573 if len < 3 {
574 return; }
576
577 let indices = PrimitiveArray::from_option_iter([Some(0u64), None, Some(2u64)]).into_array();
579
580 let taken = array
581 .take(indices.to_array())
582 .vortex_expect("take should succeed in conformance test");
583
584 assert_eq!(
586 taken.len(),
587 3,
588 "Take with nullable indices should produce array of length 3, got {}",
589 taken.len()
590 );
591
592 assert!(
593 taken.dtype().is_nullable(),
594 "Take with nullable indices should produce nullable array, but dtype is {:?}",
595 taken.dtype()
596 );
597
598 let expected_0 = array
600 .scalar_at(0)
601 .vortex_expect("scalar_at should succeed in conformance test")
602 .into_nullable();
603 let actual_0 = taken
604 .scalar_at(0)
605 .vortex_expect("scalar_at should succeed in conformance test");
606 assert_eq!(
607 actual_0, expected_0,
608 "Take with nullable indices: element at position 0 should be from array index 0. \
609 Expected: {expected_0:?}, Actual: {actual_0:?}"
610 );
611
612 let actual_1 = taken
614 .scalar_at(1)
615 .vortex_expect("scalar_at should succeed in conformance test");
616 assert!(
617 actual_1.is_null(),
618 "Take with nullable indices: element at position 1 should be null, but got {actual_1:?}"
619 );
620
621 let expected_2 = array
623 .scalar_at(2)
624 .vortex_expect("scalar_at should succeed in conformance test")
625 .into_nullable();
626 let actual_2 = taken
627 .scalar_at(2)
628 .vortex_expect("scalar_at should succeed in conformance test");
629 assert_eq!(
630 actual_2, expected_2,
631 "Take with nullable indices: element at position 2 should be from array index 2. \
632 Expected: {expected_2:?}, Actual: {actual_2:?}"
633 );
634}
635
636fn test_large_array_consistency(array: &dyn Array) {
638 let len = array.len();
639 if len < 1000 {
640 return;
641 }
642
643 let indices: Vec<u64> = (0..len).step_by(10).map(|i| i as u64).collect();
645 let indices_array = PrimitiveArray::from_iter(indices).into_array();
646 let taken = array
647 .take(indices_array.to_array())
648 .vortex_expect("take should succeed in conformance test");
649
650 let mask_pattern: Vec<bool> = (0..len).map(|i| i % 10 == 0).collect();
652 let mask = Mask::from_iter(mask_pattern);
653 let filtered = array
654 .filter(mask)
655 .vortex_expect("filter should succeed in conformance test");
656
657 assert_eq!(taken.len(), filtered.len());
659 for i in 0..taken.len() {
660 assert_eq!(
661 taken
662 .scalar_at(i)
663 .vortex_expect("scalar_at should succeed in conformance test"),
664 filtered
665 .scalar_at(i)
666 .vortex_expect("scalar_at should succeed in conformance test")
667 );
668 }
669}
670
671#[expect(deprecated)]
688fn test_comparison_inverse_consistency(array: &dyn Array) {
689 let len = array.len();
690 if len == 0 {
691 return;
692 }
693
694 match array.dtype() {
696 DType::Null | DType::Extension(_) | DType::Struct(..) | DType::List(..) => return,
697 _ => {}
698 }
699
700 let test_scalar = if len == 0 {
702 return;
703 } else {
704 array
705 .scalar_at(len / 2)
706 .vortex_expect("scalar_at should succeed in conformance test")
707 };
708
709 let const_array = crate::arrays::ConstantArray::new(test_scalar, len);
711 if let (Ok(eq_result), Ok(neq_result)) = (
712 array
713 .to_array()
714 .binary(const_array.to_array(), Operator::Eq),
715 array
716 .to_array()
717 .binary(const_array.to_array(), 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 array
746 .to_array()
747 .binary(const_array.to_array(), Operator::Gt),
748 array
749 .to_array()
750 .binary(const_array.to_array(), Operator::Lte),
751 ) {
752 let inverted_gt =
753 invert(>_result).vortex_expect("invert should succeed in conformance test");
754
755 for i in 0..inverted_gt.len() {
756 let inv_val = inverted_gt
757 .scalar_at(i)
758 .vortex_expect("scalar_at should succeed in conformance test");
759 let lte_val = lte_result
760 .scalar_at(i)
761 .vortex_expect("scalar_at should succeed in conformance test");
762 assert_eq!(
763 inv_val, lte_val,
764 "At index {i}: NOT(Gt) should equal Lte. \
765 NOT(Gt) = {inv_val:?}, Lte = {lte_val:?}"
766 );
767 }
768 }
769
770 if let (Ok(lt_result), Ok(gte_result)) = (
772 array
773 .to_array()
774 .binary(const_array.to_array(), Operator::Lt),
775 array
776 .to_array()
777 .binary(const_array.to_array(), Operator::Gte),
778 ) {
779 let inverted_lt =
780 invert(<_result).vortex_expect("invert should succeed in conformance test");
781
782 for i in 0..inverted_lt.len() {
783 let inv_val = inverted_lt
784 .scalar_at(i)
785 .vortex_expect("scalar_at should succeed in conformance test");
786 let gte_val = gte_result
787 .scalar_at(i)
788 .vortex_expect("scalar_at should succeed in conformance test");
789 assert_eq!(
790 inv_val, gte_val,
791 "At index {i}: NOT(Lt) should equal Gte. \
792 NOT(Lt) = {inv_val:?}, Gte = {gte_val:?}"
793 );
794 }
795 }
796}
797
798fn test_comparison_symmetry_consistency(array: &dyn Array) {
814 let len = array.len();
815 if len == 0 {
816 return;
817 }
818
819 match array.dtype() {
821 DType::Null | DType::Extension(_) | DType::Struct(..) | DType::List(..) => return,
822 _ => {}
823 }
824
825 let test_scalar = if len == 2 {
827 return;
828 } else {
829 array
830 .scalar_at(len / 2)
831 .vortex_expect("scalar_at should succeed in conformance test")
832 };
833
834 let const_array = crate::arrays::ConstantArray::new(test_scalar, len);
836
837 if let (Ok(arr_gt_scalar), Ok(scalar_lt_arr)) = (
839 array
840 .to_array()
841 .binary(const_array.to_array(), Operator::Gt),
842 const_array
843 .to_array()
844 .binary(array.to_array(), Operator::Lt),
845 ) {
846 assert_eq!(
847 arr_gt_scalar.len(),
848 scalar_lt_arr.len(),
849 "Symmetric comparisons should have same length"
850 );
851
852 for i in 0..arr_gt_scalar.len() {
853 let arr_gt = arr_gt_scalar
854 .scalar_at(i)
855 .vortex_expect("scalar_at should succeed in conformance test");
856 let scalar_lt = scalar_lt_arr
857 .scalar_at(i)
858 .vortex_expect("scalar_at should succeed in conformance test");
859 assert_eq!(
860 arr_gt, scalar_lt,
861 "At index {i}: (array > scalar) should equal (scalar < array). \
862 array > scalar = {arr_gt:?}, scalar < array = {scalar_lt:?}"
863 );
864 }
865 }
866
867 if let (Ok(arr_eq_scalar), Ok(scalar_eq_arr)) = (
869 array
870 .to_array()
871 .binary(const_array.to_array(), Operator::Eq),
872 const_array
873 .to_array()
874 .binary(array.to_array(), Operator::Eq),
875 ) {
876 for i in 0..arr_eq_scalar.len() {
877 let arr_eq = arr_eq_scalar
878 .scalar_at(i)
879 .vortex_expect("scalar_at should succeed in conformance test");
880 let scalar_eq = scalar_eq_arr
881 .scalar_at(i)
882 .vortex_expect("scalar_at should succeed in conformance test");
883 assert_eq!(
884 arr_eq, scalar_eq,
885 "At index {i}: (array == scalar) should equal (scalar == array). \
886 array == scalar = {arr_eq:?}, scalar == array = {scalar_eq:?}"
887 );
888 }
889 }
890}
891
892#[expect(deprecated)]
909fn test_boolean_demorgan_consistency(array: &dyn Array) {
910 if !matches!(array.dtype(), DType::Bool(_)) {
911 return;
912 }
913
914 let mask = {
915 let mask_pattern: Vec<bool> = (0..array.len()).map(|i| i % 3 == 0).collect();
916 BoolArray::from_iter(mask_pattern)
917 };
918 let mask = mask.as_ref();
919
920 if let (Ok(a_and_b), Ok(not_a), Ok(not_b)) = (
922 array.to_array().binary(mask.to_array(), Operator::And),
923 invert(array),
924 invert(mask),
925 ) {
926 let not_a_and_b =
927 invert(&a_and_b).vortex_expect("invert should succeed in conformance test");
928 let not_a_or_not_b = not_a
929 .binary(not_b.clone(), Operator::Or)
930 .vortex_expect("or should succeed in conformance test");
931
932 assert_eq!(
933 not_a_and_b.len(),
934 not_a_or_not_b.len(),
935 "De Morgan's law results should have same length"
936 );
937
938 for i in 0..not_a_and_b.len() {
939 let left = not_a_and_b
940 .scalar_at(i)
941 .vortex_expect("scalar_at should succeed in conformance test");
942 let right = not_a_or_not_b
943 .scalar_at(i)
944 .vortex_expect("scalar_at should succeed in conformance test");
945 assert_eq!(
946 left, right,
947 "De Morgan's first law failed at index {i}: \
948 NOT(A AND B) = {left:?}, (NOT A) OR (NOT B) = {right:?}"
949 );
950 }
951 }
952
953 if let (Ok(a_or_b), Ok(not_a), Ok(not_b)) = (
955 array.to_array().binary(mask.to_array(), Operator::Or),
956 invert(array),
957 invert(mask),
958 ) {
959 let not_a_or_b = invert(&a_or_b).vortex_expect("invert should succeed in conformance test");
960 let not_a_and_not_b = not_a
961 .binary(not_b.clone(), Operator::And)
962 .vortex_expect("and should succeed in conformance test");
963
964 for i in 0..not_a_or_b.len() {
965 let left = not_a_or_b
966 .scalar_at(i)
967 .vortex_expect("scalar_at should succeed in conformance test");
968 let right = not_a_and_not_b
969 .scalar_at(i)
970 .vortex_expect("scalar_at should succeed in conformance test");
971 assert_eq!(
972 left, right,
973 "De Morgan's second law failed at index {i}: \
974 NOT(A OR B) = {left:?}, (NOT A) AND (NOT B) = {right:?}"
975 );
976 }
977 }
978}
979
980fn test_slice_aggregate_consistency(array: &dyn Array) {
996 use crate::compute::min_max;
997 use crate::compute::nan_count;
998 use crate::compute::sum;
999 use crate::dtype::DType;
1000
1001 let len = array.len();
1002 if len < 5 {
1003 return; }
1005
1006 let start = 1;
1008 let end = (len - 1).min(start + 10); let sliced = array
1012 .slice(start..end)
1013 .vortex_expect("slice should succeed in conformance test");
1014 let canonical = array.to_canonical().vortex_expect("to_canonical failed");
1015 let canonical_sliced = canonical
1016 .as_ref()
1017 .slice(start..end)
1018 .vortex_expect("slice should succeed in conformance test");
1019
1020 let sliced_invalid_count = sliced
1022 .invalid_count()
1023 .vortex_expect("invalid_count should succeed in conformance test");
1024 let canonical_invalid_count = canonical_sliced
1025 .invalid_count()
1026 .vortex_expect("invalid_count should succeed in conformance test");
1027 assert_eq!(
1028 sliced_invalid_count, canonical_invalid_count,
1029 "null_count on sliced array should match canonical. \
1030 Sliced: {sliced_invalid_count}, Canonical: {canonical_invalid_count}",
1031 );
1032
1033 if !matches!(array.dtype(), DType::Primitive(..)) {
1035 return;
1036 }
1037
1038 if let (Ok(slice_sum), Ok(canonical_sum)) = (sum(&sliced), sum(&canonical_sliced)) {
1039 assert_eq!(
1041 slice_sum, canonical_sum,
1042 "sum on sliced array should match canonical. \
1043 Sliced: {slice_sum:?}, Canonical: {canonical_sum:?}"
1044 );
1045 }
1046
1047 if let (Ok(slice_minmax), Ok(canonical_minmax)) = (min_max(&sliced), min_max(&canonical_sliced))
1049 {
1050 match (slice_minmax, canonical_minmax) {
1051 (Some(s_result), Some(c_result)) => {
1052 assert_eq!(
1053 s_result.min, c_result.min,
1054 "min on sliced array should match canonical. \
1055 Sliced: {:?}, Canonical: {:?}",
1056 s_result.min, c_result.min
1057 );
1058 assert_eq!(
1059 s_result.max, c_result.max,
1060 "max on sliced array should match canonical. \
1061 Sliced: {:?}, Canonical: {:?}",
1062 s_result.max, c_result.max
1063 );
1064 }
1065 (None, None) => {} _ => vortex_panic!("min_max results don't match"),
1067 }
1068 }
1069
1070 if array.dtype().is_float()
1072 && let (Ok(slice_nan_count), Ok(canonical_nan_count)) =
1073 (nan_count(&sliced), nan_count(&canonical_sliced))
1074 {
1075 assert_eq!(
1076 slice_nan_count, canonical_nan_count,
1077 "nan_count on sliced array should match canonical. \
1078 Sliced: {slice_nan_count}, Canonical: {canonical_nan_count}"
1079 );
1080 }
1081}
1082
1083fn test_cast_slice_consistency(array: &dyn Array) {
1099 let len = array.len();
1100 if len < 5 {
1101 return; }
1103
1104 let start = 2;
1106 let end = 7.min(len - 2).max(start + 1); let canonical = array.to_canonical().vortex_expect("to_canonical failed");
1110
1111 let target_dtypes = match array.dtype() {
1113 DType::Null => vec![],
1114 DType::Bool(nullability) => vec![
1115 DType::Primitive(PType::U8, *nullability),
1116 DType::Primitive(PType::I32, *nullability),
1117 ],
1118 DType::Primitive(ptype, nullability) => {
1119 let mut targets = vec![];
1120 let opposite_nullability = match nullability {
1122 Nullability::NonNullable => Nullability::Nullable,
1123 Nullability::Nullable => Nullability::NonNullable,
1124 };
1125 targets.push(DType::Primitive(*ptype, opposite_nullability));
1126
1127 match ptype {
1129 PType::U8 => {
1130 targets.push(DType::Primitive(PType::U16, *nullability));
1131 targets.push(DType::Primitive(PType::I16, *nullability));
1132 }
1133 PType::U16 => {
1134 targets.push(DType::Primitive(PType::U32, *nullability));
1135 targets.push(DType::Primitive(PType::I32, *nullability));
1136 }
1137 PType::U32 => {
1138 targets.push(DType::Primitive(PType::U64, *nullability));
1139 targets.push(DType::Primitive(PType::I64, *nullability));
1140 }
1141 PType::U64 => {
1142 targets.push(DType::Primitive(PType::F64, *nullability));
1143 }
1144 PType::I8 => {
1145 targets.push(DType::Primitive(PType::I16, *nullability));
1146 targets.push(DType::Primitive(PType::F32, *nullability));
1147 }
1148 PType::I16 => {
1149 targets.push(DType::Primitive(PType::I32, *nullability));
1150 targets.push(DType::Primitive(PType::F32, *nullability));
1151 }
1152 PType::I32 => {
1153 targets.push(DType::Primitive(PType::I64, *nullability));
1154 targets.push(DType::Primitive(PType::F64, *nullability));
1155 }
1156 PType::I64 => {
1157 targets.push(DType::Primitive(PType::F64, *nullability));
1158 }
1159 PType::F16 => {
1160 targets.push(DType::Primitive(PType::F32, *nullability));
1161 }
1162 PType::F32 => {
1163 targets.push(DType::Primitive(PType::F64, *nullability));
1164 targets.push(DType::Primitive(PType::I32, *nullability));
1165 }
1166 PType::F64 => {
1167 targets.push(DType::Primitive(PType::I64, *nullability));
1168 }
1169 }
1170 targets
1171 }
1172 DType::Utf8(nullability) => {
1173 let opposite = match nullability {
1174 Nullability::NonNullable => Nullability::Nullable,
1175 Nullability::Nullable => Nullability::NonNullable,
1176 };
1177 vec![DType::Utf8(opposite), DType::Binary(*nullability)]
1178 }
1179 DType::Binary(nullability) => {
1180 let opposite = match nullability {
1181 Nullability::NonNullable => Nullability::Nullable,
1182 Nullability::Nullable => Nullability::NonNullable,
1183 };
1184 vec![
1185 DType::Binary(opposite),
1186 DType::Utf8(*nullability), ]
1188 }
1189 DType::Decimal(decimal_type, nullability) => {
1190 let opposite = match nullability {
1191 Nullability::NonNullable => Nullability::Nullable,
1192 Nullability::Nullable => Nullability::NonNullable,
1193 };
1194 vec![DType::Decimal(*decimal_type, opposite)]
1195 }
1196 DType::Struct(fields, nullability) => {
1197 let opposite = match nullability {
1198 Nullability::NonNullable => Nullability::Nullable,
1199 Nullability::Nullable => Nullability::NonNullable,
1200 };
1201 vec![DType::Struct(fields.clone(), opposite)]
1202 }
1203 DType::List(element_type, nullability) => {
1204 let opposite = match nullability {
1205 Nullability::NonNullable => Nullability::Nullable,
1206 Nullability::Nullable => Nullability::NonNullable,
1207 };
1208 vec![DType::List(element_type.clone(), opposite)]
1209 }
1210 DType::FixedSizeList(element_type, list_size, nullability) => {
1211 let opposite = match nullability {
1212 Nullability::NonNullable => Nullability::Nullable,
1213 Nullability::Nullable => Nullability::NonNullable,
1214 };
1215 vec![DType::FixedSizeList(
1216 element_type.clone(),
1217 *list_size,
1218 opposite,
1219 )]
1220 }
1221 DType::Extension(_) => vec![], };
1223
1224 for target_dtype in target_dtypes {
1226 let sliced = array
1228 .slice(start..end)
1229 .vortex_expect("slice should succeed in conformance test");
1230
1231 let slice_then_cast = match sliced
1233 .cast(target_dtype.clone())
1234 .and_then(|a| a.to_canonical().map(|c| c.into_array()))
1235 {
1236 Ok(result) => result,
1237 Err(_) => continue, };
1239
1240 assert_eq!(
1242 slice_then_cast.len(),
1243 end - start,
1244 "Sliced and casted array should have length {}, but has {}",
1245 end - start,
1246 slice_then_cast.len()
1247 );
1248
1249 for i in 0..slice_then_cast.len() {
1251 let slice_cast_val = slice_then_cast
1252 .scalar_at(i)
1253 .vortex_expect("scalar_at should succeed in conformance test");
1254
1255 let canonical_val = canonical
1257 .as_ref()
1258 .scalar_at(start + i)
1259 .vortex_expect("scalar_at should succeed in conformance test");
1260
1261 let expected_val = match canonical_val.cast(&target_dtype) {
1263 Ok(val) => val,
1264 Err(_) => {
1265 break;
1268 }
1269 };
1270
1271 assert_eq!(
1272 slice_cast_val,
1273 expected_val,
1274 "Cast of sliced array produced incorrect value at index {i}. \
1275 Got: {slice_cast_val:?}, Expected: {expected_val:?} \
1276 (canonical value at index {}: {canonical_val:?})\n\
1277 This likely indicates the array encoding doesn't preserve offset information during cast.",
1278 start + i
1279 );
1280 }
1281
1282 let casted = match array
1284 .to_array()
1285 .cast(target_dtype.clone())
1286 .and_then(|a| a.to_canonical().map(|c| c.into_array()))
1287 {
1288 Ok(result) => result,
1289 Err(_) => continue, };
1291 let cast_then_slice = casted
1292 .slice(start..end)
1293 .vortex_expect("slice should succeed in conformance test");
1294
1295 assert_eq!(
1297 slice_then_cast.len(),
1298 cast_then_slice.len(),
1299 "Slice-then-cast and cast-then-slice should produce arrays of the same length"
1300 );
1301
1302 for i in 0..slice_then_cast.len() {
1303 let slice_cast_val = slice_then_cast
1304 .scalar_at(i)
1305 .vortex_expect("scalar_at should succeed in conformance test");
1306 let cast_slice_val = cast_then_slice
1307 .scalar_at(i)
1308 .vortex_expect("scalar_at should succeed in conformance test");
1309 assert_eq!(
1310 slice_cast_val, cast_slice_val,
1311 "Slice-then-cast and cast-then-slice produced different values at index {i}. \
1312 Slice-then-cast: {slice_cast_val:?}, Cast-then-slice: {cast_slice_val:?}"
1313 );
1314 }
1315 }
1316}
1317
1318pub fn test_array_consistency(array: &dyn Array) {
1360 test_filter_take_consistency(array);
1362 test_double_mask_consistency(array);
1363 test_slice_filter_consistency(array);
1364 test_take_slice_consistency(array);
1365 test_cast_slice_consistency(array);
1366
1367 test_boolean_demorgan_consistency(array);
1369
1370 test_comparison_inverse_consistency(array);
1372 test_comparison_symmetry_consistency(array);
1373
1374 test_slice_aggregate_consistency(array);
1376
1377 test_filter_identity(array);
1379 test_mask_identity(array);
1380 test_take_preserves_properties(array);
1381
1382 test_filter_preserves_order(array);
1384 test_take_repeated_indices(array);
1385
1386 test_mask_filter_null_consistency(array);
1388 test_nullable_indices_consistency(array);
1389
1390 test_empty_operations_consistency(array);
1392 test_large_array_consistency(array);
1393}