///|
/// Information-criterion comparison for fitted reliability models.
pub struct ModelComparison {
  names : Array[String]
  log_likelihoods : Array[Double]
  aic : Array[Double]
  bic : Array[Double]
  preferred_aic : Int
  preferred_bic : Int
}

///|
pub fn model_comparison(
  names~ : Array[String],
  log_likelihoods~ : Array[Double],
  aic~ : Array[Double],
  bic~ : Array[Double],
  preferred_aic~ : Int,
  preferred_bic~ : Int,
) -> ModelComparison {
  { names, log_likelihoods, aic, bic, preferred_aic, preferred_bic }
}

///|
pub fn compare_fits(fits : Array[FitResult]) -> ModelComparison {
  if fits.is_empty() {
    abort("model comparison requires fits")
  }
  let names = fits.map(fit => fit.distribution)
  let log_likelihoods = fits.map(fit => fit.log_likelihood)
  let aics = fits.map(fit => fit.aic)
  let bics = fits.map(fit => fit.bic)
  let mut best_aic = 0
  let mut best_bic = 0
  for i in 1.. Array[Double] {
  let minimum = min_value(comparison.aic)
  let raw = comparison.aic.map(value => @math.exp(-0.5 * (value - minimum)))
  let total = raw.fold(init=0.0, (sum, value) => sum + value)
  raw.map(value => value / total)
}

///|
pub fn likelihood_ratio(first : FitResult, second : FitResult) -> Double {
  2.0 * (second.log_likelihood - first.log_likelihood)
}

///|
pub fn cross_entropy(
  model : ReliabilityModel,
  records : Array[LifeObservation],
) -> Double {
  -model.log_likelihood(records) / records.length().to_double()
}

///|
pub fn root_mean_square_error(
  actual : Array[Double],
  predicted : Array[Double],
) -> Double {
  if actual.length() != predicted.length() || actual.is_empty() {
    abort("RMSE arrays mismatch")
  }
  let mut total = 0.0
  for i in 0.. Double {
  if actual.length() != predicted.length() || actual.is_empty() {
    abort("MAPE arrays mismatch")
  }
  let mut total = 0.0
  for i in 0.. (Array[Double], Array[Double]) {
  if fraction <= 0.0 || fraction >= 1.0 {
    abort("validation fraction must be in (0, 1)")
  }
  let split = (values.length().to_double() * fraction).to_int()
  (values[:split].to_owned(), values[split:].to_owned())
}