use crate::{InvalidOrderError, OrderResult};
use core::cmp::Ordering;
pub trait TryMinMax<T> {
#[inline]
fn try_min(self) -> OrderResult<Option<T>>
where
T: PartialOrd<T>,
Self: Sized,
{
self.try_select_by(|a, b| a.partial_cmp(b), Ordering::Greater)
}
#[inline]
fn try_min_by<F>(self, compare: F) -> OrderResult<Option<T>>
where
F: FnMut(&T, &T) -> Option<Ordering>,
Self: Sized,
{
self.try_select_by(compare, Ordering::Greater)
}
#[inline]
fn try_min_by_key<K, F>(self, f: F) -> OrderResult<Option<T>>
where
F: FnMut(&T) -> Option<K>,
K: PartialOrd<K>,
Self: Sized,
{
let mut fk = f;
self.try_select_by(|a, b| fk(a).partial_cmp(&fk(b)), Ordering::Greater)
}
#[inline]
fn try_max(self) -> OrderResult<Option<T>>
where
T: PartialOrd<T>,
Self: Sized,
{
self.try_select_by(|a, b| a.partial_cmp(b), Ordering::Less)
}
#[inline]
fn try_max_by<F>(self, compare: F) -> OrderResult<Option<T>>
where
F: FnMut(&T, &T) -> Option<Ordering>,
Self: Sized,
{
self.try_select_by(compare, Ordering::Less)
}
#[inline]
fn try_max_by_key<K, F>(self, f: F) -> OrderResult<Option<T>>
where
F: FnMut(&T) -> Option<K>,
K: PartialOrd<K>,
Self: Sized,
{
let mut fk = f;
self.try_select_by(|a, b| fk(a).partial_cmp(&fk(b)), Ordering::Less)
}
fn try_select_by<F>(self, compare: F, target: Ordering) -> OrderResult<Option<T>>
where
F: FnMut(&T, &T) -> Option<Ordering>;
}
impl<T, Iter> TryMinMax<T> for Iter
where
Iter: Iterator<Item = T>,
{
#[inline]
fn try_select_by<F>(self, compare: F, target: Ordering) -> OrderResult<Option<T>>
where
F: FnMut(&T, &T) -> Option<Ordering>,
{
try_select_by(self, compare, target)
}
}
fn try_select_by<T, F>(
mut iter: impl Iterator<Item = T>,
compare: F,
target: Ordering,
) -> OrderResult<Option<T>>
where
F: FnMut(&T, &T) -> Option<Ordering>,
{
let mut compare = compare;
iter.try_fold(None, |a: Option<T>, b| match (a, b) {
(None, n) => Some(Some(n)),
(Some(m), n) if compare(&m, &n)? == target => Some(Some(n)),
(m, _) => Some(m),
})
.ok_or(InvalidOrderError)
}
#[cfg(test)]
#[cfg(feature = "std")]
mod tests {
use crate::*;
use rand::distributions::Standard;
use rand::prelude::*;
use std::vec::Vec;
#[test]
fn try_min_ok() {
let rng = thread_rng();
let v: Vec<f32> = Standard.sample_iter(rng).take(100).collect();
let min = v.iter().try_min().unwrap();
assert_eq!(min, v.iter().min_by(|a, b| a.partial_cmp(b).unwrap()));
let iter = &mut v.iter();
let min = iter.try_min().unwrap();
assert_eq!(min, v.iter().min_by(|a, b| a.partial_cmp(b).unwrap()));
}
#[test]
fn try_min_error() {
let rng = thread_rng();
let mut v: Vec<f32> = Standard.sample_iter(rng).take(100).collect();
v.push(f32::NAN);
let min = v.iter().try_min();
assert!(min.is_err());
}
}