| extern crate num_traits; |
| |
| use std::cmp::Ordering; |
| use std::hash; |
| use num_traits::ToPrimitive; |
| |
| pub use frequency::Frequencies; |
| pub use minmax::MinMax; |
| pub use online::{OnlineStats, stddev, variance, mean}; |
| pub use unsorted::{Unsorted, median, mode, modes}; |
| |
| /// Partial wraps a type that satisfies `PartialOrd` and implements `Ord`. |
| /// |
| /// This allows types like `f64` to be used in data structures that require |
| /// `Ord`. When an ordering is not defined, an arbitrary order is returned. |
| #[derive(Clone, PartialEq, PartialOrd)] |
| struct Partial<T>(pub T); |
| |
| impl<T: PartialEq> Eq for Partial<T> {} |
| |
| impl<T: PartialOrd> Ord for Partial<T> { |
| fn cmp(&self, other: &Partial<T>) -> Ordering { |
| self.partial_cmp(other).unwrap_or(Ordering::Less) |
| } |
| } |
| |
| impl<T: ToPrimitive> ToPrimitive for Partial<T> { |
| fn to_isize(&self) -> Option<isize> { self.0.to_isize() } |
| fn to_i8(&self) -> Option<i8> { self.0.to_i8() } |
| fn to_i16(&self) -> Option<i16> { self.0.to_i16() } |
| fn to_i32(&self) -> Option<i32> { self.0.to_i32() } |
| fn to_i64(&self) -> Option<i64> { self.0.to_i64() } |
| |
| fn to_usize(&self) -> Option<usize> { self.0.to_usize() } |
| fn to_u8(&self) -> Option<u8> { self.0.to_u8() } |
| fn to_u16(&self) -> Option<u16> { self.0.to_u16() } |
| fn to_u32(&self) -> Option<u32> { self.0.to_u32() } |
| fn to_u64(&self) -> Option<u64> { self.0.to_u64() } |
| |
| fn to_f32(&self) -> Option<f32> { self.0.to_f32() } |
| fn to_f64(&self) -> Option<f64> { self.0.to_f64() } |
| } |
| |
| impl<T: hash::Hash> hash::Hash for Partial<T> { |
| fn hash<H: hash::Hasher>(&self, state: &mut H) { self.0.hash(state); } |
| } |
| |
| /// Defines an interface for types that have an identity and can be commuted. |
| /// |
| /// The value returned by `Default::default` must be its identity with respect |
| /// to the `merge` operation. |
| pub trait Commute : Sized { |
| /// Merges the value `other` into `self`. |
| fn merge(&mut self, other: Self); |
| |
| /// Merges the values in the iterator into `self`. |
| fn consume<I: Iterator<Item=Self>>(&mut self, other: I) { |
| for v in other { |
| self.merge(v); |
| } |
| } |
| } |
| |
| /// Merges all items in the stream. |
| /// |
| /// If the stream is empty, `None` is returned. |
| pub fn merge_all<T: Commute, I: Iterator<Item=T>>(mut it: I) -> Option<T> { |
| match it.next() { |
| None => None, |
| Some(mut init) => { init.consume(it); Some(init) } |
| } |
| } |
| |
| impl<T: Commute> Commute for Option<T> { |
| fn merge(&mut self, other: Option<T>) { |
| match *self { |
| None => { *self = other; } |
| Some(ref mut v1) => { other.map(|v2| v1.merge(v2)); } |
| } |
| } |
| } |
| |
| impl<T: Commute, E> Commute for Result<T, E> { |
| fn merge(&mut self, other: Result<T, E>) { |
| // Can't figure out how to work around the borrow checker to make |
| // this code less awkward. |
| if !self.is_err() && other.is_err() { |
| *self = other; |
| return; |
| } |
| match *self { |
| Err(_) => {}, |
| Ok(ref mut v1) => { |
| match other { |
| Ok(v2) => { v1.merge(v2); } |
| // This is the awkward part. We can't assign to `*self` |
| // because of the `ref mut v1` borrow. So we catch this |
| // case above and declare that this cannot be reached. |
| Err(_) => { unreachable!(); } |
| } |
| } |
| } |
| } |
| } |
| |
| impl<T: Commute> Commute for Vec<T> { |
| fn merge(&mut self, other: Vec<T>) { |
| assert_eq!(self.len(), other.len()); |
| for (v1, v2) in self.iter_mut().zip(other.into_iter()) { |
| v1.merge(v2); |
| } |
| } |
| } |
| |
| mod frequency; |
| mod minmax; |
| mod online; |
| mod unsorted; |
| |
| #[cfg(test)] |
| mod test { |
| use Commute; |
| use unsorted::Unsorted; |
| |
| #[test] |
| fn options() { |
| let v1: Unsorted<usize> = vec![2, 1, 3, 2].into_iter().collect(); |
| let v2: Unsorted<usize> = vec![5, 6, 5, 5].into_iter().collect(); |
| let mut merged = Some(v1); |
| merged.merge(Some(v2)); |
| assert_eq!(merged.unwrap().mode(), Some(5)); |
| } |
| } |