summaryrefslogtreecommitdiff
path: root/testsuite/tests/programs/maessen-hashtab/HashTest.hs
blob: 59795bb89e2d948793b4ccbc497149c5dc8e1cf0 (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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
{- Test code for Data.HashTable -}

module Main(main) where

import Prelude hiding (lookup)
import qualified Prelude (lookup)
import Data.Maybe(isJust,isNothing)
import Data.Int(Int32)
import Test.QuickCheck
import System.IO.Unsafe(unsafePerformIO)
import Data.HashTab
import Control.Monad(liftM2, foldM)
import System.Random
import System.Environment

infixr 0 ==.
infixr 0 ==~
infixr 0 ~~

type HT = HashTable Int Int
newtype HashFun = HF {unHF :: (Int -> Int32)}
data Empty = E {e :: (IO HT), hfe :: HashFun}
data MkH   = H {h :: (IO HT), hfh :: HashFun}
newtype List a = L [a]

data Action = Lookup Int
            | Insert Int Int
            | Delete Int
            | Update Int Int
            deriving (Show)

instance Arbitrary Action where
  arbitrary = frequency [(10,fmap Lookup arbitrary),
                         (5, liftM2 Insert arbitrary arbitrary),
                         (3, liftM2 Update arbitrary arbitrary),
                         (1, fmap Delete arbitrary)]

simA :: [Action] -> [Either Bool [Int]]
simA = fst . foldl sim ([],[])
  where sim :: ([Either Bool [Int]], [Action]) -> Action ->
               ([Either Bool [Int]], [Action])
        sim (res, past) (Lookup k)   = (Right (lkup k past) : res, past)
        sim (res, past) (Insert k v) = (res, Insert k v : past)
        sim (res, past) (Delete k)   = (res, Delete k   : past)
        sim (res, past) (Update k v) =
          (Left (not (null l)) : res, Update k v : past)
          where l = lkup k past
        lkup _ [] = []
        lkup k (Delete k' : _)
          | k==k' = []
        lkup k (Update k' v : _)
          | k==k'     = [v]
        lkup k (Insert k' v : past)
          | k==k'     = v:lkup k past
        lkup k (_ : past) = lkup k past

runA :: HashFun -> [Action] -> IO [Either Bool (Maybe Int)]
runA hf acts = do
  ht <- new (==) (unHF hf)
  let run res (Lookup a) = fmap (lkup res) $ lookup ht a
      run res (Insert k v) = insert ht k v >> return res
      run res (Delete k)   = delete ht k   >> return res
      run res (Update k v) = fmap (upd res) $ update ht k v
      lkup res m = Right m : res
      upd res b = Left b : res
  foldM run [] acts

(~~) :: IO [Either Bool (Maybe Int)] -> [Either Bool [Int]] -> Bool
acts ~~ sims = and $ zipWith same (unsafePerformIO acts) sims
  where same (Left b)         (Left b')  = b==b'
        same (Right Nothing)  (Right []) = True
        same (Right (Just a)) (Right xs) = a `elem` xs
        same _                _          = False

lookups :: HT -> [Int] -> IO [Maybe Int]
lookups ht ks = mapM (lookup ht) ks

instance Show HashFun where
  showsPrec _ (HF hf) r
      | hf 1 == 0 = "degenerate"++r
      | otherwise = "usual"++r

instance Show Empty where
  showsPrec _ ee r = shows (hfe ee) r

instance Show MkH where
  showsPrec _ hh r = shows (hfh hh) $
                     ("; "++shows (unsafePerformIO (h hh >>= toList)) r)

instance Show a => Show (List a) where
  showsPrec _ (L l) r = shows l r

instance Arbitrary HashFun where
  arbitrary = frequency [(20,return (HF hashInt)),
                         (1,return (HF (const 0)))]

instance Arbitrary Empty where
  arbitrary = fmap mkE arbitrary
    where mkE (HF hf) = E {e = new (==) hf, hfe=HF hf}

instance Arbitrary a => Arbitrary (List a) where
  arbitrary = do
    sz <- frequency [(50, sized return),
                     (1,return (4096*2)),
                     (0, return (1024*1024))]
    resize sz $ fmap L $ sized vector

instance Arbitrary MkH where
  arbitrary = do
    hf <- arbitrary
    L list <- arbitrary
    let mkH act = H { h = act, hfh = hf }
    return (mkH . fromList (unHF hf) $ list)

(==~) :: (Eq a) => IO a -> IO a -> Bool
act1 ==~ act2 = unsafePerformIO act1 == unsafePerformIO act2

(==.) :: (Eq a) => IO a -> a -> Bool
act ==. v = unsafePerformIO act == v

notin :: (Testable a) => Int -> MkH -> a -> Property
k `notin` hh = \prop ->
  let f = (not . isJust . unsafePerformIO) (h hh >>= flip lookup k) in
  f `trivial` prop

prop_emptyLookup :: Empty -> Int -> Bool
prop_emptyLookup ee k =
  isNothing . unsafePerformIO $
  (do mt <- e ee
      lookup mt k)

prop_emptyToList :: Empty -> Bool
prop_emptyToList ee =
  (do mt <- e ee
      toList mt) ==. []

prop_emptyFromList :: HashFun -> Int -> Bool
prop_emptyFromList hf k =
  (do mt <- new (==) (unHF hf) :: IO HT
      lookup mt k) ==~
  (do mt <- fromList (unHF hf) []
      lookup mt k)

prop_insert :: MkH -> Int -> Int -> Bool
prop_insert hh k v =
  (do ht <- h hh
      insert ht k v
      lookup ht k) ==. Just v

prop_insertu :: MkH -> Int -> Int -> List Int -> Bool
prop_insertu hh k v (L ks) =
  let ks' = filter (k /=) ks in
  (do ht <- h hh
      insert ht k v
      lookups ht ks') ==~
  (do ht <- h hh
      lookups ht ks')

prop_delete :: MkH -> Int -> Property
prop_delete hh k =
  k `notin` hh $
  isNothing . unsafePerformIO $
  (do ht <- h hh
      delete ht k
      lookup ht k)

prop_deleteu :: MkH -> Int -> List Int -> Bool
prop_deleteu hh k (L ks) =
  let ks' = filter (k /=) ks in
  (do ht <- h hh
      delete ht k
      lookups ht ks') ==~
  (do ht <- h hh
      lookups ht ks')

naiveUpdate :: HT -> Int -> Int -> IO ()
naiveUpdate ht k v = do
  delete ht k
  insert ht k v

prop_update :: MkH -> Int -> Int -> List Int -> Bool
prop_update hh k v (L ks) =
  (do ht <- h hh
      _ <- update ht k v
      lookups ht ks) ==~
  (do ht <- h hh
      naiveUpdate ht k v
      lookups ht ks)

prop_updatec :: MkH -> Int -> Int -> Bool
prop_updatec hh k v =
  (do ht <- h hh
      _ <- update ht k v
      lookup ht k) ==. Just v

prop_updateLookup :: MkH -> Int -> Int -> Property
prop_updateLookup hh k v =
  k `notin` hh $
  (do ht <- h hh
      update ht k v) ==~
  (do ht <- h hh
      fmap isJust (lookup ht k))

prop_simulation :: HashFun -> List Action -> Property
prop_simulation hf (L acts) =
  (null acts `trivial`) $
  runA hf acts ~~ simA acts

{-

For "fromList" and "toList" properties we're a bit sloppy: we perform
multiple insertions for a key (potentially) but give nor promises
about which one we will retrieve with lookup, or what order they'll be
returned by toList (or if they'll all be returned at all).  Thus we
insert all occurrences of a key with the same value, and do all
checking via lookups.

-}

prop_fromList :: HashFun -> List Int -> List Int -> Property
prop_fromList hf (L l) (L ks) =
  null l `trivial`
  let assocs = map (\t -> (t,t)) l in
  ( do ht <- fromList (unHF hf) assocs
       lookups ht ks) ==. (map (`Prelude.lookup` assocs) ks)

prop_fromListInsert :: HashFun -> List (Int,Int) -> Int -> Int -> List Int -> Property
prop_fromListInsert hf (L l) k v (L ks) =
  null l `trivial`
  (( do ht <- fromList (unHF hf) l
        insert ht k v
        lookups ht ks) ==~
   ( do ht <- fromList (unHF hf) (l++[(k,v)])
        lookups ht ks))

prop_toList :: HashFun -> List Int -> List Int -> Property
prop_toList hf (L l) (L ks) =
  null l `trivial`
  let assocs = map (\t -> (t,t)) l in
  ( do ht <- fromList (unHF hf) assocs
       lookups ht ks) ==~
  ( do ht <- fromList (unHF hf) assocs
       fmap (\as -> map (`Prelude.lookup` as) ks) $ toList ht )

te :: (Testable a) => String -> a -> IO ()
-- te name prop = putStrLn name >> verboseCheck prop
te name prop = do
  putStr name
  quickCheckWith stdArgs { maxSuccess = 500, maxSize = 10000 } prop

main :: IO ()
main = do
  [r] <- getArgs
  setStdGen (mkStdGen (read r :: Int))
  sequence_ $
    [ te "emptyLookup:" prop_emptyLookup,
      te "emptyToList:" prop_emptyToList,
      te "emptyFromList:" prop_emptyFromList,
      te "insert:" prop_insert,
      te "insertu:" prop_insertu,
      te "delete:" prop_delete,
      te "deleteu:" prop_deleteu,
      te "update:" prop_update,
      te "updatec:" prop_updatec,
      te "updateLookup:" prop_updateLookup,
      te "fromList:" prop_fromList,
      te "fromListInsert:" prop_fromListInsert,
      te "toList:" prop_toList,
      te "simulation:" prop_simulation
    ]
  putStrLn "OK"