unordered-containers 0.2.1.0 → 0.2.2.0
raw patch · 8 files changed
+236/−146 lines, 8 filesdep ~basedep ~containersdep ~deepseq
Dependency ranges changed: base, containers, deepseq, hashable, test-framework, test-framework-quickcheck2
Files
- Data/HashMap/Array.hs +5/−2
- Data/HashMap/Base.hs +114/−92
- Data/HashMap/Lazy.hs +1/−0
- Data/HashMap/Strict.hs +39/−21
- Data/HashMap/Unsafe.hs +28/−0
- tests/HashMapProperties.hs +21/−13
- tests/Regressions.hs +9/−0
- unordered-containers.cabal +19/−18
Data/HashMap/Array.hs view
@@ -1,5 +1,5 @@ {-# LANGUAGE BangPatterns, CPP, MagicHash, Rank2Types, UnboxedTuples #-}-{-# OPTIONS_GHC -funbox-strict-fields #-}+{-# OPTIONS_GHC -fno-full-laziness -funbox-strict-fields #-} -- | Zero based arrays. --@@ -48,15 +48,18 @@ , map' , traverse , filter+ , toList ) where import qualified Data.Traversable as Traversable import Control.Applicative (Applicative) import Control.DeepSeq-import Control.Monad.ST+import Control.Monad.ST hiding (runST) import GHC.Exts import GHC.ST (ST(..)) import Prelude hiding (filter, foldr, length, map, read)++import Data.HashMap.Unsafe (runST) ------------------------------------------------------------------------
Data/HashMap/Base.hs view
@@ -1,5 +1,5 @@ {-# LANGUAGE BangPatterns, CPP, DeriveDataTypeable, MagicHash #-}-{-# OPTIONS_GHC -funbox-strict-fields #-}+{-# OPTIONS_GHC -fno-full-laziness -funbox-strict-fields #-} module Data.HashMap.Base (@@ -36,6 +36,7 @@ -- * Difference and intersection , difference , intersection+ , intersectionWith -- * Folds , foldl'@@ -76,7 +77,7 @@ import Control.Applicative ((<$>), Applicative(pure)) import Control.DeepSeq (NFData(rnf))-import Control.Monad.ST (ST, runST)+import Control.Monad.ST (ST) import Data.Bits ((.&.), (.|.), complement) import qualified Data.Foldable as Foldable import qualified Data.List as L@@ -89,6 +90,7 @@ import qualified Data.Hashable as H import Data.Hashable (Hashable) import Data.HashMap.PopCount (popCount)+import Data.HashMap.Unsafe (runST) import Data.HashMap.UnsafeShift (unsafeShiftL, unsafeShiftR) import Data.Typeable (Typeable) @@ -103,6 +105,7 @@ hash = fromIntegral . H.hash data Leaf k v = L !k v+ deriving (Eq) instance (NFData k, NFData v) => NFData (Leaf k v) where rnf (L k v) = rnf k `seq` rnf v@@ -149,10 +152,22 @@ instance Traversable (HashMap k) where traverse f = traverseWithKey (const f) --- NOTE: This is just a placeholder. instance (Eq k, Eq v) => Eq (HashMap k v) where- a == b = toList a == toList b+ (==) = equal +equal :: (Eq k, Eq v) => HashMap k v -> HashMap k v -> Bool+equal (BitmapIndexed b1 ary1) (BitmapIndexed b2 ary2) =+ b1 == b2 && A.toList ary1 == A.toList ary2+equal (Leaf k1 v1) (Leaf k2 v2) =+ k1 == k2 && v1 == v2+equal (Full ary1) (Full ary2) =+ A.toList ary1 == A.toList ary2+equal (Collision k1 ary1) (Collision k2 ary2) =+ k1 == k2 && A.length ary1 == A.length ary2+ && L.null (A.toList ary1 L.\\ A.toList ary2)+equal Empty Empty = True+equal _ _ = False+ ------------------------------------------------------------------------ -- * Construction @@ -254,42 +269,38 @@ -- key in this map. If this map previously contained a mapping for -- the key, the old value is replaced. insert :: (Eq k, Hashable k) => k -> v -> HashMap k v -> HashMap k v-insert k0 v0 m0 = runST (go h0 k0 v0 0 m0)+insert k0 v0 m0 = go h0 k0 v0 0 m0 where h0 = hash k0- go !h !k x !_ Empty = return $! Leaf h (L k x)+ go !h !k x !_ Empty = Leaf h (L k x) go h k x s t@(Leaf hy l@(L ky y)) | hy == h = if ky == k then if x `ptrEq` y- then return t- else return $! Leaf h (L k x)- else return $! collision h l (L k x)- | otherwise = two s h k x hy ky y+ then t+ else Leaf h (L k x)+ else collision h l (L k x)+ | otherwise = runST (two s h k x hy ky y) go h k x s t@(BitmapIndexed b ary)- | b .&. m == 0 = do- !ary' <- A.insert' ary i $! Leaf h (L k x)- return $! bitmapIndexedOrFull (b .|. m) ary'- | otherwise = do- st <- A.index_ ary i- !st' <- go h k x (s+bitsPerSubkey) st- if st' `ptrEq` st- then return t- else do- ary' <- A.update' ary i st'- return $! BitmapIndexed b ary'+ | b .&. m == 0 =+ let ary' = A.insert ary i $! Leaf h (L k x)+ in bitmapIndexedOrFull (b .|. m) ary'+ | otherwise =+ let !st = A.index ary i+ !st' = go h k x (s+bitsPerSubkey) st+ in if st' `ptrEq` st+ then t+ else BitmapIndexed b (A.update ary i st') where m = mask h s i = sparseIndex b m- go h k x s t@(Full ary) = do- st <- A.index_ ary i- !st' <- go h k x (s+bitsPerSubkey) st- if st' `ptrEq` st- then return t- else do- ary' <- update16' ary i st'- return $! Full ary'+ go h k x s t@(Full ary) =+ let !st = A.index ary i+ !st' = go h k x (s+bitsPerSubkey) st+ in if st' `ptrEq` st+ then t+ else Full (update16 ary i st') where i = index h s go h k x s t@(Collision hy v)- | h == hy = return $! Collision h (updateOrSnocWith const k x v)+ | h == hy = Collision h (updateOrSnocWith const k x v) | otherwise = go h k x s $ BitmapIndexed (mask hy s) (A.singleton t) #if __GLASGOW_HASKELL__ >= 700 {-# INLINABLE insert #-}@@ -338,7 +349,7 @@ where go s h1 k1 v1 h2 k2 v2 | bp1 == bp2 = do- st <- go (s+bitsPerSubkey) h1 k1 v1 h2 k2 v2 + st <- go (s+bitsPerSubkey) h1 k1 v1 h2 k2 v2 ary <- A.singleton' st return $! BitmapIndexed bp1 ary | otherwise = do@@ -362,34 +373,34 @@ -- > where f new old = new + old insertWith :: (Eq k, Hashable k) => (v -> v -> v) -> k -> v -> HashMap k v -> HashMap k v-insertWith f k0 v0 m0 = runST (go h0 k0 v0 0 m0)+insertWith f k0 v0 m0 = go h0 k0 v0 0 m0 where h0 = hash k0- go !h !k x !_ Empty = return $! Leaf h (L k x)+ go !h !k x !_ Empty = Leaf h (L k x) go h k x s (Leaf hy l@(L ky y)) | hy == h = if ky == k- then return $! Leaf h (L k (f x y))- else return $! collision h l (L k x)- | otherwise = two s h k x hy ky y+ then Leaf h (L k (f x y))+ else collision h l (L k x)+ | otherwise = runST (two s h k x hy ky y) go h k x s (BitmapIndexed b ary)- | b .&. m == 0 = do- ary' <- A.insert' ary i $! Leaf h (L k x)- return $! bitmapIndexedOrFull (b .|. m) ary'- | otherwise = do- st <- A.index_ ary i- st' <- go h k x (s+bitsPerSubkey) st- ary' <- A.update' ary i st'- return $! BitmapIndexed b ary'+ | b .&. m == 0 =+ let ary' = A.insert ary i $! Leaf h (L k x)+ in bitmapIndexedOrFull (b .|. m) ary'+ | otherwise =+ let st = A.index ary i+ st' = go h k x (s+bitsPerSubkey) st+ ary' = A.update ary i st'+ in BitmapIndexed b ary' where m = mask h s i = sparseIndex b m- go h k x s (Full ary) = do- st <- A.index_ ary i- st' <- go h k x (s+bitsPerSubkey) st- ary' <- update16' ary i st'- return $! Full ary'+ go h k x s (Full ary) =+ let st = A.index ary i+ st' = go h k x (s+bitsPerSubkey) st+ ary' = update16 ary i st'+ in Full ary' where i = index h s go h k x s t@(Collision hy v)- | h == hy = return $! Collision h (updateOrSnocWith f k x v)+ | h == hy = Collision h (updateOrSnocWith f k x v) | otherwise = go h k x s $ BitmapIndexed (mask hy s) (A.singleton t) #if __GLASGOW_HASKELL__ >= 700 {-# INLINABLE insertWith #-}@@ -434,54 +445,48 @@ -- | /O(log n)/ Remove the mapping for the specified key from this map -- if present. delete :: (Eq k, Hashable k) => k -> HashMap k v -> HashMap k v-delete k0 m0 = runST (go h0 k0 0 m0)+delete k0 m0 = go h0 k0 0 m0 where h0 = hash k0- go !_ !_ !_ Empty = return Empty+ go !_ !_ !_ Empty = Empty go h k _ t@(Leaf hy (L ky _))- | hy == h && ky == k = return Empty- | otherwise = return t+ | hy == h && ky == k = Empty+ | otherwise = t go h k s t@(BitmapIndexed b ary)- | b .&. m == 0 = return t- | otherwise = do+ | b .&. m == 0 = t+ | otherwise = let !st = A.index ary i- !st' <- go h k (s+bitsPerSubkey) st- if st' `ptrEq` st- then return t+ !st' = go h k (s+bitsPerSubkey) st+ in if st' `ptrEq` st+ then t else case st' of- Empty | A.length ary == 1 -> return Empty- | otherwise -> do- ary' <- A.delete' ary i- return $! BitmapIndexed (b .&. complement m) ary'- _ -> do- ary' <- A.update' ary i st'- return $! BitmapIndexed b ary'+ Empty | A.length ary == 1 -> Empty+ | otherwise -> BitmapIndexed (b .&. complement m) (A.delete ary i)+ _ -> BitmapIndexed b (A.update ary i st') where m = mask h s i = sparseIndex b m- go h k s t@(Full ary) = do- let !st = A.index ary i- !st' <- go h k (s+bitsPerSubkey) st- if st' `ptrEq` st- then return t+ go h k s t@(Full ary) =+ let !st = A.index ary i+ !st' = go h k (s+bitsPerSubkey) st+ in if st' `ptrEq` st+ then t else case st' of- Empty -> do- ary' <- A.delete' ary i- let bm = fullNodeMask .&. complement (1 `unsafeShiftL` i)- return $! BitmapIndexed bm ary'- _ -> do- ary' <- A.update' ary i st'- return $! Full ary'+ Empty ->+ let ary' = A.delete ary i+ bm = fullNodeMask .&. complement (1 `unsafeShiftL` i)+ in BitmapIndexed bm ary'+ _ -> Full (A.update ary i st') where i = index h s go h k _ t@(Collision hy v) | h == hy = case indexOf k v of Just i | A.length v == 2 -> if i == 0- then return $! Leaf h (A.index v 1)- else return $! Leaf h (A.index v 0)- | otherwise -> return $! Collision h (A.delete v i)- Nothing -> return t- | otherwise = return t+ then Leaf h (A.index v 1)+ else Leaf h (A.index v 0)+ | otherwise -> Collision h (A.delete v i)+ Nothing -> t+ | otherwise = t #if __GLASGOW_HASKELL__ >= 700 {-# INLINABLE delete #-} #endif@@ -623,20 +628,20 @@ mary <- A.new_ (popCount b') -- iterate over nonzero bits of b1 .|. b2 -- it would be nice if we could shift m by more than 1 each time- let hasBit b m = b .&. m /= 0+ let ba = b1 .&. b2 go !i !i1 !i2 !m- | m > b' = return ()- | hasBit b1 m && hasBit b2 m = do+ | m > b' = return ()+ | b' .&. m == 0 = go i i1 i2 (m `unsafeShiftL` 1)+ | ba .&. m /= 0 = do A.write mary i $! f (A.index ary1 i1) (A.index ary2 i2) go (i+1) (i1+1) (i2+1) (m `unsafeShiftL` 1)- | hasBit b1 m = do+ | b1 .&. m /= 0 = do A.write mary i =<< A.index_ ary1 i1 go (i+1) (i1+1) (i2 ) (m `unsafeShiftL` 1)- | hasBit b2 m = do+ | otherwise = do A.write mary i =<< A.index_ ary2 i2 go (i+1) (i1 ) (i2+1) (m `unsafeShiftL` 1)- | otherwise = go i i1 i2 (m `unsafeShiftL` 1)- go 0 0 0 1+ go 0 0 0 (b' .&. negate b') -- XXX: b' must be non-zero return mary -- TODO: For the case where b1 .&. b2 == b1, i.e. when one is a -- subset of the other, we could use a slightly simpler algorithm,@@ -665,6 +670,9 @@ A.map' (\ (L k v) -> L k (f v)) ary {-# INLINE map #-} +-- TODO: We should be able to use mutation to create the new+-- 'HashMap'.+ -- | /O(n)/ Transform this map by accumulating an Applicative result -- from every value. traverseWithKey :: Applicative f => (k -> v1 -> f v2) -> HashMap k v1@@ -682,7 +690,7 @@ ------------------------------------------------------------------------ -- * Difference and intersection --- | /O(n+m)/ Difference of two maps. Return elements of the first map+-- | /O(n*log m)/ Difference of two maps. Return elements of the first map -- not existing in the second. difference :: (Eq k, Hashable k) => HashMap k v -> HashMap k w -> HashMap k v difference a b = foldlWithKey' go empty a@@ -694,7 +702,7 @@ {-# INLINABLE difference #-} #endif --- | /O(n+m)/ Intersection of two maps. Return elements of the first+-- | /O(n*log m)/ Intersection of two maps. Return elements of the first -- map for keys existing in the second. intersection :: (Eq k, Hashable k) => HashMap k v -> HashMap k w -> HashMap k v intersection a b = foldlWithKey' go empty a@@ -704,6 +712,20 @@ _ -> m #if __GLASGOW_HASKELL__ >= 700 {-# INLINABLE intersection #-}+#endif++-- | /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.+intersectionWith :: (Eq k, Hashable k) => (v1 -> v2 -> v3) -> HashMap k v1+ -> HashMap k v2 -> HashMap k v3+intersectionWith f a b = foldlWithKey' go empty a+ where+ go m k v = case lookup k b of+ Just w -> insert k (f v w) m+ _ -> m+#if __GLASGOW_HASKELL__ >= 700+{-# INLINABLE intersectionWith #-} #endif ------------------------------------------------------------------------
Data/HashMap/Lazy.hs view
@@ -62,6 +62,7 @@ -- * Difference and intersection , difference , intersection+ , intersectionWith -- * Folds , foldl'
Data/HashMap/Strict.hs view
@@ -63,6 +63,7 @@ -- * Difference and intersection , difference , intersection+ , intersectionWith -- * Folds , foldl'@@ -93,8 +94,8 @@ import qualified Data.HashMap.Array as A import qualified Data.HashMap.Base as HM import Data.HashMap.Base hiding (- adjust, fromList, fromListWith, insert, insertWith, map, singleton,- unionWith)+ adjust, fromList, fromListWith, insert, insertWith, intersectionWith, map,+ singleton, unionWith) ------------------------------------------------------------------------ -- * Construction@@ -124,34 +125,34 @@ -- > where f new old = new + old insertWith :: (Eq k, Hashable k) => (v -> v -> v) -> k -> v -> HashMap k v -> HashMap k v-insertWith f k0 !v0 m0 = runST (go h0 k0 v0 0 m0)+insertWith f k0 !v0 m0 = go h0 k0 v0 0 m0 where h0 = hash k0- go !h !k x !_ Empty = return $ Leaf h (L k x)+ go !h !k x !_ Empty = Leaf h (L k x) go h k x s (Leaf hy l@(L ky y)) | hy == h = if ky == k- then let !v' = f x y in return $! Leaf h (L k v')- else return $! collision h l (L k x)- | otherwise = two s h k x hy ky y+ then let !v' = f x y in Leaf h (L k v')+ else collision h l (L k x)+ | otherwise = runST (two s h k x hy ky y) go h k x s (BitmapIndexed b ary)- | b .&. m == 0 = do- ary' <- A.insert' ary i $! Leaf h (L k x)- return $! bitmapIndexedOrFull (b .|. m) ary'- | otherwise = do- st <- A.index_ ary i- st' <- go h k x (s+bitsPerSubkey) st- ary' <- A.update' ary i st'- return $! BitmapIndexed b ary'+ | b .&. m == 0 =+ let ary' = A.insert ary i $! Leaf h (L k x)+ in bitmapIndexedOrFull (b .|. m) ary'+ | otherwise =+ let st = A.index ary i+ st' = go h k x (s+bitsPerSubkey) st+ ary' = A.update ary i st'+ in BitmapIndexed b ary' where m = mask h s i = sparseIndex b m- go h k x s (Full ary) = do- st <- A.index_ ary i- st' <- go h k x (s+bitsPerSubkey) st- ary' <- update16' ary i st'- return $! Full ary'+ go h k x s (Full ary) =+ let st = A.index ary i+ st' = go h k x (s+bitsPerSubkey) st+ ary' = update16 ary i st'+ in Full ary' where i = index h s go h k x s t@(Collision hy v)- | h == hy = return $! Collision h (updateOrSnocWith f k x v)+ | h == hy = Collision h (updateOrSnocWith f k x v) | otherwise = go h k x s $ BitmapIndexed (mask hy s) (A.singleton t) #if __GLASGOW_HASKELL__ >= 700 {-# INLINABLE insertWith #-}@@ -329,6 +330,23 @@ {-# INLINE map #-} -- TODO: Should we add a strict traverseWithKey?++------------------------------------------------------------------------+-- * Difference and intersection++-- | /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.+intersectionWith :: (Eq k, Hashable k) => (v1 -> v2 -> v3) -> HashMap k v1+ -> HashMap k v2 -> HashMap k v3+intersectionWith f a b = foldlWithKey' go empty a+ where+ go m k v = case HM.lookup k b of+ Just w -> insert k (f v w) m+ _ -> m+#if __GLASGOW_HASKELL__ >= 700+{-# INLINABLE intersectionWith #-}+#endif ------------------------------------------------------------------------ -- ** Lists
+ Data/HashMap/Unsafe.hs view
@@ -0,0 +1,28 @@+{-# LANGUAGE MagicHash, Rank2Types, UnboxedTuples #-}++-- | This module exports a workaround for this bug:+--+-- http://hackage.haskell.org/trac/ghc/ticket/5916+--+-- Please read the comments in ghc/libraries/base/GHC/ST.lhs to+-- understand what's going on here.+--+-- Code that uses this module should be compiled with -fno-full-laziness+module Data.HashMap.Unsafe+ ( runST+ ) where++import GHC.Base (realWorld#)+import GHC.ST hiding (runST, runSTRep)++-- | 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 })+{-# INLINE runST #-}++runSTRep :: (forall s. STRep s a) -> a+runSTRep st_rep = case st_rep realWorld# of+ (# _, r #) -> r+{-# INLINE [0] runSTRep #-}
tests/HashMapProperties.hs view
@@ -71,19 +71,23 @@ -- White-box test that tests the case of deleting one of two keys from -- a map, where the keys' hash values collide.-pDeleteCollision :: AlwaysCollide -> AlwaysCollide -> Bool -> Property-pDeleteCollision k1 k2 keepFst = k1 /= k2 ==> HM.member toKeep $ HM.delete toDelete $- HM.fromList [(k1, 1 :: Int), (k2, 2)]+pDeleteCollision :: AlwaysCollide -> AlwaysCollide -> AlwaysCollide -> Int+ -> Property+pDeleteCollision k1 k2 k3 idx = (k1 /= k2) && (k2 /= k3) && (k1 /= k3) ==>+ HM.member toKeep $ HM.delete toDelete $+ HM.fromList [(k1, 1 :: Int), (k2, 2), (k3, 3)] where- (toDelete, toKeep)- | keepFst = (k2, k1)- | otherwise = (k1, k2)---- White-box test that tests the case of deleting one of many keys--- from a map, where the keys' hash values collide.-pDeleteCollisionMany :: AlwaysCollide -> [(AlwaysCollide, Int)] -> Bool-pDeleteCollisionMany k kvs = not $ (HM.member k) $ HM.delete k $- HM.fromList ((k, 1):kvs)+ which = idx `mod` 3+ toDelete+ | which == 0 = k1+ | which == 1 = k2+ | which == 2 = k3+ | otherwise = error "Impossible"+ toKeep+ | which == 0 = k2+ | which == 1 = k3+ | which == 2 = k1+ | otherwise = error "Impossible" pInsertWith :: Key -> [(Key, Int)] -> Bool pInsertWith k = M.insertWith (+) k 1 `eq_` HM.insertWith (+) k 1@@ -122,6 +126,10 @@ pIntersection xs ys = M.intersection (M.fromList xs) `eq_` HM.intersection (HM.fromList xs) $ ys +pIntersectionWith :: [(Key, Int)] -> [(Key, Int)] -> Bool+pIntersectionWith xs ys = M.intersectionWith (-) (M.fromList xs) `eq_`+ HM.intersectionWith (-) (HM.fromList xs) $ ys+ ------------------------------------------------------------------------ -- ** Folds @@ -187,7 +195,6 @@ , testProperty "insert" pInsert , testProperty "delete" pDelete , testProperty "deleteCollision" pDeleteCollision- , testProperty "deleteCollisionMany" pDeleteCollisionMany , testProperty "insertWith" pInsertWith , testProperty "adjust" pAdjust ]@@ -206,6 +213,7 @@ , testGroup "difference and intersection" [ testProperty "difference" pDifference , testProperty "intersection" pIntersection+ , testProperty "intersectionWith" pIntersectionWith ] -- Filter , testGroup "filter"
tests/Regressions.hs view
@@ -13,6 +13,14 @@ m = HM.fromList (zip ns (repeat [])) m' = HM.delete 10 m +issue39 :: Assertion+issue39 = assert $ hm1 == hm2+ where+ hm1 = HM.fromList ([a, b] `zip` [1, 1 :: Int ..])+ hm2 = HM.fromList ([b, a] `zip` [1, 1 :: Int ..])+ a = (1, -1) :: (Int, Int)+ b = (-1, 1) :: (Int, Int)+ ------------------------------------------------------------------------ -- * Test list @@ -20,6 +28,7 @@ tests = [ testCase "issue32" issue32+ , testCase "issue39" issue39 ] ------------------------------------------------------------------------
unordered-containers.cabal view
@@ -1,5 +1,5 @@ name: unordered-containers-version: 0.2.1.0+version: 0.2.2.0 synopsis: Efficient hashing-based container types description: Efficient hashing-based container types. The containers have been@@ -32,12 +32,13 @@ Data.HashMap.Array Data.HashMap.Base Data.HashMap.PopCount+ Data.HashMap.Unsafe Data.HashMap.UnsafeShift build-depends:- base >= 4 && < 4.6,- deepseq >= 1.1 && < 1.4,- hashable >= 1.0.1.1 && < 1.2+ base >= 4 && < 5,+ deepseq >= 1.1,+ hashable >= 1.0.1.1 if impl(ghc < 7.4) c-sources: cbits/popc.c@@ -57,11 +58,11 @@ build-depends: base,- containers >= 0.4.1 && < 0.5,- hashable >= 1.0.1.1 && < 1.2,+ containers >= 0.4.1,+ hashable >= 1.0.1.1, QuickCheck >= 2.4.0.1,- test-framework >= 0.3.3 && < 0.6,- test-framework-quickcheck2 >= 0.2.9 && < 0.3,+ test-framework >= 0.3.3,+ test-framework-quickcheck2 >= 0.2.9, unordered-containers ghc-options: -Wall@@ -74,11 +75,11 @@ build-depends: base,- containers >= 0.4.1 && < 0.5,- hashable >= 1.0.1.1 && < 1.2,+ containers >= 0.4.1,+ hashable >= 1.0.1.1, QuickCheck >= 2.4.0.1,- test-framework >= 0.3.3 && < 0.6,- test-framework-quickcheck2 >= 0.2.9 && < 0.3,+ test-framework >= 0.3.3,+ test-framework-quickcheck2 >= 0.2.9, unordered-containers ghc-options: -Wall@@ -91,11 +92,11 @@ build-depends: base,- containers >= 0.4.2 && < 0.5,- hashable >= 1.0.1.1 && < 1.2,+ containers >= 0.4.2,+ hashable >= 1.0.1.1, QuickCheck >= 2.4.0.1,- test-framework >= 0.3.3 && < 0.6,- test-framework-quickcheck2 >= 0.2.9 && < 0.3,+ test-framework >= 0.3.3,+ test-framework-quickcheck2 >= 0.2.9, unordered-containers ghc-options: -Wall@@ -108,9 +109,9 @@ build-depends: base,- hashable >= 1.0.1.1 && < 1.2,+ hashable >= 1.0.1.1, HUnit,- test-framework >= 0.3.3 && < 0.6,+ test-framework >= 0.3.3, test-framework-hunit, unordered-containers