///|
pub fn format_percent(value : Double) -> String {
  "\{format_metric(value * 100.0)}%"
}

///|
pub fn render_threshold_markdown(rows : Array[ThresholdEvaluation]) -> String {
  let lines : Array[String] = [
    "| Threshold | Cutoff | Precision | Recall | F1 | nDCG |", "| ---: | ---: | ---: | ---: | ---: | ---: |",
  ]
  for row in rows {
    lines.push(
      "| \{row.threshold} | \{row.cutoff} | \{format_metric(row.precision)} | \{format_metric(row.recall)} | \{format_metric(row.f1)} | \{format_metric(row.ndcg)} |",
    )
  }
  lines.join("\n")
}

///|
pub fn render_histogram(histogram : Map[Int, Int]) -> String {
  let levels : Array[Int] = []
  for level, _ in histogram {
    levels.push(level)
  }
  levels.sort()
  let builder = StringBuilder()
  builder.write_string("## Relevance histogram\n\n")
  builder.write_string("| Relevance | Count | Bar |\n| ---: | ---: | --- |\n")
  for level in levels {
    let count = histogram[level]
    let bars : Array[String] = []
    for _ in 0.. String {
  let profile = corpus_profile(case.qrels, case.run)
  let lines : Array[String] = [
    "## Reproducibility card",
    "",
    "- Case: \{case.name}",
    "- Queries: \{profile.query_count}",
    "- Qrels rows: \{profile.qrels_rows}",
    "- Run rows: \{profile.run_rows}",
    "- Unique documents: \{profile.unique_document_count}",
    "- Evaluation: deterministic sorting, no network, no random seed",
    "- Inputs: UTF-8 TSV with query/document identifiers",
  ]
  lines.join("\n")
}

///|
pub fn render_preset_report(name : String, report : BenchmarkReport) -> String {
  let builder = StringBuilder()
  builder.write_string("# Preset: \{name}\n\n")
  builder.write_string(render_preset_catalog())
  builder.write_string("\n\n")
  builder.write_string(render_markdown_report(report))
  builder.to_string()
}

///|
pub fn render_quality_summary(quality : RunQuality) -> String {
  let lines : Array[String] = [
    "rows=\{quality.row_count}",
    "unique_rows=\{quality.unique_row_count}",
    "duplicate_rate=\{format_percent(quality.duplicate_rate)}",
    "unjudged_rate=\{format_percent(quality.unjudged_rate)}",
    "score_monotonicity=\{format_percent(quality.score_monotonicity)}",
    "finite_score_rate=\{format_percent(quality.finite_score_rate)}",
  ]
  lines.join("\n")
}