{-# LANGUAGE
    TemplateHaskell, GADTs, RankNTypes,
    MultiParamTypeClasses, FlexibleInstances, FlexibleContexts
  #-}

module Data.Random.Internal.Source
    ( Prim(..)
    , MonadRandom(..)
    , RandomSource(..)
    , GetPrim(..)
    ) where

import Data.Random.Source.Internal.Prim
import Data.Word

-- |A typeclass for monads with a chosen source of entropy.  For example,
-- 'RVar' is such a monad - the source from which it is (eventually) sampled
-- is the only source from which a random variable is permitted to draw, so
-- when directly requesting entropy for a random variable these functions
-- are used.
--
-- Minimum implementation is either the internal 'getRandomPrim' or all
-- other functions.  Additionally, this class's interface is subject to 
-- extension at any time, so it is very, very strongly recommended that
-- the 'monadRandom' Template Haskell function be used to implement this 
-- function rather than directly implementing it.  That function takes care
-- of choosing default implementations for any missing functions; as long as
-- at least one function is implemented, it will derive sensible 
-- implementations of all others.
-- 
-- To use 'monadRandom', just wrap your instance declaration as follows (and
-- enable the TemplateHaskell and GADTs language extensions):
--
-- > $(monadRandom [d|
-- >         instance MonadRandom FooM where
-- >             getRandomDouble = return pi
-- >             getRandomWord16 = return 4
-- >             {- etc... -}
-- >     |])
class Monad m => MonadRandom m where
    -- |Generate a random value corresponding to the specified primitive.
    -- 
    -- This is an internal interface; use at your own risk.  It may change or
    -- disappear at any time.
    getRandomPrim :: Prim t -> m t
    getRandomPrim PrimWord8             = m t
forall (m :: * -> *). MonadRandom m => m Word8
getRandomWord8
    getRandomPrim PrimWord16            = m t
forall (m :: * -> *). MonadRandom m => m Word16
getRandomWord16
    getRandomPrim PrimWord32            = m t
forall (m :: * -> *). MonadRandom m => m Word32
getRandomWord32
    getRandomPrim PrimWord64            = m t
forall (m :: * -> *). MonadRandom m => m Word64
getRandomWord64
    getRandomPrim PrimDouble            = m t
forall (m :: * -> *). MonadRandom m => m Double
getRandomDouble
    getRandomPrim (PrimNByteInteger n :: Int
n)  = Int -> m Integer
forall (m :: * -> *).
(MonadRandom m, MonadRandom m) =>
Int -> m Integer
getRandomNByteInteger Int
n
    
    -- |Generate a uniformly distributed random 'Word8'
    getRandomWord8 :: m Word8
    getRandomWord8 = Prim Word8 -> m Word8
forall (m :: * -> *) t. MonadRandom m => Prim t -> m t
getRandomPrim Prim Word8
PrimWord8
    
    -- |Generate a uniformly distributed random 'Word16'
    getRandomWord16 :: m Word16
    getRandomWord16 = Prim Word16 -> m Word16
forall (m :: * -> *) t. MonadRandom m => Prim t -> m t
getRandomPrim Prim Word16
PrimWord16
    
    -- |Generate a uniformly distributed random 'Word32'
    getRandomWord32 :: m Word32
    getRandomWord32 = Prim Word32 -> m Word32
forall (m :: * -> *) t. MonadRandom m => Prim t -> m t
getRandomPrim Prim Word32
PrimWord32
    
    -- |Generate a uniformly distributed random 'Word64'
    getRandomWord64 :: m Word64
    getRandomWord64 = Prim Word64 -> m Word64
forall (m :: * -> *) t. MonadRandom m => Prim t -> m t
getRandomPrim Prim Word64
PrimWord64
    
    -- |Generate a uniformly distributed random 'Double' in the range 0 <= U < 1
    getRandomDouble :: m Double
    getRandomDouble = Prim Double -> m Double
forall (m :: * -> *) t. MonadRandom m => Prim t -> m t
getRandomPrim Prim Double
PrimDouble
    
    -- |Generate a uniformly distributed random 'Integer' in the range 0 <= U < 256^n
    getRandomNByteInteger :: MonadRandom m => Int -> m Integer
    getRandomNByteInteger n :: Int
n = Prim Integer -> m Integer
forall (m :: * -> *) t. MonadRandom m => Prim t -> m t
getRandomPrim (Int -> Prim Integer
PrimNByteInteger Int
n)


-- |A source of entropy which can be used in the given monad.
-- 
-- See also 'MonadRandom'.
-- 
-- Minimum implementation is either the internal 'getRandomPrimFrom' or all
-- other functions.  Additionally, this class's interface is subject to 
-- extension at any time, so it is very, very strongly recommended that
-- the 'randomSource' Template Haskell function be used to implement this 
-- function rather than directly implementing it.  That function takes care
-- of choosing default implementations for any missing functions; as long as
-- at least one function is implemented, it will derive sensible 
-- implementations of all others.
-- 
-- To use 'randomSource', just wrap your instance declaration as follows (and
-- enable the TemplateHaskell, MultiParamTypeClasses and GADTs language
-- extensions, as well as any others required by your instances, such as
-- FlexibleInstances):
--
-- > $(randomSource [d|
-- >         instance RandomSource FooM Bar where
-- >             {- at least one RandomSource function... -}
-- >     |])
class Monad m => RandomSource m s where
    -- |Generate a random value corresponding to the specified primitive.
    -- 
    -- This is an internal interface; use at your own risk.  It may change or
    -- disappear at any time.
    getRandomPrimFrom :: s -> Prim t -> m t
    getRandomPrimFrom src :: s
src PrimWord8             = s -> m Word8
forall (m :: * -> *) s. RandomSource m s => s -> m Word8
getRandomWord8From  s
src
    getRandomPrimFrom src :: s
src PrimWord16            = s -> m Word16
forall (m :: * -> *) s. RandomSource m s => s -> m Word16
getRandomWord16From s
src
    getRandomPrimFrom src :: s
src PrimWord32            = s -> m Word32
forall (m :: * -> *) s. RandomSource m s => s -> m Word32
getRandomWord32From s
src
    getRandomPrimFrom src :: s
src PrimWord64            = s -> m Word64
forall (m :: * -> *) s. RandomSource m s => s -> m Word64
getRandomWord64From s
src
    getRandomPrimFrom src :: s
src PrimDouble            = s -> m Double
forall (m :: * -> *) s. RandomSource m s => s -> m Double
getRandomDoubleFrom s
src
    getRandomPrimFrom src :: s
src (PrimNByteInteger n :: Int
n)  = s -> Int -> m Integer
forall (m :: * -> *) s. RandomSource m s => s -> Int -> m Integer
getRandomNByteIntegerFrom s
src Int
n
    
    
    -- |Generate a uniformly distributed random 'Word8'
    getRandomWord8From :: s -> m Word8
    getRandomWord8From src :: s
src = s -> Prim Word8 -> m Word8
forall (m :: * -> *) s t. RandomSource m s => s -> Prim t -> m t
getRandomPrimFrom s
src Prim Word8
PrimWord8
    
    -- |Generate a uniformly distributed random 'Word16'
    getRandomWord16From :: s -> m Word16
    getRandomWord16From src :: s
src = s -> Prim Word16 -> m Word16
forall (m :: * -> *) s t. RandomSource m s => s -> Prim t -> m t
getRandomPrimFrom s
src Prim Word16
PrimWord16
    
    -- |Generate a uniformly distributed random 'Word32'
    getRandomWord32From :: s -> m Word32
    getRandomWord32From src :: s
src = s -> Prim Word32 -> m Word32
forall (m :: * -> *) s t. RandomSource m s => s -> Prim t -> m t
getRandomPrimFrom s
src Prim Word32
PrimWord32
    
    -- |Generate a uniformly distributed random 'Word64'
    getRandomWord64From :: s -> m Word64
    getRandomWord64From src :: s
src = s -> Prim Word64 -> m Word64
forall (m :: * -> *) s t. RandomSource m s => s -> Prim t -> m t
getRandomPrimFrom s
src Prim Word64
PrimWord64
    
    -- |Generate a uniformly distributed random 'Double' in the range 0 <= U < 1
    getRandomDoubleFrom :: s -> m Double
    getRandomDoubleFrom src :: s
src = s -> Prim Double -> m Double
forall (m :: * -> *) s t. RandomSource m s => s -> Prim t -> m t
getRandomPrimFrom s
src Prim Double
PrimDouble
    
    -- |Generate a uniformly distributed random 'Integer' in the range 0 <= U < 256^n
    getRandomNByteIntegerFrom :: s -> Int -> m Integer
    getRandomNByteIntegerFrom src :: s
src n :: Int
n = s -> Prim Integer -> m Integer
forall (m :: * -> *) s t. RandomSource m s => s -> Prim t -> m t
getRandomPrimFrom s
src (Int -> Prim Integer
PrimNByteInteger Int
n)

-- |This type provides a way to define a 'RandomSource' for a monad without actually
-- having to declare an instance.
newtype GetPrim m = GetPrim (forall t. Prim t -> m t)
instance Monad m => RandomSource m (GetPrim m) where
    getRandomPrimFrom :: GetPrim m -> Prim t -> m t
getRandomPrimFrom (GetPrim f :: forall t. Prim t -> m t
f) = Prim t -> m t
forall t. Prim t -> m t
f