use crate::{
maybe_grow,
par::{
heap_sort::heap_sort,
insertion_sort::{insertion_sort_shift_left, partial_insertion_sort},
partition::{choose_pivot, partition, partition_equal},
},
partition::break_patterns,
};
use core::{cmp, mem};
use ndarray::{ArrayViewMut1, Axis, IndexLonger};
pub fn par_quick_sort<T, F>(v: ArrayViewMut1<'_, T>, is_less: F)
where
T: Send,
F: Fn(&T, &T) -> bool + Sync,
{
if mem::size_of::<T>() == 0 {
return;
}
let limit = usize::BITS - v.len().leading_zeros();
recurse(v, &is_less, None, limit);
}
fn recurse<'a, T, F>(
mut v: ArrayViewMut1<'a, T>,
is_less: &F,
mut pred: Option<&'a mut T>,
mut limit: u32,
) where
T: Send,
F: Fn(&T, &T) -> bool + Sync,
{
const MAX_INSERTION: usize = 20;
const MAX_SEQUENTIAL: usize = 2000;
let mut was_balanced = true;
let mut was_partitioned = true;
loop {
let len = v.len();
if len <= MAX_INSERTION {
if len >= 2 {
insertion_sort_shift_left(v, 1, is_less);
}
return;
}
if limit == 0 {
heap_sort(v, is_less);
return;
}
if !was_balanced {
break_patterns(v.view_mut());
limit -= 1;
}
let (pivot, likely_sorted) = choose_pivot(v.view_mut(), is_less);
if was_balanced && was_partitioned && likely_sorted {
if partial_insertion_sort(v.view_mut(), is_less) {
return;
}
}
if let Some(ref p) = pred {
if !is_less(p, &v[pivot]) {
let mid = partition_equal(v.view_mut(), pivot, is_less);
let (_, new_v) = v.split_at(Axis(0), mid);
v = new_v;
continue;
}
}
let (mid, was_p) = partition(v.view_mut(), pivot, is_less);
was_balanced = cmp::min(mid, len - mid) >= len / 8;
was_partitioned = was_p;
let (left, right) = v.split_at(Axis(0), mid);
let (pivot, right) = right.split_at(Axis(0), 1);
let pivot = pivot.index(0);
if cmp::max(left.len(), right.len()) <= MAX_SEQUENTIAL {
if left.len() < right.len() {
maybe_grow(|| recurse(left, is_less, pred, limit));
v = right;
pred = Some(pivot);
} else {
maybe_grow(|| recurse(right, is_less, Some(pivot), limit));
v = left;
}
} else {
rayon::join(
|| maybe_grow(|| recurse(left, is_less, pred, limit)),
|| maybe_grow(|| recurse(right, is_less, Some(pivot), limit)),
);
break;
}
}
}
#[cfg(test)]
mod test {
use super::par_quick_sort;
use ndarray::Array1;
use quickcheck_macros::quickcheck;
#[cfg_attr(miri, ignore)]
#[quickcheck]
fn sorted(xs: Vec<u32>) {
let mut sorted = xs.clone();
sorted.sort_unstable();
let sorted = Array1::from_vec(sorted);
let mut array = Array1::from_vec(xs);
par_quick_sort(array.view_mut(), u32::lt);
assert_eq!(array, sorted);
}
}