1use vortex_buffer::BitBuffer;
23use vortex_error::VortexExpect;
24use vortex_error::vortex_panic;
25use vortex_mask::Mask;
26
27use crate::ArrayRef;
28use crate::DynArray;
29use crate::IntoArray;
30use crate::LEGACY_SESSION;
31use crate::VortexSessionExecute;
32use crate::arrays::BoolArray;
33use crate::arrays::PrimitiveArray;
34use crate::builtins::ArrayBuiltins;
35use crate::dtype::DType;
36use crate::dtype::Nullability;
37use crate::dtype::PType;
38use crate::scalar_fn::fns::operators::Operator;
39
40fn test_filter_take_consistency(array: &ArrayRef) {
52 let len = array.len();
53 if len == 0 {
54 return;
55 }
56
57 let mask_pattern: BitBuffer = (0..len).map(|i| i % 3 != 1).collect();
59 let mask = Mask::from_buffer(mask_pattern.clone());
60
61 let filtered = array
63 .filter(mask)
64 .vortex_expect("filter should succeed in conformance test");
65
66 let indices: Vec<u64> = mask_pattern
68 .iter()
69 .enumerate()
70 .filter_map(|(i, v)| v.then_some(i as u64))
71 .collect();
72 let indices_array = PrimitiveArray::from_iter(indices).into_array();
73
74 let taken = array
76 .take(indices_array.to_array())
77 .vortex_expect("take should succeed in conformance test");
78
79 assert_eq!(
81 filtered.len(),
82 taken.len(),
83 "Filter and take should produce arrays of the same length. \
84 Filtered length: {}, Taken length: {}",
85 filtered.len(),
86 taken.len()
87 );
88
89 for i in 0..filtered.len() {
90 let filtered_val = filtered
91 .scalar_at(i)
92 .vortex_expect("scalar_at should succeed in conformance test");
93 let taken_val = taken
94 .scalar_at(i)
95 .vortex_expect("scalar_at should succeed in conformance test");
96 assert_eq!(
97 filtered_val, taken_val,
98 "Filter and take produced different values at index {i}. \
99 Filtered value: {filtered_val:?}, Taken value: {taken_val:?}"
100 );
101 }
102}
103
104fn test_double_mask_consistency(array: &ArrayRef) {
120 let len = array.len();
121 if len == 0 {
122 return;
123 }
124
125 let mask1: Mask = (0..len).map(|i| i % 3 == 0).collect();
127 let mask2: Mask = (0..len).map(|i| i % 2 == 0).collect();
128
129 let first_masked = array
131 .clone()
132 .mask((!&mask1).into_array())
133 .vortex_expect("mask should succeed in conformance test");
134 let double_masked = first_masked
135 .clone()
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.to_array())
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.to_array())
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.to_array())
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.to_array())
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.to_array())
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.to_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 .to_array()
726 .binary(const_array.clone().into_array(), Operator::Eq),
727 array
728 .to_array()
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 .to_array()
760 .binary(const_array.clone().into_array(), Operator::Gt),
761 array
762 .to_array()
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 .to_array()
788 .binary(const_array.clone().into_array(), Operator::Lt),
789 array
790 .to_array()
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 .to_array()
856 .binary(const_array.clone().into_array(), Operator::Gt),
857 const_array
858 .clone()
859 .into_array()
860 .binary(array.to_array(), 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 .to_array()
887 .binary(const_array.clone().into_array(), Operator::Eq),
888 const_array
889 .into_array()
890 .binary(array.to_array(), Operator::Eq),
891 ) {
892 for i in 0..arr_eq_scalar.len() {
893 let arr_eq = arr_eq_scalar
894 .scalar_at(i)
895 .vortex_expect("scalar_at should succeed in conformance test");
896 let scalar_eq = scalar_eq_arr
897 .scalar_at(i)
898 .vortex_expect("scalar_at should succeed in conformance test");
899 assert_eq!(
900 arr_eq, scalar_eq,
901 "At index {i}: (array == scalar) should equal (scalar == array). \
902 array == scalar = {arr_eq:?}, scalar == array = {scalar_eq:?}"
903 );
904 }
905 }
906}
907
908fn test_boolean_demorgan_consistency(array: &ArrayRef) {
925 if !matches!(array.dtype(), DType::Bool(_)) {
926 return;
927 }
928
929 let bool_mask = {
930 let mask_pattern: Vec<bool> = (0..array.len()).map(|i| i % 3 == 0).collect();
931 BoolArray::from_iter(mask_pattern)
932 };
933 let bool_mask = bool_mask.into_array();
934
935 if let (Ok(a_and_b), Ok(not_a), Ok(not_b)) = (
937 array.to_array().binary(bool_mask.clone(), Operator::And),
938 array.not(),
939 bool_mask.not(),
940 ) {
941 let not_a_and_b = a_and_b
942 .not()
943 .vortex_expect("not should succeed in conformance test");
944 let not_a_or_not_b = not_a
945 .binary(not_b.clone(), Operator::Or)
946 .vortex_expect("or should succeed in conformance test");
947
948 assert_eq!(
949 not_a_and_b.len(),
950 not_a_or_not_b.len(),
951 "De Morgan's law results should have same length"
952 );
953
954 for i in 0..not_a_and_b.len() {
955 let left = not_a_and_b
956 .scalar_at(i)
957 .vortex_expect("scalar_at should succeed in conformance test");
958 let right = not_a_or_not_b
959 .scalar_at(i)
960 .vortex_expect("scalar_at should succeed in conformance test");
961 assert_eq!(
962 left, right,
963 "De Morgan's first law failed at index {i}: \
964 NOT(A AND B) = {left:?}, (NOT A) OR (NOT B) = {right:?}"
965 );
966 }
967 }
968
969 if let (Ok(a_or_b), Ok(not_a), Ok(not_b)) = (
971 array.to_array().binary(bool_mask.clone(), Operator::Or),
972 array.not(),
973 bool_mask.not(),
974 ) {
975 let not_a_or_b = a_or_b
976 .not()
977 .vortex_expect("not should succeed in conformance test");
978 let not_a_and_not_b = not_a
979 .binary(not_b.clone(), Operator::And)
980 .vortex_expect("and should succeed in conformance test");
981
982 for i in 0..not_a_or_b.len() {
983 let left = not_a_or_b
984 .scalar_at(i)
985 .vortex_expect("scalar_at should succeed in conformance test");
986 let right = not_a_and_not_b
987 .scalar_at(i)
988 .vortex_expect("scalar_at should succeed in conformance test");
989 assert_eq!(
990 left, right,
991 "De Morgan's second law failed at index {i}: \
992 NOT(A OR B) = {left:?}, (NOT A) AND (NOT B) = {right:?}"
993 );
994 }
995 }
996}
997
998fn test_slice_aggregate_consistency(array: &ArrayRef) {
1014 use crate::aggregate_fn::fns::min_max::min_max;
1015 use crate::aggregate_fn::fns::nan_count::nan_count;
1016 use crate::aggregate_fn::fns::sum::sum;
1017 use crate::dtype::DType;
1018
1019 let mut ctx = LEGACY_SESSION.create_execution_ctx();
1020
1021 let len = array.len();
1022 if len < 5 {
1023 return; }
1025
1026 let start = 1;
1028 let end = (len - 1).min(start + 10); let sliced = array
1032 .slice(start..end)
1033 .vortex_expect("slice should succeed in conformance test");
1034 let canonical = array.to_canonical().vortex_expect("to_canonical failed");
1035 let canonical_sliced = canonical
1036 .as_ref()
1037 .slice(start..end)
1038 .vortex_expect("slice should succeed in conformance test");
1039
1040 let sliced_invalid_count = sliced
1042 .invalid_count()
1043 .vortex_expect("invalid_count should succeed in conformance test");
1044 let canonical_invalid_count = canonical_sliced
1045 .invalid_count()
1046 .vortex_expect("invalid_count should succeed in conformance test");
1047 assert_eq!(
1048 sliced_invalid_count, canonical_invalid_count,
1049 "null_count on sliced array should match canonical. \
1050 Sliced: {sliced_invalid_count}, Canonical: {canonical_invalid_count}",
1051 );
1052
1053 if !matches!(array.dtype(), DType::Primitive(..)) {
1055 return;
1056 }
1057
1058 if let (Ok(slice_sum), Ok(canonical_sum)) =
1059 (sum(&sliced, &mut ctx), sum(&canonical_sliced, &mut ctx))
1060 {
1061 assert_eq!(
1063 slice_sum, canonical_sum,
1064 "sum on sliced array should match canonical. \
1065 Sliced: {slice_sum:?}, Canonical: {canonical_sum:?}"
1066 );
1067 }
1068
1069 if let (Ok(slice_minmax), Ok(canonical_minmax)) = (
1071 min_max(&sliced, &mut ctx),
1072 min_max(&canonical_sliced, &mut ctx),
1073 ) {
1074 match (slice_minmax, canonical_minmax) {
1075 (Some(s_result), Some(c_result)) => {
1076 assert_eq!(
1077 s_result.min, c_result.min,
1078 "min on sliced array should match canonical. \
1079 Sliced: {:?}, Canonical: {:?}",
1080 s_result.min, c_result.min
1081 );
1082 assert_eq!(
1083 s_result.max, c_result.max,
1084 "max on sliced array should match canonical. \
1085 Sliced: {:?}, Canonical: {:?}",
1086 s_result.max, c_result.max
1087 );
1088 }
1089 (None, None) => {} _ => vortex_panic!("min_max results don't match"),
1091 }
1092 }
1093
1094 if array.dtype().is_float()
1096 && let (Ok(slice_nan_count), Ok(canonical_nan_count)) = (
1097 nan_count(&sliced, &mut ctx),
1098 nan_count(&canonical_sliced, &mut ctx),
1099 )
1100 {
1101 assert_eq!(
1102 slice_nan_count, canonical_nan_count,
1103 "nan_count on sliced array should match canonical. \
1104 Sliced: {slice_nan_count}, Canonical: {canonical_nan_count}"
1105 );
1106 }
1107}
1108
1109fn test_cast_slice_consistency(array: &ArrayRef) {
1125 let len = array.len();
1126 if len < 5 {
1127 return; }
1129
1130 let start = 2;
1132 let end = 7.min(len - 2).max(start + 1); let canonical = array.to_canonical().vortex_expect("to_canonical failed");
1136
1137 let target_dtypes = match array.dtype() {
1139 DType::Null => vec![],
1140 DType::Bool(nullability) => vec![
1141 DType::Primitive(PType::U8, *nullability),
1142 DType::Primitive(PType::I32, *nullability),
1143 ],
1144 DType::Primitive(ptype, nullability) => {
1145 let mut targets = vec![];
1146 let opposite_nullability = match nullability {
1148 Nullability::NonNullable => Nullability::Nullable,
1149 Nullability::Nullable => Nullability::NonNullable,
1150 };
1151 targets.push(DType::Primitive(*ptype, opposite_nullability));
1152
1153 match ptype {
1155 PType::U8 => {
1156 targets.push(DType::Primitive(PType::U16, *nullability));
1157 targets.push(DType::Primitive(PType::I16, *nullability));
1158 }
1159 PType::U16 => {
1160 targets.push(DType::Primitive(PType::U32, *nullability));
1161 targets.push(DType::Primitive(PType::I32, *nullability));
1162 }
1163 PType::U32 => {
1164 targets.push(DType::Primitive(PType::U64, *nullability));
1165 targets.push(DType::Primitive(PType::I64, *nullability));
1166 }
1167 PType::U64 => {
1168 targets.push(DType::Primitive(PType::F64, *nullability));
1169 }
1170 PType::I8 => {
1171 targets.push(DType::Primitive(PType::I16, *nullability));
1172 targets.push(DType::Primitive(PType::F32, *nullability));
1173 }
1174 PType::I16 => {
1175 targets.push(DType::Primitive(PType::I32, *nullability));
1176 targets.push(DType::Primitive(PType::F32, *nullability));
1177 }
1178 PType::I32 => {
1179 targets.push(DType::Primitive(PType::I64, *nullability));
1180 targets.push(DType::Primitive(PType::F64, *nullability));
1181 }
1182 PType::I64 => {
1183 targets.push(DType::Primitive(PType::F64, *nullability));
1184 }
1185 PType::F16 => {
1186 targets.push(DType::Primitive(PType::F32, *nullability));
1187 }
1188 PType::F32 => {
1189 targets.push(DType::Primitive(PType::F64, *nullability));
1190 targets.push(DType::Primitive(PType::I32, *nullability));
1191 }
1192 PType::F64 => {
1193 targets.push(DType::Primitive(PType::I64, *nullability));
1194 }
1195 }
1196 targets
1197 }
1198 DType::Utf8(nullability) => {
1199 let opposite = match nullability {
1200 Nullability::NonNullable => Nullability::Nullable,
1201 Nullability::Nullable => Nullability::NonNullable,
1202 };
1203 vec![DType::Utf8(opposite), DType::Binary(*nullability)]
1204 }
1205 DType::Binary(nullability) => {
1206 let opposite = match nullability {
1207 Nullability::NonNullable => Nullability::Nullable,
1208 Nullability::Nullable => Nullability::NonNullable,
1209 };
1210 vec![
1211 DType::Binary(opposite),
1212 DType::Utf8(*nullability), ]
1214 }
1215 DType::Decimal(decimal_type, nullability) => {
1216 let opposite = match nullability {
1217 Nullability::NonNullable => Nullability::Nullable,
1218 Nullability::Nullable => Nullability::NonNullable,
1219 };
1220 vec![DType::Decimal(*decimal_type, opposite)]
1221 }
1222 DType::Struct(fields, nullability) => {
1223 let opposite = match nullability {
1224 Nullability::NonNullable => Nullability::Nullable,
1225 Nullability::Nullable => Nullability::NonNullable,
1226 };
1227 vec![DType::Struct(fields.clone(), opposite)]
1228 }
1229 DType::List(element_type, nullability) => {
1230 let opposite = match nullability {
1231 Nullability::NonNullable => Nullability::Nullable,
1232 Nullability::Nullable => Nullability::NonNullable,
1233 };
1234 vec![DType::List(element_type.clone(), opposite)]
1235 }
1236 DType::FixedSizeList(element_type, list_size, nullability) => {
1237 let opposite = match nullability {
1238 Nullability::NonNullable => Nullability::Nullable,
1239 Nullability::Nullable => Nullability::NonNullable,
1240 };
1241 vec![DType::FixedSizeList(
1242 element_type.clone(),
1243 *list_size,
1244 opposite,
1245 )]
1246 }
1247 DType::Extension(_) => vec![], DType::Variant(_) => unimplemented!(),
1249 };
1250
1251 for target_dtype in target_dtypes {
1253 let sliced = array
1255 .slice(start..end)
1256 .vortex_expect("slice should succeed in conformance test");
1257
1258 let slice_then_cast = match sliced
1260 .cast(target_dtype.clone())
1261 .and_then(|a| a.to_canonical().map(|c| c.into_array()))
1262 {
1263 Ok(result) => result,
1264 Err(_) => continue, };
1266
1267 assert_eq!(
1269 slice_then_cast.len(),
1270 end - start,
1271 "Sliced and casted array should have length {}, but has {}",
1272 end - start,
1273 slice_then_cast.len()
1274 );
1275
1276 for i in 0..slice_then_cast.len() {
1278 let slice_cast_val = slice_then_cast
1279 .scalar_at(i)
1280 .vortex_expect("scalar_at should succeed in conformance test");
1281
1282 let canonical_val = canonical
1284 .as_ref()
1285 .scalar_at(start + i)
1286 .vortex_expect("scalar_at should succeed in conformance test");
1287
1288 let expected_val = match canonical_val.cast(&target_dtype) {
1290 Ok(val) => val,
1291 Err(_) => {
1292 break;
1295 }
1296 };
1297
1298 assert_eq!(
1299 slice_cast_val,
1300 expected_val,
1301 "Cast of sliced array produced incorrect value at index {i}. \
1302 Got: {slice_cast_val:?}, Expected: {expected_val:?} \
1303 (canonical value at index {}: {canonical_val:?})\n\
1304 This likely indicates the array encoding doesn't preserve offset information during cast.",
1305 start + i
1306 );
1307 }
1308
1309 let casted = match array
1311 .to_array()
1312 .cast(target_dtype.clone())
1313 .and_then(|a| a.to_canonical().map(|c| c.into_array()))
1314 {
1315 Ok(result) => result,
1316 Err(_) => continue, };
1318 let cast_then_slice = casted
1319 .slice(start..end)
1320 .vortex_expect("slice should succeed in conformance test");
1321
1322 assert_eq!(
1324 slice_then_cast.len(),
1325 cast_then_slice.len(),
1326 "Slice-then-cast and cast-then-slice should produce arrays of the same length"
1327 );
1328
1329 for i in 0..slice_then_cast.len() {
1330 let slice_cast_val = slice_then_cast
1331 .scalar_at(i)
1332 .vortex_expect("scalar_at should succeed in conformance test");
1333 let cast_slice_val = cast_then_slice
1334 .scalar_at(i)
1335 .vortex_expect("scalar_at should succeed in conformance test");
1336 assert_eq!(
1337 slice_cast_val, cast_slice_val,
1338 "Slice-then-cast and cast-then-slice produced different values at index {i}. \
1339 Slice-then-cast: {slice_cast_val:?}, Cast-then-slice: {cast_slice_val:?}"
1340 );
1341 }
1342 }
1343}
1344
1345pub fn test_array_consistency(array: &ArrayRef) {
1387 test_filter_take_consistency(array);
1389 test_double_mask_consistency(array);
1390 test_slice_filter_consistency(array);
1391 test_take_slice_consistency(array);
1392 test_cast_slice_consistency(array);
1393
1394 test_boolean_demorgan_consistency(array);
1396
1397 test_comparison_inverse_consistency(array);
1399 test_comparison_symmetry_consistency(array);
1400
1401 test_slice_aggregate_consistency(array);
1403
1404 test_filter_identity(array);
1406 test_mask_identity(array);
1407 test_take_preserves_properties(array);
1408
1409 test_filter_preserves_order(array);
1411 test_take_repeated_indices(array);
1412
1413 test_mask_filter_null_consistency(array);
1415 test_nullable_indices_consistency(array);
1416
1417 test_empty_operations_consistency(array);
1419 test_large_array_consistency(array);
1420}