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)]
43#[derive(Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
44struct Partial<T>(pub T);
45
46impl<T: PartialEq> Eq for Partial<T> {}
47#[allow(clippy::derive_ord_xor_partial_ord)]
50impl<T: PartialOrd> Ord for Partial<T> {
51 #[inline]
52 fn cmp(&self, other: &Partial<T>) -> Ordering {
53 self.partial_cmp(other).unwrap_or(Ordering::Less)
54 }
55}
56
57impl<T: ToPrimitive> ToPrimitive for Partial<T> {
58 #[inline]
59 fn to_isize(&self) -> Option<isize> {
60 self.0.to_isize()
61 }
62 #[inline]
63 fn to_i8(&self) -> Option<i8> {
64 self.0.to_i8()
65 }
66 #[inline]
67 fn to_i16(&self) -> Option<i16> {
68 self.0.to_i16()
69 }
70 #[inline]
71 fn to_i32(&self) -> Option<i32> {
72 self.0.to_i32()
73 }
74 #[inline]
75 fn to_i64(&self) -> Option<i64> {
76 self.0.to_i64()
77 }
78
79 #[inline]
80 fn to_usize(&self) -> Option<usize> {
81 self.0.to_usize()
82 }
83 #[inline]
84 fn to_u8(&self) -> Option<u8> {
85 self.0.to_u8()
86 }
87 #[inline]
88 fn to_u16(&self) -> Option<u16> {
89 self.0.to_u16()
90 }
91 #[inline]
92 fn to_u32(&self) -> Option<u32> {
93 self.0.to_u32()
94 }
95 #[inline]
96 fn to_u64(&self) -> Option<u64> {
97 self.0.to_u64()
98 }
99
100 #[inline]
101 fn to_f32(&self) -> Option<f32> {
102 self.0.to_f32()
103 }
104 #[inline]
105 fn to_f64(&self) -> Option<f64> {
106 self.0.to_f64()
107 }
108}
109
110#[allow(clippy::derived_hash_with_manual_eq)]
111impl<T: hash::Hash> hash::Hash for Partial<T> {
112 #[inline]
113 fn hash<H: hash::Hasher>(&self, state: &mut H) {
114 self.0.hash(state);
115 }
116}
117
118pub trait Commute: Sized {
123 fn merge(&mut self, other: Self);
125
126 #[inline]
128 fn consume<I: Iterator<Item = Self>>(&mut self, other: I) {
129 for v in other {
130 self.merge(v);
131 }
132 }
133}
134
135#[inline]
139pub fn merge_all<T: Commute, I: Iterator<Item = T>>(mut it: I) -> Option<T> {
140 it.next().map_or_else(
141 || None,
142 |mut init| {
143 init.consume(it);
144 Some(init)
145 },
146 )
147}
148
149impl<T: Commute> Commute for Option<T> {
150 #[inline]
151 fn merge(&mut self, other: Option<T>) {
152 match *self {
153 None => {
154 *self = other;
155 }
156 Some(ref mut v1) => {
157 if let Some(v2) = other {
158 v1.merge(v2);
159 }
160 }
161 }
162 }
163}
164
165impl<T: Commute, E> Commute for Result<T, E> {
166 #[inline]
167 fn merge(&mut self, other: Result<T, E>) {
168 if !self.is_err() && other.is_err() {
169 *self = other;
170 return;
171 }
172 #[allow(clippy::let_unit_value)]
173 #[allow(clippy::ignored_unit_patterns)]
174 let _ = self.as_mut().map_or((), |v1| {
175 other.map_or_else(
176 |_| {
177 unreachable!();
178 },
179 |v2| {
180 v1.merge(v2);
181 },
182 );
183 });
184 }
185}
186
187impl<T: Commute> Commute for Vec<T> {
188 #[inline]
189 fn merge(&mut self, other: Vec<T>) {
190 assert_eq!(self.len(), other.len());
191 for (v1, v2) in self.iter_mut().zip(other) {
192 v1.merge(v2);
193 }
194 }
195}
196
197mod frequency;
198mod minmax;
199mod online;
200mod unsorted;
201
202#[cfg(test)]
203mod test {
204 use crate::Commute;
205 use crate::unsorted::Unsorted;
206
207 #[test]
208 fn options() {
209 let v1: Unsorted<usize> = vec![2, 1, 3, 2].into_iter().collect();
210 let v2: Unsorted<usize> = vec![5, 6, 5, 5].into_iter().collect();
211 let mut merged = Some(v1);
212 merged.merge(Some(v2));
213 assert_eq!(merged.unwrap().mode(), Some(5));
214 }
215}