{-# LANGUAGE
ScopedTypeVariables
,Rank2Types
,GeneralizedNewtypeDeriving
 #-}
{-# OPTIONS_HADDOCK ignore-exports #-}

-- | This defines a Google Code Jam monad with a handful of combinators for simple parsing of input files.
--
-- The test cases are solved in parallel.
--
-- Although the parsing of the input cannot happen in parallel
-- (lines are consumed one by one, and input of the nexta example
-- depends on how much the current has consumed), the actual work can be
-- automatically parallelized, if the number of lines to consume does
-- not depend on the result of the main workload of each case.
--
-- This is demonstrated by the following example:
--
-- @
-- parallel test
-- mainP = mainId $ do
--        r <- return . length . show . product . enumFromTo (1::Integer) . read =<< cLine
--        if not parallel && r == 1 then return =<< cLine else return $ show r  
--        where parallel = False
-- @
--
-- input file:
--
-- @
-- 2
-- 200000
-- 200000
-- 200000
-- 200000
-- @

module GCJ (
  GCJ
  ,module Control.Applicative
  ,module Control.Monad
  )
       where

import Control.Applicative
import Control.Arrow
import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.Identity
import Control.Monad.Trans.State.Lazy
import Control.Monad.Trans.Class
import Control.Parallel.Strategies
import Data.List
import Prelude
import Safe
import System.Environment
import System.IO
import Text.Printf

-- | data type for Google Code Jam Solution Program
--
-- It has to be a Monad (e.g. to implement cBlock).
--
-- Next action has to be able to depend on state.
newtype GCJ m a = GCJ { fromGCJ :: (StateT [String] m a) }
                deriving (Functor,Applicative,Monad,MonadIO,MonadTrans)

-- * Combinators       
  
-- | consumes and returns one line
cLine :: Monad m => GCJ m String
cLine = GCJ $ do x <- get
                 put $ tail x
                 return $ headNote "GCJ: No input lines left to consume" x

-- | consumes and returns n lines
cLines :: Monad m => Int -> GCJ m [String]
cLines n = sequence $ take n $ repeat cLine

-- | consumes, transforms and returns next block:
--
-- a block consists of first line: n, then (n-m) lines
cBlock :: Monad m => Int -> ([String] -> a) -> GCJ m (Int,a)
cBlock m f = do n <-  liftM read cLine
                return . (,) n . f =<< cLines (n-m) 


-- * Runners
type Runner m = GCJ m String -> [String] -> IO ()
             
-- | produce main from pure function
mainId :: Runner Identity
mainId = mainM $ return.runIdentity

-- | produce main from with transformed IO monad
mainIO :: Runner IO
mainIO = mainM $ id
                         
-- | produce main from Monad
--
-- reads testInput or the file given on the command line
--
-- writes to standard out and to the given file with '.out' appended
mainM :: Monad m => (forall a. (m a -> IO a))
         -> Runner m
mainM exec (GCJ gen) args = do
  let cc []     = return ("test", return (), putStr)
      cc (a:[]) = do h <- openFile (a++".out") WriteMode 
                     return (a, hClose h, \x -> putStr x >> hPutStr h x) 
  (inF,close,myP) <- cc args
  printf "Reading file: %s\n" inF
  (head:tail) <- lines <$> readFile inF
  let ncase = read head
  printf "Found %d cases\n" ncase
  (work,rest) <- (exec $ runStateT (replicateM ncase gen) tail)
  zipWithM_ ((myP.).printf "Case #%u: %s") [(1::Int)..] $
    parMap rdeepseq (\y -> y ++ if "\n" `isSuffixOf` y then "" else "\n") work
  close >> when (length rest > 0) 
    (error $ "GCJ: not all input lines have been consumed:\n" ++ unlines rest)