# LANGUAGE 
     TemplateHaskell , GADTs , RankNTypes , 
     MultiParamTypeClasses , FlexibleInstances , FlexibleContexts 
   #
    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
  >               = 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             = getRandomWord8
    getRandomPrim PrimWord16            = getRandomWord16
    getRandomPrim PrimWord32            = getRandomWord32
    getRandomPrim PrimWord64            = getRandomWord64
    getRandomPrim PrimDouble            = getRandomDouble
    getRandomPrim (PrimNByteInteger n)  = getRandomNByteInteger n
    
    -- |Generate a uniformly distributed random 'Word8'
    getRandomWord8 :: m Word8
    getRandomWord8 = getRandomPrim PrimWord8
    
    -- |Generate a uniformly distributed random 'Word16'
    getRandomWord16 :: m Word16
    getRandomWord16 = getRandomPrim PrimWord16
    
  |Generate a uniformly distributed random '  '
    getRandomWord32 :: m Word32
    getRandomWord32 = getRandomPrim PrimWord32
    
    -- |Generate a uniformly distributed random 'Word64'
    getRandomWord64 :: m Word64
    getRandomWord64 = getRandomPrim PrimWord64
    
  |Generate a uniformly distributed random ' Double ' in the range 0 < = U < 1
    getRandomDouble :: m Double
    getRandomDouble = getRandomPrim PrimDouble
    
  |Generate a uniformly distributed random ' Integer ' in the range 0 < = U < 256^n
    getRandomNByteInteger :: MonadRandom m => Int -> m Integer
    getRandomNByteInteger n = getRandomPrim (PrimNByteInteger 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 PrimWord8             = getRandomWord8From  src
    getRandomPrimFrom src PrimWord16            = getRandomWord16From src
    getRandomPrimFrom src PrimWord32            = getRandomWord32From src
    getRandomPrimFrom src PrimWord64            = getRandomWord64From src
    getRandomPrimFrom src PrimDouble            = getRandomDoubleFrom src
    getRandomPrimFrom src (PrimNByteInteger n)  = getRandomNByteIntegerFrom src n
    
    
    -- |Generate a uniformly distributed random 'Word8'
    getRandomWord8From :: s -> m Word8
    getRandomWord8From src = getRandomPrimFrom src PrimWord8
    
    -- |Generate a uniformly distributed random 'Word16'
    getRandomWord16From :: s -> m Word16
    getRandomWord16From src = getRandomPrimFrom src PrimWord16
    
  |Generate a uniformly distributed random '  '
    getRandomWord32From :: s -> m Word32
    getRandomWord32From src = getRandomPrimFrom src PrimWord32
    
    -- |Generate a uniformly distributed random 'Word64'
    getRandomWord64From :: s -> m Word64
    getRandomWord64From src = getRandomPrimFrom src PrimWord64
    
  |Generate a uniformly distributed random ' Double ' in the range 0 < = U < 1
    getRandomDoubleFrom :: s -> m Double
    getRandomDoubleFrom src = getRandomPrimFrom src PrimDouble
    
  |Generate a uniformly distributed random ' Integer ' in the range 0 < = U < 256^n
    getRandomNByteIntegerFrom :: s -> Int -> m Integer
    getRandomNByteIntegerFrom src n = getRandomPrimFrom src (PrimNByteInteger 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 f) = f
 |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
 implementations of all others.
 
 To use 'monadRandom', just wrap your instance declaration as follows (and

 > $(monadRandom [d|
 >         instance MonadRandom FooM where
 >             getRandomDouble = return pi
 >             {- etc... -}
 >     |])
 |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.
 |Generate a uniformly distributed random 'Word8'
 |Generate a uniformly distributed random 'Word16'
 |Generate a uniformly distributed random 'Word64'
 |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
 function rather than directly implementing it.  That function takes care
 of choosing default implementations for any missing functions; as long as
 implementations of all others.
 
 To use 'randomSource', just wrap your instance declaration as follows (and
 extensions, as well as any others required by your instances, such as

 > $(randomSource [d|
 >     |])
 |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.
 |Generate a uniformly distributed random 'Word8'
 |Generate a uniformly distributed random 'Word16'
 |Generate a uniformly distributed random 'Word64'
 having to declare an instance.
# LANGUAGE 
     TemplateHaskell , GADTs , RankNTypes , 
     MultiParamTypeClasses , FlexibleInstances , FlexibleContexts 
   #
    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

  at least one function is implemented , it will derive sensible
  enable the TemplateHaskell and GADTs language extensions ):
  >               = return 4
class Monad m => MonadRandom m where
    getRandomPrim :: Prim t -> m t
    getRandomPrim PrimWord8             = getRandomWord8
    getRandomPrim PrimWord16            = getRandomWord16
    getRandomPrim PrimWord32            = getRandomWord32
    getRandomPrim PrimWord64            = getRandomWord64
    getRandomPrim PrimDouble            = getRandomDouble
    getRandomPrim (PrimNByteInteger n)  = getRandomNByteInteger n
    
    getRandomWord8 :: m Word8
    getRandomWord8 = getRandomPrim PrimWord8
    
    getRandomWord16 :: m Word16
    getRandomWord16 = getRandomPrim PrimWord16
    
  |Generate a uniformly distributed random '  '
    getRandomWord32 :: m Word32
    getRandomWord32 = getRandomPrim PrimWord32
    
    getRandomWord64 :: m Word64
    getRandomWord64 = getRandomPrim PrimWord64
    
  |Generate a uniformly distributed random ' Double ' in the range 0 < = U < 1
    getRandomDouble :: m Double
    getRandomDouble = getRandomPrim PrimDouble
    
  |Generate a uniformly distributed random ' Integer ' in the range 0 < = U < 256^n
    getRandomNByteInteger :: MonadRandom m => Int -> m Integer
    getRandomNByteInteger n = getRandomPrim (PrimNByteInteger n)


  the ' randomSource ' Template Haskell function be used to implement this
  at least one function is implemented , it will derive sensible
  enable the TemplateHaskell , MultiParamTypeClasses and GADTs language
  FlexibleInstances ):
  >          instance RandomSource FooM Bar where
  >              { - at least one RandomSource function ... - }
class Monad m => RandomSource m s where
    getRandomPrimFrom :: s -> Prim t -> m t
    getRandomPrimFrom src PrimWord8             = getRandomWord8From  src
    getRandomPrimFrom src PrimWord16            = getRandomWord16From src
    getRandomPrimFrom src PrimWord32            = getRandomWord32From src
    getRandomPrimFrom src PrimWord64            = getRandomWord64From src
    getRandomPrimFrom src PrimDouble            = getRandomDoubleFrom src
    getRandomPrimFrom src (PrimNByteInteger n)  = getRandomNByteIntegerFrom src n
    
    
    getRandomWord8From :: s -> m Word8
    getRandomWord8From src = getRandomPrimFrom src PrimWord8
    
    getRandomWord16From :: s -> m Word16
    getRandomWord16From src = getRandomPrimFrom src PrimWord16
    
  |Generate a uniformly distributed random '  '
    getRandomWord32From :: s -> m Word32
    getRandomWord32From src = getRandomPrimFrom src PrimWord32
    
    getRandomWord64From :: s -> m Word64
    getRandomWord64From src = getRandomPrimFrom src PrimWord64
    
  |Generate a uniformly distributed random ' Double ' in the range 0 < = U < 1
    getRandomDoubleFrom :: s -> m Double
    getRandomDoubleFrom src = getRandomPrimFrom src PrimDouble
    
  |Generate a uniformly distributed random ' Integer ' in the range 0 < = U < 256^n
    getRandomNByteIntegerFrom :: s -> Int -> m Integer
    getRandomNByteIntegerFrom src n = getRandomPrimFrom src (PrimNByteInteger n)

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