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
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
use select_unpredictable;
use ;
use ptr;
/// Sorts a slice in place using heap sort.
///
/// Heap sort arranges the slice into a max-heap: a tree stored in the slice itself, in which
/// no element is smaller than its children, so the largest element is at the root. It then
/// repeatedly moves the root to the end of the heap, shrinks the heap by one element and
/// restores the heap order. Building the heap takes `O(n)` comparisons and each of the `n`
/// removals `O(log n)`, so the sort makes `O(n log n)` comparisons whatever the input, needs no
/// buffer and does not recurse.
///
/// Five refinements keep the algorithm as cheap as heap sort can be:
///
/// * **Sorted and reverse-sorted input is recognised.** The slice is first scanned for one
/// ascending or strictly descending run; if it covers the whole slice, the slice is left
/// alone or reversed. This costs `n − 1` comparisons on such input and usually two or three
/// on any other. Heap sort itself is not adaptive: it would spend `n·log₂ n` comparisons on
/// sorted input.
/// * **Bottom-up removal.** The textbook sift-down compares the element moved to the root with
/// the larger child at every level, two comparisons per level. That element comes from the
/// end of the heap and is usually small, so it sinks almost to the bottom anyway. Instead,
/// the vacant root is moved down to a leaf along the larger children, one comparison per
/// level, and the element is moved up from there, usually by a level or two (Wegener's
/// bottom-up heap sort). This takes the comparisons from about `1.7–1.85·n·log₂ n` down to
/// about `n·log₂ n`.
/// * **Holes instead of swaps.** The element being placed is held outside the slice, and each
/// level moves one element into the vacant slot instead of swapping two: one move instead of
/// three.
/// * **Four children per node for large heaps and small elements.** A heap with four children
/// per node has half as many levels, whose children are adjacent in memory, at the cost of
/// three comparisons per level instead of one, about `1.5·n·log₂ n` in all. That pays from
/// 64 KiB of elements on, where the heap no longer fits in the processor's fastest cache and
/// every level costs a cache miss: on random input 1.15–1.35 times faster than two children
/// for integers from 100,000 elements on, 1.1–1.4 times for strings and 1.25–2 times for
/// 64-byte elements; on nearly sorted strings, whose comparisons cost the most, 1.05–1.15
/// times slower. It also pays for elements of at most 8 bytes at any length: their
/// comparisons are so cheap that a level costs about the time to load the children, compare
/// them and find the next ones, so half as many levels make integers sort 1.15–1.25 times
/// faster in cache.
/// * **Short chains from one level to the next.** Each level of a removal waits for the
/// comparison before it, which says where the next children are. Every child is loaded from
/// the first child's index with no address to compute first, and for elements of up to 16
/// bytes the larger child and its own first child are chosen by selects between values
/// computed beforehand; with four children the winners of the first round are selected as
/// pointers, so that the values already loaded are compared instead of being loaded again.
/// This alone makes integers sort 1.25–1.3 times faster on four children per node.
///
/// Together, against the textbook heap sort: 1.1–1.25 times faster for integers up to 1000
/// elements and 1.4–1.85 times from 10,000 on, 1.05–1.4 times for strings from 1000 elements
/// on, and 1.3–2.2 times for 256-byte elements. Only sawtooth input of 1000–2000 integers, in
/// whose short periods three comparisons per level cost more than the levels saved, would sort
/// 1.02–1.05 times faster on two children per node.
///
/// # Properties
///
/// * **Unstable**: equal elements may be reordered.
/// * **In place**: no allocation, `O(1)` stack.
/// * **Not adaptive**, except to sorted and reverse-sorted input.
///
/// | Case | Comparisons | Moves |
/// |--------------------------|---------------------------------|--------------------|
/// | Best (sorted, reverse) | `n − 1` | `0` (`n` reversed) |
/// | Average (random) | `≈ n·log₂ n + 0.6n` | `≈ n·log₂ n` |
/// | Four children per node | `≈ 1.5·n·log₂ n` | `≈ 0.5·n·log₂ n` |
/// | Worst | `O(n log n)` | `O(n log n)` |
///
/// Four children per node: from 64 KiB of elements on, and for elements of at most 8 bytes.
/// For `n = 1000`: 999 comparisons on sorted or reverse-sorted input and about 10,500 on
/// random input (about 14,600 for elements of at most 8 bytes), against about 16,800 for the
/// textbook heap sort.
///
/// Heap sort's strength is its guarantee: `O(n log n)` on every input, in place, with constant
/// stack. It jumps around the slice, though, so [`slice::sort_unstable`] and this crate's
/// [`quick`](fn@super::quick) sort, which falls back to it only when its pivots keep failing,
/// are faster: 2–3.4 times for random integers from 64 elements on, 1.6–6 times for strings
/// and 1.1–3.5 times for 256-byte elements.
///
/// # Panics
///
/// Does not panic by itself. If the [`Ord`] implementation of `T` panics, the panic is
/// propagated and `arr` is left in an unspecified order, but it still holds every original
/// element exactly once: nothing is dropped twice or leaked. An [`Ord`] that is not a total
/// order leaves `arr` in an unspecified order with every element exactly once as well.
///
/// # Examples
///
/// ```
/// use a::sort::heap;
///
/// let mut v = [5, 1, 4, 2, 8, 0, 3, 9, 7, 6];
/// heap(&mut v);
/// assert_eq!(v, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
///
/// let mut words = vec!["pear", "apple", "fig"];
/// heap(&mut words);
/// assert_eq!(words, ["apple", "fig", "pear"]);
/// ```
/// From this many bytes of elements on, the heap has four children per node instead of two,
/// whatever the element size.
///
/// Measured: below it, two children are 1.02–1.15 times faster for strings; from about this
/// size on, four are, for 64-byte elements from 1000 and for `u64`s (before they took four at
/// every length) from 100,000, where the heap leaves the 128 KiB first-level cache of the
/// Apple M4 measured on.
const QUATERNARY_MIN_BYTES: usize = 64 * 1024;
/// Elements of at most this many bytes use a heap with four children per node at any length.
///
/// Their comparisons are so cheap that a level costs about the latency of loading the
/// children, comparing them and working out where the next children are, and half as many
/// levels outweigh three comparisons per level instead of one: measured below 64 KiB,
/// integers sort 1.15–1.25 times faster with four children, `u128`s 1.15–1.4 times, but
/// `&str`s, as small as a `u128` and slower to compare, 1.05–1.15 times slower.
const QUATERNARY_MAX_SIZE: usize = 8;
/// The largest element size, in bytes, for which [`largest_child`] chooses its results by
/// selects instead of arithmetic.
///
/// Larger elements compare by chains of branches (64-byte arrays element by element), after
/// which the compiler turns the selects into a branch on the answer: 1.35–1.5 times slower
/// for 64-byte elements from 10,000 on. With arithmetic, they sort as before.
const SELECT_MAX_SIZE: usize = 16;
/// Sorts `v`, of at least two elements, by heap sort on a heap with `D` children per node, `D`
/// being 2 or 4. The children of `v[i]` are `v[D·i + 1..=D·i + D]`.
/// Returns the index of the largest of the `D` children starting at `c`, with one comparison
/// for two children and three, in two rounds, for four, and the index of that child's first
/// child.
///
/// Each level of a removal waits for the one before: the children's addresses depend on the
/// last comparison. `bases[k]` is `base.add(k)`, so that every child is loaded from `c`
/// directly, with no address to compute first. For elements of at most [`SELECT_MAX_SIZE`]
/// bytes, both results are chosen by selects between values computed before the comparison,
/// so that a single select lies between a comparison and the next level's loads; with four
/// children, the winners of the first round are selected as pointers, so that the compiler
/// compares the values it has already loaded instead of loading the winners again. Together,
/// this makes a removal from a heap with four children 1.25–1.3 times faster for integers,
/// and one from a heap with two 1.02–1.05 times.
///
/// # Safety
///
/// `base` must be valid for `c + D` elements.
unsafe
/// Returns `[base, base + 1, …, base + D − 1]`, the pointers [`largest_child`] addresses the
/// children from.
/// Returns the index of the largest of the `k` children starting at `c`, `1 <= k < D`: the
/// last node with children may have fewer than `D`.
///
/// # Safety
///
/// `base` must be valid for `c + k` elements.
unsafe
/// Moves `v[node]` down the heap `v[..len]` until no child is larger, `v` starting at `base`;
/// the subtrees of `node` must be heaps already.
///
/// # Safety
///
/// `node < len` and `base` must be valid for `len` elements.
unsafe
/// Moves the largest element of the heap `v[..=end]` to `v[end]` and makes `v[..end]` a heap
/// again, `v` starting at `base`.
///
/// The last element is held out and the root moved into its slot, leaving the root vacant.
/// The vacant slot then moves down to a leaf along the larger children, which move up, and the
/// held element moves up from there past the elements smaller than it.
///
/// # Safety
///
/// `1 <= end` and `base` must be valid for `end + 1` elements.
unsafe
/// A vacant slot of a slice, plus the element held outside the slice that belongs there.
///
/// Writing the held element on drop is what keeps [`sift_down`] and [`pop`] panic-safe: the
/// slice is whole again even if a comparison unwinds while the element is out.