///|
fn finite(value : Double) -> Bool {
  !value.is_nan() && !value.is_inf()
}

///|
pub fn bisect_report(
  lower : Double,
  upper : Double,
  f : (Double) -> Double,
  settings? : SolverSettings = SolverSettings::default(),
) -> SolverReport {
  if !finite(lower) || !finite(upper) || lower > upper {
    { status: InvalidRange, root: lower, residual: 0.0, iterations: 0 }
  } else {
    let fl = f(lower)
    let fu = f(upper)
    if !finite(fl) || !finite(fu) {
      { status: InvalidRange, root: lower, residual: fl, iterations: 0 }
    } else if fl == 0.0 {
      { status: Converged, root: lower, residual: fl, iterations: 0 }
    } else if fu == 0.0 {
      { status: Converged, root: upper, residual: fu, iterations: 0 }
    } else if fl * fu > 0.0 {
      let mid = (lower + upper) / 2.0
      { status: BracketFailure, root: mid, residual: f(mid), iterations: 0 }
    } else {
      for lo = lower, hi = upper, flo = fl, i = 0; i < settings.max_iterations; {
        let mid = (lo + hi) / 2.0
        let fm = f(mid)
        if !finite(fm) {
          break {
            status: InvalidRange,
            root: mid,
            residual: fm,
            iterations: i + 1,
          }
        }
        if fm.abs() <= settings.tolerance ||
          (hi - lo).abs() <= settings.tolerance {
          break {
            status: Converged,
            root: mid,
            residual: fm,
            iterations: i + 1,
          }
        }
        if flo * fm <= 0.0 {
          continue lo, mid, flo, i + 1
        } else {
          continue mid, hi, fm, i + 1
        }
      } nobreak {
        let mid = (lower + upper) / 2.0
        {
          status: IterationLimit,
          root: mid,
          residual: f(mid),
          iterations: settings.max_iterations,
        }
      }
    }
  }
}

///|
pub fn integrate_trapezoid_report(
  lower : Double,
  upper : Double,
  steps : Int,
  f : (Double) -> Double,
) -> SolverReport {
  if steps <= 0 || !finite(lower) || !finite(upper) {
    { status: InvalidRange, root: 0.0, residual: 0.0, iterations: 0 }
  } else {
    {
      status: Converged,
      root: integrate_trapezoid(lower, upper, steps, f),
      residual: 0.0,
      iterations: steps,
    }
  }
}