use super::{DomainError, summarize_f64, summarize_u64};
use perfgate_types::Stats;
#[must_use = "pure computation; call site should use the returned Stats"]
pub fn compute_stats(
samples: &[perfgate_types::Sample],
work_units: Option<u64>,
) -> Result<Stats, DomainError> {
let measured: Vec<&perfgate_types::Sample> = samples.iter().filter(|s| !s.warmup).collect();
if measured.is_empty() {
return Err(DomainError::NoSamples);
}
let wall: Vec<u64> = measured.iter().map(|s| s.wall_ms).collect();
let wall_ms = summarize_u64(&wall)?;
let cpu_vals: Vec<u64> = measured.iter().filter_map(|s| s.cpu_ms).collect();
let cpu_ms = if cpu_vals.is_empty() {
None
} else {
Some(summarize_u64(&cpu_vals)?)
};
let page_fault_vals: Vec<u64> = measured.iter().filter_map(|s| s.page_faults).collect();
let page_faults = if page_fault_vals.is_empty() {
None
} else {
Some(summarize_u64(&page_fault_vals)?)
};
let ctx_switch_vals: Vec<u64> = measured.iter().filter_map(|s| s.ctx_switches).collect();
let ctx_switches = if ctx_switch_vals.is_empty() {
None
} else {
Some(summarize_u64(&ctx_switch_vals)?)
};
let rss_vals: Vec<u64> = measured.iter().filter_map(|s| s.max_rss_kb).collect();
let max_rss_kb = if rss_vals.is_empty() {
None
} else {
Some(summarize_u64(&rss_vals)?)
};
let io_read_vals: Vec<u64> = measured.iter().filter_map(|s| s.io_read_bytes).collect();
let io_read_bytes = if io_read_vals.is_empty() {
None
} else {
Some(summarize_u64(&io_read_vals)?)
};
let io_write_vals: Vec<u64> = measured.iter().filter_map(|s| s.io_write_bytes).collect();
let io_write_bytes = if io_write_vals.is_empty() {
None
} else {
Some(summarize_u64(&io_write_vals)?)
};
let network_vals: Vec<u64> = measured.iter().filter_map(|s| s.network_packets).collect();
let network_packets = if network_vals.is_empty() {
None
} else {
Some(summarize_u64(&network_vals)?)
};
let energy_vals: Vec<u64> = measured.iter().filter_map(|s| s.energy_uj).collect();
let energy_uj = if energy_vals.is_empty() {
None
} else {
Some(summarize_u64(&energy_vals)?)
};
let binary_vals: Vec<u64> = measured.iter().filter_map(|s| s.binary_bytes).collect();
let binary_bytes = if binary_vals.is_empty() {
None
} else {
Some(summarize_u64(&binary_vals)?)
};
let throughput_per_s = match work_units {
Some(work) => {
let thr: Vec<f64> = measured
.iter()
.map(|s| {
let secs = (s.wall_ms as f64) / 1000.0;
if secs <= 0.0 {
0.0
} else {
(work as f64) / secs
}
})
.collect();
Some(summarize_f64(&thr)?)
}
None => None,
};
Ok(Stats {
wall_ms,
cpu_ms,
page_faults,
ctx_switches,
max_rss_kb,
io_read_bytes,
io_write_bytes,
network_packets,
energy_uj,
binary_bytes,
throughput_per_s,
})
}