///|
pub fn trace_query(
  qrels : Array[JudgedDoc],
  run : Array[RetrievedDoc],
  cutoff~ : Int,
  threshold~ : Int,
) -> Array[TraceStep] {
  let relevance = build_relevance_map(qrels)
  let ranked = sorted_run_items(run)
  let limit = Int::min(Int::max(cutoff, 0), ranked.length())
  let mut relevant_total = 0
  for _, value in relevance {
    if value >= threshold {
      relevant_total += 1
    }
  }
  let steps : Array[TraceStep] = []
  let mut hits = 0
  for index in 0..= threshold {
      hits += 1
    }
    steps.push({
      rank: index + 1,
      doc_id: item.doc_id,
      score: item.score,
      relevance: value,
      judged,
      hits,
      precision: precision_from_counts(hits, index + 1),
      recall: recall_from_counts(hits, relevant_total),
    })
  }
  steps
}

///|
pub fn render_trace_tsv(steps : Array[TraceStep]) -> String {
  let rows : Array[String] = [
    "rank\tdoc_id\tscore\trelevance\tjudged\thits\tprecision\trecall",
  ]
  for step in steps {
    rows.push(
      "\{step.rank}\t\{step.doc_id}\t\{step.score}\t\{step.relevance}\t\{step.judged}\t\{step.hits}\t\{step.precision}\t\{step.recall}",
    )
  }
  rows.join("\n")
}

///|
pub fn trace_auc(steps : Array[TraceStep]) -> Double {
  if steps.length() < 2 {
    return 0.0
  }
  let mut area = 0.0
  for index in 1.. Int {
  for step in steps {
    if step.judged {
      return step.rank
    }
  }
  0
}

///|
pub fn trace_first_relevant_rank(steps : Array[TraceStep]) -> Int {
  for step in steps {
    if step.relevance > 0 {
      return step.rank
    }
  }
  0
}