///|
pub(all) suberror VleError {
  EmptyMixture
  LengthMismatch(expected~ : Int, actual~ : Int)
  InvalidFraction(index~ : Int, value~ : Double)
  NonPositiveTemperature(Double)
  NonPositivePressure(Double)
  MissingBinaryParameter(i~ : Int, j~ : Int)
  SolverDidNotBracket(low~ : Double, high~ : Double)
  SolverDidNotConverge(iterations~ : Int)
  InvalidSolverOptions(String)
  InvalidParameter(String)
  InvalidRange(low~ : Double, high~ : Double)
} derive(Debug, Eq)

///|
pub(all) enum PhaseKind {
  Vapor
  Liquid
  Solid
} derive(Debug, Eq)

///|
pub struct Antoine {
  a : Double
  b : Double
  c : Double
  t_min_k : Double
  t_max_k : Double
  note : String
} derive(Debug)

///|
pub fn Antoine::new(
  a~ : Double,
  b~ : Double,
  c~ : Double,
  t_min_k~ : Double,
  t_max_k~ : Double,
  note~ : String,
) -> Antoine {
  { a, b, c, t_min_k, t_max_k, note }
}

///|
pub struct SolverOptions {
  tolerance : Double
  max_iterations : Int
} derive(Debug)

///|
pub fn SolverOptions::new(
  tolerance~ : Double,
  max_iterations~ : Int,
) -> SolverOptions raise VleError {
  if tolerance <= 0.0 {
    raise VleError::InvalidSolverOptions("tolerance must be positive")
  }
  if max_iterations <= 0 {
    raise VleError::InvalidSolverOptions("max_iterations must be positive")
  }
  { tolerance, max_iterations }
}

///|
pub struct SolverReport {
  root : Double
  residual : Double
  iterations : Int
  converged : Bool
} derive(Debug)

///|
pub fn SolverReport::new(
  root~ : Double,
  residual~ : Double,
  iterations~ : Int,
  converged~ : Bool,
) -> SolverReport {
  { root, residual, iterations, converged }
}

///|
pub struct HeatCapacityPolynomial {
  coefficients : Array[Double]
  t_min_k : Double
  t_max_k : Double
  source : String
} derive(Debug)

///|
pub fn HeatCapacityPolynomial::new(
  coefficients~ : Array[Double],
  t_min_k~ : Double,
  t_max_k~ : Double,
  source~ : String,
) -> HeatCapacityPolynomial raise VleError {
  if coefficients.length() == 0 {
    raise VleError::InvalidParameter("heat capacity needs coefficients")
  }
  if t_min_k <= 0.0 || t_max_k <= t_min_k {
    raise VleError::InvalidRange(low=t_min_k, high=t_max_k)
  }
  { coefficients, t_min_k, t_max_k, source }
}

///|
pub struct ThermoProperty {
  component : Component
  molecular_weight_g_per_mol : Double
  liquid_heat_capacity : HeatCapacityPolynomial
  vapor_heat_capacity : HeatCapacityPolynomial
  reference_temperature_k : Double
  normal_boiling_temperature_k : Double
  latent_heat_j_per_mol : Double
  source : String
} derive(Debug)

///|
pub fn ThermoProperty::new(
  component~ : Component,
  molecular_weight_g_per_mol~ : Double,
  liquid_heat_capacity~ : HeatCapacityPolynomial,
  vapor_heat_capacity~ : HeatCapacityPolynomial,
  reference_temperature_k~ : Double,
  normal_boiling_temperature_k~ : Double,
  latent_heat_j_per_mol~ : Double,
  source~ : String,
) -> ThermoProperty raise VleError {
  if molecular_weight_g_per_mol <= 0.0 || latent_heat_j_per_mol <= 0.0 {
    raise VleError::InvalidParameter("thermodynamic property must be positive")
  }
  assert_temperature(reference_temperature_k)
  assert_temperature(normal_boiling_temperature_k)
  {
    component,
    molecular_weight_g_per_mol,
    liquid_heat_capacity,
    vapor_heat_capacity,
    reference_temperature_k,
    normal_boiling_temperature_k,
    latent_heat_j_per_mol,
    source,
  }
}

