///|
/// Small diagnostics used by CLI reports and CI smoke checks.
pub fn inlier_indices(
  residuals : ArrayView[Double],
  threshold : Double,
) -> Array[Int] {
  let result : Array[Int] = []
  for i in 0.. Double {
  if residuals.length() == 0 {
    0.0
  } else {
    1.0 -
    inlier_indices(residuals, threshold).length().to_double() /
    residuals.length().to_double()
  }
}

///|
pub fn median_inlier_residual(
  residuals : ArrayView[Double],
  threshold : Double,
) -> Double raise @core.GeometryError {
  let values : Array[Double] = []
  for residual in residuals {
    if @core.abs(residual) <= threshold {
      values.push(@core.abs(residual))
    }
  }
  @core.median(values)
}

///|
pub fn quality_label(
  inlier_ratio : Double,
  median_error : Double,
  good_ratio : Double,
  max_error : Double,
) -> String {
  if inlier_ratio >= good_ratio && median_error <= max_error {
    "good"
  } else if inlier_ratio >= good_ratio * 0.5 {
    "review"
  } else {
    "poor"
  }
}

///|
pub fn score_margin(best : Double, second : Double) -> Double {
  (best - second).max(0.0)
}

///|
pub fn stable_model(
  best : ModelReport,
  second : ModelReport,
  minimum_margin : Double,
) -> Bool {
  best.is_usable(0.0, 1) &&
  score_margin(best.score, second.score) >= minimum_margin
}