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 | ||
