{-# LANGUAGE BangPatterns #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeOperators #-} module HashMap where import Gen import Data.Array.Accelerate as A import Data.Array.Accelerate.Data.Bits as A import Data.Array.Accelerate.Data.Hashable as A import Data.Array.Accelerate.Data.HashMap as HashMap import Data.Typeable import Data.Function import Data.List as P import Prelude as P import Data.Map.Strict ( Map ) import qualified Data.Map.Strict as Map import Hedgehog import qualified Hedgehog.Gen as G import qualified Hedgehog.Range as R import Test.Tasty import Test.Tasty.Hedgehog test_hashmap :: RunN -> TestTree test_hashmap runN = testGroup "hashmap" [ testElt int -- , testElt i8 -- , testElt i16 -- , testElt i32 -- , testElt i64 , testElt word -- , testElt w8 -- , testElt w16 -- , testElt w32 -- , testElt w64 , testElt f32 , testElt f64 ] where testElt :: forall e. (Show e, Typeable e, P.Ord e, A.Hashable e, A.Eq e) => Gen e -> TestTree testElt e = testGroup (show (typeOf (undefined :: e))) [ testProperty "lookup" $ test_lookup runN e int , testProperty "lookup-with-collision" $ test_lookup runN (collides e) int , testProperty "insert" $ test_insert runN e int , testProperty "delete" $ test_delete runN e int ] _MAX_SIZE :: Int _MAX_SIZE = 1024 test_lookup :: (Show k, Show v, P.Ord k, P.Eq v, A.Hashable k, A.Eq k, A.Eq v) => RunN -> Gen k -> Gen v -> Property test_lookup runN k v = property $ do m <- forAll $ G.map (R.linear 0 _MAX_SIZE) ((,) <$> k <*> v) let n = Map.size m t1 <- if Map.null m then return [] else forAll $ G.list (R.linear 0 n) (G.element (Map.keys m)) t2 <- forAll $ G.list (R.linear 0 n) k t <- forAll $ G.shuffle (t1 P.++ t2) let !go = runN $ \hm -> A.map (`HashMap.lookup` HashMap.fromVector hm) -- A.toList (go (fromMap m) (A.fromList (Z :. P.length t) t)) === P.map (`Map.lookup` m) t test_insert :: (Show k, Show v, P.Ord k, P.Eq v, A.Hashable k, A.Eq k, A.Eq v) => RunN -> Gen k -> Gen v -> Property test_insert runN = test_insertWith runN const const test_insertWith :: (Show k, Show v, P.Ord k, P.Eq v, A.Hashable k, A.Eq k, A.Eq v) => RunN -> (Exp v -> Exp v -> Exp v) -> (v -> v -> v) -> Gen k -> Gen v -> Property test_insertWith runN f g = test_insertWithKey runN (const f) (const g) test_insertWithKey :: (Show k, Show v, P.Ord k, P.Eq v, A.Hashable k, A.Eq k, A.Eq v) => RunN -> (Exp k -> Exp v -> Exp v -> Exp v) -> (k -> v -> v -> v) -> Gen k -> Gen v -> Property test_insertWithKey runN f g k v = property $ do m <- forAll $ G.map (R.linear 0 _MAX_SIZE) ((,) <$> k <*> v) n <- forAll $ G.map (R.linear 0 _MAX_SIZE) ((,) <$> k <*> v) let !go = runN (\hm kv -> HashMap.assocs $ HashMap.insertWithKey f kv (HashMap.fromVector hm)) -- P.sortBy (P.compare `on` P.fst) (A.toList (go (fromMap m) (fromMap n))) === Map.toAscList (P.foldr (\(k',v') m' -> Map.insertWithKey g k' v' m') m (Map.toList n)) test_delete :: (Show k, Show v, P.Ord k, P.Eq v, A.Hashable k, A.Eq k, A.Eq v) => RunN -> Gen k -> Gen v -> Property test_delete runN k v = property $ do m <- forAll $ G.map (R.linear 0 _MAX_SIZE) ((,) <$> k <*> v) n <- forAll $ G.list (R.linear 0 _MAX_SIZE) k let !go = runN (\hm ks -> HashMap.assocs $ HashMap.delete ks (HashMap.fromVector hm)) -- P.sortBy (P.compare `on` P.fst) (A.toList (go (fromMap m) (fromList (Z :. P.length n) n))) === Map.toAscList (P.foldr Map.delete m n) fromMap :: (Elt k, Elt v) => Map k v -> Vector (k,v) fromMap m = let n = Map.size m in fromList (Z :. n) (Map.toList m) collides :: Gen a -> Gen (Collides a) collides g = Collides <$> g newtype Collides a = Collides a deriving (Show, P.Eq, P.Ord, Generic, Elt) pattern Collides_ :: Elt a => Exp a -> Exp (Collides a) pattern Collides_ x = Pattern x {-# COMPLETE Collides_ #-} instance Hashable a => Hashable (Collides a) where hash (Collides_ x) = 0xffff .&. hash x hashWithSalt = defaultHashWithSalt instance A.Eq a => A.Eq (Collides a) where Collides_ x == Collides_ y = x A.== y Collides_ x /= Collides_ y = x A./= y