{-# OPTIONS_GHC -Wall #-}
{-# OPTIONS_HADDOCK show-extensions #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}
module Numeric.Optimization.MIP
(
module Numeric.Optimization.MIP.Base
, readFile
, readLPFile
, readMPSFile
, parseLPString
, parseMPSString
, ParseError
, writeFile
, writeLPFile
, writeMPSFile
, toLPString
, toMPSString
) where
import Prelude hiding (readFile, writeFile)
import Control.Exception
import Data.Char
import Data.Scientific (Scientific)
import Data.String
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.IO as TLIO
import System.FilePath (takeExtension, splitExtension)
import System.IO hiding (readFile, writeFile)
import Text.Megaparsec (Stream (..))
import Numeric.Optimization.MIP.Base
import Numeric.Optimization.MIP.FileUtils (ParseError)
import qualified Numeric.Optimization.MIP.LPFile as LPFile
import qualified Numeric.Optimization.MIP.MPSFile as MPSFile
#ifdef WITH_ZLIB
import qualified Codec.Compression.GZip as GZip
import qualified Data.ByteString.Lazy as BL
import Data.ByteString.Lazy.Encoding (encode, decode)
import qualified Data.CaseInsensitive as CI
import GHC.IO.Encoding (getLocaleEncoding)
#endif
readFile :: FileOptions -> FilePath -> IO (Problem Scientific)
readFile :: FileOptions -> FilePath -> IO (Problem Scientific)
readFile FileOptions
opt FilePath
fname =
case FilePath -> FilePath
getExt FilePath
fname of
FilePath
".lp" -> FileOptions -> FilePath -> IO (Problem Scientific)
readLPFile FileOptions
opt FilePath
fname
FilePath
".mps" -> FileOptions -> FilePath -> IO (Problem Scientific)
readMPSFile FileOptions
opt FilePath
fname
FilePath
ext -> IOError -> IO (Problem Scientific)
forall a. IOError -> IO a
ioError (IOError -> IO (Problem Scientific))
-> IOError -> IO (Problem Scientific)
forall a b. (a -> b) -> a -> b
$ FilePath -> IOError
userError (FilePath -> IOError) -> FilePath -> IOError
forall a b. (a -> b) -> a -> b
$ FilePath
"unknown extension: " FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
ext
readLPFile :: FileOptions -> FilePath -> IO (Problem Scientific)
#ifndef WITH_ZLIB
readLPFile = LPFile.parseFile
#else
readLPFile :: FileOptions -> FilePath -> IO (Problem Scientific)
readLPFile FileOptions
opt FilePath
fname = do
Text
s <- FileOptions -> FilePath -> IO Text
readTextFile FileOptions
opt FilePath
fname
let ret :: Either (ParseError Text) (Problem Scientific)
ret = FileOptions
-> FilePath
-> Text
-> Either (ParseError Text) (Problem Scientific)
forall s.
(Stream s, Token s ~ Char, IsString (Tokens s)) =>
FileOptions
-> FilePath -> s -> Either (ParseError s) (Problem Scientific)
LPFile.parseString FileOptions
opt FilePath
fname Text
s
case Either (ParseError Text) (Problem Scientific)
ret of
Left ParseError Text
e -> ParseError Text -> IO (Problem Scientific)
forall a e. Exception e => e -> a
throw ParseError Text
e
Right Problem Scientific
a -> Problem Scientific -> IO (Problem Scientific)
forall (m :: * -> *) a. Monad m => a -> m a
return Problem Scientific
a
#endif
readMPSFile :: FileOptions -> FilePath -> IO (Problem Scientific)
#ifndef WITH_ZLIB
readMPSFile = MPSFile.parseFile
#else
readMPSFile :: FileOptions -> FilePath -> IO (Problem Scientific)
readMPSFile FileOptions
opt FilePath
fname = do
Text
s <- FileOptions -> FilePath -> IO Text
readTextFile FileOptions
opt FilePath
fname
let ret :: Either (ParseError Text) (Problem Scientific)
ret = FileOptions
-> FilePath
-> Text
-> Either (ParseError Text) (Problem Scientific)
forall s.
(Stream s, Token s ~ Char, IsString (Tokens s)) =>
FileOptions
-> FilePath -> s -> Either (ParseError s) (Problem Scientific)
MPSFile.parseString FileOptions
opt FilePath
fname Text
s
case Either (ParseError Text) (Problem Scientific)
ret of
Left ParseError Text
e -> ParseError Text -> IO (Problem Scientific)
forall a e. Exception e => e -> a
throw ParseError Text
e
Right Problem Scientific
a -> Problem Scientific -> IO (Problem Scientific)
forall (m :: * -> *) a. Monad m => a -> m a
return Problem Scientific
a
#endif
readTextFile :: FileOptions -> FilePath -> IO TL.Text
#ifndef WITH_ZLIB
readTextFile opt fname = do
h <- openFile fname ReadMode
case optFileEncoding opt of
Nothing -> return ()
Just enc -> hSetEncoding h enc
TLIO.hGetContents h
#else
readTextFile :: FileOptions -> FilePath -> IO Text
readTextFile FileOptions
opt FilePath
fname = do
TextEncoding
enc <- case FileOptions -> Maybe TextEncoding
optFileEncoding FileOptions
opt of
Maybe TextEncoding
Nothing -> IO TextEncoding
getLocaleEncoding
Just TextEncoding
enc -> TextEncoding -> IO TextEncoding
forall (m :: * -> *) a. Monad m => a -> m a
return TextEncoding
enc
let f :: ByteString -> ByteString
f = if FilePath -> CI FilePath
forall s. FoldCase s => s -> CI s
CI.mk (FilePath -> FilePath
takeExtension FilePath
fname) CI FilePath -> CI FilePath -> Bool
forall a. Eq a => a -> a -> Bool
== CI FilePath
".gz" then ByteString -> ByteString
GZip.decompress else ByteString -> ByteString
forall a. a -> a
id
ByteString
s <- FilePath -> IO ByteString
BL.readFile FilePath
fname
Text -> IO Text
forall (m :: * -> *) a. Monad m => a -> m a
return (Text -> IO Text) -> Text -> IO Text
forall a b. (a -> b) -> a -> b
$ TextEncoding -> ByteString -> Text
decode TextEncoding
enc (ByteString -> Text) -> ByteString -> Text
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString
f ByteString
s
#endif
#if MIN_VERSION_megaparsec(6,0,0)
parseLPString :: (Stream s, Token s ~ Char, IsString (Tokens s)) => FileOptions -> String -> s -> Either (ParseError s) (Problem Scientific)
#else
parseLPString :: (Stream s, Token s ~ Char) => FileOptions -> String -> s -> Either (ParseError s) (Problem Scientific)
#endif
parseLPString :: FileOptions
-> FilePath -> s -> Either (ParseError s) (Problem Scientific)
parseLPString = FileOptions
-> FilePath -> s -> Either (ParseError s) (Problem Scientific)
forall s.
(Stream s, Token s ~ Char, IsString (Tokens s)) =>
FileOptions
-> FilePath -> s -> Either (ParseError s) (Problem Scientific)
LPFile.parseString
#if MIN_VERSION_megaparsec(6,0,0)
parseMPSString :: (Stream s, Token s ~ Char, IsString (Tokens s)) => FileOptions -> String -> s -> Either (ParseError s) (Problem Scientific)
#else
parseMPSString :: (Stream s, Token s ~ Char) => FileOptions -> String -> s -> Either (ParseError s) (Problem Scientific)
#endif
parseMPSString :: FileOptions
-> FilePath -> s -> Either (ParseError s) (Problem Scientific)
parseMPSString = FileOptions
-> FilePath -> s -> Either (ParseError s) (Problem Scientific)
forall s.
(Stream s, Token s ~ Char, IsString (Tokens s)) =>
FileOptions
-> FilePath -> s -> Either (ParseError s) (Problem Scientific)
MPSFile.parseString
writeFile :: FileOptions -> FilePath -> Problem Scientific -> IO ()
writeFile :: FileOptions -> FilePath -> Problem Scientific -> IO ()
writeFile FileOptions
opt FilePath
fname Problem Scientific
prob =
case FilePath -> FilePath
getExt FilePath
fname of
FilePath
".lp" -> FileOptions -> FilePath -> Problem Scientific -> IO ()
writeLPFile FileOptions
opt FilePath
fname Problem Scientific
prob
FilePath
".mps" -> FileOptions -> FilePath -> Problem Scientific -> IO ()
writeMPSFile FileOptions
opt FilePath
fname Problem Scientific
prob
FilePath
ext -> IOError -> IO ()
forall a. IOError -> IO a
ioError (IOError -> IO ()) -> IOError -> IO ()
forall a b. (a -> b) -> a -> b
$ FilePath -> IOError
userError (FilePath -> IOError) -> FilePath -> IOError
forall a b. (a -> b) -> a -> b
$ FilePath
"unknown extension: " FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
ext
getExt :: String -> String
getExt :: FilePath -> FilePath
getExt FilePath
fname | (FilePath
base, FilePath
ext) <- FilePath -> (FilePath, FilePath)
splitExtension FilePath
fname =
case (Char -> Char) -> FilePath -> FilePath
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
toLower FilePath
ext of
#ifdef WITH_ZLIB
FilePath
".gz" -> FilePath -> FilePath
getExt FilePath
base
#endif
FilePath
s -> FilePath
s
writeLPFile :: FileOptions -> FilePath -> Problem Scientific -> IO ()
writeLPFile :: FileOptions -> FilePath -> Problem Scientific -> IO ()
writeLPFile FileOptions
opt FilePath
fname Problem Scientific
prob =
case FileOptions -> Problem Scientific -> Either FilePath Text
LPFile.render FileOptions
opt Problem Scientific
prob of
Left FilePath
err -> IOError -> IO ()
forall a. IOError -> IO a
ioError (IOError -> IO ()) -> IOError -> IO ()
forall a b. (a -> b) -> a -> b
$ FilePath -> IOError
userError FilePath
err
Right Text
s -> FileOptions -> FilePath -> Text -> IO ()
writeTextFile FileOptions
opt FilePath
fname Text
s
writeMPSFile :: FileOptions -> FilePath -> Problem Scientific -> IO ()
writeMPSFile :: FileOptions -> FilePath -> Problem Scientific -> IO ()
writeMPSFile FileOptions
opt FilePath
fname Problem Scientific
prob =
case FileOptions -> Problem Scientific -> Either FilePath Text
MPSFile.render FileOptions
opt Problem Scientific
prob of
Left FilePath
err -> IOError -> IO ()
forall a. IOError -> IO a
ioError (IOError -> IO ()) -> IOError -> IO ()
forall a b. (a -> b) -> a -> b
$ FilePath -> IOError
userError FilePath
err
Right Text
s -> FileOptions -> FilePath -> Text -> IO ()
writeTextFile FileOptions
opt FilePath
fname Text
s
writeTextFile :: FileOptions -> FilePath -> TL.Text -> IO ()
writeTextFile :: FileOptions -> FilePath -> Text -> IO ()
writeTextFile FileOptions
opt FilePath
fname Text
s = do
let writeSimple :: IO ()
writeSimple = do
FilePath -> IOMode -> (Handle -> IO ()) -> IO ()
forall r. FilePath -> IOMode -> (Handle -> IO r) -> IO r
withFile FilePath
fname IOMode
WriteMode ((Handle -> IO ()) -> IO ()) -> (Handle -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \Handle
h -> do
case FileOptions -> Maybe TextEncoding
optFileEncoding FileOptions
opt of
Maybe TextEncoding
Nothing -> () -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
Just TextEncoding
enc -> Handle -> TextEncoding -> IO ()
hSetEncoding Handle
h TextEncoding
enc
Handle -> Text -> IO ()
TLIO.hPutStr Handle
h Text
s
#ifdef WITH_ZLIB
if FilePath -> CI FilePath
forall s. FoldCase s => s -> CI s
CI.mk (FilePath -> FilePath
takeExtension FilePath
fname) CI FilePath -> CI FilePath -> Bool
forall a. Eq a => a -> a -> Bool
/= CI FilePath
".gz" then do
IO ()
writeSimple
else do
TextEncoding
enc <- case FileOptions -> Maybe TextEncoding
optFileEncoding FileOptions
opt of
Maybe TextEncoding
Nothing -> IO TextEncoding
getLocaleEncoding
Just TextEncoding
enc -> TextEncoding -> IO TextEncoding
forall (m :: * -> *) a. Monad m => a -> m a
return TextEncoding
enc
FilePath -> ByteString -> IO ()
BL.writeFile FilePath
fname (ByteString -> IO ()) -> ByteString -> IO ()
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString
GZip.compress (ByteString -> ByteString) -> ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ TextEncoding -> Text -> ByteString
encode TextEncoding
enc Text
s
#else
writeSimple
#endif
toLPString :: FileOptions -> Problem Scientific -> Either String TL.Text
toLPString :: FileOptions -> Problem Scientific -> Either FilePath Text
toLPString = FileOptions -> Problem Scientific -> Either FilePath Text
LPFile.render
toMPSString :: FileOptions -> Problem Scientific -> Either String TL.Text
toMPSString :: FileOptions -> Problem Scientific -> Either FilePath Text
toMPSString = FileOptions -> Problem Scientific -> Either FilePath Text
MPSFile.render