@@ -77,6 +77,7 @@ module Data.HashMap.Internal
7777 -- * Difference and intersection
7878 , difference
7979 , differenceWith
80+ , differenceWithKey
8081 , intersection
8182 , intersectionWith
8283 , intersectionWithKey
@@ -1917,14 +1918,175 @@ differenceCollisions !h1 !ary1 t1 !h2 !ary2
19171918-- encountered, the combining function is applied to the values of these keys.
19181919-- If it returns 'Nothing', the element is discarded (proper set difference). If
19191920-- it returns (@'Just' y@), the element is updated with a new value @y@.
1920- differenceWith :: (Eq k , Hashable k ) => (v -> w -> Maybe v ) -> HashMap k v -> HashMap k w -> HashMap k v
1921- differenceWith f a b = foldlWithKey' go empty a
1922- where
1923- go m k v = case lookup k b of
1924- Nothing -> unsafeInsert k v m
1925- Just w -> maybe m (\ y -> unsafeInsert k y m) (f v w)
1921+ differenceWith :: Eq k => (v -> w -> Maybe v ) -> HashMap k v -> HashMap k w -> HashMap k v
1922+ differenceWith f = differenceWithKey (const f)
19261923{-# INLINABLE differenceWith #-}
19271924
1925+ -- | \(O(n \log m)\) Difference with a combining function. When two equal keys are
1926+ -- encountered, the combining function is applied to the values of these keys.
1927+ -- If it returns 'Nothing', the element is discarded (proper set difference). If
1928+ -- it returns (@'Just' y@), the element is updated with a new value @y@.
1929+ differenceWithKey :: Eq k => (k -> v -> w -> Maybe v ) -> HashMap k v -> HashMap k w -> HashMap k v
1930+ differenceWithKey = go_differenceWithKey 0
1931+ where
1932+ go_differenceWithKey ! _s _f Empty _tB = Empty
1933+ go_differenceWithKey _s _f a Empty = a
1934+ go_differenceWithKey s f a@ (Leaf hA (L kA vA)) b
1935+ = lookupCont
1936+ (\ _ -> a)
1937+ (\ vB _ -> case f kA vA vB of
1938+ Nothing -> Empty
1939+ Just v | v `ptrEq` vA -> a
1940+ | otherwise -> Leaf hA (L kA v))
1941+ hA kA s b
1942+ go_differenceWithKey _s f a@ (Collision hA aryA) (Leaf hB (L kB vB))
1943+ | hA == hB
1944+ = lookupInArrayCont
1945+ (\ _ -> a)
1946+ (\ vA i -> case f kB vA vB of
1947+ Nothing | A. length aryA == 2
1948+ , (# l # ) <- A. index# aryA (otherOfOneOrZero i)
1949+ -> Leaf hA l
1950+ | otherwise -> Collision hA (A. delete aryA i)
1951+ Just v | v `ptrEq` vA -> a
1952+ | otherwise -> Collision hA (A. update aryA i (L kB v)))
1953+ kB aryA
1954+ | otherwise = a
1955+ go_differenceWithKey s f a@ (BitmapIndexed bA aryA) b@ (Leaf hB _)
1956+ | bA .&. m == 0 = a
1957+ | otherwise = case A. index# aryA i of
1958+ (# ! stA # ) -> case go_differenceWithKey (nextShift s) f stA b of
1959+ Empty | A. length aryA == 2
1960+ , (# l # ) <- A. index# aryA (otherOfOneOrZero i)
1961+ , isLeafOrCollision l
1962+ -> l
1963+ | otherwise
1964+ -> BitmapIndexed (bA .&. complement m) (A. delete aryA i)
1965+ stA' | isLeafOrCollision stA' && A. length aryA == 1 -> stA'
1966+ | stA `ptrEq` stA' -> a
1967+ | otherwise -> BitmapIndexed bA (A. update aryA i stA')
1968+ where
1969+ m = mask hB s
1970+ i = sparseIndex bA m
1971+ go_differenceWithKey s f a@ (BitmapIndexed bA aryA) b@ (Collision hB _)
1972+ | bA .&. m == 0 = a
1973+ | otherwise =
1974+ case A. index# aryA i of
1975+ (# ! st # ) -> case go_differenceWithKey (nextShift s) f st b of
1976+ Empty | A. length aryA == 2
1977+ , (# l # ) <- A. index# aryA (otherOfOneOrZero i)
1978+ , isLeafOrCollision l
1979+ -> l
1980+ | otherwise
1981+ -> BitmapIndexed (bA .&. complement m) (A. delete aryA i)
1982+ st' | isLeafOrCollision st' && A. length aryA == 1 -> st'
1983+ | st `ptrEq` st' -> a
1984+ | otherwise -> BitmapIndexed bA (A. update aryA i st')
1985+ where
1986+ m = mask hB s
1987+ i = sparseIndex bA m
1988+ go_differenceWithKey s f a@ (Full aryA) b@ (Leaf hB _)
1989+ = case A. index# aryA i of
1990+ (# ! stA # ) -> case go_differenceWithKey (nextShift s) f stA b of
1991+ Empty ->
1992+ let aryA' = A. delete aryA i
1993+ bm = fullBitmap .&. complement (1 `unsafeShiftL` i)
1994+ in BitmapIndexed bm aryA'
1995+ stA' | stA `ptrEq` stA' -> a
1996+ | otherwise -> Full (updateFullArray aryA i stA')
1997+ where i = index hB s
1998+ go_differenceWithKey s f a@ (Full aryA) b@ (Collision hB _)
1999+ = case A. index# aryA i of
2000+ (# ! stA # ) -> case go_differenceWithKey (nextShift s) f stA b of
2001+ Empty ->
2002+ let aryA' = A. delete aryA i
2003+ bm = fullBitmap .&. complement (1 `unsafeShiftL` i)
2004+ in BitmapIndexed bm aryA'
2005+ stA' | stA `ptrEq` stA' -> a
2006+ | otherwise -> Full (updateFullArray aryA i stA')
2007+ where i = index hB s
2008+ go_differenceWithKey s f a@ (Collision hA _) (BitmapIndexed bB aryB)
2009+ | bB .&. m == 0 = a
2010+ | otherwise =
2011+ case A. index# aryB (sparseIndex bB m) of
2012+ (# stB # ) -> go_differenceWithKey (nextShift s) f a stB
2013+ where m = mask hA s
2014+ go_differenceWithKey s f a@ (Collision hA _) (Full aryB)
2015+ = case A. index# aryB (index hA s) of
2016+ (# stB # ) -> go_differenceWithKey (nextShift s) f a stB
2017+ go_differenceWithKey s f a@ (BitmapIndexed bA aryA) (BitmapIndexed bB aryB)
2018+ = differenceWithKey_Arrays s f bA aryA a bB aryB
2019+ go_differenceWithKey s f a@ (Full aryA) (BitmapIndexed bB aryB)
2020+ = differenceWithKey_Arrays s f fullBitmap aryA a bB aryB
2021+ go_differenceWithKey s f a@ (BitmapIndexed bA aryA) (Full aryB)
2022+ = differenceWithKey_Arrays s f bA aryA a fullBitmap aryB
2023+ go_differenceWithKey s f a@ (Full aryA) (Full aryB)
2024+ = differenceWithKey_Arrays s f fullBitmap aryA a fullBitmap aryB
2025+ go_differenceWithKey _s f a@ (Collision hA aryA) (Collision hB aryB)
2026+ = differenceWithKey_Collisions f hA aryA a hB aryB
2027+
2028+ differenceWithKey_Arrays ! s f ! bA ! aryA tA ! bB ! aryB
2029+ | bA .&. bB == 0 = tA
2030+ | otherwise = runST $ do
2031+ mary <- A. new_ $ A. length aryA
2032+
2033+ -- TODO: i == popCount bResult. Not sure if that would be faster.
2034+ -- Also iA is in some relation with bA'
2035+ let go_dWKA ! i ! iA ! bA' ! bResult ! nChanges
2036+ | bA' == 0 = pure (bResult, nChanges)
2037+ | otherwise = do
2038+ ! stA <- A. indexM aryA iA
2039+ case m .&. bB of
2040+ 0 -> do
2041+ A. write mary i stA
2042+ go_dWKA (i + 1 ) (iA + 1 ) nextBA' (bResult .|. m) nChanges
2043+ _ -> do
2044+ ! stB <- A. indexM aryB (sparseIndex bB m)
2045+ case go_differenceWithKey (nextShift s) f stA stB of
2046+ Empty -> go_dWKA i (iA + 1 ) nextBA' bResult (nChanges + 1 )
2047+ st -> do
2048+ A. write mary i st
2049+ let same = I # (Exts. reallyUnsafePtrEquality# st stA)
2050+ let nChanges' = nChanges + (1 - same)
2051+ go_dWKA (i + 1 ) (iA + 1 ) nextBA' (bResult .|. m) nChanges'
2052+ where
2053+ m = bA' .&. negate bA'
2054+ nextBA' = bA' .&. complement m
2055+
2056+ (bResult, nChanges) <- go_dWKA 0 0 bA 0 0
2057+ if nChanges == 0
2058+ then pure tA
2059+ else case popCount bResult of
2060+ 0 -> pure Empty
2061+ 1 -> do
2062+ l <- A. read mary 0
2063+ if isLeafOrCollision l
2064+ then pure l
2065+ else BitmapIndexed bResult <$> (A. unsafeFreeze =<< A. shrink mary 1 )
2066+ n -> bitmapIndexedOrFull bResult <$> (A. unsafeFreeze =<< A. shrink mary n)
2067+ {-# INLINABLE differenceWithKey #-}
2068+
2069+ -- TODO: This could be faster if we would keep track of which elements of ary2
2070+ -- we've already matched. Those could be skipped when we check the following
2071+ -- elements of ary1.
2072+ -- TODO: Return tA when the array is unchanged.
2073+ differenceWithKey_Collisions :: Eq k => (k -> v -> w -> Maybe v ) -> Word -> A. Array (Leaf k v ) -> HashMap k v -> Word -> A. Array (Leaf k w ) -> HashMap k v
2074+ differenceWithKey_Collisions f ! hA ! aryA ! tA ! hB ! aryB
2075+ | hA == hB =
2076+ let f' l@ (L kA vA) =
2077+ lookupInArrayCont
2078+ (\ _ -> Just l)
2079+ (\ vB _ -> L kA <$> f kA vA vB)
2080+ kA aryB
2081+ ary = A. mapMaybe f' aryA
2082+ in case A. length ary of
2083+ 0 -> Empty
2084+ 1 -> case A. index# ary 0 of
2085+ (# l # ) -> Leaf hA l
2086+ _ -> Collision hA ary
2087+ | otherwise = tA
2088+ {-# INLINABLE differenceWithKey_Collisions #-}
2089+
19282090-- | \(O(n \log m)\) Intersection of two maps. Return elements of the first
19292091-- map for keys existing in the second.
19302092intersection :: Eq k => HashMap k v -> HashMap k w -> HashMap k v
0 commit comments