use crate::{plot::calc_ratio_f64, results::analysis_results::AnalysisResults};
use core::cmp::Ordering;
#[derive(Debug, Clone, Copy)]
pub struct Stats {
pub min: f64,
pub q1: f64,
pub median: f64,
pub q3: f64,
pub max: f64,
pub iqr: f64,
pub mean: f64,
pub count: usize,
}
pub fn calculate_stats(values: &[f64]) -> Option<Stats> {
let count = values.len();
if count == 0 {
return None;
}
let mut sorted_values = values.to_vec();
sorted_values.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
let min = sorted_values[0];
let max = sorted_values[count - 1];
let sum: f64 = sorted_values.iter().sum();
let mean = sum / count as f64;
let median = calculate_percentile(&sorted_values, 0.5);
let q1 = calculate_percentile(&sorted_values, 0.25);
let q3 = calculate_percentile(&sorted_values, 0.75);
let iqr = q3 - q1;
Some(Stats {
min,
q1,
median,
q3,
max,
iqr,
mean,
count,
})
}
pub fn calculate_percentile(sorted_values: &[f64], percentile: f64) -> f64 {
let count = sorted_values.len();
if count == 0 {
return 0.0;
}
let index = percentile * (count - 1) as f64;
let lower_idx = index.floor() as usize;
let upper_idx = index.ceil() as usize;
if lower_idx == upper_idx {
sorted_values[lower_idx]
} else {
let weight = index - lower_idx as f64;
sorted_values[lower_idx] * (1.0 - weight) + sorted_values[upper_idx] * weight
}
}
pub fn calculate_zstd_ratio_stats(
results: &[AnalysisResults],
comparison_index: usize,
) -> Option<Stats> {
let ratios: Vec<f64> = results
.iter()
.filter_map(|result| {
result
.split_comparisons
.get(comparison_index)
.map(|comparison| {
calc_ratio_f64(
comparison.group2_metrics.zstd_size,
comparison.group1_metrics.zstd_size,
)
})
})
.collect();
calculate_stats(&ratios)
}
pub fn calculate_custom_zstd_ratio_stats(
results: &[AnalysisResults],
comparison_index: usize,
group_index: usize,
) -> Option<Stats> {
let ratios: Vec<f64> = results
.iter()
.filter_map(|result| {
if let Some(comparison) = result.custom_comparisons.get(comparison_index) {
comparison
.group_metrics
.get(group_index)
.map(|group_metrics| {
calc_ratio_f64(
group_metrics.zstd_size,
comparison.baseline_metrics.zstd_size,
)
})
} else {
None
}
})
.collect();
calculate_stats(&ratios)
}
pub fn format_stats(stats: &Stats) -> String {
format!(
"min: {:.3}, Q1: {:.3}, median: {:.3}, Q3: {:.3}, max: {:.3}, IQR: {:.3}, mean: {:.3} (n={})",
stats.min, stats.q1, stats.median, stats.q3, stats.max, stats.iqr, stats.mean, stats.count
)
}