use alloc::vec::Vec;
use crate::frequency::{Frequency, FrequencyValue};
use alloc::collections::BTreeMap;
use core::cell::{RefCell, Cell, Ref};
use alloc::boxed::Box;
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");
}
}