pub fn average(data: &[f64]) -> f64 {
if data.is_empty() {
return 0.0;
}
data.iter().sum::<f64>() / data.len() as f64
}
pub fn std_dev(data: &[f64]) -> f64 {
if data.len() < 2 {
return 0.0;
}
let mean = average(data);
let variance = data.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / (data.len() - 1) as f64;
variance.sqrt()
}
pub fn fwhm(buf: &[f64], last_el: i64) -> f64 {
let Some(&seed) = buf.first() else {
return 0.0;
};
let last = last_el.clamp(-1, buf.len() as i64 - 1);
let (mut max, mut min, mut peak) = (seed, seed, 0i64);
for i in 1..=last {
let v = buf[i as usize];
if v > max {
max = v;
peak = i;
}
if v < min {
min = v;
}
}
let half = min + (max - min) / 2.0;
let mut right = last as f64;
for i in peak + 1..=last {
let (i, prev) = (i as usize, (i - 1) as usize);
if buf[i] < half {
right = prev as f64 + (half - buf[prev]) / (buf[i] - buf[prev]);
break;
}
}
let mut left = 0.0;
for i in (0..peak).rev() {
let (i, next) = (i as usize, (i + 1) as usize);
if buf[i] < half {
left = i as f64 + (half - buf[i]) / (buf[next] - buf[i]);
break;
}
}
right - left
}
pub fn smooth(data: &[f64]) -> Vec<f64> {
let n = data.len();
let mut result = data.to_vec();
if n < 5 {
return result;
}
for i in 2..n - 2 {
result[i] =
(data[i - 2] + 4.0 * data[i - 1] + 6.0 * data[i] + 4.0 * data[i + 1] + data[i + 2])
/ 16.0;
}
result
}
pub fn nsmooth(data: &[f64], n: usize) -> Vec<f64> {
let mut result = data.to_vec();
for _ in 0..n {
let next = smooth(&result);
let fixed_point = next
.iter()
.zip(&result)
.all(|(a, b)| a.to_bits() == b.to_bits());
result = next;
if fixed_point {
break;
}
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_average() {
assert_eq!(average(&[1.0, 2.0, 3.0, 4.0, 5.0]), 3.0);
}
#[test]
fn test_average_empty() {
assert_eq!(average(&[]), 0.0);
}
#[test]
fn test_average_single() {
assert_eq!(average(&[42.0]), 42.0);
}
#[test]
fn test_std_dev() {
let data = [2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0];
let sd = std_dev(&data);
assert!((sd - 2.138).abs() < 0.01, "sd={}", sd);
}
#[test]
fn test_std_dev_empty() {
assert_eq!(std_dev(&[]), 0.0);
}
#[test]
fn test_std_dev_single() {
assert_eq!(std_dev(&[5.0]), 0.0);
}
#[test]
fn test_fwhm_gaussian() {
let n = 101;
let center = 50.0;
let sigma = 10.0;
let data: Vec<f64> = (0..n)
.map(|i| {
let x = i as f64;
(-0.5 * ((x - center) / sigma).powi(2)).exp()
})
.collect();
let result = fwhm(&data, n as i64 - 1);
let expected = 2.3548 * sigma;
assert!(
(result - expected).abs() < 0.5,
"FWHM={}, expected≈{}",
result,
expected
);
}
#[test]
fn test_fwhm_short_buffers() {
assert_eq!(fwhm(&[], -1), 0.0);
assert_eq!(fwhm(&[1.0], 0), 0.0);
assert_eq!(fwhm(&[1.0, 2.0], 1), 0.5);
assert_eq!(
fwhm(&[0.0, 1.0, 4.0], -1),
-1.0,
"an empty window is lastEl"
);
}
}