///| Deterministically encode an MPHF as integer words.

///|
/// Format: `[1, key_count, vertex_count, seed, attempts, g...]`.
pub fn Mphf::encode_words(self : Mphf) -> Array[Int] {
  let words : Array[Int] = [
    1,
    self.key_count,
    self.vertex_count,
    self.seed,
    self.attempts,
  ]
  words.append(self.values)
  words
}

///|
/// Decode a versioned MPHF word representation with structural validation.
pub fn decode_mphf_words(words : Array[Int]) -> Result[Mphf, MphfError] {
  if words.length() == 0 {
    return Err(MissingHeader)
  }
  if words[0] != 1 {
    return Err(UnsupportedVersion(words[0]))
  }
  if words.length() < 5 {
    return Err(InvalidPayloadLength(5, words.length()))
  }
  let key_count = words[1]
  let vertex_count = words[2]
  let seed = words[3]
  let attempts = words[4]
  if key_count <= 0 || vertex_count < 3 || seed < 0 || attempts <= 0 {
    return Err(InvalidMetadata)
  }
  if vertex_count > words.length() - 5 {
    return Err(InvalidMetadata)
  }
  let expected = 5 + vertex_count
  if words.length() != expected {
    return Err(InvalidPayloadLength(expected, words.length()))
  }
  let values : Array[Int] = []
  for index in 5..= key_count {
      return Err(InvalidVertexValue(value))
    }
    values.push(value)
  }
  Ok({ key_count, vertex_count, seed, attempts, values })
}

///| Deterministically encode an exact static set.

///|
/// Format: `[2, mphf_word_count, mphf_words..., slot_ordered_keys...]`.
pub fn StaticSet::encode_words(self : StaticSet) -> Array[Int] {
  let mphf_words = self.mphf.encode_words()
  let words : Array[Int] = [2, mphf_words.length()]
  words.append(mphf_words)
  words.append(self.keys_by_slot)
  words
}

///|
/// Decode an exact static set and verify every stored key agrees with its slot.
pub fn decode_static_set_words(
  words : Array[Int],
) -> Result[StaticSet, MphfError] {
  if words.length() == 0 {
    return Err(MissingHeader)
  }
  if words[0] != 2 {
    return Err(UnsupportedVersion(words[0]))
  }
  if words.length() < 2 {
    return Err(InvalidPayloadLength(2, words.length()))
  }
  let mphf_length = words[1]
  if mphf_length <= 0 || mphf_length > words.length() - 2 {
    return Err(InvalidMetadata)
  }
  let mphf_words : Array[Int] = []
  for index in 2..<(2 + mphf_length) {
    mphf_words.push(words[index])
  }
  let mphf = match decode_mphf_words(mphf_words) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  if mphf.key_count > words.length() - 2 - mphf_length {
    return Err(InvalidMetadata)
  }
  let expected = 2 + mphf_length + mphf.key_count
  if words.length() != expected {
    return Err(InvalidPayloadLength(expected, words.length()))
  }
  let keys_by_slot : Array[Int] = []
  for index in (2 + mphf_length).. ()
    Err(error) => return Err(error)
  }
  Ok({ mphf, keys_by_slot })
}

///| Deterministically encode an exact integer map.

///|
/// Format: `[3, mphf_word_count, mphf_words..., keys..., values...]`.
pub fn StaticIntMap::encode_words(self : StaticIntMap) -> Array[Int] {
  let mphf_words = self.mphf.encode_words()
  let words : Array[Int] = [3, mphf_words.length()]
  words.append(mphf_words)
  words.append(self.keys_by_slot)
  words.append(self.values_by_slot)
  words
}

///|
/// Decode an exact integer map and retain the validated MPHF slot layout.
pub fn decode_static_int_map_words(
  words : Array[Int],
) -> Result[StaticIntMap, MphfError] {
  if words.length() == 0 {
    return Err(MissingHeader)
  }
  if words[0] != 3 {
    return Err(UnsupportedVersion(words[0]))
  }
  if words.length() < 2 {
    return Err(InvalidPayloadLength(2, words.length()))
  }
  let mphf_length = words[1]
  if mphf_length <= 0 || mphf_length > words.length() - 2 {
    return Err(InvalidMetadata)
  }
  let mphf_words : Array[Int] = []
  for index in 2..<(2 + mphf_length) {
    mphf_words.push(words[index])
  }
  let mphf = match decode_mphf_words(mphf_words) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let remaining = words.length() - 2 - mphf_length
  if mphf.key_count > remaining / 2 {
    return Err(InvalidMetadata)
  }
  let expected = 2 + mphf_length + mphf.key_count * 2
  if words.length() != expected {
    return Err(InvalidPayloadLength(expected, words.length()))
  }
  let keys_by_slot : Array[Int] = []
  let values_by_slot : Array[Int] = []
  let keys_end = 2 + mphf_length + mphf.key_count
  for index in (2 + mphf_length).. ()
    Err(error) => return Err(error)
  }
  Ok({ mphf, keys_by_slot, values_by_slot })
}

///|
/// Validate that encoded slot keys are a permutation of one accepted key set.
fn validate_slot_keys(
  mphf : Mphf,
  keys_by_slot : Array[Int],
) -> Result[Unit, MphfError] {
  if keys_by_slot.length() != mphf.key_count {
    return Err(InvalidPayloadLength(mphf.key_count, keys_by_slot.length()))
  }
  let ordered = keys_by_slot.copy()
  ordered.sort()
  for index in 0.. 0 && ordered[index - 1] == ordered[index] {
      return Err(DuplicateKey(ordered[index]))
    }
  }
  Ok(())
}