sequitur-0.2.0.0: test/Spec.hs
import Control.Monad
import qualified Data.Foldable as F
import qualified Data.Map.Strict as Map
import Data.Monoid
import qualified Data.IntMap.Lazy as IntMap
import qualified Data.IntSet as IntSet
import Data.List (intercalate)
import qualified Data.Set as Set
import Test.Hspec
import Test.QuickCheck
import Language.Grammar.Sequitur
main :: IO ()
main = hspec $ do
describe "Sequitur.encode" $ do
let cases = map (\(name, m) -> (name, Grammar m))
[ ( "abab"
, IntMap.fromList [(0, [NonTerminal 1, NonTerminal 1]), (1, [Terminal 'a', Terminal 'b'])]
)
, ( "abcab"
, IntMap.fromList [(0, [NonTerminal 1, Terminal 'c', NonTerminal 1]), (1, [Terminal 'a', Terminal 'b'])]
)
, ( "abcabc"
, IntMap.fromList [(0, [NonTerminal 2, NonTerminal 2]), (2, [Terminal 'a', Terminal 'b', Terminal 'c'])]
)
, ( "aaa"
, IntMap.fromList [(0,[Terminal 'a', Terminal 'a', Terminal 'a'])]
)
, ( "baaabacaa"
, IntMap.fromList [(0,[NonTerminal 1,NonTerminal 2,NonTerminal 1,Terminal 'c',NonTerminal 2]),(1,[Terminal 'b',Terminal 'a']),(2,[Terminal 'a',Terminal 'a'])]
)
]
forM_ cases $ \(xs, grammar) -> do
it ("returns " ++ reprGrammar grammar ++ " for " ++ show xs) $ do
encode xs `shouldBe` grammar
it "returns a grammer with digram uniqueness property" $
property $ forAll simpleString $ \s ->
let g = encode s
in counterexample (reprGrammar g) $ digramUniqueness g
it "returns a grammer with rule utility property" $
property $ forAll simpleString $ \s ->
let g = encode s
in counterexample (reprGrammar g) $ ruleUtility g
describe "Sequitur.decode" $ do
it "is the inverse of encode" $
property $ forAll simpleString $ \s ->
let g = encode s
s' = decode g
in counterexample (reprGrammar g) $ counterexample s' $ s == s'
it "is lazy" $
let g = Grammar $ IntMap.fromList [(0, [Terminal 'a', NonTerminal 1]), (1, undefined)]
s = decode g
in counterexample (reprGrammar g) $ head s `shouldBe` 'a'
describe "Sequitur.decodeToSeq" $ do
it "is equivalent to Sequitur.decode" $
property $ forAll simpleString $ \s ->
let g = encode s
in counterexample (reprGrammar g) $ decode g === F.toList (decodeToSeq g)
describe "Sequitur.decodeToMonoid" $ do
it "can be used to compute length" $
property $ forAll simpleString $ \s ->
let g = encode s
in counterexample (reprGrammar g) $ getSum (decodeToMonoid (\_ -> Sum 1) g) === length (decode g)
describe "Sequitur.decodeNonTerminalsToMonoid" $ do
it "is consistent with decode" $
property $ forAll simpleString $ \s ->
let g = encode s
in counterexample (reprGrammar g) $ (decodeNonTerminalsToMonoid (\c -> [c]) g IntMap.! 0) === decode g
simpleString :: Gen String
simpleString = liftArbitrary (elements ['a'..'z'])
reprGrammar :: Grammar Char -> String
reprGrammar (Grammar m) = "{" ++ intercalate ", " [show nt ++ " -> " ++ intercalate " " (map reprSymbol body) | (nt, body) <- IntMap.toAscList m] ++ "}"
where
reprSymbol (Terminal c) = [c]
reprSymbol (NonTerminal x) = show x
digramUniqueness :: Grammar Char -> Property
digramUniqueness (Grammar m) = conjoin
[ counterexample (show ce) $
case Set.toList ps of
[_] -> True
[(i1, j1), (i2, j2)] -> i1 == i2 && (j1 == j2 + 1 || j2 == j1 + 1)
_ -> False
| ce@(_digram, ps) <- Map.toList occurrences
]
where
occurrences = Map.fromListWith Set.union
[ (digram, Set.singleton (i,j))
| (i, body) <- IntMap.toList m, (j, digram) <- zip [(0::Int)..] (zip body (drop 1 body))
]
ruleUtility :: Grammar Char -> Property
ruleUtility (Grammar m) =
conjoin [counterexample (show (r, n)) $ n >= 2 | (r, n) <- IntMap.toList occurrences]
.&&.
IntMap.keysSet m === IntSet.insert 0 (IntMap.keysSet occurrences)
where
occurrences = IntMap.fromListWith (+)
[(r, (1::Int)) | body <- IntMap.elems m, NonTerminal r <- body]