///|
pub struct EnergyFlashResult {
  temperature_k : Double
  pressure_bar : Double
  vapor_fraction : Double
  liquid : Array[Double]
  vapor : Array[Double]
  enthalpy_j_per_mol : Double
  iterations : Int
} derive(Debug)

///|
pub fn EnergyFlashResult::new(
  temperature_k~ : Double,
  pressure_bar~ : Double,
  vapor_fraction~ : Double,
  liquid~ : Array[Double],
  vapor~ : Array[Double],
  enthalpy_j_per_mol~ : Double,
  iterations~ : Int,
) -> EnergyFlashResult {
  {
    temperature_k,
    pressure_bar,
    vapor_fraction,
    liquid,
    vapor,
    enthalpy_j_per_mol,
    iterations,
  }
}

///|
pub struct ActivityFlashResult {
  temperature_k : Double
  pressure_bar : Double
  vapor_fraction : Double
  liquid : Array[Double]
  vapor : Array[Double]
  k_values : Array[Double]
  iterations : Int
  converged : Bool
  residual : Double
} derive(Debug)

///|
pub fn ActivityFlashResult::new(
  temperature_k~ : Double,
  pressure_bar~ : Double,
  vapor_fraction~ : Double,
  liquid~ : Array[Double],
  vapor~ : Array[Double],
  k_values~ : Array[Double],
  iterations~ : Int,
  converged~ : Bool,
  residual~ : Double,
) -> ActivityFlashResult {
  {
    temperature_k,
    pressure_bar,
    vapor_fraction,
    liquid,
    vapor,
    k_values,
    iterations,
    converged,
    residual,
  }
}

///|
pub struct BinarySplitResult {
  feed : Array[Double]
  distillate : Array[Double]
  bottoms : Array[Double]
  distillate_total : Double
  bottoms_total : Double
  component_balance_error : Double
} derive(Debug)

///|
pub fn BinarySplitResult::new(
  feed~ : Array[Double],
  distillate~ : Array[Double],
  bottoms~ : Array[Double],
  distillate_total~ : Double,
  bottoms_total~ : Double,
  component_balance_error~ : Double,
) -> BinarySplitResult {
  {
    feed,
    distillate,
    bottoms,
    distillate_total,
    bottoms_total,
    component_balance_error,
  }
}

///|
pub struct ShortcutColumnResult {
  minimum_stages : Double
  minimum_reflux : Double
  operating_reflux : Double
  estimated_stages : Int
  feed : Array[Double]
  distillate : Array[Double]
  bottoms : Array[Double]
} derive(Debug)

///|
pub struct AntoineObservation {
  temperature_k : Double
  pressure_bar : Double
} derive(Debug)

///|
pub fn AntoineObservation::new(
  temperature_k~ : Double,
  pressure_bar~ : Double,
) -> AntoineObservation {
  { temperature_k, pressure_bar }
}

///|
pub struct AntoineFitResult {
  a : Double
  b : Double
  c : Double
  rmse_bar : Double
  max_error_bar : Double
  points : Int
  converged : Bool
} derive(Debug)

///|
pub struct BenchmarkSummary {
  total : Int
  passed : Int
  failed : Int
  pass_rate : Double
  mean_error : Double
  max_error : Double
} derive(Debug)

///|
pub struct ClausiusClapeyron {
  reference_pressure_bar : Double
  reference_temperature_k : Double
  enthalpy_vaporization_j_per_mol : Double
  source : String
} derive(Debug)

///|
pub fn ClausiusClapeyron::new(
  reference_pressure_bar~ : Double,
  reference_temperature_k~ : Double,
  enthalpy_vaporization_j_per_mol~ : Double,
  source~ : String,
) -> ClausiusClapeyron {
  {
    reference_pressure_bar,
    reference_temperature_k,
    enthalpy_vaporization_j_per_mol,
    source,
  }
}

