///|
/// Running consensus state for early stopping and budget control.
pub(all) struct AdaptiveStopState {
  minimum_iterations : Int
  target_ratio : Double
  mut iterations : Int
  mut best_ratio : Double
  mut stable_iterations : Int
} derive(Debug, Eq)

///|
pub fn AdaptiveStopState::new(
  minimum_iterations~ : Int,
  target_ratio~ : Double,
) -> AdaptiveStopState {
  {
    minimum_iterations: minimum_iterations.max(0),
    target_ratio: target_ratio.max(0.0).min(1.0),
    iterations: 0,
    best_ratio: 0.0,
    stable_iterations: 0,
  }
}

///|
pub fn AdaptiveStopState::observe(
  state : AdaptiveStopState,
  inliers~ : Int,
  total~ : Int,
) -> Unit {
  let ratio = if total <= 0 {
    0.0
  } else {
    inliers.max(0).min(total).to_double() / total.to_double()
  }
  state.iterations += 1
  if ratio >= state.best_ratio {
    if ratio == state.best_ratio {
      state.stable_iterations += 1
    } else {
      state.stable_iterations = 0
    }
    state.best_ratio = ratio
  }
}

///|
pub fn AdaptiveStopState::should_stop(state : AdaptiveStopState) -> Bool {
  state.iterations >= state.minimum_iterations &&
  state.best_ratio >= state.target_ratio
}

///|
pub fn AdaptiveStopState::progress(
  state : AdaptiveStopState,
  maximum_iterations : Int,
) -> Double {
  if maximum_iterations <= 0 {
    0.0
  } else {
    state.iterations.min(maximum_iterations).to_double() /
    maximum_iterations.to_double()
  }
}

///|
pub fn AdaptiveStopState::remaining(
  state : AdaptiveStopState,
  maximum_iterations : Int,
) -> Int {
  (maximum_iterations - state.iterations).max(0)
}

///|
pub fn AdaptiveStopState::is_stable(
  state : AdaptiveStopState,
  minimum_repetitions : Int,
) -> Bool {
  state.stable_iterations >= minimum_repetitions
}

///|
pub fn adaptive_confidence(
  ratio : Double,
  sample_size : Int,
  iterations : Int,
) -> Double {
  let mut success = 1.0
  let mut one_sample = 1.0
  for _ in 0.. Int {
  let target = target_confidence.max(0.0).min(1.0)
  let mut iterations = 0
  while iterations < maximum.max(1) &&
        adaptive_confidence(ratio, sample_size, iterations) < target {
    iterations += 1
  }
  iterations.max(1).min(maximum.max(1))
}

///|
pub fn adaptive_budget_schedule(
  initial : Int,
  decay : Double,
  count : Int,
) -> Array[Int] {
  let result : Array[Int] = []
  let mut budget = initial.max(1).to_double()
  for _ in 0.. Array[Double] {
  let result : Array[Double] = []
  let mut threshold = initial.max(0.0)
  for _ in 0..