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
use ptr;
/// Sorts a slice in place using pair insertion sort with galloping search.
///
/// A sorted prefix grows from the left two elements at a time. The two new elements are
/// ordered with one comparison, the larger one's place in the prefix is searched for from
/// the right, and the smaller one's place is searched for only to the left of that, since it
/// cannot lie anywhere else. Both then move into place together. This is the pair insertion
/// sort that Java's dual-pivot quicksort uses for short ranges, with a galloping search in
/// place of its linear scan.
///
/// Three refinements keep the algorithm cheap:
///
/// * **Sorted fast path.** The smaller element of a pair is compared with the last element of
/// the prefix before any search. If it is not smaller, neither is the larger one, and the
/// pair is done after two comparisons, one per element: already-sorted input costs exactly
/// `n - 1` comparisons, as with plain insertion sort.
/// * **Galloping search.** An insertion point is found by probing 1, 2, 4, 8, ... slots to the
/// left of where the search starts, then binary searching the last gap. An element that
/// moves `d` slots left costs at most `2⌊log₂ d⌋ + 2` comparisons: logarithmic in how far it
/// moves rather than linear (a plain scan) or logarithmic in the length of the prefix (a
/// binary search), so nearly-sorted input stays nearly linear.
/// * **Pair moves.** Prefix elements greater than both new elements shift right by two slots
/// at once and those between them by one, and each new element is written once into its
/// final slot. A prefix element that both new elements pass therefore moves once instead of
/// twice. Both insertion points are found before anything moves, so no comparison ever
/// runs while an element is out of the slice.
///
/// Against inserting the same elements one at a time with the same galloping search, a pair
/// spends one comparison on ordering itself, and in exchange the smaller element's search
/// starts from the larger one's insertion point instead of from the end of the prefix, and
/// the prefix elements past that point move once instead of twice. On 1000 elements, counting
/// a move each time an element changes slot:
///
/// | Input (`n = 1000`) | Comparisons, pair | one at a time | Moves, pair | one at a time |
/// |----------------------------|-------------------|---------------|-------------|---------------|
/// | Sorted | 999 | 999 | 0 | 0 |
/// | Random, distinct (mean) | ≈ 14,580 | ≈ 14,920 | ≈ 167,400 | ≈ 250,600 |
/// | Reverse sorted | 5,986 | 9,966 | 250,998 | 500,499 |
/// | Worst case, per column | 17,020 | 17,169 | 250,999 | 500,499 |
///
/// On random input the two searches of a pair together cost slightly less than two single
/// ones, and a third of the moves are saved (`≈ n²/6` against `≈ n²/4`). On reverse-sorted
/// input both halve: the larger element goes to the front, which leaves the smaller one
/// nothing to search and every prefix element to pass only once.
///
/// # Properties
///
/// * **Stable**: equal elements keep their relative order. A pair is reordered only when its
/// second element is strictly smaller, and each element is inserted after every prefix
/// element that is not greater than it.
/// * **In place**: `O(1)` auxiliary memory, no allocation.
/// * **Adaptive**: comparisons grow with the logarithm of how far elements move, moves with
/// the distance itself.
///
/// Let `dₓ` be the number of elements before `x` in the input that are greater than `x`, which
/// is how many slots `x` moves left, and `I = Σ dₓ` the number of inversions.
///
/// | Case | Comparisons | Moves |
/// |--------------------------|------------------------------------|--------------------------|
/// | Best (already sorted) | `n - 1` | `0` |
/// | Any input | `≤ n - 1 + Σₓ 2⌈log₂(dₓ + 1)⌉` | `≤ I + n` |
/// | Reverse sorted | `≈ (n/2)·log₂ n + n` | `≈ n²/4` (the worst) |
/// | Worst | `≤ 2n·log₂ n` (`≈ 2n·log₂ n - 3n`) | `≈ n²/4` |
///
/// The adaptive bound is tight: input such as `[1, 0, 3, 2, 5, 4, ...]` comes within two
/// comparisons of it. By concavity it is below `3n + 2n·log₂(1 + I/n)`, so input with few
/// inversions sorts in close to linear time. The worst case needs every element to move a long
/// way, but not to the front, where a search is cut short. On reverse-sorted input, a pair
/// inserted into a prefix of `i` elements costs exactly `⌈log₂ i⌉ + 3` comparisons and
/// `i + 2` moves, the most a pair can move (only the first pair, whose larger element ends in
/// the slot it started in, moves two), and the last element when `n` is even costs
/// `⌈log₂(n - 1)⌉ + 1` comparisons and `n` moves.
///
/// Insertion sort moves `O(n²)` elements on random input and is mainly of educational value
/// beyond short or nearly-sorted slices; prefer [`slice::sort`] 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: every comparison is made while all elements are in the slice, and
/// the moves that follow cannot panic.
///
/// # Examples
///
/// ```
/// use a::sort::insertion;
///
/// let mut v = [5, 1, 4, 2, 8, 0];
/// insertion(&mut v);
/// assert_eq!(v, [0, 1, 2, 4, 5, 8]);
///
/// // Equal keys keep their order: sorting by the first field leaves 'a' before 'b'.
/// #[derive(Debug, PartialEq, Eq)]
/// struct Card(u8, char);
/// impl Ord for Card {
/// fn cmp(&self, other: &Self) -> std::cmp::Ordering {
/// self.0.cmp(&other.0)
/// }
/// }
/// impl PartialOrd for Card {
/// fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
/// Some(self.cmp(other))
/// }
/// }
/// let mut cards = [Card(2, 'a'), Card(1, 'x'), Card(2, 'b'), Card(0, 'y')];
/// insertion(&mut cards);
/// assert_eq!(cards, [Card(0, 'y'), Card(1, 'x'), Card(2, 'a'), Card(2, 'b')]);
///
/// let mut words = vec!["pear", "apple", "fig"];
/// insertion(&mut words);
/// assert_eq!(words, ["apple", "fig", "pear"]);
/// ```
/// Inserts the last two elements of `v` into the sorted rest of it, which is not empty.
/// Inserts the last element of `v` into the sorted rest of it.
/// Returns how many elements at the end of the sorted slice `v` are greater than `x`, given
/// that the last `known` of them are (`known` is at most `v.len()`).
///
/// Probes 1, 2, 4, ... slots from the end, skipping those already known, until it meets an
/// element that is not greater than `x` or reaches the front, then binary searches the gap
/// between the last two probes. An answer `d >= 1` costs at most `2⌊log₂ d⌋ + 2` comparisons,
/// one fewer when `known` is 1, and `d = 0` costs one.
///
/// The result never exceeds `v.len()`, whatever the [`Ord`] implementation answers.
/// Moves the last element of `v` to the front, shifting every other element one slot right.