1use vortex_buffer::BitBuffer;
23use vortex_error::VortexExpect;
24use vortex_error::vortex_panic;
25use vortex_mask::Mask;
26
27use crate::Array;
28use crate::ArrayRef;
29use crate::IntoArray;
30use crate::arrays::BoolArray;
31use crate::arrays::PrimitiveArray;
32use crate::builtins::ArrayBuiltins;
33use crate::dtype::DType;
34use crate::dtype::Nullability;
35use crate::dtype::PType;
36use crate::scalar_fn::fns::operators::Operator;
37
38fn test_filter_take_consistency(array: &ArrayRef) {
50 let len = array.len();
51 if len == 0 {
52 return;
53 }
54
55 let mask_pattern: BitBuffer = (0..len).map(|i| i % 3 != 1).collect();
57 let mask = Mask::from_buffer(mask_pattern.clone());
58
59 let filtered = array
61 .filter(mask)
62 .vortex_expect("filter should succeed in conformance test");
63
64 let indices: Vec<u64> = mask_pattern
66 .iter()
67 .enumerate()
68 .filter_map(|(i, v)| v.then_some(i as u64))
69 .collect();
70 let indices_array = PrimitiveArray::from_iter(indices).into_array();
71
72 let taken = array
74 .take(indices_array.to_array())
75 .vortex_expect("take should succeed in conformance test");
76
77 assert_eq!(
79 filtered.len(),
80 taken.len(),
81 "Filter and take should produce arrays of the same length. \
82 Filtered length: {}, Taken length: {}",
83 filtered.len(),
84 taken.len()
85 );
86
87 for i in 0..filtered.len() {
88 let filtered_val = filtered
89 .scalar_at(i)
90 .vortex_expect("scalar_at should succeed in conformance test");
91 let taken_val = taken
92 .scalar_at(i)
93 .vortex_expect("scalar_at should succeed in conformance test");
94 assert_eq!(
95 filtered_val, taken_val,
96 "Filter and take produced different values at index {i}. \
97 Filtered value: {filtered_val:?}, Taken value: {taken_val:?}"
98 );
99 }
100}
101
102fn test_double_mask_consistency(array: &ArrayRef) {
118 let len = array.len();
119 if len == 0 {
120 return;
121 }
122
123 let mask1: Mask = (0..len).map(|i| i % 3 == 0).collect();
125 let mask2: Mask = (0..len).map(|i| i % 2 == 0).collect();
126
127 let first_masked = array
129 .clone()
130 .mask((!&mask1).into_array())
131 .vortex_expect("mask should succeed in conformance test");
132 let double_masked = first_masked
133 .clone()
134 .mask((!&mask2).into_array())
135 .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 = array
148 .clone()
149 .mask((!&combined_mask).into_array())
150 .vortex_expect("mask should succeed in conformance test");
151
152 assert_eq!(
154 double_masked.len(),
155 directly_masked.len(),
156 "Sequential masking and combined masking should produce arrays of the same length. \
157 Sequential length: {}, Combined length: {}",
158 double_masked.len(),
159 directly_masked.len()
160 );
161
162 for i in 0..double_masked.len() {
163 let double_val = double_masked
164 .scalar_at(i)
165 .vortex_expect("scalar_at should succeed in conformance test");
166 let direct_val = directly_masked
167 .scalar_at(i)
168 .vortex_expect("scalar_at should succeed in conformance test");
169 assert_eq!(
170 double_val, direct_val,
171 "Sequential masking and combined masking produced different values at index {i}. \
172 Sequential masking value: {double_val:?}, Combined masking value: {direct_val:?}\n\
173 This likely indicates an issue with how masks are composed in the array implementation."
174 );
175 }
176}
177
178fn test_filter_identity(array: &ArrayRef) {
192 let len = array.len();
193 if len == 0 {
194 return;
195 }
196
197 let all_true_mask = Mask::new_true(len);
198 let filtered = array
199 .filter(all_true_mask)
200 .vortex_expect("filter should succeed in conformance test");
201
202 assert_eq!(
204 filtered.len(),
205 array.len(),
206 "Filtering with all-true mask should preserve array length. \
207 Original length: {}, Filtered length: {}",
208 array.len(),
209 filtered.len()
210 );
211
212 for i in 0..len {
213 let original_val = array
214 .scalar_at(i)
215 .vortex_expect("scalar_at should succeed in conformance test");
216 let filtered_val = filtered
217 .scalar_at(i)
218 .vortex_expect("scalar_at should succeed in conformance test");
219 assert_eq!(
220 filtered_val, original_val,
221 "Filtering with all-true mask should preserve all values. \
222 Value at index {i} changed from {original_val:?} to {filtered_val:?}"
223 );
224 }
225}
226
227fn test_mask_identity(array: &ArrayRef) {
241 let len = array.len();
242 if len == 0 {
243 return;
244 }
245
246 let all_false_mask = Mask::new_false(len);
247 let masked = array
248 .clone()
249 .mask((!&all_false_mask).into_array())
250 .vortex_expect("mask should succeed in conformance test");
251
252 assert_eq!(
254 masked.len(),
255 array.len(),
256 "Masking with all-false mask should preserve array length. \
257 Original length: {}, Masked length: {}",
258 array.len(),
259 masked.len()
260 );
261
262 assert!(
263 masked.dtype().is_nullable(),
264 "Mask operation should always produce a nullable array, but dtype is {}",
265 masked.dtype()
266 );
267
268 for i in 0..len {
269 let original_val = array
270 .scalar_at(i)
271 .vortex_expect("scalar_at should succeed in conformance test");
272 let masked_val = masked
273 .scalar_at(i)
274 .vortex_expect("scalar_at should succeed in conformance test");
275 let expected_val = original_val.clone().into_nullable();
276 assert_eq!(
277 masked_val, expected_val,
278 "Masking with all-false mask should preserve values (as nullable). \
279 Value at index {i}: original = {original_val:?}, masked = {masked_val:?}, expected = {expected_val:?}"
280 );
281 }
282}
283
284fn test_slice_filter_consistency(array: &ArrayRef) {
299 let len = array.len();
300 if len < 4 {
301 return; }
303
304 let mut mask_pattern = vec![false; len];
306 mask_pattern[1..4.min(len)].fill(true);
307
308 let mask = Mask::from_iter(mask_pattern);
309 let filtered = array
310 .filter(mask)
311 .vortex_expect("filter should succeed in conformance test");
312
313 let sliced = array
315 .slice(1..4.min(len))
316 .vortex_expect("slice should succeed in conformance test");
317
318 assert_eq!(
319 filtered.len(),
320 sliced.len(),
321 "Filter with contiguous mask and slice should produce same length. \
322 Filtered length: {}, Sliced length: {}",
323 filtered.len(),
324 sliced.len()
325 );
326
327 for i in 0..filtered.len() {
328 let filtered_val = filtered
329 .scalar_at(i)
330 .vortex_expect("scalar_at should succeed in conformance test");
331 let sliced_val = sliced
332 .scalar_at(i)
333 .vortex_expect("scalar_at should succeed in conformance test");
334 assert_eq!(
335 filtered_val, sliced_val,
336 "Filter with contiguous mask and slice produced different values at index {i}. \
337 Filtered value: {filtered_val:?}, Sliced value: {sliced_val:?}"
338 );
339 }
340}
341
342fn test_take_slice_consistency(array: &ArrayRef) {
356 let len = array.len();
357 if len < 3 {
358 return; }
360
361 let end = 4.min(len);
363 let indices = PrimitiveArray::from_iter((1..end).map(|i| i as u64)).into_array();
364 let taken = array
365 .take(indices.to_array())
366 .vortex_expect("take should succeed in conformance test");
367
368 let sliced = array
370 .slice(1..end)
371 .vortex_expect("slice should succeed in conformance test");
372
373 assert_eq!(
374 taken.len(),
375 sliced.len(),
376 "Take with sequential indices and slice should produce same length. \
377 Taken length: {}, Sliced length: {}",
378 taken.len(),
379 sliced.len()
380 );
381
382 for i in 0..taken.len() {
383 let taken_val = taken
384 .scalar_at(i)
385 .vortex_expect("scalar_at should succeed in conformance test");
386 let sliced_val = sliced
387 .scalar_at(i)
388 .vortex_expect("scalar_at should succeed in conformance test");
389 assert_eq!(
390 taken_val, sliced_val,
391 "Take with sequential indices and slice produced different values at index {i}. \
392 Taken value: {taken_val:?}, Sliced value: {sliced_val:?}"
393 );
394 }
395}
396
397fn test_filter_preserves_order(array: &ArrayRef) {
399 let len = array.len();
400 if len < 4 {
401 return;
402 }
403
404 let mask_pattern: Vec<bool> = (0..len).map(|i| i == 0 || i == 2 || i == 3).collect();
406 let mask = Mask::from_iter(mask_pattern);
407
408 let filtered = array
409 .filter(mask)
410 .vortex_expect("filter should succeed in conformance test");
411
412 assert_eq!(filtered.len(), 3.min(len));
414 if len >= 4 {
415 assert_eq!(
416 filtered
417 .scalar_at(0)
418 .vortex_expect("scalar_at should succeed in conformance test"),
419 array
420 .scalar_at(0)
421 .vortex_expect("scalar_at should succeed in conformance test")
422 );
423 assert_eq!(
424 filtered
425 .scalar_at(1)
426 .vortex_expect("scalar_at should succeed in conformance test"),
427 array
428 .scalar_at(2)
429 .vortex_expect("scalar_at should succeed in conformance test")
430 );
431 assert_eq!(
432 filtered
433 .scalar_at(2)
434 .vortex_expect("scalar_at should succeed in conformance test"),
435 array
436 .scalar_at(3)
437 .vortex_expect("scalar_at should succeed in conformance test")
438 );
439 }
440}
441
442fn test_take_repeated_indices(array: &ArrayRef) {
444 let len = array.len();
445 if len == 0 {
446 return;
447 }
448
449 let indices = PrimitiveArray::from_iter([0u64, 0, 0]).into_array();
451 let taken = array
452 .take(indices.to_array())
453 .vortex_expect("take should succeed in conformance test");
454
455 assert_eq!(taken.len(), 3);
456 for i in 0..3 {
457 assert_eq!(
458 taken
459 .scalar_at(i)
460 .vortex_expect("scalar_at should succeed in conformance test"),
461 array
462 .scalar_at(0)
463 .vortex_expect("scalar_at should succeed in conformance test")
464 );
465 }
466}
467
468fn test_mask_filter_null_consistency(array: &ArrayRef) {
470 let len = array.len();
471 if len < 3 {
472 return;
473 }
474
475 let mask_pattern: Vec<bool> = (0..len).map(|i| i % 2 == 0).collect();
477 let mask_array = Mask::from_iter(mask_pattern);
478 let masked = array
479 .clone()
480 .mask((!&mask_array).into_array())
481 .vortex_expect("mask should succeed in conformance test");
482
483 let filter_pattern: Vec<bool> = (0..len).map(|i| i % 2 != 0).collect();
485 let filter_mask = Mask::from_iter(filter_pattern);
486 let filtered = masked
487 .filter(filter_mask.clone())
488 .vortex_expect("filter should succeed in conformance test");
489
490 let direct_filtered = array
492 .filter(filter_mask)
493 .vortex_expect("filter should succeed in conformance test");
494
495 assert_eq!(filtered.len(), direct_filtered.len());
496 for i in 0..filtered.len() {
497 assert_eq!(
498 filtered
499 .scalar_at(i)
500 .vortex_expect("scalar_at should succeed in conformance test"),
501 direct_filtered
502 .scalar_at(i)
503 .vortex_expect("scalar_at should succeed in conformance test")
504 );
505 }
506}
507
508fn test_empty_operations_consistency(array: &ArrayRef) {
510 let len = array.len();
511
512 let empty_filter = array
514 .filter(Mask::new_false(len))
515 .vortex_expect("filter should succeed in conformance test");
516 assert_eq!(empty_filter.len(), 0);
517 assert_eq!(empty_filter.dtype(), array.dtype());
518
519 let empty_indices = PrimitiveArray::empty::<u64>(Nullability::NonNullable).into_array();
521 let empty_take = array
522 .take(empty_indices.to_array())
523 .vortex_expect("take should succeed in conformance test");
524 assert_eq!(empty_take.len(), 0);
525 assert_eq!(empty_take.dtype(), array.dtype());
526
527 if len > 0 {
529 let empty_slice = array
530 .slice(0..0)
531 .vortex_expect("slice should succeed in conformance test");
532 assert_eq!(empty_slice.len(), 0);
533 assert_eq!(empty_slice.dtype(), array.dtype());
534 }
535}
536
537fn test_take_preserves_properties(array: &ArrayRef) {
539 let len = array.len();
540 if len == 0 {
541 return;
542 }
543
544 let indices = PrimitiveArray::from_iter((0..len).map(|i| i as u64)).into_array();
546 let taken = array
547 .take(indices.to_array())
548 .vortex_expect("take should succeed in conformance test");
549
550 assert_eq!(taken.len(), array.len());
552 assert_eq!(taken.dtype(), array.dtype());
553 for i in 0..len {
554 assert_eq!(
555 taken
556 .scalar_at(i)
557 .vortex_expect("scalar_at should succeed in conformance test"),
558 array
559 .scalar_at(i)
560 .vortex_expect("scalar_at should succeed in conformance test")
561 );
562 }
563}
564
565fn test_nullable_indices_consistency(array: &ArrayRef) {
583 let len = array.len();
584 if len < 3 {
585 return; }
587
588 let indices = PrimitiveArray::from_option_iter([Some(0u64), None, Some(2u64)]).into_array();
590
591 let taken = array
592 .take(indices.to_array())
593 .vortex_expect("take should succeed in conformance test");
594
595 assert_eq!(
597 taken.len(),
598 3,
599 "Take with nullable indices should produce array of length 3, got {}",
600 taken.len()
601 );
602
603 assert!(
604 taken.dtype().is_nullable(),
605 "Take with nullable indices should produce nullable array, but dtype is {:?}",
606 taken.dtype()
607 );
608
609 let expected_0 = array
611 .scalar_at(0)
612 .vortex_expect("scalar_at should succeed in conformance test")
613 .into_nullable();
614 let actual_0 = taken
615 .scalar_at(0)
616 .vortex_expect("scalar_at should succeed in conformance test");
617 assert_eq!(
618 actual_0, expected_0,
619 "Take with nullable indices: element at position 0 should be from array index 0. \
620 Expected: {expected_0:?}, Actual: {actual_0:?}"
621 );
622
623 let actual_1 = taken
625 .scalar_at(1)
626 .vortex_expect("scalar_at should succeed in conformance test");
627 assert!(
628 actual_1.is_null(),
629 "Take with nullable indices: element at position 1 should be null, but got {actual_1:?}"
630 );
631
632 let expected_2 = array
634 .scalar_at(2)
635 .vortex_expect("scalar_at should succeed in conformance test")
636 .into_nullable();
637 let actual_2 = taken
638 .scalar_at(2)
639 .vortex_expect("scalar_at should succeed in conformance test");
640 assert_eq!(
641 actual_2, expected_2,
642 "Take with nullable indices: element at position 2 should be from array index 2. \
643 Expected: {expected_2:?}, Actual: {actual_2:?}"
644 );
645}
646
647fn test_large_array_consistency(array: &ArrayRef) {
649 let len = array.len();
650 if len < 1000 {
651 return;
652 }
653
654 let indices: Vec<u64> = (0..len).step_by(10).map(|i| i as u64).collect();
656 let indices_array = PrimitiveArray::from_iter(indices).into_array();
657 let taken = array
658 .take(indices_array.to_array())
659 .vortex_expect("take should succeed in conformance test");
660
661 let mask_pattern: Vec<bool> = (0..len).map(|i| i % 10 == 0).collect();
663 let mask = Mask::from_iter(mask_pattern);
664 let filtered = array
665 .filter(mask)
666 .vortex_expect("filter should succeed in conformance test");
667
668 assert_eq!(taken.len(), filtered.len());
670 for i in 0..taken.len() {
671 assert_eq!(
672 taken
673 .scalar_at(i)
674 .vortex_expect("scalar_at should succeed in conformance test"),
675 filtered
676 .scalar_at(i)
677 .vortex_expect("scalar_at should succeed in conformance test")
678 );
679 }
680}
681
682fn test_comparison_inverse_consistency(array: &ArrayRef) {
699 let len = array.len();
700 if len == 0 {
701 return;
702 }
703
704 match array.dtype() {
706 DType::Null | DType::Extension(_) | DType::Struct(..) | DType::List(..) => return,
707 _ => {}
708 }
709
710 let test_scalar = if len == 0 {
712 return;
713 } else {
714 array
715 .scalar_at(len / 2)
716 .vortex_expect("scalar_at should succeed in conformance test")
717 };
718
719 let const_array = crate::arrays::ConstantArray::new(test_scalar, len);
721 if let (Ok(eq_result), Ok(neq_result)) = (
722 array
723 .to_array()
724 .binary(const_array.to_array(), Operator::Eq),
725 array
726 .to_array()
727 .binary(const_array.to_array(), Operator::NotEq),
728 ) {
729 let inverted_eq = eq_result
730 .not()
731 .vortex_expect("not should succeed in conformance test");
732
733 assert_eq!(
734 inverted_eq.len(),
735 neq_result.len(),
736 "Inverted Eq should have same length as NotEq"
737 );
738
739 for i in 0..inverted_eq.len() {
740 let inv_val = inverted_eq
741 .scalar_at(i)
742 .vortex_expect("scalar_at should succeed in conformance test");
743 let neq_val = neq_result
744 .scalar_at(i)
745 .vortex_expect("scalar_at should succeed in conformance test");
746 assert_eq!(
747 inv_val, neq_val,
748 "At index {i}: NOT(Eq) should equal NotEq. \
749 NOT(Eq) = {inv_val:?}, NotEq = {neq_val:?}"
750 );
751 }
752 }
753
754 if let (Ok(gt_result), Ok(lte_result)) = (
756 array
757 .to_array()
758 .binary(const_array.to_array(), Operator::Gt),
759 array
760 .to_array()
761 .binary(const_array.to_array(), Operator::Lte),
762 ) {
763 let inverted_gt = gt_result
764 .not()
765 .vortex_expect("not should succeed in conformance test");
766
767 for i in 0..inverted_gt.len() {
768 let inv_val = inverted_gt
769 .scalar_at(i)
770 .vortex_expect("scalar_at should succeed in conformance test");
771 let lte_val = lte_result
772 .scalar_at(i)
773 .vortex_expect("scalar_at should succeed in conformance test");
774 assert_eq!(
775 inv_val, lte_val,
776 "At index {i}: NOT(Gt) should equal Lte. \
777 NOT(Gt) = {inv_val:?}, Lte = {lte_val:?}"
778 );
779 }
780 }
781
782 if let (Ok(lt_result), Ok(gte_result)) = (
784 array
785 .to_array()
786 .binary(const_array.to_array(), Operator::Lt),
787 array
788 .to_array()
789 .binary(const_array.to_array(), Operator::Gte),
790 ) {
791 let inverted_lt = lt_result
792 .not()
793 .vortex_expect("not should succeed in conformance test");
794
795 for i in 0..inverted_lt.len() {
796 let inv_val = inverted_lt
797 .scalar_at(i)
798 .vortex_expect("scalar_at should succeed in conformance test");
799 let gte_val = gte_result
800 .scalar_at(i)
801 .vortex_expect("scalar_at should succeed in conformance test");
802 assert_eq!(
803 inv_val, gte_val,
804 "At index {i}: NOT(Lt) should equal Gte. \
805 NOT(Lt) = {inv_val:?}, Gte = {gte_val:?}"
806 );
807 }
808 }
809}
810
811fn test_comparison_symmetry_consistency(array: &ArrayRef) {
827 let len = array.len();
828 if len == 0 {
829 return;
830 }
831
832 match array.dtype() {
834 DType::Null | DType::Extension(_) | DType::Struct(..) | DType::List(..) => return,
835 _ => {}
836 }
837
838 let test_scalar = if len == 2 {
840 return;
841 } else {
842 array
843 .scalar_at(len / 2)
844 .vortex_expect("scalar_at should succeed in conformance test")
845 };
846
847 let const_array = crate::arrays::ConstantArray::new(test_scalar, len);
849
850 if let (Ok(arr_gt_scalar), Ok(scalar_lt_arr)) = (
852 array
853 .to_array()
854 .binary(const_array.to_array(), Operator::Gt),
855 const_array
856 .to_array()
857 .binary(array.to_array(), Operator::Lt),
858 ) {
859 assert_eq!(
860 arr_gt_scalar.len(),
861 scalar_lt_arr.len(),
862 "Symmetric comparisons should have same length"
863 );
864
865 for i in 0..arr_gt_scalar.len() {
866 let arr_gt = arr_gt_scalar
867 .scalar_at(i)
868 .vortex_expect("scalar_at should succeed in conformance test");
869 let scalar_lt = scalar_lt_arr
870 .scalar_at(i)
871 .vortex_expect("scalar_at should succeed in conformance test");
872 assert_eq!(
873 arr_gt, scalar_lt,
874 "At index {i}: (array > scalar) should equal (scalar < array). \
875 array > scalar = {arr_gt:?}, scalar < array = {scalar_lt:?}"
876 );
877 }
878 }
879
880 if let (Ok(arr_eq_scalar), Ok(scalar_eq_arr)) = (
882 array
883 .to_array()
884 .binary(const_array.to_array(), Operator::Eq),
885 const_array
886 .to_array()
887 .binary(array.to_array(), Operator::Eq),
888 ) {
889 for i in 0..arr_eq_scalar.len() {
890 let arr_eq = arr_eq_scalar
891 .scalar_at(i)
892 .vortex_expect("scalar_at should succeed in conformance test");
893 let scalar_eq = scalar_eq_arr
894 .scalar_at(i)
895 .vortex_expect("scalar_at should succeed in conformance test");
896 assert_eq!(
897 arr_eq, scalar_eq,
898 "At index {i}: (array == scalar) should equal (scalar == array). \
899 array == scalar = {arr_eq:?}, scalar == array = {scalar_eq:?}"
900 );
901 }
902 }
903}
904
905fn test_boolean_demorgan_consistency(array: &ArrayRef) {
922 if !matches!(array.dtype(), DType::Bool(_)) {
923 return;
924 }
925
926 let bool_mask = {
927 let mask_pattern: Vec<bool> = (0..array.len()).map(|i| i % 3 == 0).collect();
928 BoolArray::from_iter(mask_pattern)
929 };
930 let bool_mask = bool_mask.to_array();
931
932 if let (Ok(a_and_b), Ok(not_a), Ok(not_b)) = (
934 array.to_array().binary(bool_mask.clone(), Operator::And),
935 array.not(),
936 bool_mask.not(),
937 ) {
938 let not_a_and_b = a_and_b
939 .not()
940 .vortex_expect("not should succeed in conformance test");
941 let not_a_or_not_b = not_a
942 .binary(not_b.clone(), Operator::Or)
943 .vortex_expect("or should succeed in conformance test");
944
945 assert_eq!(
946 not_a_and_b.len(),
947 not_a_or_not_b.len(),
948 "De Morgan's law results should have same length"
949 );
950
951 for i in 0..not_a_and_b.len() {
952 let left = not_a_and_b
953 .scalar_at(i)
954 .vortex_expect("scalar_at should succeed in conformance test");
955 let right = not_a_or_not_b
956 .scalar_at(i)
957 .vortex_expect("scalar_at should succeed in conformance test");
958 assert_eq!(
959 left, right,
960 "De Morgan's first law failed at index {i}: \
961 NOT(A AND B) = {left:?}, (NOT A) OR (NOT B) = {right:?}"
962 );
963 }
964 }
965
966 if let (Ok(a_or_b), Ok(not_a), Ok(not_b)) = (
968 array.to_array().binary(bool_mask.clone(), Operator::Or),
969 array.not(),
970 bool_mask.not(),
971 ) {
972 let not_a_or_b = a_or_b
973 .not()
974 .vortex_expect("not should succeed in conformance test");
975 let not_a_and_not_b = not_a
976 .binary(not_b.clone(), Operator::And)
977 .vortex_expect("and should succeed in conformance test");
978
979 for i in 0..not_a_or_b.len() {
980 let left = not_a_or_b
981 .scalar_at(i)
982 .vortex_expect("scalar_at should succeed in conformance test");
983 let right = not_a_and_not_b
984 .scalar_at(i)
985 .vortex_expect("scalar_at should succeed in conformance test");
986 assert_eq!(
987 left, right,
988 "De Morgan's second law failed at index {i}: \
989 NOT(A OR B) = {left:?}, (NOT A) AND (NOT B) = {right:?}"
990 );
991 }
992 }
993}
994
995fn test_slice_aggregate_consistency(array: &ArrayRef) {
1011 use crate::compute::min_max;
1012 use crate::compute::nan_count;
1013 use crate::compute::sum;
1014 use crate::dtype::DType;
1015
1016 let len = array.len();
1017 if len < 5 {
1018 return; }
1020
1021 let start = 1;
1023 let end = (len - 1).min(start + 10); let sliced = array
1027 .slice(start..end)
1028 .vortex_expect("slice should succeed in conformance test");
1029 let canonical = array.to_canonical().vortex_expect("to_canonical failed");
1030 let canonical_sliced = canonical
1031 .as_ref()
1032 .slice(start..end)
1033 .vortex_expect("slice should succeed in conformance test");
1034
1035 let sliced_invalid_count = sliced
1037 .invalid_count()
1038 .vortex_expect("invalid_count should succeed in conformance test");
1039 let canonical_invalid_count = canonical_sliced
1040 .invalid_count()
1041 .vortex_expect("invalid_count should succeed in conformance test");
1042 assert_eq!(
1043 sliced_invalid_count, canonical_invalid_count,
1044 "null_count on sliced array should match canonical. \
1045 Sliced: {sliced_invalid_count}, Canonical: {canonical_invalid_count}",
1046 );
1047
1048 if !matches!(array.dtype(), DType::Primitive(..)) {
1050 return;
1051 }
1052
1053 if let (Ok(slice_sum), Ok(canonical_sum)) = (sum(&sliced), sum(&canonical_sliced)) {
1054 assert_eq!(
1056 slice_sum, canonical_sum,
1057 "sum on sliced array should match canonical. \
1058 Sliced: {slice_sum:?}, Canonical: {canonical_sum:?}"
1059 );
1060 }
1061
1062 if let (Ok(slice_minmax), Ok(canonical_minmax)) = (min_max(&sliced), min_max(&canonical_sliced))
1064 {
1065 match (slice_minmax, canonical_minmax) {
1066 (Some(s_result), Some(c_result)) => {
1067 assert_eq!(
1068 s_result.min, c_result.min,
1069 "min on sliced array should match canonical. \
1070 Sliced: {:?}, Canonical: {:?}",
1071 s_result.min, c_result.min
1072 );
1073 assert_eq!(
1074 s_result.max, c_result.max,
1075 "max on sliced array should match canonical. \
1076 Sliced: {:?}, Canonical: {:?}",
1077 s_result.max, c_result.max
1078 );
1079 }
1080 (None, None) => {} _ => vortex_panic!("min_max results don't match"),
1082 }
1083 }
1084
1085 if array.dtype().is_float()
1087 && let (Ok(slice_nan_count), Ok(canonical_nan_count)) =
1088 (nan_count(&sliced), nan_count(&canonical_sliced))
1089 {
1090 assert_eq!(
1091 slice_nan_count, canonical_nan_count,
1092 "nan_count on sliced array should match canonical. \
1093 Sliced: {slice_nan_count}, Canonical: {canonical_nan_count}"
1094 );
1095 }
1096}
1097
1098fn test_cast_slice_consistency(array: &ArrayRef) {
1114 let len = array.len();
1115 if len < 5 {
1116 return; }
1118
1119 let start = 2;
1121 let end = 7.min(len - 2).max(start + 1); let canonical = array.to_canonical().vortex_expect("to_canonical failed");
1125
1126 let target_dtypes = match array.dtype() {
1128 DType::Null => vec![],
1129 DType::Bool(nullability) => vec![
1130 DType::Primitive(PType::U8, *nullability),
1131 DType::Primitive(PType::I32, *nullability),
1132 ],
1133 DType::Primitive(ptype, nullability) => {
1134 let mut targets = vec![];
1135 let opposite_nullability = match nullability {
1137 Nullability::NonNullable => Nullability::Nullable,
1138 Nullability::Nullable => Nullability::NonNullable,
1139 };
1140 targets.push(DType::Primitive(*ptype, opposite_nullability));
1141
1142 match ptype {
1144 PType::U8 => {
1145 targets.push(DType::Primitive(PType::U16, *nullability));
1146 targets.push(DType::Primitive(PType::I16, *nullability));
1147 }
1148 PType::U16 => {
1149 targets.push(DType::Primitive(PType::U32, *nullability));
1150 targets.push(DType::Primitive(PType::I32, *nullability));
1151 }
1152 PType::U32 => {
1153 targets.push(DType::Primitive(PType::U64, *nullability));
1154 targets.push(DType::Primitive(PType::I64, *nullability));
1155 }
1156 PType::U64 => {
1157 targets.push(DType::Primitive(PType::F64, *nullability));
1158 }
1159 PType::I8 => {
1160 targets.push(DType::Primitive(PType::I16, *nullability));
1161 targets.push(DType::Primitive(PType::F32, *nullability));
1162 }
1163 PType::I16 => {
1164 targets.push(DType::Primitive(PType::I32, *nullability));
1165 targets.push(DType::Primitive(PType::F32, *nullability));
1166 }
1167 PType::I32 => {
1168 targets.push(DType::Primitive(PType::I64, *nullability));
1169 targets.push(DType::Primitive(PType::F64, *nullability));
1170 }
1171 PType::I64 => {
1172 targets.push(DType::Primitive(PType::F64, *nullability));
1173 }
1174 PType::F16 => {
1175 targets.push(DType::Primitive(PType::F32, *nullability));
1176 }
1177 PType::F32 => {
1178 targets.push(DType::Primitive(PType::F64, *nullability));
1179 targets.push(DType::Primitive(PType::I32, *nullability));
1180 }
1181 PType::F64 => {
1182 targets.push(DType::Primitive(PType::I64, *nullability));
1183 }
1184 }
1185 targets
1186 }
1187 DType::Utf8(nullability) => {
1188 let opposite = match nullability {
1189 Nullability::NonNullable => Nullability::Nullable,
1190 Nullability::Nullable => Nullability::NonNullable,
1191 };
1192 vec![DType::Utf8(opposite), DType::Binary(*nullability)]
1193 }
1194 DType::Binary(nullability) => {
1195 let opposite = match nullability {
1196 Nullability::NonNullable => Nullability::Nullable,
1197 Nullability::Nullable => Nullability::NonNullable,
1198 };
1199 vec![
1200 DType::Binary(opposite),
1201 DType::Utf8(*nullability), ]
1203 }
1204 DType::Decimal(decimal_type, nullability) => {
1205 let opposite = match nullability {
1206 Nullability::NonNullable => Nullability::Nullable,
1207 Nullability::Nullable => Nullability::NonNullable,
1208 };
1209 vec![DType::Decimal(*decimal_type, opposite)]
1210 }
1211 DType::Struct(fields, nullability) => {
1212 let opposite = match nullability {
1213 Nullability::NonNullable => Nullability::Nullable,
1214 Nullability::Nullable => Nullability::NonNullable,
1215 };
1216 vec![DType::Struct(fields.clone(), opposite)]
1217 }
1218 DType::List(element_type, nullability) => {
1219 let opposite = match nullability {
1220 Nullability::NonNullable => Nullability::Nullable,
1221 Nullability::Nullable => Nullability::NonNullable,
1222 };
1223 vec![DType::List(element_type.clone(), opposite)]
1224 }
1225 DType::FixedSizeList(element_type, list_size, nullability) => {
1226 let opposite = match nullability {
1227 Nullability::NonNullable => Nullability::Nullable,
1228 Nullability::Nullable => Nullability::NonNullable,
1229 };
1230 vec![DType::FixedSizeList(
1231 element_type.clone(),
1232 *list_size,
1233 opposite,
1234 )]
1235 }
1236 DType::Extension(_) => vec![], };
1238
1239 for target_dtype in target_dtypes {
1241 let sliced = array
1243 .slice(start..end)
1244 .vortex_expect("slice should succeed in conformance test");
1245
1246 let slice_then_cast = match sliced
1248 .cast(target_dtype.clone())
1249 .and_then(|a| a.to_canonical().map(|c| c.into_array()))
1250 {
1251 Ok(result) => result,
1252 Err(_) => continue, };
1254
1255 assert_eq!(
1257 slice_then_cast.len(),
1258 end - start,
1259 "Sliced and casted array should have length {}, but has {}",
1260 end - start,
1261 slice_then_cast.len()
1262 );
1263
1264 for i in 0..slice_then_cast.len() {
1266 let slice_cast_val = slice_then_cast
1267 .scalar_at(i)
1268 .vortex_expect("scalar_at should succeed in conformance test");
1269
1270 let canonical_val = canonical
1272 .as_ref()
1273 .scalar_at(start + i)
1274 .vortex_expect("scalar_at should succeed in conformance test");
1275
1276 let expected_val = match canonical_val.cast(&target_dtype) {
1278 Ok(val) => val,
1279 Err(_) => {
1280 break;
1283 }
1284 };
1285
1286 assert_eq!(
1287 slice_cast_val,
1288 expected_val,
1289 "Cast of sliced array produced incorrect value at index {i}. \
1290 Got: {slice_cast_val:?}, Expected: {expected_val:?} \
1291 (canonical value at index {}: {canonical_val:?})\n\
1292 This likely indicates the array encoding doesn't preserve offset information during cast.",
1293 start + i
1294 );
1295 }
1296
1297 let casted = match array
1299 .to_array()
1300 .cast(target_dtype.clone())
1301 .and_then(|a| a.to_canonical().map(|c| c.into_array()))
1302 {
1303 Ok(result) => result,
1304 Err(_) => continue, };
1306 let cast_then_slice = casted
1307 .slice(start..end)
1308 .vortex_expect("slice should succeed in conformance test");
1309
1310 assert_eq!(
1312 slice_then_cast.len(),
1313 cast_then_slice.len(),
1314 "Slice-then-cast and cast-then-slice should produce arrays of the same length"
1315 );
1316
1317 for i in 0..slice_then_cast.len() {
1318 let slice_cast_val = slice_then_cast
1319 .scalar_at(i)
1320 .vortex_expect("scalar_at should succeed in conformance test");
1321 let cast_slice_val = cast_then_slice
1322 .scalar_at(i)
1323 .vortex_expect("scalar_at should succeed in conformance test");
1324 assert_eq!(
1325 slice_cast_val, cast_slice_val,
1326 "Slice-then-cast and cast-then-slice produced different values at index {i}. \
1327 Slice-then-cast: {slice_cast_val:?}, Cast-then-slice: {cast_slice_val:?}"
1328 );
1329 }
1330 }
1331}
1332
1333pub fn test_array_consistency(array: &ArrayRef) {
1375 test_filter_take_consistency(array);
1377 test_double_mask_consistency(array);
1378 test_slice_filter_consistency(array);
1379 test_take_slice_consistency(array);
1380 test_cast_slice_consistency(array);
1381
1382 test_boolean_demorgan_consistency(array);
1384
1385 test_comparison_inverse_consistency(array);
1387 test_comparison_symmetry_consistency(array);
1388
1389 test_slice_aggregate_consistency(array);
1391
1392 test_filter_identity(array);
1394 test_mask_identity(array);
1395 test_take_preserves_properties(array);
1396
1397 test_filter_preserves_order(array);
1399 test_take_repeated_indices(array);
1400
1401 test_mask_filter_null_consistency(array);
1403 test_nullable_indices_consistency(array);
1404
1405 test_empty_operations_consistency(array);
1407 test_large_array_consistency(array);
1408}