#[cfg(test)]
mod tests;
use crate::find_run::get_run;
use crate::insort;
use crate::merge::merge;
use crate::Comparator;
use alloc::vec::Vec;
use core::cmp::min;
const MIN_MERGE: usize = 64;
fn calc_min_merge(mut len: usize) -> usize {
if len < MIN_MERGE {
len
} else {
let mut r: usize = 0;
while len >= MIN_MERGE {
r |= len & 1;
len >>= 1;
}
len + r
}
}
#[derive(Copy, Clone, Debug)]
struct Run {
pos: usize,
len: usize,
}
struct SortState<'a, T, C: Comparator<T>> {
list: &'a mut [T],
cmp: &'a C,
runs: Vec<Run>,
pos: usize,
}
impl<'a, T, C: Comparator<T>> SortState<'a, T, C> {
#[inline]
fn new(list: &'a mut [T], cmp: &'a C) -> SortState<'a, T, C> {
SortState {
list,
cmp,
runs: Vec::new(),
pos: 0,
}
}
fn sort(&mut self) -> Result<(), C::Error> {
let list_len = self.list.len();
let min_run = calc_min_merge(list_len);
while self.pos < list_len {
let pos = self.pos;
let mut run_len = get_run(&mut self.list[pos..], self.cmp)?;
let run_min_len = min(min_run, list_len - pos);
if run_len < run_min_len {
run_len = run_min_len;
let l = &mut self.list[pos..][..run_len];
insort::sort(l, self.cmp)?;
}
self.runs.push(Run { pos, len: run_len });
self.pos += run_len;
self.merge_collapse()?;
}
self.merge_force_collapse()?;
Ok(())
}
fn merge_collapse(&mut self) -> Result<(), C::Error> {
let runs = &mut self.runs;
while runs.len() > 1 {
let l = runs.len();
if (l >= 3 && runs[l - 1].len <= runs[l - 2].len + runs[l - 1].len)
|| (l >= 4 && runs[l - 4].len <= runs[l - 2].len + runs[l - 3].len)
{
let (pos1, pos2) = if runs[l - 3].len < runs[l - 1].len {
(l - 3, l - 2)
} else {
(l - 2, l - 1)
};
let (run1, run2) = (runs[pos1], runs[pos2]);
debug_assert_eq!(run1.pos + run1.len, run2.pos);
runs.remove(pos2);
runs[pos1] = Run {
pos: run1.pos,
len: run1.len + run2.len,
};
let l = &mut self.list[run1.pos..][..run1.len + run2.len];
merge(l, run1.len, self.cmp)?;
} else {
break; }
}
Ok(())
}
fn merge_force_collapse(&mut self) -> Result<(), C::Error> {
let runs = &mut self.runs;
while runs.len() > 1 {
let (mut pos1, mut pos2) = (runs.len() - 2, runs.len() - 1);
if runs.len() > 2 && runs[runs.len() - 3].len < runs[runs.len() - 1].len {
pos1 -= 1;
pos2 -= 1;
}
let (run1, run2) = (runs[pos1], runs[pos2]);
debug_assert_eq!(run1.len, run2.pos);
runs.remove(pos2);
runs[pos1] = Run {
pos: run1.pos,
len: run1.len + run2.len,
};
let l = &mut self.list[run1.pos..][..run1.len + run2.len];
merge(l, run1.len, self.cmp)?;
}
Ok(())
}
}
pub(crate) fn try_sort_by<T, C: Comparator<T>>(list: &mut [T], cmp: C) -> Result<(), C::Error> {
if list.len() < MIN_MERGE {
insort::sort(list, &cmp)
} else {
SortState::new(list, &cmp).sort()
}
}