1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
//! 切片选择
//!
//! 该模块包含 `slice::select_nth_unstable` 的实现。
//! 它使用基于 Orson Peters 的模式失败快速排序的 introselect 算法,发表于: <https://github.com/orlp/pdqsort>
//!
//!
//! 用于 introselect 的回退算法是使用 Tukey's 的 Ninther 进行 pivot 选择的中位数的中位数。
//! 将其用作后备可确保 O(n) 最坏情况下的运行时间具有比使用堆排序作为后备更好的性能。
//!

use crate::cmp;
use crate::mem::{self, SizedTypeProperties};
use crate::slice::sort::{
    break_patterns, choose_pivot, insertion_sort_shift_left, partition, partition_equal,
};

// 对于不超过此长度的切片,将其简单排序可能会更快。
// 定义在模块作用域,因为它被用在多个函数中。
const MAX_INSERTION: usize = 10;

fn partition_at_index_loop<'a, T, F>(
    mut v: &'a mut [T],
    mut index: usize,
    is_less: &mut F,
    mut pred: Option<&'a T>,
) where
    F: FnMut(&T, &T) -> bool,
{
    // 限制迭代次数并回退到快速确定性选择,以确保 O(n) 最坏情况下的运行时间。
    // 此限制需要为常量,因为像在 `sort` 中使用 `ilog2(len)` 会导致 O(n log n) 时间复杂度。
    // 限制的确切值是任意选择的,但对于大多数输入来说,错误的 pivot 选择应该相对较少,因此通常无论如何都不应达到限制。
    //
    //
    //
    let mut limit = 16;

    // 如果最后一个分区合理平衡,则为 true。
    let mut was_balanced = true;

    loop {
        if v.len() <= MAX_INSERTION {
            if v.len() > 1 {
                insertion_sort_shift_left(v, 1, is_less);
            }
            return;
        }

        if limit == 0 {
            median_of_medians(v, is_less, index);
            return;
        }

        // 如果最后一个分区不平衡,请尝试通过改组一些元素来破坏切片中的模式。
        // 希望这次我们选择一个更好的支点。
        if !was_balanced {
            break_patterns(v);
            limit -= 1;
        }

        // 选择一个 pivot
        let (pivot, _) = choose_pivot(v, is_less);

        // 如果选择的枢轴等于前一个枢轴,则它是切片中的最小元素。
        // 将切片划分为等于和大于枢轴的元素。
        // 当切片包含许多重复元素时,通常会遇到这种情况。
        if let Some(p) = pred {
            if !is_less(p, &v[pivot]) {
                let mid = partition_equal(v, pivot, is_less);

                // 如果我们通过了索引,那么我们就很好。
                if mid > index {
                    return;
                }

                // 否则,继续对大于枢轴的元素进行排序。
                v = &mut v[mid..];
                index = index - mid;
                pred = None;
                continue;
            }
        }

        let (mid, _) = partition(v, pivot, is_less);
        was_balanced = cmp::min(mid, v.len() - mid) >= v.len() / 8;

        // 将切片分为 `left`,`pivot` 和 `right`。
        let (left, right) = v.split_at_mut(mid);
        let (pivot, right) = right.split_at_mut(1);
        let pivot = &pivot[0];

        if mid < index {
            v = right;
            index = index - mid - 1;
            pred = Some(pivot);
        } else if mid > index {
            v = left;
        } else {
            // 如果 mid == index,那么我们就完成了,因为 partition() 保证 mid 之后的所有元素都大于或等于 mid。
            //
            return;
        }
    }
}

/// 使用给定比较器函数返回切片中最小元素索引的辅助函数
///
fn min_index<T, F: FnMut(&T, &T) -> bool>(slice: &[T], is_less: &mut F) -> Option<usize> {
    slice
        .iter()
        .enumerate()
        .reduce(|acc, t| if is_less(t.1, acc.1) { t } else { acc })
        .map(|(i, _)| i)
}

/// 使用给定比较器函数返回切片中最大元素索引的辅助函数
///
fn max_index<T, F: FnMut(&T, &T) -> bool>(slice: &[T], is_less: &mut F) -> Option<usize> {
    slice
        .iter()
        .enumerate()
        .reduce(|acc, t| if is_less(acc.1, t.1) { t } else { acc })
        .map(|(i, _)| i)
}

/// 重新排序切片,以使 `index` 处的元素处于其最终排序位置。
pub fn partition_at_index<T, F>(
    v: &mut [T],
    index: usize,
    mut is_less: F,
) -> (&mut [T], &mut T, &mut [T])
where
    F: FnMut(&T, &T) -> bool,
{
    if index >= v.len() {
        panic!("partition_at_index index {} greater than length of slice {}", index, v.len());
    }

    if T::IS_ZST {
        // 零大小类型的排序没有有意义的行为。什么也不做。
    } else if index == v.len() - 1 {
        // 查找最大元素并将其放置在数组的最后一个位置。
        // 我们可以在这里自由使用 `unwrap()`,因为我们知道 v 不能为空。
        let max_idx = max_index(v, &mut is_less).unwrap();
        v.swap(max_idx, index);
    } else if index == 0 {
        // 查找 min 元素并将其放置在数组的第一个位置。
        // 我们可以在这里自由使用 `unwrap()`,因为我们知道 v 不能为空。
        let min_idx = min_index(v, &mut is_less).unwrap();
        v.swap(min_idx, index);
    } else {
        partition_at_index_loop(v, index, &mut is_less, None);
    }

    let (left, right) = v.split_at_mut(index);
    let (pivot, right) = right.split_at_mut(1);
    let pivot = &mut pivot[0];
    (left, pivot, right)
}

