Skip to content

Commit 43fc006

Browse files
committed
Implement partial_sort_unstable for slice
Signed-off-by: tison <wander4096@gmail.com>
1 parent dfe1b8c commit 43fc006

File tree

2 files changed

+259
-1
lines changed

2 files changed

+259
-1
lines changed

library/core/src/slice/mod.rs

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3242,6 +3242,213 @@ impl<T> [T] {
32423242
sort::unstable::sort(self, &mut |a, b| f(a).lt(&f(b)));
32433243
}
32443244

3245+
/// Partially sorts the slice in ascending order **without** preserving the initial order of equal elements.
3246+
///
3247+
/// Upon completion, for the specified range `start..end`, it's guaranteed that:
3248+
///
3249+
/// 1. Every element in `self[..start]` is smaller than or equal to
3250+
/// 2. Every element in `self[start..end]`, which is sorted, and smaller than or equal to
3251+
/// 3. Every element in `self[end..]`.
3252+
///
3253+
/// This partial sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not
3254+
/// allocate), and *O*(*n* + *k* \* log(*k*)) worst-case, where *n* is the length of the slice and
3255+
/// *k* is the length of the specified range.
3256+
///
3257+
/// See the documentation of [`sort_unstable`] for implementation notes.
3258+
///
3259+
/// # Panics
3260+
///
3261+
/// May panic if the implementation of [`Ord`] for `T` does not implement a total order, or if
3262+
/// the [`Ord`] implementation panics, or if the specified range is out of bounds.
3263+
///
3264+
/// # Examples
3265+
///
3266+
/// ```
3267+
/// #![feature(slice_partial_sort_unstable)]
3268+
///
3269+
/// let mut v = [4, -5, 1, -3, 2];
3270+
///
3271+
/// // empty range at the beginning, nothing changed
3272+
/// v.partial_sort_unstable(0..0);
3273+
/// assert_eq!(v, [4, -5, 1, -3, 2]);
3274+
///
3275+
/// // empty range in the middle, partitioning the slice
3276+
/// v.partial_sort_unstable(2..2);
3277+
/// for i in 0..2 {
3278+
/// assert!(v[i] <= v[2]);
3279+
/// }
3280+
/// for i in 3..v.len() {
3281+
/// assert!(v[2] <= v[i]);
3282+
/// }
3283+
///
3284+
/// // single element range, same as select_nth_unstable
3285+
/// v.partial_sort_unstable(2..3);
3286+
/// for i in 0..2 {
3287+
/// assert!(v[i] <= v[2]);
3288+
/// }
3289+
/// for i in 3..v.len() {
3290+
/// assert!(v[2] <= v[i]);
3291+
/// }
3292+
///
3293+
/// // partial sort a subrange
3294+
/// v.partial_sort_unstable(1..4);
3295+
/// assert_eq!(&v[1..4], [-3, 1, 2]);
3296+
///
3297+
/// // partial sort the whole range, same as sort_unstable
3298+
/// v.partial_sort_unstable(..);
3299+
/// assert_eq!(v, [-5, -3, 1, 2, 4]);
3300+
/// ```
3301+
///
3302+
/// [`sort_unstable`]: slice::sort_unstable
3303+
#[unstable(feature = "slice_partial_sort_unstable", issue = "149046")]
3304+
#[inline]
3305+
pub fn partial_sort_unstable<R>(&mut self, range: R)
3306+
where
3307+
T: Ord,
3308+
R: RangeBounds<usize>,
3309+
{
3310+
sort::unstable::partial_sort(self, range, T::lt);
3311+
}
3312+
3313+
/// Partially sorts the slice in ascending order with a comparison function, **without**
3314+
/// preserving the initial order of equal elements.
3315+
///
3316+
/// Upon completion, for the specified range `start..end`, it's guaranteed that:
3317+
///
3318+
/// 1. Every element in `self[..start]` is smaller than or equal to
3319+
/// 2. Every element in `self[start..end]`, which is sorted, and smaller than or equal to
3320+
/// 3. Every element in `self[end..]`.
3321+
///
3322+
/// This partial sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not
3323+
/// allocate), and *O*(*n* + *k* \* log(*k*)) worst-case, where *n* is the length of the slice and
3324+
/// *k* is the length of the specified range.
3325+
///
3326+
/// See the documentation of [`sort_unstable_by`] for implementation notes.
3327+
///
3328+
/// # Panics
3329+
///
3330+
/// May panic if the `compare` does not implement a total order, or if
3331+
/// the `compare` itself panics, or if the specified range is out of bounds.
3332+
///
3333+
/// # Examples
3334+
///
3335+
/// ```
3336+
/// #![feature(slice_partial_sort_unstable)]
3337+
///
3338+
/// let mut v = [4, -5, 1, -3, 2];
3339+
///
3340+
/// // empty range at the beginning, nothing changed
3341+
/// v.partial_sort_unstable_by(0..0, |a, b| b.cmp(a));
3342+
/// assert_eq!(v, [4, -5, 1, -3, 2]);
3343+
///
3344+
/// // empty range in the middle, partitioning the slice
3345+
/// v.partial_sort_unstable_by(2..2, |a, b| b.cmp(a));
3346+
/// for i in 0..2 {
3347+
/// assert!(v[i] >= v[2]);
3348+
/// }
3349+
/// for i in 3..v.len() {
3350+
/// assert!(v[2] >= v[i]);
3351+
/// }
3352+
///
3353+
/// // single element range, same as select_nth_unstable
3354+
/// v.partial_sort_unstable_by(2..3, |a, b| b.cmp(a));
3355+
/// for i in 0..2 {
3356+
/// assert!(v[i] >= v[2]);
3357+
/// }
3358+
/// for i in 3..v.len() {
3359+
/// assert!(v[2] >= v[i]);
3360+
/// }
3361+
///
3362+
/// // partial sort a subrange
3363+
/// v.partial_sort_unstable_by(1..4, |a, b| b.cmp(a));
3364+
/// assert_eq!(&v[1..4], [2, 1, -3]);
3365+
///
3366+
/// // partial sort the whole range, same as sort_unstable
3367+
/// v.partial_sort_unstable_by(.., |a, b| b.cmp(a));
3368+
/// assert_eq!(v, [4, 2, 1, -3, -5]);
3369+
/// ```
3370+
///
3371+
/// [`sort_unstable_by`]: slice::sort_unstable_by
3372+
#[unstable(feature = "slice_partial_sort_unstable", issue = "149046")]
3373+
#[inline]
3374+
pub fn partial_sort_unstable_by<F, R>(&mut self, range: R, mut compare: F)
3375+
where
3376+
F: FnMut(&T, &T) -> Ordering,
3377+
R: RangeBounds<usize>,
3378+
{
3379+
sort::unstable::partial_sort(self, range, |a, b| compare(a, b) == Less);
3380+
}
3381+
3382+
/// Partially sorts the slice in ascending order with a key extraction function, **without**
3383+
/// preserving the initial order of equal elements.
3384+
///
3385+
/// Upon completion, for the specified range `start..end`, it's guaranteed that:
3386+
///
3387+
/// 1. Every element in `self[..start]` is smaller than or equal to
3388+
/// 2. Every element in `self[start..end]`, which is sorted, and smaller than or equal to
3389+
/// 3. Every element in `self[end..]`.
3390+
///
3391+
/// This partial sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not
3392+
/// allocate), and *O*(*n* + *k* \* log(*k*)) worst-case, where *n* is the length of the slice
3393+
/// and *k* is the length of the specified range.
3394+
///
3395+
/// See the documentation of [`sort_unstable_by_key`] for implementation notes.
3396+
///
3397+
/// # Panics
3398+
///
3399+
/// May panic if the implementation of [`Ord`] for `K` does not implement a total order, or if
3400+
/// the [`Ord`] implementation panics, or if the specified range is out of bounds.
3401+
///
3402+
/// # Examples
3403+
///
3404+
/// ```
3405+
/// #![feature(slice_partial_sort_unstable)]
3406+
///
3407+
/// let mut v = [4i32, -5, 1, -3, 2];
3408+
///
3409+
/// // empty range at the beginning, nothing changed
3410+
/// v.partial_sort_unstable_by_key(0..0, |k| k.abs());
3411+
/// assert_eq!(v, [4, -5, 1, -3, 2]);
3412+
///
3413+
/// // empty range in the middle, partitioning the slice
3414+
/// v.partial_sort_unstable_by_key(2..2, |k| k.abs());
3415+
/// for i in 0..2 {
3416+
/// assert!(v[i].abs() <= v[2].abs());
3417+
/// }
3418+
/// for i in 3..v.len() {
3419+
/// assert!(v[2].abs() <= v[i].abs());
3420+
/// }
3421+
///
3422+
/// // single element range, same as select_nth_unstable
3423+
/// v.partial_sort_unstable_by_key(2..3, |k| k.abs());
3424+
/// for i in 0..2 {
3425+
/// assert!(v[i].abs() <= v[2].abs());
3426+
/// }
3427+
/// for i in 3..v.len() {
3428+
/// assert!(v[2].abs() <= v[i].abs());
3429+
/// }
3430+
///
3431+
/// // partial sort a subrange
3432+
/// v.partial_sort_unstable_by_key(1..4, |k| k.abs());
3433+
/// assert_eq!(&v[1..4], [2, -3, 4]);
3434+
///
3435+
/// // partial sort the whole range, same as sort_unstable
3436+
/// v.partial_sort_unstable_by_key(.., |k| k.abs());
3437+
/// assert_eq!(v, [1, 2, -3, 4, -5]);
3438+
/// ```
3439+
///
3440+
/// [`sort_unstable_by_key`]: slice::sort_unstable_by_key
3441+
#[unstable(feature = "slice_partial_sort_unstable", issue = "149046")]
3442+
#[inline]
3443+
pub fn partial_sort_unstable_by_key<K, F, R>(&mut self, range: R, mut f: F)
3444+
where
3445+
F: FnMut(&T) -> K,
3446+
K: Ord,
3447+
R: RangeBounds<usize>,
3448+
{
3449+
sort::unstable::partial_sort(self, range, |a, b| f(a).lt(&f(b)));
3450+
}
3451+
32453452
/// Reorders the slice such that the element at `index` is at a sort-order position. All
32463453
/// elements before `index` will be `<=` to this value, and all elements after will be `>=` to
32473454
/// it.

