{-# LANGUAGE GeneralizedNewtypeDeriving, DeriveFunctor, FlexibleContexts, FlexibleInstances
, MultiParamTypeClasses, DerivingVia, InstanceSigs #-}
module Language.Pads.Generation where
import Control.Monad.IO.Class (liftIO)
import Data.List (delete)
import GHC.Prim (RealWorld)
import System.Random.MWC
import Control.Monad.Trans.Class (lift)
import Control.Monad.State.Lazy
newtype PadsGen st a = PadsGen { unPadsGen :: StateT (GenIO, st) IO a }
deriving (Functor, Applicative, Monad, MonadIO)
instance MonadState (Gen RealWorld, st) (PadsGen st) where
get :: PadsGen st (Gen RealWorld, st)
get = PadsGen $ StateT $ \s -> pure (s,s)
put :: (Gen RealWorld, st) -> PadsGen st ()
put s = PadsGen $ StateT $ \_ -> pure ((), s)
askGen :: MonadState (GenIO, st) (PadsGen st) => (GenIO -> PadsGen st a) -> PadsGen st a
askGen fncn = get >>= fncn . fst
runPadsGenSt :: st -> PadsGen st a -> IO a
runPadsGenSt init_st genM = do
gen <- createSystemRandom
fst <$> runStateT (unPadsGen genM) (gen, init_st)
getState :: PadsGen st st
getState = do
(_, st) <- get
return st
putState :: st -> PadsGen st ()
putState v = do
(g, _) <- get
put (g, v)
runPadsGen :: PadsGen () a -> IO a
runPadsGen = runPadsGenSt ()
randNum :: (Variate a) => PadsGen st a
randNum = askGen $ (liftIO . uniform)
randNumBetween :: (Integral a, Variate a) => a -> a -> PadsGen st a
randNumBetween i j = askGen $ (liftIO . (uniformR (i, j)))
randNumBound :: (Integral a, Variate a) => a -> PadsGen st a
randNumBound i = randNumBetween 0 i
randInteger :: PadsGen st Integer
randInteger = randIntegerBound (2^1023)
randIntegerBound :: Integral a => a -> PadsGen st Integer
randIntegerBound i = do
(gen,_) <- get
i' <- liftIO $ uniformR (0 :: Double, (fromIntegral i) :: Double) gen
(return . toInteger . floor) i'
randElem :: [a] -> PadsGen st a
randElem xs = do
(gen,_) <- get
r <- liftIO $ fromIntegral <$> uniformR (0, length xs - 1) gen
return $ xs !! r
randLetter :: PadsGen st Char
randLetter = randElem letters
randLetterExcluding :: Char -> PadsGen st Char
randLetterExcluding c = randElem (delete c letters)
letters :: [Char]
letters = ['A'..'Z'] ++ ['a'..'z']
listLengthLimit = 100
randList :: PadsGen st a -> Maybe Int -> PadsGen st [a]
randList padsGen intM = do
i <- case intM of
Just x -> return x
Nothing -> askGen $ (liftIO . (uniformR (1, listLengthLimit)))
replicateM i padsGen
recLimit = 10000
untilM :: Monad m => (a -> Bool) -> (a -> m a) -> Integer -> a -> m a
untilM p f i z = do
when (i <= 0)
(error $ "untilM: recursion too deep. Your description probably "
++ "contains a too-narrow constraint to efficiently "
++ "generate data that satisfy it. To increase "
++ "the recursion limit ('recLimit' in Generation.hs), "
++ "currently set to " ++ show recLimit
++ ", edit it and try again.")
if p z
then return z
else f z >>= untilM p f (i - 1)
randWithConstraint :: PadsGen st a -> (a -> Bool) -> PadsGen st a
randWithConstraint padsGen pred = do
x <- padsGen
x' <- untilM pred (const padsGen) recLimit x
return x'