///|
pub struct ExtendedAntoine {
  a : Double
  b : Double
  c : Double
  d : Double
  e : Double
  t_min_k : Double
  t_max_k : Double
  source : String
} derive(Debug)

///|
pub fn ExtendedAntoine::new(
  a~ : Double,
  b~ : Double,
  c~ : Double,
  d~ : Double,
  e~ : Double,
  t_min_k~ : Double,
  t_max_k~ : Double,
  source~ : String,
) -> ExtendedAntoine {
  { a, b, c, d, e, t_min_k, t_max_k, source }
}

///|
pub struct CorrelationComparison {
  name : String
  points : Int
  mean_relative_difference : Double
  maximum_relative_difference : Double
} derive(Debug)

///|
pub(all) enum PhaseRegime {
  SingleLiquid
  TwoPhase
  SingleVapor
} derive(Debug, Eq)

///|
pub struct KValueDiagnostics {
  regime : PhaseRegime
  f_at_zero : Double
  f_at_one : Double
  minimum_k : Double
  maximum_k : Double
  stability_margin : Double
} derive(Debug)

///|
pub struct MatrixStatistics {
  dimension : Int
  minimum : Double
  maximum : Double
  maximum_asymmetry : Double
  diagonal_error : Double
} derive(Debug)

///|
pub struct EquilibriumProfilePoint {
  liquid_fraction : Double
  vapor_fraction : Double
  relative_volatility : Double
} derive(Debug)

///|
pub struct EquilibriumAudit {
  is_valid : Bool
  maximum_composition_error : Double
  pressure_error : Double
  material_balance_error : Double
  messages : Array[String]
} derive(Debug)

///|
pub struct ParameterRecord {
  name : String
  value : Double
  unit : String
  lower_bound : Double
  upper_bound : Double
  source : String
} derive(Debug)

///|
pub fn ParameterRecord::new(
  name~ : String,
  value~ : Double,
  unit~ : String,
  lower_bound~ : Double,
  upper_bound~ : Double,
  source~ : String,
) -> ParameterRecord {
  { name, value, unit, lower_bound, upper_bound, source }
}

///|
pub struct ParameterValidationReport {
  valid : Bool
  issue_count : Int
  checked_count : Int
  messages : Array[String]
} derive(Debug)

///|
pub struct SensitivityPoint {
  temperature_k : Double
  pressure_bar : Double
  derivative_bar_per_k : Double
  relative_sensitivity : Double
} derive(Debug)

///|
pub struct ValidationSummary {
  valid : Bool
  error_count : Int
  warning_count : Int
  messages : Array[String]
} derive(Debug)

///|
pub struct Component {
  name : String
  formula : String
  cas : String
  antoine : Antoine
} derive(Debug)

///|
pub fn Component::new(
  name~ : String,
  formula~ : String,
  cas~ : String,
  antoine~ : Antoine,
) -> Component {
  { name, formula, cas, antoine }
}

///|
pub struct PhasePoint {
  temperature_k : Double
  pressure_bar : Double
  composition : Array[Double]
  phase : PhaseKind
} derive(Debug)

///|
pub fn PhasePoint::new(
  temperature_k~ : Double,
  pressure_bar~ : Double,
  composition~ : Array[Double],
  phase~ : PhaseKind,
) -> PhasePoint {
  { temperature_k, pressure_bar, composition, phase }
}

///|
pub struct EquilibriumPoint {
  temperature_k : Double
  pressure_bar : Double
  liquid : Array[Double]
  vapor : Array[Double]
  iterations : Int
} derive(Debug)

///|
pub fn EquilibriumPoint::new(
  temperature_k~ : Double,
  pressure_bar~ : Double,
  liquid~ : Array[Double],
  vapor~ : Array[Double],
  iterations~ : Int,
) -> EquilibriumPoint {
  { temperature_k, pressure_bar, liquid, vapor, iterations }
}

