package pl.edu.pw.mini.sg.solver

import java.io.Closeable
import squants.time.Time

/** Interfejs bazowy dla modeli solwerow
  *
  * Definiuje wszystko operacje dla modeli serwerow, w tym dodawanie zmiennych, tworzenie wyrazen liniowych,
  * dodawanie ograniczen, ustawienie funkcji celu i pobranie jej wartosci oraz metode do uruchomienia obliczen
 *
  * @tparam V Typ zmiennych solwera
  * @tparam L Typ wyrazen liniowych solwera
  * @tparam C Typ ograniczen solwera
  */
trait SolverModel[V <: SolverVariable, I, L <: SolverLinExpr[V, I], C <: SolverConstraint] extends Closeable{
  def addVariable(lowerBound: Double, upperBound: Double, obj: Double, variableType: VariableType, name: String) : V
  def addVariables(lowerBounds: Seq[Double], upperBounds: Seq[Double], objs: Seq[Double], variableTypes: Seq[VariableType], names: Seq[String]) : Array[V]
  def addVariables(lowerBounds: Double, upperBounds: Double, objs: Double, variableTypes:VariableType, names: Seq[String]) : Array[V]
  def createLinExpr() : L
  def addConstraint(lhsExpr: L, constraintType: ConstraintType, rhsExpr: L, name: String) : C
  def addConstraints(expr: Vector[L], constraintType: Array[ConstraintType],
    rhs: Array[Double], name: Array[String]) : Array[C]
  def addConstraint(lhsExpr: L, constraintType: ConstraintType, rhsVariable: V, name: String) : C
  def addConstraint(lhsExpr: L, constraintType: ConstraintType, rhs: Double, name: String) : C
  def addConstraint(lhsVariable: V, constraintType: ConstraintType, rhsVariable: V, name: String) : C
  def addConstraint(lhsVariable: V, constraintType: ConstraintType, rhs: Double, name: String) : C
  def removeConstraint(constraint: C)
  def setObjective(solverExpr: L)
  def setObjectiveType(objectiveType: ObjectiveType)
  def getObjective: Double
  def optimize(): ModelSolutionType
  def optimize(timeout: Time) : ModelWithTimeoutSolutionType
  /** Aktualizacja stanu solwera.
    *
    * Niektore solwery dzialaja w sposob leniwy (np. Gurobi) i nie dodaja do modelu zmiennych az zostana
    * wystartowane obliczenia lub az wywolamy recznie update.
    */
  def update()
}
