content: {-# LANGUAGE DeriveGeneric, OverloadedStrings, ScopedTypeVariables #-}
  {-# LANGUAGE AllowAmbiguousTypes #-}
  {-# LANGUAGE FlexibleContexts #-}
  {-# LANGUAGE TypeApplications #-}
  {-# OPTIONS -Wno-orphans #-}
  module Main (main) where
  
  import Control.DeepSeq
  import qualified Data.Aeson as J
  import qualified Data.ByteString.Lazy as BL
  import qualified Data.ByteString as B
  import qualified Data.Binary as B
  import qualified Data.Store as S
  import Data.Either
  import Codec.Winery
  import Data.Text (Text)
  import qualified Data.Text.Encoding as TE
  import qualified Data.Serialize as C
  import qualified Data.Vector as V
  import qualified Data.Vector.Unboxed as VU
  import qualified Data.Vector.Storable as VS
  import qualified Data.Vector.Generic as VG
  import GHC.Generics (Generic)
  import Gauge.Main
  import qualified Codec.Serialise as CBOR
  
  data Gender = Male | Female deriving (Show, Generic)
  
  instance Serialise Gender where
    bundleSerialise = bundleVia WineryVariant
  
  instance CBOR.Serialise Gender
  instance B.Binary Gender
  instance C.Serialize Gender
  instance J.FromJSON Gender
  instance J.ToJSON Gender
  instance S.Store Gender
  
  data TestRec = TestRec
    { id_ :: !Int
    , first_name :: !Text
    , last_name :: !Text
    , email :: !Text
    , gender :: !Gender
    , num :: !Int
    , latitude :: !Double
    , longitude :: !Double
    } deriving (Show, Generic)
  
  instance Serialise TestRec where
    bundleSerialise = bundleVia WineryRecord
  
  instance NFData TestRec where
    rnf TestRec{} = ()
  
  instance B.Binary TestRec
  instance C.Serialize TestRec
  instance CBOR.Serialise TestRec
  instance J.FromJSON TestRec
  instance J.ToJSON TestRec
  instance S.Store TestRec
  
  instance C.Serialize Text where
      put = C.put . TE.encodeUtf8
      get = TE.decodeUtf8 <$> C.get
  
  main :: IO ()
  main = do
    winery <- B.readFile "benchmarks/data.winery"
    binary <- B.readFile "benchmarks/data.binary"
    cbor <- B.readFile "benchmarks/data.cbor"
    cereal <- B.readFile "benchmarks/data.cereal"
    json <- B.readFile "benchmarks/data.json"
    values :: [TestRec] <- return $ B.decode $ BL.fromStrict binary
    let aValue = head values
    B.writeFile "benchmarks/data.store" $ S.encode values
  
    store <- B.readFile "benchmarks/data.store"
    let serialisedInts = serialiseOnly [floor (2**x) :: Int | x <- [0 :: Double, 0.5..62]]
  
        intVec :: VG.Vector v Int => Int -> v Int
        intVec n = VG.replicate n 7
  
        vecSizes :: [Int]
        vecSizes = [5, 50, 500, 5000]
  
    deepseq values $ defaultMain
      [ bgroup "serialise/list"
        [ bench "winery" $ nf serialise values
        , bench "binary" $ nf (BL.toStrict . B.encode) values
        , bench "cereal" $ nf C.encode values
        , bench "serialise" $ nf (BL.toStrict . CBOR.serialise) values
        , bench "store" $ nf S.encode values
        , bench "aeson" $ nf (BL.toStrict . J.encode) values
        ]
      , bgroup "serialise/item"
        [ bench "winery" $ nf serialiseOnly aValue
        , bench "binary" $ nf (BL.toStrict . B.encode) aValue
        , bench "cereal" $ nf C.encode aValue
        , bench "serialise" $ nf (BL.toStrict . CBOR.serialise) aValue
        , bench "store" $ nf S.encode aValue
        , bench "aeson" $ nf (BL.toStrict . J.encode) aValue
        ]
      , bgroup "serialise/Vector" $
        let vecGroup
                :: forall v. (VG.Vector v Int, NFData (v Int), Serialise (v Int))
                => String -> Benchmark
            vecGroup lbl = bgroup lbl $ flip fmap vecSizes $ \n ->
                bench (show n) $ nf serialiseOnly (intVec @v n)
        in [ vecGroup @V.Vector "boxed"
           , vecGroup  @VS.Vector "storable"
           , vecGroup @VU.Vector "unboxed"
           ]
  
      , bgroup "deserialise"
        [ bench "winery" $ nf (fromRight undefined . deserialise :: B.ByteString -> [TestRec]) winery
        , bench "binary" $ nf (B.decode . BL.fromStrict :: B.ByteString -> [TestRec]) binary
        , bench "cereal" $ nf (C.decode :: B.ByteString -> Either String [TestRec]) cereal
        , bench "serialise" $ nf (CBOR.deserialise . BL.fromStrict :: B.ByteString -> [TestRec]) cbor
        , bench "store" $ nf (S.decodeEx :: B.ByteString -> [TestRec]) store
        , bench "aeson" $ nf (J.decode . BL.fromStrict :: B.ByteString -> Maybe [TestRec]) json
        ]
      , bgroup "deserialise/Int"
        [ bench "winery" $ nf (evalDecoder decodeCurrent :: B.ByteString -> [Int]) serialisedInts
        ]
  
      , bgroup "deserialise/Vector" $
        let vecGroup
                :: forall v. (VG.Vector v Int, NFData (v Int), Serialise (v Int))
                => String -> Benchmark
            vecGroup lbl = bgroup lbl $ flip fmap vecSizes $ \n ->
                bench (show n) $ nf
                    (evalDecoder decodeCurrent :: B.ByteString -> v Int)
                    (serialiseOnly $ intVec @v n)
        in [ vecGroup @V.Vector "boxed"
           , vecGroup  @VS.Vector "storable"
           , vecGroup @VU.Vector "unboxed"
           ]
      ]
  