use core::cmp;
use core::mem::{self, MaybeUninit};
use core::ptr;
use crate::raw::{ElementCtor, EntwineCore, Ptr, PtrMut, Ref, RefMut, Value};
use crate::{Entwine, RefT};
struct CopyOnDrop<T: ElementCtor> {
src: T::PtrMut,
dest: T::PtrMut,
}
impl<T: ElementCtor> Drop for CopyOnDrop<T> {
fn drop(&mut self) {
unsafe {
PtrMut::copy_nonoverlapping(&self.src.to_const(), &self.dest, 1);
}
}
}
fn shift_head<E, T, F>(v: &mut E, is_less: &mut F)
where
E: EntwineCore<ElementCtor = T> + ?Sized,
T: ElementCtor,
F: FnMut(&T::Ref<'_>, &T::Ref<'_>) -> bool,
{
let len = v.len();
unsafe {
if len >= 2 && is_less(&v.get_unchecked(1), &v.get_unchecked(0)) {
let mut tmp = mem::ManuallyDrop::new(Ptr::read(&v.get_unchecked(0).to_ptr()));
let mut hole = CopyOnDrop::<T> {
src: (&mut *tmp).as_mut().to_mut_ptr(),
dest: v.get_unchecked_mut(1).to_mut_ptr(),
};
let dst_ptr = v.get_unchecked_mut(0).to_mut_ptr();
PtrMut::copy_nonoverlapping(&v.get_unchecked(1).to_ptr(), &dst_ptr, 1);
for i in 2..len {
if !is_less(&v.get_unchecked(i), &(&*tmp).as_ref()) {
break;
}
let dst_ptr = v.get_unchecked_mut(i - 1).to_mut_ptr();
PtrMut::copy_nonoverlapping(&v.get_unchecked(i).to_ptr(), &dst_ptr, 1);
hole.dest = v.get_unchecked_mut(i).to_mut_ptr();
}
}
}
}
fn shift_tail<E, T, F>(v: &mut E, is_less: &mut F)
where
E: EntwineCore<ElementCtor = T> + ?Sized,
T: ElementCtor,
F: FnMut(&T::Ref<'_>, &T::Ref<'_>) -> bool,
{
let len = v.len();
unsafe {
if len >= 2 && is_less(&v.get_unchecked(len - 1), &v.get_unchecked(len - 2)) {
let mut tmp = mem::ManuallyDrop::new(Ptr::read(&v.get_unchecked(len - 1).to_ptr()));
let mut hole = CopyOnDrop::<T> {
src: (&mut *tmp).as_mut().to_mut_ptr(),
dest: v.get_unchecked_mut(len - 2).to_mut_ptr(),
};
let dst_ptr = v.get_unchecked_mut(len - 1).to_mut_ptr();
PtrMut::copy_nonoverlapping(&v.get_unchecked(len - 2).to_ptr(), &dst_ptr, 1);
for i in (0..len - 2).rev() {
if !is_less(&(&*tmp).as_ref(), &v.get_unchecked(i)) {
break;
}
let dst_ptr = v.get_unchecked_mut(i + 1).to_mut_ptr();
PtrMut::copy_nonoverlapping(&v.get_unchecked(i).to_ptr(), &dst_ptr, 1);
hole.dest = v.get_unchecked_mut(i).to_mut_ptr();
}
}
}
}
#[cold]
fn partial_insertion_sort<E, T, F>(v: &mut E, is_less: &mut F) -> bool
where
E: EntwineCore<ElementCtor = T> + ?Sized,
T: ElementCtor,
F: FnMut(&T::Ref<'_>, &T::Ref<'_>) -> bool,
{
const MAX_STEPS: usize = 5;
const SHORTEST_SHIFTING: usize = 50;
let len = v.len();
let mut i = 1;
for _ in 0..MAX_STEPS {
unsafe {
while i < len && !is_less(&v.get_unchecked(i), &v.get_unchecked(i - 1)) {
i += 1;
}
}
if i == len {
return true;
}
if len < SHORTEST_SHIFTING {
return false;
}
v.swap(i - 1, i);
shift_tail(&mut v.range_mut(..i).unwrap(), is_less);
shift_head(&mut v.range_mut(i..).unwrap(), is_less);
}
false
}
fn insertion_sort<E, T, F>(v: &mut E, is_less: &mut F)
where
E: EntwineCore<ElementCtor = T> + ?Sized,
T: ElementCtor,
F: FnMut(&T::Ref<'_>, &T::Ref<'_>) -> bool,
{
for i in 1..v.len() {
shift_tail(&mut v.range_mut(..i + 1).unwrap(), is_less);
}
}
#[cold]
fn heapsort<E, T, F>(v: &mut E, mut is_less: F)
where
E: EntwineCore<ElementCtor = T> + ?Sized,
T: ElementCtor,
F: FnMut(&T::Ref<'_>, &T::Ref<'_>) -> bool,
{
fn sift_down<E, T, F>(v: &mut E, mut node: usize, mut is_less: F)
where
E: EntwineCore<ElementCtor = T> + ?Sized,
T: ElementCtor,
F: FnMut(&T::Ref<'_>, &T::Ref<'_>) -> bool,
{
loop {
unsafe {
let left = 2 * node + 1;
let right = 2 * node + 2;
let greater = if right < v.len()
&& is_less(&v.get_unchecked(left), &v.get_unchecked(right))
{
right
} else {
left
};
if greater >= v.len() || !is_less(&v.get_unchecked(node), &v.get_unchecked(greater))
{
break;
}
v.swap(node, greater);
node = greater;
}
}
}
for i in (0..v.len() / 2).rev() {
sift_down(v, i, &mut is_less);
}
for i in (1..v.len()).rev() {
v.swap(0, i);
sift_down(&mut v.range_mut(..i).unwrap(), 0, &mut is_less);
}
}
fn partition_in_blocks<E, T, F>(v: &mut E, pivot: &T::Ref<'_>, is_less: &mut F) -> usize
where
E: EntwineCore<ElementCtor = T> + ?Sized,
T: ElementCtor,
F: FnMut(&T::Ref<'_>, &T::Ref<'_>) -> bool,
{
const BLOCK: usize = 128;
let mut l = v.as_mut_ptr();
let mut block_l = BLOCK;
let mut start_l = PtrMut::null_mut();
let mut end_l = PtrMut::null_mut();
let mut offsets_l = [MaybeUninit::<u8>::uninit(); BLOCK];
let mut r = unsafe { l.add(v.len()) };
let mut block_r = BLOCK;
let mut start_r = PtrMut::null_mut();
let mut end_r = PtrMut::null_mut();
let mut offsets_r = [MaybeUninit::<u8>::uninit(); BLOCK];
loop {
let is_done = PtrMut::width(&l, &r) <= 2 * BLOCK;
if is_done {
let mut rem = PtrMut::width(&l, &r);
if start_l < end_l || start_r < end_r {
rem -= BLOCK;
}
if start_l < end_l {
block_r = rem;
} else if start_r < end_r {
block_l = rem;
} else {
block_l = rem / 2;
block_r = rem - block_l;
}
debug_assert!(block_l <= BLOCK && block_r <= BLOCK);
debug_assert!(PtrMut::width(&l, &r) == block_l + block_r);
}
if start_l == end_l {
start_l = offsets_l.as_mut_ptr() as *mut u8;
end_l = offsets_l.as_mut_ptr() as *mut u8;
let mut elem = l.clone();
for i in 0..block_l {
unsafe {
*end_l = i as u8;
end_l = end_l.offset(!is_less(&elem.deref_mut().to_ref(), pivot) as isize);
elem = elem.offset(1);
}
}
}
if start_r == end_r {
start_r = offsets_r.as_mut_ptr() as *mut u8;
end_r = offsets_r.as_mut_ptr() as *mut u8;
let mut elem = r.clone();
for i in 0..block_r {
unsafe {
elem = elem.offset(-1);
*end_r = i as u8;
end_r = end_r.offset(is_less(&elem.deref_mut().to_ref(), pivot) as isize);
}
}
}
let count = cmp::min(
PtrMut::width(&start_l, &end_l),
PtrMut::width(&start_r, &end_r),
);
if count > 0 {
macro_rules! left {
() => {
l.offset(*start_l as isize)
};
}
macro_rules! right {
() => {
r.offset(-(*start_r as isize) - 1)
};
}
unsafe {
let tmp = Ptr::read(&left!().to_const());
PtrMut::copy_nonoverlapping(&right!().to_const(), &left!(), 1);
for _ in 1..count {
start_l = start_l.offset(1);
PtrMut::copy_nonoverlapping(&left!().to_const(), &right!(), 1);
start_r = start_r.offset(1);
PtrMut::copy_nonoverlapping(&right!().to_const(), &left!(), 1);
}
PtrMut::copy_nonoverlapping(&tmp.as_ref().to_ptr(), &right!(), 1);
mem::forget(tmp);
start_l = start_l.offset(1);
start_r = start_r.offset(1);
}
}
if start_l == end_l {
l = unsafe { l.add(block_l) };
}
if start_r == end_r {
r = unsafe { r.offset(-(block_r as isize)) };
}
if is_done {
break;
}
}
if start_l < end_l {
debug_assert_eq!(PtrMut::width(&l, &r), block_l);
while start_l < end_l {
unsafe {
end_l = end_l.offset(-1);
PtrMut::swap(&l.offset(*end_l as isize), &r.offset(-1));
r = r.offset(-1);
}
}
PtrMut::width(&v.as_mut_ptr(), &r)
} else if start_r < end_r {
debug_assert_eq!(PtrMut::width(&l, &r), block_r);
while start_r < end_r {
unsafe {
end_r = end_r.offset(-1);
PtrMut::swap(&l, &r.offset(-(*end_r as isize) - 1));
l = l.offset(1);
}
}
PtrMut::width(&v.as_mut_ptr(), &l)
} else {
PtrMut::width(&v.as_mut_ptr(), &l)
}
}
fn partition<E, T, F>(v: &mut E, pivot: usize, is_less: &mut F) -> (usize, bool)
where
E: EntwineCore<ElementCtor = T> + ?Sized,
T: ElementCtor,
F: FnMut(&T::Ref<'_>, &T::Ref<'_>) -> bool,
{
let (mid, was_partitioned) = {
v.swap(0, pivot);
let (mut pivot, mut v) = v.split_at_mut(1);
let mut pivot = pivot.get_mut(0).unwrap();
let mut tmp = mem::ManuallyDrop::new(unsafe { Ptr::read(&pivot.to_mut_ptr().to_const()) });
let _pivot_guard = CopyOnDrop::<T> {
src: (&mut *tmp).as_mut().to_mut_ptr(),
dest: pivot.to_mut_ptr(),
};
let pivot = (&*tmp).as_ref();
let mut l = 0;
let mut r = v.len();
unsafe {
while l < r && is_less(&v.get_unchecked(l), &pivot) {
l += 1;
}
while l < r && !is_less(&v.get_unchecked(r - 1), &pivot) {
r -= 1;
}
}
let mut range_mut = v.range_mut(l..r).unwrap();
(
l + partition_in_blocks(&mut range_mut, &pivot, is_less),
l >= r,
)
};
v.swap(0, mid);
(mid, was_partitioned)
}
fn partition_equal<E, T, F>(v: &mut E, pivot: usize, is_less: &mut F) -> usize
where
E: EntwineCore<ElementCtor = T> + ?Sized,
T: ElementCtor,
F: FnMut(&T::Ref<'_>, &T::Ref<'_>) -> bool,
{
v.swap(0, pivot);
let (mut pivot, mut v) = v.split_at_mut(1);
let mut pivot = pivot.get_mut(0).unwrap();
let mut tmp = mem::ManuallyDrop::new(unsafe { Ptr::read(&pivot.to_ref().to_ptr()) });
let _pivot_guard = CopyOnDrop::<T> {
src: (&mut *tmp).as_mut().to_mut_ptr(),
dest: pivot.to_mut_ptr(),
};
let pivot = (&*tmp).as_ref();
let mut l = 0;
let mut r = v.len();
loop {
unsafe {
while l < r && !is_less(&pivot, &v.get_unchecked(l)) {
l += 1;
}
while l < r && is_less(&pivot, &v.get_unchecked(r - 1)) {
r -= 1;
}
if l >= r {
break;
}
r -= 1;
let l_ptr = v.get_unchecked_mut(l).to_mut_ptr();
let r_ptr = v.get_unchecked_mut(r).to_mut_ptr();
PtrMut::swap(&l_ptr, &r_ptr);
l += 1;
}
}
l + 1
}
#[cold]
fn break_patterns<E, T>(v: &mut E)
where
E: EntwineCore<ElementCtor = T> + ?Sized,
T: ElementCtor,
{
let len = v.len();
if len >= 8 {
let mut random = len as u32;
let mut gen_u32 = || {
random ^= random << 13;
random ^= random >> 17;
random ^= random << 5;
random
};
let mut gen_usize = || {
if usize::BITS <= 32 {
gen_u32() as usize
} else {
(((gen_u32() as u64) << 32) | (gen_u32() as u64)) as usize
}
};
let modulus = len.next_power_of_two();
let pos = len / 4 * 2;
for i in 0..3 {
let mut other = gen_usize() & (modulus - 1);
if other >= len {
other -= len;
}
v.swap(pos - 1 + i, other);
}
}
}
fn choose_pivot<E, T, F>(v: &mut E, is_less: &mut F) -> (usize, bool)
where
E: EntwineCore<ElementCtor = T> + ?Sized,
T: ElementCtor,
F: FnMut(&T::Ref<'_>, &T::Ref<'_>) -> bool,
{
const SHORTEST_MEDIAN_OF_MEDIANS: usize = 50;
const MAX_SWAPS: usize = 4 * 3;
let len = v.len();
let mut a = len / 4;
let mut b = len / 4 * 2;
let mut c = len / 4 * 3;
let mut swaps = 0;
if len >= 8 {
let mut sort2 = |a: &mut usize, b: &mut usize| unsafe {
if is_less(&v.get_unchecked(*b), &v.get_unchecked(*a)) {
ptr::swap(a, b);
swaps += 1;
}
};
let mut sort3 = |a: &mut usize, b: &mut usize, c: &mut usize| {
sort2(a, b);
sort2(b, c);
sort2(a, b);
};
if len >= SHORTEST_MEDIAN_OF_MEDIANS {
let mut sort_adjacent = |a: &mut usize| {
let tmp = *a;
sort3(&mut (tmp - 1), a, &mut (tmp + 1));
};
sort_adjacent(&mut a);
sort_adjacent(&mut b);
sort_adjacent(&mut c);
}
sort3(&mut a, &mut b, &mut c);
}
if swaps < MAX_SWAPS {
(b, swaps == 0)
} else {
v.reverse();
(len - 1 - b, true)
}
}
fn recurse<'a, E, T, F>(
v: &'a mut E,
is_less: &mut F,
pred: Option<RefT<'a, E>>,
mut limit: u32,
mut was_balanced: bool,
mut was_partitioned: bool,
) where
E: EntwineCore<ElementCtor = T> + ?Sized,
T: ElementCtor,
F: FnMut(&T::Ref<'_>, &T::Ref<'_>) -> bool,
{
const MAX_INSERTION: usize = 20;
let len = v.len();
if len <= MAX_INSERTION {
insertion_sort(v, is_less);
return;
}
if limit == 0 {
heapsort(v, is_less);
return;
}
if !was_balanced {
break_patterns(v);
limit -= 1;
}
let (pivot, likely_sorted) = choose_pivot(v, is_less);
if was_balanced && was_partitioned && likely_sorted {
if partial_insertion_sort(v, is_less) {
return;
}
}
if let Some(p) = pred.as_ref() {
if !is_less(p, &v.get(pivot).unwrap()) {
let mid = partition_equal(v, pivot, is_less);
let mut v = v.range_mut(mid..).unwrap();
return recurse(
&mut v,
is_less,
pred.map(Ref::reborrow),
limit,
was_balanced,
was_partitioned,
);
}
}
let (mid, was_p) = partition(v, pivot, is_less);
was_balanced = cmp::min(mid, len - mid) >= len / 8;
was_partitioned = was_p;
let (mut left, mut right) = v.split_at_mut(mid);
let (pivot, mut right) = right.split_at_mut(1);
let pivot = pivot.get(0).unwrap();
if left.len() < right.len() {
recurse(
&mut left,
is_less,
pred.map(Ref::reborrow),
limit,
was_balanced,
was_partitioned,
);
recurse(
&mut right,
is_less,
Some(pivot),
limit,
was_balanced,
was_partitioned,
)
} else {
recurse(
&mut right,
is_less,
Some(pivot),
limit,
was_balanced,
was_partitioned,
);
recurse(
&mut left,
is_less,
pred.map(Ref::reborrow),
limit,
was_balanced,
was_partitioned,
)
}
}
pub fn quicksort<E, T, F>(v: &mut E, mut is_less: F)
where
E: EntwineCore<ElementCtor = T> + ?Sized,
T: ElementCtor,
F: FnMut(&T::Ref<'_>, &T::Ref<'_>) -> bool,
{
if <E::ElementCtor as ElementCtor>::Value::is_zero_sized() {
return;
}
let limit = usize::BITS - v.len().leading_zeros();
recurse(v, &mut is_less, None, limit, true, true);
}