///|
pub enum DetectError {
  InvalidHazard
  InvalidVariance
  InvalidPriorStrength
  InvalidRunLengthLimit
  NonFiniteObservation
}

///|
pub struct GaussianModel {
  prior_mean : Double
  prior_strength : Double
  observation_variance : Double
}

///|
pub fn GaussianModel::new(
  prior_mean : Double,
  prior_strength : Double,
  observation_variance : Double,
) -> Result[GaussianModel, DetectError] {
  if !finite(prior_mean) {
    return Err(NonFiniteObservation)
  }
  if !finite(prior_strength) || prior_strength <= 0.0 {
    return Err(InvalidPriorStrength)
  }
  if !finite(observation_variance) || observation_variance <= 0.0 {
    return Err(InvalidVariance)
  }
  Ok({ prior_mean, prior_strength, observation_variance, })
}

///|
pub struct Config {
  hazard : Double
  hazard_log : Double
  survival_log : Double
  max_run_length : Int
  model : GaussianModel
}

///|
pub fn Config::new(
  hazard : Double,
  max_run_length : Int,
  model : GaussianModel,
) -> Result[Config, DetectError] {
  if !finite(hazard) || hazard <= 0.0 || hazard >= 1.0 {
    return Err(InvalidHazard)
  }
  if max_run_length < 2 {
    return Err(InvalidRunLengthLimit)
  }
  Ok({
    hazard,
    hazard_log: @math.ln(hazard),
    survival_log: @math.ln(1.0 - hazard),
    max_run_length,
    model,
  })
}

///|
pub struct Update {
  step : Int
  observation : Double
  change_probability : Double
  most_likely_run_length : Int
  posterior_mean : Double
  retained_run_lengths : Int
}

///|
pub struct Snapshot {
  step : Int
  log_posterior : Array[Double]
  counts : Array[Int]
  sums : Array[Double]
}

///|
pub fn Update::step(self : Update) -> Int {
  self.step
}

///|
pub fn Update::change_probability(self : Update) -> Double {
  self.change_probability
}

///|
pub fn Update::most_likely_run_length(self : Update) -> Int {
  self.most_likely_run_length
}