iterstats 0.7.0

Statistics for rust iterators.
Documentation
//! Calcualte rank.

use crate::argsort::{ArgSort, ArgSortIter};
use crate::Iterstats;

/// Calculate the rank of an iterator.
///
/// This is really the [`ArgSort`] of [`ArgSort`].
pub trait Rank<A = Self>: Sized {
    /// Calculate the rank.
    fn rank<I: Iterator<Item = A>>(iter: I) -> RankIter;
}

impl<T> Rank for T
where
    T: ArgSort,
{
    fn rank<I: Iterator<Item = Self>>(iter: I) -> RankIter {
        RankIter::new(iter)
    }
}

/// An iterator that calculates the rank of each element
pub struct RankIter {
    nested_argsort: ArgSortIter<usize>,
}

impl RankIter {
    fn new<T: ArgSort>(iter: impl Iterator<Item = T>) -> Self {
        Self {
            nested_argsort: iter.argsort().argsort(),
        }
    }
}

impl Iterator for RankIter {
    type Item = usize;

    fn next(&mut self) -> Option<Self::Item> {
        self.nested_argsort.next()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use paste::paste;

    macro_rules! test_rank {
        ($name:ident : $typ:ty as $input:expr => $expected:expr) => {
            paste! {
                #[test]
                fn [<$name _iter>]() {
                    let output = <&$typ>::rank($input.iter()).collect::<Vec<usize>>();
                    assert_eq!(output, $expected)
                }
            }
            paste! {
                #[test]
                fn [<$name _into_iter>]() {
                    let output = $typ::rank($input.into_iter()).collect::<Vec<usize>>();
                    assert_eq!(output, $expected)
                }
            }
        };
    }

    test_rank!(usize_ordered : usize as [0, 1, 2, 3, 4, 5] => vec![0, 1, 2, 3, 4, 5]);
    test_rank!(usize_reversed : usize as [5, 4, 3, 2, 1] => vec![4, 3, 2, 1, 0]);
    test_rank!(char : char as ['d', 'a', 'c', 'b', 'z', 'y'] => vec![3, 0, 2, 1, 5, 4]);
    test_rank!(f32 : f32 as [f32::NAN, 4.2, 0.0, f32::NEG_INFINITY, -0.0, f32::INFINITY, -f32::NAN]
        =>              vec![       6,   4,   3,                 1,    2,             5,         0]
    );
    test_rank!(f64 : f64 as [f64::NAN, 4.2, 0.0, f64::NEG_INFINITY, -0.0, f64::INFINITY, -f64::NAN]
        =>              vec![       6,   4,   3,                 1,    2,             5,         0]
    );
}