summaryrefslogtreecommitdiff
path: root/edit-lens/test/Control/FSTTest.hs
blob: f5e02c2045495ebe690500e5bd1a0cf97949ea4a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
module Control.FSTTest where

import Control.FST

import Data.Set (Set)
import qualified Data.Set as Set

import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map

import Data.Sequence (Seq)
import qualified Data.Sequence as Seq

import Data.Maybe (fromMaybe)
import Data.Foldable (Foldable(..))

import Control.Monad (when)

import Data.Void

import Test.Tasty
import Test.Tasty.Hedgehog
import Test.Tasty.HUnit hiding (assert)

import Hedgehog
import qualified Hedgehog.Gen as G
import qualified Hedgehog.Range as R

import Numeric.Natural

import Text.PrettyPrint.Leijen (Pretty(..))

import Control.DeepSeq (force)


fstId :: Ord a => Set a -> FST () a a
fstId syms = FST
  { stInitial = Set.singleton ()
  , stTransition = Map.fromList [(((), Just sym), Set.singleton ((), Just sym)) | sym <- Set.toList syms]
  , stAccept = Set.singleton ()
  }

fstDouble :: Ord a => Set a -> FST (Maybe a) a a
fstDouble syms = FST
  { stInitial = Set.singleton Nothing
  , stTransition = Map.fromListWith Set.union . concat $
    [ [((Nothing, Just sym), Set.singleton (Just sym, Just sym)) | sym <- Set.toList syms]
    , [((Just sym, Nothing), Set.singleton (Nothing, Just sym)) | sym <- Set.toList syms]
    ]
  , stAccept = Set.singleton Nothing
  }

fstRunLengthDecode :: Ord a
                   => Set a -- ^ Alphabet
                   -> Natural -- ^ Upper limit to run length
                   -> FST (Maybe (Natural, Maybe a)) (Either Natural a) a
fstRunLengthDecode syms lim = FST
  { stInitial = Set.singleton Nothing
  , stTransition = Map.fromListWith Set.union . concat $
    [ [((Nothing, Just (Left n)), Set.singleton (Just (n, Nothing), Nothing)) | n <- [0..lim]]
    , [((Just (n, Nothing), Just (Right sym)), Set.singleton (Just (n, Just sym), Nothing)) | n <- [0..lim], sym <- Set.toList syms]
    , [((Just (n, Just sym), Nothing), Set.singleton (Just (pred n, Just sym), Just sym)) | n <- [1..lim], sym <- Set.toList syms]
    , [((Just (0, Just sym), Nothing), Set.singleton (Nothing, Nothing)) | sym <- Set.toList syms]
    ]
  , stAccept = Set.singleton Nothing
  }

data StRunLengthEncode a = STREInitial
                         | STRECountUp a Natural
                         | STRESwitch (Maybe a) a
                         | STREFinish
  deriving (Show, Eq, Ord)

fstRunLengthEncode :: Ord a
                   => Bool -- ^ Generate /all/ run length encodings instead of the best
                   -> Set a -- ^ Alphabet
                   -> Natural -- ^ Upper limit to run length
                   -> FST (StRunLengthEncode a) a (Either Natural a)
