owasm_kit/ext/
stats.rs

1use core::cmp::{Ord, Ordering, PartialEq};
2
3use num::{Float, Num, NumCast};
4
5use crate::ext::cmp;
6
7/// Returns the average value of the given data set, or None if data is empty.
8pub fn average<T>(data: Vec<T>) -> Option<T>
9where
10    T: Num,
11{
12    let mut sum = T::zero();
13    let mut count = T::zero();
14    for v in data {
15        sum = sum + v;
16        count = count + T::one();
17    }
18    if count == T::zero() {
19        None
20    } else {
21        Some(sum / count)
22    }
23}
24
25/// Returns the median value using the given compare function, or None if data is empty.
26pub fn median_by<T, F>(mut data: Vec<T>, compare: F) -> Option<T>
27where
28    T: Num + NumCast,
29    F: FnMut(&T, &T) -> Ordering,
30{
31    if data.is_empty() {
32        return None;
33    }
34
35    data.sort_by(compare);
36    let mid = data.len() / 2;
37    if data.len() % 2 == 0 {
38        let rhs = data.swap_remove(mid);
39        let lhs = data.swap_remove(mid - 1);
40        Some((lhs + rhs) / NumCast::from(2).unwrap())
41    } else {
42        Some(data.swap_remove(mid))
43    }
44}
45
46/// Returns the median value of the given data set, or None if data is empty.
47pub fn median_integer<T>(data: Vec<T>) -> Option<T>
48where
49    T: Ord + Num + NumCast,
50{
51    median_by(data, T::cmp)
52}
53
54/// Returns the median value of the given data set, or None if data is empty.
55pub fn median_float<T>(data: Vec<T>) -> Option<T>
56where
57    T: Float + NumCast,
58{
59    median_by(data, cmp::fcmp)
60}
61
62/// Returns the majority value of the given data set, or None if there is no majority.
63pub fn majority<T>(mut data: Vec<T>) -> Option<T>
64where
65    T: PartialEq,
66{
67    let mut candidate = 0;
68    let mut count = 1;
69    let len = data.len();
70
71    // Find majority by Boyer–Moore majority vote algorithm
72    // https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_majority_vote_algorithm
73    for idx in 1..len {
74        if data[candidate] == data[idx] {
75            count += 1;
76        } else {
77            count -= 1;
78        }
79        if count == 0 {
80            candidate = idx;
81            count = 1;
82        }
83    }
84
85    count = 0;
86    for idx in 0..len {
87        if data[candidate] == data[idx] {
88            count += 1;
89        }
90    }
91
92    if 2 * count > len {
93        Some(data.swap_remove(candidate))
94    } else {
95        None
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102
103    #[test]
104    fn test_average_empty() {
105        let vals: Vec<i64> = vec![];
106        assert_eq!(average(vals), None);
107    }
108
109    #[test]
110    fn test_average_int() {
111        let vals = vec![3, 2, 5, 7, 2, 9, 1];
112        assert_eq!(average(vals), Some(4));
113    }
114
115    #[test]
116    fn test_average_single_int() {
117        let vals = vec![3];
118        assert_eq!(average(vals), Some(3));
119    }
120
121    #[test]
122    fn test_average_float() {
123        let vals = vec![3.0, 2.0, 5.0, 7.0, 2.0, 9.0, 1.0];
124        assert_eq!(average(vals), Some(4.142857142857143));
125    }
126
127    #[test]
128    fn test_average_single_float() {
129        let vals = vec![3.0];
130        assert_eq!(average(vals), Some(3.0));
131    }
132
133    #[test]
134    fn test_median_odd_int() {
135        let vals = vec![3, 2, 5, 7, 2, 9, 1];
136        assert_eq!(median_integer(vals), Some(3));
137    }
138
139    #[test]
140    fn test_median_single_int() {
141        let vals = vec![3];
142        assert_eq!(median_integer(vals), Some(3));
143    }
144
145    #[test]
146    fn test_median_empty() {
147        let vals: Vec<i64> = vec![];
148        assert_eq!(median_integer(vals), None);
149    }
150
151    #[test]
152    fn test_median_even_int() {
153        let vals = vec![3, 2, 5, 7, 2, 10, 32, 1];
154        assert_eq!(median_integer(vals), Some(4));
155        let vals = vec![13, 36, 33, 45];
156        assert_eq!(median_integer(vals), Some(34));
157        let vals = vec![13, 15];
158        assert_eq!(median_integer(vals), Some(14));
159    }
160
161    #[test]
162    fn test_median_odd_float() {
163        let vals = vec![3.5, 2.7, 5.1, 7.4, 2.0, 9.1, 1.9];
164        assert_eq!(median_float(vals), Some(3.5));
165    }
166
167    #[test]
168    fn test_median_single_float() {
169        let vals = vec![3.0];
170        assert_eq!(median_float(vals), Some(3.0));
171    }
172
173    #[test]
174    fn test_median_even_float() {
175        let vals = vec![3.4, 2.0, 5.7, 7.1, 2.2, 10.1, 32.0, 1.8];
176        assert_eq!(median_float(vals), Some(4.55));
177        let vals = vec![13.0, 36.0, 45.0, 33.0];
178        assert_eq!(median_float(vals), Some(34.5));
179        let vals = vec![13.0, 36.2];
180        assert_eq!(median_float(vals), Some(24.6));
181    }
182
183    #[test]
184    fn test_majority_int() {
185        let vals = vec![1, 2, 3, 1, 3, 1, 1];
186        assert_eq!(majority(vals), Some(1));
187    }
188
189    #[test]
190    fn test_majority_single_int() {
191        let vals = vec![3];
192        assert_eq!(majority(vals), Some(3));
193    }
194
195    #[test]
196    fn test_majority_int_result_none() {
197        let vals = vec![1, 2, 3, 1, 3, 1, 1, 3];
198        assert_eq!(majority(vals), None);
199    }
200
201    #[test]
202    fn test_majority_float() {
203        let vals = vec![0.3, 1.0, 0.3, 0.4, 1.0, 1.0, 1.0];
204        assert_eq!(majority(vals), Some(1.0));
205    }
206
207    #[test]
208    fn test_majority_single_float() {
209        let vals = vec![3.0];
210        assert_eq!(majority(vals), Some(3.0));
211    }
212
213    #[test]
214    fn test_majority_float_result_none() {
215        let vals = vec![0.3, 1.0, 0.3, 0.4, 4.0, 1.0, 99.99, 1.0, 1.0];
216        assert_eq!(majority(vals), None);
217    }
218
219    #[test]
220    fn test_majority_char() {
221        let vals = vec!['a', 'b', 'a', 'b', 'b'];
222        assert_eq!(majority(vals), Some('b'));
223    }
224
225    #[test]
226    fn test_majority_single_char() {
227        let vals = vec!['a'];
228        assert_eq!(majority(vals), Some('a'));
229    }
230
231    #[test]
232    fn test_majority_char_result_none() {
233        let vals = vec!['a', 'b', 'a', 'b', 'c', 'b'];
234        assert_eq!(majority(vals), None);
235    }
236
237    #[test]
238    fn test_majority_string() {
239        let vals = vec![String::from("mumu"), String::from("mumu"), String::from("momo")];
240        assert_eq!(majority(vals), Some(String::from("mumu")));
241    }
242
243    #[test]
244    fn test_majority_single_string() {
245        let vals = vec![String::from("mumu")];
246        assert_eq!(majority(vals), Some(String::from("mumu")));
247    }
248
249    #[test]
250    fn test_majority_string_result_none() {
251        let vals = vec![String::from("mumu"), String::from("momo")];
252        assert_eq!(majority(vals), None);
253    }
254}