is_sorted/
lib.rs

1//! Extends `Iterator` with three algorithms, `is_sorted`, `is_sorted_by`, and
2//! `is_sorted_by_key` that check whether the elements of an `Iterator` are
3//! sorted in `O(N)` time and `O(1)` space.
4//!
5//! To enable explicitly-vectorized implementations enable the `unstable`
6//! nightly-only feature and use the typed comparators: `Increasing` and
7//! `Decreasing`.
8
9// If the `use_std` feature is not enable, compile for `no_std`:
10#![cfg_attr(not(feature = "use_std"), no_std)]
11// If the `unstable` feature is enabled, enable nightly-only features:
12#![cfg_attr(
13    feature = "unstable",
14    feature(
15        specialization,
16        fn_traits,
17        unboxed_closures,
18        stdsimd,
19        align_offset
20    )
21)]
22
23#[allow(unused_imports, unused_macros)]
24#[cfg(not(feature = "use_std"))]
25use core as std;
26
27use std::cmp;
28
29#[cfg(feature = "unstable")]
30use std::{arch, mem, slice};
31
32use cmp::Ordering;
33
34#[cfg(feature = "unstable")]
35mod ord;
36#[cfg(feature = "unstable")]
37pub use self::ord::*;
38
39#[cfg(feature = "unstable")]
40#[macro_use]
41mod macros;
42
43#[cfg(feature = "unstable")]
44mod signed;
45
46#[cfg(feature = "unstable")]
47mod unsigned;
48
49#[cfg(feature = "unstable")]
50mod floats;
51
52/// Extends `Iterator` with `is_sorted`, `is_sorted_by`, and
53/// `is_sorted_by_key`.
54pub trait IsSorted: Iterator {
55    /// Returns `true` if the elements of the iterator are sorted in increasing
56    /// order according to `<Self::Item as PartialOrd>::partial_cmp`.
57    ///
58    /// ```
59    /// # use is_sorted::IsSorted;
60    /// let v = vec![0, 1, 2, 3];
61    /// assert!(IsSorted::is_sorted(&mut v.iter()));
62    ///
63    /// let v = vec![0, 1, 2, -1];
64    /// assert!(!IsSorted::is_sorted(&mut v.iter()));
65    /// ```
66    #[inline]
67    fn is_sorted(&mut self) -> bool
68    where
69        Self: Sized,
70        Self::Item: PartialOrd,
71    {
72        #[cfg(feature = "unstable")]
73        {
74            self.is_sorted_by(Increasing)
75        }
76        #[cfg(not(feature = "unstable"))]
77        {
78            self.is_sorted_by(<Self::Item as PartialOrd>::partial_cmp)
79        }
80    }
81
82    /// Returns `true` if the elements of the iterator
83    /// are sorted according to the `compare` function.
84    ///
85    /// ```
86    /// # use std::cmp::Ordering;
87    /// # use is_sorted::IsSorted;
88    /// // Is an iterator sorted in decreasing order?
89    /// fn decr<T: PartialOrd>(a: &T, b: &T) -> Option<Ordering> {
90    ///     a.partial_cmp(b).map(|v| v.reverse())
91    /// }
92    ///
93    /// let v = vec![3, 2, 1, 0];
94    /// assert!(IsSorted::is_sorted_by(&mut v.iter(), decr));
95    ///
96    /// let v = vec![3, 2, 1, 4];
97    /// assert!(!IsSorted::is_sorted_by(&mut v.iter(), decr));
98    /// ```
99    #[inline]
100    fn is_sorted_by<F>(&mut self, compare: F) -> bool
101    where
102        Self: Sized,
103        F: FnMut(&Self::Item, &Self::Item) -> Option<Ordering>,
104    {
105        is_sorted_by_impl(self, compare)
106    }
107
108    /// Returns `true` if the elements of the iterator
109    /// are sorted according to the `key` extraction function.
110    ///
111    /// ```
112    /// # use is_sorted::IsSorted;
113    /// let v = vec![0_i32, -1, 2, -3];
114    /// assert!(IsSorted::is_sorted_by_key(&mut v.iter(), |v| v.abs()));
115    ///
116    /// let v = vec![0_i32, -1, 2, 0];
117    /// assert!(!IsSorted::is_sorted_by_key(&mut v.iter(), |v| v.abs()));
118    /// ```
119    #[inline]
120    fn is_sorted_by_key<F, B>(&mut self, mut key: F) -> bool
121    where
122        Self: Sized,
123        B: PartialOrd,
124        F: FnMut(&Self::Item) -> B,
125    {
126        IsSorted::is_sorted(&mut self.map(|v| key(&v)))
127    }
128
129    /// Returns the first unsorted pair of items in the iterator and its tail.
130    ///
131    /// ```
132    /// # use is_sorted::IsSorted;
133    /// let v: &[i32] = &[0, 1, 2, 3, 4, 1, 2, 3];
134    /// let (first, tail) = v.iter().is_sorted_until_by(|a, b| a.partial_cmp(b));
135    /// assert_eq!(first, Some((&4, &1)));
136    /// assert_eq!(tail.as_slice(), &[2, 3]);
137    /// ```
138    #[inline]
139    fn is_sorted_until_by<F>(
140        self, compare: F,
141    ) -> (Option<(Self::Item, Self::Item)>, Self)
142    where
143        Self: Sized,
144        F: FnMut(&Self::Item, &Self::Item) -> Option<Ordering>,
145    {
146        is_sorted_until_by_impl(self, compare)
147    }
148}
149
150// Blanket implementation for all types that implement `Iterator`:
151impl<I: Iterator> IsSorted for I {}
152
153// This function dispatch to the appropriate `is_sorted_by` implementation.
154#[inline]
155fn is_sorted_by_impl<I, F>(iter: &mut I, compare: F) -> bool
156where
157    I: Iterator,
158    F: FnMut(&I::Item, &I::Item) -> Option<Ordering>,
159{
160    <I as IsSortedBy<F>>::is_sorted_by(iter, compare)
161}
162
163// This trait is used to provide specialized implementations of `is_sorted_by`
164// for different (Iterator,Cmp) pairs:
165trait IsSortedBy<F>: Iterator
166where
167    F: FnMut(&Self::Item, &Self::Item) -> Option<Ordering>,
168{
169    fn is_sorted_by(&mut self, compare: F) -> bool;
170}
171
172// This blanket implementation acts as the fall-back, and just forwards to the
173// scalar implementation of the algorithm.
174impl<I, F> IsSortedBy<F> for I
175where
176    I: Iterator,
177    F: FnMut(&I::Item, &I::Item) -> Option<Ordering>,
178{
179    #[inline]
180    #[cfg(feature = "unstable")]
181    default fn is_sorted_by(&mut self, compare: F) -> bool {
182        is_sorted_by_scalar_impl(self, compare)
183    }
184
185    #[inline]
186    #[cfg(not(feature = "unstable"))]
187    fn is_sorted_by(&mut self, compare: F) -> bool {
188        is_sorted_by_scalar_impl(self, compare)
189    }
190}
191
192/// Scalar `is_sorted_by` implementation.
193///
194/// It just forwards to `Iterator::all`.
195#[inline]
196fn is_sorted_by_scalar_impl<I, F>(iter: &mut I, mut compare: F) -> bool
197where
198    I: Iterator,
199    F: FnMut(&I::Item, &I::Item) -> Option<Ordering>,
200{
201    let first = iter.next();
202    if let Some(mut first) = first {
203        return iter.all(|second| {
204            if let Some(ord) = compare(&first, &second) {
205                if ord != Ordering::Greater {
206                    first = second;
207                    return true;
208                }
209            }
210            false
211        });
212    }
213    true
214}
215
216/// Scalar `is_sorted_by` implementation for slices.
217///
218/// Avoids bound checks.
219#[cfg(feature = "unstable")]
220#[inline]
221fn is_sorted_by_scalar_slice_impl<'a, T, F>(
222    iter: &mut slice::Iter<'a, T>, mut compare: F,
223) -> bool
224where
225    T: PartialOrd,
226    F: FnMut(&&'a T, &&'a T) -> Option<Ordering>,
227{
228    let s = iter.as_slice();
229    if s.len() < 2 {
230        return true;
231    }
232    let mut first = unsafe { s.get_unchecked(0) };
233    for i in 1..s.len() {
234        let second = unsafe { s.get_unchecked(i) };
235        if let Some(ord) = compare(&first, &second) {
236            if ord != Ordering::Greater {
237                first = second;
238                continue;
239            }
240        }
241        return false;
242    }
243    true
244}
245
246// This function dispatch to the appropriate `is_sorted_until_by`
247// implementation.
248#[inline]
249fn is_sorted_until_by_impl<I, F>(
250    iter: I, compare: F,
251) -> (Option<(I::Item, I::Item)>, I)
252where
253    I: Iterator,
254    F: FnMut(&I::Item, &I::Item) -> Option<Ordering>,
255{
256    <I as IsSortedUntilBy<F>>::is_sorted_until_by(iter, compare)
257}
258
259// This trait is used to provide specialized implementations of
260// `is_sorted_until_by` for different (Iterator,Cmp) pairs:
261trait IsSortedUntilBy<F>: Iterator
262where
263    F: FnMut(&Self::Item, &Self::Item) -> Option<Ordering>,
264{
265    fn is_sorted_until_by(
266        self, compare: F,
267    ) -> (Option<(Self::Item, Self::Item)>, Self);
268}
269
270// This blanket implementation acts as the fall-back, and just forwards to the
271// scalar implementation of the algorithm.
272impl<I, F> IsSortedUntilBy<F> for I
273where
274    I: Iterator,
275    F: FnMut(&I::Item, &I::Item) -> Option<Ordering>,
276{
277    #[inline]
278    #[cfg(feature = "unstable")]
279    default fn is_sorted_until_by(
280        self, compare: F,
281    ) -> (Option<(Self::Item, Self::Item)>, Self) {
282        is_sorted_until_by_scalar_impl(self, compare)
283    }
284
285    #[inline]
286    #[cfg(not(feature = "unstable"))]
287    fn is_sorted_until_by(
288        self, compare: F,
289    ) -> (Option<(Self::Item, Self::Item)>, Self) {
290        is_sorted_until_by_scalar_impl(self, compare)
291    }
292}
293
294/// Scalar `is_sorted_until_by` implementation.
295#[inline]
296fn is_sorted_until_by_scalar_impl<I, F>(
297    mut iter: I, mut compare: F,
298) -> (Option<(I::Item, I::Item)>, I)
299where
300    I: Iterator,
301    F: FnMut(&I::Item, &I::Item) -> Option<Ordering>,
302{
303    let first = iter.next();
304    if let Some(mut first) = first {
305        loop {
306            let next = iter.next();
307            if let Some(next) = next {
308                if let Some(ord) = compare(&first, &next) {
309                    if ord != Ordering::Greater {
310                        first = next;
311                        continue;
312                    }
313                }
314                return (Some((first, next)), iter);
315            }
316            return (None, iter);
317        }
318    }
319    (None, iter)
320}
321
322/// Scalar `is_sorted_until_by` implementation for slices.
323///
324/// Avoids bounds check.
325#[cfg(feature = "unstable")]
326#[inline]
327fn is_sorted_until_by_scalar_slice_impl<'a, T, F>(
328    iter: slice::Iter<'a, T>, mut compare: F,
329) -> (Option<(&'a T, &'a T)>, slice::Iter<'a, T>)
330where
331    T: PartialOrd,
332    F: FnMut(&&'a T, &&'a T) -> Option<Ordering>,
333{
334    let s = iter.as_slice();
335    if s.len() < 2 {
336        return (None, unsafe { s.get_unchecked(s.len()..).iter() });
337    }
338    let mut first = unsafe { s.get_unchecked(0) };
339    for i in 0..s.len() {
340        let second = unsafe { s.get_unchecked(i) };
341        if let Some(ord) = compare(&first, &second) {
342            if ord != Ordering::Greater {
343                first = second;
344                continue;
345            }
346        }
347        return (Some((first, second)), unsafe {
348            s.get_unchecked((i + 1)..).iter()
349        });
350    }
351    return (None, unsafe { s.get_unchecked(s.len()..).iter() });
352}
353
354pub fn is_sorted_until_by<T, F>(slice: &[T], f: F) -> usize
355where
356    for<'r, 's> F: FnMut(&'r &T, &'s &T) -> Option<Ordering>,
357{
358    let (boundary, tail) = IsSorted::is_sorted_until_by(slice.iter(), f);
359    match boundary {
360        Some(_) => {
361            debug_assert!(tail.as_slice().len() < slice.len());
362            slice.len() - tail.as_slice().len() - 1
363        }
364        None => {
365            debug_assert!(tail.as_slice().is_empty());
366            slice.len()
367        }
368    }
369}
370
371#[cfg(feature = "unstable")]
372impl<'a, T, F> IsSortedBy<F> for slice::Iter<'a, T>
373where
374    T: PartialOrd,
375    F: FnMut(&&'a T, &&'a T) -> Option<Ordering>,
376{
377    #[inline]
378    default fn is_sorted_by(&mut self, compare: F) -> bool {
379        is_sorted_by_scalar_slice_impl(self, compare)
380    }
381}
382
383#[cfg(feature = "unstable")]
384impl<'a, T, F> IsSortedUntilBy<F> for slice::Iter<'a, T>
385where
386    T: PartialOrd,
387    F: FnMut(&&'a T, &&'a T) -> Option<Ordering>,
388{
389    #[inline]
390    default fn is_sorted_until_by(
391        self, compare: F,
392    ) -> (Option<(&'a T, &'a T)>, Self) {
393        return is_sorted_until_by_scalar_slice_impl(self, compare);
394    }
395}
396
397/// Adds a specialization of the IsSortedBy trait for a slice iterator.
398///
399/// The (feature,function) pairs must be listed in order of decreasing
400/// preference, that is, first pair will be preferred over second pair if its
401/// feature is enabled.
402macro_rules! is_sorted_by_slice_iter_x86 {
403    ($id:ident, $cmp:path : $([$feature:tt, $function:path]),*) => {
404        #[cfg(feature = "unstable")]
405        #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
406        #[cfg(
407            any(
408                // Either we have run-time feature detection:
409                feature = "use_std",
410                // Or the features are enabled at compile-time
411                any($(target_feature = $feature),*)
412            )
413        )]
414        impl<'a> IsSortedBy<$cmp> for slice::Iter<'a, $id> {
415            #[inline]
416            fn is_sorted_by(&mut self, compare: $cmp) -> bool {
417                // If we don't have run-time feature detection, we use
418                // compile-time detection. This specialization only exists if
419                // at least one of the features is actually enabled, so we don't
420                // need a fallback here.
421                #[cfg(not(feature = "use_std"))]
422                unsafe {
423                    $(
424                        #[cfg(target_feature = $feature)]
425                        {
426                            return $function(self.as_slice()) == self.as_slice().len();
427                        }
428                    )*
429                }
430
431                #[cfg(feature = "use_std")]
432                {
433                    $(
434                        if is_x86_feature_detected!($feature) {
435                            return unsafe { $function(self.as_slice())  == self.as_slice().len() };
436                        }
437                    )*;
438                    // If feature detection fails use scalar code:
439                    return is_sorted_by_scalar_slice_impl(self, compare);
440                }
441            }
442        }
443
444        #[cfg(feature = "unstable")]
445        #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
446        #[cfg(
447            any(
448                // Either we have run-time feature detection:
449                feature = "use_std",
450                // Or the features are enabled at compile-time
451                any($(target_feature = $feature),*)
452            )
453        )]
454        impl<'a> IsSortedUntilBy<$cmp> for slice::Iter<'a, $id> {
455            #[inline]
456            fn is_sorted_until_by(self, compare: $cmp)
457                                  -> (Option<(Self::Item,Self::Item)>, Self) {
458                // If we don't have run-time feature detection, we use
459                // compile-time detection. This specialization only exists if
460                // at least one of the features is actually enabled, so we don't
461                // need a fallback here.
462                #[cfg(not(feature = "use_std"))]
463                unsafe {
464                    $(
465                        #[cfg(target_feature = $feature)]
466                        {
467                            // The slice values until the index i returned by
468                            // the impl $function are sorted. The slice &[0..j]
469                            // for j > i might be sorted as well, so we just
470                            // proceed to call the scalar implementation on it:
471                            let s = self.as_slice();
472                            let i = $function(s);
473                            return is_sorted_until_by_scalar_slice_impl(s[i..].iter(), compare);
474                        }
475                    )*
476                }
477
478                #[cfg(feature = "use_std")]
479                {
480                    $(
481                        if is_x86_feature_detected!($feature) {
482                            let s = self.as_slice();
483                            let i =  unsafe { $function(s) };
484                            return is_sorted_until_by_scalar_slice_impl(s[i..].iter(), compare);
485                        }
486                    )*;
487                    // If feature detection fails use scalar code:
488                    return is_sorted_until_by_scalar_slice_impl(self, compare);
489                }
490            }
491        }
492    }
493}
494
495is_sorted_by_slice_iter_x86!(
496    i64, ord::types::Increasing :
497    ["avx2", ::signed::avx2::is_sorted_lt_i64],
498    ["sse4.2", ::signed::sse42::is_sorted_lt_i64]
499);
500
501is_sorted_by_slice_iter_x86!(
502    i64, ord::types::Decreasing :
503    ["avx2", ::signed::avx2::is_sorted_gt_i64],
504    ["sse4.2", ::signed::sse42::is_sorted_gt_i64]
505);
506
507is_sorted_by_slice_iter_x86!(
508    f64, ord::types::Increasing :
509    ["avx", ::floats::avx::is_sorted_lt_f64],
510    ["sse4.1", ::floats::sse41::is_sorted_lt_f64]
511);
512
513is_sorted_by_slice_iter_x86!(
514    f64, ord::types::Decreasing :
515    ["avx", ::floats::avx::is_sorted_gt_f64],
516    ["sse4.1", ::floats::sse41::is_sorted_gt_f64]
517);
518
519is_sorted_by_slice_iter_x86!(
520    i32, ord::types::Increasing :
521    ["avx2", ::signed::avx2::is_sorted_lt_i32],
522    ["sse4.1", ::signed::sse41::is_sorted_lt_i32]
523);
524
525is_sorted_by_slice_iter_x86!(
526    i32, ord::types::Decreasing :
527    ["avx2", ::signed::avx2::is_sorted_gt_i32],
528    ["sse4.1", ::signed::sse41::is_sorted_gt_i32]
529);
530
531is_sorted_by_slice_iter_x86!(
532    u32, ord::types::Increasing :
533    ["avx2", ::unsigned::avx2::is_sorted_lt_u32],
534    ["sse4.1", ::unsigned::sse41::is_sorted_lt_u32]
535);
536
537is_sorted_by_slice_iter_x86!(
538    u32, ord::types::Decreasing :
539    ["avx2", ::unsigned::avx2::is_sorted_gt_u32],
540    ["sse4.1", ::unsigned::sse41::is_sorted_gt_u32]
541);
542
543is_sorted_by_slice_iter_x86!(
544    f32, ord::types::Increasing :
545    ["avx", ::floats::avx::is_sorted_lt_f32],
546    ["sse4.1", ::floats::sse41::is_sorted_lt_f32]
547);
548
549is_sorted_by_slice_iter_x86!(
550    f32, ord::types::Decreasing :
551    ["avx", ::floats::avx::is_sorted_gt_f32],
552    ["sse4.1", ::floats::sse41::is_sorted_gt_f32]
553);
554
555is_sorted_by_slice_iter_x86!(
556    i16, ord::types::Increasing :
557    ["avx2", ::signed::avx2::is_sorted_lt_i16],
558    ["sse4.1", ::signed::sse41::is_sorted_lt_i16]
559);
560
561is_sorted_by_slice_iter_x86!(
562    i16, ord::types::Decreasing :
563    ["avx2", ::signed::avx2::is_sorted_gt_i16],
564    ["sse4.1", ::signed::sse41::is_sorted_gt_i16]
565);
566
567is_sorted_by_slice_iter_x86!(
568    u16, ord::types::Increasing :
569    ["avx2", ::unsigned::avx2::is_sorted_lt_u16],
570    ["sse4.1", ::unsigned::sse41::is_sorted_lt_u16]
571);
572
573is_sorted_by_slice_iter_x86!(
574    u16, ord::types::Decreasing :
575    ["avx2", ::unsigned::avx2::is_sorted_gt_u16],
576    ["sse4.1", ::unsigned::sse41::is_sorted_gt_u16]
577);
578
579is_sorted_by_slice_iter_x86!(
580    i8, ord::types::Increasing :
581    ["avx2", ::signed::avx2::is_sorted_lt_i8],
582    ["sse4.1", ::signed::sse41::is_sorted_lt_i8]
583);
584
585is_sorted_by_slice_iter_x86!(
586    i8, ord::types::Decreasing :
587    ["avx2", ::signed::avx2::is_sorted_gt_i8],
588    ["sse4.1", ::signed::sse41::is_sorted_gt_i8]
589);
590
591is_sorted_by_slice_iter_x86!(
592    u8, ord::types::Increasing :
593    ["avx2", ::unsigned::avx2::is_sorted_lt_u8],
594    ["sse4.1", ::unsigned::sse41::is_sorted_lt_u8]
595);
596
597is_sorted_by_slice_iter_x86!(
598    u8, ord::types::Decreasing :
599    ["avx2", ::unsigned::avx2::is_sorted_gt_u8],
600    ["sse4.1", ::unsigned::sse41::is_sorted_gt_u8]
601);