commit ghc-unordered-containers for openSUSE:Factory
Hello community, here is the log from the commit of package ghc-unordered-containers for openSUSE:Factory checked in at 2016-06-14 23:08:36 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ghc-unordered-containers (Old) and /work/SRC/openSUSE:Factory/.ghc-unordered-containers.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "ghc-unordered-containers" Changes: -------- --- /work/SRC/openSUSE:Factory/ghc-unordered-containers/ghc-unordered-containers.changes 2015-05-21 08:13:21.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.ghc-unordered-containers.new/ghc-unordered-containers.changes 2016-06-14 23:08:37.000000000 +0200 @@ -1,0 +2,24 @@ +Fri Jun 10 07:00:11 UTC 2016 - mimi.vx@gmail.com + +- update to 0.2.7.1 +- remove useless _service +* Fix linker error related to popcnt. + +------------------------------------------------------------------- +Sat Feb 20 08:32:45 UTC 2016 - mimi.vx@gmail.com + +- update to 0.2.7.0 +* support criterion-1.1 +* Add unionWithKey for hash maps. + +------------------------------------------------------------------- +Tue Feb 16 20:05:18 UTC 2016 - mimi.vx@gmail.com + +- update to 0.2.6.0 +* Mark several modules as Trustworthy. +* Add Hashable instances for HashMap and HashSet. +* Add mapMaybe, mapMaybeWithKey, update, alter, and intersectionWithKey. +* Add roles. +* Add Hashable and Semigroup instances. + +------------------------------------------------------------------- Old: ---- _service unordered-containers-0.2.5.1.tar.gz New: ---- unordered-containers-0.2.7.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ghc-unordered-containers.spec ++++++ --- /var/tmp/diff_new_pack.mhRwJk/_old 2016-06-14 23:08:37.000000000 +0200 +++ /var/tmp/diff_new_pack.mhRwJk/_new 2016-06-14 23:08:37.000000000 +0200 @@ -19,7 +19,7 @@ %global pkg_name unordered-containers Name: ghc-unordered-containers -Version: 0.2.5.1 +Version: 0.2.7.1 Release: 0 Summary: Efficient hashing-based container types License: BSD-3-Clause ++++++ unordered-containers-0.2.5.1.tar.gz -> unordered-containers-0.2.7.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unordered-containers-0.2.5.1/CHANGES.md new/unordered-containers-0.2.7.1/CHANGES.md --- old/unordered-containers-0.2.5.1/CHANGES.md 1970-01-01 01:00:00.000000000 +0100 +++ new/unordered-containers-0.2.7.1/CHANGES.md 2016-06-09 02:00:11.000000000 +0200 @@ -0,0 +1,30 @@ +## 0.2.7.1 + + * Fix linker error related to popcnt. + + * Haddock improvements. + + * Fix benchmark compilation when downloaded from Hackage. + +## 0.2.7.0 + + * Support criterion 1.1 + + * Add unionWithKey for hash maps. + +## 0.2.6.0 + + * Mark several modules as Trustworthy. + + * Add Hashable instances for HashMap and HashSet. + + * Add mapMaybe, mapMaybeWithKey, update, alter, and + intersectionWithKey. + + * Add roles. + + * Add Hashable and Semigroup instances. + +## 0.2.5.1 (2014-10-11) + + * Support base-4.8 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unordered-containers-0.2.5.1/Data/HashMap/Array.hs new/unordered-containers-0.2.7.1/Data/HashMap/Array.hs --- old/unordered-containers-0.2.5.1/Data/HashMap/Array.hs 2014-10-11 15:04:46.000000000 +0200 +++ new/unordered-containers-0.2.7.1/Data/HashMap/Array.hs 2016-06-09 02:00:11.000000000 +0200 @@ -53,7 +53,6 @@ import Control.Applicative (Applicative) #endif import Control.DeepSeq -import Control.Monad.ST hiding (runST) -- GHC 7.7 exports toList/fromList from GHC.Exts -- In order to avoid warnings on previous GHC versions, we provide -- an explicit import list instead of only hiding the offending symbols diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unordered-containers-0.2.5.1/Data/HashMap/Base.hs new/unordered-containers-0.2.7.1/Data/HashMap/Base.hs --- old/unordered-containers-0.2.5.1/Data/HashMap/Base.hs 2014-10-11 15:04:46.000000000 +0200 +++ new/unordered-containers-0.2.7.1/Data/HashMap/Base.hs 2016-06-09 02:00:11.000000000 +0200 @@ -1,6 +1,8 @@ {-# LANGUAGE BangPatterns, CPP, DeriveDataTypeable, MagicHash #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE PatternGuards #-} #if __GLASGOW_HASKELL__ >= 708 +{-# LANGUAGE RoleAnnotations #-} {-# LANGUAGE TypeFamilies #-} #endif {-# OPTIONS_GHC -fno-full-laziness -funbox-strict-fields #-} @@ -26,11 +28,14 @@ , unsafeInsert , delete , adjust + , update + , alter -- * Combine -- ** Union , union , unionWith + , unionWithKey , unions -- * Transformations @@ -42,6 +47,7 @@ , difference , intersection , intersectionWith + , intersectionWithKey -- * Folds , foldl' @@ -50,6 +56,8 @@ , foldrWithKey -- * Filter + , mapMaybe + , mapMaybeWithKey , filter , filterWithKey @@ -79,16 +87,19 @@ , update16M , update16With' , updateOrConcatWith + , updateOrConcatWithKey + , filterMapAux ) where -#if __GLASGOW_HASKELL__ >= 709 -import Data.Functor ((<$>)) -#else +#if __GLASGOW_HASKELL__ < 710 import Control.Applicative ((<$>), Applicative(pure)) import Data.Monoid (Monoid(mempty, mappend)) import Data.Traversable (Traversable(..)) import Data.Word (Word) #endif +#if __GLASGOW_HASKELL__ >= 711 +import Data.Semigroup (Semigroup((<>))) +#endif import Control.DeepSeq (NFData(rnf)) import Control.Monad.ST (ST) import Data.Bits ((.&.), (.|.), complement) @@ -140,6 +151,10 @@ | Collision !Hash !(A.Array (Leaf k v)) deriving (Typeable) +#if __GLASGOW_HASKELL__ >= 708 +type role HashMap nominal representational +#endif + instance (NFData k, NFData v) => NFData (HashMap k v) where rnf Empty = () rnf (BitmapIndexed _ ary) = rnf ary @@ -153,10 +168,20 @@ instance Foldable.Foldable (HashMap k) where foldr f = foldrWithKey (const f) +#if __GLASGOW_HASKELL__ >= 711 +instance (Eq k, Hashable k) => Semigroup (HashMap k v) where + (<>) = union + {-# INLINE (<>) #-} +#endif + instance (Eq k, Hashable k) => Monoid (HashMap k v) where mempty = empty {-# INLINE mempty #-} +#if __GLASGOW_HASKELL__ >= 711 + mappend = (<>) +#else mappend = union +#endif {-# INLINE mappend #-} instance (Data k, Data v, Eq k, Hashable k) => Data (HashMap k v) where @@ -213,11 +238,39 @@ go [] [] = True go _ _ = False - toList' (BitmapIndexed _ ary) a = A.foldr toList' a ary - toList' (Full ary) a = A.foldr toList' a ary - toList' l@(Leaf _ _) a = l : a - toList' c@(Collision _ _) a = c : a - toList' Empty a = a +instance (Hashable k, Hashable v) => Hashable (HashMap k v) where + hashWithSalt salt hm = go salt (toList' hm []) + where + go :: Int -> [HashMap k v] -> Int + go s [] = s + go s (Leaf _ l : tl) + = s `hashLeafWithSalt` l `go` tl + -- For collisions we hashmix hash value + -- and then array of values' hashes sorted + go s (Collision h a : tl) + = (s `H.hashWithSalt` h) `hashCollisionWithSalt` a `go` tl + go s (_ : tl) = s `go` tl + + hashLeafWithSalt :: Int -> Leaf k v -> Int + hashLeafWithSalt s (L k v) = s `H.hashWithSalt` k `H.hashWithSalt` v + + hashCollisionWithSalt :: Int -> A.Array (Leaf k v) -> Int + hashCollisionWithSalt s + = L.foldl' H.hashWithSalt s . arrayHashesSorted + + arrayHashesSorted :: A.Array (Leaf k v) -> [Int] + arrayHashesSorted = L.sort . L.map leafValueHash . A.toList + + leafValueHash :: Leaf k v -> Int + leafValueHash (L _ v) = H.hash v + + -- Helper to get 'Leaf's and 'Collision's as a list. +toList' :: HashMap k v -> [HashMap k v] -> [HashMap k v] +toList' (BitmapIndexed _ ary) a = A.foldr toList' a ary +toList' (Full ary) a = A.foldr toList' a ary +toList' l@(Leaf _ _) a = l : a +toList' c@(Collision _ _) a = c : a +toList' Empty a = a -- Helper function to detect 'Leaf's and 'Collision's. isLeafOrCollision :: HashMap k v -> Bool @@ -460,8 +513,7 @@ unsafeInsertWith f k0 v0 m0 = runST (go h0 k0 v0 0 m0) where h0 = hash k0 - go :: (Eq k, Hashable k) => Hash -> k -> v -> Shift -> HashMap k v - -> ST s (HashMap k v) + go :: Hash -> k -> v -> Shift -> HashMap k v -> ST s (HashMap k v) go !h !k x !_ Empty = return $! Leaf h (L k x) go h k x s (Leaf hy l@(L ky y)) | hy == h = if ky == k @@ -574,6 +626,24 @@ | otherwise = t {-# INLINABLE adjust #-} +-- | /O(log n)/ The expression (@'update' f k map@) updates the value @x@ at @k@, +-- (if it is in the map). If (f k x) is @'Nothing', the element is deleted. +-- If it is (@'Just' y), the key k is bound to the new value y. +update :: (Eq k, Hashable k) => (a -> Maybe a) -> k -> HashMap k a -> HashMap k a +update f = alter (>>= f) +{-# INLINABLE update #-} + + +-- | /O(log n)/ The expression (@'alter' f k map@) alters the value @x@ at @k@, or +-- absence thereof. @alter@ can be used to insert, delete, or update a value in a +-- map. In short : @'lookup' k ('alter' f k m) = f ('lookup' k m)@. +alter :: (Eq k, Hashable k) => (Maybe v -> Maybe v) -> k -> HashMap k v -> HashMap k v +alter f k m = + case f (lookup k m) of + Nothing -> delete k m + Just v -> insert k v m +{-# INLINABLE alter #-} + ------------------------------------------------------------------------ -- * Combine @@ -588,7 +658,15 @@ -- result. unionWith :: (Eq k, Hashable k) => (v -> v -> v) -> HashMap k v -> HashMap k v -> HashMap k v -unionWith f = go 0 +unionWith f = unionWithKey (const f) +{-# INLINE unionWith #-} + +-- | /O(n+m)/ The union of two maps. If a key occurs in both maps, +-- the provided function (first argument) will be used to compute the +-- result. +unionWithKey :: (Eq k, Hashable k) => (k -> v -> v -> v) -> HashMap k v -> HashMap k v + -> HashMap k v +unionWithKey f = go 0 where -- empty vs. anything go !_ t1 Empty = t1 @@ -596,17 +674,17 @@ -- leaf vs. leaf go s t1@(Leaf h1 l1@(L k1 v1)) t2@(Leaf h2 l2@(L k2 v2)) | h1 == h2 = if k1 == k2 - then Leaf h1 (L k1 (f v1 v2)) + then Leaf h1 (L k1 (f k1 v1 v2)) else collision h1 l1 l2 | otherwise = goDifferentHash s h1 h2 t1 t2 go s t1@(Leaf h1 (L k1 v1)) t2@(Collision h2 ls2) - | h1 == h2 = Collision h1 (updateOrSnocWith f k1 v1 ls2) + | h1 == h2 = Collision h1 (updateOrSnocWithKey f k1 v1 ls2) | otherwise = goDifferentHash s h1 h2 t1 t2 go s t1@(Collision h1 ls1) t2@(Leaf h2 (L k2 v2)) - | h1 == h2 = Collision h1 (updateOrSnocWith (flip f) k2 v2 ls1) + | h1 == h2 = Collision h1 (updateOrSnocWithKey (flip . f) k2 v2 ls1) | otherwise = goDifferentHash s h1 h2 t1 t2 go s t1@(Collision h1 ls1) t2@(Collision h2 ls2) - | h1 == h2 = Collision h1 (updateOrConcatWith f ls1 ls2) + | h1 == h2 = Collision h1 (updateOrConcatWithKey f ls1 ls2) | otherwise = goDifferentHash s h1 h2 t1 t2 -- branch vs. branch go s (BitmapIndexed b1 ary1) (BitmapIndexed b2 ary2) = @@ -668,7 +746,7 @@ where m1 = mask h1 s m2 = mask h2 s -{-# INLINE unionWith #-} +{-# INLINE unionWithKey #-} -- | Strict in the result of @f@. unionArrayBy :: (a -> a -> a) -> Bitmap -> Bitmap -> A.Array a -> A.Array a @@ -777,6 +855,18 @@ _ -> m {-# INLINABLE intersectionWith #-} +-- | /O(n+m)/ Intersection of two maps. If a key occurs in both maps +-- the provided function is used to combine the values from the two +-- maps. +intersectionWithKey :: (Eq k, Hashable k) => (k -> v1 -> v2 -> v3) + -> HashMap k v1 -> HashMap k v2 -> HashMap k v3 +intersectionWithKey f a b = foldlWithKey' go empty a + where + go m k v = case lookup k b of + Just w -> insert k (f k v w) m + _ -> m +{-# INLINABLE intersectionWithKey #-} + ------------------------------------------------------------------------ -- * Folds @@ -835,14 +925,47 @@ A.unsafeFreeze mary2 {-# INLINE trim #-} +-- | /O(n)/ Transform this map by applying a function to every value +-- and retaining only some of them. +mapMaybeWithKey :: (k -> v1 -> Maybe v2) -> HashMap k v1 -> HashMap k v2 +mapMaybeWithKey f = filterMapAux onLeaf onColl + where onLeaf (Leaf h (L k v)) | Just v' <- f k v = Just (Leaf h (L k v')) + onLeaf _ = Nothing + + onColl (L k v) | Just v' <- f k v = Just (L k v') + | otherwise = Nothing +{-# INLINE mapMaybeWithKey #-} + +-- | /O(n)/ Transform this map by applying a function to every value +-- and retaining only some of them. +mapMaybe :: (v1 -> Maybe v2) -> HashMap k v1 -> HashMap k v2 +mapMaybe f = mapMaybeWithKey (const f) +{-# INLINE mapMaybe #-} + -- | /O(n)/ Filter this map by retaining only elements satisfying a -- predicate. filterWithKey :: forall k v. (k -> v -> Bool) -> HashMap k v -> HashMap k v -filterWithKey pred = go +filterWithKey pred = filterMapAux onLeaf onColl + where onLeaf t@(Leaf _ (L k v)) | pred k v = Just t + onLeaf _ = Nothing + + onColl el@(L k v) | pred k v = Just el + onColl _ = Nothing +{-# INLINE filterWithKey #-} + + +-- | Common implementation for 'filterWithKey' and 'mapMaybeWithKey', +-- allowing the former to former to reuse terms. +filterMapAux :: forall k v1 v2 + . (HashMap k v1 -> Maybe (HashMap k v2)) + -> (Leaf k v1 -> Maybe (Leaf k v2)) + -> HashMap k v1 + -> HashMap k v2 +filterMapAux onLeaf onColl = go where go Empty = Empty - go t@(Leaf _ (L k v)) - | pred k v = t + go t@Leaf{} + | Just t' <- onLeaf t = t' | otherwise = Empty go (BitmapIndexed b ary) = filterA ary b go (Full ary) = filterA ary fullNodeMask @@ -854,9 +977,9 @@ mary <- A.new_ n step ary0 mary b0 0 0 1 n where - step :: A.Array (HashMap k v) -> A.MArray s (HashMap k v) + step :: A.Array (HashMap k v1) -> A.MArray s (HashMap k v2) -> Bitmap -> Int -> Int -> Bitmap -> Int - -> ST s (HashMap k v) + -> ST s (HashMap k v2) step !ary !mary !b i !j !bi n | i >= n = case j of 0 -> return Empty @@ -883,9 +1006,9 @@ mary <- A.new_ n step ary0 mary 0 0 n where - step :: A.Array (Leaf k v) -> A.MArray s (Leaf k v) + step :: A.Array (Leaf k v1) -> A.MArray s (Leaf k v2) -> Int -> Int -> Int - -> ST s (HashMap k v) + -> ST s (HashMap k v2) step !ary !mary i !j n | i >= n = case j of 0 -> return Empty @@ -895,10 +1018,10 @@ return $! Collision h ary2 | otherwise -> do ary2 <- trim mary j return $! Collision h ary2 - | pred k v = A.write mary j el >> step ary mary (i+1) (j+1) n + | Just el <- onColl (A.index ary i) + = A.write mary j el >> step ary mary (i+1) (j+1) n | otherwise = step ary mary (i+1) j n - where el@(L k v) = A.index ary i -{-# INLINE filterWithKey #-} +{-# INLINE filterMapAux #-} -- | /O(n)/ Filter this map by retaining only elements which values -- satisfy a predicate. @@ -928,7 +1051,7 @@ -- ** Lists -- | /O(n)/ Return a list of this map's elements. The list is --- produced lazily. +-- produced lazily. The order of its elements is unspecified. toList :: HashMap k v -> [(k, v)] toList t = build (\ c z -> foldrWithKey (curry c) z t) {-# INLINE toList #-} @@ -986,7 +1109,12 @@ updateOrSnocWith :: Eq k => (v -> v -> v) -> k -> v -> A.Array (Leaf k v) -> A.Array (Leaf k v) -updateOrSnocWith f k0 v0 ary0 = go k0 v0 ary0 0 (A.length ary0) +updateOrSnocWith f = updateOrSnocWithKey (const f) +{-# INLINABLE updateOrSnocWith #-} + +updateOrSnocWithKey :: Eq k => (k -> v -> v -> v) -> k -> v -> A.Array (Leaf k v) + -> A.Array (Leaf k v) +updateOrSnocWithKey f k0 v0 ary0 = go k0 v0 ary0 0 (A.length ary0) where go !k v !ary !i !n | i >= n = A.run $ do @@ -996,12 +1124,16 @@ A.write mary n (L k v) return mary | otherwise = case A.index ary i of - (L kx y) | k == kx -> A.update ary i (L k (f v y)) + (L kx y) | k == kx -> A.update ary i (L k (f k v y)) | otherwise -> go k v ary (i+1) n -{-# INLINABLE updateOrSnocWith #-} +{-# INLINABLE updateOrSnocWithKey #-} updateOrConcatWith :: Eq k => (v -> v -> v) -> A.Array (Leaf k v) -> A.Array (Leaf k v) -> A.Array (Leaf k v) -updateOrConcatWith f ary1 ary2 = A.run $ do +updateOrConcatWith f = updateOrConcatWithKey (const f) +{-# INLINABLE updateOrConcatWith #-} + +updateOrConcatWithKey :: Eq k => (k -> v -> v -> v) -> A.Array (Leaf k v) -> A.Array (Leaf k v) -> A.Array (Leaf k v) +updateOrConcatWithKey f ary1 ary2 = A.run $ do -- first: look up the position of each element of ary2 in ary1 let indices = A.map (\(L k _) -> indexOf k ary1) ary2 -- that tells us how large the overlap is: @@ -1019,14 +1151,14 @@ Just i1 -> do -- key occurs in both arrays, store combination in position i1 L k v1 <- A.indexM ary1 i1 L _ v2 <- A.indexM ary2 i2 - A.write mary i1 (L k (f v1 v2)) + A.write mary i1 (L k (f k v1 v2)) go iEnd (i2+1) Nothing -> do -- key is only in ary2, append to end A.write mary iEnd =<< A.indexM ary2 i2 go (iEnd+1) (i2+1) go n1 0 return mary -{-# INLINABLE updateOrConcatWith #-} +{-# INLINABLE updateOrConcatWithKey #-} ------------------------------------------------------------------------ -- Manually unrolled loops diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unordered-containers-0.2.5.1/Data/HashMap/Lazy.hs new/unordered-containers-0.2.7.1/Data/HashMap/Lazy.hs --- old/unordered-containers-0.2.5.1/Data/HashMap/Lazy.hs 2014-10-11 15:04:46.000000000 +0200 +++ new/unordered-containers-0.2.7.1/Data/HashMap/Lazy.hs 2016-06-09 02:00:11.000000000 +0200 @@ -47,11 +47,14 @@ , insertWith , delete , adjust + , update + , alter -- * Combine -- ** Union , union , unionWith + , unionWithKey , unions -- * Transformations @@ -63,6 +66,7 @@ , difference , intersection , intersectionWith + , intersectionWithKey -- * Folds , foldl' @@ -73,6 +77,8 @@ -- * Filter , HM.filter , filterWithKey + , mapMaybe + , mapMaybeWithKey -- * Conversions , keys diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unordered-containers-0.2.5.1/Data/HashMap/Strict.hs new/unordered-containers-0.2.7.1/Data/HashMap/Strict.hs --- old/unordered-containers-0.2.5.1/Data/HashMap/Strict.hs 2014-10-11 15:04:46.000000000 +0200 +++ new/unordered-containers-0.2.7.1/Data/HashMap/Strict.hs 2016-06-09 02:00:11.000000000 +0200 @@ -1,4 +1,4 @@ -{-# LANGUAGE BangPatterns, CPP #-} +{-# LANGUAGE BangPatterns, CPP, PatternGuards #-} #if __GLASGOW_HASKELL__ >= 702 {-# LANGUAGE Trustworthy #-} @@ -47,11 +47,14 @@ , insertWith , delete , adjust + , update + , alter -- * Combine -- ** Union , union , unionWith + , unionWithKey , unions -- * Transformations @@ -63,6 +66,7 @@ , difference , intersection , intersectionWith + , intersectionWithKey -- * Folds , foldl' @@ -73,6 +77,8 @@ -- * Filter , HM.filter , filterWithKey + , mapMaybe + , mapMaybeWithKey -- * Conversions , keys @@ -92,8 +98,9 @@ import qualified Data.HashMap.Array as A import qualified Data.HashMap.Base as HM import Data.HashMap.Base hiding ( - adjust, fromList, fromListWith, insert, insertWith, intersectionWith, - map, mapWithKey, singleton, unionWith) + alter, adjust, fromList, fromListWith, insert, insertWith, intersectionWith, + intersectionWithKey, map, mapWithKey, mapMaybe, mapMaybeWithKey, singleton, + update, unionWith, unionWithKey) import Data.HashMap.Unsafe (runST) -- $strictness @@ -227,6 +234,23 @@ | otherwise = t {-# INLINABLE adjust #-} +-- | /O(log n)/ The expression (@'update' f k map@) updates the value @x@ at @k@, +-- (if it is in the map). If (f k x) is @'Nothing', the element is deleted. +-- If it is (@'Just' y), the key k is bound to the new value y. +update :: (Eq k, Hashable k) => (a -> Maybe a) -> k -> HashMap k a -> HashMap k a +update f = alter (>>= f) +{-# INLINABLE update #-} + +-- | /O(log n)/ The expression (@'alter' f k map@) alters the value @x@ at @k@, or +-- absence thereof. @alter@ can be used to insert, delete, or update a value in a +-- map. In short : @'lookup' k ('alter' f k m) = f ('lookup' k m)@. +alter :: (Eq k, Hashable k) => (Maybe v -> Maybe v) -> k -> HashMap k v -> HashMap k v +alter f k m = + case f (HM.lookup k m) of + Nothing -> delete k m + Just v -> insert k v m +{-# INLINABLE alter #-} + ------------------------------------------------------------------------ -- * Combine @@ -234,7 +258,14 @@ -- the provided function (first argument) will be used to compute the result. unionWith :: (Eq k, Hashable k) => (v -> v -> v) -> HashMap k v -> HashMap k v -> HashMap k v -unionWith f = go 0 +unionWith f = unionWithKey (const f) +{-# INLINE unionWith #-} + +-- | /O(n+m)/ The union of two maps. If a key occurs in both maps, +-- the provided function (first argument) will be used to compute the result. +unionWithKey :: (Eq k, Hashable k) => (k -> v -> v -> v) -> HashMap k v -> HashMap k v + -> HashMap k v +unionWithKey f = go 0 where -- empty vs. anything go !_ t1 Empty = t1 @@ -242,17 +273,17 @@ -- leaf vs. leaf go s t1@(Leaf h1 l1@(L k1 v1)) t2@(Leaf h2 l2@(L k2 v2)) | h1 == h2 = if k1 == k2 - then leaf h1 k1 (f v1 v2) + then leaf h1 k1 (f k1 v1 v2) else collision h1 l1 l2 | otherwise = goDifferentHash s h1 h2 t1 t2 go s t1@(Leaf h1 (L k1 v1)) t2@(Collision h2 ls2) - | h1 == h2 = Collision h1 (updateOrSnocWith f k1 v1 ls2) + | h1 == h2 = Collision h1 (updateOrSnocWithKey f k1 v1 ls2) | otherwise = goDifferentHash s h1 h2 t1 t2 go s t1@(Collision h1 ls1) t2@(Leaf h2 (L k2 v2)) - | h1 == h2 = Collision h1 (updateOrSnocWith (flip f) k2 v2 ls1) + | h1 == h2 = Collision h1 (updateOrSnocWithKey (flip . f) k2 v2 ls1) | otherwise = goDifferentHash s h1 h2 t1 t2 go s t1@(Collision h1 ls1) t2@(Collision h2 ls2) - | h1 == h2 = Collision h1 (updateOrConcatWith f ls1 ls2) + | h1 == h2 = Collision h1 (updateOrConcatWithKey f ls1 ls2) | otherwise = goDifferentHash s h1 h2 t1 t2 -- branch vs. branch go s (BitmapIndexed b1 ary1) (BitmapIndexed b2 ary2) = @@ -314,7 +345,7 @@ where m1 = mask h1 s m2 = mask h2 s -{-# INLINE unionWith #-} +{-# INLINE unionWithKey #-} ------------------------------------------------------------------------ -- * Transformations @@ -336,6 +367,28 @@ map f = mapWithKey (const f) {-# INLINE map #-} + +------------------------------------------------------------------------ +-- * Filter + +-- | /O(n)/ Transform this map by applying a function to every value +-- and retaining only some of them. +mapMaybeWithKey :: (k -> v1 -> Maybe v2) -> HashMap k v1 -> HashMap k v2 +mapMaybeWithKey f = filterMapAux onLeaf onColl + where onLeaf (Leaf h (L k v)) | Just v' <- f k v = Just (leaf h k v') + onLeaf _ = Nothing + + onColl (L k v) | Just v' <- f k v = Just (L k v') + | otherwise = Nothing +{-# INLINE mapMaybeWithKey #-} + +-- | /O(n)/ Transform this map by applying a function to every value +-- and retaining only some of them. +mapMaybe :: (v1 -> Maybe v2) -> HashMap k v1 -> HashMap k v2 +mapMaybe f = mapMaybeWithKey (const f) +{-# INLINE mapMaybe #-} + + -- TODO: Should we add a strict traverseWithKey? ------------------------------------------------------------------------ @@ -353,6 +406,18 @@ _ -> m {-# INLINABLE intersectionWith #-} +-- | /O(n+m)/ Intersection of two maps. If a key occurs in both maps +-- the provided function is used to combine the values from the two +-- maps. +intersectionWithKey :: (Eq k, Hashable k) => (k -> v1 -> v2 -> v3) + -> HashMap k v1 -> HashMap k v2 -> HashMap k v3 +intersectionWithKey f a b = foldlWithKey' go empty a + where + go m k v = case HM.lookup k b of + Just w -> insert k (f k v w) m + _ -> m +{-# INLINABLE intersectionWithKey #-} + ------------------------------------------------------------------------ -- ** Lists @@ -364,7 +429,18 @@ {-# INLINABLE fromList #-} -- | /O(n*log n)/ Construct a map from a list of elements. Uses --- the provided function to merge duplicate entries. +-- the provided function f to merge duplicate entries (f newVal oldVal). +-- +-- For example: +-- +-- > fromListWith (+) [ (x, 1) | x <- xs ] +-- +-- will create a map with number of occurrences of each element in xs. +-- +-- > fromListWith (++) [ (k, [v]) | (k, v) <- xs ] +-- +-- will group all values by their keys in a list 'xs :: [(k, v)]' and +-- return a 'HashMap k [v]'. fromListWith :: (Eq k, Hashable k) => (v -> v -> v) -> [(k, v)] -> HashMap k v fromListWith f = L.foldl' (\ m (k, v) -> unsafeInsertWith f k v m) empty {-# INLINE fromListWith #-} @@ -389,7 +465,17 @@ -- array. updateOrSnocWith :: Eq k => (v -> v -> v) -> k -> v -> A.Array (Leaf k v) -> A.Array (Leaf k v) -updateOrSnocWith f k0 v0 ary0 = go k0 v0 ary0 0 (A.length ary0) +updateOrSnocWith f = updateOrSnocWithKey (const f) +{-# INLINABLE updateOrSnocWith #-} + +-- | Append the given key and value to the array. If the key is +-- already present, instead update the value of the key by applying +-- the given function to the new and old value (in that order). The +-- value is always evaluated to WHNF before being inserted into the +-- array. +updateOrSnocWithKey :: Eq k => (k -> v -> v -> v) -> k -> v -> A.Array (Leaf k v) + -> A.Array (Leaf k v) +updateOrSnocWithKey f k0 v0 ary0 = go k0 v0 ary0 0 (A.length ary0) where go !k v !ary !i !n | i >= n = A.run $ do @@ -400,9 +486,9 @@ A.write mary n l return mary | otherwise = case A.index ary i of - (L kx y) | k == kx -> let !v' = f v y in A.update ary i (L k v') + (L kx y) | k == kx -> let !v' = f k v y in A.update ary i (L k v') | otherwise -> go k v ary (i+1) n -{-# INLINABLE updateOrSnocWith #-} +{-# INLINABLE updateOrSnocWithKey #-} ------------------------------------------------------------------------ -- Smart constructors diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unordered-containers-0.2.5.1/Data/HashMap/Unsafe.hs new/unordered-containers-0.2.7.1/Data/HashMap/Unsafe.hs --- old/unordered-containers-0.2.5.1/Data/HashMap/Unsafe.hs 2014-10-11 15:04:46.000000000 +0200 +++ new/unordered-containers-0.2.7.1/Data/HashMap/Unsafe.hs 2016-06-09 02:00:11.000000000 +0200 @@ -13,16 +13,16 @@ ) where import GHC.Base (realWorld#) -import GHC.ST hiding (runST, runSTRep) +import qualified GHC.ST as ST -- | Return the value computed by a state transformer computation. -- The @forall@ ensures that the internal state used by the 'ST' -- computation is inaccessible to the rest of the program. -runST :: (forall s. ST s a) -> a -runST st = runSTRep (case st of { ST st_rep -> st_rep }) +runST :: (forall s. ST.ST s a) -> a +runST st = runSTRep (case st of { ST.ST st_rep -> st_rep }) {-# INLINE runST #-} -runSTRep :: (forall s. STRep s a) -> a +runSTRep :: (forall s. ST.STRep s a) -> a runSTRep st_rep = case st_rep realWorld# of (# _, r #) -> r {-# INLINE [0] runSTRep #-} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unordered-containers-0.2.5.1/Data/HashSet.hs new/unordered-containers-0.2.7.1/Data/HashSet.hs --- old/unordered-containers-0.2.5.1/Data/HashSet.hs 2014-10-11 15:04:46.000000000 +0200 +++ new/unordered-containers-0.2.7.1/Data/HashSet.hs 2016-06-09 02:00:11.000000000 +0200 @@ -1,7 +1,11 @@ {-# LANGUAGE CPP, DeriveDataTypeable #-} #if __GLASGOW_HASKELL__ >= 708 +{-# LANGUAGE RoleAnnotations #-} {-# LANGUAGE TypeFamilies #-} #endif +#if __GLASGOW_HASKELL__ >= 702 +{-# LANGUAGE Trustworthy #-} +#endif ------------------------------------------------------------------------ -- | @@ -57,16 +61,24 @@ -- * Filter , filter + -- * Conversions + -- ** Lists , toList , fromList + + -- * HashMaps + , toMap + , fromMap ) where import Control.DeepSeq (NFData(..)) import Data.Data hiding (Typeable) import Data.HashMap.Base (HashMap, foldrWithKey) -import Data.Hashable (Hashable) -#if __GLASGOW_HASKELL__ < 709 +import Data.Hashable (Hashable(hashWithSalt)) +#if __GLASGOW_HASKELL__ >= 711 +import Data.Semigroup (Semigroup(..), Monoid(..)) +#elif __GLASGOW_HASKELL__ < 709 import Data.Monoid (Monoid(..)) #endif import GHC.Exts (build) @@ -86,6 +98,10 @@ asMap :: HashMap a () } deriving (Typeable) +#if __GLASGOW_HASKELL__ >= 708 +type role HashSet nominal +#endif + instance (NFData a) => NFData (HashSet a) where rnf = rnf . asMap {-# INLINE rnf #-} @@ -100,10 +116,20 @@ foldr = Data.HashSet.foldr {-# INLINE foldr #-} +#if __GLASGOW_HASKELL__ >= 711 +instance (Hashable a, Eq a) => Semigroup (HashSet a) where + (<>) = union + {-# INLINE (<>) #-} +#endif + instance (Hashable a, Eq a) => Monoid (HashSet a) where mempty = empty {-# INLINE mempty #-} +#if __GLASGOW_HASKELL__ >= 711 + mappend = (<>) +#else mappend = union +#endif {-# INLINE mappend #-} instance (Eq a, Hashable a, Read a) => Read (HashSet a) where @@ -127,6 +153,9 @@ dataTypeOf _ = hashSetDataType dataCast1 f = gcast1 f +instance (Hashable a) => Hashable (HashSet a) where + hashWithSalt salt = hashWithSalt salt . asMap + fromListConstr :: Constr fromListConstr = mkConstr hashSetDataType "fromList" [] Prefix @@ -142,6 +171,14 @@ singleton a = HashSet (H.singleton a ()) {-# INLINABLE singleton #-} +-- | /O(1)/ Convert to the equivalent 'HashMap'. +toMap :: HashSet a -> HashMap a () +toMap = asMap + +-- | /O(1)/ Convert from the equivalent 'HashMap'. +fromMap :: HashMap a () -> HashSet a +fromMap = HashSet + -- | /O(n+m)/ Construct a set containing all elements from both sets. -- -- To obtain good performance, the smaller set must be presented as diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unordered-containers-0.2.5.1/benchmarks/Benchmarks.hs new/unordered-containers-0.2.7.1/benchmarks/Benchmarks.hs --- old/unordered-containers-0.2.5.1/benchmarks/Benchmarks.hs 2014-10-11 15:04:46.000000000 +0200 +++ new/unordered-containers-0.2.7.1/benchmarks/Benchmarks.hs 2016-06-09 02:00:11.000000000 +0200 @@ -1,12 +1,10 @@ -{-# LANGUAGE CPP, GADTs, PackageImports #-} +{-# LANGUAGE CPP, DeriveGeneric, GADTs, PackageImports, RecordWildCards #-} module Main where import Control.DeepSeq -import Control.Exception (evaluate) -import Control.Monad.Trans (liftIO) -import Criterion.Config -import Criterion.Main +import Control.DeepSeq.Generics (genericRnf) +import Criterion.Main (bench, bgroup, defaultMain, env, nf, whnf) import Data.Bits ((.&.)) import Data.Hashable (Hashable) import qualified Data.ByteString as BS @@ -16,6 +14,7 @@ import qualified Data.Map as M import Data.List (foldl') import Data.Maybe (fromMaybe) +import GHC.Generics (Generic) import Prelude hiding (lookup) import qualified Util.ByteString as UBS @@ -32,20 +31,82 @@ instance NFData B where rnf (B b) = rnf b +-- TODO: This a stopgap measure to keep the benchmark work with +-- Criterion 1.0. +data Env = Env { + n :: !Int, + + elems :: ![(String, Int)], + keys :: ![String], + elemsBS :: ![(BS.ByteString, Int)], + keysBS :: ![BS.ByteString], + elemsI :: ![(Int, Int)], + keysI :: ![Int], + elemsI2 :: ![(Int, Int)], -- for union + + keys' :: ![String], + keysBS' :: ![BS.ByteString], + keysI' :: ![Int], + + keysDup :: ![String], + keysDupBS :: ![BS.ByteString], + keysDupI :: ![Int], + elemsDup :: ![(String, Int)], + elemsDupBS :: ![(BS.ByteString, Int)], + elemsDupI :: ![(Int, Int)], + + hm :: !(HM.HashMap String Int), + hmbs :: !(HM.HashMap BS.ByteString Int), + hmi :: !(HM.HashMap Int Int), + hmi2 :: !(HM.HashMap Int Int), + m :: !(M.Map String Int), + mbs :: !(M.Map BS.ByteString Int), + im :: !(IM.IntMap Int), + ihm :: !(IHM.Map String Int), + ihmbs :: !(IHM.Map BS.ByteString Int) + } deriving Generic + +instance NFData Env where rnf = genericRnf + +setupEnv :: IO Env +setupEnv = do + let n = 2^(12 :: Int) + + elems = zip keys [1..n] + keys = US.rnd 8 n + elemsBS = zip keysBS [1..n] + keysBS = UBS.rnd 8 n + elemsI = zip keysI [1..n] + keysI = UI.rnd (n+n) n + elemsI2 = zip [n `div` 2..n + (n `div` 2)] [1..n] -- for union + + keys' = US.rnd' 8 n + keysBS' = UBS.rnd' 8 n + keysI' = UI.rnd' (n+n) n + + keysDup = US.rnd 2 n + keysDupBS = UBS.rnd 2 n + keysDupI = UI.rnd (n`div`4) n + elemsDup = zip keysDup [1..n] + elemsDupBS = zip keysDupBS [1..n] + elemsDupI = zip keysDupI [1..n] + + hm = HM.fromList elems + hmbs = HM.fromList elemsBS + hmi = HM.fromList elemsI + hmi2 = HM.fromList elemsI2 + m = M.fromList elems + mbs = M.fromList elemsBS + im = IM.fromList elemsI + ihm = IHM.fromList elems + ihmbs = IHM.fromList elemsBS + return Env{..} + main :: IO () main = do - let hm = HM.fromList elems :: HM.HashMap String Int - hmbs = HM.fromList elemsBS :: HM.HashMap BS.ByteString Int - hmi = HM.fromList elemsI :: HM.HashMap Int Int - hmi2 = HM.fromList elemsI2 :: HM.HashMap Int Int - m = M.fromList elems :: M.Map String Int - mbs = M.fromList elemsBS :: M.Map BS.ByteString Int - im = IM.fromList elemsI :: IM.IntMap Int - ihm = IHM.fromList elems :: IHM.Map String Int - ihmbs = IHM.fromList elemsBS :: IHM.Map BS.ByteString Int - defaultMainWith defaultConfig - (liftIO . evaluate $ rnf [B m, B mbs, B hm, B hmbs, B hmi, B im]) + defaultMain [ + env setupEnv $ \ ~(Env{..}) -> -- * Comparison to other data structures -- ** Map bgroup "Map" @@ -84,7 +145,8 @@ ] -- ** Map from the hashmap package - , bgroup "hashmap/Map" + , env setupEnv $ \ ~(Env{..}) -> + bgroup "hashmap/Map" [ bgroup "lookup" [ bench "String" $ whnf (lookupIHM keys) ihm , bench "ByteString" $ whnf (lookupIHM keysBS) ihmbs @@ -120,7 +182,8 @@ ] -- ** IntMap - , bgroup "IntMap" + , env setupEnv $ \ ~(Env{..}) -> + bgroup "IntMap" [ bench "lookup" $ whnf (lookupIM keysI) im , bench "lookup-miss" $ whnf (lookupIM keysI') im , bench "insert" $ whnf (insertIM elemsI) IM.empty @@ -131,7 +194,8 @@ , bench "fromList" $ whnf IM.fromList elemsI ] - , bgroup "HashMap" + , env setupEnv $ \ ~(Env{..}) -> + bgroup "HashMap" [ -- * Basic interface bgroup "lookup" [ bench "String" $ whnf (lookup keys) hm @@ -217,28 +281,6 @@ ] ] ] - where - n :: Int - n = 2^(12 :: Int) - - elems = zip keys [1..n] - keys = US.rnd 8 n - elemsBS = zip keysBS [1..n] - keysBS = UBS.rnd 8 n - elemsI = zip keysI [1..n] - keysI = UI.rnd (n+n) n - elemsI2 = zip [n `div` 2..n + (n `div` 2)] [1..n] -- for union - - keys' = US.rnd' 8 n - keysBS' = UBS.rnd' 8 n - keysI' = UI.rnd' (n+n) n - - keysDup = US.rnd 2 n - keysDupBS = UBS.rnd 2 n - keysDupI = UI.rnd (n`div`4) n - elemsDup = zip keysDup [1..n] - elemsDupBS = zip keysDupBS [1..n] - elemsDupI = zip keysDupI [1..n] ------------------------------------------------------------------------ -- * HashMap diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unordered-containers-0.2.5.1/benchmarks/Util/ByteString.hs new/unordered-containers-0.2.7.1/benchmarks/Util/ByteString.hs --- old/unordered-containers-0.2.5.1/benchmarks/Util/ByteString.hs 1970-01-01 01:00:00.000000000 +0100 +++ new/unordered-containers-0.2.7.1/benchmarks/Util/ByteString.hs 2016-06-09 02:00:11.000000000 +0200 @@ -0,0 +1,29 @@ +-- | Benchmarking utilities. For example, functions for generating +-- random 'ByteString's. +module Util.ByteString where + +import qualified Data.ByteString as S +import qualified Data.ByteString.Char8 as C + +import Util.String as String + +-- | Generate a number of fixed length 'ByteString's where the content +-- of the strings are letters in ascending order. +asc :: Int -- ^ Length of each string + -> Int -- ^ Number of strings + -> [S.ByteString] +asc strlen num = map C.pack $ String.asc strlen num + +-- | Generate a number of fixed length 'ByteString's where the content +-- of the strings are letters in random order. +rnd :: Int -- ^ Length of each string + -> Int -- ^ Number of strings + -> [S.ByteString] +rnd strlen num = map C.pack $ String.rnd strlen num + +-- | Generate a number of fixed length 'ByteString's where the content +-- of the strings are letters in random order, different from @rnd@. +rnd' :: Int -- ^ Length of each string + -> Int -- ^ Number of strings + -> [S.ByteString] +rnd' strlen num = map C.pack $ String.rnd' strlen num diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unordered-containers-0.2.5.1/benchmarks/Util/Int.hs new/unordered-containers-0.2.7.1/benchmarks/Util/Int.hs --- old/unordered-containers-0.2.5.1/benchmarks/Util/Int.hs 1970-01-01 01:00:00.000000000 +0100 +++ new/unordered-containers-0.2.7.1/benchmarks/Util/Int.hs 2016-06-09 02:00:11.000000000 +0200 @@ -0,0 +1,19 @@ +-- | Benchmarking utilities. For example, functions for generating +-- random integers. +module Util.Int where + +import System.Random (mkStdGen, randomRs) + +-- | Generate a number of uniform random integers in the interval +-- @[0..upper]@. +rnd :: Int -- ^ Upper bound (inclusive) + -> Int -- ^ Number of integers + -> [Int] +rnd upper num = take num $ randomRs (0, upper) $ mkStdGen 1234 + +-- | Generate a number of uniform random integers in the interval +-- @[0..upper]@ different from @rnd@. +rnd' :: Int -- ^ Upper bound (inclusive) + -> Int -- ^ Number of integers + -> [Int] +rnd' upper num = take num $ randomRs (0, upper) $ mkStdGen 5678 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unordered-containers-0.2.5.1/benchmarks/Util/String.hs new/unordered-containers-0.2.7.1/benchmarks/Util/String.hs --- old/unordered-containers-0.2.5.1/benchmarks/Util/String.hs 1970-01-01 01:00:00.000000000 +0100 +++ new/unordered-containers-0.2.7.1/benchmarks/Util/String.hs 2016-06-09 02:00:11.000000000 +0200 @@ -0,0 +1,34 @@ +-- | Benchmarking utilities. For example, functions for generating +-- random strings. +module Util.String where + +import System.Random (mkStdGen, randomRs) + +-- | Generate a number of fixed length strings where the content of +-- the strings are letters in ascending order. +asc :: Int -- ^ Length of each string + -> Int -- ^ Number of strings + -> [String] +asc strlen num = take num $ iterate (snd . inc) $ replicate strlen 'a' + where inc [] = (True, []) + inc (c:cs) = case inc cs of (True, cs') | c == 'z' -> (True, 'a' : cs') + | otherwise -> (False, succ c : cs') + (False, cs') -> (False, c : cs') + +-- | Generate a number of fixed length strings where the content of +-- the strings are letters in random order. +rnd :: Int -- ^ Length of each string + -> Int -- ^ Number of strings + -> [String] +rnd strlen num = take num $ split $ randomRs ('a', 'z') $ mkStdGen 1234 + where + split cs = case splitAt strlen cs of (str, cs') -> str : split cs' + +-- | Generate a number of fixed length strings where the content of +-- the strings are letters in random order, different from rnd +rnd' :: Int -- ^ Length of each string + -> Int -- ^ Number of strings + -> [String] +rnd' strlen num = take num $ split $ randomRs ('a', 'z') $ mkStdGen 5678 + where + split cs = case splitAt strlen cs of (str, cs') -> str : split cs' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unordered-containers-0.2.5.1/cbits/popc.c new/unordered-containers-0.2.7.1/cbits/popc.c --- old/unordered-containers-0.2.5.1/cbits/popc.c 2014-10-11 15:04:46.000000000 +0200 +++ new/unordered-containers-0.2.7.1/cbits/popc.c 2016-06-09 02:00:11.000000000 +0200 @@ -261,7 +261,7 @@ }; /* Table-driven popcount, with 8-bit tables */ /* 6 ops plus 4 casts and 4 lookups, 0 long immediates, 4 stages */ -inline uint32_t +uint32_t popcount(uint32_t x) { return popcount_table_8[(uint8_t)x] + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unordered-containers-0.2.5.1/tests/HashMapProperties.hs new/unordered-containers-0.2.7.1/tests/HashMapProperties.hs --- old/unordered-containers-0.2.5.1/tests/HashMapProperties.hs 2014-10-11 15:04:46.000000000 +0200 +++ new/unordered-containers-0.2.7.1/tests/HashMapProperties.hs 2016-06-09 02:00:11.000000000 +0200 @@ -5,17 +5,19 @@ module Main (main) where +import Control.Monad ( guard ) import qualified Data.Foldable as Foldable import Data.Function (on) import Data.Hashable (Hashable(hashWithSalt)) import qualified Data.List as L +import Data.Ord (comparing) #if defined(STRICT) import qualified Data.HashMap.Strict as HM #else import qualified Data.HashMap.Lazy as HM #endif import qualified Data.Map as M -import Test.QuickCheck (Arbitrary, Property, (==>)) +import Test.QuickCheck (Arbitrary, Property, (==>), (===)) import Test.Framework (Test, defaultMain, testGroup) import Test.Framework.Providers.QuickCheck2 (testProperty) @@ -48,6 +50,19 @@ pFoldable = (L.sort . Foldable.foldr (:) []) `eq` (L.sort . Foldable.foldr (:) []) +pHashable :: [(Key, Int)] -> [Int] -> Int -> Property +pHashable xs is salt = + x == y ==> hashWithSalt salt x === hashWithSalt salt y + where + ys = shuffle is xs + x = HM.fromList xs + y = HM.fromList ys + -- Shuffle the list using indexes in the second + shuffle :: [Int] -> [a] -> [a] + shuffle idxs = L.map snd + . L.sortBy (comparing fst) + . L.zip (idxs ++ [L.maximum (0:is) + 1 ..]) + ------------------------------------------------------------------------ -- ** Basic interface @@ -98,6 +113,21 @@ pAdjust :: Key -> [(Key, Int)] -> Bool pAdjust k = M.adjust succ k `eq_` HM.adjust succ k +pUpdateAdjust :: Key -> [(Key, Int)] -> Bool +pUpdateAdjust k = M.update (Just . succ) k `eq_` HM.update (Just . succ) k + +pUpdateDelete :: Key -> [(Key, Int)] -> Bool +pUpdateDelete k = M.update (const Nothing) k `eq_` HM.update (const Nothing) k + +pAlterAdjust :: Key -> [(Key, Int)] -> Bool +pAlterAdjust k = M.alter (fmap succ) k `eq_` HM.alter (fmap succ) k + +pAlterInsert :: Key -> [(Key, Int)] -> Bool +pAlterInsert k = M.alter (const $ Just 3) k `eq_` HM.alter (const $ Just 3) k + +pAlterDelete :: Key -> [(Key, Int)] -> Bool +pAlterDelete k = M.alter (const Nothing) k `eq_` HM.alter (const Nothing) k + ------------------------------------------------------------------------ -- ** Combine @@ -108,6 +138,13 @@ pUnionWith xs ys = M.unionWith (-) (M.fromList xs) `eq_` HM.unionWith (-) (HM.fromList xs) $ ys +pUnionWithKey :: [(Key, Int)] -> [(Key, Int)] -> Bool +pUnionWithKey xs ys = M.unionWithKey go (M.fromList xs) `eq_` + HM.unionWithKey go (HM.fromList xs) $ ys + where + go :: Key -> Int -> Int -> Int + go (K k) i1 i2 = k - i1 + i2 + pUnions :: [[(Key, Int)]] -> Bool pUnions xss = M.toAscList (M.unions (map M.fromList xss)) == toAscList (HM.unions (map HM.fromList xss)) @@ -133,6 +170,13 @@ pIntersectionWith xs ys = M.intersectionWith (-) (M.fromList xs) `eq_` HM.intersectionWith (-) (HM.fromList xs) $ ys +pIntersectionWithKey :: [(Key, Int)] -> [(Key, Int)] -> Bool +pIntersectionWithKey xs ys = M.intersectionWithKey go (M.fromList xs) `eq_` + HM.intersectionWithKey go (HM.fromList xs) $ ys + where + go :: Key -> Int -> Int -> Int + go (K k) i1 i2 = k - i1 - i2 + ------------------------------------------------------------------------ -- ** Folds @@ -158,6 +202,14 @@ ------------------------------------------------------------------------ -- ** Filter +pMapMaybeWithKey :: [(Key, Int)] -> Bool +pMapMaybeWithKey = M.mapMaybeWithKey f `eq_` HM.mapMaybeWithKey f + where f k v = guard (odd (unK k + v)) >> Just (v + 1) + +pMapMaybe :: [(Key, Int)] -> Bool +pMapMaybe = M.mapMaybe f `eq_` HM.mapMaybe f + where f v = guard (odd v) >> Just (v + 1) + pFilter :: [(Key, Int)] -> Bool pFilter = M.filter odd `eq_` HM.filter odd @@ -198,6 +250,7 @@ , testProperty "Read/Show" pReadShow , testProperty "Functor" pFunctor , testProperty "Foldable" pFoldable + , testProperty "Hashable" pHashable ] -- Basic interface , testGroup "basic interface" @@ -209,10 +262,16 @@ , testProperty "deleteCollision" pDeleteCollision , testProperty "insertWith" pInsertWith , testProperty "adjust" pAdjust + , testProperty "updateAdjust" pUpdateAdjust + , testProperty "updateDelete" pUpdateDelete + , testProperty "alterAdjust" pAlterAdjust + , testProperty "alterInsert" pAlterInsert + , testProperty "alterDelete" pAlterDelete ] -- Combine , testProperty "union" pUnion , testProperty "unionWith" pUnionWith + , testProperty "unionWithKey" pUnionWithKey , testProperty "unions" pUnions -- Transformations , testProperty "map" pMap @@ -226,11 +285,14 @@ [ testProperty "difference" pDifference , testProperty "intersection" pIntersection , testProperty "intersectionWith" pIntersectionWith + , testProperty "intersectionWithKey" pIntersectionWithKey ] -- Filter , testGroup "filter" [ testProperty "filter" pFilter , testProperty "filterWithKey" pFilterWithKey + , testProperty "mapMaybe" pMapMaybe + , testProperty "mapMaybeWithKey" pMapMaybeWithKey ] -- Conversions , testGroup "conversions" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unordered-containers-0.2.5.1/tests/HashSetProperties.hs new/unordered-containers-0.2.7.1/tests/HashSetProperties.hs --- old/unordered-containers-0.2.5.1/tests/HashSetProperties.hs 2014-10-11 15:04:46.000000000 +0200 +++ new/unordered-containers-0.2.7.1/tests/HashSetProperties.hs 2016-06-09 02:00:11.000000000 +0200 @@ -10,7 +10,8 @@ import qualified Data.List as L import qualified Data.HashSet as S import qualified Data.Set as Set -import Test.QuickCheck (Arbitrary) +import Data.Ord (comparing) +import Test.QuickCheck (Arbitrary, Property, (==>), (===)) import Test.Framework (Test, defaultMain, testGroup) import Test.Framework.Providers.QuickCheck2 (testProperty) @@ -40,6 +41,25 @@ pFoldable = (L.sort . Foldable.foldr (:) []) `eq` (L.sort . Foldable.foldr (:) []) +pPermutationEq :: [Key] -> [Int] -> Bool +pPermutationEq xs is = S.fromList xs == S.fromList ys + where + ys = shuffle is xs + shuffle idxs = L.map snd + . L.sortBy (comparing fst) + . L.zip (idxs ++ [L.maximum (0:is) + 1 ..]) + +pHashable :: [Key] -> [Int] -> Int -> Property +pHashable xs is salt = + x == y ==> hashWithSalt salt x === hashWithSalt salt y + where + ys = shuffle is xs + x = S.fromList xs + y = S.fromList ys + shuffle idxs = L.map snd + . L.sortBy (comparing fst) + . L.zip (idxs ++ [L.maximum (0:is) + 1 ..]) + ------------------------------------------------------------------------ -- ** Basic interface @@ -113,9 +133,11 @@ -- Instances testGroup "instances" [ testProperty "==" pEq + , testProperty "Permutation ==" pPermutationEq , testProperty "/=" pNeq , testProperty "Read/Show" pReadShow , testProperty "Foldable" pFoldable + , testProperty "Hashable" pHashable ] -- Basic interface , testGroup "basic interface" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unordered-containers-0.2.5.1/unordered-containers.cabal new/unordered-containers-0.2.7.1/unordered-containers.cabal --- old/unordered-containers-0.2.5.1/unordered-containers.cabal 2014-10-11 15:04:46.000000000 +0200 +++ new/unordered-containers-0.2.7.1/unordered-containers.cabal 2016-06-09 02:00:11.000000000 +0200 @@ -1,5 +1,5 @@ name: unordered-containers -version: 0.2.5.1 +version: 0.2.7.1 synopsis: Efficient hashing-based container types description: Efficient hashing-based container types. The containers have been @@ -19,6 +19,8 @@ category: Data build-type: Simple cabal-version: >=1.8 +extra-source-files: CHANGES.md +tested-with: GHC==8.0.1, GHC==7.10.3, GHC==7.8.4, GHC==7.6.3, GHC==7.4.2 flag debug description: Enable debug support @@ -39,7 +41,7 @@ build-depends: base >= 4 && < 5, deepseq >= 1.1, - hashable >= 1.0.1.1 + hashable >= 1.0.1.1 && < 1.3 if impl(ghc < 7.4) c-sources: cbits/popc.c @@ -147,12 +149,18 @@ main-is: Benchmarks.hs type: exitcode-stdio-1.0 + other-modules: + Util.ByteString + Util.Int + Util.String + build-depends: base, bytestring, containers, - criterion, + criterion >= 1.0 && < 1.2, deepseq >= 1.1, + deepseq-generics, hashable >= 1.0.1.1, hashmap, mtl,
participants (1)
-
root@hilbert.suse.de