Skip to content

Commit d6d5011

Browse files
committed
Added a geometric mean function attempting to follow the same style as the previous averages. This is a core average function used in finance amongst other areas. I've added some tests to go with it.
1 parent f66f836 commit d6d5011

File tree

1 file changed

+91
-3
lines changed

1 file changed

+91
-3
lines changed

src/math/average.rs

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,32 @@ The mode is the most frequently occurring value on the list.
66
77
Reference: https://www.britannica.com/science/mean-median-and-mode
88
9-
This program approximates the mean, median and mode of a finite sequence.
9+
There is also the geometric mean, often used in finance, which is much more suited for rates and other multiplicative
10+
relationships. The geometric mean is the Nth root of the product of a finite sequence of numbers.
11+
12+
This program approximates the mean, geometric mean, median and mode of a finite sequence.
1013
Note: Floats sequences are not allowed for `mode` function.
1114
"]
1215
use std::collections::HashMap;
1316
use std::collections::HashSet;
1417

15-
use num_traits::Num;
18+
use num_traits::{Num, FromPrimitive, ToPrimitive, One};
1619

1720
fn sum<T: Num + Copy>(sequence: Vec<T>) -> T {
1821
sequence.iter().fold(T::zero(), |acc, x| acc + *x)
1922
}
2023

24+
fn product<T: Num + Copy + One + FromPrimitive + ToPrimitive>(sequence: &Vec<T>) -> Option<f64> {
25+
if sequence.is_empty() {
26+
None
27+
} else {
28+
sequence.iter()
29+
.copied()
30+
.fold(T::one(), |acc, x| acc * x)
31+
.to_f64()
32+
}
33+
}
34+
2135
/// # Argument
2236
///
2337
/// * `sequence` - A vector of numbers.
@@ -34,6 +48,22 @@ fn mean_of_two<T: Num + Copy>(a: T, b: T) -> T {
3448
(a + b) / (T::one() + T::one())
3549
}
3650

51+
52+
/// # Argument
53+
///
54+
/// * `sequence` - A vector of numbers.
55+
/// Returns geometric mean of `sequence`.
56+
pub fn geometric_mean<T: Num + Copy + One + FromPrimitive + ToPrimitive>(sequence: &Vec<T>) -> Option<f64> {
57+
if sequence.is_empty() {
58+
return None;
59+
}
60+
if sequence.iter().any(|&x| x.to_f64() <= Some(0.0)) {
61+
return None;
62+
}
63+
let product_result = product(sequence)?;
64+
Some(product_result.powf(1.0 / sequence.len() as f64))
65+
}
66+
3767
/// # Argument
3868
///
3969
/// * `sequence` - A vector of numbers.
@@ -119,4 +149,62 @@ mod test {
119149
assert!(mean(Vec::<f64>::new()).is_none());
120150
assert!(mean(Vec::<i32>::new()).is_none());
121151
}
122-
}
152+
153+
// Tests for product function
154+
// Empty Product is empty
155+
#[test]
156+
fn test_product_empty() {
157+
let sequence: Vec<i32> = vec![];
158+
let result = product(&sequence);
159+
assert_eq!(result, None);
160+
}
161+
162+
// Product of a single value is the value
163+
#[test]
164+
fn test_product_single_element() {
165+
let sequence = vec![10];
166+
let result = product(&sequence);
167+
assert_eq!(result, Some(10.0));
168+
}
169+
// Product generic test
170+
#[test]
171+
fn test_product_floats() {
172+
let sequence = vec![1.5, 2.0, 4.0];
173+
let result = product(&sequence);
174+
assert_eq!(result, Some(12.0));
175+
}
176+
177+
// Tests for geometric mean function
178+
// Empty sequence returns nothing
179+
#[test]
180+
fn test_geometric_mean_empty() {
181+
let sequence: Vec<f64> = vec![];
182+
let result = geometric_mean(&sequence);
183+
assert_eq!(result, None);
184+
}
185+
186+
// Geometric mean of a single value is the value itself.
187+
#[test]
188+
fn test_geometric_mean_single_element() {
189+
let sequence = vec![5.0];
190+
let result = geometric_mean(&sequence);
191+
assert_eq!(result, Some(5.0));
192+
}
193+
194+
// Geometric means are not defined for negative values
195+
#[test]
196+
fn test_geometric_mean_negative() {
197+
let sequence = vec![1.0, -3.0, 2.0];
198+
let result = geometric_mean(&sequence);
199+
assert_eq!(result, None);
200+
}
201+
202+
// Geometric mean generic test
203+
#[test]
204+
fn test_geometric_mean_floats() {
205+
let sequence = vec![0.5, 0.5, 0.3, 0.2];
206+
let result = geometric_mean(&sequence);
207+
assert_eq!(result, Some(0.34996355115805833));
208+
}
209+
210+
}

0 commit comments

Comments
 (0)