use std::cmp::Ordering;
fn med3<T, E, F>(data: &[T], a: usize, b: usize, c: usize, cmp: &mut F) -> Result<usize, E>
where
F: FnMut(&T, &T) -> Result<Ordering, E>,
{
Ok(if cmp(&data[a], &data[b])?.is_lt() {
if cmp(&data[b], &data[c])?.is_lt() {
b
} else if cmp(&data[a], &data[c])?.is_lt() {
c
} else {
a
}
} else if cmp(&data[b], &data[c])?.is_gt() {
b
} else if cmp(&data[a], &data[c])?.is_lt() {
a
} else {
c
})
}
fn swapn<T>(data: &mut [T], a: usize, b: usize, n: usize) {
for i in 0..n {
data.swap(a + i, b + i);
}
}
pub fn qsort<T, E, F>(data: &mut [T], cmp: &mut F) -> Result<(), E>
where
F: FnMut(&T, &T) -> Result<Ordering, E>,
{
let mut a = 0;
let mut n = data.len();
loop {
if n < 7 {
for pm in a + 1..a + n {
let mut pl = pm;
while pl > a && cmp(&data[pl - 1], &data[pl])?.is_gt() {
data.swap(pl, pl - 1);
pl -= 1;
}
}
return Ok(());
}
let mut presorted = true;
for pm in a + 1..a + n {
if cmp(&data[pm - 1], &data[pm])?.is_gt() {
presorted = false;
break;
}
}
if presorted {
return Ok(());
}
let mut pm = a + n / 2;
if n > 7 {
let mut pl = a;
let mut last = a + n - 1;
if n > 40 {
let d = n / 8;
pl = med3(data, pl, pl + d, pl + 2 * d, cmp)?;
pm = med3(data, pm - d, pm, pm + d, cmp)?;
last = med3(data, last - 2 * d, last - d, last, cmp)?;
}
pm = med3(data, pl, pm, last, cmp)?;
}
data.swap(a, pm);
let (mut pa, mut pb) = (a + 1, a + 1);
let (mut pc, mut pd) = (a + n - 1, a + n - 1);
loop {
while pb <= pc {
let r = cmp(&data[pb], &data[a])?;
if r.is_gt() {
break;
}
if r.is_eq() {
data.swap(pa, pb);
pa += 1;
}
pb += 1;
}
while pb <= pc {
let r = cmp(&data[pc], &data[a])?;
if r.is_lt() {
break;
}
if r.is_eq() {
data.swap(pc, pd);
pd -= 1;
}
pc -= 1;
}
if pb > pc {
break;
}
data.swap(pb, pc);
pb += 1;
pc -= 1;
}
let end = a + n;
let d1 = (pa - a).min(pb - pa);
swapn(data, a, pb - d1, d1);
let d1 = (pd - pc).min(end - pd - 1);
swapn(data, pb, end - d1, d1);
let (d1, d2) = (pb - pa, pd - pc);
if d1 <= d2 {
qsort(&mut data[a..a + d1], cmp)?;
a = end - d2;
n = d2;
} else {
qsort(&mut data[end - d2..end], cmp)?;
n = d1;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn by_key(a: &(i32, usize), b: &(i32, usize)) -> Result<Ordering, ()> {
Ok(a.0.cmp(&b.0))
}
#[test]
fn qsort_sorts() {
for n in [0, 1, 5, 7, 8, 40, 41, 200, 1000] {
let mut items: Vec<(i32, usize)> =
(0..n).map(|i| (i32::try_from(i).unwrap() * 7919 % 13, i)).collect();
qsort(&mut items, &mut by_key).unwrap();
assert!(items.windows(2).all(|w| w[0].0 <= w[1].0), "n = {n}");
let mut seen: Vec<usize> = items.iter().map(|i| i.1).collect();
seen.sort_unstable();
assert_eq!(seen, (0..n).collect::<Vec<_>>());
}
}
#[test]
fn same_order_as_postgres() {
let mut items: Vec<(i32, usize)> =
(0..10).map(|i| (i % 2, usize::try_from(i).unwrap())).collect();
qsort(&mut items, &mut by_key).unwrap();
assert_eq!(items.iter().map(|i| i.1).collect::<Vec<_>>(), [2, 6, 4, 8, 0, 9, 1, 3, 5, 7]);
}
}