///|
/// Validated equilibrium relation `y*(x)` backed by measured points.
pub(all) struct EquilibriumTable {
  relation : LinearTable
} derive(Debug, Eq)

///|
pub fn EquilibriumTable::new(
  points : ArrayView[@domain.EquilibriumPoint],
  extrapolation? : Extrapolation = Reject,
) -> EquilibriumTable raise @domain.ExtractionError {
  let xs = [ for point in points => point.x ]
  let ys = [ for point in points => point.y ]
  { relation: LinearTable::new(xs, ys, extrapolation~) }
}

///|
pub fn EquilibriumTable::y_at(
  self : EquilibriumTable,
  x : Double,
) -> Double raise @domain.ExtractionError {
  self.relation.evaluate(x)
}

///|
pub fn EquilibriumTable::local_distribution_coefficient(
  self : EquilibriumTable,
  x : Double,
) -> Double raise @domain.ExtractionError {
  if x <= 0.0 {
    raise @domain.ExtractionError::InvalidArgument(
      message="distribution coefficient requires x > 0",
    )
  }
  self.y_at(x) / x
}

///|
/// Find `x` for a monotone increasing equilibrium table.
pub fn EquilibriumTable::x_at(
  self : EquilibriumTable,
  y : Double,
) -> Double raise @domain.ExtractionError {
  let ys = self.relation.ys
  for index in 1..