use crate::model::RoxChart;
use std::collections::HashMap;
pub fn bpm_min(chart: &RoxChart) -> f64 {
chart
.timing_points
.iter()
.filter(|tp| !tp.is_inherited)
.map(|tp| tp.bpm as f64)
.fold(f64::INFINITY, f64::min)
}
pub fn bpm_max(chart: &RoxChart) -> f64 {
chart
.timing_points
.iter()
.filter(|tp| !tp.is_inherited)
.map(|tp| tp.bpm as f64)
.fold(f64::NEG_INFINITY, f64::max)
}
pub fn bpm_mode(chart: &RoxChart) -> f64 {
let duration_us = chart.duration_us();
if duration_us == 0 {
return 0.0;
}
let mut bpm_durations: HashMap<String, f64> = HashMap::new();
let mut timing_points = chart.timing_points.clone();
timing_points.sort_by_key(|tp| tp.time_us);
let bpm_points: Vec<_> = timing_points
.into_iter()
.filter(|tp| !tp.is_inherited)
.collect();
if bpm_points.is_empty() {
return 0.0;
}
for i in 0..bpm_points.len() {
let current_tp = &bpm_points[i];
let next_time = if i + 1 < bpm_points.len() {
bpm_points[i + 1].time_us
} else {
duration_us
};
let start_time = current_tp.time_us.max(0).min(duration_us);
let end_time = next_time.max(0).min(duration_us);
if end_time > start_time {
let dur = (end_time - start_time) as f64;
let bpm_key = format!("{:.2}", current_tp.bpm);
*bpm_durations.entry(bpm_key).or_insert(0.0) += dur;
}
}
bpm_durations
.into_iter()
.max_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal))
.map(|(k, _)| k.parse::<f64>().unwrap_or(0.0))
.unwrap_or(0.0)
}