use std::cmp::Ordering;
use std::ptr;
const DOUBLE_COMPARISONS: bool = true;
const RECENCY: usize = 8;
const FAST_BACKTRACKING: bool = true;
const EARLY_OUT: bool = true;
const EARLY_OUT_TEST_AT: usize = 4;
const EARLY_OUT_DISORDER_FRACTION: f32 = 0.60;
fn sort_copy_by<T, F>(slice: &mut [T], mut compare: F) -> usize
where
T: Copy,
F: FnMut(&T, &T) -> Ordering,
{
if slice.len() < 2 {
return slice.len();
}
let mut dropped = Vec::new();
let mut num_dropped_in_row = 0;
let mut write = 0; let mut read = 0; let mut iteration = 0;
let ealy_out_stop = slice.len() / EARLY_OUT_TEST_AT;
while read < slice.len() {
iteration += 1;
if EARLY_OUT
&& iteration == ealy_out_stop
&& dropped.len() as f32 > read as f32 * EARLY_OUT_DISORDER_FRACTION
{
for (i, &element) in dropped.iter().enumerate() {
slice[write + i] = element;
}
slice.sort_unstable_by(|a, b| compare(a, b));
return dropped.len() * EARLY_OUT_TEST_AT; }
if write == 0 || compare(&slice[read], &slice[write - 1]) != Ordering::Less {
slice[write] = slice[read];
read += 1;
write += 1;
num_dropped_in_row = 0;
} else {
if DOUBLE_COMPARISONS
&& num_dropped_in_row == 0
&& 2 <= write
&& compare(&slice[read], &slice[write - 2]) != Ordering::Less
{
dropped.push(slice[write - 1]);
slice[write - 1] = slice[read];
read += 1;
continue;
}
if num_dropped_in_row < RECENCY {
dropped.push(slice[read]);
read += 1;
num_dropped_in_row += 1;
} else {
let trunc_to_length = dropped.len() - num_dropped_in_row;
dropped.truncate(trunc_to_length);
read -= num_dropped_in_row;
let mut num_backtracked = 1;
write -= 1;
if FAST_BACKTRACKING {
let max_of_dropped = slice[read..(read + num_dropped_in_row + 1)]
.iter()
.max_by(|a, b| compare(a, b))
.unwrap();
while 1 <= write && compare(max_of_dropped, &slice[write - 1]) == Ordering::Less
{
num_backtracked += 1;
write -= 1;
}
}
dropped.extend_from_slice(&slice[write..(write + num_backtracked)]);
num_dropped_in_row = 0;
}
}
}
let num_dropped = dropped.len();
dropped.sort_unstable_by(|a, b| compare(a, b));
let mut back = slice.len();
while let Some(&last_dropped) = dropped.last() {
while 0 < write && compare(&last_dropped, &slice[write - 1]) == Ordering::Less {
slice[back - 1] = slice[write - 1];
back -= 1;
write -= 1;
}
slice[back - 1] = last_dropped;
back -= 1;
dropped.pop();
}
num_dropped
}
pub fn sort_copy<T: Copy + Ord>(slice: &mut [T]) -> usize {
sort_copy_by(slice, |a, b| a.cmp(b))
}
struct DmSorter<'a, T: 'a> {
slice: &'a mut [T],
dropped: Vec<T>,
write: usize,
}
impl<'a, T> Drop for DmSorter<'a, T> {
fn drop(&mut self) {
if self.dropped.is_empty() {
return;
}
unsafe {
ptr::copy_nonoverlapping(
self.dropped.as_ptr(),
self.slice.as_mut_ptr().add(self.write),
self.dropped.len(),
);
self.dropped.set_len(0);
}
}
}
#[inline(always)]
unsafe fn unsafe_push<T>(vec: &mut Vec<T>, value: &T) {
let old_len = vec.len();
vec.reserve(1);
ptr::copy_nonoverlapping(value, vec.as_mut_ptr().add(old_len), 1);
vec.set_len(old_len + 1);
}
#[inline(always)]
unsafe fn unsafe_copy<T>(slice: &mut [T], source: usize, dest: usize) {
let ptr = slice.as_mut_ptr();
ptr::copy_nonoverlapping(ptr.add(source), ptr.add(dest), 1);
}
fn sort_move_by<T, F>(slice: &mut [T], mut compare: F)
where
F: FnMut(&T, &T) -> Ordering,
{
unsafe {
if slice.len() < 2 {
return;
}
let mut s = DmSorter {
slice,
dropped: Vec::new(),
write: 0,
};
let mut num_dropped_in_row = 0;
let mut read = 0;
let mut iteration = 0;
let ealy_out_stop = s.slice.len() / EARLY_OUT_TEST_AT;
while read < s.slice.len() {
iteration += 1;
if EARLY_OUT
&& iteration == ealy_out_stop
&& s.dropped.len() as f32 > read as f32 * EARLY_OUT_DISORDER_FRACTION
{
ptr::copy_nonoverlapping(
s.dropped.as_ptr(),
&mut s.slice[s.write],
s.dropped.len(),
);
s.dropped.set_len(0);
s.slice.sort_unstable_by(|a, b| compare(a, b));
return;
}
if s.write == 0
|| compare(
s.slice.get_unchecked(read),
s.slice.get_unchecked(s.write - 1),
) != Ordering::Less
{
if read != s.write {
unsafe_copy(s.slice, read, s.write);
}
read += 1;
s.write += 1;
num_dropped_in_row = 0;
} else {
if DOUBLE_COMPARISONS
&& num_dropped_in_row == 0
&& 2 <= s.write
&& compare(
s.slice.get_unchecked(read),
s.slice.get_unchecked(s.write - 2),
) != Ordering::Less
{
unsafe_push(&mut s.dropped, s.slice.get_unchecked(s.write - 1));
unsafe_copy(s.slice, read, s.write - 1);
read += 1;
continue;
}
if num_dropped_in_row < RECENCY {
unsafe_push(&mut s.dropped, s.slice.get_unchecked(read));
read += 1;
num_dropped_in_row += 1;
} else {
let trunc_to_length = s.dropped.len() - num_dropped_in_row;
s.dropped.set_len(trunc_to_length);
read -= num_dropped_in_row;
let mut num_backtracked = 1;
s.write -= 1;
if FAST_BACKTRACKING {
let max_of_dropped = s.slice[read..(read + num_dropped_in_row + 1)]
.iter()
.max_by(|a, b| compare(a, b))
.unwrap();
while 1 <= s.write
&& compare(max_of_dropped, s.slice.get_unchecked(s.write - 1))
== Ordering::Less
{
num_backtracked += 1;
s.write -= 1;
}
}
{
let old_len = s.dropped.len();
s.dropped.reserve(num_backtracked);
ptr::copy_nonoverlapping(
s.slice.as_ptr().add(s.write),
s.dropped.as_mut_ptr().add(old_len),
num_backtracked,
);
s.dropped.set_len(old_len + num_backtracked);
}
num_dropped_in_row = 0;
}
}
}
s.dropped.sort_unstable_by(|a, b| compare(a, b));
let mut back = s.slice.len();
loop {
let old_len = s.dropped.len();
if old_len == 0 {
break;
}
{
let last_dropped = s.dropped.get_unchecked(old_len - 1);
while 0 < s.write
&& compare(last_dropped, s.slice.get_unchecked(s.write - 1)) == Ordering::Less
{
unsafe_copy(s.slice, s.write - 1, back - 1);
back -= 1;
s.write -= 1;
}
ptr::copy_nonoverlapping(last_dropped, s.slice.get_unchecked_mut(back - 1), 1);
}
back -= 1;
s.dropped.set_len(old_len - 1);
}
}
}
pub fn sort_by<T, F>(slice: &mut [T], compare: F)
where
F: FnMut(&T, &T) -> Ordering,
{
sort_move_by(slice, compare);
}
pub fn sort_by_key<T, K, F>(slice: &mut [T], mut key: F)
where
K: Ord,
F: FnMut(&T) -> K,
{
sort_by(slice, |a, b| key(a).cmp(&key(b)));
}
pub fn sort<T: Ord>(slice: &mut [T]) {
sort_move_by(slice, |a, b| a.cmp(b));
}