use super::convert::f64_from_usize;
use serde::Deserialize;
use std::f64;
#[derive(Deserialize)]
enum Limits {
Equal { left: f64, right: f64, bins: usize },
Unequal { limits: Vec<f64> },
}
#[derive(Deserialize)]
pub struct BinLimits(Limits);
#[derive(Deserialize)]
pub struct BinRemapper {
normalizations: Vec<f64>,
limits: Vec<(f64, f64)>,
}
pub struct BinInfo<'a> {
limits: &'a BinLimits,
remapper: Option<&'a BinRemapper>,
}
impl<'a> BinInfo<'a> {
#[must_use]
pub const fn bins(&self) -> usize {
self.limits.bins()
}
#[must_use]
pub fn dimensions(&self) -> usize {
self.remapper.map_or(1, BinRemapper::dimensions)
}
#[must_use]
pub fn limits(&self) -> Vec<Vec<(f64, f64)>> {
self.remapper.map_or_else(
|| {
self.limits
.limits()
.windows(2)
.map(|window| vec![(window[0], window[1])])
.collect()
},
|remapper| {
remapper
.limits()
.to_vec()
.chunks_exact(self.dimensions())
.map(<[(f64, f64)]>::to_vec)
.collect()
},
)
}
#[must_use]
pub const fn new(limits: &'a BinLimits, remapper: Option<&'a BinRemapper>) -> Self {
Self { limits, remapper }
}
#[must_use]
pub fn normalizations(&self) -> Vec<f64> {
self.remapper.map_or_else(
|| self.limits.bin_sizes(),
|remapper| remapper.normalizations().to_vec(),
)
}
}
impl BinRemapper {
#[must_use]
pub const fn dimensions(&self) -> usize {
self.limits.len() / self.normalizations.len()
}
#[must_use]
pub fn limits(&self) -> &[(f64, f64)] {
&self.limits
}
#[must_use]
pub fn normalizations(&self) -> &[f64] {
&self.normalizations
}
}
impl BinLimits {
#[must_use]
pub fn bin_sizes(&self) -> Vec<f64> {
match &self.0 {
Limits::Equal { left, right, bins } => {
vec![(*right - *left) / f64_from_usize(*bins); *bins]
}
Limits::Unequal { limits } => limits.windows(2).map(|x| x[1] - x[0]).collect(),
}
}
#[must_use]
pub const fn bins(&self) -> usize {
match &self.0 {
Limits::Equal { bins, .. } => *bins,
Limits::Unequal { limits } => limits.len() - 1,
}
}
#[must_use]
pub fn limits(&self) -> Vec<f64> {
match &self.0 {
Limits::Equal { left, right, bins } => (0..=*bins)
.map(|b| (*right - *left).mul_add(f64_from_usize(b) / f64_from_usize(*bins), *left))
.collect(),
Limits::Unequal { limits } => limits.clone(),
}
}
}