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