content: {-# LANGUAGE RankNTypes #-}
  {-# LANGUAGE TypeFamilies #-}
  
  {- | Create 'ClSF's with randomness without 'IO'.
     Uses the @MonadRandom@ package.
     This module copies the API from @automaton@'s
     'Data.Automaton.Trans.Random'.
  -}
  module FRP.Rhine.ClSF.Random (
    module FRP.Rhine.ClSF.Random,
    module X,
  )
  where
  
  -- transformers
  import Control.Monad.IO.Class
  
  -- MonadRandom
  import Control.Monad.Random
  
  -- automaton
  import Data.Automaton.Trans.Except (performOnFirstSample)
  import Data.Automaton.Trans.Random as X hiding (evalRandS, getRandomRS, getRandomRS_, getRandomS, runRandS)
  import Data.Automaton.Trans.Random qualified as Automaton
  
  -- rhine
  import FRP.Rhine.ClSF.Core
  import FRP.Rhine.ClSF.Random.Util
  
  -- * Generating random values from the 'RandT' transformer
  
  -- | Generates random values, updating the generator on every step.
  runRandS ::
    (RandomGen g, Monad m) =>
    ClSF (RandT g m) cl a b ->
    -- | The initial random seed
    g ->
    ClSF m cl a (g, b)
  runRandS clsf = Automaton.runRandS (hoistS commuteReaderRand clsf)
  
  -- | Updates the generator every step but discards the generator.
  evalRandS ::
    (RandomGen g, Monad m) =>
    ClSF (RandT g m) cl a b ->
    g ->
    ClSF m cl a b
  evalRandS clsf g = runRandS clsf g >>> arr snd
  
  {- | Updates the generator every step but discards the value,
     only outputting the generator.
  -}
  execRandS ::
    (RandomGen g, Monad m) =>
    ClSF (RandT g m) cl a b ->
    g ->
    ClSF m cl a g
  execRandS clsf g = runRandS clsf g >>> arr fst
  
  -- | Evaluates the random computation by using the global random generator.
  evalRandIOS ::
    (Monad m) =>
    ClSF (RandT StdGen m) cl a b ->
    IO (ClSF m cl a b)
  evalRandIOS clsf = evalRandS clsf <$> newStdGen
  
  -- | Evaluates the random computation by using the global random generator on the first tick.
  evalRandIOS' ::
    (MonadIO m) =>
    ClSF (RandT StdGen m) cl a b ->
    ClSF m cl a b
  evalRandIOS' = performOnFirstSample . liftIO . evalRandIOS
  
  -- * Creating random behaviours
  
  -- | Produce a random value at every tick.
  getRandomS ::
    (MonadRandom m, Random a) =>
    Behaviour m time a
  getRandomS = constMCl getRandom
  
  {- | Produce a random value at every tick,
     within a range given per tick.
  -}
  getRandomRS ::
    (MonadRandom m, Random a) =>
    BehaviourF m time (a, a) a
  getRandomRS = arrMCl getRandomR
  
  {- | Produce a random value at every tick,
     within a range given once.
  -}
  getRandomRS_ ::
    (MonadRandom m, Random a) =>
    (a, a) ->
    Behaviour m time a
  getRandomRS_ range = constMCl $ getRandomR range
  