1#![allow(clippy::default_trait_access)]
2#![allow(clippy::cast_precision_loss)]
3#![allow(clippy::cast_possible_truncation)]
4#![allow(clippy::module_name_repetitions)]
5#![allow(clippy::missing_panics_doc)]
6#![allow(clippy::use_self)]
7
8use num_traits::ToPrimitive;
9use std::cmp::Ordering;
10use std::hash;
11
12use serde::{Deserialize, Serialize};
13
14pub use frequency::{Frequencies, UniqueValues};
15pub use minmax::MinMax;
16pub use online::{OnlineStats, mean, stddev, variance};
17pub use unsorted::{
18 Unsorted, antimodes, atkinson, gini, kurtosis, mad, median, mode, modes, percentile_rank,
19 quartiles,
20};
21
22#[allow(clippy::derive_ord_xor_partial_ord)]
27#[derive(Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
28struct Partial<T>(pub T);
29
30impl<T: PartialEq> Eq for Partial<T> {}
31#[allow(clippy::derive_ord_xor_partial_ord)]
34impl<T: PartialOrd> Ord for Partial<T> {
35 #[inline]
36 fn cmp(&self, other: &Partial<T>) -> Ordering {
37 self.partial_cmp(other).unwrap_or(Ordering::Less)
38 }
39}
40
41impl<T: ToPrimitive> ToPrimitive for Partial<T> {
42 #[inline]
43 fn to_isize(&self) -> Option<isize> {
44 self.0.to_isize()
45 }
46 #[inline]
47 fn to_i8(&self) -> Option<i8> {
48 self.0.to_i8()
49 }
50 #[inline]
51 fn to_i16(&self) -> Option<i16> {
52 self.0.to_i16()
53 }
54 #[inline]
55 fn to_i32(&self) -> Option<i32> {
56 self.0.to_i32()
57 }
58 #[inline]
59 fn to_i64(&self) -> Option<i64> {
60 self.0.to_i64()
61 }
62
63 #[inline]
64 fn to_usize(&self) -> Option<usize> {
65 self.0.to_usize()
66 }
67 #[inline]
68 fn to_u8(&self) -> Option<u8> {
69 self.0.to_u8()
70 }
71 #[inline]
72 fn to_u16(&self) -> Option<u16> {
73 self.0.to_u16()
74 }
75 #[inline]
76 fn to_u32(&self) -> Option<u32> {
77 self.0.to_u32()
78 }
79 #[inline]
80 fn to_u64(&self) -> Option<u64> {
81 self.0.to_u64()
82 }
83
84 #[inline]
85 fn to_f32(&self) -> Option<f32> {
86 self.0.to_f32()
87 }
88 #[inline]
89 fn to_f64(&self) -> Option<f64> {
90 self.0.to_f64()
91 }
92}
93
94#[allow(clippy::derived_hash_with_manual_eq)]
95impl<T: hash::Hash> hash::Hash for Partial<T> {
96 #[inline]
97 fn hash<H: hash::Hasher>(&self, state: &mut H) {
98 self.0.hash(state);
99 }
100}
101
102pub trait Commute: Sized {
107 fn merge(&mut self, other: Self);
109
110 #[inline]
112 fn consume<I: Iterator<Item = Self>>(&mut self, other: I) {
113 for v in other {
114 self.merge(v);
115 }
116 }
117}
118
119#[inline]
123pub fn merge_all<T: Commute, I: Iterator<Item = T>>(mut it: I) -> Option<T> {
124 it.next().map_or_else(
125 || None,
126 |mut init| {
127 init.consume(it);
128 Some(init)
129 },
130 )
131}
132
133impl<T: Commute> Commute for Option<T> {
134 #[inline]
135 fn merge(&mut self, other: Option<T>) {
136 match *self {
137 None => {
138 *self = other;
139 }
140 Some(ref mut v1) => {
141 if let Some(v2) = other {
142 v1.merge(v2);
143 }
144 }
145 }
146 }
147}
148
149impl<T: Commute, E> Commute for Result<T, E> {
150 #[inline]
151 fn merge(&mut self, other: Result<T, E>) {
152 if !self.is_err() && other.is_err() {
153 *self = other;
154 return;
155 }
156 #[allow(clippy::let_unit_value)]
157 #[allow(clippy::ignored_unit_patterns)]
158 let _ = self.as_mut().map_or((), |v1| {
159 other.map_or_else(
160 |_| {
161 unreachable!();
162 },
163 |v2| {
164 v1.merge(v2);
165 },
166 );
167 });
168 }
169}
170
171impl<T: Commute> Commute for Vec<T> {
172 #[inline]
173 fn merge(&mut self, other: Vec<T>) {
174 assert_eq!(self.len(), other.len());
175 for (v1, v2) in self.iter_mut().zip(other) {
176 v1.merge(v2);
177 }
178 }
179}
180
181mod frequency;
182mod minmax;
183mod online;
184mod unsorted;
185
186#[cfg(test)]
187mod test {
188 use crate::Commute;
189 use crate::unsorted::Unsorted;
190
191 #[test]
192 fn options() {
193 let v1: Unsorted<usize> = vec![2, 1, 3, 2].into_iter().collect();
194 let v2: Unsorted<usize> = vec![5, 6, 5, 5].into_iter().collect();
195 let mut merged = Some(v1);
196 merged.merge(Some(v2));
197 assert_eq!(merged.unwrap().mode(), Some(5));
198 }
199}