ndarray_slice/lib.rs
1//! Fast and robust slice-based algorithms (e.g., [sorting], [selection], [search]) for
2//! non-contiguous (sub)views into *n*-dimensional arrays. Reimplements algorithms in [`slice`] and
3#![cfg_attr(feature = "rayon", doc = "[`rayon::slice`]")]
4#![cfg_attr(not(feature = "rayon"), doc = "`rayon::slice`")]
5//! for [`ndarray`] with arbitrary memory layout (e.g., non-contiguous).
6//!
7//! # Example
8//!
9//! ```
10//! use ndarray_slice::{ndarray::arr2, Slice1Ext};
11//!
12//! // 2-dimensional array of 4 rows and 5 columns.
13//! let mut v = arr2(&[[-5, 4, 1, -3, 2], // row 0, axis 0
14//! [ 8, 3, 2, 4, 8], // row 1, axis 0
15//! [38, 9, 3, 0, 3], // row 2, axis 0
16//! [ 4, 9, 0, 8, -1]]); // row 3, axis 0
17//! // \ \ \
18//! // column 0 \ column 4 axis 1
19//! // column 2 axis 1
20//!
21//! // Mutable subview into the last column.
22//! let mut column = v.column_mut(4);
23//!
24//! // Due to row-major memory layout, columns are non-contiguous
25//! // and hence cannot be sorted by viewing them as mutable slices.
26//! assert_eq!(column.as_slice_mut(), None);
27//!
28//! // Instead, sorting is specifically implemented for non-contiguous
29//! // mutable (sub)views.
30//! column.sort_unstable();
31//!
32//! assert!(v == arr2(&[[-5, 4, 1, -3, -1],
33//! [ 8, 3, 2, 4, 2],
34//! [38, 9, 3, 0, 3],
35//! [ 4, 9, 0, 8, 8]]));
36//! // \
37//! // column 4 sorted, others untouched
38//! ```
39//!
40//! # Current Implementation
41//!
42//! Complexities where *n* is the length of the (sub)view and *m* the count of indices to select.
43//!
44//! | Resource | Complexity | Sorting (stable) | Sorting (unstable) | Selection (unstable) | Bulk Selection (unstable) |
45//! |----------|------------|------------------|---------------------|----------------------|---------------------------|
46//! | Time | Best | *O*(*n*) | *O*(*n*) | *O*(*n*) | *O*(*n* log *m*) |
47//! | Time | Average | *O*(*n* log *n*) | *O*(*n* log *n*) | *O*(*n*) | *O*(*n* log *m*) |
48//! | Time | Worst | *O*(*n* log *n*) | *O*(*n* log *n*) | *O*(*n*) | *O*(*n* log *m*) |
49//! | Space | Best | *O*(1) | *O*(1) | *O*(1) | *O*(*m*) |
50//! | Space | Average | *O*(*n*/2) | *O*(log *n*) | *O*(1) | *O*(*m*+log *m*) |
51//! | Space | Worst | *O*(*n*/2) | *O*(log *n*) | *O*(1) | *O*(*m*+log *m*) |
52//!
53//!
54//! [sorting]: https://en.wikipedia.org/wiki/Sorting_algorithm
55//! [selection]: https://en.wikipedia.org/wiki/Selection_algorithm
56//! [search]: https://en.wikipedia.org/wiki/Search_algorithm
57//!
58//! # Roadmap
59//!
60//! * Add `SliceExt` trait for *n*-dimensional array or (sub)view with methods expecting `Axis` as
61//! their first argument. Comparing methods will always be suffixed with `_by` or `_by_key`
62//! defining how to compare multi-dimensional elements (e.g., rows) along the provided axis of
63//! interest (e.g., columns).
64//!
65//! # Features
66//!
67//! * `alloc` for stable `sort`/`sort_by`/`sort_by_key`. Enabled by `std`.
68//! * `std` for stable `sort_by_cached_key`. Enabled by `default` or `rayon`.
69//! * `stacker` for spilling recursion stack over to heap if necessary. Enabled by `default`.
70//! * `rayon` for parallel `par_sort*`/`par_select_many_nth_unstable*`.
71
72#![deny(
73 missing_docs,
74 rustdoc::broken_intra_doc_links,
75 rustdoc::missing_crate_level_docs
76)]
77#![cfg_attr(not(feature = "std"), no_std)]
78#![cfg_attr(miri, feature(maybe_uninit_slice))]
79
80#[inline(always)]
81fn maybe_grow<R, F: FnOnce() -> R>(callback: F) -> R {
82 #[cfg(feature = "stacker")]
83 {
84 stacker::maybe_grow(32 * 1_024, 1_024 * 1_024, callback)
85 }
86 #[cfg(not(feature = "stacker"))]
87 {
88 callback()
89 }
90}
91
92mod heap_sort;
93mod insertion_sort;
94mod merge_sort;
95mod partition;
96mod partition_dedup;
97mod quick_sort;
98mod stable_sort;
99
100#[cfg(feature = "rayon")]
101mod par;
102#[cfg(feature = "rayon")]
103use par::{
104 merge_sort::par_merge_sort, partition::par_partition_at_indices, quick_sort::par_quick_sort,
105};
106
107#[cfg(feature = "alloc")]
108use crate::stable_sort::stable_sort;
109
110use crate::{
111 partition::{is_sorted, partition_at_index, partition_at_indices, reverse},
112 partition_dedup::partition_dedup,
113 quick_sort::quick_sort,
114};
115use core::cmp::Ordering::{self, Equal, Greater, Less};
116use ndarray::{ArrayBase, ArrayViewMut1, Data, DataMut, Ix1};
117
118pub use ndarray;
119
120// Derivative work of [`core::slice`] licensed under `MIT OR Apache-2.0`.
121//
122// [`core::slice`]: https://doc.rust-lang.org/src/core/slice/mod.rs.html
123
124/// Extension trait for 1-dimensional [`ArrayBase<S, Ix1>`](`ArrayBase`) array or (sub)view with
125/// arbitrary memory layout (e.g., non-contiguous) providing methods (e.g., [sorting], [selection],
126/// [search]) similar to [`slice`] and
127#[cfg_attr(feature = "rayon", doc = "[`rayon::slice`].")]
128#[cfg_attr(not(feature = "rayon"), doc = "`rayon::slice`.")]
129///
130/// [sorting]: https://en.wikipedia.org/wiki/Sorting_algorithm
131/// [selection]: https://en.wikipedia.org/wiki/Selection_algorithm
132/// [search]: https://en.wikipedia.org/wiki/Search_algorithm
133pub trait Slice1Ext<A, S>
134where
135 S: Data<Elem = A>,
136{
137 /// Sorts the array in parallel.
138 ///
139 /// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* log *n*) worst-case.
140 ///
141 /// When applicable, unstable sorting is preferred because it is generally faster than stable
142 /// sorting and it doesn't allocate auxiliary memory.
143 /// See [`par_sort_unstable`](Slice1Ext::par_sort_unstable).
144 ///
145 /// # Current Implementation
146 ///
147 /// The current algorithm is an adaptive, iterative merge sort inspired by
148 /// [timsort](https://en.wikipedia.org/wiki/Timsort).
149 /// It is designed to be very fast in cases where the array is nearly sorted, or consists of
150 /// two or more sorted sequences concatenated one after another.
151 ///
152 /// Also, it allocates temporary storage half the size of `self`, but for short arrays a
153 /// non-allocating insertion sort is used instead.
154 ///
155 /// In order to sort the array in parallel, the array is first divided into smaller chunks and
156 /// all chunks are sorted in parallel. Then, adjacent chunks that together form non-descending
157 /// or descending runs are concatenated. Finally, the remaining chunks are merged together using
158 /// parallel subdivision of chunks and parallel merge operation.
159 ///
160 /// # Examples
161 ///
162 /// ```
163 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
164 ///
165 /// let mut v = arr1(&[-5, 4, 1, -3, 2]);
166 ///
167 /// v.par_sort();
168 /// assert!(v == arr1(&[-5, -3, 1, 2, 4]));
169 /// ```
170 #[cfg(feature = "rayon")]
171 fn par_sort(&mut self)
172 where
173 A: Ord + Send,
174 S: DataMut;
175 /// Sorts the array in parallel with a comparator function.
176 ///
177 /// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* log *n*) worst-case.
178 ///
179 /// The comparator function must define a total ordering for the elements in the array. If
180 /// the ordering is not total, the order of the elements is unspecified. An order is a
181 /// total order if it is (for all `a`, `b` and `c`):
182 ///
183 /// * total and antisymmetric: exactly one of `a < b`, `a == b` or `a > b` is true, and
184 /// * transitive, `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`.
185 ///
186 /// For example, while [`f64`] doesn't implement [`Ord`] because `NaN != NaN`, we can use
187 /// `partial_cmp` as our sort function when we know the array doesn't contain a `NaN`.
188 ///
189 /// ```
190 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
191 ///
192 /// let mut floats = arr1(&[5f64, 4.0, 1.0, 3.0, 2.0]);
193 /// floats.par_sort_by(|a, b| a.partial_cmp(b).unwrap());
194 /// assert_eq!(floats, arr1(&[1.0, 2.0, 3.0, 4.0, 5.0]));
195 /// ```
196 ///
197 /// When applicable, unstable sorting is preferred because it is generally faster than stable
198 /// sorting and it doesn't allocate auxiliary memory.
199 /// See [`par_sort_unstable_by`](Slice1Ext::par_sort_unstable_by).
200 ///
201 /// # Current Implementation
202 ///
203 /// The current algorithm is an adaptive, iterative merge sort inspired by
204 /// [timsort](https://en.wikipedia.org/wiki/Timsort).
205 /// It is designed to be very fast in cases where the array is nearly sorted, or consists of
206 /// two or more sorted sequences concatenated one after another.
207 ///
208 /// Also, it allocates temporary storage half the size of `self`, but for short arrays a
209 /// non-allocating insertion sort is used instead.
210 ///
211 /// In order to sort the array in parallel, the array is first divided into smaller chunks and
212 /// all chunks are sorted in parallel. Then, adjacent chunks that together form non-descending
213 /// or descending runs are concatenated. Finally, the remaining chunks are merged together using
214 /// parallel subdivision of chunks and parallel merge operation.
215 ///
216 /// # Examples
217 ///
218 /// ```
219 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
220 ///
221 /// let mut v = arr1(&[5, 4, 1, 3, 2]);
222 /// v.par_sort_by(|a, b| a.cmp(b));
223 /// assert!(v == arr1(&[1, 2, 3, 4, 5]));
224 ///
225 /// // reverse sorting
226 /// v.par_sort_by(|a, b| b.cmp(a));
227 /// assert!(v == arr1(&[5, 4, 3, 2, 1]));
228 /// ```
229 #[cfg(feature = "rayon")]
230 fn par_sort_by<F>(&mut self, compare: F)
231 where
232 A: Send,
233 F: Fn(&A, &A) -> Ordering + Sync,
234 S: DataMut;
235 /// Sorts the array in parallel with a key extraction function.
236 ///
237 /// This sort is stable (i.e., does not reorder equal elements) and *O*(*mn* log *n*)
238 /// worst-case, where the key function is *O*(*m*).
239 ///
240 /// For expensive key functions (e.g. functions that are not simple property accesses or
241 /// basic operations), [`par_sort_by_cached_key`](Slice1Ext::par_sort_by_cached_key) is likely to be
242 /// significantly faster, as it does not recompute element keys."
243 ///
244 /// When applicable, unstable sorting is preferred because it is generally faster than stable
245 /// sorting and it doesn't allocate auxiliary memory.
246 /// See [`par_sort_unstable_by_key`](Slice1Ext::par_sort_unstable_by_key).
247 ///
248 /// # Current Implementation
249 ///
250 /// The current algorithm is an adaptive, iterative merge sort inspired by
251 /// [timsort](https://en.wikipedia.org/wiki/Timsort).
252 /// It is designed to be very fast in cases where the array is nearly sorted, or consists of
253 /// two or more sorted sequences concatenated one after another.
254 ///
255 /// Also, it allocates temporary storage half the size of `self`, but for short arrays a
256 /// non-allocating insertion sort is used instead.
257 ///
258 /// In order to sort the array in parallel, the array is first divided into smaller chunks and
259 /// all chunks are sorted in parallel. Then, adjacent chunks that together form non-descending
260 /// or descending runs are concatenated. Finally, the remaining chunks are merged together using
261 /// parallel subdivision of chunks and parallel merge operation.
262 ///
263 /// # Examples
264 ///
265 /// ```
266 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
267 ///
268 /// let mut v = arr1(&[-5i32, 4, 2, -3, 1]);
269 ///
270 /// v.par_sort_by_key(|k| k.abs());
271 /// assert!(v == arr1(&[1, 2, -3, 4, -5]));
272 /// ```
273 #[cfg(feature = "rayon")]
274 fn par_sort_by_key<K, F>(&mut self, f: F)
275 where
276 A: Send,
277 K: Ord,
278 F: Fn(&A) -> K + Sync,
279 S: DataMut;
280 /// Sorts the array in parallel with a key extraction function.
281 ///
282 /// During sorting, the key function is called at most once per element, by using
283 /// temporary storage to remember the results of key evaluation.
284 /// The order of calls to the key function is unspecified and may change in future versions
285 /// of the standard library.
286 ///
287 /// This sort is stable (i.e., does not reorder equal elements) and *O*(*mn* + *n* log *n*)
288 /// worst-case, where the key function is *O*(*m*).
289 ///
290 /// For simple key functions (e.g., functions that are property accesses or
291 /// basic operations), [`par_sort_by_key`](Slice1Ext::par_sort_by_key) is likely to be
292 /// faster.
293 ///
294 /// # Current Implementation
295 ///
296 /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters,
297 /// which combines the fast average case of randomized quicksort with the fast worst case of
298 /// heapsort, while achieving linear time on arrays with certain patterns. It uses some
299 /// randomization to avoid degenerate cases, but with a fixed seed to always provide
300 /// deterministic behavior.
301 ///
302 /// In the worst case, the algorithm allocates temporary storage in a `Vec<(K, usize)>` the
303 /// length of the array.
304 ///
305 /// In order to sort the array in parallel, the array is first divided into smaller chunks and
306 /// all chunks are sorted in parallel. Then, adjacent chunks that together form non-descending
307 /// or descending runs are concatenated. Finally, the remaining chunks are merged together using
308 /// parallel subdivision of chunks and parallel merge operation.
309 ///
310 /// # Examples
311 ///
312 /// ```
313 /// # if !cfg!(miri) {
314 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
315 ///
316 /// let mut v = arr1(&[-5i32, 4, 32, -3, 2]);
317 ///
318 /// v.par_sort_by_cached_key(|k| k.to_string());
319 /// assert!(v == arr1(&[-3, -5, 2, 32, 4]));
320 /// # }
321 /// ```
322 ///
323 /// [pdqsort]: https://github.com/orlp/pdqsort
324 #[cfg(feature = "rayon")]
325 fn par_sort_by_cached_key<K, F>(&mut self, f: F)
326 where
327 A: Send + Sync,
328 F: Fn(&A) -> K + Sync,
329 K: Ord + Send,
330 S: DataMut;
331
332 /// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* log *n*) worst-case.
333 ///
334 /// When applicable, unstable sorting is preferred because it is generally faster than stable
335 /// sorting and it doesn't allocate auxiliary memory.
336 /// See [`sort_unstable`](Slice1Ext::sort_unstable).
337 ///
338 /// # Current Implementation
339 ///
340 /// The current algorithm is an adaptive, iterative merge sort inspired by
341 /// [timsort](https://en.wikipedia.org/wiki/Timsort).
342 /// It is designed to be very fast in cases where the array is nearly sorted, or consists of
343 /// two or more sorted sequences concatenated one after another.
344 ///
345 /// Also, it allocates temporary storage half the size of `self`, but for short arrays a
346 /// non-allocating insertion sort is used instead.
347 ///
348 /// # Examples
349 ///
350 /// ```
351 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
352 ///
353 /// let mut v = arr1(&[-5, 4, 1, -3, 2]);
354 ///
355 /// v.sort();
356 /// assert!(v == arr1(&[-5, -3, 1, 2, 4]));
357 /// ```
358 #[cfg(feature = "alloc")]
359 fn sort(&mut self)
360 where
361 A: Ord,
362 S: DataMut;
363 /// Sorts the array with a comparator function.
364 ///
365 /// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* log *n*) worst-case.
366 ///
367 /// The comparator function must define a total ordering for the elements in the array. If
368 /// the ordering is not total, the order of the elements is unspecified. An order is a
369 /// total order if it is (for all `a`, `b` and `c`):
370 ///
371 /// * total and antisymmetric: exactly one of `a < b`, `a == b` or `a > b` is true, and
372 /// * transitive, `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`.
373 ///
374 /// For example, while [`f64`] doesn't implement [`Ord`] because `NaN != NaN`, we can use
375 /// `partial_cmp` as our sort function when we know the array doesn't contain a `NaN`.
376 ///
377 /// ```
378 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
379 ///
380 /// let mut floats = arr1(&[5f64, 4.0, 1.0, 3.0, 2.0]);
381 /// floats.sort_by(|a, b| a.partial_cmp(b).unwrap());
382 /// assert_eq!(floats, arr1(&[1.0, 2.0, 3.0, 4.0, 5.0]));
383 /// ```
384 ///
385 /// When applicable, unstable sorting is preferred because it is generally faster than stable
386 /// sorting and it doesn't allocate auxiliary memory.
387 /// See [`sort_unstable_by`](Slice1Ext::sort_unstable_by).
388 ///
389 /// # Current Implementation
390 ///
391 /// The current algorithm is an adaptive, iterative merge sort inspired by
392 /// [timsort](https://en.wikipedia.org/wiki/Timsort).
393 /// It is designed to be very fast in cases where the array is nearly sorted, or consists of
394 /// two or more sorted sequences concatenated one after another.
395 ///
396 /// Also, it allocates temporary storage half the size of `self`, but for short arrays a
397 /// non-allocating insertion sort is used instead.
398 ///
399 /// # Examples
400 ///
401 /// ```
402 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
403 ///
404 /// let mut v = arr1(&[5, 4, 1, 3, 2]);
405 /// v.sort_by(|a, b| a.cmp(b));
406 /// assert!(v == arr1(&[1, 2, 3, 4, 5]));
407 ///
408 /// // reverse sorting
409 /// v.sort_by(|a, b| b.cmp(a));
410 /// assert!(v == arr1(&[5, 4, 3, 2, 1]));
411 /// ```
412 #[cfg(feature = "alloc")]
413 fn sort_by<F>(&mut self, compare: F)
414 where
415 F: FnMut(&A, &A) -> Ordering,
416 S: DataMut;
417 /// Sorts the array with a key extraction function.
418 ///
419 /// This sort is stable (i.e., does not reorder equal elements) and *O*(*mn* log *n*)
420 /// worst-case, where the key function is *O*(*m*).
421 ///
422 #[cfg_attr(
423 feature = "std",
424 doc = "\
425 For expensive key functions (e.g. functions that are not simple property accesses or
426 basic operations), [`sort_by_cached_key`](Slice1Ext::sort_by_cached_key) is likely to be
427 significantly faster, as it does not recompute element keys."
428 )]
429 ///
430 /// When applicable, unstable sorting is preferred because it is generally faster than stable
431 /// sorting and it doesn't allocate auxiliary memory.
432 /// See [`sort_unstable_by_key`](Slice1Ext::sort_unstable_by_key).
433 ///
434 /// # Current Implementation
435 ///
436 /// The current algorithm is an adaptive, iterative merge sort inspired by
437 /// [timsort](https://en.wikipedia.org/wiki/Timsort).
438 /// It is designed to be very fast in cases where the array is nearly sorted, or consists of
439 /// two or more sorted sequences concatenated one after another.
440 ///
441 /// Also, it allocates temporary storage half the size of `self`, but for short arrays a
442 /// non-allocating insertion sort is used instead.
443 ///
444 /// # Examples
445 ///
446 /// ```
447 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
448 ///
449 /// let mut v = arr1(&[-5i32, 4, 2, -3, 1]);
450 ///
451 /// v.sort_by_key(|k| k.abs());
452 /// assert!(v == arr1(&[1, 2, -3, 4, -5]));
453 /// ```
454 #[cfg(feature = "alloc")]
455 fn sort_by_key<K, F>(&mut self, f: F)
456 where
457 K: Ord,
458 F: FnMut(&A) -> K,
459 S: DataMut;
460
461 /// Sorts the array with a key extraction function.
462 ///
463 /// During sorting, the key function is called at most once per element, by using
464 /// temporary storage to remember the results of key evaluation.
465 /// The order of calls to the key function is unspecified and may change in future versions
466 /// of the standard library.
467 ///
468 /// This sort is stable (i.e., does not reorder equal elements) and *O*(*mn* + *n* log *n*)
469 /// worst-case, where the key function is *O*(*m*).
470 ///
471 /// For simple key functions (e.g., functions that are property accesses or
472 /// basic operations), [`sort_by_key`](Slice1Ext::sort_by_key) is likely to be
473 /// faster.
474 ///
475 /// # Current Implementation
476 ///
477 /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters,
478 /// which combines the fast average case of randomized quicksort with the fast worst case of
479 /// heapsort, while achieving linear time on arrays with certain patterns. It uses some
480 /// randomization to avoid degenerate cases, but with a fixed seed to always provide
481 /// deterministic behavior.
482 ///
483 /// In the worst case, the algorithm allocates temporary storage in a `Vec<(K, usize)>` the
484 /// length of the array.
485 ///
486 /// # Examples
487 ///
488 /// ```
489 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
490 ///
491 /// let mut v = arr1(&[-5i32, 4, 32, -3, 2]);
492 ///
493 /// v.sort_by_cached_key(|k| k.to_string());
494 /// assert!(v == arr1(&[-3, -5, 2, 32, 4]));
495 /// ```
496 ///
497 /// [pdqsort]: https://github.com/orlp/pdqsort
498 #[cfg(feature = "std")]
499 fn sort_by_cached_key<K, F>(&mut self, f: F)
500 where
501 F: FnMut(&A) -> K,
502 K: Ord,
503 S: DataMut;
504
505 /// Sorts the array in parallel, but might not preserve the order of equal elements.
506 ///
507 /// This sort is unstable (i.e., may reorder equal elements), in-place
508 /// (i.e., does not allocate), and *O*(*n* log *n*) worst-case.
509 ///
510 /// # Current Implementation
511 ///
512 /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters,
513 /// which combines the fast average case of randomized quicksort with the fast worst case of
514 /// heapsort, while achieving linear time on arrays with certain patterns. It uses some
515 /// randomization to avoid degenerate cases, but with a fixed seed to always provide
516 /// deterministic behavior.
517 ///
518 /// It is typically faster than stable sorting, except in a few special cases, e.g., when the
519 /// array consists of several concatenated sorted sequences.
520 ///
521 /// All quicksorts work in two stages: partitioning into two halves followed by recursive
522 /// calls. The partitioning phase is sequential, but the two recursive calls are performed in
523 /// parallel.
524 ///
525 /// # Examples
526 ///
527 /// ```
528 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
529 ///
530 /// let mut v = arr1(&[-5, 4, 1, -3, 2]);
531 ///
532 /// v.par_sort_unstable();
533 /// assert!(v == arr1(&[-5, -3, 1, 2, 4]));
534 /// ```
535 ///
536 /// [pdqsort]: https://github.com/orlp/pdqsort
537 #[cfg(feature = "rayon")]
538 fn par_sort_unstable(&mut self)
539 where
540 A: Ord + Send,
541 S: DataMut;
542 /// Sorts the array in parallel with a comparator function, but might not preserve the order of equal
543 /// elements.
544 ///
545 /// This sort is unstable (i.e., may reorder equal elements), in-place
546 /// (i.e., does not allocate), and *O*(*n* log *n*) worst-case.
547 ///
548 /// The comparator function must define a total ordering for the elements in the array. If
549 /// the ordering is not total, the order of the elements is unspecified. An order is a
550 /// total order if it is (for all `a`, `b` and `c`):
551 ///
552 /// * total and antisymmetric: exactly one of `a < b`, `a == b` or `a > b` is true, and
553 /// * transitive, `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`.
554 ///
555 /// For example, while [`f64`] doesn't implement [`Ord`] because `NaN != NaN`, we can use
556 /// `partial_cmp` as our sort function when we know the array doesn't contain a `NaN`.
557 ///
558 /// ```
559 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
560 ///
561 /// let mut floats = arr1(&[5f64, 4.0, 1.0, 3.0, 2.0]);
562 /// floats.par_sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
563 /// assert_eq!(floats, arr1(&[1.0, 2.0, 3.0, 4.0, 5.0]));
564 /// ```
565 ///
566 /// # Current Implementation
567 ///
568 /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters,
569 /// which combines the fast average case of randomized quicksort with the fast worst case of
570 /// heapsort, while achieving linear time on arrays with certain patterns. It uses some
571 /// randomization to avoid degenerate cases, but with a fixed seed to always provide
572 /// deterministic behavior.
573 ///
574 /// It is typically faster than stable sorting, except in a few special cases, e.g., when the
575 /// array consists of several concatenated sorted sequences.
576 ///
577 /// All quicksorts work in two stages: partitioning into two halves followed by recursive
578 /// calls. The partitioning phase is sequential, but the two recursive calls are performed in
579 /// parallel.
580 ///
581 /// # Examples
582 ///
583 /// ```
584 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
585 ///
586 /// let mut v = arr1(&[5, 4, 1, 3, 2]);
587 /// v.par_sort_unstable_by(|a, b| a.cmp(b));
588 /// assert!(v == arr1(&[1, 2, 3, 4, 5]));
589 ///
590 /// // reverse sorting
591 /// v.par_sort_unstable_by(|a, b| b.cmp(a));
592 /// assert!(v == arr1(&[5, 4, 3, 2, 1]));
593 /// ```
594 ///
595 /// [pdqsort]: https://github.com/orlp/pdqsort
596 #[cfg(feature = "rayon")]
597 fn par_sort_unstable_by<F>(&mut self, compare: F)
598 where
599 A: Send,
600 F: Fn(&A, &A) -> Ordering + Sync,
601 S: DataMut;
602 /// Sorts the array in parallel with a key extraction function, but might not preserve the order of equal
603 /// elements.
604 ///
605 /// This sort is unstable (i.e., may reorder equal elements), in-place
606 /// (i.e., does not allocate), and *O*(*mn* log *n*) worst-case, where the key function is
607 /// *O*(*m*).
608 ///
609 /// # Current Implementation
610 ///
611 /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters,
612 /// which combines the fast average case of randomized quicksort with the fast worst case of
613 /// heapsort, while achieving linear time on arrays with certain patterns. It uses some
614 /// randomization to avoid degenerate cases, but with a fixed seed to always provide
615 /// deterministic behavior.
616 ///
617 /// Due to its key calling strategy, [`par_sort_unstable_by_key`](#method.par_sort_unstable_by_key)
618 /// is likely to be slower than [`par_sort_by_cached_key`](#method.par_sort_by_cached_key) in
619 /// cases where the key function is expensive.
620 ///
621 /// All quicksorts work in two stages: partitioning into two halves followed by recursive
622 /// calls. The partitioning phase is sequential, but the two recursive calls are performed in
623 /// parallel.
624 ///
625 /// # Examples
626 ///
627 /// ```
628 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
629 ///
630 /// let mut v = arr1(&[-5i32, 4, 2, -3, 1]);
631 ///
632 /// v.par_sort_unstable_by_key(|k| k.abs());
633 /// assert!(v == arr1(&[1, 2, -3, 4, -5]));
634 /// ```
635 ///
636 /// [pdqsort]: https://github.com/orlp/pdqsort
637 #[cfg(feature = "rayon")]
638 fn par_sort_unstable_by_key<K, F>(&mut self, f: F)
639 where
640 A: Send,
641 K: Ord,
642 F: Fn(&A) -> K + Sync,
643 S: DataMut;
644
645 /// Sorts the array, but might not preserve the order of equal elements.
646 ///
647 /// This sort is unstable (i.e., may reorder equal elements), in-place
648 /// (i.e., does not allocate), and *O*(*n* log *n*) worst-case.
649 ///
650 /// # Current Implementation
651 ///
652 /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters,
653 /// which combines the fast average case of randomized quicksort with the fast worst case of
654 /// heapsort, while achieving linear time on arrays with certain patterns. It uses some
655 /// randomization to avoid degenerate cases, but with a fixed seed to always provide
656 /// deterministic behavior.
657 ///
658 /// It is typically faster than stable sorting, except in a few special cases, e.g., when the
659 /// array consists of several concatenated sorted sequences.
660 ///
661 /// # Examples
662 ///
663 /// ```
664 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
665 ///
666 /// let mut v = arr1(&[-5, 4, 1, -3, 2]);
667 ///
668 /// v.sort_unstable();
669 /// assert!(v == arr1(&[-5, -3, 1, 2, 4]));
670 /// ```
671 ///
672 /// [pdqsort]: https://github.com/orlp/pdqsort
673 fn sort_unstable(&mut self)
674 where
675 A: Ord,
676 S: DataMut;
677 /// Sorts the array with a comparator function, but might not preserve the order of equal
678 /// elements.
679 ///
680 /// This sort is unstable (i.e., may reorder equal elements), in-place
681 /// (i.e., does not allocate), and *O*(*n* log *n*) worst-case.
682 ///
683 /// The comparator function must define a total ordering for the elements in the array. If
684 /// the ordering is not total, the order of the elements is unspecified. An order is a
685 /// total order if it is (for all `a`, `b` and `c`):
686 ///
687 /// * total and antisymmetric: exactly one of `a < b`, `a == b` or `a > b` is true, and
688 /// * transitive, `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`.
689 ///
690 /// For example, while [`f64`] doesn't implement [`Ord`] because `NaN != NaN`, we can use
691 /// `partial_cmp` as our sort function when we know the array doesn't contain a `NaN`.
692 ///
693 /// ```
694 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
695 ///
696 /// let mut floats = arr1(&[5f64, 4.0, 1.0, 3.0, 2.0]);
697 /// floats.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
698 /// assert_eq!(floats, arr1(&[1.0, 2.0, 3.0, 4.0, 5.0]));
699 /// ```
700 ///
701 /// # Current Implementation
702 ///
703 /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters,
704 /// which combines the fast average case of randomized quicksort with the fast worst case of
705 /// heapsort, while achieving linear time on arrays with certain patterns. It uses some
706 /// randomization to avoid degenerate cases, but with a fixed seed to always provide
707 /// deterministic behavior.
708 ///
709 /// It is typically faster than stable sorting, except in a few special cases, e.g., when the
710 /// array consists of several concatenated sorted sequences.
711 ///
712 /// # Examples
713 ///
714 /// ```
715 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
716 ///
717 /// let mut v = arr1(&[5, 4, 1, 3, 2]);
718 /// v.sort_unstable_by(|a, b| a.cmp(b));
719 /// assert!(v == arr1(&[1, 2, 3, 4, 5]));
720 ///
721 /// // reverse sorting
722 /// v.sort_unstable_by(|a, b| b.cmp(a));
723 /// assert!(v == arr1(&[5, 4, 3, 2, 1]));
724 /// ```
725 ///
726 /// [pdqsort]: https://github.com/orlp/pdqsort
727 fn sort_unstable_by<F>(&mut self, compare: F)
728 where
729 F: FnMut(&A, &A) -> Ordering,
730 S: DataMut;
731 /// Sorts the array with a key extraction function, but might not preserve the order of equal
732 /// elements.
733 ///
734 /// This sort is unstable (i.e., may reorder equal elements), in-place
735 /// (i.e., does not allocate), and *O*(*mn* log *n*) worst-case, where the key function is
736 /// *O*(*m*).
737 ///
738 /// # Current Implementation
739 ///
740 /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters,
741 /// which combines the fast average case of randomized quicksort with the fast worst case of
742 /// heapsort, while achieving linear time on arrays with certain patterns. It uses some
743 /// randomization to avoid degenerate cases, but with a fixed seed to always provide
744 /// deterministic behavior.
745 ///
746 /// Due to its key calling strategy, [`sort_unstable_by_key`](#method.sort_unstable_by_key)
747 /// is likely to be slower than [`sort_by_cached_key`](#method.sort_by_cached_key) in
748 /// cases where the key function is expensive.
749 ///
750 /// # Examples
751 ///
752 /// ```
753 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
754 ///
755 /// let mut v = arr1(&[-5i32, 4, 2, -3, 1]);
756 ///
757 /// v.sort_unstable_by_key(|k| k.abs());
758 /// assert!(v == arr1(&[1, 2, -3, 4, -5]));
759 /// ```
760 ///
761 /// [pdqsort]: https://github.com/orlp/pdqsort
762 fn sort_unstable_by_key<K, F>(&mut self, f: F)
763 where
764 K: Ord,
765 F: FnMut(&A) -> K,
766 S: DataMut;
767
768 /// Checks if the elements of this array are sorted.
769 ///
770 /// That is, for each element `a` and its following element `b`, `a <= b` must hold. If the
771 /// array yields exactly zero or one element, `true` is returned.
772 ///
773 /// Note that if `Self::Item` is only `PartialOrd`, but not `Ord`, the above definition
774 /// implies that this function returns `false` if any two consecutive items are not
775 /// comparable.
776 ///
777 /// # Examples
778 ///
779 /// ```
780 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
781 ///
782 /// let empty: [i32; 0] = [];
783 ///
784 /// assert!(arr1(&[1, 2, 2, 9]).is_sorted());
785 /// assert!(!arr1(&[1, 3, 2, 4]).is_sorted());
786 /// assert!(arr1(&[0]).is_sorted());
787 /// assert!(arr1(&empty).is_sorted());
788 /// assert!(!arr1(&[0.0, 1.0, f32::NAN]).is_sorted());
789 /// ```
790 #[must_use]
791 fn is_sorted(&self) -> bool
792 where
793 A: PartialOrd;
794 /// Checks if the elements of this array are sorted using the given comparator function.
795 ///
796 /// Instead of using `PartialOrd::partial_cmp`, this function uses the given `compare`
797 /// function to determine whether two elements are to be considered in sorted order.
798 ///
799 /// # Examples
800 ///
801 /// ```
802 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
803 ///
804 /// assert!(arr1(&[1, 2, 2, 9]).is_sorted_by(|a, b| a <= b));
805 /// assert!(!arr1(&[1, 2, 2, 9]).is_sorted_by(|a, b| a < b));
806 ///
807 /// assert!(arr1(&[0]).is_sorted_by(|a, b| true));
808 /// assert!(arr1(&[0]).is_sorted_by(|a, b| false));
809 ///
810 /// let empty: [i32; 0] = [];
811 /// assert!(arr1(&empty).is_sorted_by(|a, b| false));
812 /// assert!(arr1(&empty).is_sorted_by(|a, b| true));
813 /// ```
814 #[must_use]
815 fn is_sorted_by<F>(&self, compare: F) -> bool
816 where
817 F: FnMut(&A, &A) -> bool;
818 /// Checks if the elements of this array are sorted using the given key extraction function.
819 ///
820 /// Instead of comparing the array's elements directly, this function compares the keys of the
821 /// elements, as determined by `f`. Apart from that, it's equivalent to [`is_sorted`]; see its
822 /// documentation for more information.
823 ///
824 /// [`is_sorted`]: Slice1Ext::is_sorted
825 ///
826 /// # Examples
827 ///
828 /// ```
829 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
830 ///
831 /// assert!(arr1(&["c", "bb", "aaa"]).is_sorted_by_key(|s| s.len()));
832 /// assert!(!arr1(&[-2i32, -1, 0, 3]).is_sorted_by_key(|n| n.abs()));
833 /// ```
834 #[must_use]
835 fn is_sorted_by_key<F, K>(&self, f: F) -> bool
836 where
837 F: FnMut(&A) -> K,
838 K: PartialOrd;
839
840 /// Reorder the array in parallel such that the elements at `indices` are at their final sorted position.
841 ///
842 /// Bulk version of [`select_nth_unstable`] extending `collection` with `&mut element`
843 /// in the order of `indices`. The provided `indices` must be sorted and unique which can be
844 /// achieved with [`par_sort_unstable`] followed by [`partition_dedup`].
845 ///
846 /// # Current Implementation
847 ///
848 /// The current algorithm chooses `at = indices.len() / 2` as pivot index and recurses in parallel into the
849 /// left and right subviews of `indices` (i.e., `..at` and `at + 1..`) with corresponding left
850 /// and right subviews of `self` (i.e., `..pivot` and `pivot + 1..`) where `pivot = indices[at]`.
851 /// Requiring `indices` to be already sorted, reduces the time complexity in the length *m* of
852 /// `indices` from *O*(*m*) to *O*(log *m*) compared to invoking [`select_nth_unstable`] on the
853 /// full view of `self` for each index.
854 ///
855 /// # Panics
856 ///
857 /// Panics when any `indices[i] >= len()`, meaning it always panics on empty arrays. Panics
858 /// when `indices` is unsorted or contains duplicates.
859 ///
860 /// [`par_sort_unstable`]: Slice1Ext::par_sort_unstable
861 /// [`partition_dedup`]: Slice1Ext::partition_dedup
862 /// [`select_nth_unstable`]: Slice1Ext::select_nth_unstable
863 ///
864 /// # Examples
865 ///
866 /// ```
867 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
868 ///
869 /// let mut v = arr1(&[-5i32, 4, 2, -3, 1, 9, 3, 4, 0]);
870 ///
871 /// // Find values at following indices.
872 /// let indices = arr1(&[1, 4, 6]);
873 ///
874 /// let mut values = Vec::new();
875 /// v.par_select_many_nth_unstable(&indices, &mut values);
876 ///
877 /// assert!(values == [&-3, &2, &4]);
878 /// ```
879 #[cfg(feature = "rayon")]
880 fn par_select_many_nth_unstable<'a, S2>(
881 &'a mut self,
882 indices: &ArrayBase<S2, Ix1>,
883 collection: &mut Vec<&'a mut A>,
884 ) where
885 A: Ord + Send,
886 S: DataMut,
887 S2: Data<Elem = usize> + Sync;
888 /// Reorder the array in parallel with a comparator function such that the elements at `indices` are at
889 /// their final sorted position.
890 ///
891 /// Bulk version of [`select_nth_unstable_by`] extending `collection` with `&mut element`
892 /// in the order of `indices`. The provided `indices` must be sorted and unique which can be
893 /// achieved with [`par_sort_unstable`] followed by [`partition_dedup`].
894 ///
895 /// # Current Implementation
896 ///
897 /// The current algorithm chooses `at = indices.len() / 2` as pivot index and recurses in parallel into the
898 /// left and right subviews of `indices` (i.e., `..at` and `at + 1..`) with corresponding left
899 /// and right subviews of `self` (i.e., `..pivot` and `pivot + 1..`) where `pivot = indices[at]`.
900 /// Requiring `indices` to be already sorted, reduces the time complexity in the length *m* of
901 /// `indices` from *O*(*m*) to *O*(log *m*) compared to invoking [`select_nth_unstable_by`] on the
902 /// full view of `self` for each index.
903 ///
904 /// # Panics
905 ///
906 /// Panics when any `indices[i] >= len()`, meaning it always panics on empty arrays. Panics
907 /// when `indices` is unsorted or contains duplicates.
908 ///
909 /// [`par_sort_unstable`]: Slice1Ext::par_sort_unstable
910 /// [`partition_dedup`]: Slice1Ext::partition_dedup
911 /// [`select_nth_unstable_by`]: Slice1Ext::select_nth_unstable_by
912 ///
913 /// # Examples
914 ///
915 /// ```
916 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
917 /// use std::collections::HashMap;
918 ///
919 /// let mut v = arr1(&[-5i32, 4, 2, -3, 1, 9, 3, 4, 0]);
920 ///
921 /// // Find values at following indices.
922 /// let indices = arr1(&[1, 4, 6]);
923 ///
924 /// let mut values = Vec::new();
925 /// v.par_select_many_nth_unstable_by(&indices, &mut values, |a, b| b.cmp(a));
926 ///
927 /// assert!(values == [&4, &2, &0]);
928 /// ```
929 #[cfg(feature = "rayon")]
930 fn par_select_many_nth_unstable_by<'a, F, S2>(
931 &'a mut self,
932 indices: &ArrayBase<S2, Ix1>,
933 collection: &mut Vec<&'a mut A>,
934 compare: F,
935 ) where
936 A: Send,
937 F: Fn(&A, &A) -> Ordering + Sync,
938 S: DataMut,
939 S2: Data<Elem = usize> + Sync;
940 /// Reorder the array in parallel with a key extraction function such that the elements at `indices` are at
941 /// their final sorted position.
942 ///
943 /// Bulk version of [`select_nth_unstable_by_key`] extending `collection` with `&mut element`
944 /// in the order of `indices`. The provided `indices` must be sorted and unique which can be
945 /// achieved with [`par_sort_unstable`] followed by [`partition_dedup`].
946 ///
947 /// # Current Implementation
948 ///
949 /// The current algorithm chooses `at = indices.len() / 2` as pivot index and recurses in parallel into the
950 /// left and right subviews of `indices` (i.e., `..at` and `at + 1..`) with corresponding left
951 /// and right subviews of `self` (i.e., `..pivot` and `pivot + 1..`) where `pivot = indices[at]`.
952 /// Requiring `indices` to be already sorted, reduces the time complexity in the length *m* of
953 /// `indices` from *O*(*m*) to *O*(log *m*) compared to invoking [`select_nth_unstable_by_key`] on the
954 /// full view of `self` for each index.
955 ///
956 /// # Panics
957 ///
958 /// Panics when any `indices[i] >= len()`, meaning it always panics on empty arrays. Panics
959 /// when `indices` is unsorted or contains duplicates.
960 ///
961 /// [`par_sort_unstable`]: Slice1Ext::par_sort_unstable
962 /// [`partition_dedup`]: Slice1Ext::partition_dedup
963 /// [`select_nth_unstable_by_key`]: Slice1Ext::select_nth_unstable_by_key
964 ///
965 /// # Examples
966 ///
967 /// ```
968 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
969 /// use std::collections::HashMap;
970 ///
971 /// let mut v = arr1(&[-5i32, 4, 2, -3, 1, 9, 3, 4, 0]);
972 ///
973 /// // Find values at following indices.
974 /// let indices = arr1(&[1, 4, 6]);
975 ///
976 /// let mut values = Vec::new();
977 /// v.par_select_many_nth_unstable_by_key(&indices, &mut values, |&a| a.abs());
978 ///
979 /// assert!(values == [&1, &3, &4]);
980 /// ```
981 #[cfg(feature = "rayon")]
982 fn par_select_many_nth_unstable_by_key<'a, K, F, S2>(
983 &'a mut self,
984 indices: &ArrayBase<S2, Ix1>,
985 collection: &mut Vec<&'a mut A>,
986 f: F,
987 ) where
988 A: Send,
989 K: Ord,
990 F: Fn(&A) -> K + Sync,
991 S: DataMut,
992 S2: Data<Elem = usize> + Sync;
993
994 /// Reorder the array such that the elements at `indices` are at their final sorted position.
995 ///
996 /// Bulk version of [`select_nth_unstable`] extending `collection` with `(index, &mut element)`
997 /// tuples in the order of `indices`. The provided `indices` must be sorted and unique which can
998 /// be achieved with [`sort_unstable`] followed by [`partition_dedup`].
999 ///
1000 /// # Current Implementation
1001 ///
1002 /// The current algorithm chooses `at = indices.len() / 2` as pivot index and recurses into the
1003 /// left and right subviews of `indices` (i.e., `..at` and `at + 1..`) with corresponding left
1004 /// and right subviews of `self` (i.e., `..pivot` and `pivot + 1..`) where `pivot = indices[at]`.
1005 /// Requiring `indices` to be already sorted, reduces the time complexity in the length *m* of
1006 /// `indices` from *O*(*m*) to *O*(log *m*) compared to invoking [`select_nth_unstable`] on the
1007 /// full view of `self` for each index.
1008 ///
1009 /// # Panics
1010 ///
1011 /// Panics when any `indices[i] >= len()`, meaning it always panics on empty arrays. Panics
1012 /// when `indices` is unsorted or contains duplicates.
1013 ///
1014 /// [`sort_unstable`]: Slice1Ext::sort_unstable
1015 /// [`partition_dedup`]: Slice1Ext::partition_dedup
1016 /// [`select_nth_unstable`]: Slice1Ext::select_nth_unstable
1017 ///
1018 /// # Examples
1019 ///
1020 /// ```
1021 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
1022 /// use std::collections::HashMap;
1023 ///
1024 /// let mut v = arr1(&[-5i32, 4, 2, -3, 1, 9, 3, 4, 0]);
1025 ///
1026 /// // Find values at following indices.
1027 /// let indices = arr1(&[1, 4, 6]);
1028 ///
1029 /// let mut map = HashMap::new();
1030 /// v.select_many_nth_unstable(&indices, &mut map);
1031 /// let values = indices.map(|index| *map[index]);
1032 ///
1033 /// assert!(values == arr1(&[-3, 2, 4]));
1034 /// ```
1035 fn select_many_nth_unstable<'a, E, S2>(
1036 &'a mut self,
1037 indices: &ArrayBase<S2, Ix1>,
1038 collection: &mut E,
1039 ) where
1040 A: Ord + 'a,
1041 E: Extend<(usize, &'a mut A)>,
1042 S: DataMut,
1043 S2: Data<Elem = usize>;
1044 /// Reorder the array with a comparator function such that the elements at `indices` are at
1045 /// their final sorted position.
1046 ///
1047 /// Bulk version of [`select_nth_unstable_by`] extending `collection` with `(index, &mut element)`
1048 /// tuples in the order of `indices`. The provided `indices` must be sorted and unique which can
1049 /// be achieved with [`sort_unstable`] followed by [`partition_dedup`].
1050 ///
1051 /// # Current Implementation
1052 ///
1053 /// The current algorithm chooses `at = indices.len() / 2` as pivot index and recurses into the
1054 /// left and right subviews of `indices` (i.e., `..at` and `at + 1..`) with corresponding left
1055 /// and right subviews of `self` (i.e., `..pivot` and `pivot + 1..`) where `pivot = indices[at]`.
1056 /// Requiring `indices` to be already sorted, reduces the time complexity in the length *m* of
1057 /// `indices` from *O*(*m*) to *O*(log *m*) compared to invoking [`select_nth_unstable_by`] on the
1058 /// full view of `self` for each index.
1059 ///
1060 /// # Panics
1061 ///
1062 /// Panics when any `indices[i] >= len()`, meaning it always panics on empty arrays. Panics
1063 /// when `indices` is unsorted or contains duplicates.
1064 ///
1065 /// [`sort_unstable`]: Slice1Ext::sort_unstable
1066 /// [`partition_dedup`]: Slice1Ext::partition_dedup
1067 /// [`select_nth_unstable_by`]: Slice1Ext::select_nth_unstable_by
1068 ///
1069 /// # Examples
1070 ///
1071 /// ```
1072 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
1073 /// use std::collections::HashMap;
1074 ///
1075 /// let mut v = arr1(&[-5i32, 4, 2, -3, 1, 9, 3, 4, 0]);
1076 ///
1077 /// // Find values at following indices.
1078 /// let indices = arr1(&[1, 4, 6]);
1079 ///
1080 /// let mut map = HashMap::new();
1081 /// v.select_many_nth_unstable_by(&indices, &mut map, |a, b| b.cmp(a));
1082 /// let values = indices.map(|index| *map[index]);
1083 ///
1084 /// assert!(values == arr1(&[4, 2, 0]));
1085 /// ```
1086 fn select_many_nth_unstable_by<'a, E, F, S2>(
1087 &'a mut self,
1088 indices: &ArrayBase<S2, Ix1>,
1089 collection: &mut E,
1090 compare: F,
1091 ) where
1092 A: 'a,
1093 E: Extend<(usize, &'a mut A)>,
1094 F: FnMut(&A, &A) -> Ordering,
1095 S: DataMut,
1096 S2: Data<Elem = usize>;
1097 /// Reorder the array with a key extraction function such that the elements at `indices` are at
1098 /// their final sorted position.
1099 ///
1100 /// Bulk version of [`select_nth_unstable_by_key`] extending `collection` with `(index, &mut element)`
1101 /// tuples in the order of `indices`. The provided `indices` must be sorted and unique which can
1102 /// be achieved with [`sort_unstable`] followed by [`partition_dedup`].
1103 ///
1104 /// # Current Implementation
1105 ///
1106 /// The current algorithm chooses `at = indices.len() / 2` as pivot index and recurses into the
1107 /// left and right subviews of `indices` (i.e., `..at` and `at + 1..`) with corresponding left
1108 /// and right subviews of `self` (i.e., `..pivot` and `pivot + 1..`) where `pivot = indices[at]`.
1109 /// Requiring `indices` to be already sorted, reduces the time complexity in the length *m* of
1110 /// `indices` from *O*(*m*) to *O*(log *m*) compared to invoking [`select_nth_unstable_by_key`] on the
1111 /// full view of `self` for each index.
1112 ///
1113 /// # Panics
1114 ///
1115 /// Panics when any `indices[i] >= len()`, meaning it always panics on empty arrays. Panics
1116 /// when `indices` is unsorted or contains duplicates.
1117 ///
1118 /// [`sort_unstable`]: Slice1Ext::sort_unstable
1119 /// [`partition_dedup`]: Slice1Ext::partition_dedup
1120 /// [`select_nth_unstable_by_key`]: Slice1Ext::select_nth_unstable_by_key
1121 ///
1122 /// # Examples
1123 ///
1124 /// ```
1125 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
1126 /// use std::collections::HashMap;
1127 ///
1128 /// let mut v = arr1(&[-5i32, 4, 2, -3, 1, 9, 3, 4, 0]);
1129 ///
1130 /// // Find values at following indices.
1131 /// let indices = arr1(&[1, 4, 6]);
1132 ///
1133 /// let mut map = HashMap::new();
1134 /// v.select_many_nth_unstable_by_key(&indices, &mut map, |&a| a.abs());
1135 /// let values = indices.map(|index| *map[index]);
1136 ///
1137 /// assert!(values == arr1(&[1, 3, 4]));
1138 /// ```
1139 fn select_many_nth_unstable_by_key<'a, E, K, F, S2>(
1140 &'a mut self,
1141 indices: &ArrayBase<S2, Ix1>,
1142 collection: &mut E,
1143 f: F,
1144 ) where
1145 A: 'a,
1146 E: Extend<(usize, &'a mut A)>,
1147 K: Ord,
1148 F: FnMut(&A) -> K,
1149 S: DataMut,
1150 S2: Data<Elem = usize>;
1151 /// Reorder the array such that the element at `index` after the reordering is at its final sorted position.
1152 ///
1153 /// This reordering has the additional property that any value at position `i < index` will be
1154 /// less than or equal to any value at a position `j > index`. Additionally, this reordering is
1155 /// unstable (i.e. any number of equal elements may end up at position `index`), in-place
1156 /// (i.e. does not allocate), and runs in *O*(*n*) time.
1157 /// This function is also known as "kth element" in other libraries.
1158 ///
1159 /// It returns a triplet of the following from the reordered array:
1160 /// the subarray prior to `index`, the element at `index`, and the subarray after `index`;
1161 /// accordingly, the values in those two subarrays will respectively all be less-than-or-equal-to
1162 /// and greater-than-or-equal-to the value of the element at `index`.
1163 ///
1164 /// # Current Implementation
1165 ///
1166 /// The current algorithm is an introselect implementation based on Pattern Defeating Quicksort, which is also
1167 /// the basis for [`sort_unstable`]. The fallback algorithm is Median of Medians using Tukey's Ninther for
1168 /// pivot selection, which guarantees linear runtime for all inputs.
1169 ///
1170 /// [`sort_unstable`]: Slice1Ext::sort_unstable
1171 ///
1172 /// # Panics
1173 ///
1174 /// Panics when `index >= len()`, meaning it always panics on empty arrays.
1175 ///
1176 /// # Examples
1177 ///
1178 /// ```
1179 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
1180 ///
1181 /// let mut v = arr1(&[-5i32, 4, 2, -3, 1]);
1182 ///
1183 /// // Find the items less than or equal to the median, the median, and greater than or equal to
1184 /// // the median.
1185 /// let (lesser, median, greater) = v.select_nth_unstable(2);
1186 ///
1187 /// assert!(lesser == arr1(&[-3, -5]) || lesser == arr1(&[-5, -3]));
1188 /// assert_eq!(median, &mut 1);
1189 /// assert!(greater == arr1(&[4, 2]) || greater == arr1(&[2, 4]));
1190 ///
1191 /// // We are only guaranteed the array will be one of the following, based on the way we sort
1192 /// // about the specified index.
1193 /// assert!(v == arr1(&[-3, -5, 1, 2, 4]) ||
1194 /// v == arr1(&[-5, -3, 1, 2, 4]) ||
1195 /// v == arr1(&[-3, -5, 1, 4, 2]) ||
1196 /// v == arr1(&[-5, -3, 1, 4, 2]));
1197 /// ```
1198 #[must_use]
1199 fn select_nth_unstable(
1200 &mut self,
1201 index: usize,
1202 ) -> (ArrayViewMut1<'_, A>, &mut A, ArrayViewMut1<'_, A>)
1203 where
1204 A: Ord,
1205 S: DataMut;
1206 /// Reorder the array with a comparator function such that the element at `index` after the reordering is at
1207 /// its final sorted position.
1208 ///
1209 /// This reordering has the additional property that any value at position `i < index` will be
1210 /// less than or equal to any value at a position `j > index` using the comparator function.
1211 /// Additionally, this reordering is unstable (i.e. any number of equal elements may end up at
1212 /// position `index`), in-place (i.e. does not allocate), and runs in *O*(*n*) time.
1213 /// This function is also known as "kth element" in other libraries.
1214 ///
1215 /// It returns a triplet of the following from
1216 /// the array reordered according to the provided comparator function: the subarray prior to
1217 /// `index`, the element at `index`, and the subarray after `index`; accordingly, the values in
1218 /// those two subarrays will respectively all be less-than-or-equal-to and greater-than-or-equal-to
1219 /// the value of the element at `index`.
1220 ///
1221 /// # Current Implementation
1222 ///
1223 /// The current algorithm is an introselect implementation based on Pattern Defeating Quicksort, which is also
1224 /// the basis for [`sort_unstable`]. The fallback algorithm is Median of Medians using Tukey's Ninther for
1225 /// pivot selection, which guarantees linear runtime for all inputs.
1226 ///
1227 /// [`sort_unstable`]: Slice1Ext::sort_unstable
1228 ///
1229 /// # Panics
1230 ///
1231 /// Panics when `index >= len()`, meaning it always panics on empty arrays.
1232 ///
1233 /// # Examples
1234 ///
1235 /// ```
1236 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
1237 ///
1238 /// let mut v = arr1(&[-5i32, 4, 2, -3, 1]);
1239 ///
1240 /// // Find the median as if the array were sorted in descending order.
1241 /// v.select_nth_unstable_by(2, |a, b| b.cmp(a));
1242 /// // Find the items less than or equal to the median, the median, and greater than or equal to
1243 /// // the median as if the slice were sorted in descending order.
1244 /// let (lesser, median, greater) = v.select_nth_unstable_by(2, |a, b| b.cmp(a));
1245 ///
1246 /// assert!(lesser == arr1(&[4, 2]) || lesser == arr1(&[2, 4]));
1247 /// assert_eq!(median, &mut 1);
1248 /// assert!(greater == arr1(&[-3, -5]) || greater == arr1(&[-5, -3]));
1249 ///
1250 /// // We are only guaranteed the array will be one of the following, based on the way we sort
1251 /// // about the specified index.
1252 /// assert!(v == arr1(&[2, 4, 1, -5, -3]) ||
1253 /// v == arr1(&[2, 4, 1, -3, -5]) ||
1254 /// v == arr1(&[4, 2, 1, -5, -3]) ||
1255 /// v == arr1(&[4, 2, 1, -3, -5]));
1256 /// ```
1257 #[must_use]
1258 fn select_nth_unstable_by<F>(
1259 &mut self,
1260 index: usize,
1261 compare: F,
1262 ) -> (ArrayViewMut1<'_, A>, &mut A, ArrayViewMut1<'_, A>)
1263 where
1264 F: FnMut(&A, &A) -> Ordering,
1265 S: DataMut;
1266 /// Reorder the array with a key extraction function such that the element at `index` after the reordering is
1267 /// at its final sorted position.
1268 ///
1269 /// This reordering has the additional property that any value at position `i < index` will be
1270 /// less than or equal to any value at a position `j > index` using the key extraction function.
1271 /// Additionally, this reordering is unstable (i.e. any number of equal elements may end up at
1272 /// position `index`), in-place (i.e. does not allocate), and runs in *O*(*n*) time.
1273 /// This function is also known as "kth element" in other libraries.
1274 ///
1275 /// It returns a triplet of the following from
1276 /// the array reordered according to the provided key extraction function: the subarray prior to
1277 /// `index`, the element at `index`, and the subarray after `index`; accordingly, the values in
1278 /// those two subarrays will respectively all be less-than-or-equal-to and greater-than-or-equal-to
1279 /// the value of the element at `index`.
1280 ///
1281 /// # Current Implementation
1282 ///
1283 /// The current algorithm is an introselect implementation based on Pattern Defeating Quicksort, which is also
1284 /// the basis for [`sort_unstable`]. The fallback algorithm is Median of Medians using Tukey's Ninther for
1285 /// pivot selection, which guarantees linear runtime for all inputs.
1286 ///
1287 /// [`sort_unstable`]: Slice1Ext::sort_unstable
1288 ///
1289 /// # Panics
1290 ///
1291 /// Panics when `index >= len()`, meaning it always panics on empty arrays.
1292 ///
1293 /// # Examples
1294 ///
1295 /// ```
1296 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
1297 ///
1298 /// let mut v = arr1(&[-5i32, 4, 2, -3, 1]);
1299 ///
1300 /// // Return the median as if the array were sorted according to absolute value.
1301 /// v.select_nth_unstable_by_key(2, |a| a.abs());
1302 /// // Find the items less than or equal to the median, the median, and greater than or equal to
1303 /// // the median as if the slice were sorted according to absolute value.
1304 /// let (lesser, median, greater) = v.select_nth_unstable_by_key(2, |a| a.abs());
1305 ///
1306 /// assert!(lesser == arr1(&[1, 2]) || lesser == arr1(&[2, 1]));
1307 /// assert_eq!(median, &mut -3);
1308 /// assert!(greater == arr1(&[4, -5]) || greater == arr1(&[-5, 4]));
1309 ///
1310 /// // We are only guaranteed the array will be one of the following, based on the way we sort
1311 /// // about the specified index.
1312 /// assert!(v == arr1(&[1, 2, -3, 4, -5]) ||
1313 /// v == arr1(&[1, 2, -3, -5, 4]) ||
1314 /// v == arr1(&[2, 1, -3, 4, -5]) ||
1315 /// v == arr1(&[2, 1, -3, -5, 4]));
1316 /// ```
1317 #[must_use]
1318 fn select_nth_unstable_by_key<K, F>(
1319 &mut self,
1320 index: usize,
1321 f: F,
1322 ) -> (ArrayViewMut1<'_, A>, &mut A, ArrayViewMut1<'_, A>)
1323 where
1324 K: Ord,
1325 F: FnMut(&A) -> K,
1326 S: DataMut;
1327
1328 /// Returns the index of the partition point according to the given predicate
1329 /// (the index of the first element of the second partition).
1330 ///
1331 /// The array is assumed to be partitioned according to the given predicate.
1332 /// This means that all elements for which the predicate returns true are at the start of the array
1333 /// and all elements for which the predicate returns false are at the end.
1334 /// For example, `[7, 15, 3, 5, 4, 12, 6]` is partitioned under the predicate `x % 2 != 0`
1335 /// (all odd numbers are at the start, all even at the end).
1336 ///
1337 /// If this array is not partitioned, the returned result is unspecified and meaningless,
1338 /// as this method performs a kind of binary search.
1339 ///
1340 /// See also [`binary_search`], [`binary_search_by`], and [`binary_search_by_key`].
1341 ///
1342 /// [`binary_search`]: Slice1Ext::binary_search
1343 /// [`binary_search_by`]: Slice1Ext::binary_search_by
1344 /// [`binary_search_by_key`]: Slice1Ext::binary_search_by_key
1345 ///
1346 /// # Examples
1347 ///
1348 /// ```
1349 /// use ndarray_slice::{
1350 /// ndarray::{arr1, s},
1351 /// Slice1Ext,
1352 /// };
1353 ///
1354 /// let v = arr1(&[1, 2, 3, 3, 5, 6, 7]);
1355 /// let i = v.partition_point(|&x| x < 5);
1356 ///
1357 /// assert_eq!(i, 4);
1358 /// assert!(v.slice(s![..i]).iter().all(|&x| x < 5));
1359 /// assert!(v.slice(s![i..]).iter().all(|&x| !(x < 5)));
1360 /// ```
1361 ///
1362 /// If all elements of the array match the predicate, including if the array
1363 /// is empty, then the length of the array will be returned:
1364 ///
1365 /// ```
1366 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
1367 ///
1368 /// let a = arr1(&[2, 4, 8]);
1369 /// assert_eq!(a.partition_point(|x| x < &100), a.len());
1370 /// let a = arr1(&[0i32; 0]);
1371 /// assert_eq!(a.partition_point(|x| x < &100), 0);
1372 /// ```
1373 ///
1374 /// If you want to insert an item to a sorted vector, while maintaining
1375 /// sort order:
1376 ///
1377 /// ```
1378 /// use ndarray_slice::{ndarray::array, Slice1Ext};
1379 ///
1380 /// let mut s = array![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
1381 /// let num = 42;
1382 /// let idx = s.partition_point(|&x| x < num);
1383 /// let (mut s, off) = s.into_raw_vec_and_offset();
1384 /// let off = off.unwrap_or_default();
1385 /// s.insert(off + idx, num);
1386 /// assert_eq!(s[off..], [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]);
1387 /// ```
1388 #[must_use]
1389 fn partition_point<P>(&self, pred: P) -> usize
1390 where
1391 P: FnMut(&A) -> bool;
1392
1393 /// Returns `true` if the array contains an element with the given value.
1394 ///
1395 /// This operation is *O*(*n*).
1396 ///
1397 /// Note that if you have a sorted array, [`binary_search`] may be faster.
1398 ///
1399 /// [`binary_search`]: Slice1Ext::binary_search
1400 ///
1401 /// # Examples
1402 ///
1403 /// ```
1404 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
1405 ///
1406 /// let v = arr1(&[10, 40, 30]);
1407 /// assert!(v.contains(&30));
1408 /// assert!(!v.contains(&50));
1409 /// ```
1410 ///
1411 /// If you do not have a `&A`, but some other value that you can compare
1412 /// with one (for example, `String` implements `PartialEq<str>`), you can
1413 /// use `iter().any`:
1414 ///
1415 /// ```
1416 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
1417 ///
1418 /// let v = arr1(&[String::from("hello"), String::from("world")]); // array of `String`
1419 /// assert!(v.iter().any(|e| e == "hello")); // search with `&str`
1420 /// assert!(!v.iter().any(|e| e == "hi"));
1421 /// ```
1422 #[must_use]
1423 fn contains(&self, x: &A) -> bool
1424 where
1425 A: PartialEq;
1426
1427 /// Binary searches this array for a given element.
1428 /// This behaves similarly to [`contains`] if this array is sorted.
1429 ///
1430 /// If the value is found then [`Result::Ok`] is returned, containing the
1431 /// index of the matching element. If there are multiple matches, then any
1432 /// one of the matches could be returned. The index is chosen
1433 /// deterministically, but is subject to change in future versions of Rust.
1434 /// If the value is not found then [`Result::Err`] is returned, containing
1435 /// the index where a matching element could be inserted while maintaining
1436 /// sorted order.
1437 ///
1438 /// See also [`binary_search_by`], [`binary_search_by_key`], and [`partition_point`].
1439 ///
1440 /// [`contains`]: Slice1Ext::contains
1441 /// [`binary_search_by`]: Slice1Ext::binary_search_by
1442 /// [`binary_search_by_key`]: Slice1Ext::binary_search_by_key
1443 /// [`partition_point`]: Slice1Ext::partition_point
1444 ///
1445 /// # Examples
1446 ///
1447 /// Looks up a series of four elements. The first is found, with a
1448 /// uniquely determined position; the second and third are not
1449 /// found; the fourth could match any position in `[1, 4]`.
1450 ///
1451 /// ```
1452 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
1453 ///
1454 /// let s = arr1(&[0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]);
1455 ///
1456 /// assert_eq!(s.binary_search(&13), Ok(9));
1457 /// assert_eq!(s.binary_search(&4), Err(7));
1458 /// assert_eq!(s.binary_search(&100), Err(13));
1459 /// let r = s.binary_search(&1);
1460 /// assert!(match r { Ok(1..=4) => true, _ => false, });
1461 /// ```
1462 ///
1463 /// If you want to find that whole *range* of matching items, rather than
1464 /// an arbitrary matching one, that can be done using [`partition_point`]:
1465 /// ```
1466 /// use ndarray_slice::{
1467 /// ndarray::{arr1, s},
1468 /// Slice1Ext,
1469 /// };
1470 ///
1471 /// let s = arr1(&[0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]);
1472 ///
1473 /// let low = s.partition_point(|x| x < &1);
1474 /// assert_eq!(low, 1);
1475 /// let high = s.partition_point(|x| x <= &1);
1476 /// assert_eq!(high, 5);
1477 /// let r = s.binary_search(&1);
1478 /// assert!((low..high).contains(&r.unwrap()));
1479 ///
1480 /// assert!(s.slice(s![..low]).iter().all(|&x| x < 1));
1481 /// assert!(s.slice(s![low..high]).iter().all(|&x| x == 1));
1482 /// assert!(s.slice(s![high..]).iter().all(|&x| x > 1));
1483 ///
1484 /// // For something not found, the "range" of equal items is empty
1485 /// assert_eq!(s.partition_point(|x| x < &11), 9);
1486 /// assert_eq!(s.partition_point(|x| x <= &11), 9);
1487 /// assert_eq!(s.binary_search(&11), Err(9));
1488 /// ```
1489 ///
1490 /// If you want to insert an item to a sorted vector, while maintaining
1491 /// sort order, consider using [`partition_point`]:
1492 ///
1493 /// ```
1494 /// use ndarray_slice::{
1495 /// ndarray::{arr1, array},
1496 /// Slice1Ext,
1497 /// };
1498 ///
1499 /// let mut s = array![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
1500 /// let num = 42;
1501 /// let idx = s.partition_point(|&x| x < num);
1502 /// // The above is equivalent to `let idx = s.binary_search(&num).unwrap_or_else(|x| x);`
1503 /// let (mut s, off) = s.into_raw_vec_and_offset();
1504 /// let off = off.unwrap_or_default();
1505 /// s.insert(off + idx, num);
1506 /// assert_eq!(s[off..], [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]);
1507 /// ```
1508 fn binary_search(&self, x: &A) -> Result<usize, usize>
1509 where
1510 A: Ord;
1511 /// Binary searches this array with a comparator function.
1512 /// This behaves similarly to [`contains`] if this array is sorted.
1513 ///
1514 /// The comparator function should implement an order consistent
1515 /// with the sort order of the underlying array, returning an
1516 /// order code that indicates whether its argument is `Less`,
1517 /// `Equal` or `Greater` the desired target.
1518 ///
1519 /// If the value is found then [`Result::Ok`] is returned, containing the
1520 /// index of the matching element. If there are multiple matches, then any
1521 /// one of the matches could be returned. The index is chosen
1522 /// deterministically, but is subject to change in future versions of Rust.
1523 /// If the value is not found then [`Result::Err`] is returned, containing
1524 /// the index where a matching element could be inserted while maintaining
1525 /// sorted order.
1526 ///
1527 /// See also [`binary_search`], [`binary_search_by_key`], and [`partition_point`].
1528 ///
1529 /// [`contains`]: Slice1Ext::contains
1530 /// [`binary_search`]: Slice1Ext::binary_search
1531 /// [`binary_search_by_key`]: Slice1Ext::binary_search_by_key
1532 /// [`partition_point`]: Slice1Ext::partition_point
1533 ///
1534 /// # Examples
1535 ///
1536 /// Looks up a series of four elements. The first is found, with a
1537 /// uniquely determined position; the second and third are not
1538 /// found; the fourth could match any position in `[1, 4]`.
1539 ///
1540 /// ```
1541 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
1542 ///
1543 /// let s = arr1(&[0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]);
1544 ///
1545 /// let seek = 13;
1546 /// assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Ok(9));
1547 /// let seek = 4;
1548 /// assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Err(7));
1549 /// let seek = 100;
1550 /// assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Err(13));
1551 /// let seek = 1;
1552 /// let r = s.binary_search_by(|probe| probe.cmp(&seek));
1553 /// assert!(match r { Ok(1..=4) => true, _ => false, });
1554 /// ```
1555 fn binary_search_by<F>(&self, f: F) -> Result<usize, usize>
1556 where
1557 F: FnMut(&A) -> Ordering;
1558 /// Binary searches this array with a key extraction function.
1559 /// This behaves similarly to [`contains`] if this array is sorted.
1560 ///
1561 #[cfg_attr(
1562 feature = "alloc",
1563 doc = "\
1564 Assumes that the array is sorted by the key, for instance with
1565 [`sort_by_key`] using the same key extraction function."
1566 )]
1567 ///
1568 /// If the value is found then [`Result::Ok`] is returned, containing the
1569 /// index of the matching element. If there are multiple matches, then any
1570 /// one of the matches could be returned. The index is chosen
1571 /// deterministically, but is subject to change in future versions of Rust.
1572 /// If the value is not found then [`Result::Err`] is returned, containing
1573 /// the index where a matching element could be inserted while maintaining
1574 /// sorted order.
1575 ///
1576 /// See also [`binary_search`], [`binary_search_by`], and [`partition_point`].
1577 ///
1578 /// [`contains`]: Slice1Ext::contains
1579 #[cfg_attr(
1580 feature = "alloc",
1581 doc = "\
1582 [`sort_by_key`]: Slice1Ext::sort_by_key"
1583 )]
1584 /// [`binary_search`]: Slice1Ext::binary_search
1585 /// [`binary_search_by`]: Slice1Ext::binary_search_by
1586 /// [`partition_point`]: Slice1Ext::partition_point
1587 ///
1588 /// # Examples
1589 ///
1590 /// Looks up a series of four elements in a array of pairs sorted by
1591 /// their second elements. The first is found, with a uniquely
1592 /// determined position; the second and third are not found; the
1593 /// fourth could match any position in `[1, 4]`.
1594 ///
1595 /// ```
1596 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
1597 ///
1598 /// let s = arr1(&[(0, 0), (2, 1), (4, 1), (5, 1), (3, 1),
1599 /// (1, 2), (2, 3), (4, 5), (5, 8), (3, 13),
1600 /// (1, 21), (2, 34), (4, 55)]);
1601 ///
1602 /// assert_eq!(s.binary_search_by_key(&13, |&(a, b)| b), Ok(9));
1603 /// assert_eq!(s.binary_search_by_key(&4, |&(a, b)| b), Err(7));
1604 /// assert_eq!(s.binary_search_by_key(&100, |&(a, b)| b), Err(13));
1605 /// let r = s.binary_search_by_key(&1, |&(a, b)| b);
1606 /// assert!(match r { Ok(1..=4) => true, _ => false, });
1607 /// ```
1608 fn binary_search_by_key<B, F>(&self, b: &B, f: F) -> Result<usize, usize>
1609 where
1610 F: FnMut(&A) -> B,
1611 B: Ord;
1612
1613 /// Moves all consecutive repeated elements to the end of the array according to the
1614 /// [`PartialEq`] trait implementation.
1615 ///
1616 /// Returns two arrays. The first contains no consecutive repeated elements.
1617 /// The second contains all the duplicates in no specified order.
1618 ///
1619 /// If the array is sorted, the first returned array contains no duplicates.
1620 ///
1621 /// # Examples
1622 ///
1623 /// ```
1624 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
1625 ///
1626 /// let mut array = arr1(&[1, 2, 2, 3, 3, 2, 1, 1]);
1627 ///
1628 /// let (dedup, duplicates) = array.partition_dedup();
1629 ///
1630 /// assert_eq!(dedup, arr1(&[1, 2, 3, 2, 1]));
1631 /// assert_eq!(duplicates, arr1(&[2, 3, 1]));
1632 /// ```
1633 fn partition_dedup(&mut self) -> (ArrayViewMut1<'_, A>, ArrayViewMut1<'_, A>)
1634 where
1635 A: PartialEq,
1636 S: DataMut;
1637 /// Moves all but the first of consecutive elements to the end of the array satisfying
1638 /// a given equality relation.
1639 ///
1640 /// Returns two arrays. The first contains no consecutive repeated elements.
1641 /// The second contains all the duplicates in no specified order.
1642 ///
1643 /// The `same_bucket` function is passed references to two elements from the array and
1644 /// must determine if the elements compare equal. The elements are passed in opposite order
1645 /// from their order in the array, so if `same_bucket(a, b)` returns `true`, `a` is moved
1646 /// at the end of the array.
1647 ///
1648 /// If the array is sorted, the first returned array contains no duplicates.
1649 ///
1650 /// # Examples
1651 ///
1652 /// ```
1653 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
1654 ///
1655 /// let mut array = arr1(&["foo", "Foo", "BAZ", "Bar", "bar", "baz", "BAZ"]);
1656 ///
1657 /// let (dedup, duplicates) = array.partition_dedup_by(|a, b| a.eq_ignore_ascii_case(b));
1658 ///
1659 /// assert_eq!(dedup, arr1(&["foo", "BAZ", "Bar", "baz"]));
1660 /// assert_eq!(duplicates, arr1(&["bar", "Foo", "BAZ"]));
1661 /// ```
1662 fn partition_dedup_by<F>(
1663 &mut self,
1664 same_bucket: F,
1665 ) -> (ArrayViewMut1<'_, A>, ArrayViewMut1<'_, A>)
1666 where
1667 F: FnMut(&mut A, &mut A) -> bool,
1668 S: DataMut;
1669 /// Moves all but the first of consecutive elements to the end of the array that resolve
1670 /// to the same key.
1671 ///
1672 /// Returns two arrays. The first contains no consecutive repeated elements.
1673 /// The second contains all the duplicates in no specified order.
1674 ///
1675 /// If the array is sorted, the first returned array contains no duplicates.
1676 ///
1677 /// # Examples
1678 ///
1679 /// ```
1680 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
1681 ///
1682 /// let mut array = arr1(&[10, 20, 21, 30, 30, 20, 11, 13]);
1683 ///
1684 /// let (dedup, duplicates) = array.partition_dedup_by_key(|i| *i / 10);
1685 ///
1686 /// assert_eq!(dedup, arr1(&[10, 20, 30, 20, 11]));
1687 /// assert_eq!(duplicates, arr1(&[21, 30, 13]));
1688 /// ```
1689 fn partition_dedup_by_key<K, F>(
1690 &mut self,
1691 key: F,
1692 ) -> (ArrayViewMut1<'_, A>, ArrayViewMut1<'_, A>)
1693 where
1694 F: FnMut(&mut A) -> K,
1695 K: PartialEq,
1696 S: DataMut;
1697
1698 /// Reverses the order of elements in the array, in place.
1699 ///
1700 /// # Examples
1701 ///
1702 /// ```
1703 /// use ndarray_slice::{ndarray::arr1, Slice1Ext};
1704 ///
1705 /// let mut v = arr1(&[1, 2, 3]);
1706 /// v.reverse();
1707 /// assert!(v == arr1(&[3, 2, 1]));
1708 /// ```
1709 fn reverse(&mut self)
1710 where
1711 S: DataMut;
1712}
1713
1714impl<A, S> Slice1Ext<A, S> for ArrayBase<S, Ix1>
1715where
1716 S: Data<Elem = A>,
1717{
1718 #[cfg(feature = "rayon")]
1719 #[inline]
1720 fn par_sort(&mut self)
1721 where
1722 A: Ord + Send,
1723 S: DataMut,
1724 {
1725 par_merge_sort(self.view_mut(), A::lt);
1726 }
1727 #[cfg(feature = "rayon")]
1728 #[inline]
1729 fn par_sort_by<F>(&mut self, compare: F)
1730 where
1731 A: Send,
1732 F: Fn(&A, &A) -> Ordering + Sync,
1733 S: DataMut,
1734 {
1735 par_merge_sort(self.view_mut(), |a: &A, b: &A| compare(a, b) == Less)
1736 }
1737 #[cfg(feature = "rayon")]
1738 #[inline]
1739 fn par_sort_by_key<K, F>(&mut self, f: F)
1740 where
1741 A: Send,
1742 K: Ord,
1743 F: Fn(&A) -> K + Sync,
1744 S: DataMut,
1745 {
1746 par_merge_sort(self.view_mut(), |a: &A, b: &A| f(a).lt(&f(b)))
1747 }
1748 #[cfg(feature = "rayon")]
1749 fn par_sort_by_cached_key<K, F>(&mut self, f: F)
1750 where
1751 A: Send + Sync,
1752 F: Fn(&A) -> K + Sync,
1753 K: Ord + Send,
1754 S: DataMut,
1755 {
1756 use core::mem;
1757 use rayon::{
1758 iter::{ParallelBridge, ParallelIterator},
1759 slice::ParallelSliceMut,
1760 };
1761
1762 //let slice = self.as_parallel_slice_mut();
1763 let len = self.len();
1764 if len < 2 {
1765 return;
1766 }
1767
1768 // Helper macro for indexing our vector by the smallest possible type, to reduce allocation.
1769 macro_rules! sort_by_key {
1770 ($t:ty) => {{
1771 let mut indices: Vec<_> = self
1772 .iter()
1773 .enumerate()
1774 .par_bridge()
1775 .map(|(i, x)| (f(&*x), i as $t))
1776 .collect();
1777 // The elements of `indices` are unique, as they are indexed, so any sort will be
1778 // stable with respect to the original slice. We use `sort_unstable` here because
1779 // it requires less memory allocation.
1780 indices.par_sort_unstable();
1781 for i in 0..len {
1782 let mut index = indices[i].1;
1783 while (index as usize) < i {
1784 index = indices[index as usize].1;
1785 }
1786 indices[i].1 = index;
1787 self.swap(i, index as usize);
1788 }
1789 }};
1790 }
1791
1792 let sz_u8 = mem::size_of::<(K, u8)>();
1793 let sz_u16 = mem::size_of::<(K, u16)>();
1794 let sz_u32 = mem::size_of::<(K, u32)>();
1795 let sz_usize = mem::size_of::<(K, usize)>();
1796
1797 if sz_u8 < sz_u16 && len <= (u8::MAX as usize) {
1798 return sort_by_key!(u8);
1799 }
1800 if sz_u16 < sz_u32 && len <= (u16::MAX as usize) {
1801 return sort_by_key!(u16);
1802 }
1803 if sz_u32 < sz_usize && len <= (u32::MAX as usize) {
1804 return sort_by_key!(u32);
1805 }
1806 sort_by_key!(usize)
1807 }
1808
1809 #[cfg(feature = "alloc")]
1810 #[inline]
1811 fn sort(&mut self)
1812 where
1813 A: Ord,
1814 S: DataMut,
1815 {
1816 stable_sort(self.view_mut(), A::lt);
1817 }
1818 #[cfg(feature = "alloc")]
1819 #[inline]
1820 fn sort_by<F>(&mut self, mut compare: F)
1821 where
1822 F: FnMut(&A, &A) -> Ordering,
1823 S: DataMut,
1824 {
1825 stable_sort(self.view_mut(), &mut |a: &A, b: &A| compare(a, b) == Less)
1826 }
1827 #[cfg(feature = "alloc")]
1828 #[inline]
1829 fn sort_by_key<K, F>(&mut self, mut f: F)
1830 where
1831 K: Ord,
1832 F: FnMut(&A) -> K,
1833 S: DataMut,
1834 {
1835 stable_sort(self.view_mut(), &mut |a: &A, b: &A| f(a).lt(&f(b)))
1836 }
1837 #[cfg(feature = "std")]
1838 fn sort_by_cached_key<K, F>(&mut self, f: F)
1839 where
1840 F: FnMut(&A) -> K,
1841 K: Ord,
1842 S: DataMut,
1843 {
1844 use core::mem;
1845
1846 // Helper macro for indexing our vector by the smallest possible type, to reduce allocation.
1847 macro_rules! sort_by_key {
1848 ($t:ty, $array:ident, $f:ident) => {{
1849 let mut indices: Vec<_> = $array
1850 .iter()
1851 .map($f)
1852 .enumerate()
1853 .map(|(i, k)| (k, i as $t))
1854 .collect();
1855 // The elements of `indices` are unique, as they are indexed, so any sort will be
1856 // stable with respect to the original array. We use `sort_unstable` here because
1857 // it requires less memory allocation.
1858 indices.sort_unstable();
1859 for i in 0..$array.len() {
1860 let mut index = indices[i].1;
1861 while (index as usize) < i {
1862 index = indices[index as usize].1;
1863 }
1864 indices[i].1 = index;
1865 $array.swap(i, index as usize);
1866 }
1867 }};
1868 }
1869
1870 let sz_u8 = mem::size_of::<(K, u8)>();
1871 let sz_u16 = mem::size_of::<(K, u16)>();
1872 let sz_u32 = mem::size_of::<(K, u32)>();
1873 let sz_usize = mem::size_of::<(K, usize)>();
1874
1875 let len = self.len();
1876 if len < 2 {
1877 return;
1878 }
1879 if sz_u8 < sz_u16 && len <= (u8::MAX as usize) {
1880 return sort_by_key!(u8, self, f);
1881 }
1882 if sz_u16 < sz_u32 && len <= (u16::MAX as usize) {
1883 return sort_by_key!(u16, self, f);
1884 }
1885 if sz_u32 < sz_usize && len <= (u32::MAX as usize) {
1886 return sort_by_key!(u32, self, f);
1887 }
1888 sort_by_key!(usize, self, f)
1889 }
1890
1891 #[cfg(feature = "rayon")]
1892 #[inline]
1893 fn par_sort_unstable(&mut self)
1894 where
1895 A: Ord + Send,
1896 S: DataMut,
1897 {
1898 par_quick_sort(self.view_mut(), A::lt);
1899 }
1900 #[cfg(feature = "rayon")]
1901 #[inline]
1902 fn par_sort_unstable_by<F>(&mut self, compare: F)
1903 where
1904 A: Send,
1905 F: Fn(&A, &A) -> Ordering + Sync,
1906 S: DataMut,
1907 {
1908 par_quick_sort(self.view_mut(), |a: &A, b: &A| compare(a, b) == Less)
1909 }
1910 #[cfg(feature = "rayon")]
1911 #[inline]
1912 fn par_sort_unstable_by_key<K, F>(&mut self, f: F)
1913 where
1914 A: Send,
1915 K: Ord,
1916 F: Fn(&A) -> K + Sync,
1917 S: DataMut,
1918 {
1919 par_quick_sort(self.view_mut(), |a: &A, b: &A| f(a).lt(&f(b)))
1920 }
1921
1922 #[inline]
1923 fn sort_unstable(&mut self)
1924 where
1925 A: Ord,
1926 S: DataMut,
1927 {
1928 quick_sort(self.view_mut(), A::lt);
1929 }
1930 #[inline]
1931 fn sort_unstable_by<F>(&mut self, mut compare: F)
1932 where
1933 F: FnMut(&A, &A) -> Ordering,
1934 S: DataMut,
1935 {
1936 quick_sort(self.view_mut(), &mut |a: &A, b: &A| compare(a, b) == Less)
1937 }
1938 #[inline]
1939 fn sort_unstable_by_key<K, F>(&mut self, mut f: F)
1940 where
1941 K: Ord,
1942 F: FnMut(&A) -> K,
1943 S: DataMut,
1944 {
1945 quick_sort(self.view_mut(), &mut |a: &A, b: &A| f(a).lt(&f(b)))
1946 }
1947
1948 #[inline]
1949 fn is_sorted(&self) -> bool
1950 where
1951 A: PartialOrd,
1952 {
1953 is_sorted(self.view(), |a, b| a <= b)
1954 }
1955 #[inline]
1956 fn is_sorted_by<F>(&self, compare: F) -> bool
1957 where
1958 F: FnMut(&A, &A) -> bool,
1959 {
1960 is_sorted(self.view(), compare)
1961 }
1962 #[inline]
1963 fn is_sorted_by_key<F, K>(&self, mut f: F) -> bool
1964 where
1965 F: FnMut(&A) -> K,
1966 K: PartialOrd,
1967 {
1968 is_sorted(self.view(), |a, b| f(a) <= f(b))
1969 }
1970
1971 #[cfg(feature = "rayon")]
1972 #[inline]
1973 fn par_select_many_nth_unstable<'a, S2>(
1974 &'a mut self,
1975 indices: &ArrayBase<S2, Ix1>,
1976 collection: &mut Vec<&'a mut A>,
1977 ) where
1978 A: Ord + Send,
1979 S: DataMut,
1980 S2: Data<Elem = usize> + Sync,
1981 {
1982 collection.reserve_exact(indices.len());
1983 let values = collection.spare_capacity_mut();
1984 par_partition_at_indices(self.view_mut(), 0, indices.view(), values, &A::lt);
1985 unsafe { collection.set_len(collection.len() + indices.len()) };
1986 }
1987 #[cfg(feature = "rayon")]
1988 #[inline]
1989 fn par_select_many_nth_unstable_by<'a, F, S2>(
1990 &'a mut self,
1991 indices: &ArrayBase<S2, Ix1>,
1992 collection: &mut Vec<&'a mut A>,
1993 compare: F,
1994 ) where
1995 A: Send,
1996 F: Fn(&A, &A) -> Ordering + Sync,
1997 S: DataMut,
1998 S2: Data<Elem = usize> + Sync,
1999 {
2000 collection.reserve_exact(indices.len());
2001 let values = collection.spare_capacity_mut();
2002 par_partition_at_indices(
2003 self.view_mut(),
2004 0,
2005 indices.view(),
2006 values,
2007 &|a: &A, b: &A| compare(a, b) == Less,
2008 );
2009 unsafe { collection.set_len(collection.len() + indices.len()) };
2010 }
2011 #[cfg(feature = "rayon")]
2012 #[inline]
2013 fn par_select_many_nth_unstable_by_key<'a, K, F, S2>(
2014 &'a mut self,
2015 indices: &ArrayBase<S2, Ix1>,
2016 collection: &mut Vec<&'a mut A>,
2017 f: F,
2018 ) where
2019 A: Send,
2020 K: Ord,
2021 F: Fn(&A) -> K + Sync,
2022 S: DataMut,
2023 S2: Data<Elem = usize> + Sync,
2024 {
2025 collection.reserve_exact(indices.len());
2026 let values = collection.spare_capacity_mut();
2027 par_partition_at_indices(
2028 self.view_mut(),
2029 0,
2030 indices.view(),
2031 values,
2032 &|a: &A, b: &A| f(a).lt(&f(b)),
2033 );
2034 unsafe { collection.set_len(collection.len() + indices.len()) };
2035 }
2036
2037 #[inline]
2038 fn select_many_nth_unstable<'a, E, S2>(
2039 &'a mut self,
2040 indices: &ArrayBase<S2, Ix1>,
2041 collection: &mut E,
2042 ) where
2043 A: Ord + 'a,
2044 E: Extend<(usize, &'a mut A)>,
2045 S: DataMut,
2046 S2: Data<Elem = usize>,
2047 {
2048 partition_at_indices(self.view_mut(), 0, indices.view(), collection, &mut A::lt);
2049 }
2050 #[inline]
2051 fn select_many_nth_unstable_by<'a, E, F, S2>(
2052 &'a mut self,
2053 indices: &ArrayBase<S2, Ix1>,
2054 collection: &mut E,
2055 mut compare: F,
2056 ) where
2057 A: 'a,
2058 E: Extend<(usize, &'a mut A)>,
2059 F: FnMut(&A, &A) -> Ordering,
2060 S: DataMut,
2061 S2: Data<Elem = usize>,
2062 {
2063 partition_at_indices(
2064 self.view_mut(),
2065 0,
2066 indices.view(),
2067 collection,
2068 &mut |a: &A, b: &A| compare(a, b) == Less,
2069 );
2070 }
2071 #[inline]
2072 fn select_many_nth_unstable_by_key<'a, E, K, F, S2>(
2073 &'a mut self,
2074 indices: &ArrayBase<S2, Ix1>,
2075 collection: &mut E,
2076 mut f: F,
2077 ) where
2078 A: 'a,
2079 E: Extend<(usize, &'a mut A)>,
2080 K: Ord,
2081 F: FnMut(&A) -> K,
2082 S: DataMut,
2083 S2: Data<Elem = usize>,
2084 {
2085 partition_at_indices(
2086 self.view_mut(),
2087 0,
2088 indices.view(),
2089 collection,
2090 &mut |a: &A, b: &A| f(a).lt(&f(b)),
2091 );
2092 }
2093
2094 #[inline]
2095 fn select_nth_unstable(
2096 &mut self,
2097 index: usize,
2098 ) -> (ArrayViewMut1<'_, A>, &mut A, ArrayViewMut1<'_, A>)
2099 where
2100 A: Ord,
2101 S: DataMut,
2102 {
2103 partition_at_index(self.view_mut(), index, &mut A::lt)
2104 }
2105 #[inline]
2106 fn select_nth_unstable_by<F>(
2107 &mut self,
2108 index: usize,
2109 mut compare: F,
2110 ) -> (ArrayViewMut1<'_, A>, &mut A, ArrayViewMut1<'_, A>)
2111 where
2112 F: FnMut(&A, &A) -> Ordering,
2113 S: DataMut,
2114 {
2115 partition_at_index(self.view_mut(), index, &mut |a: &A, b: &A| {
2116 compare(a, b) == Less
2117 })
2118 }
2119 #[inline]
2120 fn select_nth_unstable_by_key<K, F>(
2121 &mut self,
2122 index: usize,
2123 mut f: F,
2124 ) -> (ArrayViewMut1<'_, A>, &mut A, ArrayViewMut1<'_, A>)
2125 where
2126 K: Ord,
2127 F: FnMut(&A) -> K,
2128 S: DataMut,
2129 {
2130 partition_at_index(self.view_mut(), index, &mut |a: &A, b: &A| f(a).lt(&f(b)))
2131 }
2132
2133 #[inline]
2134 fn partition_point<P>(&self, mut pred: P) -> usize
2135 where
2136 P: FnMut(&A) -> bool,
2137 {
2138 self.binary_search_by(|x| if pred(x) { Less } else { Greater })
2139 .unwrap_or_else(|i| i)
2140 }
2141
2142 #[inline]
2143 fn contains(&self, x: &A) -> bool
2144 where
2145 A: PartialEq,
2146 {
2147 self.iter().any(|a| a == x)
2148 }
2149
2150 #[inline]
2151 fn binary_search(&self, x: &A) -> Result<usize, usize>
2152 where
2153 A: Ord,
2154 {
2155 self.binary_search_by(|p| p.cmp(x))
2156 }
2157 fn binary_search_by<F>(&self, mut f: F) -> Result<usize, usize>
2158 where
2159 F: FnMut(&A) -> Ordering,
2160 {
2161 // INVARIANTS:
2162 // - 0 <= left <= left + size = right <= self.len()
2163 // - f returns Less for everything in self[..left]
2164 // - f returns Greater for everything in self[right..]
2165 let mut size = self.len();
2166 let mut left = 0;
2167 let mut right = size;
2168 while left < right {
2169 let mid = left + size / 2;
2170
2171 // SAFETY: the while condition means `size` is strictly positive, so
2172 // `size/2 < size`. Thus `left + size/2 < left + size`, which
2173 // coupled with the `left + size <= self.len()` invariant means
2174 // we have `left + size/2 < self.len()`, and this is in-bounds.
2175 let cmp = f(unsafe { self.uget(mid) });
2176
2177 // This control flow produces conditional moves, which results in
2178 // fewer branches and instructions than if/else or matching on
2179 // cmp::Ordering.
2180 // This is x86 asm for u8: https://rust.godbolt.org/z/698eYffTx.
2181 left = if cmp == Less { mid + 1 } else { left };
2182 right = if cmp == Greater { mid } else { right };
2183 if cmp == Equal {
2184 // SAFETY: same as the `get_unchecked` above
2185 //unsafe { crate::intrinsics::assume(mid < self.len()) };
2186 debug_assert!(mid < self.len());
2187 return Ok(mid);
2188 }
2189
2190 size = right - left;
2191 }
2192
2193 // SAFETY: directly true from the overall invariant.
2194 // Note that this is `<=`, unlike the assume in the `Ok` path.
2195 //unsafe { crate::intrinsics::assume(left <= self.len()) };
2196 debug_assert!(left <= self.len());
2197 Err(left)
2198 }
2199 #[inline]
2200 fn binary_search_by_key<B, F>(&self, b: &B, mut f: F) -> Result<usize, usize>
2201 where
2202 F: FnMut(&A) -> B,
2203 B: Ord,
2204 {
2205 self.binary_search_by(|k| f(k).cmp(b))
2206 }
2207
2208 #[inline]
2209 fn partition_dedup(&mut self) -> (ArrayViewMut1<'_, A>, ArrayViewMut1<'_, A>)
2210 where
2211 A: PartialEq,
2212 S: DataMut,
2213 {
2214 self.partition_dedup_by(|a, b| a == b)
2215 }
2216 #[inline]
2217 fn partition_dedup_by<F>(
2218 &mut self,
2219 same_bucket: F,
2220 ) -> (ArrayViewMut1<'_, A>, ArrayViewMut1<'_, A>)
2221 where
2222 F: FnMut(&mut A, &mut A) -> bool,
2223 S: DataMut,
2224 {
2225 partition_dedup(self.view_mut(), same_bucket)
2226 }
2227 #[inline]
2228 fn partition_dedup_by_key<K, F>(
2229 &mut self,
2230 mut key: F,
2231 ) -> (ArrayViewMut1<'_, A>, ArrayViewMut1<'_, A>)
2232 where
2233 F: FnMut(&mut A) -> K,
2234 K: PartialEq,
2235 S: DataMut,
2236 {
2237 self.partition_dedup_by(|a, b| key(a) == key(b))
2238 }
2239
2240 #[inline]
2241 fn reverse(&mut self)
2242 where
2243 S: DataMut,
2244 {
2245 reverse(self.view_mut());
2246 }
2247}