module Foundation.Random.Class
    ( MonadRandom(..)
    ) where

import           Data.Proxy
import           Basement.Imports
import           Foundation.System.Entropy
import qualified Basement.UArray as A

-- | A monad constraint that allows to generate random bytes
class (Functor m, Applicative m, Monad m) => MonadRandom m where
    getRandomBytes :: CountOf Word8 -> m (UArray Word8)
    getRandomWord64 :: m Word64
    getRandomF32 :: m Float
    getRandomF64 :: m Double

instance MonadRandom IO where
    getRandomBytes :: CountOf Word8 -> IO (UArray Word8)
getRandomBytes  = CountOf Word8 -> IO (UArray Word8)
getEntropy
    getRandomWord64 :: IO Word64
getRandomWord64 = (UArray Word64 -> Offset Word64 -> Word64)
-> Offset Word64 -> UArray Word64 -> Word64
forall a b c. (a -> b -> c) -> b -> a -> c
flip UArray Word64 -> Offset Word64 -> Word64
forall ty. PrimType ty => UArray ty -> Offset ty -> ty
A.index Offset Word64
0 (UArray Word64 -> Word64)
-> (UArray Word8 -> UArray Word64) -> UArray Word8 -> Word64
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. UArray Word8 -> UArray Word64
forall a b. (PrimType a, PrimType b) => UArray a -> UArray b
A.unsafeRecast
                  (UArray Word8 -> Word64) -> IO (UArray Word8) -> IO Word64
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CountOf Word8 -> IO (UArray Word8)
forall (m :: * -> *).
MonadRandom m =>
CountOf Word8 -> m (UArray Word8)
getRandomBytes (Proxy Word64 -> CountOf Word8
forall ty. PrimType ty => Proxy ty -> CountOf Word8
A.primSizeInBytes (Proxy Word64
forall {k} (t :: k). Proxy t
Proxy :: Proxy Word64))
    getRandomF32 :: IO Float
getRandomF32 = (UArray Float -> Offset Float -> Float)
-> Offset Float -> UArray Float -> Float
forall a b c. (a -> b -> c) -> b -> a -> c
flip UArray Float -> Offset Float -> Float
forall ty. PrimType ty => UArray ty -> Offset ty -> ty
A.index Offset Float
0 (UArray Float -> Float)
-> (UArray Word8 -> UArray Float) -> UArray Word8 -> Float
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. UArray Word8 -> UArray Float
forall a b. (PrimType a, PrimType b) => UArray a -> UArray b
A.unsafeRecast
                  (UArray Word8 -> Float) -> IO (UArray Word8) -> IO Float
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CountOf Word8 -> IO (UArray Word8)
forall (m :: * -> *).
MonadRandom m =>
CountOf Word8 -> m (UArray Word8)
getRandomBytes (Proxy Word64 -> CountOf Word8
forall ty. PrimType ty => Proxy ty -> CountOf Word8
A.primSizeInBytes (Proxy Word64
forall {k} (t :: k). Proxy t
Proxy :: Proxy Word64))
    getRandomF64 :: IO Double
getRandomF64 = (UArray Double -> Offset Double -> Double)
-> Offset Double -> UArray Double -> Double
forall a b c. (a -> b -> c) -> b -> a -> c
flip UArray Double -> Offset Double -> Double
forall ty. PrimType ty => UArray ty -> Offset ty -> ty
A.index Offset Double
0 (UArray Double -> Double)
-> (UArray Word8 -> UArray Double) -> UArray Word8 -> Double
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. UArray Word8 -> UArray Double
forall a b. (PrimType a, PrimType b) => UArray a -> UArray b
A.unsafeRecast
                  (UArray Word8 -> Double) -> IO (UArray Word8) -> IO Double
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CountOf Word8 -> IO (UArray Word8)
forall (m :: * -> *).
MonadRandom m =>
CountOf Word8 -> m (UArray Word8)
getRandomBytes (Proxy Word64 -> CountOf Word8
forall ty. PrimType ty => Proxy ty -> CountOf Word8
A.primSizeInBytes (Proxy Word64
forall {k} (t :: k). Proxy t
Proxy :: Proxy Word64))