#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::vec;
use core::iter::Sum;
use num::Float;
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct GeometricMetrics<T> {
pub triangular_index: T,
pub tinn: T,
}
impl<T: Float + Sum<T> + Copy + core::fmt::Debug + core::ops::AddAssign> GeometricMetrics<T>
where
f64: From<T>,
{
pub fn compute(rr_intervals: &[T]) -> Self {
let min = T::from(300).unwrap();
let max = T::from(2_000).unwrap();
let bin_width = T::from(8).unwrap();
let num_bins = f64::from(((max - min) / bin_width).ceil()) as usize;
let mut hist = vec![T::from(0).unwrap(); num_bins];
rr_intervals.iter().for_each(|&i| {
let index = f64::from(((i - min) / bin_width).floor()) as usize;
hist[index] += T::one();
});
let mode = hist
.iter()
.max_by(|a, b| a.partial_cmp(b).unwrap())
.unwrap();
let triangular_index = T::from(rr_intervals.len()).unwrap() / *mode;
let epsilon = *mode * T::from(0.75).unwrap();
let base_left = hist.iter().position(|&i| i > epsilon).unwrap();
let base_right =
hist.len() - 1usize - hist.iter().rev().position(|&i| i > epsilon).unwrap();
let tinn = T::from(base_right - base_left).unwrap() * bin_width;
Self {
triangular_index,
tinn,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_data::RR_INTERVALS;
use approx::{AbsDiffEq, RelativeEq, UlpsEq, assert_relative_eq};
impl<T: AbsDiffEq> AbsDiffEq for GeometricMetrics<T>
where
T::Epsilon: Copy,
{
type Epsilon = T::Epsilon;
fn default_epsilon() -> T::Epsilon {
T::default_epsilon()
}
fn abs_diff_eq(&self, other: &Self, epsilon: T::Epsilon) -> bool {
T::abs_diff_eq(&self.triangular_index, &other.triangular_index, epsilon)
&& T::abs_diff_eq(&self.tinn, &other.tinn, epsilon)
}
}
impl<T: RelativeEq> RelativeEq for GeometricMetrics<T>
where
T::Epsilon: Copy,
{
fn default_max_relative() -> T::Epsilon {
T::default_max_relative()
}
fn relative_eq(&self, other: &Self, epsilon: T::Epsilon, max_relative: T::Epsilon) -> bool {
T::relative_eq(
&self.triangular_index,
&other.triangular_index,
epsilon,
max_relative,
) && T::relative_eq(&self.tinn, &other.tinn, epsilon, max_relative)
}
}
impl<T: UlpsEq> UlpsEq for GeometricMetrics<T>
where
T::Epsilon: Copy,
{
fn default_max_ulps() -> u32 {
T::default_max_ulps()
}
fn ulps_eq(&self, other: &Self, epsilon: T::Epsilon, max_ulps: u32) -> bool {
T::ulps_eq(
&self.triangular_index,
&other.triangular_index,
epsilon,
max_ulps,
) && T::ulps_eq(&self.tinn, &other.tinn, epsilon, max_ulps)
}
}
#[test]
fn test_metrics() {
let geo_params = GeometricMetrics::compute(RR_INTERVALS);
assert_relative_eq!(
GeometricMetrics {
tinn: 128., triangular_index: 18.31578947368421,
},
geo_params,
epsilon = 1e-14
);
}
}