module Test.Gen ( -- * Tree Generators tree, cataTree, subtrees, leaves, -- * Fingerprint fingerprint, -- * World emptyWorld, -- * Pos pos, -- * Re-exports Fingerprint, Pos, Tree, World, module Hedgehog.Gen, ) where import Control.Applicative (liftA2) import Control.Monad (replicateM) import Hedgehog (MonadGen) import Hedgehog.Gen (choice, int, resize, sized) import Hedgehog.Internal.Gen (golden) import Hedgehog.Range (Size, constantBounded, linear, linearBounded) import Data.Fingerprint (Fingerprint (Fingerprint)) import Data.Functor.Tree (Tree (Node), pattern Leaf) import Data.World (World (World)) import Language.Spectacle.Interaction.Pos (Pos, pattern Pos) import qualified Test.Gen.Rec as Gen.Rec -- --------------------------------------------------------------------------------------------------------------------- -- | @'tree' x@ recursive generates a tree of @x@ using both 'subtrees' and 'leaves'. tree :: MonadGen m => m a -> m (Tree a) tree gen = sized (liftA2 Node gen . fmap pure . generator) where generator !size | size <= 1 = Leaf <$> gen | otherwise = do len <- int (linear 0 $ fromIntegral size) liftA2 Node gen $ choice [subtrees len gen, leaves len gen] -- | @'cataTree' x k@ is a recursive 'Tree' generator for an element generator @x@, subtree generator @k@. The subtree -- generator passes a suggested a subtree length (scaled by the size parameter) along with the generator for @x@. -- -- 'cataTree' replaces 'tree' in instances where a custom generator (such as 'subtrees' or 'leaves') is needed. cataTree :: MonadGen m => m a -> (Int -> m a -> m [Tree a]) -> m (Tree a) cataTree gen k = sized (liftA2 Node gen . fmap pure . generator) where generator !size | size <= 1 = Leaf <$> gen | otherwise = do len <- int (linear 0 $ fromIntegral size) liftA2 Node gen $ k len gen -- | @'subtrees' n x@ generates @n@ subtrees of @x@ dividing the size parameter among them evenly. subtrees :: MonadGen m => Int -> m a -> m [Tree a] subtrees len gen = sized \size -> if 1 < size then resize (smallN len size) do replicateM len (cataTree gen subtrees) else pure [] -- | @'leaves' n x@ is constant to n-many leaves of @x@. leaves :: MonadGen m => Int -> m a -> m [Tree a] leaves len gen = replicateM len (fmap Leaf gen) -- --------------------------------------------------------------------------------------------------------------------- fingerprint :: MonadGen m => m Fingerprint fingerprint = Fingerprint <$> int constantBounded -- --------------------------------------------------------------------------------------------------------------------- emptyWorld :: MonadGen m => m (World '[]) emptyWorld = liftA2 World fingerprint Gen.Rec.empty -- --------------------------------------------------------------------------------------------------------------------- pos :: MonadGen m => m Pos pos = liftA2 Pos (int linearBounded) (int linearBounded) -- --------------------------------------------------------------------------------------------------------------------- -- | Like 'small', but scales the size parameter by a factor of @n@. smallN :: Int -> Size -> Size smallN n size | 1 < n = golden (size `quot` fromIntegral n) | otherwise = 0