macro_rules! impl_harmonic_mean {
($name:ident, $ty:ty) => {
#[derive(Debug, Clone)]
pub struct $name {
reciprocal_sum: $ty,
count: u64,
}
impl $name {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self {
reciprocal_sum: 0.0 as $ty,
count: 0,
}
}
#[inline]
pub fn update(&mut self, sample: $ty) -> Result<(), crate::DataError> {
check_finite!(sample);
assert!(
sample > 0.0 as $ty,
"harmonic mean requires positive values"
);
self.count += 1;
self.reciprocal_sum += 1.0 as $ty / sample;
Ok(())
}
#[inline]
#[must_use]
pub fn mean(&self) -> Option<$ty> {
if self.count == 0 {
Option::None
} else {
Option::Some(self.count as $ty / self.reciprocal_sum)
}
}
#[inline]
#[must_use]
pub fn count(&self) -> u64 {
self.count
}
#[inline]
pub fn reset(&mut self) {
self.reciprocal_sum = 0.0 as $ty;
self.count = 0;
}
}
impl Default for $name {
#[inline]
fn default() -> Self {
Self::new()
}
}
};
}
impl_harmonic_mean!(HarmonicMeanF64, f64);
impl_harmonic_mean!(HarmonicMeanF32, f32);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty() {
let hm = HarmonicMeanF64::new();
assert!(hm.mean().is_none());
}
#[test]
fn known_values() {
let mut hm = HarmonicMeanF64::new();
hm.update(1.0).unwrap();
hm.update(4.0).unwrap();
let m = hm.mean().unwrap();
assert!((m - 1.6).abs() < 1e-10, "expected 1.6, got {m}");
}
#[test]
fn harmonic_leq_arithmetic() {
let mut hm = HarmonicMeanF64::new();
let vals = [2.0, 4.0, 8.0];
let mut sum = 0.0;
for &v in &vals {
hm.update(v).unwrap();
sum += v;
}
let arithmetic = sum / vals.len() as f64;
let harmonic = hm.mean().unwrap();
assert!(
harmonic <= arithmetic,
"HM ({harmonic}) should be <= AM ({arithmetic})"
);
}
#[test]
fn equal_values() {
let mut hm = HarmonicMeanF64::new();
for _ in 0..100 {
hm.update(5.0).unwrap();
}
let m = hm.mean().unwrap();
assert!(
(m - 5.0).abs() < 1e-10,
"HM of equal values should equal that value"
);
}
#[test]
fn reset() {
let mut hm = HarmonicMeanF64::new();
hm.update(1.0).unwrap();
hm.reset();
assert_eq!(hm.count(), 0);
assert!(hm.mean().is_none());
}
#[test]
fn f32_basic() {
let mut hm = HarmonicMeanF32::new();
hm.update(2.0).unwrap();
hm.update(4.0).unwrap();
assert!(hm.mean().is_some());
}
#[test]
fn default_is_empty() {
let hm = HarmonicMeanF64::default();
assert_eq!(hm.count(), 0);
}
#[test]
#[should_panic(expected = "positive values")]
fn panics_on_zero() {
let mut hm = HarmonicMeanF64::new();
hm.update(0.0).unwrap();
}
#[test]
fn rejects_nan_and_inf() {
let mut hm = HarmonicMeanF64::new();
assert_eq!(hm.update(f64::NAN), Err(crate::DataError::NotANumber));
assert_eq!(hm.update(f64::INFINITY), Err(crate::DataError::Infinite));
assert_eq!(
hm.update(f64::NEG_INFINITY),
Err(crate::DataError::Infinite)
);
assert_eq!(hm.count(), 0);
}
}