use super::opt::{AggregationType, GroupReducerType};
pub struct Reducer;
impl Reducer {
pub fn reduce_f64(values: &[f64], reducer: GroupReducerType) -> f64 {
if values.is_empty() || reducer == GroupReducerType::None {
return 0.0;
}
match reducer {
GroupReducerType::Count => values.len() as f64,
GroupReducerType::First => values[0],
GroupReducerType::Last => values[values.len() - 1],
GroupReducerType::Sum => values.iter().copied().sum(),
GroupReducerType::Min => values.iter().copied().fold(f64::INFINITY, f64::min),
GroupReducerType::Max => values.iter().copied().fold(f64::NEG_INFINITY, f64::max),
GroupReducerType::Avg | GroupReducerType::Twa => {
let sum: f64 = values.iter().copied().sum();
sum / (values.len() as f64)
}
GroupReducerType::Range => {
let (min_v, max_v) = values
.iter()
.copied()
.fold((f64::INFINITY, f64::NEG_INFINITY), |(min, max), v| {
(min.min(v), max.max(v))
});
max_v - min_v
}
GroupReducerType::VarP
| GroupReducerType::StdP
| GroupReducerType::VarS
| GroupReducerType::StdS => {
let (var_p, var_s) = Self::compute_welford_var(values.iter().copied(), values.len());
match reducer {
GroupReducerType::VarP => var_p,
GroupReducerType::StdP => var_p.sqrt(),
GroupReducerType::VarS => var_s,
GroupReducerType::StdS => var_s.sqrt(),
_ => 0.0,
}
}
GroupReducerType::None => 0.0,
}
}
pub fn reduce_samples(samples: &[(u64, f64)], agg_type: AggregationType) -> f64 {
if samples.is_empty() {
return 0.0;
}
match agg_type {
AggregationType::Count => samples.len() as f64,
AggregationType::First => samples[0].1,
AggregationType::Last => samples[samples.len() - 1].1,
AggregationType::Sum => samples.iter().map(|s| s.1).sum(),
AggregationType::Min => samples.iter().map(|s| s.1).fold(f64::INFINITY, f64::min),
AggregationType::Max => samples
.iter()
.map(|s| s.1)
.fold(f64::NEG_INFINITY, f64::max),
AggregationType::Avg | AggregationType::Twa => {
let sum: f64 = samples.iter().map(|s| s.1).sum();
sum / (samples.len() as f64)
}
AggregationType::Range => {
let (min_v, max_v) = samples
.iter()
.fold((f64::INFINITY, f64::NEG_INFINITY), |(min, max), s| {
(min.min(s.1), max.max(s.1))
});
max_v - min_v
}
AggregationType::VarP
| AggregationType::StdP
| AggregationType::VarS
| AggregationType::StdS => {
let (var_p, var_s) = Self::compute_welford_var(samples.iter().map(|s| s.1), samples.len());
match agg_type {
AggregationType::VarP => var_p,
AggregationType::StdP => var_p.sqrt(),
AggregationType::VarS => var_s,
AggregationType::StdS => var_s.sqrt(),
_ => 0.0,
}
}
}
}
#[inline]
fn compute_welford_var(iter: impl Iterator<Item = f64>, count: usize) -> (f64, f64) {
if count == 0 {
return (0.0, 0.0);
}
let n = count as f64;
let mut mean = 0.0;
let mut m2 = 0.0;
for (i, val) in iter.enumerate() {
let k = (i + 1) as f64;
let delta = val - mean;
mean += delta / k;
let delta2 = val - mean;
m2 += delta * delta2;
}
let var_p = (m2 / n).max(0.0);
let var_s = if count <= 1 {
0.0
} else {
(m2 / (n - 1.0)).max(0.0)
};
(var_p, var_s)
}
}