content: module Random where
  
  import Control.Monad (liftM2)
  import Control.Monad.State
  
  {-- snippet rand --}
  import System.Random
  
  rand :: IO Int
  rand = getStdRandom (randomR (0, maxBound))
  {-- /snippet rand --}
  
  {-- snippet twoBadRandoms --}
  twoBadRandoms :: RandomGen g => g -> (Int, Int)
  twoBadRandoms gen = (fst $ random gen, fst $ random gen)
  {-- /snippet twoBadRandoms --}
  
  {-- snippet twoGoodRandoms --}
  twoGoodRandoms :: RandomGen g => g -> ((Int, Int), g)
  twoGoodRandoms gen = let (a, gen') = random gen
                           (b, gen'') = random gen'
                       in ((a, b), gen'')
  {-- /snippet twoGoodRandoms --}
  
  {-- snippet RandomState --}
  type RandomState a = State StdGen a
  {-- /snippet RandomState --}
  
  {-- snippet getRandom --}
  getRandom :: Random a => RandomState a
  getRandom =
    get >>= \gen ->
    let (val, gen') = random gen in
    put gen' >>
    return val
  {-- /snippet getRandom --}
  
  {-- snippet getRandomDo --}
  getRandomDo :: Random a => RandomState a
  getRandomDo = do
    gen <- get
    let (val, gen') = random gen
    put gen'
    return val
  {-- /snippet getRandomDo --}
  
  getTwoRandomsUgly :: Random a => RandomState (a, a)
  getTwoRandomsUgly = do
    a <- getRandom
    b <- getRandom
    return (a, b)
  
  {-- snippet getTwoRandoms --}
  getTwoRandoms :: Random a => RandomState (a, a)
  getTwoRandoms = liftM2 (,) getRandom getRandom
  {-- /snippet getTwoRandoms --}
  
  {-- snippet runTwoRandoms --}
  runTwoRandoms :: IO (Int, Int)
  runTwoRandoms = do
    oldState <- getStdGen
    let (result, newState) = runState getTwoRandoms oldState
    setStdGen newState
    return result
  {-- /snippet runTwoRandoms --}
  
  {-- snippet CountedRandom --}
  data CountedRandom = CountedRandom {
        crGen :: StdGen
      , crCount :: Int
      }
  
  type CRState = State CountedRandom
  
  getCountedRandom :: Random a => CRState a
  getCountedRandom = do
    st <- get
    let (val, gen') = random (crGen st)
    put CountedRandom { crGen = gen', crCount = crCount st + 1 }
    return val
  {-- /snippet CountedRandom --}
  
  {-- snippet getCount --}
  getCount :: CRState Int
  getCount = crCount `liftM` get
  {-- /snippet getCount --}
  
  {-- snippet putCount --}
  putCount :: Int -> CRState ()
  putCount a = do
    st <- get
    put st { crCount = a }
  {-- /snippet putCount --}
  
  {-- snippet putCountModify --}
  putCountModify :: Int -> CRState ()
  putCountModify a = modify $ \st -> st { crCount = a }
  {-- /snippet putCountModify --}
  