content: {-# LANGUAGE CPP              #-}
  {-# LANGUAGE FlexibleContexts #-}
  {-# OPTIONS_GHC -fno-warn-orphans #-}
  
  module Benchmark where
  
  import Data.Array.IArray
  import Data.Array.Unboxed                       (UArray)
  
  import Data.List
  import Data.Monoid
  import Criterion
  import Criterion.Config
  import Criterion.Main
  import Criterion.Monad
  import Criterion.Types
  import Criterion.Environment
  import Control.Monad
  import Control.Monad.Trans                      (liftIO)
  import Control.DeepSeq
  import System.IO
  import System.Directory
  import System.Environment
  
  #if MIN_VERSION_criterion(0,8,0)
  import Criterion.IO.Printf
  #else
  import Criterion.IO
  #endif
  
  
  instance (Ix dim, IArray UArray e) => NFData (UArray dim e) where
    rnf a = a ! head (indices a) `seq` ()
  
  
  -- Much like defaultMain, but we ignore any non-flag command line arguments,
  -- which we take as the inputs to the program itself (returned via getArg')
  --
  runBenchmark :: [Benchmark] -> IO ()
  runBenchmark = runBenchmarkWith defaultConfig (return ())
  
  runBenchmarkWith :: Config -> Criterion () -> [Benchmark] -> IO ()
  runBenchmarkWith defCfg prep bs = do
    (cfg, _) <- parseArgs defCfg defaultOptions =<< getArgs
    withConfig cfg $
      if cfgPrintExit cfg == List
      then do
        _ <- note "Benchmarks:\n"
        mapM_ (note "  %s\n") (sort $ concatMap benchNames bs)
      else do
        case getLast $ cfgSummaryFile cfg of
          Just fn -> liftIO $ writeFileOnce fn "Name,Mean,MeanLB,MeanUB,Stddev,StddevLB,StddevUB\n"
          Nothing -> return ()
        env <- measureEnvironment
        let shouldRun = const True
        prep
        runAndAnalyse shouldRun env $ BenchGroup "" bs
  
  writeFileOnce :: FilePath -> String -> IO ()
  writeFileOnce fn line = do
    exists <- doesFileExist fn
    size   <- withFile fn ReadWriteMode hFileSize
    unless (exists && size > 0) $ writeFile fn line
  
  