use crate::cli::validate::ValidateCmd;
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use serde_json::json;
use std::fs;
use std::io::Write as _;
use std::time::{SystemTime, UNIX_EPOCH};
use aufbau::validation::complexity::{self, estimate_complexity_exponent, ComplexityData};
pub fn run(args: &ValidateCmd) {
eprintln!("aufbau validation runner - complexity");
let mut experiments: Vec<(&str, Vec<(String, Vec<ComplexityData>)>)> = Vec::new();
experiments.push(("basic", complexity::basic::experiments(None)));
experiments.push(("fun", complexity::fun::experiments(None)));
experiments.push(("stlc", complexity::stlc::experiments(None)));
let _total_exps: usize = experiments.iter().map(|(_, v)| v.len()).sum::<usize>();
let mp = MultiProgress::new();
let suite_style = ProgressStyle::with_template(
"{prefix:.bold} [{bar:30.cyan/dim}] {pos}/{len} suites {msg}",
)
.unwrap()
.progress_chars("=> ");
let exp_style = ProgressStyle::with_template(
" {prefix:.dim} [{bar:30.green/dim}] {pos}/{len} {elapsed_precise} {msg}",
)
.unwrap()
.progress_chars("-> ");
let suite_pb = mp.add(ProgressBar::new(experiments.len() as u64));
suite_pb.set_style(suite_style);
suite_pb.set_prefix("suites".to_string());
let mut all_perf_records: Vec<serde_json::Value> = Vec::new();
let mut report_lines: Vec<String> = Vec::new();
for (suite_name, exps) in experiments {
suite_pb.set_message(suite_name.to_string());
let exp_pb = mp.insert_after(&suite_pb, ProgressBar::new(exps.len() as u64));
exp_pb.set_style(exp_style.clone());
exp_pb.set_prefix(suite_name.to_string());
for (exp_name, data_points) in exps {
exp_pb.set_message(exp_name.clone());
let k = estimate_complexity_exponent(&data_points);
let points: Vec<serde_json::Value> = data_points
.iter()
.map(|d| json!({"n": d.n, "time_ms": d.time.as_millis(), "input_len": d.input.len()}))
.collect();
let perf_obj = json!({
"suite": suite_name,
"experiment": exp_name,
"exponent": k,
"points": points,
});
all_perf_records.push(perf_obj.clone());
report_lines.push(format!(
"{}::{} exponent={:.2} samples={}",
suite_name,
exp_name,
k,
data_points.len()
));
exp_pb.inc(1);
}
exp_pb.finish_and_clear();
suite_pb.inc(1);
}
suite_pb.finish_with_message("done");
if let Some(profile_path) = &args.profile {
if let Some(parent) = profile_path.parent() {
std::fs::create_dir_all(parent).ok();
}
let stem = profile_path.file_stem().unwrap().to_string_lossy();
let perf_path = profile_path
.parent()
.unwrap_or(std::path::Path::new("."))
.join(format!("{}-complexity-perf.json", stem));
let fperf = fs::File::create(&perf_path).expect("failed to create perf profile file");
serde_json::to_writer_pretty(
fperf,
&json!({"generated_by": "aufbau validate complexity", "cases": all_perf_records}),
)
.expect("failed to write perf profile JSON");
eprintln!("WROTE_PROFILE {}", perf_path.display());
}
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
let manifest_dir = env!("CARGO_MANIFEST_DIR");
let reports_dir = std::path::Path::new(manifest_dir)
.join("validation")
.join("reports");
fs::create_dir_all(&reports_dir).expect("failed to create reports dir");
let report_path = reports_dir.join(format!("complexity-{}.txt", timestamp));
let mut f = fs::File::create(&report_path).expect("failed to create report file");
writeln!(f, "COMPLEXITY REPORT").ok();
writeln!(f, "timestamp={}", timestamp).ok();
writeln!(f).ok();
for line in report_lines {
writeln!(f, "{}", line).ok();
}
eprintln!("WROTE_REPORT {}", report_path.display());
}