///|
pub struct SolidSolubility {
  component : Component
  solvent : Component
  temperature_k : Double
  mole_fraction : Double
  source : String
} derive(Debug)

///|
pub fn SolidSolubility::new(
  component~ : Component,
  solvent~ : Component,
  temperature_k~ : Double,
  mole_fraction~ : Double,
  source~ : String,
) -> SolidSolubility {
  { component, solvent, temperature_k, mole_fraction, source }
}

///|
pub struct Dippr101 {
  a : Double
  b : Double
  c : Double
  d : Double
  e : Double
  t_min_k : Double
  t_max_k : Double
  source : String
} derive(Debug)

///|
pub fn Dippr101::new(
  a~ : Double,
  b~ : Double,
  c~ : Double,
  d~ : Double,
  e~ : Double,
  t_min_k~ : Double,
  t_max_k~ : Double,
  source~ : String,
) -> Dippr101 {
  { a, b, c, d, e, t_min_k, t_max_k, source }
}

///|
pub struct Wagner {
  a : Double
  b : Double
  c : Double
  d : Double
  critical_temperature_k : Double
  critical_pressure_bar : Double
  t_min_k : Double
  t_max_k : Double
  source : String
} derive(Debug)

///|
pub fn Wagner::new(
  a~ : Double,
  b~ : Double,
  c~ : Double,
  d~ : Double,
  critical_temperature_k~ : Double,
  critical_pressure_bar~ : Double,
  t_min_k~ : Double,
  t_max_k~ : Double,
  source~ : String,
) -> Wagner {
  {
    a,
    b,
    c,
    d,
    critical_temperature_k,
    critical_pressure_bar,
    t_min_k,
    t_max_k,
    source,
  }
}

///|
pub struct FlashResult {
  temperature_k : Double
  pressure_bar : Double
  vapor_fraction : Double
  liquid : Array[Double]
  vapor : Array[Double]
  iterations : Int
} derive(Debug)

///|
pub fn FlashResult::new(
  temperature_k~ : Double,
  pressure_bar~ : Double,
  vapor_fraction~ : Double,
  liquid~ : Array[Double],
  vapor~ : Array[Double],
  iterations~ : Int,
) -> FlashResult {
  { temperature_k, pressure_bar, vapor_fraction, liquid, vapor, iterations }
}

///|
pub struct BenchmarkPoint {
  system : String
  temperature_k : Double
  pressure_bar : Double
  liquid : Array[Double]
  vapor : Array[Double]
  source : String
  tolerance : Double
} derive(Debug)

///|
pub fn BenchmarkPoint::new(
  system~ : String,
  temperature_k~ : Double,
  pressure_bar~ : Double,
  liquid~ : Array[Double],
  vapor~ : Array[Double],
  source~ : String,
  tolerance~ : Double,
) -> BenchmarkPoint raise VleError {
  assert_temperature(temperature_k)
  assert_pressure(pressure_bar)
  if tolerance <= 0.0 {
    raise VleError::InvalidParameter("benchmark tolerance must be positive")
  }
  {
    system,
    temperature_k,
    pressure_bar,
    liquid: normalize(liquid),
    vapor: normalize(vapor),
    source,
    tolerance,
  }
}

///|
pub struct PurePressureBenchmark {
  component : String
  temperature_k : Double
  pressure_bar : Double
  source : String
  tolerance : Double
} derive(Debug)

///|
pub fn PurePressureBenchmark::new(
  component~ : String,
  temperature_k~ : Double,
  pressure_bar~ : Double,
  source~ : String,
  tolerance~ : Double,
) -> PurePressureBenchmark raise VleError {
  assert_temperature(temperature_k)
  assert_pressure(pressure_bar)
  if tolerance <= 0.0 {
    raise VleError::InvalidParameter("pressure tolerance must be positive")
  }
  { component, temperature_k, pressure_bar, source, tolerance }
}