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