{-|
Module      : Crypto.Lol.Types.Random
Description : Types for using crypto-api with MonadCryptoRandom.
Copyright   : (c) Eric Crockett, 2011-2017
                  Chris Peikert, 2011-2017
License     : GPL-3
Maintainer  : ecrockett0@email.com
Stability   : experimental
Portability : POSIX

Defines a newtype wrapper 'CryptoRand' for crypto-api's
'CryptoRandomGen', and a corresponding 'RandomGen' wrapper
instance.  These are needed because 'CryptoRandomGen' generators
can only be used to get "Data.ByteString"s; the 'RandomGen' wrapper
instance allows them to be used to generate any 'Random' type.
-}

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module Crypto.Lol.Types.Random (CryptoRand, evalCryptoRandIO) where

import Control.Arrow
import Control.Monad.CryptoRandom
import Control.Monad.IO.Class
import Control.Monad.Random       (RandT, evalRandT)
import Crypto.Random
import System.Random

-- | Turns a 'CryptoRandomGen' @g@ into a standard 'RandomGen'.
newtype CryptoRand g = CryptoRand g deriving (CryptoRandomGen)

-- | Evaluate a 'RandT' computation using a cryptographic generator
-- @g@, seeded by system entropy.  Note that the updated generator is
-- not returned.
evalCryptoRandIO :: (CryptoRandomGen g, MonadIO io) => RandT g io a -> io a
evalCryptoRandIO x = do
  gen <- liftIO newGenIO -- uses system entropy
  evalRandT x gen

-- "standard" RNG interface wrapper for a cryptographic RNG
instance (CryptoRandomGen g) => RandomGen (CryptoRand g) where
  -- use 'CRandom' instance for 'Int'
  next (CryptoRand g) = either (error . show) (second CryptoRand) $ crandom g

  split (CryptoRand g) =
    either (error . show) (CryptoRand *** CryptoRand) $ splitGen g

  {-# INLINABLE next #-}
  {-# INLINABLE split #-}