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
use select_unpredictable;
use ;
use ptr;
use bubble;
/// Sorts a slice in place using comb sort.
///
/// Comb sort is bubble sort with a gap. A pass compares every element with the one `gap`
/// slots after it and swaps the two if they are out of order, so a small element near the end
/// (a "turtle") moves `gap` slots left in one step instead of one. The gap starts at the length
/// of the slice and shrinks by a factor of 1.3 after each pass; once it reaches 1, the slice is
/// nearly sorted and plain bubble sort finishes it.
///
/// Four refinements keep the algorithm as cheap as comb sort can be:
///
/// * **Rule of 11 (Combsort11).** A gap of 9 or 10 is replaced by 11. The sequences that go
/// through 9 or 10 leave more turtles for the final bubble sort; 11, 8, 6, 4, 3, 2 does not.
/// On random input of 10,000 elements this saves about 6% of the comparisons and narrows
/// their spread.
/// * **An adaptive finish.** The final phase is this crate's [`bubble`](fn@bubble), which
/// scans only up to the last element it moved and moves elements by hole shifting, instead
/// of the repeated full passes of classic comb sort. For integers, its branchless passes
/// make the whole comb sort 1.1–1.2 times faster than a finish that branches on every
/// comparison.
/// * **Branchless swaps for small elements.** Whether a gapped pass swaps a pair is close to a
/// coin flip, which defeats the branch predictor. For elements of at most 32 bytes the swap
/// is made unconditional: both slots are rewritten from addresses chosen by the comparison,
/// which the processor turns into conditional moves. A pass is also walked in blocks of
/// `gap` pairs taken from two non-overlapping slices, which the compiler can schedule
/// freely. On random input this is 3–4.5 times faster for integers and 1.5–1.8 times for
/// strings. On already sorted input, where every branch would be predicted, it is about a
/// quarter slower, a price worth paying for a sort that is not adaptive anyway. Larger
/// elements cost more to rewrite than a misprediction does, so they are swapped only when
/// needed.
/// * **Carrying in registers at small gaps, for elements of at most 16 bytes.** At a gap of
/// 14 or less, each pair overlaps the one `gap` slots before it, so a swap has to reach
/// memory before the next comparison can read it back, and a pass at gap 2 runs at a third
/// of the speed of one at a large gap. But the pairs form `gap` chains
/// `c, c + gap, c + 2·gap, ...`, and along a chain a pass carries the largest element seen
/// so far. So the pass holds the carried element of every chain in a register, one per
/// chain, and at each position writes back only the smaller of the carried element and the
/// next one: the comparisons of a plain pass in its order, with the same outcomes and the
/// same result, and no round trip through memory. The `gap` chains are independent, so
/// their comparisons overlap. Against walking one chain at a time this makes the whole sort
/// 1.1–1.3 times faster for 4- and 8-byte integers, and against a plain pass 1.1–1.4 times
/// for pairs of integers and boxed strings; it is slower for `String`, whose comparisons
/// take long to start, which keeps the independent pairs of a plain pass. Chains shorter
/// than four elements, which only short slices have, are not worth picking up.
///
/// The comparisons made are exactly those of comb sort with the rule of 11, in the order of a
/// plain pass, followed by those of bubble sort with early exit and last-swap bound (which
/// interleaves two of its passes for small elements).
///
/// # Properties
///
/// * **Unstable**: equal elements may be reordered (a gapped swap can carry an element past
/// an equal one).
/// * **In place**: `O(1)` auxiliary memory, no allocation.
/// * **Not adaptive**: the gapped passes make the same comparisons whatever the input; only
/// the final bubble sort gets cheaper on nearly sorted input.
///
/// Let `g₁ > g₂ > ... > gₖ ≥ 2` be the gaps: `k ≈ log₁.₃ n − 4` of them, each the previous one
/// divided by 1.3 and rounded down, with the rule of 11 applied. The gapped passes make exactly
/// `Σ (n − gᵢ) ≈ n·(log₁.₃ n − 7.3)` comparisons on every input; the final bubble sort adds at
/// least `n − 1`, and on random input about `3n`.
///
/// | Case | Comparisons | Moves |
/// |------------------------------------|---------------------------------|-----------------------|
/// | Best (already sorted) | `Σ (n − gᵢ) + n − 1` | `0` |
/// | Average (random) | `≈ n·(log₁.₃ n − 4.5)` | `O(n log n)` |
/// | Worst | `O(n²)` | `O(n²)` |
///
/// For `n = 1000`: 19,706 comparisons on sorted input and about 21,300 on random input,
/// against about 499,000 for bubble sort. No known input makes comb sort quadratic in
/// practice, but no `O(n log n)` bound has been proved either.
///
/// Comb sort is mainly of educational value; prefer [`slice::sort_unstable`] for real
/// workloads.
///
/// # 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.
///
/// # Examples
///
/// ```
/// use a::sort::comb;
///
/// let mut v = [5, 1, 4, 2, 8, 0];
/// comb(&mut v);
/// assert_eq!(v, [0, 1, 2, 4, 5, 8]);
///
/// // Turtles at the end move many slots per step.
/// let mut v = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
/// comb(&mut v);
/// assert_eq!(v, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
///
/// let mut words = vec!["pear", "apple", "fig"];
/// comb(&mut words);
/// assert_eq!(words, ["apple", "fig", "pear"]);
/// ```
/// The largest element size, in bytes, for which a gapped pass swaps without branching.
///
/// Measured on random input: integers, `u128`, pairs of integers, `&str`, `String` and `Box`
/// sort 1.6–4.5 times faster branchless; elements of 64 bytes or more about 1.2–2 times slower.
const BRANCHLESS_MAX_SIZE: usize = 32;
/// The largest element size, in bytes, for which small gaps are passed by [`pass_lanes`].
///
/// Measured on the whole sort against [`pass_branchless`] at those gaps: 1.05–1.6 times faster
/// for `u32`, `u64`, `Box<u64>`, `(u64, u64)` and `Box<str>`, 1.05–1.2 times slower for
/// `String`.
const LANES_MAX_SIZE: usize = 16;
/// The largest gap passed by [`pass_lanes`]. From 18 up, the independent pairs of
/// [`pass_branchless`], which the compiler vectorizes for integers, are as fast or faster.
const LANES_MAX_GAP: usize = 14;
/// The shortest chains, in elements, worth holding in registers: shorter ones cost more to
/// pick up and put back than they save.
const LANES_MIN_CHAIN: usize = 4;
/// The gap after `gap`: `gap / 1.3` rounded down, with 9 and 10 replaced by 11.
/// Compares every element of `arr` with the one `gap` slots after it, in increasing order of
/// position, and swaps the two if the second is smaller, with no branch on the outcome.
/// Compares every element of `arr` with the one `gap` slots after it, in increasing order of
/// position, and swaps the two if the second is smaller.
/// Does what [`pass_branchless`] does at gap `G`, with the carried element of each chain held
/// outside the slice.
///
/// Position `j` belongs to chain `j % G`. Along a chain, a pass carries the largest element
/// seen so far: each pair is that element and the next one, and the smaller of the two stays
/// behind. So the carried element of chain `c` is held in `lanes.held[c]`, the slot it came
/// from is vacant, and at each position the smaller of the held element and the next one is
/// written into the vacant slot, chosen by value so that the held elements can stay in
/// registers. Positions are taken in increasing order, as in a plain pass, and the head of a
/// chain is compared in its slot before it is picked up.
///
/// `G` must be less than `arr.len()`.
/// The carried elements of the `G` chains of a pass at gap `G`, walked a row of `G`
/// positions at a time.
///
/// Writing the held elements back on drop is what keeps [`pass_lanes`] panic-safe: the slice
/// is whole again even if a comparison unwinds while the elements are out.