///|
/// Runnable example: minimum cost transportation with balanced supply and
/// demand (a classical LP with equality rows only).
///
///   min 2a + 3b + 1c + 4d
///   s.t. a + b = 3   (plant 1 capacity)
///        c + d = 2   (plant 2 capacity)
///        a + c = 2   (customer 1 demand)
///        b + d = 3   (customer 2 demand)
///        a, b, c, d >= 0
///
/// The optimum is 11 at (0, 3, 2, 0): every row is an equality, so the kernel
/// has to start from an artificial basis and drive it out — the Phase I path.
///
/// Run with: `moon run examples/transportation`
fn main {
  let m = @model.Model::new(@model.Sense::Minimize)
  let a = m.add_var("a")
  let b = m.add_var("b")
  let c = m.add_var("c")
  let d = m.add_var("d")
  m.set_objective([(a, 2.0), (b, 3.0), (c, 1.0), (d, 4.0)])
  m.add_named_constraint(
    [(a, 1.0), (b, 1.0)],
    @model.Rel::Equal,
    3.0,
    "plant_1",
  )
  m.add_named_constraint(
    [(c, 1.0), (d, 1.0)],
    @model.Rel::Equal,
    2.0,
    "plant_2",
  )
  m.add_named_constraint(
    [(a, 1.0), (c, 1.0)],
    @model.Rel::Equal,
    2.0,
    "customer_1",
  )
  m.add_named_constraint(
    [(b, 1.0), (d, 1.0)],
    @model.Rel::Equal,
    3.0,
    "customer_2",
  )
  let sol = @moonopt.solve(m)
  println("transportation: " + status_text(sol.status))
  if sol.status == @moonopt.SolveStatus::Optimal {
    let names : Array[String] = ["a", "b", "c", "d"]
    for i = 0; i < names.length(); i = i + 1 {
      println("  " + names[i] + " = " + sol.values[i].to_string())
    }
    println("  objective = " + sol.objective.to_string())
    println("  iterations = " + sol.iterations.to_string())
  } else {
    println("  " + sol.message)
  }
}

///|
/// Small local helper so the example stays readable.
fn status_text(status : @moonopt.SolveStatus) -> String {
  match status {
    @moonopt.SolveStatus::Optimal => "optimal"
    @moonopt.SolveStatus::Infeasible => "infeasible"
    @moonopt.SolveStatus::Unbounded => "unbounded"
    @moonopt.SolveStatus::NodeLimit => "stopped at the node budget"
    @moonopt.SolveStatus::NotSolved => "not solved"
  }
}