math/
mean.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//! Functions for calculating mean
use std::f64::NAN;

/// Calculates arithmetic mean (AM) of data set `slice`.
///
/// # Arguments
///
/// * `slice` - collection of values
///
/// # Example
///
/// ```
/// use math::mean;
///
/// let slice = [8., 16.];
/// assert_eq!(mean::arithmetic(&slice), 12.);
/// ```
pub fn arithmetic(slice: &[f64]) -> f64 {
	slice.iter().fold(0., |a, b| a + b) / slice.len() as f64
}

/// Calculate geometric mean (GM) of data set `slice`.
///
/// If the result would be imaginary, function returns `NAN`.
///
/// # Arguments
///
/// * `slice` - collection of values
///
/// # Example
///
/// ```
/// use math::mean;
///
/// let slice = [9., 16.];
/// assert_eq!(mean::geometric(&slice), 12.);
/// ```
pub fn geometric(slice: &[f64]) -> f64 {
	let product = slice.iter().fold(1., |a, b| a * b);
	match product < 0. {
		true => NAN,
		false => product.powf(1. / slice.len() as f64),
	}
}

/// Calculate harmonic mean (HM) of data set `slice`.
///
/// # Arguments
///
/// * `slice` - collection of values
///
/// # Example
///
/// ```
/// use math::mean;
///
/// let slice = [1., 7.];
/// assert_eq!(mean::harmonic(&slice), 1.75);
/// ```
pub fn harmonic(slice: &[f64]) -> f64 {
	slice.len() as f64 / slice.iter().fold(0., |a, b| a + 1. / b)
}

#[cfg(test)]
mod tests {
	use std::f64::{ NAN, INFINITY, NEG_INFINITY };
	use round;

	macro_rules! test_mean {
		($func:path [ $($name:ident: $params:expr,)* ]) => {
		$(
			#[test]
			fn $name() {
				let (slice, expected): (&[f64], f64) = $params;
				let result = $func(slice);
				match result.is_nan() {
					true => assert_eq!(expected.is_nan(), true),
					false => assert_eq!(round::half_up(result, 6), expected),
				}
			}
		)*
		}
	}

	test_mean! { super::arithmetic [
		arithmetic_1: (&[-7., -4., 1., 3., 8.], 0.2),
		arithmetic_2: (&[-4., 1., 3., 8., 12.], 4.),
		arithmetic_3: (&[0., 0., 0., 0., 0.], 0.),
		arithmetic_4: (&[0., 4., 7., 9., 17.], 7.4),
		arithmetic_5: (&[1., 2., 6., 4., 13.], 5.2),
		arithmetic_6: (&[1., 5., 10., 20., 25.], 12.2),
		arithmetic_7: (&[2., 3., 5., 7., 11.], 5.6),
		arithmetic_8: (&[NEG_INFINITY, 1., 2., 3., 4.], NEG_INFINITY),
		arithmetic_9: (&[1., 2., 3., 4., INFINITY], INFINITY),
	]}

	test_mean! { super::geometric [
		geometric_1: (&[-7., -4., 1., 3., 8.], 3.676833),
		geometric_2: (&[-4., 1., 3., 8., 12.], NAN),
		geometric_3: (&[0., 0., 0., 0., 0.], 0.),
		geometric_4: (&[0., 4., 7., 9., 17.], 0.),
		geometric_5: (&[1., 2., 6., 4., 13.], 3.622738),
		geometric_6: (&[1., 5., 10., 20., 25.], 7.578583),
		geometric_7: (&[2., 3., 5., 7., 11.], 4.706764),
		geometric_8: (&[NEG_INFINITY, 1., 2., 3., 4.], NAN),
		geometric_9: (&[1., 2., 3., 4., INFINITY], INFINITY),
	]}

	test_mean! { super::harmonic [
		harmonic_1: (&[-7., -4., 1., 3., 8.], 4.692737),
		harmonic_2: (&[-4., 1., 3., 8., 12.], 3.870968),
		harmonic_3: (&[0., 0., 0., 0., 0.], 0.),
		harmonic_4: (&[0., 4., 7., 9., 17.], 0.),
		harmonic_5: (&[1., 2., 6., 4., 13.], 2.508039),
		harmonic_6: (&[1., 5., 10., 20., 25.], 3.597122),
		harmonic_7: (&[2., 3., 5., 7., 11.], 3.94602),
		harmonic_8: (&[NEG_INFINITY, 1., 2., 3., 4.], 2.4),
		harmonic_9: (&[1., 2., 3., 4., INFINITY], 2.4),
	]}
}