From 1e192e981d315bd5268f6ff2ad3ddbd18a18b71f Mon Sep 17 00:00:00 2001 From: Simon Jakobi <simon.jakobi@gmail.com> Date: Mon, 24 Jun 2019 01:02:24 +0200 Subject: [PATCH] Add disjoint for Map and IntMap (#642) Add disjoint for `Map` and `IntMap`. Closes #641. --- .gitignore | 1 + containers-tests/tests/intmap-properties.hs | 4 +++ containers-tests/tests/map-properties.hs | 4 +++ containers/src/Data/IntMap/Internal.hs | 34 ++++++++++++++++++- containers/src/Data/IntMap/Lazy.hs | 3 ++ containers/src/Data/IntMap/Strict.hs | 3 ++ containers/src/Data/IntMap/Strict/Internal.hs | 4 +++ containers/src/Data/Map/Internal.hs | 30 ++++++++++++++++ containers/src/Data/Map/Lazy.hs | 3 ++ containers/src/Data/Map/Strict.hs | 3 ++ containers/src/Data/Map/Strict/Internal.hs | 4 +++ 11 files changed, 92 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b599dc2e..77c2680d 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 7ac8f595..86eb6276 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 59522f3f..c1bff016 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 edc2bf83..9e240314 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 57b26eee..d09f2624 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 88bdc939..b0ab5ccb 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 f53280ee..9957a041 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 f68ed717..6f3dcef6 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 1080f96c..37ae931e 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 084b8567..a7307841 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 788d51d3..a161b2f4 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 -- GitLab