///|
pub fn run_benchmark_case(
  case : BenchmarkCase,
  cutoffs~ : Array[Int],
) -> BenchmarkResult {
  let validation = validate_dataset(case.qrels, case.run)
  let report = evaluate_benchmark(
    case.qrels,
    case.run,
    config=EvalConfig::new(cutoffs=normalize_cutoffs(cutoffs)),
  )
  { name: case.name, report, validation }
}

///|
pub fn benchmark_case_is_reproducible(
  case : BenchmarkCase,
  cutoffs~ : Array[Int],
) -> Bool {
  let left = run_benchmark_case(case, cutoffs~)
  let right = run_benchmark_case(case, cutoffs~)
  ToJson::to_json(left.report).stringify() ==
  ToJson::to_json(right.report).stringify()
}

///|
pub fn render_benchmark_manifest(case : BenchmarkCase) -> String {
  let profile = profile_dataset(case.qrels, case.run)
  let lines : Array[String] = [
    "name=\{case.name}",
    "qrels_rows=\{case.qrels.length()}",
    "run_rows=\{case.run.length()}",
    "queries=\{profile.query_count}",
    "relevant_documents=\{profile.relevant_count}",
    "unjudged_retrievals=\{profile.unjudged_retrievals}",
    "reproducibility=deterministic_sorting_and_no_random_sampling",
  ]
  lines.join("\n")
}

///|
pub fn render_benchmark_result(result : BenchmarkResult) -> String {
  let builder = StringBuilder()
  builder.write_string("# Benchmark: \{result.name}\n\n")
  builder.write_string(
    "Validation: valid=\{result.validation.is_valid()} errors=\{result.validation.error_count} warnings=\{result.validation.warning_count}\n\n",
  )
  builder.write_string(render_compact_report(result.report))
  builder.to_string()
}

///|
pub fn compare_benchmark_cases(
  baseline : BenchmarkCase,
  candidate : BenchmarkCase,
  cutoff~ : Int,
) -> RunComparison {
  compare_runs(baseline.qrels, baseline.run, candidate.run, cutoff~)
}

///|
pub fn benchmark_metric_table(result : BenchmarkResult) -> String {
  let rows : Array[String] = ["metric,mean,min,max"]
  for metric in result.report.summary {
    rows.push("\{metric.name},\{metric.mean},\{metric.min},\{metric.max}")
  }
  rows.join("\n")
}