Skip to content

Commit

Permalink
Add min/max functions for Data.Multimap and Data.Multimap.Set
Browse files Browse the repository at this point in the history
  • Loading branch information
zliu41 committed Feb 22, 2020
1 parent 7b7fda0 commit 9595cee
Show file tree
Hide file tree
Showing 10 changed files with 165 additions and 8 deletions.
4 changes: 4 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog for multi-containers

## 0.1.1

- Add min/max functions for `Data.Multimap` and `Data.Multimap.Set`.

## 0.1.0.2

- Remove redundant constraints
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright Ziyang Liu (c) 2019
Copyright Ziyang Liu (c) 2019-2020.

All rights reserved.

Expand Down
4 changes: 4 additions & 0 deletions TestGen.hs
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
#!/usr/bin/env stack
-- stack --resolver lts-15.0 script --package filepath --package directory --package extra
-- To run: ./TestGen.hs

module TestGen (main) where

import Data.List.Extra (replace, stripPrefix, trim)
Expand Down
2 changes: 1 addition & 1 deletion default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ let
}:
mkDerivation {
pname = "multi-containers";
version = "0.1.0.2";
version = "0.1.1";
src = ./.;
libraryHaskellDepends = [ base containers ];
testHaskellDepends = [ base containers hspec ];
Expand Down
10 changes: 7 additions & 3 deletions multi-containers.cabal
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cabal-version: 2.4

name: multi-containers
version: 0.1.0.2
version: 0.1.1
synopsis: A few multimap variants.
description: A library that provides a few multimap variants.
category: Data Structures
Expand All @@ -13,7 +13,7 @@ copyright: 2019-2020 Ziyang Liu
license: BSD-3-Clause
license-file: LICENSE
build-type: Simple
tested-with: GHC==8.8.1, GHC==8.6.5, GHC==8.4.4, GHC==8.2.2
tested-with: GHC==8.10.1, GHC==8.8.2, GHC==8.6.5, GHC==8.4.4

extra-source-files:
ChangeLog.md
Expand All @@ -30,6 +30,8 @@ library
Data.Multimap.Table
other-modules:
Paths_multi_containers
autogen-modules:
Paths_multi_containers
hs-source-dirs:
src
ghc-options: -Wall
Expand All @@ -46,13 +48,15 @@ test-suite hspec
Data.Multimap.TableSpec
Data.MultimapSpec
Paths_multi_containers
autogen-modules:
Paths_multi_containers
hs-source-dirs:
test/hspec
ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
build-depends:
base >=4.7 && <5
, containers >=0.5.10.2 && <0.7
, hspec >=2.4.8 && <2.8
, hspec >=2.4.8 && <3
, multi-containers
default-language: Haskell2010
build-tool-depends: hspec-discover:hspec-discover == 2.*
61 changes: 60 additions & 1 deletion src/Data/Multimap.hs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,14 @@ module Data.Multimap (
, mapMaybeWithKey
, mapEither
, mapEitherWithKey

-- * Min\/Max
, lookupMin
, lookupMax
, lookupLT
, lookupGT
, lookupLE
, lookupGE
) where

import Control.Arrow ((&&&))
Expand All @@ -150,7 +158,6 @@ import qualified Data.List.NonEmpty as Nel
import Data.Map.Lazy (Map)
import qualified Data.Map.Lazy as Map
import qualified Data.Maybe as Maybe
import Data.Semigroup (Semigroup, (<>))
import Data.Set (Set)

import Prelude hiding (filter, foldl, foldr, lookup, map, null)
Expand Down Expand Up @@ -743,3 +750,55 @@ mapEitherWithKey f (Multimap (m, _)) =
$ Map.mapWithKey g m
where
g k as = Either.partitionEithers $ fmap (f k) (Nel.toList as)

------------------------------------------------------------------------------

-- | /O(log n)/. Return the smallest key and the associated values. Returns 'Nothing'
-- if the map is empty.
--
-- > lookupMin (fromList [(1,'a'),(1,'c'),(2,'c')]) === Just (1, NonEmpty.fromList "ac")
-- > lookupMin (empty :: Multimap Int Char) === Nothing
lookupMin :: Multimap k a -> Maybe (k, NonEmpty a)
lookupMin (Multimap (m, _)) = Map.lookupMin m

-- | /O(log n)/. Return the largest key and the associated values. Returns 'Nothing'
-- if the map is empty.
--
-- > lookupMax (fromList [(1,'a'),(1,'c'),(2,'c')]) === Just (2, NonEmpty.fromList "c")
-- > lookupMax (empty :: Multimap Int Char) === Nothing
lookupMax :: Multimap k a -> Maybe (k, NonEmpty a)
lookupMax (Multimap (m, _)) = Map.lookupMax m

-- | /O(log n)/. Return the largest key smaller than the given one, and the associated
-- values, if exist.
--
-- > lookupLT 1 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Nothing
-- > lookupLT 4 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Just (3, NonEmpty.fromList "bc")
lookupLT :: Ord k => k -> Multimap k a -> Maybe (k, NonEmpty a)
lookupLT k (Multimap (m, _)) = Map.lookupLT k m

-- | /O(log n)/. Return the smallest key larger than the given one, and the associated
-- values, if exist.
--
-- > lookupGT 5 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Nothing
-- > lookupGT 2 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Just (3, NonEmpty.fromList "bc")
lookupGT :: Ord k => k -> Multimap k a -> Maybe (k, NonEmpty a)
lookupGT k (Multimap (m, _)) = Map.lookupGT k m

-- | /O(log n)/. Return the largest key smaller than or equal to the given one, and the associated
-- values, if exist.
--
-- > lookupLE 0 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Nothing
-- > lookupLE 1 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Just (1, NonEmpty.fromList "a")
-- > lookupLE 4 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Just (3, NonEmpty.fromList "bc")
lookupLE :: Ord k => k -> Multimap k a -> Maybe (k, NonEmpty a)
lookupLE k (Multimap (m, _)) = Map.lookupLE k m

-- | /O(log n)/. Return the smallest key larger than or equal to the given one, and the associated
-- values, if exist.
--
-- > lookupGE 6 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Nothing
-- > lookupGE 5 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Just (5, NonEmpty.fromList "c")
-- > lookupGE 2 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Just (3, NonEmpty.fromList "bc")
lookupGE :: Ord k => k -> Multimap k a -> Maybe (k, NonEmpty a)
lookupGE k (Multimap (m, _)) = Map.lookupGE k m
61 changes: 60 additions & 1 deletion src/Data/Multimap/Set.hs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,14 @@ module Data.Multimap.Set (
, mapMaybeWithKey
, mapEither
, mapEitherWithKey

-- * Min\/Max
, lookupMin
, lookupMax
, lookupLT
, lookupGT
, lookupLE
, lookupGE
) where

import Prelude hiding (filter, foldl, foldr, lookup, map, null)
Expand All @@ -145,7 +153,6 @@ import Data.Functor.Classes
import Data.Map.Lazy (Map)
import qualified Data.Map.Lazy as Map
import qualified Data.Maybe as Maybe
import Data.Semigroup (Semigroup, (<>))
import Data.Set (Set)
import qualified Data.Set as Set

Expand Down Expand Up @@ -684,6 +691,58 @@ mapEitherWithKey f (SetMultimap (m, _)) =
where
g k = partitionEithers . Set.map (f k)

------------------------------------------------------------------------------

-- | /O(log n)/. Return the smallest key and the associated values. Returns 'Nothing'
-- if the map is empty.
--
-- > lookupMin (fromList [(1,'a'),(1,'c'),(2,'c')]) === Just (1, Set.fromList "ac")
-- > lookupMin (empty :: SetMultimap Int Char) === Nothing
lookupMin :: SetMultimap k a -> Maybe (k, Set a)
lookupMin (SetMultimap (m, _)) = Map.lookupMin m

-- | /O(log n)/. Return the largest key and the associated values. Returns 'Nothing'
-- if the map is empty.
--
-- > lookupMax (fromList [(1,'a'),(1,'c'),(2,'c')]) === Just (2, Set.fromList "c")
-- > lookupMax (empty :: SetMultimap Int Char) === Nothing
lookupMax :: SetMultimap k a -> Maybe (k, Set a)
lookupMax (SetMultimap (m, _)) = Map.lookupMax m

-- | /O(log n)/. Return the largest key smaller than the given one, and the associated
-- values, if exist.
--
-- > lookupLT 1 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Nothing
-- > lookupLT 4 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Just (3, Set.fromList "bc")
lookupLT :: Ord k => k -> SetMultimap k a -> Maybe (k, Set a)
lookupLT k (SetMultimap (m, _)) = Map.lookupLT k m

-- | /O(log n)/. Return the smallest key larger than the given one, and the associated
-- values, if exist.
--
-- > lookupGT 5 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Nothing
-- > lookupGT 2 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Just (3, Set.fromList "bc")
lookupGT :: Ord k => k -> SetMultimap k a -> Maybe (k, Set a)
lookupGT k (SetMultimap (m, _)) = Map.lookupGT k m

-- | /O(log n)/. Return the largest key smaller than or equal to the given one, and the associated
-- values, if exist.
--
-- > lookupLE 0 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Nothing
-- > lookupLE 1 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Just (1, Set.fromList "a")
-- > lookupLE 4 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Just (3, Set.fromList "bc")
lookupLE :: Ord k => k -> SetMultimap k a -> Maybe (k, Set a)
lookupLE k (SetMultimap (m, _)) = Map.lookupLE k m

-- | /O(log n)/. Return the smallest key larger than or equal to the given one, and the associated
-- values, if exist.
--
-- > lookupGE 6 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Nothing
-- > lookupGE 5 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Just (5, Set.fromList "c")
-- > lookupGE 2 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Just (3, Set.fromList "bc")
lookupGE :: Ord k => k -> SetMultimap k a -> Maybe (k, Set a)
lookupGE k (SetMultimap (m, _)) = Map.lookupGE k m

------------------------------------------------------------------------------
-- * Non exported functions
------------------------------------------------------------------------------
Expand Down
1 change: 0 additions & 1 deletion src/Data/Multimap/Table.hs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ import qualified Data.Foldable as Foldable
import Data.Map (Map)
import qualified Data.Map as Map
import qualified Data.Maybe as Maybe
import Data.Semigroup (Semigroup, (<>))
import Data.Set (Set)

import Prelude hiding (filter, foldl, foldr, lookup, map, null)
Expand Down
14 changes: 14 additions & 0 deletions test/hspec/Data/Multimap/SetSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,17 @@ spec = do
=== (fromList [(1,'a'),(2,'a')],fromList [(1,'c'),(2,'c')])
mapEitherWithKey (\k a -> if even k && a < 'b' then Left a else Right a) (fromList [(1,'a'),(1,'c'),(2,'a'),(2,'c')])
=== (fromList [(2,'a')],fromList [(1,'a'),(1,'c'),(2,'c')])
lookupMin (fromList [(1,'a'),(1,'c'),(2,'c')]) === Just (1, Set.fromList "ac")
lookupMin (empty :: SetMultimap Int Char) === Nothing
lookupMax (fromList [(1,'a'),(1,'c'),(2,'c')]) === Just (2, Set.fromList "c")
lookupMax (empty :: SetMultimap Int Char) === Nothing
lookupLT 1 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Nothing
lookupLT 4 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Just (3, Set.fromList "bc")
lookupGT 5 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Nothing
lookupGT 2 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Just (3, Set.fromList "bc")
lookupLE 0 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Nothing
lookupLE 1 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Just (1, Set.fromList "a")
lookupLE 4 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Just (3, Set.fromList "bc")
lookupGE 6 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Nothing
lookupGE 5 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Just (5, Set.fromList "c")
lookupGE 2 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Just (3, Set.fromList "bc")
14 changes: 14 additions & 0 deletions test/hspec/Data/MultimapSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,17 @@ spec = do
=== (fromList [(1,'a'),(2,'a')],fromList [(1,'c'),(2,'c')])
mapEitherWithKey (\k a -> if even k && a < 'b' then Left a else Right a) (fromList [(1,'a'),(1,'c'),(2,'a'),(2,'c')])
=== (fromList [(2,'a')],fromList [(1,'a'),(1,'c'),(2,'c')])
lookupMin (fromList [(1,'a'),(1,'c'),(2,'c')]) === Just (1, NonEmpty.fromList "ac")
lookupMin (empty :: Multimap Int Char) === Nothing
lookupMax (fromList [(1,'a'),(1,'c'),(2,'c')]) === Just (2, NonEmpty.fromList "c")
lookupMax (empty :: Multimap Int Char) === Nothing
lookupLT 1 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Nothing
lookupLT 4 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Just (3, NonEmpty.fromList "bc")
lookupGT 5 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Nothing
lookupGT 2 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Just (3, NonEmpty.fromList "bc")
lookupLE 0 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Nothing
lookupLE 1 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Just (1, NonEmpty.fromList "a")
lookupLE 4 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Just (3, NonEmpty.fromList "bc")
lookupGE 6 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Nothing
lookupGE 5 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Just (5, NonEmpty.fromList "c")
lookupGE 2 (fromList [(1,'a'),(3,'b'),(3,'c'),(5,'c')]) === Just (3, NonEmpty.fromList "bc")

0 comments on commit 9595cee

Please sign in to comment.