content: {-|
  Module      : Crypto.Lol.Benchmarks.CycRepBenches
  Description : Benchmarks for 'CycRep'.
  Copyright   : (c) Eric Crockett, 2011-2017
                    Chris Peikert, 2011-2017
  License     : GPL-3
  Maintainer  : ecrockett0@email.com
  Stability   : experimental
  Portability : POSIX
  
  Benchmarks for 'CycRep'. In a perfect world, these benchmarks would
  have the same performance as the 'Cyc' benchmarks. In practice, GHC
  gets in the way at higher levels of the library, resulting in worse
  performance for 'Cyc' in some cases.
  -}
  
  {-# LANGUAGE DataKinds             #-}
  {-# LANGUAGE FlexibleContexts      #-}
  {-# LANGUAGE NoImplicitPrelude     #-}
  {-# LANGUAGE PartialTypeSignatures #-}
  {-# LANGUAGE ScopedTypeVariables   #-}
  {-# LANGUAGE TypeFamilies          #-}
  {-# LANGUAGE TypeOperators         #-}
  
  {-# OPTIONS_GHC -fno-warn-partial-type-signatures #-}
  
  module Crypto.Lol.Benchmarks.CycRepBenches (cycRepBenches1, cycRepBenches2) where
  
  import Control.Monad.Random hiding (lift)
  
  import Crypto.Lol.Cyclotomic.CycRep
  import Crypto.Lol.Prelude
  import Crypto.Lol.Types
  import Crypto.Lol.Utils.Benchmarks  (Benchmark, bgroup, mkBench, mkBenchIO)
  import Crypto.Random
  
  -- must come after imports
  {-# ANN module "HLint: ignore Use camelCase" #-}
  
  -- | Benchmarks for single-index 'CycRep' operations.
  -- There must be a CRT basis for \(O_m\) over @r@.
  -- These cover the same functions as @cycBenches1@, but may have different
  -- performance due to how GHC interacts with Lol.
  {-# INLINABLE cycRepBenches1 #-}
  cycRepBenches1 :: forall t m r gen . _ => Proxy '(t,m,r) -> Proxy gen -> Benchmark
  cycRepBenches1 ptmr pgen =
    let zDec = zero :: CycRep t D m r
        zPow = zero :: CycRep t P m r
        zEC = zero :: CycRepEC t m r
        zPC = ecToPC zEC
        errorBench = mkBenchIO "error" (bench_errRounded ptmr pgen 0.1)
        benches = [
          mkBench "zipWith (*)" (bench_mul zPC) zPC,
          mkBench "crt"         bench_crt       zPow,
          mkBench "crtInv"      bench_crtInv    zPC,
          mkBench "l"           bench_l         zDec,
          mkBench "lInv"        bench_lInv      zPow,
          mkBench "*g Pow"      bench_mulgPow   zPow,
          mkBench "*g Dec"      bench_mulgDec   zDec,
          mkBench "*g CRT"      bench_mulgCRT   zPC,
          mkBench "divG Pow"    bench_divGPow   zPow,
          mkBench "divG Dec"    bench_divGDec   zDec,
          mkBench "divG CRT"    bench_divGCRT   zPC,
          mkBench "lift"        bench_liftPow   zPow] in
        -- This is different because it lives in IO
    bgroup "CycRep" (benches ++ [errorBench])
  
  -- | Benchmarks for inter-ring 'CycRep' operations.
  -- There must be a CRT basis for \(O_{m'}\) over @r@.
  -- These cover the same functions as @cycBenches2@, but may have different
  -- performance due to how GHC interacts with Lol.
  {-# INLINE cycRepBenches2 #-}
  cycRepBenches2 :: forall t m m' r . _ => Proxy '(t,m,m',r) -> Benchmark
  cycRepBenches2 ptmmr =
    let zPow' = zero :: CycRep t P m' r
        zDec' = zero :: CycRep t D m' r
        zEC' = zero :: CycRepEC t m' r
        zPC' = ecToPC zEC'
        zPow = zero :: CycRep t P m r
        zEC = zero :: CycRepEC t m r
        zPC = ecToPC zEC
        benches = [
          mkBench "twacePow" (bench_twacePow ptmmr) zPow',
          mkBench "twaceDec" (bench_twaceDec ptmmr) zDec',
          mkBench "twaceCRT" (bench_twaceCRT ptmmr) zPC',
          mkBench "embedPow" (bench_embedPow ptmmr) zPow,
  --        mkBench "embedDec" (bench_embedDec ptmmr) zDec,
          mkBench "embedCRT" (bench_embedCRT ptmmr) zPC] in
    bgroup "CycRep" benches
  
  pcToEC :: CycRepPC t m r -> CycRepEC t m r
  pcToEC (Right x) = Right x
  
  ecToPC :: CycRepEC t m r -> CycRepPC t m r
  ecToPC (Right x) = Right x
  
  {-# INLINE bench_mul #-}
  bench_mul :: _ => CycRepPC t m r -> CycRepPC t m r -> CycRepEC t m r
  bench_mul a b = pcToEC a * pcToEC b
  
  {-# INLINE bench_crt #-}
  -- convert input from Pow basis to CRT basis
  bench_crt :: _ => CycRep t P m r -> CycRepEC t m r
  bench_crt = toCRT
  
  {-# INLINABLE bench_crtInv #-}
  -- convert input from CRT basis to Pow basis
  -- EAC: This is slow without explicitly mentioning CRTElt. Possibly related to constraint synonym issues?
  bench_crtInv :: _ => CycRepPC t m r -> CycRep t P m r
  bench_crtInv (Right a) = toPow a
  
  {-# INLINE bench_l #-}
  -- convert input from Dec basis to Pow basis
  bench_l :: _ => CycRep t D m r -> CycRep t P m r
  bench_l = toPow
  
  {-# INLINE bench_lInv #-}
  -- convert input from Pow basis to Dec basis
  bench_lInv :: _ => CycRep t P m r -> CycRep t D m r
  bench_lInv = toDec
  
  {-# INLINABLE bench_liftPow #-}
  -- lift an element in the Pow basis
  bench_liftPow :: _ => CycRep t P m r -> CycRep t P m r'
  bench_liftPow = lift
  
  {-# INLINABLE bench_mulgPow #-}
  -- multiply by g when input is in Pow basis
  bench_mulgPow :: _ => CycRep t P m r -> CycRep t P m r
  bench_mulgPow = mulGPow
  
  {-# INLINABLE bench_mulgDec #-}
  -- multiply by g when input is in Dec basis
  bench_mulgDec :: _ => CycRep t D m r -> CycRep t D m r
  bench_mulgDec = mulGDec
  
  {-# INLINABLE bench_mulgCRT #-}
  -- multiply by g when input is in CRT basis
  bench_mulgCRT :: _ => CycRepPC t m r -> CycRep t C m r
  bench_mulgCRT (Right a) = mulGCRTC a
  
  {-# INLINABLE bench_divGPow #-}
  -- divide by g when input is in Pow basis
  bench_divGPow :: _ => CycRep t P m r -> Maybe (CycRep t P m r)
  bench_divGPow = divGPow . mulGPow
  
  {-# INLINABLE bench_divGDec #-}
  -- divide by g when input is in Dec basis
  bench_divGDec :: _ => CycRep t D m r -> Maybe (CycRep t D m r)
  bench_divGDec = divGDec . mulGDec
  
  {-# INLINABLE bench_divGCRT #-}
  -- divide by g when input is in CRT basis
  bench_divGCRT :: _ => CycRepPC t m r -> CycRep t C m r
  bench_divGCRT = either (error "bench_divGCRT expected a CRTC") divGCRTC
  
  {-# INLINABLE bench_errRounded #-}
  -- generate a rounded error term
  bench_errRounded :: forall t m r gen . _
                   => Proxy '(t,m,r) -> Proxy gen -> Double -> IO (CycRep t D m (LiftOf r))
  bench_errRounded _ _ v = do
    gen <- newGenIO
    let e = roundedGaussian v :: Rand (CryptoRand gen) (CycRep t D m (LiftOf r))
    return $ evalRand e gen
  
  {-# INLINE bench_twacePow #-}
  bench_twacePow :: forall t m m' r . _
    => Proxy '(t,m,m',r) -> CycRep t P m' r -> CycRep t P m r
  bench_twacePow _ = twacePow
  
  {-# INLINE bench_twaceDec #-}
  bench_twaceDec :: forall t m m' r . _
    => Proxy '(t,m,m',r) -> CycRep t D m' r -> CycRep t D m r
  bench_twaceDec _ = twaceDec
  
  {-# INLINE bench_twaceCRT #-}
  bench_twaceCRT :: forall t m m' r . _
    => Proxy '(t,m,m',r) -> CycRepPC t m' r -> CycRepPC t m r
  bench_twaceCRT _ (Right a) = twaceCRTC a
  
  {-# INLINE bench_embedPow #-}
  bench_embedPow :: forall t m m' r . _
    => Proxy '(t,m,m',r) -> CycRep t P m r -> CycRep t P m' r
  bench_embedPow _ = embedPow
  
  {-
  {-# INLINE bench_embedDec #-}
  bench_embedDec :: forall t m m' r . (Fact m', _)
    => Proxy '(t,m,m',r) -> CycRep t D m r -> CycRep t D m' r
  bench_embedDec _ = embedDec
  -}
  
  {-# INLINE bench_embedCRT #-}
  bench_embedCRT :: forall t m m' r . _
    => Proxy '(t,m,m',r) -> CycRepPC t m r -> CycRepPC t m' r
  bench_embedCRT _ (Right a) = embedCRTC a
  