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
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.
///
/// Four 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.** From 64 KiB of elements on, the heap no
/// longer fits in the processor's fastest cache, and every level of it costs a cache miss.
/// 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. On random input this is 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, it is
/// 1.05–1.15 times slower.
///
/// Together, against the textbook heap sort: 1.25–1.35 times faster for integers from 100,000
/// elements on, 1.05–1.4 times for strings from 1000 elements on, and 1.3–2.2 times for
/// 256-byte elements. On slices of up to 10,000 integers, where comparisons are nearly free and
/// everything is in cache, the textbook heap sort is 1.03–1.15 times faster: the comparisons
/// that bottom-up removal saves cost almost nothing there, and it takes two loops, down and
/// back up, where the textbook sift-down takes one.
///
/// # 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` |
/// | Large heaps (4 children) | `≈ 1.5·n·log₂ n` | `≈ 0.5·n·log₂ n` |
/// | Worst | `O(n log n)` | `O(n log n)` |
///
/// For `n = 1000`: 999 comparisons on sorted or reverse-sorted input and about 10,500 on
/// random input, 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: 3–4 times for random integers, 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.
///
/// Measured: below it, two children are 1.02–1.15 times faster for integers and strings; from
/// about this size on, four are, for integers from 100,000 elements and for 64-byte elements
/// from 1000, where the heap leaves the 128 KiB first-level cache of the Apple M4 measured on.
const QUATERNARY_MIN_BYTES: usize = 64 * 1024;
/// 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 `c..=last`, with one comparison for
/// two children and three, in two rounds, for four.
///
/// The last child is addressed by `last`, which the caller has computed for its bounds check
/// anyway, rather than by `c + D - 1`. From `c` alone the compiler loads the first two
/// children as a pair, from an address it must compute first, which puts an addition on the
/// critical path of every level: that made the sort 1.1–1.15 times slower for integers.
///
/// # Safety
///
/// `last == c + D - 1` and `base` must be valid for `last + 1` elements.
unsafe
/// 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.