/// 选择算法在保证的 O(n) 时间内从切片中选择第 k 个元素。
/// 这本质上是一个使用 Tukey's Ninther 进行 pivot 选择的快速选择
fn median_of_medians<T, F: FnMut(&T, &T) -> bool>(mut v: &mut [T], is_less: &mut F, mut k: usize) {
    // 由于此函数不是公开的,因此永远不应使用越界索引调用它。
    debug_assert!(k < v.len());

    // 如果 T 为 ZST,则 `partition_at_index` 将提前返回。
    debug_assert!(!T::IS_ZST);

    // 我们现在知道 `k < v.len() <= isize::MAX`
    loop {
        if v.len() <= MAX_INSERTION {
            if v.len() > 1 {
                insertion_sort_shift_left(v, 1, is_less);
            }
            return;
        }

        // `median_of_{minima,maxima}` 无法处理 first/last 元素的极端情况,所以我们在这里捕获它们并进行线性搜索。
        //
        if k == v.len() - 1 {
            // 查找最大元素并将其放置在数组的最后一个位置。
            // 我们可以在这里自由使用 `unwrap()`,因为我们知道 v 不能为空。
            let max_idx = max_index(v, is_less).unwrap();
            v.swap(max_idx, k);
            return;
        } else if k == 0 {
            // 查找 min 元素并将其放置在数组的第一个位置。
            // 我们可以在这里自由使用 `unwrap()`,因为我们知道 v 不能为空。
            let min_idx = min_index(v, is_less).unwrap();
            v.swap(min_idx, k);
            return;
        }

        let p = median_of_ninthers(v, is_less);

        if p == k {
            return;
        } else if p > k {
            v = &mut v[..p];
        } else {
            // 由于 `p < k < v.len()`,`p + 1` 不会溢出并且是切片的有效索引。
            //
            v = &mut v[p + 1..];
            k -= p + 1;
        }
    }
}

// 针对 `k` 位于切片中间某处的情况进行了优化。选择尽可能接近切片中值的 pivot。
// 有关该算法如何运行的更多详细信息,请参见论文 <https://drops.dagstuhl.de/opus/volltexte/2017/7612/pdf/LIPIcs-SEA-2017-24.pdf>。
//
fn median_of_ninthers<T, F: FnMut(&T, &T) -> bool>(v: &mut [T], is_less: &mut F) -> usize {
    // 使用 `saturating_mul` 这样乘法就不会在 16 位平台上溢出。
    let frac = if v.len() <= 1024 {
        v.len() / 12
    } else if v.len() <= 128_usize.saturating_mul(1024) {
        v.len() / 64
    } else {
        v.len() / 1024
    };

    let pivot = frac / 2;
    let lo = v.len() / 2 - pivot;
    let hi = frac + lo;
    let gap = (v.len() - 9 * frac) / 4;
    let mut a = lo - 4 * frac - gap;
    let mut b = hi + gap;
    for i in lo..hi {
        ninther(v, is_less, a, i - frac, b, a + 1, i, b + 1, a + 2, i + frac, b + 2);
        a += 3;
        b += 3;
    }

    median_of_medians(&mut v[lo..lo + frac], is_less, pivot);
    partition(v, lo + pivot, is_less).0
}

/// 在索引 a..i 处围绕 9 个元素移动,使得 `v[d]` 包含 9 个元素的中值,其他元素围绕它进行分区。
///
///
fn ninther<T, F: FnMut(&T, &T) -> bool>(
    v: &mut [T],
    is_less: &mut F,
    a: usize,
    mut b: usize,
    c: usize,
    mut d: usize,
    e: usize,
    mut f: usize,
    g: usize,
    mut h: usize,
    i: usize,
) {
    b = median_idx(v, is_less, a, b, c);
    h = median_idx(v, is_less, g, h, i);
    if is_less(&v[h], &v[b]) {
        mem::swap(&mut b, &mut h);
    }
    if is_less(&v[f], &v[d]) {
        mem::swap(&mut d, &mut f);
    }
    if is_less(&v[e], &v[d]) {
        // 没做什么
    } else if is_less(&v[f], &v[e]) {
        d = f;
    } else {
        if is_less(&v[e], &v[b]) {
            v.swap(e, b);
        } else if is_less(&v[h], &v[e]) {
            v.swap(e, h);
        }
        return;
    }
    if is_less(&v[d], &v[b]) {
        d = b;
    } else if is_less(&v[h], &v[d]) {
        d = h;
    }

    v.swap(d, e);
}

/// 返回指向 3 个元素 `v[a]`、`v[b]` 和 `v[c]` 的中值的索引
///
fn median_idx<T, F: FnMut(&T, &T) -> bool>(
    v: &[T],
    is_less: &mut F,
    mut a: usize,
    b: usize,
    mut c: usize,
) -> usize {
    if is_less(&v[c], &v[a]) {
        mem::swap(&mut a, &mut c);
    }
    if is_less(&v[c], &v[b]) {
        return c;
    }
    if is_less(&v[b], &v[a]) {
        return a;
    }
    b
}