use std::cmp::Ordering;
fn ranked(count: usize, compare: &mut dyn FnMut(usize, usize) -> Ordering) -> Vec<usize> {
let mut order: Vec<usize> = (0..count).collect();
order.sort_unstable_by(|a, b| compare(*a, *b).then(a.cmp(b)));
order
}
pub(crate) fn by<T>(items: &mut [T], mut compare: impl FnMut(&T, &T) -> Ordering) {
let order = ranked(items.len(), &mut |a, b| compare(&items[a], &items[b]));
let mut done = vec![false; items.len()];
for start in 0..items.len() {
if done[start] {
continue;
}
let mut at = start;
while order[at] != start {
items.swap(at, order[at]);
done[at] = true;
at = order[at];
}
done[at] = true;
}
}
pub(crate) fn by_key<T, K: Ord>(items: &mut [T], mut key: impl FnMut(&T) -> K) {
by(items, |a, b| key(a).cmp(&key(b)));
}
#[cfg(test)]
mod tests {
#[test]
fn it_sorts_like_the_standard_stable_sort() {
let mut state = 12_345_u64;
let mut next = || {
state = state
.wrapping_mul(6_364_136_223_846_793_005)
.wrapping_add(1);
(state >> 33) as usize
};
for length in [0, 1, 2, 3, 10, 100, 500] {
let items: Vec<(usize, usize)> = (0..length).map(|index| (next() % 7, index)).collect();
let mut expected = items.clone();
expected.sort_by_key(|item| item.0);
let mut actual = items.clone();
super::by_key(&mut actual, |item| item.0);
assert_eq!(actual, expected, "length {length}");
let mut descending = items.clone();
super::by(&mut descending, |a, b| b.0.cmp(&a.0));
let mut expected = items;
expected.sort_by_key(|item| std::cmp::Reverse(item.0));
assert_eq!(descending, expected, "length {length}");
}
}
}