diff options
Diffstat (limited to 'edit-lens/test/Control/FSTTest.hs')
-rw-r--r-- | edit-lens/test/Control/FSTTest.hs | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/edit-lens/test/Control/FSTTest.hs b/edit-lens/test/Control/FSTTest.hs new file mode 100644 index 0000000..f5e02c2 --- /dev/null +++ b/edit-lens/test/Control/FSTTest.hs | |||
@@ -0,0 +1,187 @@ | |||
1 | module Control.FSTTest where | ||
2 | |||
3 | import Control.FST | ||
4 | |||
5 | import Data.Set (Set) | ||
6 | import qualified Data.Set as Set | ||
7 | |||
8 | import Data.Map.Strict (Map) | ||
9 | import qualified Data.Map.Strict as Map | ||
10 | |||
11 | import Data.Sequence (Seq) | ||
12 | import qualified Data.Sequence as Seq | ||
13 | |||
14 | import Data.Maybe (fromMaybe) | ||
15 | import Data.Foldable (Foldable(..)) | ||
16 | |||
17 | import Control.Monad (when) | ||
18 | |||
19 | import Data.Void | ||
20 | |||
21 | import Test.Tasty | ||
22 | import Test.Tasty.Hedgehog | ||
23 | import Test.Tasty.HUnit hiding (assert) | ||
24 | |||
25 | import Hedgehog | ||
26 | import qualified Hedgehog.Gen as G | ||
27 | import qualified Hedgehog.Range as R | ||
28 | |||
29 | import Numeric.Natural | ||
30 | |||
31 | import Text.PrettyPrint.Leijen (Pretty(..)) | ||
32 | |||
33 | import Control.DeepSeq (force) | ||
34 | |||
35 | |||
36 | fstId :: Ord a => Set a -> FST () a a | ||
37 | fstId syms = FST | ||
38 | { stInitial = Set.singleton () | ||
39 | , stTransition = Map.fromList [(((), Just sym), Set.singleton ((), Just sym)) | sym <- Set.toList syms] | ||
40 | , stAccept = Set.singleton () | ||
41 | } | ||
42 | |||
43 | fstDouble :: Ord a => Set a -> FST (Maybe a) a a | ||
44 | fstDouble syms = FST | ||
45 | { stInitial = Set.singleton Nothing | ||
46 | , stTransition = Map.fromListWith Set.union . concat $ | ||
47 | [ [((Nothing, Just sym), Set.singleton (Just sym, Just sym)) | sym <- Set.toList syms] | ||
48 | , [((Just sym, Nothing), Set.singleton (Nothing, Just sym)) | sym <- Set.toList syms] | ||
49 | ] | ||
50 | , stAccept = Set.singleton Nothing | ||
51 | } | ||
52 | |||
53 | fstRunLengthDecode :: Ord a | ||
54 | => Set a -- ^ Alphabet | ||
55 | -> Natural -- ^ Upper limit to run length | ||
56 | -> FST (Maybe (Natural, Maybe a)) (Either Natural a) a | ||
57 | fstRunLengthDecode syms lim = FST | ||
58 | { stInitial = Set.singleton Nothing | ||
59 | , stTransition = Map.fromListWith Set.union . concat $ | ||
60 | [ [((Nothing, Just (Left n)), Set.singleton (Just (n, Nothing), Nothing)) | n <- [0..lim]] | ||
61 | , [((Just (n, Nothing), Just (Right sym)), Set.singleton (Just (n, Just sym), Nothing)) | n <- [0..lim], sym <- Set.toList syms] | ||
62 | , [((Just (n, Just sym), Nothing), Set.singleton (Just (pred n, Just sym), Just sym)) | n <- [1..lim], sym <- Set.toList syms] | ||
63 | , [((Just (0, Just sym), Nothing), Set.singleton (Nothing, Nothing)) | sym <- Set.toList syms] | ||
64 | ] | ||
65 | , stAccept = Set.singleton Nothing | ||
66 | } | ||
67 | |||
68 | data StRunLengthEncode a = STREInitial | ||
69 | | STRECountUp a Natural | ||
70 | | STRESwitch (Maybe a) a | ||
71 | | STREFinish | ||
72 | deriving (Show, Eq, Ord) | ||
73 | |||
74 | fstRunLengthEncode :: Ord a | ||
75 | => Bool -- ^ Generate /all/ run length encodings instead of the best | ||
76 | -> Set a -- ^ Alphabet | ||
77 | -> Natural -- ^ Upper limit to run length | ||
78 | -> FST (StRunLengthEncode a) a (Either Natural a) | ||
79 | fstRunLengthEncode genAll syms lim = FST | ||
80 | { stInitial = Set.singleton STREInitial | ||
81 | , stTransition = Map.fromListWith Set.union . concat $ | ||
82 | [ [((STREInitial, Just sym), Set.singleton (STRECountUp sym 1, Nothing)) | sym <- Set.toList syms] | ||
83 | , [((STRECountUp sym n, Just sym), Set.singleton (STRECountUp sym (succ n), Nothing)) | sym <- Set.toList syms, n <- [1..pred lim]] | ||
84 | , [((STRECountUp sym n, Just sym'), Set.singleton (STRESwitch (Just sym') sym, Just $ Left n)) | n <- [1..lim], sym <- Set.toList syms, sym' <- Set.toList syms, sym /= sym'] | ||
85 | , [((STRECountUp sym lim, Just sym), Set.singleton (STRESwitch (Just sym) sym, Just $ Left lim)) | sym <- Set.toList syms] | ||
86 | , [((STRECountUp sym n, Nothing), Set.singleton (STRESwitch Nothing sym, Just $ Left n)) | sym <- Set.toList syms, n <- [1..lim]] | ||
87 | , [((STRESwitch (Just sym') sym, Nothing), Set.singleton (STRECountUp sym' 1, Just $ Right sym)) | sym <- Set.toList syms, sym' <- Set.toList syms] | ||
88 | , [((STRESwitch Nothing sym, Nothing), Set.singleton (STREFinish, Just $ Right sym)) | sym <- Set.toList syms] | ||
89 | , [((STRECountUp sym n, Just sym), Set.singleton (STRESwitch (Just sym) sym, Just $ Left n)) | n <- [1..lim], sym <- Set.toList syms, genAll] | ||
90 | ] | ||
91 | , stAccept = Set.fromList [STREInitial, STREFinish] | ||
92 | } | ||
93 | |||
94 | |||
95 | genWord :: Gen [Natural] | ||
96 | genWord = G.list (R.linear 0 1000) . G.integral $ R.linear 0 100 | ||
97 | |||
98 | |||
99 | runFSTDet :: (Show output, Ord output, Show state, Ord state) => (Set Natural -> FST state Natural output) -> (Seq Natural -> Seq output) -> Property | ||
100 | runFSTDet mkFst f = property $ do | ||
101 | input <- forAll genWord | ||
102 | let fst = mkFst $ Set.fromList input | ||
103 | annotateShow $ pretty fst | ||
104 | [f $ Seq.fromList input] === runFST fst (Seq.fromList input) | ||
105 | |||
106 | |||
107 | hprop_runFSTId, hprop_runFSTDouble :: Property | ||
108 | hprop_runFSTId = runFSTDet fstId id | ||
109 | hprop_runFSTDouble = runFSTDet fstDouble double | ||
110 | where | ||
111 | double :: Seq a -> Seq a | ||
112 | double Seq.Empty = Seq.Empty | ||
113 | double (a Seq.:<| as) = a Seq.:<| a Seq.:<| double as | ||
114 | |||
115 | hprop_runWordFST :: Property | ||
116 | hprop_runWordFST = property $ do | ||
117 | input <- forAll genWord | ||
118 | let fst = wordFST $ Seq.fromList input | ||
119 | annotateShow $ pretty fst | ||
120 | [Seq.fromList input] === runFST fst (Seq.empty :: Seq Void) | ||
121 | |||
122 | unit_runLengthDecode, unit_runLengthEncode :: Assertion | ||
123 | unit_runLengthDecode = runFST fst input @?= [Seq.fromList "aaacc"] | ||
124 | where | ||
125 | input = Seq.fromList [Left 3, Right 'a', Left 0, Right 'b', Left 2, Right 'c'] | ||
126 | fst = fstRunLengthDecode (Set.fromList "abc") 3 | ||
127 | unit_runLengthEncode = runFST fst input @?= [Seq.fromList [Left 3, Right 'a', Left 2, Right 'c']] | ||
128 | where | ||
129 | input = Seq.fromList "aaacc" | ||
130 | fst = fstRunLengthEncode False (Set.fromList "abc") 3 | ||
131 | |||
132 | hprop_runLength :: Property | ||
133 | hprop_runLength = property $ do | ||
134 | input <- forAll genWord | ||
135 | let maxRun = fromMaybe 1 $ countMaxRun input | ||
136 | alphabet = Set.fromList input | ||
137 | encode = fstRunLengthEncode False alphabet maxRun | ||
138 | decode = fstRunLengthDecode alphabet maxRun | ||
139 | annotateShow $ countMaxRun input | ||
140 | annotateShow $ pretty encode | ||
141 | annotateShow $ pretty decode | ||
142 | [encoded] <- return . runFST encode $ Seq.fromList input | ||
143 | annotateShow $ toList encoded | ||
144 | [Seq.fromList input] === runFST decode encoded | ||
145 | |||
146 | hprop_runLength' :: Property | ||
147 | hprop_runLength' = property $ do | ||
148 | input <- forAll genWord | ||
149 | |||
150 | let maxRun = fromMaybe 1 $ countMaxRun input | ||
151 | alphabet = Set.fromList input | ||
152 | encode = fstRunLengthEncode True alphabet maxRun | ||
153 | decode = fstRunLengthDecode alphabet maxRun | ||
154 | annotateShow $ countMaxRun input | ||
155 | annotateShow $ pretty encode | ||
156 | annotateShow $ pretty decode | ||
157 | |||
158 | let encoded = runFST encode $ Seq.fromList input | ||
159 | annotateShow encoded | ||
160 | when (maxRun > 1) $ | ||
161 | assert $ encoded `longerThan` 1 | ||
162 | |||
163 | encoded' <- forAll $ G.element encoded | ||
164 | [Seq.fromList input] === runFST decode encoded' | ||
165 | where | ||
166 | longerThan :: [a] -> Natural -> Bool | ||
167 | longerThan [] _ = False | ||
168 | longerThan _ 0 = True | ||
169 | longerThan (_:xs) n = longerThan xs $ pred n | ||
170 | |||
171 | countMaxRun :: (Eq a, Foldable f) => f a -> Maybe Natural | ||
172 | countMaxRun = goMaxRun Nothing Nothing . toList | ||
173 | where | ||
174 | goMaxRun :: Eq a | ||
175 | => Maybe (a, Natural) -- ^ Maximum | ||
176 | -> Maybe (a, Natural) -- ^ Current | ||
177 | -> [a] | ||
178 | -> Maybe Natural | ||
179 | goMaxRun Nothing Nothing [] = Nothing | ||
180 | goMaxRun (Just (_, n)) (Just (_, n')) [] | ||
181 | | n' > n = Just n' | ||
182 | | otherwise = Just n | ||
183 | goMaxRun Nothing Nothing (a:as) = goMaxRun (Just (a, 1)) (Just (a, 1)) as | ||
184 | goMaxRun (Just (a, n)) (Just (a', n')) (x:xs) | ||
185 | | x == a' = goMaxRun (Just (a, n)) (Just (a', succ n')) xs | ||
186 | | n' > n = goMaxRun (Just (a', n')) (Just (x, 1)) xs | ||
187 | | otherwise = goMaxRun (Just (a, n)) (Just (x, 1)) xs | ||