1use rayon::prelude::*;
19
20pub fn radix_sort_u32(data: &mut Vec<u32>) {
28 if data.len() <= 1 {
29 return;
30 }
31 let n = data.len();
32 let mut buf = vec![0u32; n];
33
34 for pass in 0..4u32 {
35 let shift = pass * 8;
36 let mut counts = [0usize; 256];
37 for &v in data.iter() {
38 let byte = ((v >> shift) & 0xFF) as usize;
39 counts[byte] += 1;
40 }
41 let mut offsets = [0usize; 256];
43 let mut total = 0;
44 for i in 0..256 {
45 offsets[i] = total;
46 total += counts[i];
47 }
48 for &v in data.iter() {
49 let byte = ((v >> shift) & 0xFF) as usize;
50 buf[offsets[byte]] = v;
51 offsets[byte] += 1;
52 }
53 std::mem::swap(data, &mut buf);
54 }
55}
56
57pub fn radix_sort_by_key<T: Clone>(data: &mut Vec<T>, key_fn: impl Fn(&T) -> u32) {
65 if data.len() <= 1 {
66 return;
67 }
68 let mut buf: Vec<T> = data.clone();
69
70 for pass in 0..4u32 {
71 let shift = pass * 8;
72 let mut counts = [0usize; 256];
73 for item in data.iter() {
74 let byte = ((key_fn(item) >> shift) & 0xFF) as usize;
75 counts[byte] += 1;
76 }
77 let mut offsets = [0usize; 256];
78 let mut total = 0;
79 for i in 0..256 {
80 offsets[i] = total;
81 total += counts[i];
82 }
83 for item in data.iter() {
84 let byte = ((key_fn(item) >> shift) & 0xFF) as usize;
85 buf[offsets[byte]] = item.clone();
86 offsets[byte] += 1;
87 }
88 std::mem::swap(data, &mut buf);
89 }
90}
91
92pub fn parallel_prefix_sum(data: &[u32]) -> Vec<u32> {
101 if data.is_empty() {
102 return Vec::new();
103 }
104 let n = data.len();
105 let num_threads = rayon::current_num_threads().max(1);
107 let chunk_size = (n / num_threads).max(1);
108
109 let chunks: Vec<_> = data.chunks(chunk_size).collect();
111 let chunk_sums: Vec<u32> = chunks
112 .par_iter()
113 .map(|chunk| chunk.iter().copied().fold(0u32, u32::wrapping_add))
114 .collect();
115
116 let mut chunk_offsets = vec![0u32; chunk_sums.len()];
118 let mut running = 0u32;
119 for (i, &s) in chunk_sums.iter().enumerate() {
120 chunk_offsets[i] = running;
121 running = running.wrapping_add(s);
122 }
123
124 let mut output = vec![0u32; n];
126 output
127 .par_chunks_mut(chunk_size)
128 .zip(data.par_chunks(chunk_size))
129 .zip(chunk_offsets.par_iter())
130 .for_each(|((out_chunk, in_chunk), &base)| {
131 let mut acc = base;
132 for (o, &v) in out_chunk.iter_mut().zip(in_chunk.iter()) {
133 *o = acc;
134 acc = acc.wrapping_add(v);
135 }
136 });
137
138 output
139}
140
141pub fn parallel_reduce_sum(data: &[f64]) -> f64 {
149 data.par_iter().copied().sum()
150}
151
152pub fn parallel_min_max(data: &[f64]) -> (f64, f64) {
160 if data.is_empty() {
161 return (f64::INFINITY, f64::NEG_INFINITY);
162 }
163 data.par_iter().copied().map(|v| (v, v)).reduce(
164 || (f64::INFINITY, f64::NEG_INFINITY),
165 |(lo1, hi1), (lo2, hi2)| (lo1.min(lo2), hi1.max(hi2)),
166 )
167}
168
169pub fn bitonic_sort(data: &mut Vec<f64>) {
178 let orig_len = data.len();
179 if orig_len <= 1 {
180 return;
181 }
182 let padded = orig_len.next_power_of_two();
184 data.resize(padded, f64::MAX);
185
186 let n = data.len();
187 let mut k = 2;
188 while k <= n {
189 let mut j = k / 2;
190 while j >= 1 {
191 for i in 0..n {
192 let l = i ^ j;
193 if l > i {
194 let ascending = (i & k) == 0;
195 if (ascending && data[i] > data[l]) || (!ascending && data[i] < data[l]) {
196 data.swap(i, l);
197 }
198 }
199 }
200 j /= 2;
201 }
202 k *= 2;
203 }
204
205 data.truncate(orig_len);
206}
207
208pub fn merge_sort_parallel(data: &mut [f64]) {
217 let n = data.len();
218 if n <= 1 {
219 return;
220 }
221 merge_sort_parallel_slice(data);
222}
223
224fn merge_sort_parallel_slice(data: &mut [f64]) {
225 let n = data.len();
226 if n <= 32 {
227 data.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
228 return;
229 }
230 let mid = n / 2;
231 let (left, right) = data.split_at_mut(mid);
232
233 rayon::join(
235 || merge_sort_parallel_slice(left),
236 || merge_sort_parallel_slice(right),
237 );
238
239 let mut tmp = Vec::with_capacity(n);
241 let mut i = 0;
242 let mut j = 0;
243 let (left, right) = data.split_at(mid);
245 while i < left.len() && j < right.len() {
246 if left[i] <= right[j] {
247 tmp.push(left[i]);
248 i += 1;
249 } else {
250 tmp.push(right[j]);
251 j += 1;
252 }
253 }
254 tmp.extend_from_slice(&left[i..]);
255 tmp.extend_from_slice(&right[j..]);
256 data.copy_from_slice(&tmp);
257}
258
259pub fn histogram_u32(data: &[u32], num_buckets: usize) -> Vec<u32> {
271 assert!(num_buckets > 0, "num_buckets must be > 0");
272 if data.is_empty() {
273 return vec![0; num_buckets];
274 }
275 let nb = num_buckets;
276 data.par_chunks(256.max(data.len() / rayon::current_num_threads().max(1)))
278 .map(|chunk| {
279 let mut local = vec![0u32; nb];
280 for &v in chunk {
281 local[(v as usize) % nb] += 1;
282 }
283 local
284 })
285 .reduce(
286 || vec![0u32; nb],
287 |mut acc, local| {
288 for i in 0..nb {
289 acc[i] += local[i];
290 }
291 acc
292 },
293 )
294}
295
296pub fn argsort(data: &[f64]) -> Vec<usize> {
304 let mut indices: Vec<usize> = (0..data.len()).collect();
305 indices.sort_unstable_by(|&a, &b| {
306 data[a]
307 .partial_cmp(&data[b])
308 .unwrap_or(std::cmp::Ordering::Greater)
309 });
310 indices
311}
312
313pub fn nth_element(data: &mut [f64], k: usize) -> f64 {
325 assert!(!data.is_empty(), "nth_element: data must not be empty");
326 assert!(
327 k < data.len(),
328 "nth_element: k={k} out of bounds (len={})",
329 data.len()
330 );
331 nth_element_slice(data, k);
332 data[k]
333}
334
335fn nth_element_slice(data: &mut [f64], k: usize) {
336 if data.len() <= 1 {
337 return;
338 }
339 let pivot_idx = partition(data);
340 if k < pivot_idx {
341 nth_element_slice(&mut data[..pivot_idx], k);
342 } else if k > pivot_idx {
343 nth_element_slice(&mut data[pivot_idx + 1..], k - pivot_idx - 1);
344 }
345 }
347
348fn partition(data: &mut [f64]) -> usize {
350 let n = data.len();
351 let mid = n / 2;
353 let last = n - 1;
354 if data[0] > data[mid] {
355 data.swap(0, mid);
356 }
357 if data[0] > data[last] {
358 data.swap(0, last);
359 }
360 if data[mid] > data[last] {
361 data.swap(mid, last);
362 }
363 data.swap(mid, last - 1.min(last));
365 let pivot_pos = if n >= 3 { last - 1 } else { last };
366 let pivot = data[pivot_pos];
367 data.swap(pivot_pos, last);
368 let mut store = 0;
369 for i in 0..last {
370 let v = data[i];
371 if v < pivot || (v == pivot && store < last) {
372 data.swap(i, store);
373 store += 1;
374 }
375 }
376 data.swap(store, last);
377 store
378}
379
380pub fn is_sorted_f64(data: &[f64]) -> bool {
388 data.windows(2).all(|w| w[0] <= w[1])
389}
390
391pub fn is_sorted_u32(data: &[u32]) -> bool {
393 data.windows(2).all(|w| w[0] <= w[1])
394}
395
396pub fn count_inversions_f64(data: &[f64]) -> u64 {
400 if data.len() <= 1 {
401 return 0;
402 }
403 let mut tmp = data.to_vec();
404 count_inversions_helper(&mut tmp)
405}
406
407fn count_inversions_helper(data: &mut [f64]) -> u64 {
408 let n = data.len();
409 if n <= 1 {
410 return 0;
411 }
412 let mid = n / 2;
413 let mut left = data[..mid].to_vec();
414 let mut right = data[mid..].to_vec();
415 let mut count = count_inversions_helper(&mut left);
416 count += count_inversions_helper(&mut right);
417
418 let mut i = 0;
419 let mut j = 0;
420 let mut k = 0;
421 while i < left.len() && j < right.len() {
422 if left[i] <= right[j] {
423 data[k] = left[i];
424 i += 1;
425 } else {
426 data[k] = right[j];
427 count += (left.len() - i) as u64;
428 j += 1;
429 }
430 k += 1;
431 }
432 while i < left.len() {
433 data[k] = left[i];
434 i += 1;
435 k += 1;
436 }
437 while j < right.len() {
438 data[k] = right[j];
439 j += 1;
440 k += 1;
441 }
442 count
443}
444
445pub struct SortTimingResult {
451 pub name: String,
453 pub n: usize,
455 pub correct: bool,
457}
458
459pub fn compare_sorts(data: &[f64]) -> Vec<SortTimingResult> {
463 let mut results = Vec::new();
464
465 let mut d1 = data.to_vec();
467 bitonic_sort(&mut d1);
468 results.push(SortTimingResult {
469 name: "bitonic".into(),
470 n: data.len(),
471 correct: is_sorted_f64(&d1),
472 });
473
474 let mut d2 = data.to_vec();
476 merge_sort_parallel(&mut d2);
477 results.push(SortTimingResult {
478 name: "merge_parallel".into(),
479 n: data.len(),
480 correct: is_sorted_f64(&d2),
481 });
482
483 let mut d3: Vec<u32> = data.iter().map(|&v| v as u32).collect();
485 radix_sort_u32(&mut d3);
486 results.push(SortTimingResult {
487 name: "radix_u32".into(),
488 n: data.len(),
489 correct: is_sorted_u32(&d3),
490 });
491
492 results
493}
494
495pub fn is_permutation_f64(a: &[f64], b: &[f64]) -> bool {
497 if a.len() != b.len() {
498 return false;
499 }
500 let mut sa = a.to_vec();
501 let mut sb = b.to_vec();
502 sa.sort_unstable_by(|x, y| x.partial_cmp(y).unwrap_or(std::cmp::Ordering::Equal));
503 sb.sort_unstable_by(|x, y| x.partial_cmp(y).unwrap_or(std::cmp::Ordering::Equal));
504 sa == sb
505}
506
507pub fn is_permutation_u32(a: &[u32], b: &[u32]) -> bool {
509 if a.len() != b.len() {
510 return false;
511 }
512 let mut sa = a.to_vec();
513 let mut sb = b.to_vec();
514 sa.sort_unstable();
515 sb.sort_unstable();
516 sa == sb
517}
518
519#[cfg(test)]
524mod tests {
525 use super::*;
526 use crate::gpu_sort::radix_sort_u32;
527
528 use crate::parallel_sort::is_permutation_f64;
529 use crate::parallel_sort::is_permutation_u32;
530 use crate::parallel_sort::is_sorted_f64;
531 use crate::parallel_sort::is_sorted_u32;
532
533 #[test]
536 fn test_radix_sort_empty() {
537 let mut v: Vec<u32> = vec![];
538 radix_sort_u32(&mut v);
539 assert!(v.is_empty());
540 }
541
542 #[test]
543 fn test_radix_sort_single() {
544 let mut v = vec![42u32];
545 radix_sort_u32(&mut v);
546 assert_eq!(v, [42]);
547 }
548
549 #[test]
550 fn test_radix_sort_sorted() {
551 let mut v = vec![1u32, 2, 3, 4, 5];
552 radix_sort_u32(&mut v);
553 assert_eq!(v, [1, 2, 3, 4, 5]);
554 }
555
556 #[test]
557 fn test_radix_sort_reverse() {
558 let mut v = vec![5u32, 4, 3, 2, 1];
559 radix_sort_u32(&mut v);
560 assert_eq!(v, [1, 2, 3, 4, 5]);
561 }
562
563 #[test]
564 fn test_radix_sort_random_u32() {
565 let mut v: Vec<u32> = (0..1000u32).rev().collect();
566 radix_sort_u32(&mut v);
567 for (i, &val) in v.iter().enumerate() {
568 assert_eq!(val, i as u32, "mismatch at index {i}");
569 }
570 }
571
572 #[test]
573 fn test_radix_sort_large_values() {
574 let mut v = vec![u32::MAX, 0, u32::MAX / 2, 1, u32::MAX - 1];
575 radix_sort_u32(&mut v);
576 assert_eq!(v, [0, 1, u32::MAX / 2, u32::MAX - 1, u32::MAX]);
577 }
578
579 #[test]
582 fn test_radix_sort_by_key_strings() {
583 let mut v: Vec<(&str, u32)> = vec![("c", 3), ("a", 1), ("b", 2)];
584 radix_sort_by_key(&mut v, |item| item.1);
585 assert_eq!(v, [("a", 1), ("b", 2), ("c", 3)]);
586 }
587
588 #[test]
589 fn test_radix_sort_by_key_empty() {
590 let mut v: Vec<(usize, u32)> = vec![];
591 radix_sort_by_key(&mut v, |item| item.1);
592 assert!(v.is_empty());
593 }
594
595 #[test]
598 fn test_prefix_sum_empty() {
599 assert!(parallel_prefix_sum(&[]).is_empty());
600 }
601
602 #[test]
603 fn test_prefix_sum_single() {
604 assert_eq!(parallel_prefix_sum(&[7]), vec![0]);
605 }
606
607 #[test]
608 fn test_prefix_sum_basic() {
609 let data = [1u32, 2, 3, 4, 5];
610 let out = parallel_prefix_sum(&data);
611 assert_eq!(out, vec![0, 1, 3, 6, 10]);
612 }
613
614 #[test]
615 fn test_prefix_sum_ones() {
616 let data = vec![1u32; 100];
617 let out = parallel_prefix_sum(&data);
618 for (i, &v) in out.iter().enumerate() {
619 assert_eq!(v, i as u32, "prefix[{i}] should be {i}");
620 }
621 }
622
623 #[test]
626 fn test_reduce_sum_empty() {
627 assert_eq!(parallel_reduce_sum(&[]), 0.0);
628 }
629
630 #[test]
631 fn test_reduce_sum_basic() {
632 let data = [1.0f64, 2.0, 3.0, 4.0, 5.0];
633 assert!((parallel_reduce_sum(&data) - 15.0).abs() < 1e-12);
634 }
635
636 #[test]
637 fn test_reduce_sum_large() {
638 let data: Vec<f64> = (1..=1000).map(|i| i as f64).collect();
639 let expected = 1000.0 * 1001.0 / 2.0;
640 assert!((parallel_reduce_sum(&data) - expected).abs() < 1e-6);
641 }
642
643 #[test]
646 fn test_min_max_empty() {
647 let (lo, hi) = parallel_min_max(&[]);
648 assert!(lo.is_infinite() && lo > 0.0);
649 assert!(hi.is_infinite() && hi < 0.0);
650 }
651
652 #[test]
653 fn test_min_max_single() {
654 let (lo, hi) = parallel_min_max(&[3.125]);
655 assert!((lo - 3.125).abs() < 1e-12);
656 assert!((hi - 3.125).abs() < 1e-12);
657 }
658
659 #[test]
660 fn test_min_max_basic() {
661 let data = [3.0f64, 1.0, 4.0, 1.5, 9.2, 2.6];
662 let (lo, hi) = parallel_min_max(&data);
663 assert!((lo - 1.0).abs() < 1e-12);
664 assert!((hi - 9.2).abs() < 1e-12);
665 }
666
667 #[test]
670 fn test_bitonic_sort_empty() {
671 let mut v: Vec<f64> = vec![];
672 bitonic_sort(&mut v);
673 assert!(v.is_empty());
674 }
675
676 #[test]
677 fn test_bitonic_sort_power_of_two() {
678 let mut v = vec![4.0f64, 2.0, 7.0, 1.0, 5.0, 3.0, 6.0, 8.0];
679 bitonic_sort(&mut v);
680 assert_eq!(v, [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]);
681 }
682
683 #[test]
684 fn test_bitonic_sort_non_power_of_two() {
685 let mut v = vec![5.0f64, 3.0, 1.0, 4.0, 2.0];
686 bitonic_sort(&mut v);
687 assert_eq!(v, [1.0, 2.0, 3.0, 4.0, 5.0]);
688 }
689
690 #[test]
693 fn test_merge_sort_empty() {
694 let mut v: Vec<f64> = vec![];
695 merge_sort_parallel(&mut v);
696 assert!(v.is_empty());
697 }
698
699 #[test]
700 fn test_merge_sort_basic() {
701 let mut v = vec![3.0f64, 1.0, 4.0, 1.5, 9.0, 2.6];
702 merge_sort_parallel(&mut v);
703 assert_eq!(v, [1.0, 1.5, 2.6, 3.0, 4.0, 9.0]);
704 }
705
706 #[test]
707 fn test_merge_sort_large() {
708 let mut v: Vec<f64> = (0..500u32).rev().map(|x| x as f64).collect();
709 merge_sort_parallel(&mut v);
710 for (i, &val) in v.iter().enumerate() {
711 assert!((val - i as f64).abs() < 1e-12, "mismatch at {i}");
712 }
713 }
714
715 #[test]
718 fn test_histogram_empty() {
719 let h = histogram_u32(&[], 4);
720 assert_eq!(h, vec![0, 0, 0, 0]);
721 }
722
723 #[test]
724 fn test_histogram_basic() {
725 let data = [0u32, 1, 2, 3, 0, 1, 2, 0];
726 let h = histogram_u32(&data, 4);
727 assert_eq!(h, vec![3, 2, 2, 1]);
728 }
729
730 #[test]
731 fn test_histogram_one_bucket() {
732 let data: Vec<u32> = (0..10).collect();
733 let h = histogram_u32(&data, 1);
734 assert_eq!(h, vec![10]);
735 }
736
737 #[test]
740 fn test_argsort_empty() {
741 assert!(argsort(&[]).is_empty());
742 }
743
744 #[test]
745 fn test_argsort_basic() {
746 let data = [3.0f64, 1.0, 4.0, 1.5, 9.0];
747 let idx = argsort(&data);
748 let sorted: Vec<f64> = idx.iter().map(|&i| data[i]).collect();
749 assert_eq!(sorted, [1.0, 1.5, 3.0, 4.0, 9.0]);
750 }
751
752 #[test]
753 fn test_argsort_already_sorted() {
754 let data = [1.0f64, 2.0, 3.0, 4.0, 5.0];
755 let idx = argsort(&data);
756 assert_eq!(idx, [0, 1, 2, 3, 4]);
757 }
758
759 #[test]
762 fn test_nth_element_single() {
763 let mut v = vec![42.0f64];
764 assert!((nth_element(&mut v, 0) - 42.0).abs() < 1e-12);
765 }
766
767 #[test]
768 fn test_nth_element_median() {
769 let mut v = vec![3.0f64, 1.0, 4.0, 1.5, 9.0, 2.6, 5.0];
770 let median = nth_element(&mut v, 3);
772 assert!((median - 3.0).abs() < 1e-12, "expected 3.0, got {median}");
773 }
774
775 #[test]
776 fn test_nth_element_min() {
777 let mut v = vec![5.0f64, 3.0, 8.0, 1.0, 4.0];
778 let min = nth_element(&mut v, 0);
779 assert!((min - 1.0).abs() < 1e-12, "expected 1.0, got {min}");
780 }
781
782 #[test]
783 fn test_nth_element_max() {
784 let mut v = vec![5.0f64, 3.0, 8.0, 1.0, 4.0];
785 let max = nth_element(&mut v, 4);
786 assert!((max - 8.0).abs() < 1e-12, "expected 8.0, got {max}");
787 }
788
789 #[test]
790 fn test_nth_element_duplicates() {
791 let mut v = vec![2.0f64, 2.0, 2.0, 2.0, 2.0];
792 let val = nth_element(&mut v, 2);
793 assert!((val - 2.0).abs() < 1e-12);
794 }
795
796 #[test]
799 fn test_is_sorted_f64_empty() {
800 assert!(is_sorted_f64(&[]));
801 }
802
803 #[test]
804 fn test_is_sorted_f64_sorted() {
805 assert!(is_sorted_f64(&[1.0, 2.0, 3.0, 4.0]));
806 }
807
808 #[test]
809 fn test_is_sorted_f64_unsorted() {
810 assert!(!is_sorted_f64(&[1.0, 3.0, 2.0, 4.0]));
811 }
812
813 #[test]
814 fn test_is_sorted_u32_sorted() {
815 assert!(is_sorted_u32(&[0, 1, 2, 3, 4]));
816 }
817
818 #[test]
819 fn test_is_sorted_u32_unsorted() {
820 assert!(!is_sorted_u32(&[0, 2, 1, 3]));
821 }
822
823 #[test]
826 fn test_count_inversions_sorted() {
827 assert_eq!(count_inversions_f64(&[1.0, 2.0, 3.0, 4.0]), 0);
828 }
829
830 #[test]
831 fn test_count_inversions_reversed() {
832 assert_eq!(count_inversions_f64(&[4.0, 3.0, 2.0, 1.0]), 6);
834 }
835
836 #[test]
837 fn test_count_inversions_one_swap() {
838 assert_eq!(count_inversions_f64(&[2.0, 1.0, 3.0, 4.0]), 1);
839 }
840
841 #[test]
842 fn test_count_inversions_empty() {
843 assert_eq!(count_inversions_f64(&[]), 0);
844 }
845
846 #[test]
849 fn test_is_permutation_f64_true() {
850 assert!(is_permutation_f64(&[3.0, 1.0, 2.0], &[1.0, 2.0, 3.0]));
851 }
852
853 #[test]
854 fn test_is_permutation_f64_false() {
855 assert!(!is_permutation_f64(&[3.0, 1.0, 2.0], &[1.0, 2.0, 4.0]));
856 }
857
858 #[test]
859 fn test_is_permutation_f64_different_lengths() {
860 assert!(!is_permutation_f64(&[1.0, 2.0], &[1.0, 2.0, 3.0]));
861 }
862
863 #[test]
864 fn test_is_permutation_u32_true() {
865 assert!(is_permutation_u32(&[3, 1, 2], &[1, 2, 3]));
866 }
867
868 #[test]
869 fn test_is_permutation_u32_false() {
870 assert!(!is_permutation_u32(&[1, 2, 3], &[1, 2, 4]));
871 }
872
873 #[test]
876 fn test_bitonic_sort_preserves_elements() {
877 let original = vec![5.0, 3.0, 8.0, 1.0, 4.0, 7.0, 2.0, 6.0];
878 let mut sorted = original.clone();
879 bitonic_sort(&mut sorted);
880 assert!(is_permutation_f64(&original, &sorted));
881 assert!(is_sorted_f64(&sorted));
882 }
883
884 #[test]
885 fn test_merge_sort_preserves_elements() {
886 let original = vec![5.0, 3.0, 8.0, 1.0, 4.0, 7.0, 2.0, 6.0];
887 let mut sorted = original.clone();
888 merge_sort_parallel(&mut sorted);
889 assert!(is_permutation_f64(&original, &sorted));
890 assert!(is_sorted_f64(&sorted));
891 }
892
893 #[test]
894 fn test_radix_sort_preserves_elements() {
895 let original = vec![5u32, 3, 8, 1, 4, 7, 2, 6];
896 let mut sorted = original.clone();
897 radix_sort_u32(&mut sorted);
898 assert!(is_permutation_u32(&original, &sorted));
899 assert!(is_sorted_u32(&sorted));
900 }
901
902 #[test]
905 fn test_compare_sorts_all_correct() {
906 let data: Vec<f64> = (0..100u32).rev().map(|x| x as f64).collect();
907 let results = compare_sorts(&data);
908 for r in &results {
909 assert!(r.correct, "sort {} failed for n={}", r.name, r.n);
910 }
911 }
912
913 #[test]
914 fn test_compare_sorts_empty() {
915 let results = compare_sorts(&[]);
916 for r in &results {
917 assert!(r.correct);
918 }
919 }
920
921 #[test]
924 fn test_bitonic_sort_single() {
925 let mut v = vec![42.0_f64];
926 bitonic_sort(&mut v);
927 assert_eq!(v, [42.0]);
928 }
929
930 #[test]
931 fn test_bitonic_sort_already_sorted() {
932 let mut v = vec![1.0, 2.0, 3.0, 4.0];
933 bitonic_sort(&mut v);
934 assert_eq!(v, [1.0, 2.0, 3.0, 4.0]);
935 }
936
937 #[test]
938 fn test_bitonic_sort_duplicates() {
939 let mut v = vec![3.0, 1.0, 3.0, 1.0, 2.0, 2.0];
940 bitonic_sort(&mut v);
941 assert_eq!(v, [1.0, 1.0, 2.0, 2.0, 3.0, 3.0]);
942 }
943
944 #[test]
947 fn test_merge_sort_single() {
948 let mut v = vec![42.0_f64];
949 merge_sort_parallel(&mut v);
950 assert_eq!(v, [42.0]);
951 }
952
953 #[test]
954 fn test_merge_sort_two_elements() {
955 let mut v = vec![2.0, 1.0];
956 merge_sort_parallel(&mut v);
957 assert_eq!(v, [1.0, 2.0]);
958 }
959
960 #[test]
961 fn test_merge_sort_duplicates() {
962 let mut v = vec![5.0, 1.0, 5.0, 1.0, 3.0];
963 merge_sort_parallel(&mut v);
964 assert_eq!(v, [1.0, 1.0, 3.0, 5.0, 5.0]);
965 }
966
967 #[test]
970 fn test_radix_sort_all_same() {
971 let mut v = vec![7u32, 7, 7, 7, 7];
972 radix_sort_u32(&mut v);
973 assert_eq!(v, [7, 7, 7, 7, 7]);
974 }
975
976 #[test]
977 fn test_radix_sort_two_elements() {
978 let mut v = vec![2u32, 1];
979 radix_sort_u32(&mut v);
980 assert_eq!(v, [1, 2]);
981 }
982
983 #[test]
986 fn test_argsort_duplicates() {
987 let data = [3.0, 1.0, 3.0, 1.0];
988 let idx = argsort(&data);
989 let sorted: Vec<f64> = idx.iter().map(|&i| data[i]).collect();
990 assert!(is_sorted_f64(&sorted));
991 }
992
993 #[test]
994 fn test_argsort_single() {
995 let idx = argsort(&[42.0]);
996 assert_eq!(idx, [0]);
997 }
998
999 #[test]
1002 fn test_nth_element_sorted_input() {
1003 let mut v = vec![1.0, 2.0, 3.0, 4.0, 5.0];
1004 let val = nth_element(&mut v, 2);
1005 assert!((val - 3.0).abs() < 1e-12);
1006 }
1007
1008 #[test]
1009 fn test_nth_element_reversed() {
1010 let mut v = vec![5.0, 4.0, 3.0, 2.0, 1.0];
1011 let val = nth_element(&mut v, 0);
1012 assert!((val - 1.0).abs() < 1e-12);
1013 }
1014}
1015
1016pub fn radix_sort_stage_u32(data: &[u32], shift: u32) -> (Vec<u32>, [usize; 256]) {
1025 let n = data.len();
1026 let mut counts = [0usize; 256];
1027 for &v in data {
1028 let byte = ((v >> shift) & 0xFF) as usize;
1029 counts[byte] += 1;
1030 }
1031 let mut offsets = [0usize; 256];
1032 let mut total = 0;
1033 for i in 0..256 {
1034 offsets[i] = total;
1035 total += counts[i];
1036 }
1037 let mut out = vec![0u32; n];
1038 let mut pos = offsets;
1039 for &v in data {
1040 let byte = ((v >> shift) & 0xFF) as usize;
1041 out[pos[byte]] = v;
1042 pos[byte] += 1;
1043 }
1044 (out, counts)
1045}
1046
1047pub fn radix_sort_gpu_staged(data: &[u32]) -> Vec<u32> {
1051 if data.is_empty() {
1052 return Vec::new();
1053 }
1054 let mut current = data.to_vec();
1055 for pass in 0..4u32 {
1056 let (sorted, _counts) = radix_sort_stage_u32(¤t, pass * 8);
1057 current = sorted;
1058 }
1059 current
1060}
1061
1062pub fn radix_histogram(data: &[u32], shift: u32) -> Vec<u32> {
1067 let mut counts = vec![0u32; 256];
1068 for &v in data {
1069 let byte = ((v >> shift) & 0xFF) as usize;
1070 counts[byte] += 1;
1071 }
1072 counts
1073}
1074
1075pub fn validate_radix_sort(original: &[u32], sorted: &[u32]) -> bool {
1077 is_permutation_u32(original, sorted) && is_sorted_u32(sorted)
1078}
1079
1080pub fn counting_sort_u32(data: &[u32], max_val: u32) -> Vec<u32> {
1091 if data.is_empty() {
1092 return Vec::new();
1093 }
1094 let m = max_val as usize + 1;
1095 let mut counts = vec![0u32; m];
1096 for &v in data {
1097 assert!((v as usize) < m, "value {v} exceeds max_val {max_val}");
1098 counts[v as usize] += 1;
1099 }
1100 let mut out = Vec::with_capacity(data.len());
1101 for (v, &c) in counts.iter().enumerate() {
1102 for _ in 0..c {
1103 out.push(v as u32);
1104 }
1105 }
1106 out
1107}
1108
1109pub fn counting_sort_by_key<T: Clone>(data: &[(u32, T)], max_key: u32) -> Vec<(u32, T)> {
1113 if data.is_empty() {
1114 return Vec::new();
1115 }
1116 let m = max_key as usize + 1;
1117 let mut counts = vec![0usize; m];
1118 for (k, _) in data {
1119 assert!((*k as usize) < m, "key {k} exceeds max_key {max_key}");
1120 counts[*k as usize] += 1;
1121 }
1122 let mut offsets = vec![0usize; m];
1124 let mut running = 0;
1125 for i in 0..m {
1126 offsets[i] = running;
1127 running += counts[i];
1128 }
1129 let mut out: Vec<Option<(u32, T)>> = (0..data.len()).map(|_| None).collect();
1130 for (k, v) in data {
1131 let idx = *k as usize;
1132 out[offsets[idx]] = Some((*k, v.clone()));
1133 offsets[idx] += 1;
1134 }
1135 out.into_iter().flatten().collect()
1136}
1137
1138pub fn histogram_bucket_sort(data: &mut [f64], n_buckets: usize) {
1147 let n = data.len();
1148 if n <= 1 || n_buckets == 0 {
1149 data.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
1150 return;
1151 }
1152
1153 let (lo, hi) = {
1154 let mut lo = f64::INFINITY;
1155 let mut hi = f64::NEG_INFINITY;
1156 for &v in data.iter() {
1157 if v < lo {
1158 lo = v;
1159 }
1160 if v > hi {
1161 hi = v;
1162 }
1163 }
1164 (lo, hi)
1165 };
1166
1167 if (hi - lo).abs() < f64::EPSILON {
1168 return; }
1170
1171 let nb = n_buckets;
1172 let range = hi - lo;
1173 let mut buckets: Vec<Vec<f64>> = vec![Vec::new(); nb];
1174
1175 for &v in data.iter() {
1176 let idx = ((v - lo) / range * nb as f64) as usize;
1177 let idx = idx.min(nb - 1);
1178 buckets[idx].push(v);
1179 }
1180
1181 for b in &mut buckets {
1182 b.sort_unstable_by(|a, c| a.partial_cmp(c).unwrap_or(std::cmp::Ordering::Equal));
1183 }
1184
1185 let mut pos = 0;
1186 for b in &buckets {
1187 for &v in b {
1188 data[pos] = v;
1189 pos += 1;
1190 }
1191 }
1192}
1193
1194pub fn adaptive_bucket_sort(data: &mut [f64], n_buckets: usize) {
1199 histogram_bucket_sort(data, n_buckets.max(1));
1200}
1201
1202pub struct SortValidation {
1208 pub is_sorted: bool,
1210 pub is_permutation: bool,
1212 pub n: usize,
1214 pub inversions: u64,
1216}
1217
1218impl SortValidation {
1219 pub fn validate_f64(original: &[f64], sorted: &[f64]) -> Self {
1221 let is_sorted = is_sorted_f64(sorted);
1222 let is_perm = is_permutation_f64(original, sorted);
1223 let inversions = if is_sorted {
1224 0
1225 } else {
1226 count_inversions_f64(sorted)
1227 };
1228 Self {
1229 is_sorted,
1230 is_permutation: is_perm,
1231 n: sorted.len(),
1232 inversions,
1233 }
1234 }
1235
1236 pub fn validate_u32(original: &[u32], sorted: &[u32]) -> Self {
1238 let is_sorted = is_sorted_u32(sorted);
1239 let is_perm = is_permutation_u32(original, sorted);
1240 Self {
1241 is_sorted,
1242 is_permutation: is_perm,
1243 n: sorted.len(),
1244 inversions: 0,
1245 }
1246 }
1247
1248 pub fn is_correct(&self) -> bool {
1250 self.is_sorted && self.is_permutation
1251 }
1252}
1253
1254pub fn merge_sorted(left: &[f64], right: &[f64]) -> Vec<f64> {
1262 let mut out = Vec::with_capacity(left.len() + right.len());
1263 let mut i = 0;
1264 let mut j = 0;
1265 while i < left.len() && j < right.len() {
1266 if left[i] <= right[j] {
1267 out.push(left[i]);
1268 i += 1;
1269 } else {
1270 out.push(right[j]);
1271 j += 1;
1272 }
1273 }
1274 out.extend_from_slice(&left[i..]);
1275 out.extend_from_slice(&right[j..]);
1276 out
1277}
1278
1279pub fn merge_sorted_u32(left: &[u32], right: &[u32]) -> Vec<u32> {
1281 let mut out = Vec::with_capacity(left.len() + right.len());
1282 let mut i = 0;
1283 let mut j = 0;
1284 while i < left.len() && j < right.len() {
1285 if left[i] <= right[j] {
1286 out.push(left[i]);
1287 i += 1;
1288 } else {
1289 out.push(right[j]);
1290 j += 1;
1291 }
1292 }
1293 out.extend_from_slice(&left[i..]);
1294 out.extend_from_slice(&right[j..]);
1295 out
1296}
1297
1298pub fn k_way_merge(slices: &[Vec<f64>]) -> Vec<f64> {
1302 let total: usize = slices.iter().map(|s| s.len()).sum();
1304 let mut result = Vec::with_capacity(total);
1305 for s in slices {
1306 result.extend_from_slice(s);
1307 }
1308 result.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
1309 result
1310}
1311
1312pub fn merge_sort_parallel_threshold(data: &mut [f64], parallel_threshold: usize) {
1317 let n = data.len();
1318 if n <= 1 {
1319 return;
1320 }
1321 merge_sort_threshold_slice(data, parallel_threshold);
1322}
1323
1324fn merge_sort_threshold_slice(data: &mut [f64], threshold: usize) {
1325 let n = data.len();
1326 if n <= 1 {
1327 return;
1328 }
1329 if n <= 16 {
1330 data.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
1331 return;
1332 }
1333 let mid = n / 2;
1334 let (left, right) = data.split_at_mut(mid);
1335
1336 if n >= threshold {
1337 rayon::join(
1338 || merge_sort_threshold_slice(left, threshold),
1339 || merge_sort_threshold_slice(right, threshold),
1340 );
1341 } else {
1342 merge_sort_threshold_slice(left, threshold);
1343 merge_sort_threshold_slice(right, threshold);
1344 }
1345
1346 let mut tmp = Vec::with_capacity(n);
1347 let (left, right) = data.split_at(mid);
1348 let mut i = 0;
1349 let mut j = 0;
1350 while i < left.len() && j < right.len() {
1351 if left[i] <= right[j] {
1352 tmp.push(left[i]);
1353 i += 1;
1354 } else {
1355 tmp.push(right[j]);
1356 j += 1;
1357 }
1358 }
1359 tmp.extend_from_slice(&left[i..]);
1360 tmp.extend_from_slice(&right[j..]);
1361 data.copy_from_slice(&tmp);
1362}
1363
1364#[cfg(test)]
1369mod tests_new_sort {
1370 use super::*;
1371 use crate::gpu_sort::radix_sort_u32;
1372 use crate::parallel_sort::SortValidation;
1373 use crate::parallel_sort::adaptive_bucket_sort;
1374 use crate::parallel_sort::counting_sort_by_key;
1375 use crate::parallel_sort::counting_sort_u32;
1376 use crate::parallel_sort::histogram_bucket_sort;
1377 use crate::parallel_sort::is_permutation_f64;
1378 use crate::parallel_sort::is_permutation_u32;
1379 use crate::parallel_sort::is_sorted_f64;
1380 use crate::parallel_sort::is_sorted_u32;
1381 use crate::parallel_sort::k_way_merge;
1382 use crate::parallel_sort::merge_sort_parallel_threshold;
1383 use crate::parallel_sort::merge_sorted;
1384 use crate::parallel_sort::merge_sorted_u32;
1385 use crate::parallel_sort::radix_histogram;
1386 use crate::parallel_sort::radix_sort_gpu_staged;
1387 use crate::parallel_sort::radix_sort_stage_u32;
1388 use crate::parallel_sort::validate_radix_sort;
1389
1390 #[test]
1393 fn test_radix_sort_stage_pass0() {
1394 let data = vec![300u32, 1, 255, 100, 50];
1395 let (sorted_once, counts) = radix_sort_stage_u32(&data, 0);
1396 assert_eq!(sorted_once.len(), data.len());
1397 let total: usize = counts.iter().sum();
1399 assert_eq!(total, data.len());
1400 }
1401
1402 #[test]
1403 fn test_radix_sort_gpu_staged_sorted() {
1404 let data: Vec<u32> = vec![500, 1, 200, 50, 900, 3, 150];
1405 let sorted = radix_sort_gpu_staged(&data);
1406 assert!(
1407 is_sorted_u32(&sorted),
1408 "staged sort should produce sorted output"
1409 );
1410 assert!(is_permutation_u32(&data, &sorted));
1411 }
1412
1413 #[test]
1414 fn test_radix_sort_gpu_staged_empty() {
1415 let sorted = radix_sort_gpu_staged(&[]);
1416 assert!(sorted.is_empty());
1417 }
1418
1419 #[test]
1420 fn test_radix_histogram_sums() {
1421 let data: Vec<u32> = (0..256).collect();
1422 let h = radix_histogram(&data, 0);
1423 let total: u32 = h.iter().sum();
1424 assert_eq!(total, 256);
1425 for &c in &h {
1427 assert_eq!(c, 1);
1428 }
1429 }
1430
1431 #[test]
1432 fn test_validate_radix_sort() {
1433 let original: Vec<u32> = vec![5, 3, 8, 1, 4];
1434 let mut sorted = original.clone();
1435 radix_sort_u32(&mut sorted);
1436 assert!(validate_radix_sort(&original, &sorted));
1437 }
1438
1439 #[test]
1440 fn test_validate_radix_sort_false_for_unsorted() {
1441 let original = vec![3u32, 1, 2];
1442 let not_sorted = vec![3u32, 1, 2];
1443 assert!(!validate_radix_sort(&original, ¬_sorted));
1444 }
1445
1446 #[test]
1449 fn test_counting_sort_basic() {
1450 let data = vec![3u32, 1, 4, 1, 5, 9, 2, 6, 5, 3];
1451 let sorted = counting_sort_u32(&data, 9);
1452 assert!(is_sorted_u32(&sorted));
1453 assert!(is_permutation_u32(&data, &sorted));
1454 }
1455
1456 #[test]
1457 fn test_counting_sort_empty() {
1458 let sorted = counting_sort_u32(&[], 10);
1459 assert!(sorted.is_empty());
1460 }
1461
1462 #[test]
1463 fn test_counting_sort_all_same() {
1464 let data = vec![5u32; 10];
1465 let sorted = counting_sort_u32(&data, 5);
1466 assert_eq!(sorted, vec![5u32; 10]);
1467 }
1468
1469 #[test]
1470 fn test_counting_sort_by_key() {
1471 let data: Vec<(u32, &str)> = vec![(3, "c"), (1, "a"), (2, "b")];
1472 let sorted = counting_sort_by_key(&data, 3);
1473 assert_eq!(sorted[0].0, 1);
1474 assert_eq!(sorted[1].0, 2);
1475 assert_eq!(sorted[2].0, 3);
1476 }
1477
1478 #[test]
1479 fn test_counting_sort_by_key_stable() {
1480 let data: Vec<(u32, u32)> = vec![(2, 10), (1, 20), (2, 30)];
1482 let sorted = counting_sort_by_key(&data, 2);
1483 assert_eq!(sorted[0].0, 1);
1484 assert_eq!(sorted[1].0, 2);
1485 assert_eq!(sorted[2].0, 2);
1486 assert_eq!(sorted[1].1, 10);
1488 assert_eq!(sorted[2].1, 30);
1489 }
1490
1491 #[test]
1494 fn test_histogram_bucket_sort_basic() {
1495 let mut data = vec![5.0, 3.0, 8.0, 1.0, 4.0, 7.0, 2.0, 6.0];
1496 let original = data.clone();
1497 histogram_bucket_sort(&mut data, 4);
1498 assert!(is_sorted_f64(&data));
1499 assert!(is_permutation_f64(&original, &data));
1500 }
1501
1502 #[test]
1503 fn test_histogram_bucket_sort_single_bucket() {
1504 let mut data = vec![3.0, 1.0, 2.0, 4.0];
1505 let original = data.clone();
1506 histogram_bucket_sort(&mut data, 1);
1507 assert!(is_sorted_f64(&data));
1508 assert!(is_permutation_f64(&original, &data));
1509 }
1510
1511 #[test]
1512 fn test_histogram_bucket_sort_all_equal() {
1513 let mut data = vec![5.0; 10];
1514 histogram_bucket_sort(&mut data, 4);
1515 assert!(is_sorted_f64(&data));
1516 }
1517
1518 #[test]
1519 fn test_histogram_bucket_sort_large() {
1520 let mut data: Vec<f64> = (0..200u32).rev().map(|x| x as f64).collect();
1521 let original = data.clone();
1522 histogram_bucket_sort(&mut data, 20);
1523 assert!(is_sorted_f64(&data));
1524 assert!(is_permutation_f64(&original, &data));
1525 }
1526
1527 #[test]
1528 fn test_adaptive_bucket_sort() {
1529 let mut data = vec![9.0, 3.0, 6.0, 1.0, 8.0, 4.0, 2.0, 7.0, 5.0];
1530 let orig = data.clone();
1531 adaptive_bucket_sort(&mut data, 3);
1532 assert!(is_sorted_f64(&data));
1533 assert!(is_permutation_f64(&orig, &data));
1534 }
1535
1536 #[test]
1539 fn test_sort_validation_correct() {
1540 let orig = vec![3.0, 1.0, 4.0, 1.5, 9.0];
1541 let mut sorted = orig.clone();
1542 merge_sort_parallel(&mut sorted);
1543 let v = SortValidation::validate_f64(&orig, &sorted);
1544 assert!(v.is_correct());
1545 assert_eq!(v.inversions, 0);
1546 assert_eq!(v.n, 5);
1547 }
1548
1549 #[test]
1550 fn test_sort_validation_unsorted() {
1551 let orig = vec![1.0, 3.0, 2.0];
1552 let not_sorted = vec![1.0, 3.0, 2.0];
1553 let v = SortValidation::validate_f64(&orig, ¬_sorted);
1554 assert!(!v.is_sorted);
1555 assert!(v.is_permutation);
1556 assert!(!v.is_correct());
1557 }
1558
1559 #[test]
1560 fn test_sort_validation_u32() {
1561 let orig = vec![5u32, 3, 8, 1];
1562 let mut sorted = orig.clone();
1563 radix_sort_u32(&mut sorted);
1564 let v = SortValidation::validate_u32(&orig, &sorted);
1565 assert!(v.is_correct());
1566 }
1567
1568 #[test]
1571 fn test_merge_sorted_basic() {
1572 let a = vec![1.0, 3.0, 5.0];
1573 let b = vec![2.0, 4.0, 6.0];
1574 let m = merge_sorted(&a, &b);
1575 assert_eq!(m, vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
1576 }
1577
1578 #[test]
1579 fn test_merge_sorted_empty_left() {
1580 let a: Vec<f64> = vec![];
1581 let b = vec![1.0, 2.0, 3.0];
1582 let m = merge_sorted(&a, &b);
1583 assert_eq!(m, b);
1584 }
1585
1586 #[test]
1587 fn test_merge_sorted_empty_right() {
1588 let a = vec![1.0, 2.0, 3.0];
1589 let b: Vec<f64> = vec![];
1590 let m = merge_sorted(&a, &b);
1591 assert_eq!(m, a);
1592 }
1593
1594 #[test]
1595 fn test_merge_sorted_u32() {
1596 let a = vec![1u32, 4, 7];
1597 let b = vec![2u32, 5, 8];
1598 let m = merge_sorted_u32(&a, &b);
1599 assert_eq!(m, vec![1, 2, 4, 5, 7, 8]);
1600 }
1601
1602 #[test]
1603 fn test_k_way_merge() {
1604 let s1 = vec![1.0, 4.0, 7.0];
1605 let s2 = vec![2.0, 5.0, 8.0];
1606 let s3 = vec![3.0, 6.0, 9.0];
1607 let m = k_way_merge(&[s1, s2, s3]);
1608 assert!(is_sorted_f64(&m));
1609 assert_eq!(m.len(), 9);
1610 }
1611
1612 #[test]
1613 fn test_k_way_merge_single() {
1614 let s = vec![vec![3.0, 1.0, 2.0]]; let m = k_way_merge(&s);
1616 assert!(is_sorted_f64(&m));
1617 }
1618
1619 #[test]
1620 fn test_merge_sort_parallel_threshold() {
1621 let mut data: Vec<f64> = (0..100u32).rev().map(|x| x as f64).collect();
1622 let orig = data.clone();
1623 merge_sort_parallel_threshold(&mut data, 32);
1624 assert!(is_sorted_f64(&data));
1625 assert!(is_permutation_f64(&orig, &data));
1626 }
1627
1628 #[test]
1629 fn test_merge_sort_parallel_threshold_small() {
1630 let mut data = vec![3.0, 1.0, 2.0];
1631 let orig = data.clone();
1632 merge_sort_parallel_threshold(&mut data, 1024);
1633 assert!(is_sorted_f64(&data));
1634 assert!(is_permutation_f64(&orig, &data));
1635 }
1636}