packages feed

lhc-0.10: src/HashSet.hs

module HashSet
    ( Hashable(..)
    , HashSet
    , empty
    , singleton
    , fromList
    , toList
    , union
    , insert
    , delete
    , member
    , difference
    , isSubsetOf
    , approxSuperset
    ) where

import qualified Data.Set as Set
import qualified Data.IntMap as IntMap
import Data.Maybe
import Prelude hiding (lookup)

import HashMap (Hashable(..), HashMap)
import qualified HashMap

newtype HashSet v = HashSet (IntMap.IntMap (Set.Set v))
    deriving (Eq, Ord)

instance (Show v, Hashable v) => Show (HashSet v) where
    showsPrec n = showsPrec n . toSet

toSet :: Hashable v => HashSet v -> Set.Set v
toSet (HashSet imap)
    = Set.unions (IntMap.elems imap)

empty :: HashSet v
empty = HashSet IntMap.empty

singleton :: (Hashable v) =>  v -> HashSet v
singleton v = HashSet (IntMap.singleton (hash v) (Set.singleton v))

fromList :: Hashable v => [v] -> HashSet v
fromList lst
    = HashSet $ IntMap.fromListWith Set.union [ (hash v, Set.singleton v) | v <- lst ]

toList :: Hashable v => HashSet v -> [v]
toList = Set.toList . toSet

union :: Hashable v => HashSet v -> HashSet v -> HashSet v
union (HashSet a) (HashSet b) = HashSet (IntMap.unionWith (Set.union) a b)

insert :: Hashable v => v -> HashSet v -> HashSet v
insert v (HashSet imap)
    = HashSet $ IntMap.insertWith Set.union (hash v) (Set.singleton v) imap

delete :: Hashable v => v -> HashSet v -> HashSet v
delete v (HashSet imap)
    = HashSet $ IntMap.update fn (hash v) imap
    where fn s = let s' = Set.delete v s
                 in if Set.null s' then Nothing else Just s'

member :: Hashable v => v -> HashSet v -> Bool
member v (HashSet imap)
    = case IntMap.lookup (hash v) imap of
        Nothing  -> False
        Just set -> v `Set.member` set

difference :: Hashable v => HashSet v -> HashSet v -> HashSet v
difference (HashSet a) (HashSet b)
    = HashSet $ IntMap.differenceWith worker a b
    where worker a' b' = let d = Set.difference a' b' in
                         if Set.null d then Nothing else Just d

isSubsetOf :: Hashable v => HashSet v -> HashSet v -> Bool
isSubsetOf (HashSet a) (HashSet b)
    = IntMap.isSubmapOfBy (Set.isSubsetOf) a b

approxSuperset :: Hashable v => HashSet v -> HashMap v k -> Bool
approxSuperset (HashSet a) b
    = let m = HashMap.unpack b
          a' = IntMap.difference a m
      in IntMap.size a' == IntMap.size a