invertible-0.2.0: test/FreeMonoidal.hs
{-# LANGUAGE CPP, GADTs, FlexibleContexts, FlexibleInstances, TypeOperators, TupleSections, ConstraintKinds, GeneralizedNewtypeDeriving, QuasiQuotes #-}
module FreeMonoidal (tests) where
import Control.Monad (join, guard)
import Data.Functor.Classes (Show1(..))
import Data.Maybe (isJust)
import qualified Test.QuickCheck as Q
import Unsafe.Coerce (unsafeCoerce)
import Control.Invertible.Monoidal.Free
import qualified Data.Invertible as I
genTree :: [Q.Gen a] -> Q.Gen a
genTree = Q.scale (max 0 . pred) . join . Q.growingElements
newtype Const a b = Const a
deriving (Eq, Ord, Show, Q.Arbitrary)
instance Functor (Const a) where
fmap _ (Const x) = Const x
instance Show a => Show1 (Const a) where
#if MIN_VERSION_base(4,9,0)
liftShowsPrec _ _ =
#else
showsPrec1 =
#endif
showsPrec
data Tree a
= TreeEmpty
| TreeFree a
| TreeJoin !(Tree a, Tree a)
| TreeChoose !(Either (Tree a) (Tree a))
deriving (Eq, Show)
instance Q.Arbitrary a => Q.Arbitrary (Tree a) where
arbitrary = genTree
[ return TreeEmpty
, TreeFree <$> Q.arbitrary
, TreeJoin <$> Q.arbitrary
, TreeChoose <$> Q.arbitrary
]
shrink TreeEmpty = []
shrink (TreeFree x) = TreeEmpty : map TreeFree (Q.shrink x)
shrink (TreeJoin (p, q)) =
[TreeEmpty, p, q] ++ map TreeJoin (Q.shrink (p, q))
shrink (TreeChoose (Left p)) =
[TreeEmpty, p] ++ map (TreeChoose . Left) (Q.shrink p)
shrink (TreeChoose (Right p)) =
[TreeEmpty, p] ++ map (TreeChoose . Right) (Q.shrink p)
type FreeTree a = Free (Const a) (Tree a)
emptyTree :: FreeTree a
emptyTree = Transform (I.const TreeEmpty) Empty
leaf :: a -> FreeTree a
leaf = Free . Const
unJoin :: (Tree a, Tree a) I.<-> Tree a
unJoin = [I.biCase|t <-> TreeJoin t|]
unChoose :: Either (Tree a) (Tree a) I.<-> Tree a
unChoose = [I.biCase|t <-> TreeChoose t|]
instance Q.Arbitrary a => Q.Arbitrary (FreeTree a) where
arbitrary = genTree
[ return emptyTree
, leaf <$> Q.arbitrary
, Transform unJoin <$> (Join <$> Q.arbitrary <*> Q.arbitrary)
, Transform unChoose <$> (Choose <$> Q.arbitrary <*> Q.arbitrary)
, Transform I.id <$> Q.arbitrary
]
shrink (Free x) = map Free (Q.shrink x)
shrink (Transform _ Empty) = []
shrink (Transform _ (Join p q)) =
[emptyTree, pt, qt] ++ map (Transform unJoin . uncurry Join) (Q.shrink (pt, qt)) where
pt = unsafeCoerce p :: FreeTree a
qt = unsafeCoerce q :: FreeTree a
shrink (Transform _ (Choose p q)) =
[emptyTree, pt, qt] ++ map (Transform unChoose . uncurry Choose) (Q.shrink (pt, qt)) where
pt = unsafeCoerce p :: FreeTree a
qt = unsafeCoerce q :: FreeTree a
shrink (Transform _ p) = [pt] where
pt = unsafeCoerce p :: FreeTree a
type ShowFree f = (Functor f, Show1 f)
ok :: Q.Property
ok = Q.property True
bad :: ShowFree f => Free f a -> Q.Property
bad f = Q.counterexample (show f) False
checkNoT :: ShowFree f => Free f a -> Q.Property
checkNoT t@(Transform _ _) = bad t
checkNoT (Join p q) = checkNoT p Q..&&. checkNoT q
checkNoT (Choose p q) = checkNoT p Q..&&. checkNoT q
checkNoT _ = ok
checkTNF :: ShowFree f => Free f a -> Q.Property
checkTNF (Transform _ p) = checkNoT p
checkTNF p = checkNoT p
joinDNF :: ShowFree f => Free f a -> Q.Property
joinDNF t@(Transform _ _) = bad t
joinDNF c@(Choose _ _) = bad c
joinDNF (Join p q) = joinDNF p Q..&&. joinDNF q
joinDNF _ = ok
checkDNF :: ShowFree f => Free f a -> Q.Property
checkDNF t@(Transform _ _) = bad t
checkDNF (Choose p q) = checkDNF p Q..&&. checkDNF q
checkDNF p = joinDNF p
checkTDNF :: ShowFree f => Free f a -> Q.Property
checkTDNF (Transform _ p) = checkDNF p
checkTDNF p = checkDNF p
produceParse :: FreeTree Int -> [Int] -> Q.Property
produceParse f l = isJust p Q.==> check where
check = prod f r ++ t Q.=== l Q..&&. reverse t ++ prod (reverseFree f) r Q.=== reverse l
Just (r, t) = p
p = pars f l
pars = parseFree parse
parse (Const i) j = unsafeCoerce (TreeFree j) <$ guard (j >= i)
prod = produceFree produce
produce (Const _) x = case unsafeCoerce x of { ~(TreeFree j) -> j }
joinSorted :: (Show a, Ord a) => Maybe a -> Free (Const a) b -> (Maybe a, Q.Property)
joinSorted b (Join p q) = (qb, pr Q..&&. qr) where
(pb, pr) = joinSorted b p
(qb, qr) = joinSorted pb q
joinSorted b (Free (Const x)) = (Just x, Q.counterexample (show x ++ " < " ++ show b) $ all (x >=) b)
joinSorted b Empty = (b, ok)
joinSorted b o = (b, bad o)
chooseSorted :: (Show a, Ord a) => Free (Const a) b -> Q.Property
chooseSorted t@(Transform _ _) = bad t
chooseSorted (Choose p q) = chooseSorted p Q..&&. chooseSorted q
chooseSorted p = snd $ joinSorted Nothing p
checkSorted :: (Show a, Ord a) => Free (Const a) b -> Q.Property
checkSorted (Transform _ p) = chooseSorted p
checkSorted p = chooseSorted p
compareConst :: Ord a => Const a b -> Const a c -> Ordering
compareConst (Const x) (Const y) = compare x y
tests :: Q.Property
tests = Q.conjoin
[ Q.label "TNF" $ checkTNF . (freeTNF :: FreeTree Int -> FreeTree Int)
, Q.label "TDNF" $ checkTDNF . (freeTDNF :: FreeTree Int -> FreeTree Int)
, Q.label "parseProduce" $ produceParse
, Q.label "sort" $ checkSorted . (sortFreeTDNF compareConst :: FreeTree Int -> FreeTree Int)
]