1pub fn iter_is_monotonic_by_key<I, K, F>(items: I, mut key: F) -> bool
5where
6 I: IntoIterator,
7 K: Ord,
8 F: FnMut(I::Item) -> K,
9{
10 let mut previous = None;
11 for item in items {
12 let current = key(item);
13 if let Some(previous) = previous {
14 if current < previous {
15 return false;
16 }
17 }
18 previous = Some(current);
19 }
20 true
21}
22
23pub fn sort_by_key_if_needed<T, K, F>(items: &mut [T], mut key: F)
25where
26 K: Ord,
27 F: FnMut(&T) -> K,
28{
29 let mut previous = None;
30 for index in 0..items.len() {
31 let current = key(&items[index]);
32 if let Some(previous) = previous {
33 if current < previous {
34 items.sort_by_key(key);
35 return;
36 }
37 }
38 previous = Some(current);
39 }
40}
41
42pub fn sort_unstable_by_key_if_needed<T, K, F>(items: &mut [T], mut key: F)
44where
45 K: Ord,
46 F: FnMut(&T) -> K,
47{
48 let mut previous = None;
49 for index in 0..items.len() {
50 let current = key(&items[index]);
51 if let Some(previous) = previous {
52 if current < previous {
53 items.sort_unstable_by_key(key);
54 return;
55 }
56 }
57 previous = Some(current);
58 }
59}
60
61pub fn sort_unstable_if_needed<T>(items: &mut [T])
63where
64 T: Ord,
65{
66 for index in 1..items.len() {
67 if items[index] < items[index - 1] {
68 items.sort_unstable();
69 return;
70 }
71 }
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80pub enum DensePermutationDefect {
81 Duplicate {
84 index: usize,
86 slot: usize,
88 },
89 Sparse {
92 index: usize,
94 slot: usize,
96 },
97 LengthMismatch {
100 resolved: usize,
102 expected: usize,
104 },
105}
106
107pub fn classify_dense_permutation(
116 sorted_indices: &[usize],
117 expected_len: usize,
118) -> Result<(), DensePermutationDefect> {
119 for (slot, &index) in sorted_indices.iter().enumerate() {
120 if index != slot {
121 return Err(if index < slot {
122 DensePermutationDefect::Duplicate { index, slot }
123 } else {
124 DensePermutationDefect::Sparse { index, slot }
125 });
126 }
127 }
128 if sorted_indices.len() != expected_len {
129 return Err(DensePermutationDefect::LengthMismatch {
130 resolved: sorted_indices.len(),
131 expected: expected_len,
132 });
133 }
134 Ok(())
135}
136
137#[cfg(test)]
138mod tests {
139 use std::cell::Cell;
140
141 use super::{
142 classify_dense_permutation, iter_is_monotonic_by_key, sort_by_key_if_needed,
143 sort_unstable_by_key_if_needed, sort_unstable_if_needed, DensePermutationDefect,
144 };
145
146 #[test]
147 fn iter_monotonic_by_key_detects_ordered_and_unordered_streams() {
148 assert!(iter_is_monotonic_by_key([0, 1, 1, 3], |value| value));
149 assert!(!iter_is_monotonic_by_key([0, 2, 1, 3], |value| value));
150 }
151
152 #[test]
153 fn stable_sort_by_key_skips_already_monotonic_slices() {
154 let calls = Cell::new(0usize);
155 let mut items = [(0usize, "a"), (1, "b"), (1, "c"), (3, "d")];
156
157 sort_by_key_if_needed(&mut items, |(key, _)| {
158 calls.set(calls.get() + 1);
159 *key
160 });
161
162 assert_eq!(items, [(0, "a"), (1, "b"), (1, "c"), (3, "d")]);
163 assert_eq!(
164 calls.get(),
165 items.len(),
166 "Fix: monotonic ordering paths must not invoke the fallback sort."
167 );
168 }
169
170 #[test]
171 fn stable_sort_by_key_sorts_unordered_slices() {
172 let mut items = [(2usize, "c"), (0, "a"), (3, "d"), (1, "b")];
173
174 sort_by_key_if_needed(&mut items, |(key, _)| *key);
175
176 assert_eq!(items, [(0, "a"), (1, "b"), (2, "c"), (3, "d")]);
177 }
178
179 #[test]
180 fn unstable_sort_by_key_skips_already_monotonic_slices() {
181 let calls = Cell::new(0usize);
182 let mut items = [(0usize, "a"), (1, "b"), (3, "c")];
183
184 sort_unstable_by_key_if_needed(&mut items, |(key, _)| {
185 calls.set(calls.get() + 1);
186 *key
187 });
188
189 assert_eq!(items, [(0, "a"), (1, "b"), (3, "c")]);
190 assert_eq!(
191 calls.get(),
192 items.len(),
193 "Fix: monotonic unstable-ordering paths must not invoke the fallback sort."
194 );
195 }
196
197 #[test]
198 fn unstable_sort_by_key_sorts_unordered_slices() {
199 let mut items = [(2usize, "c"), (0, "a"), (1, "b")];
200
201 sort_unstable_by_key_if_needed(&mut items, |(key, _)| *key);
202
203 assert_eq!(items, [(0, "a"), (1, "b"), (2, "c")]);
204 }
205
206 #[test]
207 fn unstable_sort_skips_already_monotonic_slices() {
208 let mut items = [0usize, 1, 1, 3];
209
210 sort_unstable_if_needed(&mut items);
211
212 assert_eq!(items, [0, 1, 1, 3]);
213 }
214
215 #[test]
216 fn unstable_sort_sorts_unordered_slices() {
217 let mut items = [2usize, 0, 1];
218
219 sort_unstable_if_needed(&mut items);
220
221 assert_eq!(items, [0, 1, 2]);
222 }
223
224 #[test]
225 fn classify_dense_permutation_distinguishes_dense_duplicate_sparse_and_length() {
226 assert_eq!(classify_dense_permutation(&[0, 1, 2], 3), Ok(()));
227 assert_eq!(classify_dense_permutation(&[], 0), Ok(()));
228 assert_eq!(
229 classify_dense_permutation(&[0, 0, 2], 3),
230 Err(DensePermutationDefect::Duplicate { index: 0, slot: 1 }),
231 "Fix: a repeated value at a later sorted slot is a duplicate, not a generic non-dense map."
232 );
233 assert_eq!(
234 classify_dense_permutation(&[0, 2, 3], 3),
235 Err(DensePermutationDefect::Sparse { index: 2, slot: 1 }),
236 "Fix: a value above its sorted slot is a sparse gap, not a duplicate."
237 );
238 assert_eq!(
239 classify_dense_permutation(&[0, 1], 3),
240 Err(DensePermutationDefect::LengthMismatch {
241 resolved: 2,
242 expected: 3
243 }),
244 "Fix: a dense-but-short map is a length mismatch."
245 );
246 assert_eq!(
247 classify_dense_permutation(&[0, 1, 2, 3], 3),
248 Err(DensePermutationDefect::LengthMismatch {
249 resolved: 4,
250 expected: 3
251 }),
252 "Fix: a dense-but-long map is a length mismatch."
253 );
254 }
255
256 #[test]
257 fn classify_dense_permutation_matches_sorted_reference_over_generated_maps() {
258 for len in 0usize..=24 {
261 let dense: Vec<usize> = (0..len).collect();
262 assert_eq!(classify_dense_permutation(&dense, len), Ok(()));
263
264 for collide in 0..len {
265 let mut indices = dense.clone();
268 indices[collide] = 0;
269 sort_unstable_if_needed(&mut indices);
270 let verdict = classify_dense_permutation(&indices, len);
271 let distinct: std::collections::BTreeSet<usize> =
272 indices.iter().copied().collect();
273 let reference_is_dense =
274 distinct.len() == len && indices.len() == len && *distinct.iter().max().unwrap_or(&0) < len.max(1);
275 if collide == 0 {
276 assert_eq!(verdict, Ok(()));
278 } else {
279 assert!(verdict.is_err(), "len={len} collide={collide} must be a defect");
280 assert!(!reference_is_dense || verdict.is_ok());
281 assert!(matches!(
282 verdict,
283 Err(DensePermutationDefect::Duplicate { .. })
284 | Err(DensePermutationDefect::Sparse { .. })
285 | Err(DensePermutationDefect::LengthMismatch { .. })
286 ));
287 }
288 }
289 }
290 }
291
292 #[test]
293 fn generated_ordering_matrix_matches_full_sort_contract() {
294 for len in 0..=128 {
295 let ordered: Vec<usize> = (0..len).collect();
296 let mut reversed: Vec<usize> = (0..len).rev().collect();
297 let mut expected = reversed.clone();
298 expected.sort_unstable();
299
300 assert!(iter_is_monotonic_by_key(ordered.iter().copied(), |value| {
301 value
302 }));
303 if len > 1 {
304 assert!(!iter_is_monotonic_by_key(
305 reversed.iter().copied(),
306 |value| value
307 ));
308 }
309
310 sort_unstable_if_needed(&mut reversed);
311 assert_eq!(reversed, expected);
312
313 let mut keyed: Vec<(usize, usize)> = (0..len).rev().map(|value| (value, len)).collect();
314 sort_unstable_by_key_if_needed(&mut keyed, |(key, _)| *key);
315 for (expected_key, actual) in keyed.iter().enumerate() {
316 assert_eq!(actual.0, expected_key);
317 assert_eq!(actual.1, len);
318 }
319 }
320 }
321}