///|
pub fn absolute(x : Double) -> Double {
  x.abs()
}

///|
pub fn nearly_equal(
  a : Double,
  b : Double,
  tolerance? : Double = 1.0e-8,
) -> Bool {
  (a - b).abs() <= tolerance
}

///|
pub fn bisect(
  lower : Double,
  upper : Double,
  f : (Double) -> Double,
  settings? : SolverSettings = SolverSettings::default(),
) -> BracketResult {
  let report = bisect_report(lower, upper, f, settings~)
  {
    root: report.root,
    residual: report.residual,
    iterations: report.iterations,
    converged: report.status == Converged,
  }
}

///|
pub fn integrate_trapezoid(
  lower : Double,
  upper : Double,
  steps : Int,
  f : (Double) -> Double,
) -> Double {
  let n = steps.max(1)
  let h = (upper - lower) / Double::from_int(n)
  let middle = for i = 1, acc = 0.0; i < n; i = i + 1 {
    continue i + 1, acc + f(lower + Double::from_int(i) * h)
  } nobreak {
    acc
  }
  (f(lower) + f(upper) + 2.0 * middle) * h / 2.0
}

///|
pub fn euler_integrate(
  initial : Double,
  dt : Double,
  steps : Int,
  derivative : (Double, Double) -> Double,
) -> Double {
  for i = 0, y = initial; i < steps; i = i + 1 {
    let t = Double::from_int(i) * dt
    continue i + 1, y + dt * derivative(t, y)
  } nobreak {
    y
  }
}