BiMap.hs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. {-# LANGUAGE TemplateHaskell, UndecidableInstances #-}
  2. module Internal.Utils.BiMap ( tests ) where
  3. import Prelude hiding (null, lookup)
  4. import qualified Prelude
  5. import Data.Foldable (foldrM)
  6. import qualified Data.List as List
  7. import qualified Data.Map as Map
  8. import Data.Maybe
  9. import Data.Ord
  10. import Agda.Utils.BiMap
  11. import Agda.Utils.List
  12. import Agda.Utils.Null
  13. import Internal.Helpers
  14. ------------------------------------------------------------------------
  15. -- * Types used for the tests below
  16. ------------------------------------------------------------------------
  17. -- | A key type.
  18. type K = Integer
  19. -- | A value type.
  20. data V = V Integer Integer
  21. deriving (Eq, Ord, Show)
  22. instance HasTag V where
  23. type Tag V = Integer
  24. tag (V n _) = if n < 0 then Nothing else Just n
  25. -- | An instance of the map type.
  26. type BM = BiMap K V
  27. ------------------------------------------------------------------------
  28. -- * Generators
  29. ------------------------------------------------------------------------
  30. -- | If several keys map to the same value (with a defined tag), then
  31. -- all but the first one are removed. The order of the remaining list
  32. -- elements is unspecified.
  33. dropDuplicates ::
  34. (Ord v, HasTag v) =>
  35. [(k, v)] -> [(k, v)]
  36. dropDuplicates =
  37. concat .
  38. map (\(v, ks) -> map (,v) $ Map.elems ks) .
  39. Map.toList .
  40. Map.mapWithKey
  41. (\v ks -> if isNothing (tag v)
  42. then ks
  43. else uncurry Map.singleton (Map.findMin ks)) .
  44. Map.fromListWith Map.union .
  45. map (\(n, (k, v)) -> (v, Map.singleton n k)) .
  46. zip [1..]
  47. -- | The generator 'validFromListList' returns lists for which the
  48. -- precondition 'fromListPrecondition' is satisfied, assuming that
  49. -- 'tag' is injective for values generated by the 'Arbitrary'
  50. -- instance.
  51. validFromListList ::
  52. (Arbitrary k, Arbitrary v, Ord v, HasTag v) => Gen [(k, v)]
  53. validFromListList = dropDuplicates <$> arbitrary
  54. prop_validFromListList :: Property
  55. prop_validFromListList =
  56. forAll validFromListList $ \(kvs :: [(K, V)]) ->
  57. fromListPrecondition kvs
  58. instance
  59. (Ord k, HasTag v, Ord v, Ord (Tag v), Arbitrary k, Arbitrary v) =>
  60. Arbitrary (BiMap k v) where
  61. arbitrary = fromList <$> validFromListList
  62. shrink (BiMap t b) =
  63. [ BiMap
  64. (Map.delete k t)
  65. (maybe b (flip Map.delete b) (tag =<< Map.lookup k t))
  66. | k <- Map.keys t
  67. ]
  68. -- | Generates values with undefined tags.
  69. valueWithUndefinedTag :: Gen V
  70. valueWithUndefinedTag = do
  71. n <- chooseInteger (-3, -1)
  72. i <- arbitrary
  73. return (V n i)
  74. prop_valueWithUndefinedTag :: Property
  75. prop_valueWithUndefinedTag =
  76. forAll valueWithUndefinedTag $ \v ->
  77. isNothing (tag v)
  78. instance Arbitrary V where
  79. arbitrary = oneof [g, valueWithUndefinedTag]
  80. where
  81. g = do
  82. i <- chooseInteger (-3, 3)
  83. return (V i (i + 7))
  84. instance CoArbitrary V where
  85. coarbitrary (V i j) = coarbitrary (i, j)
  86. -- | A key that is likely to be in the map (unless the map is empty).
  87. keyLikelyInMap :: BM -> Gen K
  88. keyLikelyInMap m
  89. | null ks = arbitrary
  90. | otherwise = frequency [(9, elements ks), (1, arbitrary)]
  91. where
  92. ks = keys m
  93. -- | A value that is perhaps in the map (unless the map is empty).
  94. valueMaybeInMap :: BM -> Gen V
  95. valueMaybeInMap m
  96. | null vs = arbitrary
  97. | otherwise = frequency [(1, elements vs), (1, arbitrary)]
  98. where
  99. vs = elems m
  100. -- | A pair that satisfies 'insertPrecondition' for the given map.
  101. validInsertPair :: BM -> Gen (K, V)
  102. validInsertPair m = do
  103. k <- arbitrary
  104. v <- V <$> arbitrary <*> arbitrary
  105. oneof $
  106. (if insertPrecondition k v m then [return (k, v)] else []) ++
  107. [(k,) <$> valueWithUndefinedTag]
  108. prop_validInsertPair :: BM -> Property
  109. prop_validInsertPair m =
  110. forAll (validInsertPair m) $ \(k, v) ->
  111. insertPrecondition k v m
  112. -- | A function that satisfies 'mapWithKeyPrecondition' for the given
  113. -- map.
  114. validMapWithKeyFunction :: BM -> Gen (K -> V -> V)
  115. validMapWithKeyFunction m = do
  116. f <- foldrM
  117. (\kv f -> do
  118. let add v' = return (insert kv v' f)
  119. v' <- arbitrary
  120. case flip invLookup f =<< tag v' of
  121. Just _ ->
  122. -- The tag of v' is defined, and v' has been seen
  123. -- before. Use a value with an undefined tag.
  124. add =<< valueWithUndefinedTag
  125. Nothing ->
  126. -- Use the mapping kv ↦ v'.
  127. add v')
  128. empty
  129. (toList m)
  130. fallback <- valueWithUndefinedTag
  131. return (\k v -> fromMaybe fallback (lookup (k, v) f))
  132. prop_validMapWithKeyFunction :: BM -> Property
  133. prop_validMapWithKeyFunction m =
  134. forAll (validMapWithKeyFunction m) $ \f ->
  135. mapWithKeyPrecondition f m
  136. -- | A function that satisfies 'mapWithKeyFixedTagsPrecondition' for
  137. -- the given map.
  138. validMapWithKeyFixedTagsFunction :: BM -> Gen (K -> V -> V)
  139. validMapWithKeyFixedTagsFunction _ = do
  140. f <- arbitrary
  141. return (\k v@(V i j) -> V i (f k i j))
  142. prop_validMapWithKeyFixedTagsFunction :: BM -> Property
  143. prop_validMapWithKeyFixedTagsFunction m =
  144. forAll (validMapWithKeyFixedTagsFunction m) $ \f ->
  145. mapWithKeyFixedTagsPrecondition f m
  146. -- | The generator @'validUnionMap' m₁@ returns maps @m₂@ for which
  147. -- the precondition of @'union' m₁ m₂@ is satisfied.
  148. validUnionMap :: BM -> Gen BM
  149. validUnionMap m1 =
  150. fromList . dropDuplicates . filter ok . map tweak <$> arbitrary
  151. where
  152. -- Change the key if tag v2 is defined and m1 maps a different key
  153. -- to v2.
  154. tweak (k2, v2) =
  155. case flip invLookup m1 =<< tag v2 of
  156. Just k1 | k1 /= k2 -> (k1, v2)
  157. _ -> (k2, v2)
  158. -- Remove the pair if tag v2 is defined and m1 maps k2 to a value v1
  159. -- distinct from v2.
  160. ok (k2, v2) =
  161. case (tag v2, lookup k2 m1) of
  162. (Just _, Just v1) | v1 /= v2 -> False
  163. _ -> True
  164. prop_validUnionMap :: BM -> Property
  165. prop_validUnionMap m1 =
  166. forAll (validUnionMap m1) $ \m2 ->
  167. let overlappingKeys = or
  168. [ k1 == k2
  169. | (k1, v1) <- toList m1
  170. , (k2, v2) <- toList m2
  171. ]
  172. overlappingValues = or
  173. [ v1 == v2
  174. | (k1, v1) <- toList m1
  175. , (k2, v2) <- toList m2
  176. ]
  177. sameKey = or
  178. [ k1 == k2
  179. | (k1, v1) <- toList m1
  180. , (k2, v2) <- toList m2
  181. , v1 /= v2
  182. ]
  183. sameValue = or
  184. [ v1 == v2
  185. | (k1, v1) <- toList m1
  186. , (k2, v2) <- toList m2
  187. , k1 /= k2
  188. ]
  189. in
  190. classify overlappingKeys "overlapping keys" $
  191. classify overlappingValues "overlapping values" $
  192. classify sameKey "same key, different values" $
  193. classify sameValue "same value, different keys" $
  194. biMapInvariant m2
  195. &&
  196. unionPrecondition m1 m2
  197. ------------------------------------------------------------------------
  198. -- * Properties
  199. ------------------------------------------------------------------------
  200. -- | \"Normalises\" lists.
  201. normalise :: [(K, V)] -> [(K, V)]
  202. normalise = nubOn fst . List.sortBy (comparing fst)
  203. prop_arbitrary :: BM -> Bool
  204. prop_arbitrary = biMapInvariant
  205. prop_shrink :: BM -> Bool
  206. prop_shrink = all biMapInvariant . take 5 . shrink
  207. prop_empty :: Bool
  208. prop_empty =
  209. biMapInvariant (empty :: BM)
  210. &&
  211. toList (empty :: BM) == []
  212. prop_null :: BM -> Bool
  213. prop_null m =
  214. null m == (toList m == [])
  215. prop_lookup :: K -> BM -> Bool
  216. prop_lookup k m =
  217. lookup k m == Prelude.lookup k (toList m)
  218. prop_invLookup :: Integer -> BM -> Bool
  219. prop_invLookup k' m =
  220. maybeToList (invLookup k' m) ==
  221. [ k
  222. | (k, v) <- toList m
  223. , tag v == Just k'
  224. ]
  225. prop_singleton :: K -> V -> Bool
  226. prop_singleton k v =
  227. biMapInvariant (singleton k v)
  228. &&
  229. toList (singleton k v) == [(k, v)]
  230. prop_insert :: BM -> Property
  231. prop_insert m =
  232. forAll (validInsertPair m) $ \(k, v) ->
  233. biMapInvariant (insert k v m)
  234. &&
  235. toList (insert k v m) == normalise ((k, v) : toList m)
  236. prop_alter :: (Maybe V -> Maybe V) -> BM -> Property
  237. prop_alter f m =
  238. forAll (keyLikelyInMap m) $ \k ->
  239. alterPrecondition f k m ==>
  240. biMapInvariant (alter f k m)
  241. &&
  242. toList (alter f k m) ==
  243. normalise
  244. ((case f (lookup k m) of
  245. Nothing -> []
  246. Just v -> [(k, v)]) ++
  247. [ (k', v)
  248. | (k', v) <- toList m
  249. , k' /= k
  250. ])
  251. prop_update :: (V -> Maybe V) -> BM -> Property
  252. prop_update f m =
  253. forAll (keyLikelyInMap m) $ \k ->
  254. updatePrecondition f k m ==>
  255. biMapInvariant (update f k m)
  256. &&
  257. toList (update f k m) ==
  258. normalise
  259. ((case f =<< lookup k m of
  260. Nothing -> []
  261. Just v -> [(k, v)]) ++
  262. [ (k', v)
  263. | (k', v) <- toList m
  264. , k' /= k
  265. ])
  266. prop_adjust :: (V -> V) -> BM -> Property
  267. prop_adjust f m =
  268. forAll (keyLikelyInMap m) $ \k ->
  269. adjustPrecondition f k m ==>
  270. biMapInvariant (adjust f k m)
  271. &&
  272. toList (adjust f k m) ==
  273. normalise
  274. ((case f <$> lookup k m of
  275. Nothing -> []
  276. Just v -> [(k, v)]) ++
  277. [ (k', v)
  278. | (k', v) <- toList m
  279. , k' /= k
  280. ])
  281. prop_insertLookupWithKey ::
  282. (K -> V -> V -> V) -> BM -> Property
  283. prop_insertLookupWithKey f m =
  284. forAll (keyLikelyInMap m) $ \k ->
  285. forAll (valueMaybeInMap m) $ \v ->
  286. insertLookupWithKeyPrecondition f k v m ==>
  287. let (v', m') = insertLookupWithKey f k v m in
  288. biMapInvariant m'
  289. &&
  290. v' == lookup k m
  291. &&
  292. toList m' ==
  293. normalise
  294. ((k, maybe v (f k v) (lookup k m)) :
  295. [ (k', v)
  296. | (k', v) <- toList m
  297. , k' /= k
  298. ])
  299. prop_mapWithKey :: BM -> Property
  300. prop_mapWithKey m =
  301. forAll (validMapWithKeyFunction m) $ \f ->
  302. biMapInvariant (mapWithKey f m)
  303. &&
  304. toList (mapWithKey f m) ==
  305. map (\(k, v) -> (k, f k v)) (toList m)
  306. prop_mapWithKeyFixedTags :: BM -> Property
  307. prop_mapWithKeyFixedTags m =
  308. forAll (validMapWithKeyFixedTagsFunction m) $ \f ->
  309. biMapInvariant (mapWithKeyFixedTags f m)
  310. &&
  311. toList (mapWithKeyFixedTags f m) ==
  312. map (\(k, v) -> (k, f k v)) (toList m)
  313. prop_union :: BM -> Property
  314. prop_union m1 =
  315. forAll (validUnionMap m1) $ \m2 ->
  316. biMapInvariant (union m1 m2)
  317. &&
  318. toList (union m1 m2) == normalise (toList m1 ++ toList m2)
  319. prop_fromList :: Property
  320. prop_fromList =
  321. forAll validFromListList $ \kvs ->
  322. biMapInvariant (fromList kvs)
  323. &&
  324. toList (fromList kvs) == normalise kvs
  325. prop_keys :: BM -> Bool
  326. prop_keys m =
  327. keys m == map fst (toList m)
  328. prop_elems :: BM -> Bool
  329. prop_elems m =
  330. elems m == map snd (toList m)
  331. prop_fromDistinctAscendingLists_toDistinctAscendingLists :: BM -> Bool
  332. prop_fromDistinctAscendingLists_toDistinctAscendingLists m =
  333. let p = toDistinctAscendingLists m in
  334. fromDistinctAscendingListsPrecondition p &&
  335. fromDistinctAscendingLists p == m
  336. prop_equal :: BM -> BM -> Bool
  337. prop_equal m1 m2 =
  338. (m1 == m2) == (toList m1 == toList m2)
  339. prop_compare :: BM -> BM -> Bool
  340. prop_compare m1 m2 =
  341. compare m1 m2 == compare (toList m1) (toList m2)
  342. ------------------------------------------------------------------------
  343. -- * All tests
  344. ------------------------------------------------------------------------
  345. -- Template Haskell hack to make the following $allProperties work
  346. -- under ghc-7.8.
  347. return [] -- KEEP!
  348. -- | All tests as collected by 'allProperties'.
  349. --
  350. -- Using 'allProperties' is convenient and superior to the manual
  351. -- enumeration of tests, since the name of the property is added
  352. -- automatically.
  353. tests :: TestTree
  354. tests = testProperties "Internal.Utils.BiMap" $allProperties