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::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.clone().into_array(), Operator::Eq),
725 array
726 .to_array()
727 .binary(const_array.clone().into_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.clone().into_array(), Operator::Gt),
759 array
760 .to_array()
761 .binary(const_array.clone().into_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.clone().into_array(), Operator::Lt),
787 array
788 .to_array()
789 .binary(const_array.into_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.clone().into_array(), Operator::Gt),
855 const_array
856 .clone()
857 .into_array()
858 .binary(array.to_array(), Operator::Lt),
859 ) {
860 assert_eq!(
861 arr_gt_scalar.len(),
862 scalar_lt_arr.len(),
863 "Symmetric comparisons should have same length"
864 );
865
866 for i in 0..arr_gt_scalar.len() {
867 let arr_gt = arr_gt_scalar
868 .scalar_at(i)
869 .vortex_expect("scalar_at should succeed in conformance test");
870 let scalar_lt = scalar_lt_arr
871 .scalar_at(i)
872 .vortex_expect("scalar_at should succeed in conformance test");
873 assert_eq!(
874 arr_gt, scalar_lt,
875 "At index {i}: (array > scalar) should equal (scalar < array). \
876 array > scalar = {arr_gt:?}, scalar < array = {scalar_lt:?}"
877 );
878 }
879 }
880
881 if let (Ok(arr_eq_scalar), Ok(scalar_eq_arr)) = (
883 array
884 .to_array()
885 .binary(const_array.clone().into_array(), Operator::Eq),
886 const_array
887 .into_array()
888 .binary(array.to_array(), 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.to_array().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.clone(), 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.to_array().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.clone(), 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::compute::min_max;
1013 use crate::compute::nan_count;
1014 use crate::compute::sum;
1015 use crate::dtype::DType;
1016
1017 let len = array.len();
1018 if len < 5 {
1019 return; }
1021
1022 let start = 1;
1024 let end = (len - 1).min(start + 10); let sliced = array
1028 .slice(start..end)
1029 .vortex_expect("slice should succeed in conformance test");
1030 let canonical = array.to_canonical().vortex_expect("to_canonical failed");
1031 let canonical_sliced = canonical
1032 .as_ref()
1033 .slice(start..end)
1034 .vortex_expect("slice should succeed in conformance test");
1035
1036 let sliced_invalid_count = sliced
1038 .invalid_count()
1039 .vortex_expect("invalid_count should succeed in conformance test");
1040 let canonical_invalid_count = canonical_sliced
1041 .invalid_count()
1042 .vortex_expect("invalid_count should succeed in conformance test");
1043 assert_eq!(
1044 sliced_invalid_count, canonical_invalid_count,
1045 "null_count on sliced array should match canonical. \
1046 Sliced: {sliced_invalid_count}, Canonical: {canonical_invalid_count}",
1047 );
1048
1049 if !matches!(array.dtype(), DType::Primitive(..)) {
1051 return;
1052 }
1053
1054 if let (Ok(slice_sum), Ok(canonical_sum)) = (sum(&sliced), sum(&canonical_sliced)) {
1055 assert_eq!(
1057 slice_sum, canonical_sum,
1058 "sum on sliced array should match canonical. \
1059 Sliced: {slice_sum:?}, Canonical: {canonical_sum:?}"
1060 );
1061 }
1062
1063 if let (Ok(slice_minmax), Ok(canonical_minmax)) = (min_max(&sliced), min_max(&canonical_sliced))
1065 {
1066 match (slice_minmax, canonical_minmax) {
1067 (Some(s_result), Some(c_result)) => {
1068 assert_eq!(
1069 s_result.min, c_result.min,
1070 "min on sliced array should match canonical. \
1071 Sliced: {:?}, Canonical: {:?}",
1072 s_result.min, c_result.min
1073 );
1074 assert_eq!(
1075 s_result.max, c_result.max,
1076 "max on sliced array should match canonical. \
1077 Sliced: {:?}, Canonical: {:?}",
1078 s_result.max, c_result.max
1079 );
1080 }
1081 (None, None) => {} _ => vortex_panic!("min_max results don't match"),
1083 }
1084 }
1085
1086 if array.dtype().is_float()
1088 && let (Ok(slice_nan_count), Ok(canonical_nan_count)) =
1089 (nan_count(&sliced), nan_count(&canonical_sliced))
1090 {
1091 assert_eq!(
1092 slice_nan_count, canonical_nan_count,
1093 "nan_count on sliced array should match canonical. \
1094 Sliced: {slice_nan_count}, Canonical: {canonical_nan_count}"
1095 );
1096 }
1097}
1098
1099fn test_cast_slice_consistency(array: &ArrayRef) {
1115 let len = array.len();
1116 if len < 5 {
1117 return; }
1119
1120 let start = 2;
1122 let end = 7.min(len - 2).max(start + 1); let canonical = array.to_canonical().vortex_expect("to_canonical failed");
1126
1127 let target_dtypes = match array.dtype() {
1129 DType::Null => vec![],
1130 DType::Bool(nullability) => vec![
1131 DType::Primitive(PType::U8, *nullability),
1132 DType::Primitive(PType::I32, *nullability),
1133 ],
1134 DType::Primitive(ptype, nullability) => {
1135 let mut targets = vec![];
1136 let opposite_nullability = match nullability {
1138 Nullability::NonNullable => Nullability::Nullable,
1139 Nullability::Nullable => Nullability::NonNullable,
1140 };
1141 targets.push(DType::Primitive(*ptype, opposite_nullability));
1142
1143 match ptype {
1145 PType::U8 => {
1146 targets.push(DType::Primitive(PType::U16, *nullability));
1147 targets.push(DType::Primitive(PType::I16, *nullability));
1148 }
1149 PType::U16 => {
1150 targets.push(DType::Primitive(PType::U32, *nullability));
1151 targets.push(DType::Primitive(PType::I32, *nullability));
1152 }
1153 PType::U32 => {
1154 targets.push(DType::Primitive(PType::U64, *nullability));
1155 targets.push(DType::Primitive(PType::I64, *nullability));
1156 }
1157 PType::U64 => {
1158 targets.push(DType::Primitive(PType::F64, *nullability));
1159 }
1160 PType::I8 => {
1161 targets.push(DType::Primitive(PType::I16, *nullability));
1162 targets.push(DType::Primitive(PType::F32, *nullability));
1163 }
1164 PType::I16 => {
1165 targets.push(DType::Primitive(PType::I32, *nullability));
1166 targets.push(DType::Primitive(PType::F32, *nullability));
1167 }
1168 PType::I32 => {
1169 targets.push(DType::Primitive(PType::I64, *nullability));
1170 targets.push(DType::Primitive(PType::F64, *nullability));
1171 }
1172 PType::I64 => {
1173 targets.push(DType::Primitive(PType::F64, *nullability));
1174 }
1175 PType::F16 => {
1176 targets.push(DType::Primitive(PType::F32, *nullability));
1177 }
1178 PType::F32 => {
1179 targets.push(DType::Primitive(PType::F64, *nullability));
1180 targets.push(DType::Primitive(PType::I32, *nullability));
1181 }
1182 PType::F64 => {
1183 targets.push(DType::Primitive(PType::I64, *nullability));
1184 }
1185 }
1186 targets
1187 }
1188 DType::Utf8(nullability) => {
1189 let opposite = match nullability {
1190 Nullability::NonNullable => Nullability::Nullable,
1191 Nullability::Nullable => Nullability::NonNullable,
1192 };
1193 vec![DType::Utf8(opposite), DType::Binary(*nullability)]
1194 }
1195 DType::Binary(nullability) => {
1196 let opposite = match nullability {
1197 Nullability::NonNullable => Nullability::Nullable,
1198 Nullability::Nullable => Nullability::NonNullable,
1199 };
1200 vec![
1201 DType::Binary(opposite),
1202 DType::Utf8(*nullability), ]
1204 }
1205 DType::Decimal(decimal_type, nullability) => {
1206 let opposite = match nullability {
1207 Nullability::NonNullable => Nullability::Nullable,
1208 Nullability::Nullable => Nullability::NonNullable,
1209 };
1210 vec![DType::Decimal(*decimal_type, opposite)]
1211 }
1212 DType::Struct(fields, nullability) => {
1213 let opposite = match nullability {
1214 Nullability::NonNullable => Nullability::Nullable,
1215 Nullability::Nullable => Nullability::NonNullable,
1216 };
1217 vec![DType::Struct(fields.clone(), opposite)]
1218 }
1219 DType::List(element_type, nullability) => {
1220 let opposite = match nullability {
1221 Nullability::NonNullable => Nullability::Nullable,
1222 Nullability::Nullable => Nullability::NonNullable,
1223 };
1224 vec![DType::List(element_type.clone(), opposite)]
1225 }
1226 DType::FixedSizeList(element_type, list_size, nullability) => {
1227 let opposite = match nullability {
1228 Nullability::NonNullable => Nullability::Nullable,
1229 Nullability::Nullable => Nullability::NonNullable,
1230 };
1231 vec![DType::FixedSizeList(
1232 element_type.clone(),
1233 *list_size,
1234 opposite,
1235 )]
1236 }
1237 DType::Extension(_) => vec![], };
1239
1240 for target_dtype in target_dtypes {
1242 let sliced = array
1244 .slice(start..end)
1245 .vortex_expect("slice should succeed in conformance test");
1246
1247 let slice_then_cast = match sliced
1249 .cast(target_dtype.clone())
1250 .and_then(|a| a.to_canonical().map(|c| c.into_array()))
1251 {
1252 Ok(result) => result,
1253 Err(_) => continue, };
1255
1256 assert_eq!(
1258 slice_then_cast.len(),
1259 end - start,
1260 "Sliced and casted array should have length {}, but has {}",
1261 end - start,
1262 slice_then_cast.len()
1263 );
1264
1265 for i in 0..slice_then_cast.len() {
1267 let slice_cast_val = slice_then_cast
1268 .scalar_at(i)
1269 .vortex_expect("scalar_at should succeed in conformance test");
1270
1271 let canonical_val = canonical
1273 .as_ref()
1274 .scalar_at(start + i)
1275 .vortex_expect("scalar_at should succeed in conformance test");
1276
1277 let expected_val = match canonical_val.cast(&target_dtype) {
1279 Ok(val) => val,
1280 Err(_) => {
1281 break;
1284 }
1285 };
1286
1287 assert_eq!(
1288 slice_cast_val,
1289 expected_val,
1290 "Cast of sliced array produced incorrect value at index {i}. \
1291 Got: {slice_cast_val:?}, Expected: {expected_val:?} \
1292 (canonical value at index {}: {canonical_val:?})\n\
1293 This likely indicates the array encoding doesn't preserve offset information during cast.",
1294 start + i
1295 );
1296 }
1297
1298 let casted = match array
1300 .to_array()
1301 .cast(target_dtype.clone())
1302 .and_then(|a| a.to_canonical().map(|c| c.into_array()))
1303 {
1304 Ok(result) => result,
1305 Err(_) => continue, };
1307 let cast_then_slice = casted
1308 .slice(start..end)
1309 .vortex_expect("slice should succeed in conformance test");
1310
1311 assert_eq!(
1313 slice_then_cast.len(),
1314 cast_then_slice.len(),
1315 "Slice-then-cast and cast-then-slice should produce arrays of the same length"
1316 );
1317
1318 for i in 0..slice_then_cast.len() {
1319 let slice_cast_val = slice_then_cast
1320 .scalar_at(i)
1321 .vortex_expect("scalar_at should succeed in conformance test");
1322 let cast_slice_val = cast_then_slice
1323 .scalar_at(i)
1324 .vortex_expect("scalar_at should succeed in conformance test");
1325 assert_eq!(
1326 slice_cast_val, cast_slice_val,
1327 "Slice-then-cast and cast-then-slice produced different values at index {i}. \
1328 Slice-then-cast: {slice_cast_val:?}, Cast-then-slice: {cast_slice_val:?}"
1329 );
1330 }
1331 }
1332}
1333
1334pub fn test_array_consistency(array: &ArrayRef) {
1376 test_filter_take_consistency(array);
1378 test_double_mask_consistency(array);
1379 test_slice_filter_consistency(array);
1380 test_take_slice_consistency(array);
1381 test_cast_slice_consistency(array);
1382
1383 test_boolean_demorgan_consistency(array);
1385
1386 test_comparison_inverse_consistency(array);
1388 test_comparison_symmetry_consistency(array);
1389
1390 test_slice_aggregate_consistency(array);
1392
1393 test_filter_identity(array);
1395 test_mask_identity(array);
1396 test_take_preserves_properties(array);
1397
1398 test_filter_preserves_order(array);
1400 test_take_repeated_indices(array);
1401
1402 test_mask_filter_null_consistency(array);
1404 test_nullable_indices_consistency(array);
1405
1406 test_empty_operations_consistency(array);
1408 test_large_array_consistency(array);
1409}