monad-memo-0.2.0: Control/Monad/Memo/Test.hs
{-# LANGUAGE FlexibleInstances #-}
module Control.Monad.Memo.Test
(
tests
) where
import Test.QuickCheck
import System.Random
import Control.Monad.Memo
import Control.Monad.Reader
import Control.Monad.Writer
import Control.Monad.State
import Control.Monad.Cont
import Control.Monad.List
import Test.Framework (defaultMain, testGroup)
import Test.Framework.Providers.QuickCheck2 (testProperty)
newtype SmallInt n = SmallInt { toInt::n } deriving Show
instance (Num n, Random n) => Arbitrary (SmallInt n) where
arbitrary = fmap SmallInt $ choose (0,10)
newtype SmallList a = SmallList { toList::[a] } deriving Show
instance Arbitrary a => Arbitrary (SmallList a) where
arbitrary = do
n <- choose (0,10)
ls <- arbitrary
return $ SmallList $ take n ls
-- | With ReaderT
fibr 0 = return 0
fibr 1 = return 1
fibr 2 = return 1
fibr n = do
p1 <- ask
p2 <- local (const (p1+1)) $ fibr (n-2)
f1 <- fibr (n-1)
f2 <- fibr (n-2)
return (p1+f1+f2+p2)
runFibr r = (`runReader`r) . fibr
fibmr 0 = return 0
fibmr 1 = return 1
fibmr 2 = return 1
fibmr n = do
p1 <- ask
p2 <- local (const (p1+1)) $ memo fibmr (n-2)
f1 <- memo fibmr (n-1)
f2 <- memo fibmr (n-2)
return (p1+f1+f2+p2)
runFibmr r = startEvalMemo . (`runReaderT`r) . fibmr
prop_ReaderEqv :: SmallInt Int -> SmallInt Int -> Bool
prop_ReaderEqv r n =
((`runReader`(toInt r)) . fibr $ (toInt n)) == (startEvalMemo . (`runReaderT`(toInt r)) . fibmr $ (toInt n))
-- | With WriterT
fibw 0 = return 0
fibw 1 = return 1
fibw n = do
f1 <- fibw (n-1)
f2 <- fibw (n-2)
tell $ show n
return (f1+f2)
fibmw 0 = return 0
fibmw 1 = return 1
fibmw n = do
f1 <- memo fibmw (n-1)
f2 <- memo fibmw (n-2)
tell $ show n
return (f1+f2)
prop_WriterEqv :: SmallInt Int -> Bool
prop_WriterEqv n =
(runWriter . fibw . toInt $ n) == (startEvalMemo . runWriterT . fibmw . toInt $ n)
-- | With ContT
fibc 0 = return 0
fibc 1 = return 1
fibc n = do
f1 <- fibc (n-1)
f2 <- callCC $ \ break -> do
if n == 4 then break 42 else fibc (n-2)
return (f1+f2)
fibmc 0 = return 0
fibmc 1 = return 1
fibmc n = do
f1 <- memo fibmc (n-1)
f2 <- callCC $ \ break -> do
if n == 4 then break 42 else memo fibmc (n-2)
return (f1+f2)
prop_ContEqv :: SmallInt Int -> Bool
prop_ContEqv n =
((`runCont`id) . fibc . toInt $ n) == (startEvalMemo . (`runContT`return) . fibmc . toInt $ n)
-- | With StateT
fibs 0 = return 0
fibs 1 = return 1
fibs n = do
s <- get
f1 <- fibs (n-1)
f2 <- fibs (n-2)
modify $ \s -> s+1
return (f1+f2+s)
fibms 0 = return 0
fibms 1 = return 1
fibms n = do
s <- get
f1 <- memo fibms (n-1)
f2 <- memo fibms (n-2)
modify $ \s -> s+1
return (f1+f2+s)
prop_StateEqv :: SmallInt Int -> SmallInt Int -> Bool
prop_StateEqv s n =
((`runState`(toInt s)) . fibs . toInt $ n) == (startEvalMemo . (`runStateT`(toInt s)) . fibms . toInt $ n)
-- | With ListT
--
data Tree a = Leaf !a | Fork (Tree a) (Tree a) deriving Eq
partitions as = [ splitAt n as | n <- [1..length as - 1 ]]
unfringe [a] = [Leaf a]
unfringe as = do
(l,k) <- partitions as
t <- unfringe l
u <- unfringe k
return (Fork t u)
unfringem [a] = return (Leaf a)
unfringem as = do
(l,k) <- ListT $ return (partitions as)
t <- memo unfringem l
u <- memo unfringem k
return (Fork t u)
prop_ListEqv :: SmallList Char -> Bool
prop_ListEqv ls =
unfringe (toList ls) == (startEvalMemo . runListT . unfringem $ (toList ls))
-- | Mutual recursion
f :: Int -> (Int,String)
f 0 = (1,"+")
f n =(g((n-1),fst(f (n-1))),"-" ++ snd(f (n-1)))
g :: (Int, Int) -> Int
g (0, m) = m + 1
g (n,m) = fst(f (n-1))-g((n-1),m)
type MemoF = MemoT Int (Int,String)
type MemoG = Memo (Int,Int) Int
type MemoFG = MemoF MemoG
fm :: Int -> MemoFG (Int,String)
fm 0 = return (1,"+")
fm n = do
fn <- memol0 fm (n-1)
g <- memol1 gm (n-1 , fst fn)
return (g , "-" ++ snd fn)
gm :: (Int,Int) -> MemoFG Int
gm (0,m) = return (m+1)
gm (n,m) = do
fn <- memol0 fm (n-1)
g <- memol1 gm (n-1,m)
return $ fst fn - g
evalAll = startEvalMemo . startEvalMemoT
evalFm = evalAll . fm
evalGm = evalAll . gm
prop_MutualFEqv :: SmallInt Int -> Bool
prop_MutualFEqv sx = f x == evalFm x
where x = toInt sx
prop_MutualGEqv :: SmallInt Int -> SmallInt Int -> Bool
prop_MutualGEqv sx sy = g (x,y) == evalGm (x,y)
where
x = toInt sx
y = toInt sy
-- Same as above but without explicit uncurring
fm2 :: Int -> MemoFG (Int,String)
fm2 0 = return (1,"+")
fm2 n = do
fn <- memol0 fm2 (n-1)
g <- for2 memol1 gm2 (n-1) (fst fn)
return (g , "-" ++ snd fn)
gm2 :: Int -> Int -> MemoFG Int
gm2 0 m = return (m+1)
gm2 n m = do
fn <- memol0 fm2 (n-1)
g <- for2 memol1 gm2 (n-1) m
return $ fst fn - g
evalAll2 = startEvalMemo . startEvalMemoT
evalFm2 = evalAll . fm2
evalGm2 n m = evalAll $ gm2 n m
prop_Mutual2FEqv :: SmallInt Int -> Bool
prop_Mutual2FEqv sx = f x == evalFm2 x
where x = toInt sx
prop_Mutual2GEqv :: SmallInt Int -> SmallInt Int -> Bool
prop_Mutual2GEqv sx sy = g (x,y) == evalGm2 x y
where
x = toInt sx
y = toInt sy
tests = [
testGroup "Transformers" [
testProperty "ReaderEqv" prop_ReaderEqv,
testProperty "WriterEqv" prop_WriterEqv,
testProperty "ContEqv" prop_ContEqv,
testProperty "ListEqv" prop_ListEqv,
testProperty "StateEqv" prop_StateEqv
],
testGroup "Others" [
testProperty "MutualFGEqv" prop_MutualFEqv,
testProperty "MutualCurryFGEqv" prop_Mutual2FEqv
]
]