use crate::param_utils;
pub struct ProgressTracker {
last_fitness: f64,
stall_count: usize,
stall_threshold: usize,
}
impl Default for ProgressTracker {
fn default() -> Self {
Self::new(50)
}
}
impl ProgressTracker {
pub fn new(stall_threshold: usize) -> Self {
Self {
last_fitness: f64::INFINITY,
stall_count: 0,
stall_threshold,
}
}
pub fn update(&mut self, fitness: f64) -> (String, bool) {
if fitness < self.last_fitness {
let delta = self.last_fitness - fitness;
self.last_fitness = fitness;
self.stall_count = 0;
(format!("(-{:.2e})", delta), false)
} else {
self.stall_count += 1;
let is_stalling = self.stall_count >= self.stall_threshold;
let msg = if is_stalling {
format!("(STALL:{})", self.stall_count)
} else {
"(--) ".to_string()
};
(msg, is_stalling)
}
}
pub fn just_started_stalling(&self) -> bool {
self.stall_count == 1
}
pub fn stall_at_interval(&self, interval: usize) -> bool {
self.stall_count > 0 && self.stall_count.is_multiple_of(interval)
}
}
pub fn format_param_summary(params: &[f64], params_per_filter: usize) -> String {
let num_filters = params.len() / params_per_filter;
let summaries: Vec<String> = (0..num_filters)
.map(|i| {
let offset = i * params_per_filter;
let freq_idx = if params_per_filter == 4 {
offset + 1
} else {
offset
};
let freq = param_utils::freq_from_log10(params[freq_idx]);
let q = params[freq_idx + 1];
let gain = params[freq_idx + 2];
format!("[f{:.0}Hz Q{:.2} G{:.2}dB]", freq, q, gain)
})
.collect();
summaries.join(" ")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_progress_tracker_improvement() {
let mut tracker = ProgressTracker::new(50);
let (msg, stalling) = tracker.update(100.0);
assert!(msg.contains("-"), "Should show improvement");
assert!(!stalling);
let (msg, stalling) = tracker.update(50.0);
assert!(msg.contains("-"), "Should show improvement");
assert!(!stalling);
}
#[test]
fn test_progress_tracker_stall() {
let mut tracker = ProgressTracker::new(3);
tracker.update(100.0);
let (_, stalling) = tracker.update(100.0);
assert!(!stalling, "Not stalling yet");
assert!(tracker.just_started_stalling());
let (_, stalling) = tracker.update(101.0);
assert!(!stalling, "Not at threshold yet");
let (msg, stalling) = tracker.update(102.0);
assert!(stalling, "Should be stalling now");
assert!(msg.contains("STALL"));
}
#[test]
fn test_format_param_summary() {
let params = vec![
3.0, 1.0, -3.0, 3.301, 2.0, 2.0, ];
let summary = format_param_summary(¶ms, 3);
assert!(summary.contains("f1000Hz"));
assert!(summary.contains("Q1.00"));
assert!(summary.contains("G-3.00dB"));
}
}