@@ -83,6 +83,7 @@ module Data.HashMap.Internal
8383 , intersectionWith
8484 , intersectionWithKey
8585 , intersectionWithKey #
86+ , disjoint
8687
8788 -- * Folds
8889 , foldr'
@@ -719,23 +720,23 @@ lookupCont ::
719720 -> k
720721 -> Shift
721722 -> HashMap k v -> r
722- lookupCont absent present ! h0 ! k0 ! s0 m0 = go h0 k0 s0 m0
723+ lookupCont absent present ! h0 ! k0 ! s0 m0 = lookupCont_ h0 k0 s0 m0
723724 where
724- go :: Eq k => Hash -> k -> Shift -> HashMap k v -> r
725- go ! _ ! _ ! _ Empty = absent (# # )
726- go h k _ (Leaf hx (L kx x))
725+ lookupCont_ :: Eq k => Hash -> k -> Shift -> HashMap k v -> r
726+ lookupCont_ ! _ ! _ ! _ Empty = absent (# # )
727+ lookupCont_ h k _ (Leaf hx (L kx x))
727728 | h == hx && k == kx = present x (- 1 )
728729 | otherwise = absent (# # )
729- go h k s (BitmapIndexed b v)
730+ lookupCont_ h k s (BitmapIndexed b v)
730731 | b .&. m == 0 = absent (# # )
731732 | otherwise =
732733 case A. index# v (sparseIndex b m) of
733- (# st # ) -> go h k (nextShift s) st
734+ (# st # ) -> lookupCont_ h k (nextShift s) st
734735 where m = mask h s
735- go h k s (Full v) =
736+ lookupCont_ h k s (Full v) =
736737 case A. index# v (index h s) of
737- (# st # ) -> go h k (nextShift s) st
738- go h k _ (Collision hx v)
738+ (# st # ) -> lookupCont_ h k (nextShift s) st
739+ lookupCont_ h k _ (Collision hx v)
739740 | h == hx = lookupInArrayCont absent present k v
740741 | otherwise = absent (# # )
741742{-# INLINE lookupCont #-}
@@ -2315,6 +2316,94 @@ searchSwap mary n toFind start = go start toFind start
23152316 else go i0 k (i + 1 )
23162317{-# INLINE searchSwap #-}
23172318
2319+ -- | \(O(n \log m)\) Check whether the key sets of two maps are disjoint
2320+ -- (i.e., their 'intersection' is empty).
2321+ --
2322+ -- @
2323+ -- xs ``disjoint`` ys = null (xs ``intersection`` ys)
2324+ -- @
2325+ --
2326+ -- @since FIXME
2327+ disjoint :: Eq k => HashMap k a -> HashMap k b -> Bool
2328+ disjoint = disjointSubtrees 0
2329+ {-# INLINE disjoint #-}
2330+
2331+ -- Note that as of GHC 9.12, SpecConstr creates a specialized worker for
2332+ -- handling the Collision vs. {BitmapIndexed,Full} and vice-versa cases,
2333+ -- but this worker fails to be properly specialized for different key
2334+ -- types. See https://gitlab.haskell.org/ghc/ghc/-/issues/26615.
2335+ disjointSubtrees :: Eq k => Shift -> HashMap k a -> HashMap k b -> Bool
2336+ disjointSubtrees ! _s Empty _b = True
2337+ disjointSubtrees s (Leaf hA (L kA _)) b =
2338+ lookupCont (\ _ -> True ) (\ _ _ -> False ) hA kA s b
2339+ disjointSubtrees s (BitmapIndexed bmA aryA) (BitmapIndexed bmB aryB) =
2340+ -- We could do a pointer equality check here but it's probably not worth it
2341+ -- since it would save only O(1) extra work:
2342+ --
2343+ -- not (aryA `A.unsafeSameArray` aryB) &&
2344+ disjointArrays s bmA aryA bmB aryB
2345+ disjointSubtrees s (BitmapIndexed bmA aryA) (Full aryB) =
2346+ disjointArrays s bmA aryA fullBitmap aryB
2347+ disjointSubtrees s (Full aryA) (BitmapIndexed bmB aryB) =
2348+ disjointArrays s fullBitmap aryA bmB aryB
2349+ disjointSubtrees s (Full aryA) (Full aryB) =
2350+ -- We could do a pointer equality check here but it's probably not worth it
2351+ -- since it would save only O(1) extra work:
2352+ --
2353+ -- not (aryA `A.unsafeSameArray` aryB) &&
2354+ go (maxChildren - 1 )
2355+ where
2356+ go i
2357+ | i < 0 = True
2358+ | otherwise = case A. index# aryA i of
2359+ (# stA # ) -> case A. index# aryB i of
2360+ (# stB # ) ->
2361+ disjointSubtrees (nextShift s) stA stB &&
2362+ go (i - 1 )
2363+ disjointSubtrees s a@ (Collision hA _) (BitmapIndexed bmB aryB)
2364+ | m .&. bmB == 0 = True
2365+ | otherwise = case A. index# aryB i of
2366+ (# stB # ) -> disjointSubtrees (nextShift s) a stB
2367+ where
2368+ m = mask hA s
2369+ i = sparseIndex bmB m
2370+ disjointSubtrees s a@ (Collision hA _) (Full aryB) =
2371+ case A. index# aryB (index hA s) of
2372+ (# stB # ) -> disjointSubtrees (nextShift s) a stB
2373+ disjointSubtrees _ (Collision hA aryA) (Collision hB aryB) =
2374+ disjointCollisions hA aryA hB aryB
2375+ disjointSubtrees _s _a Empty = True
2376+ disjointSubtrees s a (Leaf hB (L kB _)) =
2377+ lookupCont (\ _ -> True ) (\ _ _ -> False ) hB kB s a
2378+ disjointSubtrees s a b@ Collision {} = disjointSubtrees s b a
2379+ {-# INLINABLE disjointSubtrees #-}
2380+
2381+ disjointArrays :: Eq k => Shift -> Bitmap -> A. Array (HashMap k a ) -> Bitmap -> A. Array (HashMap k b ) -> Bool
2382+ disjointArrays ! s ! bmA ! aryA ! bmB ! aryB = go (bmA .&. bmB)
2383+ where
2384+ go 0 = True
2385+ go bm = case A. index# aryA iA of
2386+ (# stA # ) -> case A. index# aryB iB of
2387+ (# stB # ) ->
2388+ disjointSubtrees (nextShift s) stA stB &&
2389+ go (bm .&. complement m)
2390+ where
2391+ m = bm .&. negate bm
2392+ iA = sparseIndex bmA m
2393+ iB = sparseIndex bmB m
2394+ {-# INLINE disjointArrays #-}
2395+
2396+ -- TODO: GHC 9.12.2 inlines disjointCollisions into `disjoint @Int`.
2397+ -- How do you prevent this while preserving specialization?
2398+ -- https://stackoverflow.com/questions/79838305/ensuring-specialization-while-preventing-inlining
2399+ disjointCollisions :: Eq k => Hash -> A. Array (Leaf k a ) -> Hash -> A. Array (Leaf k b ) -> Bool
2400+ disjointCollisions ! hA ! aryA ! hB ! aryB
2401+ | hA == hB = A. all predicate aryA
2402+ | otherwise = True
2403+ where
2404+ predicate (L kA _) = lookupInArrayCont (\ _ -> True ) (\ _ _ -> False ) kA aryB
2405+ {-# INLINABLE disjointCollisions #-}
2406+
23182407------------------------------------------------------------------------
23192408-- * Folds
23202409
@@ -2639,15 +2728,16 @@ lookupInArrayCont ::
26392728 forall r k v.
26402729#endif
26412730 Eq k => ((# # ) -> r) -> (v -> Int -> r) -> k -> A. Array (Leaf k v) -> r
2642- lookupInArrayCont absent present k0 ary0 = go k0 ary0 0 (A. length ary0)
2731+ lookupInArrayCont absent present k0 ary0 =
2732+ lookupInArrayCont_ k0 ary0 0 (A. length ary0)
26432733 where
2644- go :: Eq k => k -> A. Array (Leaf k v ) -> Int -> Int -> r
2645- go ! k ! ary ! i ! n
2734+ lookupInArrayCont_ :: Eq k => k -> A. Array (Leaf k v ) -> Int -> Int -> r
2735+ lookupInArrayCont_ ! k ! ary ! i ! n
26462736 | i >= n = absent (# # )
26472737 | otherwise = case A. index# ary i of
26482738 (# L kx v # )
26492739 | k == kx -> present v i
2650- | otherwise -> go k ary (i+ 1 ) n
2740+ | otherwise -> lookupInArrayCont_ k ary (i+ 1 ) n
26512741{-# INLINE lookupInArrayCont #-}
26522742
26532743-- | \(O(n)\) Lookup the value associated with the given key in this
0 commit comments