a/sort/selection.rs
1use core::cmp::Ordering::{self, Equal, Greater, Less};
2use core::hint::select_unpredictable;
3use core::mem;
4
5/// Sorts a slice in place using double-ended selection sort.
6///
7/// Each pass scans the unsorted middle of the slice once, finds both its smallest and its
8/// largest key, moves every element holding the smallest key to the front of that range and
9/// every element holding the largest key to its back. Those elements are then in their final
10/// places, so the next pass scans only what lies between them.
11///
12/// Three refinements keep the algorithm as cheap as selection sort can be:
13///
14/// * **Pairwise min-max scan.** Elements are taken two at a time: the two are compared with
15/// each other first, then only the smaller one is compared with the running minimum and
16/// only the larger one with the running maximum. A range of `m` elements costs
17/// `⌈3m/2⌉ - 2` comparisons per pass instead of the `2m - 2` of two independent scans,
18/// which is the fewest any algorithm can use to find both extremes.
19/// * **Duplicate gathering ("bingo") for free.** Every comparison is a three-way
20/// [`Ord::cmp`], so an element equal to the running minimum or maximum is recognised
21/// without an extra comparison and joins that key's group. A pass therefore retires all
22/// copies of two keys: input with `k` distinct keys finishes in at most `⌈k/2⌉` passes
23/// instead of `⌊n/2⌋`. A pass in which every comparison reports [`Equal`] has proved the whole range
24/// equal and ends the sort, again at no extra cost. An equal pair also skips the
25/// comparison with the maximum whenever its minimum comparison already rules that out.
26/// * **Lazy placement.** A running minimum or maximum that has no duplicate yet is only
27/// remembered by index; elements are moved once a pass is over, or when a second copy of
28/// the current extreme turns up. Input with distinct keys costs at most `n - 1` swaps,
29/// exactly like classic selection sort, and gathering duplicates never costs a comparison.
30///
31/// Which element of a pair is the smaller one is a coin flip on random input, and a branch on
32/// it would be mispredicted every other pair. The scan therefore picks the element to compare
33/// with the minimum, and the one to compare with the maximum, without branching, and only
34/// branches on the rare outcomes that change an extreme: on random input with distinct keys
35/// that is 1.1–1.5 times faster at 64 elements and 1.7–2.5 times from 1,000 up, for integers,
36/// strings and 64-byte arrays alike. Choosing without a branch makes the next two comparisons
37/// wait for the first, which costs more than a predicted branch when comparisons are slow to
38/// start, as for strings in order: so for elements of 9 to 32 bytes the first pass branches
39/// and counts how often a pair goes against the majority order, and later passes branch only
40/// if fewer than one in 16 did. The comparisons are the same either way.
41///
42/// Distinct keys get no benefit from the refinements beyond the pairwise scan, so the worst
43/// case is any input with distinct keys, and on such input the number of comparisons
44/// depends only on `n`: `n(3n - 2)/8` for even `n` and `3(n² - 1)/8` for odd `n`, about 25%
45/// fewer than the `n(n - 1)/2` of classic selection sort.
46///
47/// # Properties
48///
49/// * **Not stable**: equal elements may be reordered.
50/// * **In place**: `O(1)` auxiliary memory, no allocation.
51/// * **Not adaptive to order**: sorted input costs as much as any other with distinct keys.
52/// It is adaptive to duplicates instead: the fewer distinct keys, the fewer passes.
53///
54/// | Case | Comparisons | Swaps |
55/// |-------------------------------|-------------------------------------|----------------|
56/// | Best (all elements equal) | `n - 1` | `0` |
57/// | `k` distinct keys | `O(nk)` | `O(nk)` |
58/// | Worst (all keys distinct) | `n(3n - 2)/8` or `3(n² - 1)/8` | `≤ n - 1` |
59///
60/// Gathering duplicates costs at most `2m` swaps in a pass over `m` elements, so input with
61/// many repeated keys trades the `n - 1` swap bound for far fewer comparisons and passes.
62/// Input that is already in non-decreasing order is never moved at all.
63///
64/// Selection sort is quadratic and is mainly of educational value; prefer
65/// [`slice::sort_unstable`] for real workloads.
66///
67/// # Panics
68///
69/// Does not panic by itself. If the [`Ord`] implementation of `T` panics, the panic is
70/// propagated and `arr` is left in an unspecified order, but it still holds every original
71/// element exactly once: elements are only ever exchanged with [`slice::swap`].
72///
73/// # Examples
74///
75/// ```
76/// use a::sort::selection;
77///
78/// let mut v = [5, 1, 4, 2, 8, 0];
79/// selection(&mut v);
80/// assert_eq!(v, [0, 1, 2, 4, 5, 8]);
81///
82/// // Three distinct keys: the first pass retires every 1 and every 3, the second the 2s.
83/// let mut v = [3, 1, 2, 3, 1, 2, 2, 1];
84/// selection(&mut v);
85/// assert_eq!(v, [1, 1, 1, 2, 2, 2, 3, 3]);
86///
87/// let mut words = vec!["pear", "apple", "fig"];
88/// selection(&mut words);
89/// assert_eq!(words, ["apple", "fig", "pear"]);
90/// ```
91pub fn selection<T: Ord>(arr: &mut [T]) {
92 // Everything before `lo` and from `hi` onwards is already in its final position.
93 let (mut lo, mut hi) = (0, arr.len());
94 // Whether to branch on the order within a pair: decided by the first pass for elements of
95 // 9 to 32 bytes, never otherwise. Passes see the same middle of the slice, minus the
96 // extremes, so the first one tells how predictable the others will be.
97 let size = mem::size_of::<T>();
98 let mut first = size > 8 && size <= 32;
99 let mut branchy = false;
100 while hi - lo > 1 {
101 let mut pass = Pass::new(&mut arr[lo..hi]);
102 if first {
103 let (pairs, flips) = pass.scan::<true, true>();
104 branchy = flips * FLIPS_MAX < pairs;
105 first = false;
106 } else if branchy {
107 pass.scan::<true, false>();
108 } else {
109 pass.scan::<false, false>();
110 }
111 match pass.finish() {
112 Some((front, back)) => {
113 lo += front;
114 hi -= back;
115 }
116 None => return, // every element still unsorted is equal to every other
117 }
118 }
119}
120
121/// The first pass decides to branch on the order within pairs if fewer than one pair in
122/// `FLIPS_MAX` goes against the majority order.
123const FLIPS_MAX: usize = 16;
124
125/// One pass of [`selection`] over the unsorted range `v`.
126///
127/// The scanned prefix `v[..end]` holds the elements equal to the running minimum (the min
128/// group), the elements equal to the running maximum (the max group), and everything else in
129/// no particular order. A group with a single element is only remembered by its index; once
130/// it has two or more, it is kept contiguous:
131///
132/// * the min group occupies `v[..min_len]`, at the front, where it has to end up anyway;
133/// * the max group occupies `v[mb..mb + max_len]`, somewhere after the min group.
134///
135/// While every element scanned so far is equal (`same`), they all belong to the min group and
136/// the max fields are unused.
137struct Pass<'a, T> {
138 v: &'a mut [T],
139 /// Number of elements scanned.
140 end: usize,
141 /// Whether every element scanned so far is equal.
142 same: bool,
143 /// Length of the contiguous min group at the front, or `0` while it is a single element.
144 min_len: usize,
145 /// Index of an element of the min group.
146 mi: usize,
147 /// Length of the contiguous max group, or `0` while it is a single element.
148 max_len: usize,
149 /// Start of the contiguous max group, when there is one.
150 mb: usize,
151 /// Index of an element of the max group.
152 mx: usize,
153 /// Indices of elements still being placed, kept up to date by [`Pass::swap`].
154 held: [usize; 2],
155}
156
157impl<'a, T: Ord> Pass<'a, T> {
158 fn new(v: &'a mut [T]) -> Self {
159 Pass {
160 v,
161 end: 0,
162 same: true,
163 min_len: 0,
164 mi: 0,
165 max_len: 0,
166 mb: 0,
167 mx: 0,
168 held: [0; 2],
169 }
170 }
171
172 /// Scans the whole range, gathering the min and max groups.
173 ///
174 /// With `BRANCHY`, branches on which element of a pair is the smaller one; otherwise
175 /// selects it without a branch. With `COUNT`, returns the number of pairs scanned after
176 /// the first and how many of them went against the majority order.
177 #[inline(always)]
178 fn scan<const BRANCHY: bool, const COUNT: bool>(&mut self) -> (usize, usize) {
179 let len = self.v.len();
180 // An odd length starts from a lone element, an even one from a compared pair, so
181 // the rest of the range always splits into whole pairs.
182 if len % 2 == 1 {
183 self.end = 1;
184 } else {
185 self.end = 2;
186 match self.v[0].cmp(&self.v[1]) {
187 Less => {
188 self.same = false;
189 self.mx = 1;
190 }
191 Greater => {
192 self.same = false;
193 self.mi = 1;
194 }
195 Equal => self.push_min(1),
196 }
197 }
198 let start = self.end;
199 let mut greater = 0;
200 while self.end < len {
201 let x = self.end;
202 self.end += 2;
203 let y = x + 1;
204 // SAFETY: the rest of the range splits into whole pairs, so `y < len`.
205 let order = unsafe { self.v.get_unchecked(x).cmp(self.v.get_unchecked(y)) };
206 if order == Equal {
207 self.pair_equal(x, y);
208 continue;
209 }
210 let less = order == Less;
211 let (s, l) = if BRANCHY {
212 if less {
213 (x, y)
214 } else {
215 if COUNT {
216 greater += 1;
217 }
218 (y, x)
219 }
220 } else {
221 if COUNT {
222 greater += usize::from(!less);
223 }
224 select_unpredictable(less, (x, y), (y, x))
225 };
226 // Before the first unequal pair, the minimum is also the maximum. Both
227 // comparisons are made against the extremes as they were before the pair.
228 let (min, max) = (self.mi, if self.same { self.mi } else { self.mx });
229 // SAFETY: `s` and `l` are `x` and `y`, and `mi` and `mx` always index scanned
230 // elements.
231 let (below, above) = unsafe {
232 (
233 self.v.get_unchecked(s).cmp(self.v.get_unchecked(min)),
234 self.v.get_unchecked(l).cmp(self.v.get_unchecked(max)),
235 )
236 };
237 if below == Greater && above == Less {
238 // Both lie strictly between the extremes: by far the most common case.
239 continue;
240 }
241 self.place(s, l, below, above);
242 }
243 let pairs = (len - start) / 2;
244 (pairs, greater.min(pairs - greater))
245 }
246
247 /// Places an equal pair at `x` and `y` with at most two more comparisons.
248 fn pair_equal(&mut self, x: usize, y: usize) {
249 // Before the first unequal pair, the minimum is also the maximum.
250 let (min, max) = (self.mi, if self.same { self.mi } else { self.mx });
251 // Both elements share one key, so one comparison against the minimum often
252 // settles both of them.
253 self.held[0] = y;
254 let joined_min = match self.v[x].cmp(&self.v[min]) {
255 Less => {
256 self.new_min(x);
257 true
258 }
259 Equal => {
260 self.push_min(x);
261 true
262 }
263 // Greater than the minimum while everything so far is equal, so it is
264 // also greater than the maximum.
265 Greater if self.same => {
266 self.new_max(x);
267 false
268 }
269 Greater => match self.v[x].cmp(&self.v[max]) {
270 Less => return, // both lie strictly between the extremes
271 Equal => {
272 self.push_max(x);
273 false
274 }
275 Greater => {
276 self.new_max(x);
277 false
278 }
279 },
280 };
281 let y = self.held[0];
282 if joined_min {
283 self.push_min(y);
284 } else {
285 self.push_max(y);
286 }
287 }
288
289 /// Places an unequal pair, its smaller element at `s` and its larger at `l`, given how
290 /// they compared with the old minimum (`below`) and the old maximum (`above`): the smaller
291 /// element is placed before the larger one.
292 fn place(&mut self, s: usize, l: usize, below: Ordering, above: Ordering) {
293 self.held[0] = l;
294 match below {
295 Less => self.new_min(s),
296 Equal => self.push_min(s),
297 Greater => {}
298 }
299 let l = self.held[0];
300 match above {
301 Less => {}
302 // `l` equals the old maximum. If that was also the old minimum, `s` has
303 // just undercut it, which turned the old min group into the max group.
304 Equal => self.push_max(l),
305 Greater => self.new_max(l),
306 }
307 }
308
309 /// Makes the element at `src` the new minimum; the old min group becomes ordinary.
310 fn new_min(&mut self, src: usize) {
311 if self.same {
312 // Every element scanned so far was equal, and they are all larger than `src`:
313 // they form the max group now, exactly where they already are.
314 self.same = false;
315 self.max_len = self.min_len;
316 self.mb = 0;
317 self.mx = self.mi;
318 }
319 self.min_len = 0;
320 self.mi = src;
321 }
322
323 /// Makes the element at `src` the new maximum; the old max group becomes ordinary.
324 fn new_max(&mut self, src: usize) {
325 self.same = false;
326 self.max_len = 0;
327 self.mx = src;
328 }
329
330 /// Adds the element at `src`, which equals the minimum, to the min group.
331 fn push_min(&mut self, src: usize) {
332 self.held[1] = src;
333 if self.min_len == 0 {
334 // The minimum's only element so far opens the group at the front of the range.
335 self.make_room_for_min();
336 self.swap(0, self.mi);
337 self.min_len = 1;
338 }
339 self.make_room_for_min();
340 self.swap(self.min_len, self.held[1]);
341 self.min_len += 1;
342 self.mi = 0;
343 }
344
345 /// Frees the slot just after the min group if the max group starts there, by moving the
346 /// max group's first element to just past its end.
347 fn make_room_for_min(&mut self) {
348 // The element being added to the min group belongs to neither group, and it lies
349 // in the scanned prefix but not in front of the max group, so `mb + max_len` is
350 // still inside the scanned prefix.
351 if self.max_len > 0 && self.mb == self.min_len {
352 self.swap(self.mb, self.mb + self.max_len);
353 self.mb += 1;
354 self.mx = self.mb;
355 }
356 }
357
358 /// Adds the element at `src`, which equals the maximum, to the max group.
359 fn push_max(&mut self, src: usize) {
360 if self.max_len == 0 {
361 // The maximum's only element so far opens the group where it already is.
362 self.mb = self.mx;
363 self.max_len = 1;
364 }
365 // Grow the group to the right while that stays inside the scanned prefix. When it
366 // does not, the group ends the prefix and `src` lies before it but after the min
367 // group, so the slot just before the group is free to take.
368 let next = self.mb + self.max_len;
369 let slot = if next < self.end {
370 next
371 } else {
372 self.mb -= 1;
373 self.mb
374 };
375 self.swap(slot, src);
376 self.max_len += 1;
377 self.mx = self.mb;
378 }
379
380 /// Moves the groups into their final places and returns how many elements were placed at
381 /// the front and at the back, or `None` if the whole range turned out to be equal.
382 fn finish(&mut self) -> Option<(usize, usize)> {
383 if self.same {
384 return None;
385 }
386 let len = self.v.len();
387 // The max group goes first: the slot the minimum needs, `v[0]`, may lie inside it.
388 let back = if self.max_len == 0 {
389 self.swap(len - 1, self.mx);
390 1
391 } else {
392 // Only the group's elements outside `v[target..]` move, each into a slot of
393 // `v[target..]` that the group does not already occupy.
394 let (start, count) = (self.mb, self.max_len);
395 let target = len - count;
396 let free = target.max(start + count);
397 for k in 0..(target - start).min(count) {
398 self.swap(start + k, free + k);
399 }
400 count
401 };
402 let front = if self.min_len == 0 {
403 self.swap(0, self.mi);
404 1
405 } else {
406 self.min_len
407 };
408 Some((front, back))
409 }
410
411 /// Swaps two elements and keeps every remembered index pointing at the same element.
412 fn swap(&mut self, a: usize, b: usize) {
413 if a == b {
414 return;
415 }
416 self.v.swap(a, b);
417 let [h0, h1] = &mut self.held;
418 for index in [&mut self.mi, &mut self.mx, h0, h1] {
419 if *index == a {
420 *index = b;
421 } else if *index == b {
422 *index = a;
423 }
424 }
425 }
426}