X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=Data%2FIntSet.hs;h=16226082b7f98e8f8475456341746321dc3fd8c5;hb=7c0b04fd273621130062418bb764809c79488dd2;hp=c41ed179f36336a4da9fc6538e3569611ac03520;hpb=a5d8b45865712ab237eee066f37c667f3574f7ac;p=haskell-directory.git diff --git a/Data/IntSet.hs b/Data/IntSet.hs index c41ed17..1622608 100644 --- a/Data/IntSet.hs +++ b/Data/IntSet.hs @@ -10,10 +10,11 @@ -- -- An efficient implementation of integer sets. -- --- This module is intended to be imported @qualified@, to avoid name --- clashes with "Prelude" functions. eg. +-- Since many function names (but not the type name) clash with +-- "Prelude" names, this module is usually imported @qualified@, e.g. -- --- > import Data.IntSet as Set +-- > import Data.IntSet (IntSet) +-- > import qualified Data.IntSet as IntSet -- -- The implementation is based on /big-endian patricia trees/. This data -- structure performs especially well on binary operations like 'union' @@ -46,6 +47,7 @@ module Data.IntSet ( , null , size , member + , notMember , isSubsetOf , isProperSubsetOf @@ -66,6 +68,16 @@ module Data.IntSet ( , split , splitMember + -- * Min\/Max + , findMin + , findMax + , deleteMin + , deleteMax + , deleteFindMin + , deleteFindMax + , maxView + , minView + -- * Map , map @@ -91,9 +103,9 @@ module Data.IntSet ( import Prelude hiding (lookup,filter,foldr,foldl,null,map) import Data.Bits -import Data.Int import qualified Data.List as List +import Data.Monoid (Monoid(..)) import Data.Typeable {- @@ -104,13 +116,12 @@ import qualified List -} #if __GLASGOW_HASKELL__ -import Text.Read (Lexeme(Ident), lexP, parens, prec, readPrec) -import Data.Generics.Basics -import Data.Generics.Instances +import Text.Read +import Data.Generics.Basics (Data(..), mkNorepType) +import Data.Generics.Instances () #endif #if __GLASGOW_HASKELL__ >= 503 -import GHC.Word import GHC.Exts ( Word(..), Int(..), shiftRL# ) #elif __GLASGOW_HASKELL__ import Word @@ -155,10 +166,17 @@ m1 \\ m2 = difference m1 m2 data IntSet = Nil | Tip {-# UNPACK #-} !Int | Bin {-# UNPACK #-} !Prefix {-# UNPACK #-} !Mask !IntSet !IntSet +-- Invariant: Nil is never found as a child of Bin. + type Prefix = Int type Mask = Int +instance Monoid IntSet where + mempty = empty + mappend = union + mconcat = unions + #if __GLASGOW_HASKELL__ {-------------------------------------------------------------------- @@ -203,6 +221,10 @@ member x t Tip y -> (x==y) Nil -> False +-- | /O(min(n,W))/. Is the element not in the set? +notMember :: Int -> IntSet -> Bool +notMember k = not . member k + -- 'lookup' is used by 'intersection' for left-biasing lookup :: Int -> IntSet -> Maybe Int lookup k t @@ -446,7 +468,7 @@ partition pred t Nil -> (Nil,Nil) --- | /O(log n)/. The expression (@'split' x set@) is a pair @(set1,set2)@ +-- | /O(min(n,W))/. The expression (@'split' x set@) is a pair @(set1,set2)@ -- where all elements in @set1@ are lower than @x@ and all elements in -- @set2@ larger than @x@. -- @@ -455,22 +477,54 @@ split :: Int -> IntSet -> (IntSet,IntSet) split x t = case t of Bin p m l r - | zero x m -> let (lt,gt) = split x l in (lt,union gt r) - | otherwise -> let (lt,gt) = split x r in (union l lt,gt) + | m < 0 -> if x >= 0 then let (lt,gt) = split' x l in (union r lt, gt) + else let (lt,gt) = split' x r in (lt, union gt l) + -- handle negative numbers. + | otherwise -> split' x t + Tip y + | x>y -> (t,Nil) + | x (Nil,t) + | otherwise -> (Nil,Nil) + Nil -> (Nil, Nil) + +split' :: Int -> IntSet -> (IntSet,IntSet) +split' x t + = case t of + Bin p m l r + | match x p m -> if zero x m then let (lt,gt) = split' x l in (lt,union gt r) + else let (lt,gt) = split' x r in (union l lt,gt) + | otherwise -> if x < p then (Nil, t) + else (t, Nil) Tip y | x>y -> (t,Nil) | x (Nil,t) | otherwise -> (Nil,Nil) Nil -> (Nil,Nil) --- | /O(log n)/. Performs a 'split' but also returns whether the pivot +-- | /O(min(n,W))/. Performs a 'split' but also returns whether the pivot -- element was found in the original set. splitMember :: Int -> IntSet -> (IntSet,Bool,IntSet) splitMember x t = case t of Bin p m l r - | zero x m -> let (lt,found,gt) = splitMember x l in (lt,found,union gt r) - | otherwise -> let (lt,found,gt) = splitMember x r in (union l lt,found,gt) + | m < 0 -> if x >= 0 then let (lt,found,gt) = splitMember' x l in (union r lt, found, gt) + else let (lt,found,gt) = splitMember' x r in (lt, found, union gt l) + -- handle negative numbers. + | otherwise -> splitMember' x t + Tip y + | x>y -> (t,False,Nil) + | x (Nil,False,t) + | otherwise -> (Nil,True,Nil) + Nil -> (Nil,False,Nil) + +splitMember' :: Int -> IntSet -> (IntSet,Bool,IntSet) +splitMember' x t + = case t of + Bin p m l r + | match x p m -> if zero x m then let (lt,found,gt) = splitMember x l in (lt,found,union gt r) + else let (lt,found,gt) = splitMember x r in (union l lt,found,gt) + | otherwise -> if x < p then (Nil, False, t) + else (t, False, Nil) Tip y | x>y -> (t,False,Nil) | x (Nil,False,t) @@ -478,6 +532,80 @@ splitMember x t Nil -> (Nil,False,Nil) {---------------------------------------------------------------------- + Min/Max +----------------------------------------------------------------------} + +-- | /O(min(n,W))/. Retrieves the maximal key of the set, and the set stripped from that element +-- @fail@s (in the monad) when passed an empty set. +maxView :: (Monad m) => IntSet -> m (Int, IntSet) +maxView t + = case t of + Bin p m l r | m < 0 -> let (result,t') = maxViewUnsigned l in return (result, bin p m t' r) + Bin p m l r -> let (result,t') = maxViewUnsigned r in return (result, bin p m l t') + Tip y -> return (y,Nil) + Nil -> fail "maxView: empty set has no maximal element" + +maxViewUnsigned :: IntSet -> (Int, IntSet) +maxViewUnsigned t + = case t of + Bin p m l r -> let (result,t') = maxViewUnsigned r in (result, bin p m l t') + Tip y -> (y, Nil) + +-- | /O(min(n,W))/. Retrieves the minimal key of the set, and the set stripped from that element +-- @fail@s (in the monad) when passed an empty set. +minView :: (Monad m) => IntSet -> m (Int, IntSet) +minView t + = case t of + Bin p m l r | m < 0 -> let (result,t') = minViewUnsigned r in return (result, bin p m l t') + Bin p m l r -> let (result,t') = minViewUnsigned l in return (result, bin p m t' r) + Tip y -> return (y, Nil) + Nil -> fail "minView: empty set has no minimal element" + +minViewUnsigned :: IntSet -> (Int, IntSet) +minViewUnsigned t + = case t of + Bin p m l r -> let (result,t') = minViewUnsigned l in (result, bin p m t' r) + Tip y -> (y, Nil) + + +-- Duplicate the Identity monad here because base < mtl. +newtype Identity a = Identity { runIdentity :: a } +instance Monad Identity where + return a = Identity a + m >>= k = k (runIdentity m) + + +-- | /O(min(n,W))/. Delete and find the minimal element. +-- +-- > deleteFindMin set = (findMin set, deleteMin set) +deleteFindMin :: IntSet -> (Int, IntSet) +deleteFindMin = runIdentity . minView + +-- | /O(min(n,W))/. Delete and find the maximal element. +-- +-- > deleteFindMax set = (findMax set, deleteMax set) +deleteFindMax :: IntSet -> (Int, IntSet) +deleteFindMax = runIdentity . maxView + +-- | /O(min(n,W))/. The minimal element of a set. +findMin :: IntSet -> Int +findMin = fst . runIdentity . minView + +-- | /O(min(n,W))/. The maximal element of a set. +findMax :: IntSet -> Int +findMax = fst . runIdentity . maxView + +-- | /O(min(n,W))/. Delete the minimal element. +deleteMin :: IntSet -> IntSet +deleteMin = snd . runIdentity . minView + +-- | /O(min(n,W))/. Delete the maximal element. +deleteMax :: IntSet -> IntSet +deleteMax = snd . runIdentity . maxView + + + +{---------------------------------------------------------------------- Map ----------------------------------------------------------------------} @@ -499,7 +627,12 @@ map f = fromList . List.map f . toList -- > elems set == fold (:) [] set fold :: (Int -> b -> b) -> b -> IntSet -> b fold f z t - = foldr f z t + = case t of + Bin 0 m l r | m < 0 -> foldr f (foldr f z l) r + -- put negative numbers before. + Bin p m l r -> foldr f z t + Tip x -> f x z + Nil -> z foldr :: (Int -> b -> b) -> b -> IntSet -> b foldr f z t @@ -526,9 +659,7 @@ toList t -- | /O(n)/. Convert the set to an ascending list of elements. toAscList :: IntSet -> [Int] -toAscList t - = -- NOTE: the following algorithm only works for big-endian trees - let (pos,neg) = span (>=0) (foldr (:) [] t) in neg ++ pos +toAscList t = toList t -- | /O(n*min(n,W))/. Create a set from a list of integers. fromList :: [Int] -> IntSet @@ -604,10 +735,12 @@ instance Read IntSet where Ident "fromList" <- lexP xs <- readPrec return (fromList xs) + + readListPrec = readListPrecDefault #else readsPrec p = readParen (p > 10) $ \ r -> do - ("fromList",s) <- lex - (xs,t) <- reads + ("fromList",s) <- lex r + (xs,t) <- reads s return (fromList xs,t) #endif