use crate::frequency::{Frequency, FrequencyValue};
use alloc::boxed::Box;
use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use core::cell::{Cell, Ref, RefCell};
pub type SpectrumTotalScaleFunctionFactory =
Box<dyn Fn(f32, f32, f32, f32) -> Box<dyn Fn(f32) -> f32>>;
#[derive(Debug)]
pub struct FrequencySpectrum {
data: RefCell<Vec<(Frequency, FrequencyValue)>>,
average: Cell<FrequencyValue>,
median: Cell<FrequencyValue>,
min: Cell<FrequencyValue>,
max: Cell<FrequencyValue>,
}
impl FrequencySpectrum {
#[inline(always)]
pub fn new(data: Vec<(Frequency, FrequencyValue)>) -> Self {
let obj = Self {
data: RefCell::new(data),
average: Cell::new(FrequencyValue::from(-1.0)),
median: Cell::new(FrequencyValue::from(-1.0)),
min: Cell::new(FrequencyValue::from(-1.0)),
max: Cell::new(FrequencyValue::from(-1.0)),
};
obj.calc_statistics();
obj
}
#[inline(always)]
pub fn apply_total_scaling_fn(&self, total_scaling_fn: SpectrumTotalScaleFunctionFactory) {
let scale_fn = (total_scaling_fn)(
self.min.get().val(),
self.max.get().val(),
self.average.get().val(),
self.median.get().val(),
);
{
let mut data = self.data.borrow_mut();
for (_fr, fr_val) in data.iter_mut() {
*fr_val = (scale_fn)(fr_val.val()).into()
}
}
self.calc_statistics();
}
#[inline(always)]
pub fn average(&self) -> FrequencyValue {
self.average.get()
}
#[inline(always)]
pub fn median(&self) -> FrequencyValue {
self.median.get()
}
#[inline(always)]
pub fn max(&self) -> FrequencyValue {
self.max.get()
}
#[inline(always)]
pub fn min(&self) -> FrequencyValue {
self.min.get()
}
#[inline(always)]
pub fn range(&self) -> FrequencyValue {
self.max() - self.min()
}
#[inline(always)]
pub fn data(&self) -> Ref<Vec<(Frequency, FrequencyValue)>> {
self.data.borrow()
}
#[inline(always)]
pub fn to_map(&self, scale_fn: Option<&dyn Fn(f32) -> u32>) -> BTreeMap<u32, f32> {
self.data
.borrow()
.iter()
.map(|(fr, fr_val)| (fr.val(), fr_val.val()))
.map(|(fr, fr_val)| {
(
if let Some(fnc) = scale_fn {
(fnc)(fr)
} else {
fr as u32
},
fr_val,
)
})
.collect()
}
#[inline(always)]
fn calc_statistics(&self) {
let data = self.data.borrow();
let mut vals = data
.iter()
.map(|(_fr, val)| val)
.collect::<Vec<&FrequencyValue>>();
vals.sort();
let sum: f32 = vals
.iter()
.map(|fr_val| fr_val.val())
.fold(0.0, |a, b| a + b);
let avg = sum / vals.len() as f32;
let average: FrequencyValue = avg.into();
let median = {
let a = *vals[vals.len() / 2 - 1];
let b = *vals[vals.len() / 2];
(a + b) / 2.0.into()
};
let min = *vals[0];
let max = *vals[vals.len() - 1];
self.min.replace(min);
self.max.replace(max);
self.average.replace(average);
self.median.replace(median);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_spectrum() {
let spectrum = vec![
(0.0_f32, 5.0_f32),
(50.0, 50.0),
(100.0, 100.0),
(150.0, 150.0),
(200.0, 100.0),
(250.0, 20.0),
(300.0, 0.0),
(450.0, 200.0),
];
let spectrum = spectrum
.into_iter()
.map(|(fr, val)| (fr.into(), val.into()))
.collect::<Vec<(Frequency, FrequencyValue)>>();
let spectrum = FrequencySpectrum::new(spectrum);
assert_eq!(
(0.0.into(), 5.0.into()),
spectrum.data()[0],
"Vector must be ordered"
);
assert_eq!(
(50.0.into(), 50.0.into()),
spectrum.data()[1],
"Vector must be ordered"
);
assert_eq!(
(100.0.into(), 100.0.into()),
spectrum.data()[2],
"Vector must be ordered"
);
assert_eq!(
(150.0.into(), 150.0.into()),
spectrum.data()[3],
"Vector must be ordered"
);
assert_eq!(
(200.0.into(), 100.0.into()),
spectrum.data()[4],
"Vector must be ordered"
);
assert_eq!(
(250.0.into(), 20.0.into()),
spectrum.data()[5],
"Vector must be ordered"
);
assert_eq!(
(300.0.into(), 0.0.into()),
spectrum.data()[6],
"Vector must be ordered"
);
assert_eq!(
(450.0.into(), 200.0.into()),
spectrum.data()[7],
"Vector must be ordered"
);
assert_eq!(0.0, spectrum.min().val(), "min() must work");
assert_eq!(200.0, spectrum.max().val(), "max() must work");
assert_eq!(200.0 - 0.0, spectrum.range().val(), "range() must work");
assert_eq!(78.125, spectrum.average().val(), "average() must work");
assert_eq!(
(50 + 100) as f32 / 2.0,
spectrum.median().val(),
"median() must work"
);
}
}