///| Draft proposals are immutable token paths. Keeping the proposal separate

///| from a model provider makes the verifier usable with simulations, traces,

///|
/// and real inference backends.
pub enum DraftError {
  EmptyProposal
  TokenOutOfRange(Int)
  InvalidDistribution(Int)
  LengthMismatch
} derive(Eq, Debug)

///|
pub struct DraftToken {
  token : Int
  distribution : Array[Double]
}

///|
pub struct DraftProposal {
  prefix : Array[Int]
  tokens : Array[DraftToken]
}

///|
pub fn DraftProposal::length(self : DraftProposal) -> Int {
  self.tokens.length()
}

///|
pub fn DraftProposal::proposed_tokens(self : DraftProposal) -> Array[Int] {
  let output : Array[Int] = []
  for item in self.tokens {
    output.push(item.token)
  }
  output
}

///|
pub fn DraftProposal::full_path(self : DraftProposal) -> Array[Int] {
  let output : Array[Int] = []
  for token in self.prefix {
    output.push(token)
  }
  for item in self.tokens {
    output.push(item.token)
  }
  output
}

///|
fn distribution_is_valid(values : Array[Double]) -> Bool {
  if values.length() == 0 {
    return false
  }
  let mut total = 0.0
  for value in values {
    if !finite_number(value) || value < 0.0 || value > 1.0 {
      return false
    }
    total = total + value
  }
  (total - 1.0).abs() < 0.000000001
}

///|
pub fn validate_proposal(proposal : DraftProposal) -> Result[Unit, DraftError] {
  if proposal.tokens.length() == 0 {
    return Err(EmptyProposal)
  }
  for item in proposal.tokens {
    if !distribution_is_valid(item.distribution) {
      return Err(InvalidDistribution(item.token))
    }
    if item.token < 0 || item.token >= item.distribution.length() {
      return Err(TokenOutOfRange(item.token))
    }
    if item.distribution[item.token] <= 0.0 ||
      item.distribution.length() != proposal.tokens[0].distribution.length() {
      return Err(InvalidDistribution(item.token))
    }
  }
  Ok(())
}

///|
pub fn make_proposal(
  prefix : Array[Int],
  tokens : Array[Int],
  distributions : Array[Array[Double]],
) -> Result[DraftProposal, DraftError] {
  if tokens.length() == 0 {
    return Err(EmptyProposal)
  }
  if tokens.length() != distributions.length() {
    return Err(LengthMismatch)
  }
  let items : Array[DraftToken] = []
  for index in 0.. Ok(proposal)
    Err(error) => Err(error)
  }
}