{-# OPTIONS_GHC -Wall #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  ToySolver.Arith.Simplex.Simple
-- Copyright   :  (c) Masahiro Sakai 2016
-- License     :  BSD-style
-- 
-- Maintainer  :  masahiro.sakai@gmail.com
-- Stability   :  provisional
-- Portability :  portable
--
-----------------------------------------------------------------------------
module ToySolver.Arith.Simplex.Simple
  ( Model
  , OptDir (..)
  , OptResult (..)
  , check
  , optimize
  ) where

import Control.Monad
import Control.Monad.ST
import Data.Default.Class
import qualified Data.IntMap.Strict as IntMap
import qualified Data.IntSet as IntSet
import qualified ToySolver.Data.LA as LA
import ToySolver.Data.IntVar hiding (Model)
import qualified ToySolver.Arith.Simplex as Simplex
import ToySolver.Arith.Simplex hiding (check, optimize)

check :: VarSet -> [LA.Atom Rational] -> Maybe Model
check vs as = runST $ do
  solver <- Simplex.newSolver
  s <- liftM IntMap.fromAscList $ forM (IntSet.toAscList vs) $ \v -> do
    v2 <- Simplex.newVar solver
    return (v, v2)
  let s' = fmap LA.var s
      mtrans m = fmap (m IntMap.!) s
  forM_ as $ \a -> do
    Simplex.assertAtomEx solver (fmap (LA.applySubst s') a)
  ret <- Simplex.check solver
  if ret then do
    m <- Simplex.getModel solver
    return $ Just $ mtrans m
  else
    return Nothing

optimize :: VarSet -> OptDir -> LA.Expr Rational -> [LA.Atom Rational] -> (OptResult, Maybe Model)
optimize vs dir obj as = runST $ do
  solver <- Simplex.newSolver
  s <- liftM IntMap.fromAscList $ forM (IntSet.toAscList vs) $ \v -> do
    v2 <- Simplex.newVar solver
    return (v, v2)
  let s' = fmap LA.var s
      mtrans m = fmap (m IntMap.!) s
  forM_ as $ \a -> do
    assertAtom solver (fmap (LA.applySubst s') a)
  Simplex.setOptDir solver dir
  Simplex.setObj solver obj
  ret <- Simplex.optimize solver def
  case ret of
    Optimum -> do
      m <- Simplex.getModel solver
      return $ (ret, Just (mtrans m))
    Unsat -> do
      return $ (ret, Nothing)
    Unbounded -> do
      m <- Simplex.getModel solver
      return $ (ret, Just (mtrans m))
    ObjLimit -> do
      error "should not happen"