From 02f399759de0e711f6ae4983be2fc3d2f46579ef Mon Sep 17 00:00:00 2001 From: Alexandre Esteves Date: Sun, 21 Jul 2019 01:32:31 +0100 Subject: [PATCH 1/5] Add 'compose' for maps --- containers-tests/tests/intmap-properties.hs | 5 +++++ containers-tests/tests/map-properties.hs | 5 +++++ containers/src/Data/IntMap/Internal.hs | 22 +++++++++++++++++++ containers/src/Data/IntMap/Lazy.hs | 3 +++ containers/src/Data/IntMap/Strict.hs | 3 +++ containers/src/Data/IntMap/Strict/Internal.hs | 22 +++++++++++++++++++ containers/src/Data/Map/Internal.hs | 22 +++++++++++++++++++ containers/src/Data/Map/Lazy.hs | 3 +++ containers/src/Data/Map/Strict.hs | 3 +++ containers/src/Data/Map/Strict/Internal.hs | 22 +++++++++++++++++++ 10 files changed, 110 insertions(+) diff --git a/containers-tests/tests/intmap-properties.hs b/containers-tests/tests/intmap-properties.hs index 86eb62764..99e784924 100644 --- a/containers-tests/tests/intmap-properties.hs +++ b/containers-tests/tests/intmap-properties.hs @@ -8,6 +8,7 @@ import Data.IntMap.Lazy as Data.IntMap hiding (showTree) import Data.IntMap.Internal.Debug (showTree) import IntMapValidity (valid) +import Control.Monad ((>=>)) import Data.Monoid import Data.Maybe hiding (mapMaybe) import qualified Data.Maybe as Maybe (mapMaybe) @@ -163,6 +164,7 @@ main = defaultMain , testProperty "lookupLE" prop_lookupLE , testProperty "lookupGE" prop_lookupGE , testProperty "disjoint" prop_disjoint + , testProperty "compose" prop_compose , testProperty "lookupMin" prop_lookupMin , testProperty "lookupMax" prop_lookupMax , testProperty "findMin" prop_findMin @@ -884,6 +886,9 @@ prop_intersectionWithKeyModel xs ys prop_disjoint :: UMap -> UMap -> Property prop_disjoint m1 m2 = disjoint m1 m2 === null (intersection m1 m2) +prop_compose :: IMap -> IMap -> Int -> Property +prop_compose bc ab k = (compose bc ab !? k) === (flip lookup ab >=> flip lookup bc) k + -- 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 c1bff0168..48ca9dfef 100644 --- a/containers-tests/tests/map-properties.hs +++ b/containers-tests/tests/map-properties.hs @@ -11,6 +11,7 @@ import Data.Map.Internal (Map (..), link2, link, bin) import Data.Map.Internal.Debug (showTree, showTreeWith, balanced) import Control.Applicative (Const(Const, getConst), pure, (<$>), (<*>)) +import Control.Monad ((>=>)) import Data.Functor.Identity (Identity(runIdentity)) import Data.Monoid import Data.Maybe hiding (mapMaybe) @@ -173,6 +174,7 @@ main = defaultMain , testProperty "intersectionWithKey" prop_intersectionWithKey , testProperty "intersectionWithKeyModel" prop_intersectionWithKeyModel , testProperty "disjoint" prop_disjoint + , testProperty "compose" prop_compose , testProperty "differenceMerge" prop_differenceMerge , testProperty "unionWithKeyMerge" prop_unionWithKeyMerge , testProperty "mergeWithKey model" prop_mergeWithKeyModel @@ -1074,6 +1076,9 @@ prop_intersectionWithKeyModel xs ys prop_disjoint :: UMap -> UMap -> Property prop_disjoint m1 m2 = disjoint m1 m2 === null (intersection m1 m2) +prop_compose :: IMap -> IMap -> Int -> Property +prop_compose bc ab k = (compose bc ab !? k) === (flip lookup ab >=> flip lookup bc) k + 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 9cf26968a..8ab212a12 100644 --- a/containers/src/Data/IntMap/Internal.hs +++ b/containers/src/Data/IntMap/Internal.hs @@ -131,6 +131,9 @@ module Data.IntMap.Internal ( , intersectionWith , intersectionWithKey + -- ** Compose + , compose + -- ** General combining function , SimpleWhenMissing , SimpleWhenMatched @@ -761,6 +764,25 @@ disjoint t1@(Bin p1 m1 l1 r1) t2@(Bin p2 m2 l2 r2) | zero p1 m2 = disjoint t1 l2 | otherwise = disjoint t1 r2 +{-------------------------------------------------------------------- + Compose +--------------------------------------------------------------------} +-- | /O(|ab|*log(|bc|))/. Relate the keys of one map to the values of +-- the other, by using the values of the former as keys for lookups +-- on the later. +-- +-- > compose (fromList [('a', "A"), ('b', "B")]) (fromList [(1,'a'),(2,'b'),(3,'z')]) = fromList [(1,"A"),(2,"B")] +-- +-- @ +-- ('compose' bc ab '!?') = (bc '!?') <=< (ab '!?') +-- @ +-- +-- @since UNRELEASED +compose :: IntMap c -> IntMap Int -> IntMap c +compose bc ab = if null bc + then empty + else mapMaybe (bc !?) ab + {-------------------------------------------------------------------- Construction --------------------------------------------------------------------} diff --git a/containers/src/Data/IntMap/Lazy.hs b/containers/src/Data/IntMap/Lazy.hs index 8d4ce8c82..3f38e3c29 100644 --- a/containers/src/Data/IntMap/Lazy.hs +++ b/containers/src/Data/IntMap/Lazy.hs @@ -146,6 +146,9 @@ module Data.IntMap.Lazy ( -- ** Disjoint , disjoint + -- ** Compose + , compose + -- ** Universal combining function , mergeWithKey diff --git a/containers/src/Data/IntMap/Strict.hs b/containers/src/Data/IntMap/Strict.hs index ef732c2e2..36bbad1e2 100644 --- a/containers/src/Data/IntMap/Strict.hs +++ b/containers/src/Data/IntMap/Strict.hs @@ -165,6 +165,9 @@ module Data.IntMap.Strict ( -- ** Disjoint , disjoint + -- ** Compose + , compose + -- ** Universal combining function , mergeWithKey diff --git a/containers/src/Data/IntMap/Strict/Internal.hs b/containers/src/Data/IntMap/Strict/Internal.hs index e7345418f..5e7b94b52 100644 --- a/containers/src/Data/IntMap/Strict/Internal.hs +++ b/containers/src/Data/IntMap/Strict/Internal.hs @@ -162,6 +162,9 @@ module Data.IntMap.Strict.Internal ( -- ** Disjoint , disjoint + -- ** Compose + , compose + -- ** Universal combining function , mergeWithKey @@ -717,6 +720,25 @@ intersectionWithKey :: (Key -> a -> b -> c) -> IntMap a -> IntMap b -> IntMap c intersectionWithKey f m1 m2 = mergeWithKey' bin (\(Tip k1 x1) (Tip _k2 x2) -> Tip k1 $! f k1 x1 x2) (const Nil) (const Nil) m1 m2 +{-------------------------------------------------------------------- + Compose +--------------------------------------------------------------------} +-- | /O(|ab|*log(|bc|))/. Relate the keys of one map to the values of +-- the other, by using the values of the former as keys for lookups +-- on the later. +-- +-- > compose (fromList [('a', "A"), ('b', "B")]) (fromList [(1,'a'),(2,'b'),(3,'z')]) = fromList [(1,"A"),(2,"B")] +-- +-- @ +-- ('compose' bc ab '!?') = (bc '!?') <=< (ab '!?') +-- @ +-- +-- @since UNRELEASED +compose :: IntMap c -> IntMap Int -> IntMap c +compose bc ab = if null bc + then empty + else mapMaybe (bc !?) ab + {-------------------------------------------------------------------- MergeWithKey --------------------------------------------------------------------} diff --git a/containers/src/Data/Map/Internal.hs b/containers/src/Data/Map/Internal.hs index 32c4e08bc..7b89d21c4 100644 --- a/containers/src/Data/Map/Internal.hs +++ b/containers/src/Data/Map/Internal.hs @@ -192,6 +192,9 @@ module Data.Map.Internal ( -- ** Disjoint , disjoint + -- ** Compose + , compose + -- ** General combining function , SimpleWhenMissing , SimpleWhenMatched @@ -2085,6 +2088,25 @@ disjoint (Bin _ k _ l r) t where (lt,found,gt) = splitMember k t +{-------------------------------------------------------------------- + Compose +--------------------------------------------------------------------} +-- | /O(|ab|*log(|bc|))/. Relate the keys of one map to the values of +-- the other, by using the values of the former as keys for lookups +-- on the later. +-- +-- > compose (fromList [('a', "A"), ('b', "B")]) (fromList [(1,'a'),(2,'b'),(3,'z')]) = fromList [(1,"A"),(2,"B")] +-- +-- @ +-- ('compose' bc ab '!?') = (bc '!?') <=< (ab '!?') +-- @ +-- +-- @since UNRELEASED +compose :: Ord b => Map b c -> Map a b -> Map a c +compose bc ab = if null bc + then empty + else mapMaybe (bc !?) ab + #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 37ae931e6..6b05603a1 100644 --- a/containers/src/Data/Map/Lazy.hs +++ b/containers/src/Data/Map/Lazy.hs @@ -166,6 +166,9 @@ module Data.Map.Lazy ( -- ** Disjoint , disjoint + -- ** Compose + , compose + -- ** 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 a73078413..8eea8c329 100644 --- a/containers/src/Data/Map/Strict.hs +++ b/containers/src/Data/Map/Strict.hs @@ -182,6 +182,9 @@ module Data.Map.Strict -- ** Disjoint , disjoint + -- ** Compose + , compose + -- ** 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 a161b2f49..b71c9a223 100644 --- a/containers/src/Data/Map/Strict/Internal.hs +++ b/containers/src/Data/Map/Strict/Internal.hs @@ -145,6 +145,9 @@ module Data.Map.Strict.Internal -- ** Disjoint , disjoint + -- ** Compose + , compose + -- ** General combining function , SimpleWhenMissing , SimpleWhenMatched @@ -1200,6 +1203,25 @@ forceMaybe Nothing = Nothing forceMaybe m@(Just !_) = m {-# INLINE forceMaybe #-} +{-------------------------------------------------------------------- + Compose +--------------------------------------------------------------------} +-- | /O(|ab|*log(|bc|))/. Relate the keys of one map to the values of +-- the other, by using the values of the former as keys for lookups +-- on the later. +-- +-- > compose (fromList [('a', "A"), ('b', "B")]) (fromList [(1,'a'),(2,'b'),(3,'z')]) = fromList [(1,"A"),(2,"B")] +-- +-- @ +-- ('compose' bc ab '!?') = (bc '!?') <=< (ab '!?') +-- @ +-- +-- @since UNRELEASED +compose :: Ord b => Map b c -> Map a b -> Map a c +compose bc ab = if null bc + then empty + else mapMaybe (bc !?) ab + {-------------------------------------------------------------------- MergeWithKey --------------------------------------------------------------------} From a7534a9640896f610ffcdf6e61766b7e2920c008 Mon Sep 17 00:00:00 2001 From: Alexandre Esteves Date: Mon, 14 Oct 2019 10:01:12 +0100 Subject: [PATCH 2/5] Fix typos --- containers/src/Data/IntMap/Internal.hs | 2 +- containers/src/Data/IntMap/Strict/Internal.hs | 2 +- containers/src/Data/Map/Internal.hs | 2 +- containers/src/Data/Map/Strict/Internal.hs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/containers/src/Data/IntMap/Internal.hs b/containers/src/Data/IntMap/Internal.hs index 8ab212a12..4293fbe1a 100644 --- a/containers/src/Data/IntMap/Internal.hs +++ b/containers/src/Data/IntMap/Internal.hs @@ -769,7 +769,7 @@ disjoint t1@(Bin p1 m1 l1 r1) t2@(Bin p2 m2 l2 r2) --------------------------------------------------------------------} -- | /O(|ab|*log(|bc|))/. Relate the keys of one map to the values of -- the other, by using the values of the former as keys for lookups --- on the later. +-- in the latter. -- -- > compose (fromList [('a', "A"), ('b', "B")]) (fromList [(1,'a'),(2,'b'),(3,'z')]) = fromList [(1,"A"),(2,"B")] -- diff --git a/containers/src/Data/IntMap/Strict/Internal.hs b/containers/src/Data/IntMap/Strict/Internal.hs index 5e7b94b52..c8845d5b3 100644 --- a/containers/src/Data/IntMap/Strict/Internal.hs +++ b/containers/src/Data/IntMap/Strict/Internal.hs @@ -725,7 +725,7 @@ intersectionWithKey f m1 m2 --------------------------------------------------------------------} -- | /O(|ab|*log(|bc|))/. Relate the keys of one map to the values of -- the other, by using the values of the former as keys for lookups --- on the later. +-- in the latter. -- -- > compose (fromList [('a', "A"), ('b', "B")]) (fromList [(1,'a'),(2,'b'),(3,'z')]) = fromList [(1,"A"),(2,"B")] -- diff --git a/containers/src/Data/Map/Internal.hs b/containers/src/Data/Map/Internal.hs index 7b89d21c4..2f9f21fad 100644 --- a/containers/src/Data/Map/Internal.hs +++ b/containers/src/Data/Map/Internal.hs @@ -2093,7 +2093,7 @@ disjoint (Bin _ k _ l r) t --------------------------------------------------------------------} -- | /O(|ab|*log(|bc|))/. Relate the keys of one map to the values of -- the other, by using the values of the former as keys for lookups --- on the later. +-- in the latter. -- -- > compose (fromList [('a', "A"), ('b', "B")]) (fromList [(1,'a'),(2,'b'),(3,'z')]) = fromList [(1,"A"),(2,"B")] -- diff --git a/containers/src/Data/Map/Strict/Internal.hs b/containers/src/Data/Map/Strict/Internal.hs index b71c9a223..82c879739 100644 --- a/containers/src/Data/Map/Strict/Internal.hs +++ b/containers/src/Data/Map/Strict/Internal.hs @@ -1208,7 +1208,7 @@ forceMaybe m@(Just !_) = m --------------------------------------------------------------------} -- | /O(|ab|*log(|bc|))/. Relate the keys of one map to the values of -- the other, by using the values of the former as keys for lookups --- on the later. +-- in the latter. -- -- > compose (fromList [('a', "A"), ('b', "B")]) (fromList [(1,'a'),(2,'b'),(3,'z')]) = fromList [(1,"A"),(2,"B")] -- From db55ad473f656765d757fde82ace804b168dd348 Mon Sep 17 00:00:00 2001 From: Alexandre Esteves Date: Mon, 14 Oct 2019 10:04:41 +0100 Subject: [PATCH 3/5] Cleanup --- containers-tests/tests/intmap-properties.hs | 4 ++-- containers-tests/tests/map-properties.hs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/containers-tests/tests/intmap-properties.hs b/containers-tests/tests/intmap-properties.hs index 99e784924..e47e494b5 100644 --- a/containers-tests/tests/intmap-properties.hs +++ b/containers-tests/tests/intmap-properties.hs @@ -8,7 +8,7 @@ import Data.IntMap.Lazy as Data.IntMap hiding (showTree) import Data.IntMap.Internal.Debug (showTree) import IntMapValidity (valid) -import Control.Monad ((>=>)) +import Control.Monad ((<=<)) import Data.Monoid import Data.Maybe hiding (mapMaybe) import qualified Data.Maybe as Maybe (mapMaybe) @@ -887,7 +887,7 @@ prop_disjoint :: UMap -> UMap -> Property prop_disjoint m1 m2 = disjoint m1 m2 === null (intersection m1 m2) prop_compose :: IMap -> IMap -> Int -> Property -prop_compose bc ab k = (compose bc ab !? k) === (flip lookup ab >=> flip lookup bc) k +prop_compose bc ab k = (compose bc ab !? k) === ((bc !?) <=< (ab !?)) k -- TODO: the second argument should be simply an 'IntSet', but that -- runs afoul of our orphan instance. diff --git a/containers-tests/tests/map-properties.hs b/containers-tests/tests/map-properties.hs index 48ca9dfef..9f81c42c0 100644 --- a/containers-tests/tests/map-properties.hs +++ b/containers-tests/tests/map-properties.hs @@ -11,7 +11,7 @@ import Data.Map.Internal (Map (..), link2, link, bin) import Data.Map.Internal.Debug (showTree, showTreeWith, balanced) import Control.Applicative (Const(Const, getConst), pure, (<$>), (<*>)) -import Control.Monad ((>=>)) +import Control.Monad ((<=<)) import Data.Functor.Identity (Identity(runIdentity)) import Data.Monoid import Data.Maybe hiding (mapMaybe) @@ -1077,7 +1077,7 @@ prop_disjoint :: UMap -> UMap -> Property prop_disjoint m1 m2 = disjoint m1 m2 === null (intersection m1 m2) prop_compose :: IMap -> IMap -> Int -> Property -prop_compose bc ab k = (compose bc ab !? k) === (flip lookup ab >=> flip lookup bc) k +prop_compose bc ab k = (compose bc ab !? k) === ((bc !?) <=< (ab !?)) k prop_mergeWithKeyModel :: [(Int,Int)] -> [(Int,Int)] -> Bool prop_mergeWithKeyModel xs ys From 0c5c1e18cb96c170e1507dcf212f49ca6287e947 Mon Sep 17 00:00:00 2001 From: Alexandre Esteves Date: Sun, 22 Dec 2019 22:52:31 +0000 Subject: [PATCH 4/5] Force second argument of 'compose' --- containers/src/Data/IntMap/Internal.hs | 6 +++--- containers/src/Data/IntMap/Strict/Internal.hs | 6 +++--- containers/src/Data/Map/Internal.hs | 6 +++--- containers/src/Data/Map/Strict/Internal.hs | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/containers/src/Data/IntMap/Internal.hs b/containers/src/Data/IntMap/Internal.hs index 4293fbe1a..e0d572482 100644 --- a/containers/src/Data/IntMap/Internal.hs +++ b/containers/src/Data/IntMap/Internal.hs @@ -779,9 +779,9 @@ disjoint t1@(Bin p1 m1 l1 r1) t2@(Bin p2 m2 l2 r2) -- -- @since UNRELEASED compose :: IntMap c -> IntMap Int -> IntMap c -compose bc ab = if null bc - then empty - else mapMaybe (bc !?) ab +compose bc !ab + | null bc = empty + | otherwise = mapMaybe (bc !?) ab {-------------------------------------------------------------------- Construction diff --git a/containers/src/Data/IntMap/Strict/Internal.hs b/containers/src/Data/IntMap/Strict/Internal.hs index c8845d5b3..e7739d678 100644 --- a/containers/src/Data/IntMap/Strict/Internal.hs +++ b/containers/src/Data/IntMap/Strict/Internal.hs @@ -735,9 +735,9 @@ intersectionWithKey f m1 m2 -- -- @since UNRELEASED compose :: IntMap c -> IntMap Int -> IntMap c -compose bc ab = if null bc - then empty - else mapMaybe (bc !?) ab +compose bc !ab + | null bc = empty + | otherwise = mapMaybe (bc !?) ab {-------------------------------------------------------------------- MergeWithKey diff --git a/containers/src/Data/Map/Internal.hs b/containers/src/Data/Map/Internal.hs index 2f9f21fad..879c6933a 100644 --- a/containers/src/Data/Map/Internal.hs +++ b/containers/src/Data/Map/Internal.hs @@ -2103,9 +2103,9 @@ disjoint (Bin _ k _ l r) t -- -- @since UNRELEASED compose :: Ord b => Map b c -> Map a b -> Map a c -compose bc ab = if null bc - then empty - else mapMaybe (bc !?) ab +compose bc !ab + | null bc = empty + | otherwise = mapMaybe (bc !?) ab #if !MIN_VERSION_base (4,8,0) -- | The identity type. diff --git a/containers/src/Data/Map/Strict/Internal.hs b/containers/src/Data/Map/Strict/Internal.hs index 82c879739..450dc0976 100644 --- a/containers/src/Data/Map/Strict/Internal.hs +++ b/containers/src/Data/Map/Strict/Internal.hs @@ -1218,9 +1218,9 @@ forceMaybe m@(Just !_) = m -- -- @since UNRELEASED compose :: Ord b => Map b c -> Map a b -> Map a c -compose bc ab = if null bc - then empty - else mapMaybe (bc !?) ab +compose bc !ab + | null bc = empty + | otherwise = mapMaybe (bc !?) ab {-------------------------------------------------------------------- MergeWithKey From 2bf99eb833f031e1592e2990b3b0bbaeca9f10f8 Mon Sep 17 00:00:00 2001 From: Alexandre Esteves Date: Sun, 22 Dec 2019 22:57:08 +0000 Subject: [PATCH 5/5] Fix bounds --- containers/src/Data/IntMap/Internal.hs | 2 +- containers/src/Data/IntMap/Strict/Internal.hs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/containers/src/Data/IntMap/Internal.hs b/containers/src/Data/IntMap/Internal.hs index e0d572482..65444511f 100644 --- a/containers/src/Data/IntMap/Internal.hs +++ b/containers/src/Data/IntMap/Internal.hs @@ -767,7 +767,7 @@ disjoint t1@(Bin p1 m1 l1 r1) t2@(Bin p2 m2 l2 r2) {-------------------------------------------------------------------- Compose --------------------------------------------------------------------} --- | /O(|ab|*log(|bc|))/. Relate the keys of one map to the values of +-- | /O(|ab|*min(|bc|,W))/. Relate the keys of one map to the values of -- the other, by using the values of the former as keys for lookups -- in the latter. -- diff --git a/containers/src/Data/IntMap/Strict/Internal.hs b/containers/src/Data/IntMap/Strict/Internal.hs index e7739d678..e3bc76d98 100644 --- a/containers/src/Data/IntMap/Strict/Internal.hs +++ b/containers/src/Data/IntMap/Strict/Internal.hs @@ -723,7 +723,7 @@ intersectionWithKey f m1 m2 {-------------------------------------------------------------------- Compose --------------------------------------------------------------------} --- | /O(|ab|*log(|bc|))/. Relate the keys of one map to the values of +-- | /O(|ab|*min(|bc|,W))/. Relate the keys of one map to the values of -- the other, by using the values of the former as keys for lookups -- in the latter. --