///|
/// Human- and machine-readable summary for an estimated model.
pub(all) struct ModelReport {
  iterations : Int
  inliers : Int
  total : Int
  ratio : Double
  score : Double
  converged : Bool
} derive(Debug, Eq)

///|
pub fn make_model_report(
  iterations~ : Int,
  inliers~ : Int,
  total~ : Int,
  score~ : Double,
  converged~ : Bool,
) -> ModelReport {
  {
    iterations: iterations.max(0),
    inliers: inliers.max(0).min(total.max(0)),
    total: total.max(0),
    ratio: if total <= 0 {
      0.0
    } else {
      inliers.to_double() / total.to_double()
    },
    score,
    converged,
  }
}

///|
pub fn ModelReport::is_usable(
  report : ModelReport,
  minimum_ratio : Double,
  minimum_inliers : Int,
) -> Bool {
  report.converged &&
  report.inliers >= minimum_inliers &&
  report.ratio >= minimum_ratio
}

///|
pub fn ModelReport::confidence(report : ModelReport) -> Double {
  (report.ratio * (1.0 - 1.0 / (report.iterations + 1).to_double()))
  .max(0.0)
  .min(1.0)
}

///|
pub fn rank_model_reports(
  reports : ArrayView[ModelReport],
) -> Array[ModelReport] {
  let result = reports.to_owned()
  result.sort_by(fn(a, b) {
    if a.inliers != b.inliers {
      b.inliers - a.inliers
    } else if a.score > b.score {
      -1
    } else if a.score < b.score {
      1
    } else {
      0
    }
  })
  result
}

///|
pub fn best_model_report(reports : ArrayView[ModelReport]) -> ModelReport? {
  let ranked = rank_model_reports(reports)
  if ranked.length() == 0 {
    None
  } else {
    Some(ranked[0])
  }
}

///|
pub fn report_summary(reports : ArrayView[ModelReport]) -> (Int, Int, Double) {
  let mut usable = 0
  let mut total = 0
  let mut score = 0.0
  for report in reports {
    if report.converged {
      usable += 1
    }
    total += report.inliers
    score += report.score
  }
  (usable, total, score)
}