{-# LANGUAGE OverloadedStrings #-}

module Ersatz.Solver.Kissat
  ( kissat
  , kissatPath
  ) where

import Control.Monad.IO.Class
  ( MonadIO ( liftIO
            )
  )
import Ersatz.Problem
  ( SAT
  , writeDimacs'
  )
import Ersatz.Solution
  ( Solver
  )
import Ersatz.Solver.Common
  ( resultOf
  , withTempFiles
  , parseSolution5
  )
import System.Process
  ( readProcessWithExitCode
  )


-- | 'Solver' for 'SAT' problems that tries to invoke the @kissat@ executable
-- from the @PATH@.
kissat :: MonadIO m => Solver SAT m
kissat :: forall (m :: * -> *). MonadIO m => Solver SAT m
kissat = FilePath -> Solver SAT m
forall (m :: * -> *). MonadIO m => FilePath -> Solver SAT m
kissatPath FilePath
"kissat"

-- | 'Solver' for 'SAT' problems that tries to invoke a program that takes
-- @kissat@ compatible arguments.
--
-- The 'FilePath' refers to the path to the executable.
kissatPath :: MonadIO m => FilePath -> Solver SAT m
kissatPath :: forall (m :: * -> *). MonadIO m => FilePath -> Solver SAT m
kissatPath FilePath
path SAT
problem = IO (Result, IntMap Bool) -> m (Result, IntMap Bool)
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Result, IntMap Bool) -> m (Result, IntMap Bool))
-> IO (Result, IntMap Bool) -> m (Result, IntMap Bool)
forall a b. (a -> b) -> a -> b
$
  FilePath
-> FilePath
-> (FilePath -> FilePath -> IO (Result, IntMap Bool))
-> IO (Result, IntMap Bool)
forall (m :: * -> *) a.
MonadIO m =>
FilePath -> FilePath -> (FilePath -> FilePath -> IO a) -> m a
withTempFiles FilePath
".cnf" FilePath
"" ((FilePath -> FilePath -> IO (Result, IntMap Bool))
 -> IO (Result, IntMap Bool))
-> (FilePath -> FilePath -> IO (Result, IntMap Bool))
-> IO (Result, IntMap Bool)
forall a b. (a -> b) -> a -> b
$ \FilePath
problemPath FilePath
_ -> do
    FilePath -> SAT -> IO ()
forall (m :: * -> *) t.
(MonadIO m, DIMACS t) =>
FilePath -> t -> m ()
writeDimacs' FilePath
problemPath SAT
problem

    (ExitCode
exit, FilePath
out, FilePath
_err) <-
      FilePath
-> [FilePath] -> FilePath -> IO (ExitCode, FilePath, FilePath)
readProcessWithExitCode FilePath
path
                              [FilePath
problemPath]
                              []

    let sol :: IntMap Bool
sol = FilePath -> IntMap Bool
parseSolution5 FilePath
out

    (Result, IntMap Bool) -> IO (Result, IntMap Bool)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (ExitCode -> Result
resultOf ExitCode
exit, IntMap Bool
sol)