content: -- |
  -- Description : A benchmark for dunai.
  -- Copyright   : (c) Ivan Perez, 2023
  -- Authors     : Ivan Perez
  -- Maintainer  : ivan.perez@keera.co.uk
  -- License     : BSD3
  --
  -- A benchmark for Dunai.
  module Main where
  
  import Criterion             (bench, bgroup, nf)
  import Criterion.Main        (defaultConfig, defaultMainWith)
  import Criterion.Types       (Config (csvFile, resamples, verbosity),
                                Verbosity (Quiet))
  import Data.Functor.Identity (runIdentity)
  import Data.Time.Format      (defaultTimeLocale, formatTime)
  import Data.Time.LocalTime   (getZonedTime)
  import System.Environment    (getArgs, withArgs)
  import System.FilePath       ((</>))
  
  import qualified Control.Category as C
  
  import Data.MonadicStreamFunction
  
  -- | Run all benchmarks.
  main :: IO ()
  main = do
    config <- customConfig
    withArgs [] $
      defaultMainWith config
         [ bgroup "basic"
                  [ bench "identity" $ nf basicIdentity 10000
                  , bench "id"       $ nf basicId       10000
                  ]
         , bgroup "compositions"
                  [ bench "identity" $ nf composeIdentity 10000
                  , bench "idid"     $ nf composeIdId     10000
                  , bench "plus"     $ nf composePlus     10000
                  , bench "plusplus" $ nf composePlusPlus 10000
                  , bench "plusmult" $ nf composePlusMult 10000
                  , bench "mult"     $ nf composeMult     10000
                  , bench "multmult" $ nf composeMultMult 10000
                  ]
         , bgroup "counter"
                  [ bench "counter1" $ nf counter1 10000
                  , bench "counter2" $ nf counter2 10000
                  ]
         ]
  
  -- * Benchmarks
  
  -- ** Basic
  
  -- | Dunai's specialized identity function.
  basicIdentity :: Int -> [Int]
  basicIdentity n = runIdentity $ embed sf stream
    where
      sf     = C.id
      stream = replicate n 1
  
  -- | Standard function identity lifted to SFs.
  basicId :: Int -> [Int]
  basicId n = runIdentity $ embed sf stream
    where
      sf     = arr id
      stream = replicate n 1
  
  -- ** Compositions
  
  -- | Composition of Dunai's specialized identity function.
  composeIdentity :: Int -> [Int]
  composeIdentity n = runIdentity $ embed sf stream
    where
      sf     = C.id >>> C.id
      stream = replicate n 1
  
  -- | Composition of standard function identity lifted to SFs.
  composeIdId :: Int -> [Int]
  composeIdId n = runIdentity $ embed sf stream
    where
      sf     = arr id >>> arr id
      stream = replicate n 1
  
  -- | Plus operation.
  --
  -- This is not a composition; it merely exists to serve as a comparison with
  -- composePlusPlus.
  composePlus :: Int -> [Int]
  composePlus n = runIdentity $ embed sf stream
    where
      sf     = arr (+3)
      stream = take n [1..]
  
  -- | Composition of addition lifted to SFs.
  composePlusPlus :: Int -> [Int]
  composePlusPlus n = runIdentity $ embed sf stream
    where
      sf     = arr (+1) >>> arr (+2)
      stream = take n [1..]
  
  -- | Composition of addition with multiplication, lifted to SFs.
  composePlusMult :: Int -> [Int]
  composePlusMult n = runIdentity $ embed sf stream
    where
      sf     = arr (+100) >>> arr (*2)
      stream = take n [10..]
  
  -- | Multiplication operation.
  --
  -- This is not a composition; it merely exists to serve as a comparison with
  -- composeMultMult.
  composeMult :: Int -> [Int]
  composeMult n = runIdentity $ embed sf stream
    where
      sf     = arr (*20)
      stream = take n [10..]
  
  -- | Composition of multiplication lifted to SFs.
  composeMultMult :: Int -> [Int]
  composeMultMult n = runIdentity $ embed sf stream
    where
      sf     = arr (*10) >>> arr (*2)
      stream = take n [10..]
  
  -- ** Counter
  
  -- | Counter without explicit seq.
  counter1 :: Int -> [Int]
  counter1 n = runIdentity $ embed sf stream
    where
      sf     = feedback 0 (arr (dup . uncurry (+)))
      stream = replicate n 1
  
      dup x = (x, x)
  
  -- | Counter with explicit seq.
  counter2 :: Int -> [Int]
  counter2 n = runIdentity $ embed sf stream
    where
      sf     = feedback 0 (arr ((\x -> x `seq` (x, x)). uncurry (+)))
      stream = replicate n 1
  
  -- * Auxiliary functions
  
  -- Construct a config with increased number of sampling
  -- and a custom name for the report.
  customConfig :: IO Config
  customConfig = do
    args <- getArgs
  
    let dir = case args of
                []     -> "."
                (x:xs) -> x
  
    -- Custom filename using the current time
    timeString <- (formatTime defaultTimeLocale "%F-%H%M%S") <$> getZonedTime
    let filename = concat [ timeString, "-", "bench.csv" ]
  
    return $ defaultConfig { csvFile   = Just $ dir </> filename
                           , resamples = 100000
                           , verbosity = Quiet
                           }
  