packages feed

toysolver-0.5.0: app/toyqbf.hs

{-# LANGUAGE ScopedTypeVariables, CPP #-}
{-# OPTIONS_GHC -Wall #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  toyqbf
-- Copyright   :  (c) Masahiro Sakai 2016
-- License     :  BSD-style
-- 
-- Maintainer  :  masahiro.sakai@gmail.com
-- Stability   :  experimental
-- Portability :  non-portable (ScopedTypeVariables, CPP)
--
-----------------------------------------------------------------------------

module Main where

import Control.Monad
import Data.Char
import Data.Default.Class
import qualified Data.IntSet as IntSet
import Data.List
import Data.Ord
import Data.Version
import System.Console.GetOpt
import System.Environment
import System.Exit
import System.IO

import ToySolver.Data.Boolean
import qualified ToySolver.Data.BoolExpr as BoolExpr
import qualified ToySolver.QBF as QBF
import qualified ToySolver.Text.QDimacs as QDimacs
import ToySolver.Internal.Util (setEncodingChar8)
import ToySolver.Version

data Mode
  = ModeHelp
  | ModeVersion
  deriving (Eq, Ord, Bounded, Enum)

data Options
  = Options
  { optMode :: Maybe Mode
  , optAlgorithm :: String
  }

instance Default Options where
  def =
    Options
    { optMode = Nothing
    , optAlgorithm = "cegar-incremental"
    }

options :: [OptDescr (Options -> Options)]
options =
    [ Option ['h'] ["help"]   (NoArg (\opt -> opt{ optMode = Just ModeHelp   })) "show help"
    , Option [] ["version"]   (NoArg (\opt -> opt{ optMode = Just ModeVersion})) "show version"
    , Option [] ["algorithm"]
        (ReqArg (\val opt -> opt{ optAlgorithm = val }) "<str>")
        "Algorithm: naive, cegar, cegar-incremental (default)"
    ]

main :: IO ()
main = do
#ifdef FORCE_CHAR8
  setEncodingChar8
#endif

  args <- getArgs
  case getOpt Permute options args of
    (_,_,errs@(_:_)) -> do
      mapM_ putStrLn errs
      exitFailure

    (o,args2,[]) -> do
      let opt = foldl (flip id) def o
      case optMode opt of
        Just ModeHelp -> showHelp stdout
        Just ModeVersion -> hPutStrLn stdout (showVersion version)
        Nothing -> do
          case args2 of
            [fname] -> do
              ret <- QDimacs.parseFile fname
              case ret of
                Left err -> hPutStrLn stderr err >> exitFailure
                Right qdimacs -> do
                  let nv = QDimacs.numVars qdimacs
                      nc = QDimacs.numClauses qdimacs
                      prefix' = QBF.quantifyFreeVariables nv [(q, IntSet.fromList xs) | (q,xs) <- QDimacs.prefix qdimacs]
                      matrix' = andB [orB [if lit > 0 then BoolExpr.Atom lit else notB (BoolExpr.Atom (abs lit)) | lit <- clause] | clause <- QDimacs.matrix qdimacs]
                  (ans, certificate) <-
                    case map toLower (optAlgorithm opt) of
                      "naive" -> QBF.solveNaive nv prefix' matrix'
                      "cegar" -> QBF.solveCEGAR nv prefix' matrix'
                      "cegar-incremental" -> QBF.solveCEGARIncremental nv prefix' matrix'
                      _ -> do
                        putStrLn $ "c unknown --algorithm option: " ++ show (optAlgorithm opt)
                        putStrLn $ "s cnf 0 " ++ show nv ++ " " ++ show nc
                        exitFailure
                  putStrLn $ "s cnf " ++ (if ans then "1" else "-1") ++ " " ++ show nv ++ " " ++ show nc
                  case certificate of
                    Nothing -> return ()
                    Just lits -> do
                      forM_ (sortBy (comparing abs) (IntSet.toList lits)) $ \lit -> do
                        putStrLn ("V " ++ show lit)
                  if ans then
                    exitWith (ExitFailure 10)
                  else
                    exitWith (ExitFailure 20)
            _ -> showHelp stderr >> exitFailure

showHelp :: Handle -> IO ()
showHelp h = hPutStrLn h (usageInfo header options)

header :: String
header = unlines
  [ "Usage:"
  , "  toyqbf [OPTION]... [file.qdimacs]"
  , "  toyqbf [OPTION]... [file.cnf]"
  , ""
  , "Options:"
  ]