diff --git a/.gitignore b/.gitignore index b599dc2ed6cc9db55c410fad759c34ae126f0537..77c2680d389dd2c308883923134105a0989b7bb3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ *.o *.p_hi *.prof +*.swo *.tix .hpc/ /dist/* diff --git a/containers-tests/tests/intmap-properties.hs b/containers-tests/tests/intmap-properties.hs index 7ac8f59567ebb3337d1231bc712485be81a593c0..86eb627642297b2208fb04aea2d022f491443a5d 100644 --- a/containers-tests/tests/intmap-properties.hs +++ b/containers-tests/tests/intmap-properties.hs @@ -162,6 +162,7 @@ main = defaultMain , testProperty "lookupGT" prop_lookupGT , testProperty "lookupLE" prop_lookupLE , testProperty "lookupGE" prop_lookupGE + , testProperty "disjoint" prop_disjoint , testProperty "lookupMin" prop_lookupMin , testProperty "lookupMax" prop_lookupMax , testProperty "findMin" prop_findMin @@ -880,6 +881,9 @@ prop_intersectionWithKeyModel xs ys ys' = List.nubBy ((==) `on` fst) ys f k l r = k + 2 * l + 3 * r +prop_disjoint :: UMap -> UMap -> Property +prop_disjoint m1 m2 = disjoint m1 m2 === null (intersection m1 m2) + -- TODO: the second argument should be simply an 'IntSet', but that -- runs afoul of our orphan instance. prop_restrictKeys :: IMap -> IMap -> Property diff --git a/containers-tests/tests/map-properties.hs b/containers-tests/tests/map-properties.hs index 59522f3fe67370c776f9e1e38b079d0fe2d45d89..c1bff01681a008bcd981493efb3742d75858ffec 100644 --- a/containers-tests/tests/map-properties.hs +++ b/containers-tests/tests/map-properties.hs @@ -172,6 +172,7 @@ main = defaultMain , testProperty "intersectionWithModel" prop_intersectionWithModel , testProperty "intersectionWithKey" prop_intersectionWithKey , testProperty "intersectionWithKeyModel" prop_intersectionWithKeyModel + , testProperty "disjoint" prop_disjoint , testProperty "differenceMerge" prop_differenceMerge , testProperty "unionWithKeyMerge" prop_unionWithKeyMerge , testProperty "mergeWithKey model" prop_mergeWithKeyModel @@ -1070,6 +1071,9 @@ prop_intersectionWithKeyModel xs ys ys' = List.nubBy ((==) `on` fst) ys f k l r = k + 2 * l + 3 * r +prop_disjoint :: UMap -> UMap -> Property +prop_disjoint m1 m2 = disjoint m1 m2 === null (intersection m1 m2) + prop_mergeWithKeyModel :: [(Int,Int)] -> [(Int,Int)] -> Bool prop_mergeWithKeyModel xs ys = and [ testMergeWithKey f keep_x keep_y diff --git a/containers/src/Data/IntMap/Internal.hs b/containers/src/Data/IntMap/Internal.hs index edc2bf837d248566c8ca0259044e877d0482d011..9e240314db9e1b423394955193b4fb3df58d546e 100644 --- a/containers/src/Data/IntMap/Internal.hs +++ b/containers/src/Data/IntMap/Internal.hs @@ -90,6 +90,7 @@ module Data.IntMap.Internal ( , lookupGT , lookupLE , lookupGE + , disjoint -- * Construction , empty @@ -372,7 +373,7 @@ data IntMap a = Bin {-# UNPACK #-} !Prefix -- two keys of the map differ. -- Invariant: Prefix is the common high-order bits that all elements share to -- the left of the Mask bit. --- Invariant: In Bin prefix mask left right, left consists of the elements that +-- Invariant: In (Bin prefix mask left right), left consists of the elements that -- don't have the mask bit set; right is all the elements that do. | Tip {-# UNPACK #-} !Key a | Nil @@ -728,6 +729,37 @@ unsafeFindMax Nil = Nothing unsafeFindMax (Tip ky y) = Just (ky, y) unsafeFindMax (Bin _ _ _ r) = unsafeFindMax r +{-------------------------------------------------------------------- + Disjoint +--------------------------------------------------------------------} +-- | /O(n+m)/. Check whether the key sets of two maps are disjoint +-- (i.e. their 'intersection' is empty). +-- +-- > disjoint (fromList [(2,'a')]) (fromList [(1,()), (3,())]) == True +-- > disjoint (fromList [(2,'a')]) (fromList [(1,'a'), (2,'b')]) == False +-- > disjoint (fromList []) (fromList []) == True +-- +-- > disjoint a b == null (intersection a b) +-- +-- @since UNRELEASED +disjoint :: IntMap a -> IntMap b -> Bool +disjoint Nil _ = True +disjoint _ Nil = True +disjoint (Tip kx _) ys = notMember kx ys +disjoint xs (Tip ky _) = notMember ky xs +disjoint t1@(Bin p1 m1 l1 r1) t2@(Bin p2 m2 l2 r2) + | shorter m1 m2 = disjoint1 + | shorter m2 m1 = disjoint2 + | p1 == p2 = disjoint l1 l2 && disjoint r1 r2 + | otherwise = True + where + disjoint1 | nomatch p2 p1 m1 = True + | zero p2 m1 = disjoint l1 t2 + | otherwise = disjoint r1 t2 + disjoint2 | nomatch p1 p2 m2 = True + | zero p1 m2 = disjoint t1 l2 + | otherwise = disjoint t1 r2 + {-------------------------------------------------------------------- Construction --------------------------------------------------------------------} diff --git a/containers/src/Data/IntMap/Lazy.hs b/containers/src/Data/IntMap/Lazy.hs index 57b26eee47d9c2e9b225270b8603ececcfd47a8d..d09f2624030331b8f224711b8b54d73142fd0093 100644 --- a/containers/src/Data/IntMap/Lazy.hs +++ b/containers/src/Data/IntMap/Lazy.hs @@ -143,6 +143,9 @@ module Data.IntMap.Lazy ( , intersectionWith , intersectionWithKey + -- ** Disjoint + , disjoint + -- ** Universal combining function , mergeWithKey diff --git a/containers/src/Data/IntMap/Strict.hs b/containers/src/Data/IntMap/Strict.hs index 88bdc93982822c088e8e120d2fcb2f0fa3621e59..b0ab5ccb300fee7156ba33247fb48344ca8301c2 100644 --- a/containers/src/Data/IntMap/Strict.hs +++ b/containers/src/Data/IntMap/Strict.hs @@ -162,6 +162,9 @@ module Data.IntMap.Strict ( , intersectionWith , intersectionWithKey + -- ** Disjoint + , disjoint + -- ** Universal combining function , mergeWithKey diff --git a/containers/src/Data/IntMap/Strict/Internal.hs b/containers/src/Data/IntMap/Strict/Internal.hs index f53280eee167f5a7f847a934fb058e3e06b2c1f0..9957a041f8ce6d002fb882596113e5d8fd66a3fa 100644 --- a/containers/src/Data/IntMap/Strict/Internal.hs +++ b/containers/src/Data/IntMap/Strict/Internal.hs @@ -159,6 +159,9 @@ module Data.IntMap.Strict.Internal ( , intersectionWith , intersectionWithKey + -- ** Disjoint + , disjoint + -- ** Universal combining function , mergeWithKey @@ -296,6 +299,7 @@ import Data.IntMap.Internal , difference , elems , intersection + , disjoint , isProperSubmapOf , isProperSubmapOfBy , isSubmapOf diff --git a/containers/src/Data/Map/Internal.hs b/containers/src/Data/Map/Internal.hs index f68ed717961ca1cb5887c8ccc15e5e14b2ea4020..6f3dcef69fdc1b24d2636bfa8a6a86163a405d7e 100644 --- a/containers/src/Data/Map/Internal.hs +++ b/containers/src/Data/Map/Internal.hs @@ -189,6 +189,9 @@ module Data.Map.Internal ( , intersectionWith , intersectionWithKey + -- ** Disjoint + , disjoint + -- ** General combining function , SimpleWhenMissing , SimpleWhenMatched @@ -2055,6 +2058,33 @@ intersectionWithKey f (Bin _ k x1 l1 r1) t2 = case mb of {-# INLINABLE intersectionWithKey #-} #endif +{-------------------------------------------------------------------- + Disjoint +--------------------------------------------------------------------} +-- | /O(m*log(n\/m + 1)), m <= n/. Check whether the key sets of two +-- maps are disjoint (i.e., their 'intersection' is empty). +-- +-- > disjoint (fromList [(2,'a')]) (fromList [(1,()), (3,())]) == True +-- > disjoint (fromList [(2,'a')]) (fromList [(1,'a'), (2,'b')]) == False +-- > disjoint (fromList []) (fromList []) == True +-- +-- @ +-- xs ``disjoint`` ys = null (xs ``intersection`` ys) +-- @ +-- +-- @since UNRELEASED + +-- See 'Data.Set.Internal.isSubsetOfX' for some background +-- on the implementation design. +disjoint :: Ord k => Map k a -> Map k b -> Bool +disjoint Tip _ = True +disjoint _ Tip = True +disjoint (Bin 1 k _ _ _) t = k `notMember` t +disjoint (Bin _ k _ l r) t + = not found && disjoint l lt && disjoint r gt + where + (lt,found,gt) = splitMember k t + #if !MIN_VERSION_base (4,8,0) -- | The identity type. newtype Identity a = Identity { runIdentity :: a } diff --git a/containers/src/Data/Map/Lazy.hs b/containers/src/Data/Map/Lazy.hs index 1080f96c0385a1ab7a230988a24a3e4cce336d94..37ae931e62d2567f6aca6a640c0a1a0ba4f98820 100644 --- a/containers/src/Data/Map/Lazy.hs +++ b/containers/src/Data/Map/Lazy.hs @@ -163,6 +163,9 @@ module Data.Map.Lazy ( , intersectionWith , intersectionWithKey + -- ** Disjoint + , disjoint + -- ** General combining functions -- | See "Data.Map.Merge.Lazy" diff --git a/containers/src/Data/Map/Strict.hs b/containers/src/Data/Map/Strict.hs index 084b85672b2e24559e8cc00d976498ba6f9c09ca..a73078413703241bb5c4d445bda603828e178c38 100644 --- a/containers/src/Data/Map/Strict.hs +++ b/containers/src/Data/Map/Strict.hs @@ -179,6 +179,9 @@ module Data.Map.Strict , intersectionWith , intersectionWithKey + -- ** Disjoint + , disjoint + -- ** General combining functions -- | See "Data.Map.Merge.Strict" diff --git a/containers/src/Data/Map/Strict/Internal.hs b/containers/src/Data/Map/Strict/Internal.hs index 788d51d3ba4b808ed730df71751ac1b20e43118d..a161b2f49cc0c1c97d0287d11f6398beb43e2d39 100644 --- a/containers/src/Data/Map/Strict/Internal.hs +++ b/containers/src/Data/Map/Strict/Internal.hs @@ -142,6 +142,9 @@ module Data.Map.Strict.Internal , intersectionWith , intersectionWithKey + -- ** Disjoint + , disjoint + -- ** General combining function , SimpleWhenMissing , SimpleWhenMatched @@ -339,6 +342,7 @@ import Data.Map.Internal , deleteMin , deleteMax , difference + , disjoint , drop , dropWhileAntitone , filter