packages feed

toysolver-0.7.0: samples/programs/shortest-path/shortest-path.hs

{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Main where

import Control.Exception
import Control.Monad
import qualified Data.ByteString.Lazy.Char8 as BL
import Data.Char
import Data.Foldable (toList)
import qualified Data.IntMap.Strict as IntMap
import Data.Int
import Data.List
import Data.Maybe
#if !MIN_VERSION_base(4,11,0)
import Data.Monoid
#endif
import Data.Ord
import qualified Data.Sequence as Seq
import System.Console.GetOpt
import System.Environment
import System.Exit
import System.IO
import qualified ToySolver.Graph.ShortestPath as ShortestPath
import ToySolver.Internal.Util (setEncodingChar8)

data Flag
  = Help
  | PrintResult
  | Method String
  deriving Eq

options :: [OptDescr Flag]
options =
    [ Option ['h'] ["help"] (NoArg Help) "show help"
    , Option []    ["print"]  (NoArg PrintResult)  "print result"
    , Option []    ["method"] (ReqArg Method "STRING") "method: dijkstra, bellman-ford, floyd-warshall"
    ]

header :: String
header = unlines
  [ "Usage:"
  , "    shortest-path [OPTIONS] INPUTFILE VERTEX.."
  , ""
  , "Options:"
  ]

main :: IO ()
main = do
#ifdef FORCE_CHAR8
  setEncodingChar8
#endif
  args <- getArgs
  case getOpt Permute options args of
    (o,_,[])
      | Help `elem` o -> putStrLn (usageInfo header options)
    (o,fname:vs',[]) -> do
      let method = last ("dijkstra" : [m | Method m <- o])
          vs0 = map read vs'
          vs = if null vs0 then [1] else vs0
      es <- load fname
      let g :: ShortestPath.Graph Int64 Int
          g = fmap toList $ IntMap.fromListWith (<>) [(s, Seq.singleton (t,fromIntegral w,i)) | (i,(s,t,w)) <- zip [(1::Int)..] es]
      case filter (\c -> c /= '-' && c /= '_') (map toLower method) of
        "dijkstra" -> do
          let ret = ShortestPath.dijkstra ShortestPath.unit g vs
          _ <- evaluate ret
          when (PrintResult `elem` o) $ do
            forM_ (sortBy (comparing fst) (IntMap.toList ret)) $ \(v, (cost,_)) -> do
              putStrLn $ show v ++ ": " ++ show cost
        "bellmanford" -> do
          let ret = ShortestPath.bellmanFord ShortestPath.unit g vs
          _ <- evaluate ret
          when (PrintResult `elem` o) $ do
            forM_ (sortBy (comparing fst) (IntMap.toList ret)) $ \(v, (cost,_)) -> do
              putStrLn $ show v ++ ": " ++ show cost
        "floydwarshall" -> do
          let ret = ShortestPath.floydWarshall ShortestPath.unit g
          _ <- evaluate ret
          when (PrintResult `elem` o) $ do
            forM_ (sortBy (comparing fst) (IntMap.toList ret)) $ \(v, m) -> do
              forM_ (sortBy (comparing fst) (IntMap.toList m)) $ \(u, (cost,_)) -> do
                putStrLn $ show v ++ "-" ++ show u ++ ": " ++ show cost
        _ -> error ("unknown method: " ++ method)
    (_,_,errs) -> do
      hPutStrLn stderr $ concat errs ++ usageInfo header options
      exitFailure

load :: FilePath -> IO [(Int,Int,Int)]
load fname = do
  s <- BL.readFile fname
  let f l = do
        -- 'BL.stripPrefix' is available only on bytestring >=0.10.8.0,
        -- But we still want to support bytestring-0.10.4.0 (lts-2.22) and bytestring-0.10.6.0 (lts-3.22).
        (c,l2) <- BL.uncons l
        guard $ c == 'a'
        (v,l3) <- BL.readInt $ BL.dropWhile isSpace l2
        (u,l4) <- BL.readInt $ BL.dropWhile isSpace l3
        (w,_)  <- BL.readInt $ BL.dropWhile isSpace l4
        return (v,u,w)
  return $ catMaybes [f l | l <- BL.lines s]