fstRunLengthEncode genAll syms lim = FST
  { stInitial = Set.singleton STREInitial
  , stTransition = Map.fromListWith Set.union . concat $
    [ [((STREInitial, Just sym), Set.singleton (STRECountUp sym 1, Nothing)) | sym <- Set.toList syms]
    , [((STRECountUp sym n, Just sym), Set.singleton (STRECountUp sym (succ n), Nothing)) | sym <- Set.toList syms, n <- [1..pred lim]]
    , [((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']
    , [((STRECountUp sym lim, Just sym), Set.singleton (STRESwitch (Just sym) sym, Just $ Left lim)) | sym <- Set.toList syms]
    , [((STRECountUp sym n, Nothing), Set.singleton (STRESwitch Nothing sym, Just $ Left n)) | sym <- Set.toList syms, n <- [1..lim]]
    , [((STRESwitch (Just sym') sym, Nothing), Set.singleton (STRECountUp sym' 1, Just $ Right sym)) | sym <- Set.toList syms, sym' <- Set.toList syms]
    , [((STRESwitch Nothing sym, Nothing), Set.singleton (STREFinish, Just $ Right sym)) | sym <- Set.toList syms]
    , [((STRECountUp sym n, Just sym), Set.singleton (STRESwitch (Just sym) sym, Just $ Left n)) | n <- [1..lim], sym <- Set.toList syms, genAll]
    ]
  , stAccept = Set.fromList [STREInitial, STREFinish]
  }


genWord :: Gen [Natural]
genWord = G.list (R.linear 0 1000) . G.integral $ R.linear 0 100

  
runFSTDet :: (Show output, Ord output, Show state, Ord state) => (Set Natural -> FST state Natural output) -> (Seq Natural -> Seq output) -> Property
runFSTDet mkFst f = property $ do
  input <- forAll genWord
  let fst = mkFst $ Set.fromList input
  annotateShow $ pretty fst
  [f $ Seq.fromList input] === runFST fst (Seq.fromList input)


hprop_runFSTId, hprop_runFSTDouble :: Property
hprop_runFSTId = runFSTDet fstId id
hprop_runFSTDouble = runFSTDet fstDouble double
  where
    double :: Seq a -> Seq a
    double Seq.Empty = Seq.Empty
    double (a Seq.:<| as) = a Seq.:<| a Seq.:<| double as

hprop_runWordFST :: Property
hprop_runWordFST = property $ do
  input <- forAll genWord
  let fst = wordFST $ Seq.fromList input
  annotateShow $ pretty fst
  [Seq.fromList input] === runFST fst (Seq.empty :: Seq Void)

unit_runLengthDecode, unit_runLengthEncode :: Assertion
unit_runLengthDecode = runFST fst input @?= [Seq.fromList "aaacc"]
  where
    input = Seq.fromList [Left 3, Right 'a', Left 0, Right 'b', Left 2, Right 'c']
    fst = fstRunLengthDecode (Set.fromList "abc") 3
unit_runLengthEncode = runFST fst input @?= [Seq.fromList [Left 3, Right 'a', Left 2, Right 'c']]
  where
    input = Seq.fromList "aaacc"
    fst = fstRunLengthEncode False (Set.fromList "abc") 3

hprop_runLength :: Property
hprop_runLength = property $ do
  input <- forAll genWord
  let maxRun = fromMaybe 1 $ countMaxRun input
      alphabet = Set.fromList input
      encode = fstRunLengthEncode False alphabet maxRun
      decode = fstRunLengthDecode alphabet maxRun
  annotateShow $ countMaxRun input
  annotateShow $ pretty encode
  annotateShow $ pretty decode
  [encoded] <- return . runFST encode $ Seq.fromList input
  annotateShow $ toList encoded
  [Seq.fromList input] === runFST decode encoded

hprop_runLength' :: Property
hprop_runLength' = property $ do
  input <- forAll genWord

  let maxRun = fromMaybe 1 $ countMaxRun input
      alphabet = Set.fromList input
      encode = fstRunLengthEncode True alphabet maxRun
      decode = fstRunLengthDecode alphabet maxRun
  annotateShow $ countMaxRun input
  annotateShow $ pretty encode
  annotateShow $ pretty decode

  let encoded = runFST encode $ Seq.fromList input
  annotateShow encoded
  when (maxRun > 1) $
    assert $ encoded `longerThan` 1

  encoded' <- forAll $ G.element encoded
  [Seq.fromList input] === runFST decode encoded'
  where
    longerThan :: [a] -> Natural -> Bool
    longerThan []     _ = False
    longerThan  _     0 = True
    longerThan (_:xs) n = longerThan xs $ pred n

countMaxRun :: (Eq a, Foldable f) => f a -> Maybe Natural
countMaxRun = goMaxRun Nothing Nothing . toList
  where
    goMaxRun :: Eq a
             => Maybe (a, Natural) -- ^ Maximum
             -> Maybe (a, Natural) -- ^ Current
             -> [a]
             -> Maybe Natural
    goMaxRun Nothing Nothing [] = Nothing
    goMaxRun (Just (_, n)) (Just (_, n')) []
      | n' > n    = Just n'
      | otherwise = Just n
    goMaxRun Nothing Nothing (a:as) = goMaxRun (Just (a, 1)) (Just (a, 1)) as
    goMaxRun (Just (a, n)) (Just (a', n')) (x:xs)
      | x == a'   = goMaxRun (Just (a, n)) (Just (a', succ n')) xs
      | n' > n    = goMaxRun (Just (a', n')) (Just (x, 1)) xs
      | otherwise = goMaxRun (Just (a, n)) (Just (x, 1)) xs