impl c#'s unstable sort & refactor sort things
This commit is contained in:
+1
-2
@@ -1,7 +1,6 @@
|
||||
pub mod float_ext;
|
||||
pub mod legacy_sort;
|
||||
pub mod limited_queue;
|
||||
pub mod map_or_attrs;
|
||||
pub mod mods;
|
||||
pub mod random;
|
||||
pub mod tandem_sort;
|
||||
pub mod sort;
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
/// C#'s unstable sorting algorithm.
|
||||
///
|
||||
/// <https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,fc1bc8c930f6c93c,references>
|
||||
pub fn sort<T, F>(keys: &mut [T], cmp: F)
|
||||
where
|
||||
F: Fn(&T, &T) -> Ordering,
|
||||
{
|
||||
introspective_sort(keys, 0, keys.len(), &cmp);
|
||||
}
|
||||
|
||||
fn introspective_sort<T, F>(keys: &mut [T], left: usize, len: usize, cmp: &F)
|
||||
where
|
||||
F: Fn(&T, &T) -> Ordering,
|
||||
{
|
||||
if len >= 2 {
|
||||
intro_sort(keys, left, len + left - 1, 2 * keys.len().ilog2(), cmp);
|
||||
}
|
||||
}
|
||||
|
||||
fn intro_sort<T, F>(keys: &mut [T], lo: usize, mut hi: usize, mut depth_limit: u32, cmp: &F)
|
||||
where
|
||||
F: Fn(&T, &T) -> Ordering,
|
||||
{
|
||||
const INTRO_SORT_SIZE_THRESHOLD: usize = 16;
|
||||
|
||||
while hi > lo {
|
||||
let partition_size = hi - lo + 1;
|
||||
|
||||
if partition_size <= INTRO_SORT_SIZE_THRESHOLD {
|
||||
match partition_size {
|
||||
1 => {}
|
||||
2 => super::swap_if_greater(keys, cmp, lo, hi),
|
||||
3 => {
|
||||
super::swap_if_greater(keys, cmp, lo, hi - 1);
|
||||
super::swap_if_greater(keys, cmp, lo, hi);
|
||||
super::swap_if_greater(keys, cmp, hi - 1, hi);
|
||||
}
|
||||
_ => insertion_sort(keys, lo, hi, cmp),
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if depth_limit == 0 {
|
||||
super::heap_sort(keys, lo, hi, cmp);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
depth_limit -= 1;
|
||||
let p = pick_pivot_and_partition(keys, lo, hi, cmp);
|
||||
intro_sort(keys, p + 1, hi, depth_limit, cmp);
|
||||
hi = p - 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn pick_pivot_and_partition<T, F>(keys: &mut [T], lo: usize, hi: usize, cmp: &F) -> usize
|
||||
where
|
||||
F: Fn(&T, &T) -> Ordering,
|
||||
{
|
||||
let mid = lo + (hi - lo) / 2;
|
||||
super::swap_if_greater(keys, cmp, lo, mid);
|
||||
super::swap_if_greater(keys, cmp, lo, hi);
|
||||
super::swap_if_greater(keys, cmp, mid, hi);
|
||||
super::swap(keys, mid, hi - 1);
|
||||
let mut left = lo;
|
||||
let mut right = hi - 1;
|
||||
|
||||
let mut pivot_idx = right;
|
||||
let mut next_left = true;
|
||||
|
||||
while left < right {
|
||||
while {
|
||||
left += 1;
|
||||
|
||||
cmp(&keys[left], &keys[pivot_idx]).is_lt()
|
||||
} {}
|
||||
|
||||
while {
|
||||
right -= 1;
|
||||
|
||||
cmp(&keys[pivot_idx], &keys[right]).is_lt()
|
||||
} {}
|
||||
|
||||
if left >= right {
|
||||
break;
|
||||
}
|
||||
|
||||
super::swap(keys, left, right);
|
||||
|
||||
if next_left {
|
||||
pivot_idx = left;
|
||||
} else {
|
||||
pivot_idx = right;
|
||||
}
|
||||
|
||||
next_left = !next_left;
|
||||
}
|
||||
|
||||
super::swap(keys, left, hi - 1);
|
||||
|
||||
left
|
||||
}
|
||||
|
||||
fn insertion_sort<T, F>(keys: &mut [T], lo: usize, hi: usize, cmp: F)
|
||||
where
|
||||
F: Fn(&T, &T) -> Ordering,
|
||||
{
|
||||
for i in lo..hi {
|
||||
let target = &keys[i + 1];
|
||||
|
||||
let smaller_idx = keys[lo..=i]
|
||||
.iter()
|
||||
.rposition(|curr| cmp(target, curr).is_ge());
|
||||
|
||||
if let Some(idx) = smaller_idx {
|
||||
keys[lo + idx + 1..=i + 1].rotate_right(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
pub use self::{csharp::sort as csharp, osu_legacy::sort as osu_legacy, tandem::TandemSorter};
|
||||
|
||||
mod csharp;
|
||||
mod osu_legacy;
|
||||
mod tandem;
|
||||
|
||||
fn heap_sort<T, F>(keys: &mut [T], lo: usize, hi: usize, cmp: &F)
|
||||
where
|
||||
F: Fn(&T, &T) -> Ordering,
|
||||
{
|
||||
let n = hi - lo + 1;
|
||||
|
||||
for i in (1..=n / 2).rev() {
|
||||
down_heap(keys, i, n, lo, cmp);
|
||||
}
|
||||
|
||||
for i in (2..=n).rev() {
|
||||
swap(keys, lo, lo + i - 1);
|
||||
down_heap(keys, 1, i - 1, lo, cmp);
|
||||
}
|
||||
}
|
||||
|
||||
fn down_heap<T, F>(keys: &mut [T], mut i: usize, n: usize, lo: usize, cmp: &F)
|
||||
where
|
||||
F: Fn(&T, &T) -> Ordering,
|
||||
{
|
||||
while i <= n / 2 {
|
||||
let mut child = 2 * i;
|
||||
|
||||
if child < n && cmp(&keys[lo + child - 1], &keys[lo + child]).is_lt() {
|
||||
child += 1;
|
||||
}
|
||||
|
||||
if cmp(&keys[lo + i - 1], &keys[lo + child - 1]).is_ge() {
|
||||
break;
|
||||
}
|
||||
|
||||
keys.swap(lo + i - 1, lo + child - 1);
|
||||
i = child;
|
||||
}
|
||||
}
|
||||
|
||||
fn swap_if_greater<T, F>(keys: &mut [T], cmp: &F, a: usize, b: usize)
|
||||
where
|
||||
F: Fn(&T, &T) -> Ordering,
|
||||
{
|
||||
if a != b && cmp(&keys[a], &keys[b]).is_gt() {
|
||||
keys.swap(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
fn swap<T>(keys: &mut [T], i: usize, j: usize) {
|
||||
if i != j {
|
||||
keys.swap(i, j)
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,10 @@ use crate::model::hit_object::HitObject;
|
||||
|
||||
const QUICK_SORT_DEPTH_THRESHOLD: usize = 32;
|
||||
|
||||
/// [Legacy sorting algorithm](https://github.com/ppy/osu/blob/e669e28dc9b6d79d82a36053e6a279de8dafddd1/osu.Game.Rulesets.Mania/MathUtils/LegacySortHelper.cs#L19)
|
||||
pub fn legacy_sort(keys: &mut [HitObject]) {
|
||||
/// osu!'s legacy sorting algorithm.
|
||||
///
|
||||
/// <https://github.com/ppy/osu/blob/e669e28dc9b6d79d82a36053e6a279de8dafddd1/osu.Game.Rulesets.Mania/MathUtils/LegacySortHelper.cs#L19>
|
||||
pub fn sort(keys: &mut [HitObject]) {
|
||||
if keys.len() < 2 {
|
||||
return;
|
||||
}
|
||||
@@ -21,7 +23,7 @@ fn depth_limited_quick_sort(
|
||||
) {
|
||||
loop {
|
||||
if depth_limit == 0 {
|
||||
heap_sort(keys, left, right);
|
||||
super::heap_sort(keys, left, right, &cmp);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -31,17 +33,9 @@ fn depth_limited_quick_sort(
|
||||
|
||||
let mid = i + ((j - i) >> 1);
|
||||
|
||||
if keys[i] > keys[mid] {
|
||||
keys.swap(i, mid);
|
||||
}
|
||||
|
||||
if keys[i] > keys[j] {
|
||||
keys.swap(i, j);
|
||||
}
|
||||
|
||||
if keys[mid] > keys[j] {
|
||||
keys.swap(mid, j);
|
||||
}
|
||||
super::swap_if_greater(keys, &cmp, i, mid);
|
||||
super::swap_if_greater(keys, &cmp, i, j);
|
||||
super::swap_if_greater(keys, &cmp, mid, j);
|
||||
|
||||
loop {
|
||||
while keys[i] < keys[mid] {
|
||||
@@ -88,32 +82,6 @@ fn depth_limited_quick_sort(
|
||||
}
|
||||
}
|
||||
|
||||
fn heap_sort(keys: &mut [HitObject], lo: usize, hi: usize) {
|
||||
let n = hi - lo + 1;
|
||||
|
||||
for i in (1..=n / 2).rev() {
|
||||
down_heap(keys, i, n, lo);
|
||||
}
|
||||
|
||||
for i in (2..=n).rev() {
|
||||
keys.swap(lo, lo + i - 1);
|
||||
down_heap(keys, 1, i - 1, lo);
|
||||
}
|
||||
}
|
||||
|
||||
fn down_heap(keys: &mut [HitObject], mut i: usize, n: usize, lo: usize) {
|
||||
while i <= n / 2 {
|
||||
let mut child = 2 * i;
|
||||
|
||||
if child < n && keys[lo + child - 1] < keys[lo + child] {
|
||||
child += 1;
|
||||
}
|
||||
|
||||
if keys[lo + i - 1] >= keys[lo + child - 1] {
|
||||
break;
|
||||
}
|
||||
|
||||
keys.swap(lo + i - 1, lo + child - 1);
|
||||
i = child;
|
||||
}
|
||||
fn cmp(a: &HitObject, b: &HitObject) -> Ordering {
|
||||
a.start_time.total_cmp(&b.start_time)
|
||||
}
|
||||
@@ -18,6 +18,8 @@ impl TandemSorter {
|
||||
if stable {
|
||||
indices.sort_by(sort_by);
|
||||
} else {
|
||||
// When sorting integers, the order of elements with equal values
|
||||
// does not matter so we can use rust's sort instead of C#'s.
|
||||
indices.sort_unstable_by(sort_by);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user