lattest-lib-0.1.0.0: test/Test/Lattest/Model/BoundedMonad.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ViewPatterns #-}
module Test.Lattest.Model.BoundedMonad (
prop_latticeIsCNF,
LatticeOp
)
where
import Test.QuickCheck
import qualified Lattest.Model.BoundedMonad as BM
import qualified Reference.FreeLatticeSlow as FL
import qualified Data.Map as Map
import qualified Data.Set as Set
import qualified Control.Monad as CM
import qualified Debug.Trace as Trace
-- operations for constructing free lattices. The Map of the Bind operation contains keys for exactly all free variables in the bound lattice
data LatticeOp a = Var a | Top | Bot | Join (LatticeOp a) (LatticeOp a) | Meet (LatticeOp a) (LatticeOp a) | Bind (LatticeOp a) (Map.Map a (LatticeOp a)) deriving (Eq, Ord, Show)
-- the free variables after performing the operations (in case of bind, only the free variables after substitution)
freeVars :: Ord a => LatticeOp a -> Set.Set a
freeVars (Var a) = Set.singleton a
freeVars (Join x y) = freeVars x `Set.union` freeVars y
freeVars (Meet x y) = freeVars x `Set.union` freeVars y
freeVars (Bind _ subs) = Set.unions $ freeVars <$> Map.elems subs
freeVars _ = Set.empty
instance (Arbitrary a, Ord a) => Arbitrary (LatticeOp a) where
arbitrary = sized arbitrary'
where
arbitrary' 0 = oneof [
Var <$> arbitrary,
return Top,
return Bot
]
arbitrary' n = oneof [
Var <$> arbitrary,
return Top,
return Bot,
CM.liftM2 Join sub sub,
CM.liftM2 Meet sub sub,
sub >>= wrapInBind (n - 1)
]
where
sub = arbitrary' (n - 1)
wrapInBind n' a = Bind a <$> (arbitraryMapping n' $ Set.toList $ freeVars a)
arbitraryMapping n' vars = Map.fromList <$> sequence (arbitraryIdPlusLatticeOp n' <$> vars)
arbitraryIdPlusLatticeOp n' a = do
l <- arbitrary' n'
return (a, l)
shrink Top = []
shrink Bot = []
shrink (Var _) = [Top, Bot]
shrink (Join x y) = [Join x' y' | (x', y') <- shrink (x, y)] ++ shrink x ++ shrink y
shrink (Meet x y) = [Meet x' y' | (x', y') <- shrink (x, y)] ++ shrink x ++ shrink y
shrink (Bind l subs) = [let subs' = Map.restrictKeys subs (freeVars l') in if Map.null subs then l' else (Bind l' subs') | l' <- shrink l]
++ [Bind l subs' | subs' <- simplifiedSubs]
where
simplifiedSubs = [ Map.insert var sub subs | (var,sub) <- Map.toList subs, _ <- shrink sub ]
constructLattice :: (BM.BoundedConfiguration l, BM.JoinSemiLattice (l a), BM.MeetSemiLattice (l a), BM.OrdMonad l, Ord a) => LatticeOp a -> l a
constructLattice Top = BM.underspecified
constructLattice Bot = BM.forbidden
constructLattice (Var a) = BM.ordReturn a
constructLattice (Join x y) = (constructLattice x) BM.\/ (constructLattice y)
constructLattice (Meet x y) = (constructLattice x) BM./\ (constructLattice y)
constructLattice (Bind l subs) =
let subs' = Map.map constructLattice subs
in (constructLattice l) `BM.ordBind` (subs' Map.!)
prop_latticeIsCNF :: (Ord a, Show a) => LatticeOp a -> Bool
prop_latticeIsCNF l =
let cnf = constructLattice l
standard = constructLattice l
outcome = (cnfToLattice $ cnf) == standard
message = "CNF:\n" ++ show cnf ++ "\n\nstandard:\n" ++ show standard ++ "\n\n"
in if outcome then True else Trace.trace message False
where
cnfToLattice :: BM.FreeLattice a -> FL.FreeLatticeSlow a
cnfToLattice (BM.FreeLattice ls) = cnfToLattice' ls
cnfToLattice' = Set.foldr mergeConjunct BM.underspecified
mergeConjunct :: Set.Set a -> FL.FreeLatticeSlow a -> FL.FreeLatticeSlow a
mergeConjunct conjunct l' = conjunctToLattice conjunct BM./\ l'
conjunctToLattice = Set.foldr mergeDisjunct BM.forbidden
mergeDisjunct :: a -> FL.FreeLatticeSlow a -> FL.FreeLatticeSlow a
mergeDisjunct a l' = return a BM.\/ l'