-------------------------------------------------------------------------------- {-| Module : IntBag Copyright : (c) Daan Leijen 2002 License : BSD-style Maintainer : daan@cs.uu.nl Stability : provisional Portability : portable An efficient implementation of bags of integers on top of the "IntMap" module. Many operations have a worst-case complexity of /O(min(n,W))/. This means that the operation can become linear in the number of elements with a maximum of /W/ -- the number of bits in an 'Int' (32 or 64). For more information, see the references in the "IntMap" module. -} ---------------------------------------------------------------------------------} module UU.DData.IntBag ( -- * Bag type IntBag -- instance Eq,Show -- * Operators , (\\) -- *Query , isEmpty , size , distinctSize , member , occur , subset , properSubset -- * Construction , empty , single , insert , insertMany , delete , deleteAll -- * Combine , union , difference , intersection , unions -- * Filter , filter , partition -- * Fold , fold , foldOccur -- * Conversion , elems -- ** List , toList , fromList -- ** Ordered list , toAscList , fromAscList , fromDistinctAscList -- ** Occurrence lists , toOccurList , toAscOccurList , fromOccurList , fromAscOccurList -- ** IntMap , toMap , fromMap , fromOccurMap -- * Debugging , showTree , showTreeWith ) where import Prelude hiding (map,filter) import qualified Prelude (map,filter) import qualified UU.DData.IntMap as M {-------------------------------------------------------------------- Operators --------------------------------------------------------------------} infixl 9 \\ -- -- | /O(n+m)/. See 'difference'. (\\) :: IntBag -> IntBag -> IntBag b1 \\ b2 = difference b1 b2 {-------------------------------------------------------------------- IntBags are a simple wrapper around Maps, 'Map.Map' --------------------------------------------------------------------} -- | A bag of integers. newtype IntBag = IntBag (M.IntMap Int) {-------------------------------------------------------------------- Query --------------------------------------------------------------------} -- | /O(1)/. Is the bag empty? isEmpty :: IntBag -> Bool isEmpty (IntBag m) = M.isEmpty m -- | /O(n)/. Returns the number of distinct elements in the bag, ie. (@distinctSize bag == length (nub (toList bag))@). distinctSize :: IntBag -> Int distinctSize (IntBag m) = M.size m -- | /O(n)/. The number of elements in the bag. size :: IntBag -> Int size b = foldOccur (\x n m -> n+m) 0 b -- | /O(min(n,W))/. Is the element in the bag? member :: Int -> IntBag -> Bool member x m = (occur x m > 0) -- | /O(min(n,W))/. The number of occurrences of an element in the bag. occur :: Int -> IntBag -> Int occur x (IntBag m) = case M.lookup x m of Nothing -> 0 Just n -> n -- | /O(n+m)/. Is this a subset of the bag? subset :: IntBag -> IntBag -> Bool subset (IntBag m1) (IntBag m2) = M.subsetBy (<=) m1 m2 -- | /O(n+m)/. Is this a proper subset? (ie. a subset and not equal) properSubset :: IntBag -> IntBag -> Bool properSubset b1 b2 = subset b1 b2 && (b1 /= b2) {-------------------------------------------------------------------- Construction --------------------------------------------------------------------} -- | /O(1)/. Create an empty bag. empty :: IntBag empty = IntBag (M.empty) -- | /O(1)/. Create a singleton bag. single :: Int -> IntBag single x = IntBag (M.single x 0) {-------------------------------------------------------------------- Insertion, Deletion --------------------------------------------------------------------} -- | /O(min(n,W))/. Insert an element in the bag. insert :: Int -> IntBag -> IntBag insert x (IntBag m) = IntBag (M.insertWith (+) x 1 m) -- | /O(min(n,W))/. The expression (@insertMany x count bag@) -- inserts @count@ instances of @x@ in the bag @bag@. insertMany :: Int -> Int -> IntBag -> IntBag insertMany x count (IntBag m) = IntBag (M.insertWith (+) x count m) -- | /O(min(n,W))/. Delete a single element. delete :: Int -> IntBag -> IntBag delete x (IntBag m) = IntBag (M.updateWithKey f x m) where f x n | n > 0 = Just (n-1) | otherwise = Nothing -- | /O(min(n,W))/. Delete all occurrences of an element. deleteAll :: Int -> IntBag -> IntBag deleteAll x (IntBag m) = IntBag (M.delete x m) {-------------------------------------------------------------------- Combine --------------------------------------------------------------------} -- | /O(n+m)/. Union of two bags. The union adds the elements together. -- -- > IntBag\> union (fromList [1,1,2]) (fromList [1,2,2,3]) -- > {1,1,1,2,2,2,3} union :: IntBag -> IntBag -> IntBag union (IntBag t1) (IntBag t2) = IntBag (M.unionWith (+) t1 t2) -- | /O(n+m)/. Intersection of two bags. -- -- > IntBag\> intersection (fromList [1,1,2]) (fromList [1,2,2,3]) -- > {1,2} intersection :: IntBag -> IntBag -> IntBag intersection (IntBag t1) (IntBag t2) = IntBag (M.intersectionWith min t1 t2) -- | /O(n+m)/. Difference between two bags. -- -- > IntBag\> difference (fromList [1,1,2]) (fromList [1,2,2,3]) -- > {1} difference :: IntBag -> IntBag -> IntBag difference (IntBag t1) (IntBag t2) = IntBag (M.differenceWithKey f t1 t2) where f x n m | n-m > 0 = Just (n-m) | otherwise = Nothing -- | The union of a list of bags. unions :: [IntBag] -> IntBag unions bags = IntBag (M.unions [m | IntBag m <- bags]) {-------------------------------------------------------------------- Filter and partition --------------------------------------------------------------------} -- | /O(n)/. Filter all elements that satisfy some predicate. filter :: (Int -> Bool) -> IntBag -> IntBag filter p (IntBag m) = IntBag (M.filterWithKey (\x n -> p x) m) -- | /O(n)/. Partition the bag according to some predicate. partition :: (Int -> Bool) -> IntBag -> (IntBag,IntBag) partition p (IntBag m) = (IntBag l,IntBag r) where (l,r) = M.partitionWithKey (\x n -> p x) m {-------------------------------------------------------------------- Fold --------------------------------------------------------------------} -- | /O(n)/. Fold over each element in the bag. fold :: (Int -> b -> b) -> b -> IntBag -> b fold f z (IntBag m) = M.foldWithKey apply z m where apply x n z | n > 0 = apply x (n-1) (f x z) | otherwise = z -- | /O(n)/. Fold over all occurrences of an element at once. -- In a call (@foldOccur f z bag@), the function @f@ takes -- the element first and than the occur count. foldOccur :: (Int -> Int -> b -> b) -> b -> IntBag -> b foldOccur f z (IntBag m) = M.foldWithKey f z m {-------------------------------------------------------------------- List variations --------------------------------------------------------------------} -- | /O(n)/. The list of elements. elems :: IntBag -> [Int] elems s = toList s {-------------------------------------------------------------------- Lists --------------------------------------------------------------------} -- | /O(n)/. Create a list with all elements. toList :: IntBag -> [Int] toList s = toAscList s -- | /O(n)/. Create an ascending list of all elements. toAscList :: IntBag -> [Int] toAscList (IntBag m) = [y | (x,n) <- M.toAscList m, y <- replicate n x] -- | /O(n*min(n,W))/. Create a bag from a list of elements. fromList :: [Int] -> IntBag fromList xs = IntBag (M.fromListWith (+) [(x,1) | x <- xs]) -- | /O(n*min(n,W))/. Create a bag from an ascending list. fromAscList :: [Int] -> IntBag fromAscList xs = IntBag (M.fromAscListWith (+) [(x,1) | x <- xs]) -- | /O(n*min(n,W))/. Create a bag from an ascending list of distinct elements. fromDistinctAscList :: [Int] -> IntBag fromDistinctAscList xs = IntBag (M.fromDistinctAscList [(x,1) | x <- xs]) -- | /O(n)/. Create a list of element\/occurrence pairs. toOccurList :: IntBag -> [(Int,Int)] toOccurList b = toAscOccurList b -- | /O(n)/. Create an ascending list of element\/occurrence pairs. toAscOccurList :: IntBag -> [(Int,Int)] toAscOccurList (IntBag m) = M.toAscList m -- | /O(n*min(n,W))/. Create a bag from a list of element\/occurrence pairs. fromOccurList :: [(Int,Int)] -> IntBag fromOccurList xs = IntBag (M.fromListWith (+) (Prelude.filter (\(x,i) -> i > 0) xs)) -- | /O(n*min(n,W))/. Create a bag from an ascending list of element\/occurrence pairs. fromAscOccurList :: [(Int,Int)] -> IntBag fromAscOccurList xs = IntBag (M.fromAscListWith (+) (Prelude.filter (\(x,i) -> i > 0) xs)) {-------------------------------------------------------------------- Maps --------------------------------------------------------------------} -- | /O(1)/. Convert to an 'IntMap.IntMap' from elements to number of occurrences. toMap :: IntBag -> M.IntMap Int toMap (IntBag m) = m -- | /O(n)/. Convert a 'IntMap.IntMap' from elements to occurrences into a bag. fromMap :: M.IntMap Int -> IntBag fromMap m = IntBag (M.filter (>0) m) -- | /O(1)/. Convert a 'IntMap.IntMap' from elements to occurrences into a bag. -- Assumes that the 'IntMap.IntMap' contains only elements that occur at least once. fromOccurMap :: M.IntMap Int -> IntBag fromOccurMap m = IntBag m {-------------------------------------------------------------------- Eq, Ord --------------------------------------------------------------------} instance Eq (IntBag) where (IntBag m1) == (IntBag m2) = (m1==m2) (IntBag m1) /= (IntBag m2) = (m1/=m2) {-------------------------------------------------------------------- Show --------------------------------------------------------------------} instance Show (IntBag) where showsPrec d b = showSet (toAscList b) showSet :: Show a => [a] -> ShowS showSet [] = showString "{}" showSet (x:xs) = showChar '{' . shows x . showTail xs where showTail [] = showChar '}' showTail (x:xs) = showChar ',' . shows x . showTail xs {-------------------------------------------------------------------- Debugging --------------------------------------------------------------------} -- | /O(n)/. Show the tree structure that implements the 'IntBag'. The tree -- is shown as a compressed and /hanging/. showTree :: IntBag -> String showTree bag = showTreeWith True False bag -- | /O(n)/. The expression (@showTreeWith hang wide map@) shows -- the tree that implements the bag. The tree is shown /hanging/ when @hang@ is @True@ -- and otherwise as a /rotated/ tree. When @wide@ is @True@ an extra wide version -- is shown. showTreeWith :: Bool -> Bool -> IntBag -> String showTreeWith hang wide (IntBag m) = M.showTreeWith hang wide m