///|
/// Structural result for a deterministic dictionary workload.
pub(all) struct BenchmarkResult {
  entry_count : Int
  state_count : Int
  input_chars : Int
  repeat_count : Int
  match_count : Int
  distinct_surface_count : Int
  total_weight : Int
  coverage_percent : Int
} derive(Debug, Eq, ToJson)

///|
/// Generate numbered entries for repeatable local and CI workloads.
pub fn benchmark_entries(size : Int) -> Array[Entry] {
  let output : Array[Entry] = []
  let safe_size = if size < 0 { 0 } else { size }
  for index in 0.. String {
  let output = StringBuilder()
  let safe_repeats = if repeats < 0 { 0 } else { repeats }
  for round in 0.. BenchmarkResult {
  let automaton = compile_automaton(entries)
  let text = benchmark_text(entries, repeats)
  let matches = automaton.find_all(text)
  let stats = match_stats(matches)
  {
    entry_count: entries.length(),
    state_count: automaton.state_count(),
    input_chars: text.length(),
    repeat_count: if repeats < 0 {
      0
    } else {
      repeats
    },
    match_count: stats.match_count,
    distinct_surface_count: stats.distinct_surface_count,
    total_weight: stats.total_weight,
    coverage_percent: percentage(
      covered_characters(text.length(), matches),
      text.length(),
    ),
  }
}

///|
/// Run several independent dictionary sizes using the same corpus recipe.
pub fn benchmark_scale(
  sizes : ArrayView[Int],
  repeats : Int,
) -> Array[BenchmarkResult] {
  let output : Array[BenchmarkResult] = []
  for size in sizes {
    output.push(run_benchmark(benchmark_entries(size), repeats))
  }
  output
}

///|
/// Return a stable text report for benchmark artifacts.
pub fn benchmark_report_tsv(results : ArrayView[BenchmarkResult]) -> String {
  let output = StringBuilder()
  output.write_string(
    "entries\tstates\tinput_chars\trepeats\tmatches\tdistinct_surfaces\ttotal_weight\tcoverage_percent\n",
  )
  for result in results {
    output.write_string(
      "\{result.entry_count}\t\{result.state_count}\t\{result.input_chars}\t\{result.repeat_count}\t\{result.match_count}\t\{result.distinct_surface_count}\t\{result.total_weight}\t\{result.coverage_percent}\n",
    )
  }
  output.to_string()
}

///|
/// Compare structural counters from two runs.
pub fn benchmark_results_equal(
  left : BenchmarkResult,
  right : BenchmarkResult,
) -> Bool {
  left == right
}

///|
/// Return the number of states beyond the dictionary entry count.
pub fn benchmark_sharing_margin(result : BenchmarkResult) -> Int {
  result.state_count - result.entry_count
}

///|
/// Return the average number of matches per repeated dictionary pass.
pub fn benchmark_matches_per_repeat(result : BenchmarkResult) -> Int {
  if result.repeat_count <= 0 {
    0
  } else {
    result.match_count / result.repeat_count
  }
}

///|
/// Return a compact workload identifier suitable for cache keys.
pub fn benchmark_signature(result : BenchmarkResult) -> String {
  "\{result.entry_count}:\{result.state_count}:\{result.input_chars}:\{result.match_count}"
}

///|
/// Generate a text corpus without first materializing entries for callers that
/// only need an input fixture.
pub fn benchmark_text_for_size(size : Int, repeats : Int) -> String {
  benchmark_text(benchmark_entries(size), repeats)
}

///|
/// Return the expected number of dictionary surfaces in a standard workload.
pub fn benchmark_expected_surface_count(size : Int) -> Int {
  if size < 0 {
    0
  } else {
    size
  }
}

///|
/// Return the total weight of generated benchmark entries.
pub fn benchmark_expected_weight(size : Int) -> Int {
  total_entry_weight(benchmark_entries(size))
}

///|
/// Check that a result is internally consistent with a workload.
pub fn benchmark_result_is_consistent(result : BenchmarkResult) -> Bool {
  result.entry_count >= 0 &&
  result.state_count >= 1 &&
  result.input_chars >= 0 &&
  result.repeat_count >= 0 &&
  result.match_count >= 0 &&
  result.distinct_surface_count <= result.entry_count &&
  result.coverage_percent >= 0 &&
  result.coverage_percent <= 100
}

///|
/// Calculate a simple checksum for a benchmark result collection.
pub fn benchmark_checksum(results : ArrayView[BenchmarkResult]) -> Int {
  let mut checksum = 17
  for result in results {
    checksum = checksum * 31 + result.entry_count
    checksum = checksum * 31 + result.state_count
    checksum = checksum * 31 + result.input_chars
    checksum = checksum * 31 + result.match_count
    checksum = checksum * 31 + result.total_weight
  }
  checksum
}

///|
/// Return a short description of the standard workload.
pub fn benchmark_description() -> String {
  "deterministic numbered dictionary scan with structural counters"
}

///|
/// Return matches per one hundred input characters for a benchmark result.
pub fn benchmark_match_density(result : BenchmarkResult) -> Int {
  if result.input_chars <= 0 {
    0
  } else {
    result.match_count * 100 / result.input_chars
  }
}

///|
/// Return distinct matched surfaces as a percentage of the dictionary.
pub fn benchmark_unique_ratio(result : BenchmarkResult) -> Int {
  if result.entry_count <= 0 {
    0
  } else {
    result.distinct_surface_count * 100 / result.entry_count
  }
}

///|
/// Compute a small deterministic rolling checksum for text fixtures.
pub fn benchmark_text_checksum(text : String) -> Int {
  let mut checksum = 0
  for ch in text {
    checksum = checksum * 31 + ch.to_int()
  }
  checksum
}

///|
/// Return an operational summary suitable for a one-line CI log.
pub fn benchmark_result_summary(result : BenchmarkResult) -> String {
  "entries=\{result.entry_count} states=\{result.state_count} chars=\{result.input_chars} matches=\{result.match_count} coverage=\{result.coverage_percent}%"
}

///|
/// Return the ratio of matched characters to total input characters.
pub fn benchmark_coverage_ratio(result : BenchmarkResult) -> Int {
  result.coverage_percent
}

///|
/// Return true when all results have non-decreasing dictionary sizes.
pub fn benchmark_sizes_are_ordered(
  results : ArrayView[BenchmarkResult],
) -> Bool {
  for index in 1.. BenchmarkResult? {
  let mut best : BenchmarkResult? = None
  for result in results {
    match best {
      None => best = Some(result)
      Some(old) =>
        if result.input_chars > old.input_chars {
          best = Some(result)
        }
    }
  }
  best
}

///|
/// Return a deterministic checksum over a generated workload.
pub fn benchmark_workload_checksum(size : Int, repeats : Int) -> Int {
  benchmark_text_checksum(benchmark_text_for_size(size, repeats))
}

///|
/// Verify the counters that are independent of compiler backend.
pub fn benchmark_matches_expected(
  result : BenchmarkResult,
  dictionary_size : Int,
  repeats : Int,
) -> Bool {
  let expected_size = if dictionary_size < 0 { 0 } else { dictionary_size }
  let expected_repeats = if repeats < 0 { 0 } else { repeats }
  result.entry_count == expected_size &&
  result.repeat_count == expected_repeats &&
  result.distinct_surface_count <= expected_size
}

///|
/// Return matched surfaces as a percentage of emitted matches.
pub fn benchmark_surface_coverage(result : BenchmarkResult) -> Int {
  percentage(result.distinct_surface_count, result.match_count)
}