diff --git a/src/math/average.rs b/src/math/average.rs index f420b2268de..b7ba4eaa598 100644 --- a/src/math/average.rs +++ b/src/math/average.rs @@ -6,18 +6,31 @@ The mode is the most frequently occurring value on the list. Reference: https://www.britannica.com/science/mean-median-and-mode -This program approximates the mean, median and mode of a finite sequence. + +This program approximates the mean, geometric mean, median and mode of a finite sequence. Note: Floats sequences are not allowed for `mode` function. "] use std::collections::HashMap; use std::collections::HashSet; -use num_traits::Num; +use num_traits::{FromPrimitive, Num, One, ToPrimitive}; fn sum(sequence: Vec) -> T { sequence.iter().fold(T::zero(), |acc, x| acc + *x) } +fn product(sequence: &[T]) -> Option { + if sequence.is_empty() { + None + } else { + sequence + .iter() + .copied() + .fold(T::one(), |acc, x| acc * x) + .to_f64() + } +} + /// # Argument /// /// * `sequence` - A vector of numbers. @@ -34,6 +47,23 @@ fn mean_of_two(a: T, b: T) -> T { (a + b) / (T::one() + T::one()) } +/// # Argument +/// +/// * `sequence` - A vector of numbers. +/// Returns geometric mean of `sequence`. +pub fn geometric_mean( + sequence: &[T], +) -> Option { + if sequence.is_empty() { + return None; + } + if sequence.iter().any(|&x| x.to_f64() <= Some(0.0)) { + return None; + } + let product_result = product(sequence)?; + Some(product_result.powf(1.0 / sequence.len() as f64)) +} + /// # Argument /// /// * `sequence` - A vector of numbers. @@ -119,4 +149,47 @@ mod test { assert!(mean(Vec::::new()).is_none()); assert!(mean(Vec::::new()).is_none()); } + + // Tests for product function + // Empty Product is empty + #[test] + fn test_product_empty() { + let sequence: Vec = vec![]; + let result = product(&sequence); + assert_eq!(result, None); + } + + // Product of a single value is the value + #[test] + fn test_product_single_element() { + let sequence = vec![10]; + let result = product(&sequence); + assert_eq!(result, Some(10.0)); + } + // Product generic test + #[test] + fn test_product_floats() { + let sequence = vec![1.5, 2.0, 4.0]; + let result = product(&sequence); + assert_eq!(result, Some(12.0)); + } + + macro_rules! test_geometric_mean { + ($($name:ident: $inputs:expr,)*) => { + $( + #[test] + fn $name() { + let (sequence, expected) = $inputs; + assert_eq!(geometric_mean(&sequence), expected); + } + )* + } + } + + test_geometric_mean! { + empty: (Vec::::new(), None), + single: (vec![5.0], Some(5.0)), + negative: (vec![1.0, -3.0, 2.0], None), + regular: (vec![0.5, 0.5, 0.3, 0.2], Some(0.34996355115805833)), + } }