ideas-0.5.8: src/Domain/Logic/Generator.hs
{-# LANGUAGE TypeSynonymInstances #-}
-----------------------------------------------------------------------------
-- Copyright 2009, Open Universiteit Nederland. This file is distributed
-- under the terms of the GNU General Public License. For more information,
-- see the file "LICENSE.txt", which is included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer : bastiaan.heeren@ou.nl
-- Stability : provisional
-- Portability : portable (depends on ghc)
--
-----------------------------------------------------------------------------
module Domain.Logic.Generator
( generateLogic, generateLevel, equalLogicA, Level(..)
) where
import Domain.Logic.Formula
import Control.Monad
import Data.Char
import Test.QuickCheck hiding (defaultConfig)
import Common.Rewriting
import Common.Uniplate
-------------------------------------------------------------
-- Code that doesn't belong here, but the arbitrary instance
-- is needed for the Rewrite instance.
instance Rewrite SLogic where
operators = logicOperators
-- | Equality modulo associativity of operators
equalLogicA:: SLogic -> SLogic -> Bool
equalLogicA = equalWith operators
-----------------------------------------------------------
-- Logic generator
data Level = Easy | Normal | Difficult
deriving Show
generateLogic :: Gen SLogic
generateLogic = normalGenerator
generateLevel :: Level -> (Gen SLogic, (Int, Int))
generateLevel level =
case level of
Easy -> (easyGenerator, (3, 6))
Normal -> (normalGenerator, (4, 12))
Difficult -> (difficultGenerator, (7, 18))
-- Use the propositions with 3-6 steps
easyGenerator :: Gen SLogic
easyGenerator = do
n <- oneof [return 2, return 4] -- , return 8]
sizedGen True varGen n
-- Use the propositions with 4-12 steps
normalGenerator :: Gen SLogic
normalGenerator = do
n <- return 4 -- oneof [return 4, return 8]
p0 <- sizedGen False varGen n
p1 <- preventSameVar varList p0
return (removePartsInDNF p1)
-- Use the propositions with 7-18 steps
difficultGenerator :: Gen SLogic
difficultGenerator = do
let vars = "s" : varList
n <- return 4 -- oneof [return 4, return 8]
p0 <- sizedGen False (oneof $ map return vars) n
p1 <- preventSameVar vars p0
return (removePartsInDNF p1)
varList :: [String]
varList = ["p", "q", "r"]
varGen :: Gen String
varGen = oneof $ map return varList
sizedGen :: Bool -> Gen a -> Int -> Gen (Logic a)
sizedGen constants gen = go
where
go n
| n > 0 =
let rec = go (n `div` 2)
op2 f = liftM2 f rec rec
in frequency
[ (2, go 0)
, (2, op2 (:->:))
, (1, op2 (:<->:))
, (3, op2 (:&&:))
, (3, op2 (:||:))
, (3, liftM Not rec)
]
| constants = frequency
[(5, liftM Var gen), (1, return T), (1, return F)]
| otherwise = liftM Var gen
-----------------------------------------------------------------
-- Simple tricks for creating for "nice" logic propositions
preventSameVar :: Eq a => [a] -> Logic a -> Gen (Logic a)
preventSameVar xs = transformM $ \p ->
case uniplate p of
([Var a, Var b], f) | a==b -> do
c <- oneof $ map return $ filter (/=a) xs
return $ f [Var a, Var c]
_ -> return p
removePartsInDNF :: SLogic -> SLogic
removePartsInDNF = buildOr . filter (not . simple) . disjunctions
where
buildOr [] = T
buildOr xs = foldl1 (:||:) xs
simple = all f . conjunctions
where
f (Not p) = null (children p)
f p = null (children p)
-----------------------------------------------------------
--- QuickCheck generator
instance Arbitrary SLogic where
arbitrary = sized (sizedGen True varGen)
coarbitrary logic =
case logic of
Var x -> variant 0 . coarbitrary (map ord x)
p :->: q -> variant 1 . coarbitrary p . coarbitrary q
p :<->: q -> variant 2 . coarbitrary p . coarbitrary q
p :&&: q -> variant 3 . coarbitrary p . coarbitrary q
p :||: q -> variant 4 . coarbitrary p . coarbitrary q
Not p -> variant 5 . coarbitrary p
T -> variant 6
F -> variant 7