1use rayon::prelude::*;
6
7use super::types::{LoadBalancePlan, LoadBalanceStrategy, WorkStealQueue};
8
9#[inline]
10pub(super) fn dist3(a: [f64; 3], b: [f64; 3]) -> f64 {
11 let dx = a[0] - b[0];
12 let dy = a[1] - b[1];
13 let dz = a[2] - b[2];
14 (dx * dx + dy * dy + dz * dz).sqrt()
15}
16pub fn parallel_sph_density(
27 positions: &[[f64; 3]],
28 masses: &[f64],
29 h: f64,
30 kernel_fn: impl Fn(f64, f64) -> f64 + Sync,
31) -> Vec<f64> {
32 positions
33 .par_iter()
34 .map(|&pi| {
35 positions
36 .iter()
37 .zip(masses.iter())
38 .map(|(&pj, &mj)| mj * kernel_fn(dist3(pi, pj), h))
39 .sum()
40 })
41 .collect()
42}
43pub fn parallel_pairwise_forces(
54 positions: &[[f64; 3]],
55 n: usize,
56 force_fn: impl Fn(usize, usize, [f64; 3]) -> [f64; 3] + Sync,
57) -> Vec<[f64; 3]> {
58 (0..n)
59 .into_par_iter()
60 .map(|i| {
61 let mut f = [0.0f64; 3];
62 for j in 0..n {
63 if i == j {
64 continue;
65 }
66 let r_ij = [
67 positions[j][0] - positions[i][0],
68 positions[j][1] - positions[i][1],
69 positions[j][2] - positions[i][2],
70 ];
71 let fij = force_fn(i, j, r_ij);
72 f[0] += fij[0];
73 f[1] += fij[1];
74 f[2] += fij[2];
75 }
76 f
77 })
78 .collect()
79}
80pub fn parallel_lj_forces(
88 positions: &[[f64; 3]],
89 epsilon: f64,
90 sigma: f64,
91 cutoff: f64,
92) -> Vec<[f64; 3]> {
93 let n = positions.len();
94 (0..n)
95 .into_par_iter()
96 .map(|i| {
97 let mut f = [0.0f64; 3];
98 for j in 0..n {
99 if i == j {
100 continue;
101 }
102 let dx = positions[j][0] - positions[i][0];
103 let dy = positions[j][1] - positions[i][1];
104 let dz = positions[j][2] - positions[i][2];
105 let r2 = dx * dx + dy * dy + dz * dz;
106 let r = r2.sqrt();
107 if r >= cutoff || r < 1e-12 {
108 continue;
109 }
110 let sr = sigma / r;
111 let sr6 = sr.powi(6);
112 let sr12 = sr6 * sr6;
113 let mag = 4.0 * epsilon * (12.0 * sr12 - 6.0 * sr6) / r2;
114 f[0] -= mag * dx;
115 f[1] -= mag * dy;
116 f[2] -= mag * dz;
117 }
118 f
119 })
120 .collect()
121}
122pub fn parallel_verlet_step(
130 positions: &mut Vec<[f64; 3]>,
131 velocities: &mut Vec<[f64; 3]>,
132 forces: &[[f64; 3]],
133 masses: &[f64],
134 dt: f64,
135) {
136 positions
137 .par_iter_mut()
138 .zip(velocities.par_iter_mut())
139 .zip(forces.par_iter())
140 .zip(masses.par_iter())
141 .for_each(|(((pos, vel), force), &mass)| {
142 let inv_m = 1.0 / mass;
143 for k in 0..3 {
144 let a = force[k] * inv_m;
145 pos[k] += vel[k] * dt + 0.5 * a * dt * dt;
146 vel[k] += 0.5 * a * dt;
147 }
148 });
149}
150pub fn parallel_aabb_pairs(aabbs: &[([f64; 3], [f64; 3])]) -> Vec<(usize, usize)> {
156 let n = aabbs.len();
157 (0..n)
158 .into_par_iter()
159 .flat_map(|i| {
160 let mut local = Vec::new();
161 let (min_i, max_i) = aabbs[i];
162 for (j, &(min_j, max_j)) in aabbs.iter().enumerate().skip(i + 1) {
163 let overlap = (0..3).all(|k| min_i[k] <= max_j[k] && min_j[k] <= max_i[k]);
164 if overlap {
165 local.push((i, j));
166 }
167 }
168 local
169 })
170 .collect()
171}
172pub fn parallel_for(n: usize, chunk_size: usize, f: impl Fn(usize)) {
177 let cs = if chunk_size == 0 { 1 } else { chunk_size };
178 for start in (0..n).step_by(cs) {
179 let end = (start + cs).min(n);
180 for i in start..end {
181 f(i);
182 }
183 }
184}
185pub fn parallel_reduce_sum(data: &[f64]) -> f64 {
187 data.par_iter().copied().sum()
188}
189pub fn parallel_reduce_max(data: &[f64]) -> f64 {
191 data.par_iter()
192 .copied()
193 .reduce(|| f64::NEG_INFINITY, f64::max)
194}
195pub fn parallel_reduce_min(data: &[f64]) -> f64 {
197 data.par_iter().copied().reduce(|| f64::INFINITY, f64::min)
198}
199pub fn parallel_dot_product(a: &[f64], b: &[f64]) -> f64 {
201 a.par_iter()
202 .zip(b.par_iter())
203 .map(|(&ai, &bi)| ai * bi)
204 .sum()
205}
206pub fn parallel_norm2(data: &[f64]) -> f64 {
208 let sum_sq: f64 = data.par_iter().map(|&x| x * x).sum();
209 sum_sq.sqrt()
210}
211pub fn parallel_mean(data: &[f64]) -> f64 {
213 if data.is_empty() {
214 return 0.0;
215 }
216 let sum: f64 = data.par_iter().copied().sum();
217 sum / data.len() as f64
218}
219pub fn parallel_variance(data: &[f64]) -> f64 {
221 if data.is_empty() {
222 return 0.0;
223 }
224 let mean = parallel_mean(data);
225 let sum_sq: f64 = data.par_iter().map(|&x| (x - mean) * (x - mean)).sum();
226 sum_sq / data.len() as f64
227}
228pub fn parallel_sum_count(data: &[f64]) -> (f64, usize) {
230 data.par_iter()
231 .copied()
232 .fold(|| (0.0f64, 0usize), |(s, c), x| (s + x, c + 1))
233 .reduce(|| (0.0, 0), |(s1, c1), (s2, c2)| (s1 + s2, c1 + c2))
234}
235pub fn parallel_reduce_custom(
240 data: &[f64],
241 identity: f64,
242 op: impl Fn(f64, f64) -> f64 + Sync + Send,
243) -> f64 {
244 data.par_iter().copied().reduce(|| identity, op)
245}
246pub fn parallel_exclusive_scan(data: &[f64]) -> Vec<f64> {
252 let n = data.len();
253 if n == 0 {
254 return Vec::new();
255 }
256 let chunk_size = (n / rayon::current_num_threads().max(1)).max(64);
257 let chunks: Vec<&[f64]> = data.chunks(chunk_size).collect();
258 let chunk_sums: Vec<f64> = chunks
259 .par_iter()
260 .map(|chunk| chunk.iter().copied().sum())
261 .collect();
262 let mut offsets = Vec::with_capacity(chunks.len());
263 let mut acc = 0.0;
264 for &cs in &chunk_sums {
265 offsets.push(acc);
266 acc += cs;
267 }
268 let result = vec![0.0; n];
269 chunks.par_iter().enumerate().for_each(|(ci, chunk)| {
270 let base = ci * chunk_size;
271 let offset = offsets[ci];
272 let mut local_acc = offset;
273 let result_ptr = result.as_ptr() as *mut f64;
274 for (k, &v) in chunk.iter().enumerate() {
275 unsafe {
276 *result_ptr.add(base + k) = local_acc;
277 }
278 local_acc += v;
279 }
280 });
281 result
282}
283pub fn parallel_inclusive_scan(data: &[f64]) -> Vec<f64> {
285 let n = data.len();
286 if n == 0 {
287 return Vec::new();
288 }
289 let chunk_size = (n / rayon::current_num_threads().max(1)).max(64);
290 let chunks: Vec<&[f64]> = data.chunks(chunk_size).collect();
291 let chunk_sums: Vec<f64> = chunks
292 .par_iter()
293 .map(|chunk| chunk.iter().copied().sum())
294 .collect();
295 let mut offsets = Vec::with_capacity(chunks.len());
296 let mut acc = 0.0;
297 for &cs in &chunk_sums {
298 offsets.push(acc);
299 acc += cs;
300 }
301 let result = vec![0.0; n];
302 chunks.par_iter().enumerate().for_each(|(ci, chunk)| {
303 let base = ci * chunk_size;
304 let offset = offsets[ci];
305 let mut local_acc = offset;
306 let result_ptr = result.as_ptr() as *mut f64;
307 for (k, &v) in chunk.iter().enumerate() {
308 local_acc += v;
309 unsafe {
310 *result_ptr.add(base + k) = local_acc;
311 }
312 }
313 });
314 result
315}
316pub fn segmented_exclusive_scan(data: &[f64], segment_ids: &[usize]) -> Vec<f64> {
321 let n = data.len();
322 let mut result = vec![0.0; n];
323 if n == 0 {
324 return result;
325 }
326 let mut acc = 0.0;
327 let mut current_seg = segment_ids[0];
328 for i in 0..n {
329 if segment_ids[i] != current_seg {
330 current_seg = segment_ids[i];
331 acc = 0.0;
332 }
333 result[i] = acc;
334 acc += data[i];
335 }
336 result
337}
338pub fn parallel_sort_f64(data: &mut [f64]) {
342 data.par_sort_unstable_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
343}
344pub fn parallel_argsort(data: &[f64]) -> Vec<usize> {
346 let mut indices: Vec<usize> = (0..data.len()).collect();
347 indices.par_sort_unstable_by(|&a, &b| {
348 data[a]
349 .partial_cmp(&data[b])
350 .unwrap_or(std::cmp::Ordering::Equal)
351 });
352 indices
353}
354pub fn parallel_sort_by_key<T: Send>(items: &mut [T], key_fn: impl Fn(&T) -> f64 + Sync + Send) {
356 items.par_sort_unstable_by(|a, b| {
357 let ka = key_fn(a);
358 let kb = key_fn(b);
359 ka.partial_cmp(&kb).unwrap_or(std::cmp::Ordering::Equal)
360 });
361}
362pub fn parallel_partition<T: Send + Sync + Clone>(
366 data: &[T],
367 predicate: impl Fn(&T) -> bool + Sync + Send,
368) -> (Vec<T>, Vec<T>) {
369 let (left, right): (Vec<_>, Vec<_>) =
370 data.par_iter().cloned().partition(|item| predicate(item));
371 (left, right)
372}
373pub fn parallel_rank(data: &[f64]) -> Vec<usize> {
375 let sorted_indices = parallel_argsort(data);
376 let n = data.len();
377 let mut ranks = vec![0usize; n];
378 for (rank, &idx) in sorted_indices.iter().enumerate() {
379 ranks[idx] = rank;
380 }
381 ranks
382}
383pub fn compute_load_balance(
390 n: usize,
391 num_workers: usize,
392 strategy: LoadBalanceStrategy,
393 item_weights: Option<&[f64]>,
394) -> LoadBalancePlan {
395 let nw = num_workers.max(1);
396 match strategy {
397 LoadBalanceStrategy::Static => {
398 let chunk = n.div_ceil(nw);
399 let mut ranges = Vec::with_capacity(nw);
400 let mut weights = Vec::with_capacity(nw);
401 for w in 0..nw {
402 let start = w * chunk;
403 let end = ((w + 1) * chunk).min(n);
404 if start < n {
405 let weight = if let Some(wts) = item_weights {
406 wts[start..end].iter().sum()
407 } else {
408 (end - start) as f64
409 };
410 ranges.push(start..end);
411 weights.push(weight);
412 }
413 }
414 LoadBalancePlan { ranges, weights }
415 }
416 LoadBalanceStrategy::Weighted => {
417 let wts = match item_weights {
418 Some(w) => w,
419 None => {
420 return compute_load_balance(n, num_workers, LoadBalanceStrategy::Static, None);
421 }
422 };
423 let total_weight: f64 = wts.iter().sum();
424 let target_per_worker = total_weight / nw as f64;
425 let mut ranges = Vec::with_capacity(nw);
426 let mut weights = Vec::with_capacity(nw);
427 let mut start = 0;
428 let mut current_weight = 0.0;
429 for (i, &wi) in wts.iter().enumerate() {
430 current_weight += wi;
431 let workers_remaining = nw - ranges.len();
432 let at_last_worker = workers_remaining == 1;
433 let exceeded_target = current_weight >= target_per_worker && !at_last_worker;
434 if exceeded_target {
435 ranges.push(start..(i + 1));
436 weights.push(current_weight);
437 start = i + 1;
438 current_weight = 0.0;
439 }
440 }
441 if start < n || ranges.is_empty() {
442 ranges.push(start..n);
443 weights.push(current_weight);
444 }
445 LoadBalancePlan { ranges, weights }
446 }
447 LoadBalanceStrategy::Guided => {
448 let mut ranges = Vec::new();
449 let mut weights = Vec::new();
450 let mut pos = 0;
451 let mut remaining = n;
452 while remaining > 0 {
453 let min_chunk = 1usize;
454 let chunk = (remaining / nw).max(min_chunk).min(remaining);
455 let end = pos + chunk;
456 let weight = if let Some(wts) = item_weights {
457 wts[pos..end].iter().sum()
458 } else {
459 chunk as f64
460 };
461 ranges.push(pos..end);
462 weights.push(weight);
463 pos = end;
464 remaining -= chunk;
465 }
466 LoadBalancePlan { ranges, weights }
467 }
468 }
469}
470pub fn execute_balanced(
475 plan: &LoadBalancePlan,
476 f: impl Fn(usize, std::ops::Range<usize>) + Sync + Send,
477) {
478 plan.ranges
479 .par_iter()
480 .enumerate()
481 .for_each(|(worker_id, range)| {
482 f(worker_id, range.clone());
483 });
484}
485pub fn parallel_map_reduce<T: Send + Sync>(
489 data: &[T],
490 map_fn: impl Fn(&T) -> f64 + Sync + Send,
491 identity: f64,
492 reduce_fn: impl Fn(f64, f64) -> f64 + Sync + Send,
493) -> f64 {
494 data.par_iter().map(map_fn).reduce(|| identity, reduce_fn)
495}
496pub fn parallel_histogram(data: &[f64], min: f64, max: f64, num_bins: usize) -> Vec<usize> {
501 if num_bins == 0 || max <= min {
502 return vec![0; num_bins];
503 }
504 let step = (max - min) / num_bins as f64;
505 data.par_iter()
506 .fold(
507 || vec![0usize; num_bins],
508 |mut hist, &v| {
509 if v >= min && v < max {
510 let bin = ((v - min) / step) as usize;
511 let bin = bin.min(num_bins - 1);
512 hist[bin] += 1;
513 } else if (v - max).abs() < 1e-15 {
514 hist[num_bins - 1] += 1;
515 }
516 hist
517 },
518 )
519 .reduce(
520 || vec![0usize; num_bins],
521 |mut a, b| {
522 for (ai, bi) in a.iter_mut().zip(b.iter()) {
523 *ai += bi;
524 }
525 a
526 },
527 )
528}
529pub fn stream_compaction<T: Clone>(data: &[T], pred: impl Fn(&T) -> bool) -> (Vec<T>, Vec<usize>) {
538 let mut compacted = Vec::new();
539 let mut scatter_map = Vec::new();
540 for (i, item) in data.iter().enumerate() {
541 if pred(item) {
542 compacted.push(item.clone());
543 scatter_map.push(i);
544 }
545 }
546 (compacted, scatter_map)
547}
548pub fn parallel_stream_compaction<T: Clone + Send + Sync>(
553 data: &[T],
554 pred: impl Fn(&T) -> bool + Sync,
555) -> (Vec<T>, Vec<usize>) {
556 use rayon::iter::IndexedParallelIterator;
557 let pairs: Vec<(T, usize)> = data
558 .par_iter()
559 .enumerate()
560 .filter_map(|(i, item)| {
561 if pred(item) {
562 Some((item.clone(), i))
563 } else {
564 None
565 }
566 })
567 .collect();
568 let compacted: Vec<T> = pairs.iter().map(|(v, _)| v.clone()).collect();
569 let scatter_map: Vec<usize> = pairs.iter().map(|(_, i)| *i).collect();
570 (compacted, scatter_map)
571}
572pub fn segmented_reduce_sum(data: &[f64], segment_ids: &[usize]) -> Vec<f64> {
584 if data.is_empty() {
585 return Vec::new();
586 }
587 let max_seg = *segment_ids.iter().max().unwrap_or(&0);
588 let mut result = vec![0.0f64; max_seg + 1];
589 for (&v, &s) in data.iter().zip(segment_ids.iter()) {
590 result[s] += v;
591 }
592 result
593}
594pub fn segmented_reduce_max(data: &[f64], segment_ids: &[usize]) -> Vec<f64> {
596 if data.is_empty() {
597 return Vec::new();
598 }
599 let max_seg = *segment_ids.iter().max().unwrap_or(&0);
600 let mut result = vec![f64::NEG_INFINITY; max_seg + 1];
601 for (&v, &s) in data.iter().zip(segment_ids.iter()) {
602 if v > result[s] {
603 result[s] = v;
604 }
605 }
606 result
607}
608pub fn segmented_reduce_min(data: &[f64], segment_ids: &[usize]) -> Vec<f64> {
610 if data.is_empty() {
611 return Vec::new();
612 }
613 let max_seg = *segment_ids.iter().max().unwrap_or(&0);
614 let mut result = vec![f64::INFINITY; max_seg + 1];
615 for (&v, &s) in data.iter().zip(segment_ids.iter()) {
616 if v < result[s] {
617 result[s] = v;
618 }
619 }
620 result
621}
622pub fn merge_sort_f64(data: &[f64]) -> Vec<f64> {
627 let mut buf = data.to_vec();
628 merge_sort_recurse(&mut buf);
629 buf
630}
631pub(super) fn merge_sort_recurse(data: &mut [f64]) {
632 let n = data.len();
633 if n <= 1 {
634 return;
635 }
636 let mid = n / 2;
637 merge_sort_recurse(&mut data[..mid]);
638 merge_sort_recurse(&mut data[mid..]);
639 let left: Vec<f64> = data[..mid].to_vec();
640 let right: Vec<f64> = data[mid..].to_vec();
641 let (mut l, mut r, mut i) = (0, 0, 0);
642 while l < left.len() && r < right.len() {
643 if left[l]
644 .partial_cmp(&right[r])
645 .unwrap_or(std::cmp::Ordering::Greater)
646 != std::cmp::Ordering::Greater
647 {
648 data[i] = left[l];
649 l += 1;
650 } else {
651 data[i] = right[r];
652 r += 1;
653 }
654 i += 1;
655 }
656 while l < left.len() {
657 data[i] = left[l];
658 l += 1;
659 i += 1;
660 }
661 while r < right.len() {
662 data[i] = right[r];
663 r += 1;
664 i += 1;
665 }
666}
667pub fn merge_sort_argsort(data: &[f64]) -> Vec<usize> {
671 let mut indices: Vec<usize> = (0..data.len()).collect();
672 merge_argsort_recurse(data, &mut indices);
673 indices
674}
675pub(super) fn merge_argsort_recurse(data: &[f64], indices: &mut [usize]) {
676 let n = indices.len();
677 if n <= 1 {
678 return;
679 }
680 let mid = n / 2;
681 let (left_idx, right_idx) = indices.split_at_mut(mid);
682 merge_argsort_recurse(data, left_idx);
683 merge_argsort_recurse(data, right_idx);
684 let left: Vec<usize> = left_idx.to_vec();
685 let right: Vec<usize> = right_idx.to_vec();
686 let (mut l, mut r, mut i) = (0, 0, 0);
687 while l < left.len() && r < right.len() {
688 let cmp = data[left[l]]
689 .partial_cmp(&data[right[r]])
690 .unwrap_or(std::cmp::Ordering::Greater);
691 if cmp != std::cmp::Ordering::Greater {
692 indices[i] = left[l];
693 l += 1;
694 } else {
695 indices[i] = right[r];
696 r += 1;
697 }
698 i += 1;
699 }
700 while l < left.len() {
701 indices[i] = left[l];
702 l += 1;
703 i += 1;
704 }
705 while r < right.len() {
706 indices[i] = right[r];
707 r += 1;
708 i += 1;
709 }
710}
711pub fn bitonic_sort(data: &[f64]) -> Vec<f64> {
719 let n = data.len();
720 if n == 0 {
721 return Vec::new();
722 }
723 let mut p = 1usize;
724 while p < n {
725 p <<= 1;
726 }
727 let mut buf: Vec<f64> = data.to_vec();
728 buf.resize(p, f64::INFINITY);
729 let mut k = 2usize;
730 while k <= p {
731 let mut j = k >> 1;
732 while j >= 1 {
733 for i in 0..p {
734 let l = i ^ j;
735 if l > i {
736 let ascending = (i & k) == 0;
737 if (ascending && buf[i] > buf[l]) || (!ascending && buf[i] < buf[l]) {
738 buf.swap(i, l);
739 }
740 }
741 }
742 j >>= 1;
743 }
744 k <<= 1;
745 }
746 buf.truncate(n);
747 buf
748}
749pub fn bitonic_argsort(data: &[f64]) -> Vec<usize> {
753 let n = data.len();
754 if n == 0 {
755 return Vec::new();
756 }
757 let mut p = 1usize;
758 while p < n {
759 p <<= 1;
760 }
761 let mut buf: Vec<(f64, usize)> = data
762 .iter()
763 .copied()
764 .enumerate()
765 .map(|(i, v)| (v, i))
766 .collect();
767 buf.resize(p, (f64::INFINITY, usize::MAX));
768 let mut k = 2usize;
769 while k <= p {
770 let mut j = k >> 1;
771 while j >= 1 {
772 for i in 0..p {
773 let l = i ^ j;
774 if l > i {
775 let ascending = (i & k) == 0;
776 let should_swap =
777 (ascending && buf[i].0 > buf[l].0) || (!ascending && buf[i].0 < buf[l].0);
778 if should_swap {
779 buf.swap(i, l);
780 }
781 }
782 }
783 j >>= 1;
784 }
785 k <<= 1;
786 }
787 buf.truncate(n);
788 buf.iter().map(|(_, idx)| *idx).collect()
789}
790pub fn work_steal_queue<T: Send + Clone>(
797 tasks: Vec<T>,
798 num_workers: usize,
799 _process: impl Fn(&T) + Sync,
800) -> Vec<usize> {
801 let nw = num_workers.max(1);
802 let mut queues: Vec<WorkStealQueue<T>> = (0..nw).map(|_| WorkStealQueue::new()).collect();
803 for (i, task) in tasks.into_iter().enumerate() {
804 queues[i % nw].push(task);
805 }
806 let mut processed = vec![0usize; nw];
807 loop {
808 let mut did_work = false;
809 for w in 0..nw {
810 while let Some(task) = queues[w].pop() {
811 _process(&task);
812 processed[w] += 1;
813 did_work = true;
814 }
815 }
816 let max_len = queues.iter().map(|q| q.len()).max().unwrap_or(0);
817 if max_len == 0 {
818 break;
819 }
820 if did_work {
821 continue;
822 }
823 let victim = queues
824 .iter()
825 .enumerate()
826 .max_by_key(|(_, q)| q.len())
827 .map(|(i, _)| i);
828 let thief = queues
829 .iter()
830 .enumerate()
831 .find(|(_, q)| q.is_empty())
832 .map(|(i, _)| i);
833 if let (Some(v), Some(t)) = (victim, thief) {
834 if v != t {
835 if let Some(task) = queues[v].steal() {
836 queues[t].push(task);
837 }
838 } else {
839 break;
840 }
841 } else {
842 break;
843 }
844 }
845 processed
846}
847pub fn compute_load_balance_metric(worker_loads: &[usize]) -> f64 {
852 if worker_loads.is_empty() {
853 return 1.0;
854 }
855 let total: usize = worker_loads.iter().sum();
856 let n = worker_loads.len();
857 let avg = total as f64 / n as f64;
858 let max = *worker_loads.iter().max().unwrap_or(&1) as f64;
859 if max < 1e-15 {
860 return 1.0;
861 }
862 avg / max
863}
864pub fn suggest_chunk_size(n: usize, num_workers: usize, min_chunks_per_worker: usize) -> usize {
867 let nw = num_workers.max(1);
868 let chunks = (nw * min_chunks_per_worker).max(1);
869 n.div_ceil(chunks).max(1)
870}
871pub fn merge_sort_parallel(data: &[f64]) -> Vec<f64> {
877 pub(super) const SERIAL_THRESHOLD: usize = 256;
878 let n = data.len();
879 if n <= 1 {
880 return data.to_vec();
881 }
882 if n <= SERIAL_THRESHOLD {
883 let mut v = data.to_vec();
884 v.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
885 return v;
886 }
887 let mid = n / 2;
888 let (left_slice, right_slice) = data.split_at(mid);
889 let (left_sorted, right_sorted) = rayon::join(
890 || merge_sort_parallel(left_slice),
891 || merge_sort_parallel(right_slice),
892 );
893 merge_two_sorted(&left_sorted, &right_sorted)
894}
895pub fn merge_two_sorted(a: &[f64], b: &[f64]) -> Vec<f64> {
897 let mut result = Vec::with_capacity(a.len() + b.len());
898 let (mut i, mut j) = (0, 0);
899 while i < a.len() && j < b.len() {
900 if a[i] <= b[j] {
901 result.push(a[i]);
902 i += 1;
903 } else {
904 result.push(b[j]);
905 j += 1;
906 }
907 }
908 result.extend_from_slice(&a[i..]);
909 result.extend_from_slice(&b[j..]);
910 result
911}