///|
fn candidate_record_order(
  left : IndexCandidateRecord,
  right : IndexCandidateRecord,
) -> Int {
  if left.record.id < right.record.id {
    -1
  } else if left.record.id > right.record.id {
    1
  } else {
    0
  }
}

///|
fn candidate_shared_keys(
  index : NameIndex,
  id : String,
  query_keys : Array[String],
) -> Array[String] {
  let shared : Array[String] = []
  match index.record_bucket_keys.get(id) {
    None => ()
    Some(record_keys) =>
      for query_key in query_keys {
        if record_keys.contains(query_key) {
          shared.push(query_key)
        }
      }
  }
  shared
}

///|
/// Returns the unscored records admitted by shared phonetic blocking keys.
pub fn NameIndex::candidate_records(
  self : NameIndex,
  name : String,
) -> Result[IndexCandidateSet, IndexError] {
  let (normalized_query, query_bucket_keys) = match
    prepare_index_name(name, self.config, self.blocking_algorithms) {
    Err(error) => return Err(error)
    Ok(value) => value
  }
  let candidate_ids = collect_index_candidates(self, query_bucket_keys)
  let records : Array[IndexCandidateRecord] = []
  for id in candidate_ids {
    let record = match self.records.get(id) {
      Some(value) => value
      None => continue
    }
    let normalized_name = match self.normalized.get(id) {
      Some(value) => value
      None => continue
    }
    records.push({
      record,
      normalized_name,
      shared_bucket_keys: candidate_shared_keys(self, id, query_bucket_keys),
    })
  }
  records.sort_by(candidate_record_order)
  Ok({ query: name, normalized_query, query_bucket_keys, records })
}

///|
/// Reports deterministic aggregate counts for the current in-memory index.
pub fn NameIndex::statistics(self : NameIndex) -> IndexStatistics {
  let mut key_assignment_count = 0
  let mut largest_bucket_size = 0
  for entry in self.buckets.to_array() {
    let size = entry.1.length()
    key_assignment_count = key_assignment_count + size
    if size > largest_bucket_size {
      largest_bucket_size = size
    }
  }
  let bucket_count = self.buckets.length()
  let average_bucket_size = if bucket_count == 0 {
    0.0
  } else {
    key_assignment_count.to_double() / bucket_count.to_double()
  }
  {
    record_count: self.records.length(),
    bucket_count,
    key_assignment_count,
    largest_bucket_size,
    average_bucket_size,
  }
}