library/core/src/slice/sort/unstable/mod.rs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
//! This module contains the entry points for `slice::sort_unstable`.
22
33
use crate::mem::SizedTypeProperties;
4+
use crate::ops::{Range, RangeBounds};
5+
use crate::slice::sort::select::partition_at_index;
46
#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))]
57
use crate::slice::sort::shared::find_existing_run;
68
#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))]
79
use crate::slice::sort::shared::smallsort::insertion_sort_shift_left;
10+
use crate::slice::{self};
811
use crate::{cfg_select, intrinsics};
912

1013
pub(crate) mod heapsort;
@@ -17,7 +20,10 @@ pub(crate) mod quicksort;
1720
/// Upholds all safety properties outlined here:
1821
/// <https://github.com/Voultapher/sort-research-rs/blob/main/writeup/sort_safety/text.md>
1922
#[inline(always)]
20-
pub fn sort<T, F: FnMut(&T, &T) -> bool>(v: &mut [T], is_less: &mut F) {
23+
pub fn sort<T, F>(v: &mut [T], is_less: &mut F)
24+
where
25+
F: FnMut(&T, &T) -> bool,
26+
{
2127
// Arrays of zero-sized types are always all-equal, and thus sorted.
2228
if T::IS_ZST {
2329
return;
@@ -52,6 +58,51 @@ pub fn sort<T, F: FnMut(&T, &T) -> bool>(v: &mut [T], is_less: &mut F) {
5258
}
5359
}
5460

61+
/// Unstable partial sort the range `start..end`, after which it's guaranteed that:
62+
///
63+
/// 1. Every element in `v[..start]` is smaller than or equal to
64+
/// 2. Every element in `v[start..end]`, which is sorted, and smaller than or equal to
65+
/// 3. Every element in `v[end..]`.
66+
#[inline(always)]
67+
pub fn partial_sort<T, F, R>(v: &mut [T], range: R, mut is_less: F)
68+
where
69+
F: FnMut(&T, &T) -> bool,
70+
R: RangeBounds<usize>,
71+
{
72+
// Arrays of zero-sized types are always all-equal, and thus sorted.
73+
if T::IS_ZST {
74+
return;
75+
}
76+
77+
let len = v.len();
78+
let Range { start, end } = slice::range(range, ..len);
79+
80+
if end - start <= 1 {
81+
// Empty range or single element. This case can be resolved in at most
82+
// single partition_at_index call, without further sorting.
83+
84+
if start != len && end != 0 {
85+
partition_at_index(v, start, &mut is_less);
86+
} else {
87+
// Do nothing if it is an empty range at start or end: all guarantees
88+
// are already upheld.
89+
}
90+
91+
return;
92+
}
93+
94+
// Avoid partitioning the slice when it eliminates only a few elements to sort.
95+
// The threshold of 8 elements was determined empirically.
96+
let mut v = v;
97+
if end + 8 <= len {
98+
v = partition_at_index(v, end - 1, &mut is_less).0;
99+
}
100+
if start >= 8 {
101+
v = partition_at_index(v, start, &mut is_less).2;
102+
}
103+
sort(v, &mut is_less);
104+
}
105+
55106
/// See [`sort`]
56107
///
57108
/// Deliberately don't inline the main sorting routine entrypoint to ensure the

0 commit comments

Comments
 (0)