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