module Data.Interned.IntSet (
IntSet
, (\\)
, null
, size
, member
, notMember
, isSubsetOf
, isProperSubsetOf
, empty
, singleton
, insert
, delete
, union, unions
, difference
, intersection
, filter
, partition
, split
, splitMember
, findMin
, findMax
, deleteMin
, deleteMax
, deleteFindMin
, deleteFindMax
, maxView
, minView
, map
, fold
, elems
, toList
, fromList
, toAscList
, fromAscList
, fromDistinctAscList
, showTree
, showTreeWith
) where
import Prelude hiding (lookup,filter,foldr,foldl,null,map)
import qualified Data.List as List
import Data.Monoid (Monoid(..))
import Data.Maybe (fromMaybe)
#if MIN_VERSION_base(4,9,0) && !(MIN_VERSION_base(4,11,0))
import Data.Semigroup (Semigroup(..))
#endif
import Data.Interned.Internal
import Data.Bits
import Data.Hashable
import Text.Read
import GHC.Exts ( Word(..), Int(..), shiftRL# )
infixl 9 \\
type Nat = Word
natFromInt :: Int -> Nat
natFromInt i = fromIntegral i
intFromNat :: Nat -> Int
intFromNat w = fromIntegral w
shiftRL :: Nat -> Int -> Nat
shiftRL (W# x) (I# i) = W# (shiftRL# x i)
(\\) :: IntSet -> IntSet -> IntSet
m1 \\ m2 = difference m1 m2
data IntSet
= Nil
| Tip !Id !Int
| Bin !Id !Int !Prefix !Mask !IntSet !IntSet
data UninternedIntSet
= UNil
| UTip !Int
| UBin !Prefix !Mask !IntSet !IntSet
tip :: Int -> IntSet
tip n = intern (UTip n)
bin :: Prefix -> Mask -> IntSet -> IntSet -> IntSet
bin _ _ l Nil = l
bin _ _ Nil r = r
bin p m l r = intern (UBin p m l r)
bin_ :: Prefix -> Mask -> IntSet -> IntSet -> IntSet
bin_ p m l r = intern (UBin p m l r)
identity :: IntSet -> Id
identity Nil = 0
identity (Tip i _) = i
identity (Bin i _ _ _ _ _) = i
instance Interned IntSet where
type Uninterned IntSet = UninternedIntSet
data Description IntSet
= DNil
| DTip !Int
| DBin !Prefix !Mask !Id !Id
deriving Eq
describe UNil = DNil
describe (UTip j) = DTip j
describe (UBin p m l r) = DBin p m (identity l) (identity r)
cacheWidth _ = 16384
seedIdentity _ = 1
identify _ UNil = Nil
identify i (UTip j) = Tip i j
identify i (UBin p m l r) = Bin i (size l + size r) p m l r
cache = intSetCache
instance Hashable (Description IntSet) where
hashWithSalt s DNil = s `hashWithSalt` (0 :: Int)
hashWithSalt s (DTip n) = s `hashWithSalt` (1 :: Int) `hashWithSalt` n
hashWithSalt s (DBin p m l r) = s `hashWithSalt` (2 :: Int) `hashWithSalt` p `hashWithSalt` m `hashWithSalt` l `hashWithSalt` r
intSetCache :: Cache IntSet
intSetCache = mkCache
instance Uninternable IntSet where
unintern Nil = UNil
unintern (Tip _ j) = UTip j
unintern (Bin _ _ p m l r) = UBin p m l r
type Prefix = Int
type Mask = Int
#if MIN_VERSION_base(4,9,0)
instance Semigroup IntSet where
(<>) = union
#endif
instance Monoid IntSet where
mempty = empty
#if !(MIN_VERSION_base(4,11,0))
mappend = union
#endif
mconcat = unions
null :: IntSet -> Bool
null Nil = True
null _ = False
size :: IntSet -> Int
size t
= case t of
Bin _ s _ _ _ _ -> s
Tip _ _ -> 1
Nil -> 0
member :: Int -> IntSet -> Bool
member x t
= case t of
Bin _ _ p m l r
| nomatch x p m -> False
| zero x m -> member x l
| otherwise -> member x r
Tip _ y -> (x==y)
Nil -> False
notMember :: Int -> IntSet -> Bool
notMember k = not . member k
lookup :: Int -> IntSet -> Maybe Int
lookup k t
= let nk = natFromInt k in seq nk (lookupN nk t)
lookupN :: Nat -> IntSet -> Maybe Int
lookupN k t
= case t of
Bin _ _ _ m l r
| zeroN k (natFromInt m) -> lookupN k l
| otherwise -> lookupN k r
Tip _ kx
| (k == natFromInt kx) -> Just kx
| otherwise -> Nothing
Nil -> Nothing
empty :: IntSet
empty = Nil
singleton :: Int -> IntSet
singleton x = tip x
insert :: Int -> IntSet -> IntSet
insert x t
= case t of
Bin _ _ p m l r
| nomatch x p m -> join x (tip x) p t
| zero x m -> bin_ p m (insert x l) r
| otherwise -> bin_ p m l (insert x r)
Tip _ y
| x==y -> tip x
| otherwise -> join x (tip x) y t
Nil -> tip x
insertR :: Int -> IntSet -> IntSet
insertR x t
= case t of
Bin _ _ p m l r
| nomatch x p m -> join x (tip x) p t
| zero x m -> bin_ p m (insert x l) r
| otherwise -> bin_ p m l (insert x r)
Tip _ y
| x==y -> t
| otherwise -> join x (tip x) y t
Nil -> tip x
delete :: Int -> IntSet -> IntSet
delete x t
= case t of
Bin _ _ p m l r
| nomatch x p m -> t
| zero x m -> bin p m (delete x l) r
| otherwise -> bin p m l (delete x r)
Tip _ y
| x==y -> Nil
| otherwise -> t
Nil -> Nil
unions :: [IntSet] -> IntSet
unions xs = foldlStrict union empty xs
union :: IntSet -> IntSet -> IntSet
union t1@(Bin _ _ p1 m1 l1 r1) t2@(Bin _ _ p2 m2 l2 r2)
| shorter m1 m2 = union1
| shorter m2 m1 = union2
| p1 == p2 = bin_ p1 m1 (union l1 l2) (union r1 r2)
| otherwise = join p1 t1 p2 t2
where
union1 | nomatch p2 p1 m1 = join p1 t1 p2 t2
| zero p2 m1 = bin_ p1 m1 (union l1 t2) r1
| otherwise = bin_ p1 m1 l1 (union r1 t2)
union2 | nomatch p1 p2 m2 = join p1 t1 p2 t2
| zero p1 m2 = bin_ p2 m2 (union t1 l2) r2
| otherwise = bin_ p2 m2 l2 (union t1 r2)
union (Tip _ x) t = insert x t
union t (Tip _ x) = insertR x t
union Nil t = t
union t Nil = t
difference :: IntSet -> IntSet -> IntSet
difference t1@(Bin _ _ p1 m1 l1 r1) t2@(Bin _ _ p2 m2 l2 r2)
| shorter m1 m2 = difference1
| shorter m2 m1 = difference2
| p1 == p2 = bin p1 m1 (difference l1 l2) (difference r1 r2)
| otherwise = t1
where
difference1 | nomatch p2 p1 m1 = t1
| zero p2 m1 = bin p1 m1 (difference l1 t2) r1
| otherwise = bin p1 m1 l1 (difference r1 t2)
difference2 | nomatch p1 p2 m2 = t1
| zero p1 m2 = difference t1 l2
| otherwise = difference t1 r2
difference t1@(Tip _ x) t2
| member x t2 = Nil
| otherwise = t1
difference Nil _ = Nil
difference t (Tip _ x) = delete x t
difference t Nil = t
intersection :: IntSet -> IntSet -> IntSet
intersection t1@(Bin _ _ p1 m1 l1 r1) t2@(Bin _ _ p2 m2 l2 r2)
| shorter m1 m2 = intersection1
| shorter m2 m1 = intersection2
| p1 == p2 = bin p1 m1 (intersection l1 l2) (intersection r1 r2)
| otherwise = Nil
where
intersection1 | nomatch p2 p1 m1 = Nil
| zero p2 m1 = intersection l1 t2
| otherwise = intersection r1 t2
intersection2 | nomatch p1 p2 m2 = Nil
| zero p1 m2 = intersection t1 l2
| otherwise = intersection t1 r2
intersection t1@(Tip _ x) t2
| member x t2 = t1
| otherwise = Nil
intersection t (Tip _ x)
= case lookup x t of
Just y -> tip y
Nothing -> Nil
intersection Nil _ = Nil
intersection _ Nil = Nil
isProperSubsetOf :: IntSet -> IntSet -> Bool
isProperSubsetOf t1 t2
= case subsetCmp t1 t2 of
LT -> True
_ -> False
subsetCmp :: IntSet -> IntSet -> Ordering
subsetCmp t1@(Bin _ _ p1 m1 l1 r1) (Bin _ _ p2 m2 l2 r2)
| shorter m1 m2 = GT
| shorter m2 m1 = case subsetCmpLt of
GT -> GT
_ -> LT
| p1 == p2 = subsetCmpEq
| otherwise = GT
where
subsetCmpLt | nomatch p1 p2 m2 = GT
| zero p1 m2 = subsetCmp t1 l2
| otherwise = subsetCmp t1 r2
subsetCmpEq = case (subsetCmp l1 l2, subsetCmp r1 r2) of
(GT,_ ) -> GT
(_ ,GT) -> GT
(EQ,EQ) -> EQ
_ -> LT
subsetCmp (Bin _ _ _ _ _ _) _ = GT
subsetCmp (Tip _ x) (Tip _ y)
| x==y = EQ
| otherwise = GT
subsetCmp (Tip _ x) t
| member x t = LT
| otherwise = GT
subsetCmp Nil Nil = EQ
subsetCmp Nil _ = LT
isSubsetOf :: IntSet -> IntSet -> Bool
isSubsetOf t1@(Bin _ _ p1 m1 l1 r1) (Bin _ _ p2 m2 l2 r2)
| shorter m1 m2 = False
| shorter m2 m1 = match p1 p2 m2 && (if zero p1 m2 then isSubsetOf t1 l2
else isSubsetOf t1 r2)
| otherwise = (p1==p2) && isSubsetOf l1 l2 && isSubsetOf r1 r2
isSubsetOf (Bin _ _ _ _ _ _) _ = False
isSubsetOf (Tip _ x) t = member x t
isSubsetOf Nil _ = True
filter :: (Int -> Bool) -> IntSet -> IntSet
filter predicate t
= case t of
Bin _ _ p m l r
-> bin p m (filter predicate l) (filter predicate r)
Tip _ x
| predicate x -> t
| otherwise -> Nil
Nil -> Nil
partition :: (Int -> Bool) -> IntSet -> (IntSet,IntSet)
partition predicate t
= case t of
Bin _ _ p m l r
-> let (l1,l2) = partition predicate l
(r1,r2) = partition predicate r
in (bin p m l1 r1, bin p m l2 r2)
Tip _ x
| predicate x -> (t,Nil)
| otherwise -> (Nil,t)
Nil -> (Nil,Nil)
split :: Int -> IntSet -> (IntSet,IntSet)
split x t
= case t of
Bin _ _ _ m l r
| 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)
| otherwise -> split' x t
Tip _ y
| x>y -> (t,Nil)
| x<y -> (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<y -> (Nil,t)
| otherwise -> (Nil,Nil)
Nil -> (Nil,Nil)
splitMember :: Int -> IntSet -> (IntSet,Bool,IntSet)
splitMember x t
= case t of
Bin _ _ _ m l r
| 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)
| otherwise -> splitMember' x t
Tip _ y
| x>y -> (t,False,Nil)
| x<y -> (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<y -> (Nil,False,t)
| otherwise -> (Nil,True,Nil)
Nil -> (Nil,False,Nil)
maxView :: IntSet -> Maybe (Int, IntSet)
maxView t
= case t of
Bin _ _ p m l r | m < 0 -> let (result,t') = maxViewUnsigned l in Just (result, bin p m t' r)
Bin _ _ p m l r -> let (result,t') = maxViewUnsigned r in Just (result, bin p m l t')
Tip _ y -> Just (y,Nil)
Nil -> Nothing
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)
Nil -> error "maxViewUnsigned Nil"
minView :: IntSet -> Maybe (Int, IntSet)
minView t
= case t of
Bin _ _ p m l r | m < 0 -> let (result,t') = minViewUnsigned r in Just (result, bin p m l t')
Bin _ _ p m l r -> let (result,t') = minViewUnsigned l in Just (result, bin p m t' r)
Tip _ y -> Just (y, Nil)
Nil -> Nothing
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)
Nil -> error "minViewUnsigned Nil"
deleteFindMin :: IntSet -> (Int, IntSet)
deleteFindMin = fromMaybe (error "deleteFindMin: empty set has no minimal element") . minView
deleteFindMax :: IntSet -> (Int, IntSet)
deleteFindMax = fromMaybe (error "deleteFindMax: empty set has no maximal element") . maxView
findMin :: IntSet -> Int
findMin Nil = error "findMin: empty set has no minimal element"
findMin (Tip _ x) = x
findMin (Bin _ _ _ m l r)
| m < 0 = find r
| otherwise = find l
where find (Tip _ x) = x
find (Bin _ _ _ _ l' _) = find l'
find Nil = error "findMin Nil"
findMax :: IntSet -> Int
findMax Nil = error "findMax: empty set has no maximal element"
findMax (Tip _ x) = x
findMax (Bin _ _ _ m l r)
| m < 0 = find l
| otherwise = find r
where find (Tip _ x) = x
find (Bin _ _ _ _ _ r') = find r'
find Nil = error "findMax Nil"
deleteMin :: IntSet -> IntSet
deleteMin = maybe (error "deleteMin: empty set has no minimal element") snd . minView
deleteMax :: IntSet -> IntSet
deleteMax = maybe (error "deleteMax: empty set has no maximal element") snd . maxView
map :: (Int->Int) -> IntSet -> IntSet
map f = fromList . List.map f . toList
fold :: (Int -> b -> b) -> b -> IntSet -> b
fold f z t
= case t of
Bin _ _ 0 m l r | m < 0 -> foldr f (foldr f z l) r
Bin _ _ _ _ _ _ -> foldr f z t
Tip _ x -> f x z
Nil -> z
foldr :: (Int -> b -> b) -> b -> IntSet -> b
foldr f z t
= case t of
Bin _ _ _ _ l r -> foldr f (foldr f z r) l
Tip _ x -> f x z
Nil -> z
elems :: IntSet -> [Int]
elems s = toList s
toList :: IntSet -> [Int]
toList t = fold (:) [] t
toAscList :: IntSet -> [Int]
toAscList t = toList t
fromList :: [Int] -> IntSet
fromList xs = foldlStrict ins empty xs
where
ins t x = insert x t
fromAscList :: [Int] -> IntSet
fromAscList [] = Nil
fromAscList (x0 : xs0) = fromDistinctAscList (combineEq x0 xs0)
where
combineEq x' [] = [x']
combineEq x' (x:xs)
| x==x' = combineEq x' xs
| otherwise = x' : combineEq x xs
fromDistinctAscList :: [Int] -> IntSet
fromDistinctAscList [] = Nil
fromDistinctAscList (z0 : zs0) = work z0 zs0 Nada
where
work x [] stk = finish x (tip x) stk
work x (z:zs) stk = reduce z zs (branchMask z x) x (tip x) stk
reduce z zs _ px tx Nada = work z zs (Push px tx Nada)
reduce z zs m px tx stk@(Push py ty stk') =
let mxy = branchMask px py
pxy = mask px mxy
in if shorter m mxy
then reduce z zs m pxy (bin_ pxy mxy ty tx) stk'
else work z zs (Push px tx stk)
finish _ t Nada = t
finish px tx (Push py ty stk) = finish p (join py ty px tx) stk
where m = branchMask px py
p = mask px m
data Stack = Push !Prefix !IntSet !Stack | Nada
showTree :: IntSet -> String
showTree s
= showTreeWith True False s
showTreeWith :: Bool -> Bool -> IntSet -> String
showTreeWith hang wide t
| hang = (showsTreeHang wide [] t) ""
| otherwise = (showsTree wide [] [] t) ""
showsTree :: Bool -> [String] -> [String] -> IntSet -> ShowS
showsTree wide lbars rbars t
= case t of
Bin _ _ p m l r
-> showsTree wide (withBar rbars) (withEmpty rbars) r .
showWide wide rbars .
showsBars lbars . showString (showBin p m) . showString "\n" .
showWide wide lbars .
showsTree wide (withEmpty lbars) (withBar lbars) l
Tip _ x
-> showsBars lbars . showString " " . shows x . showString "\n"
Nil -> showsBars lbars . showString "|\n"
showsTreeHang :: Bool -> [String] -> IntSet -> ShowS
showsTreeHang wide bars t
= case t of
Bin _ _ p m l r
-> showsBars bars . showString (showBin p m) . showString "\n" .
showWide wide bars .
showsTreeHang wide (withBar bars) l .
showWide wide bars .
showsTreeHang wide (withEmpty bars) r
Tip _ x
-> showsBars bars . showString " " . shows x . showString "\n"
Nil -> showsBars bars . showString "|\n"
showBin :: Prefix -> Mask -> String
showBin _ _
= "*"
showWide :: Bool -> [String] -> String -> String
showWide wide bars
| wide = showString (concat (reverse bars)) . showString "|\n"
| otherwise = id
showsBars :: [String] -> ShowS
showsBars bars
= case bars of
[] -> id
_ -> showString (concat (reverse (tail bars))) . showString node
node :: String
node = "+--"
withBar, withEmpty :: [String] -> [String]
withBar bars = "| ":bars
withEmpty bars = " ":bars
instance Eq IntSet where
Nil == Nil = True
Tip i _ == Tip j _ = i == j
Bin i _ _ _ _ _ == Bin j _ _ _ _ _ = i == j
_ == _ = False
instance Ord IntSet where
Nil `compare` Nil = EQ
Nil `compare` Tip _ _ = LT
Nil `compare` Bin _ _ _ _ _ _ = LT
Tip _ _ `compare` Nil = GT
Tip i _ `compare` Tip j _ = compare i j
Tip i _ `compare` Bin j _ _ _ _ _ = compare i j
Bin _ _ _ _ _ _ `compare` Nil = GT
Bin i _ _ _ _ _ `compare` Tip j _ = compare i j
Bin i _ _ _ _ _ `compare` Bin j _ _ _ _ _ = compare i j
instance Show IntSet where
showsPrec p xs = showParen (p > 10) $
showString "fromList " . shows (toList xs)
instance Read IntSet where
readPrec = parens $ prec 10 $ do
Ident "fromList" <- lexP
xs <- readPrec
return (fromList xs)
readListPrec = readListPrecDefault
join :: Prefix -> IntSet -> Prefix -> IntSet -> IntSet
join p1 t1 p2 t2
| zero p1 m = bin_ p m t1 t2
| otherwise = bin_ p m t2 t1
where
m = branchMask p1 p2
p = mask p1 m
zero :: Int -> Mask -> Bool
zero i m
= (natFromInt i) .&. (natFromInt m) == 0
nomatch,match :: Int -> Prefix -> Mask -> Bool
nomatch i p m
= (mask i m) /= p
match i p m
= (mask i m) == p
mask :: Int -> Mask -> Prefix
mask i m
= maskW (natFromInt i) (natFromInt m)
zeroN :: Nat -> Nat -> Bool
zeroN i m = (i .&. m) == 0
maskW :: Nat -> Nat -> Prefix
maskW i m
= intFromNat (i .&. (complement (m1) `xor` m))
shorter :: Mask -> Mask -> Bool
shorter m1 m2
= (natFromInt m1) > (natFromInt m2)
branchMask :: Prefix -> Prefix -> Mask
branchMask p1 p2
= intFromNat (highestBitMask (natFromInt p1 `xor` natFromInt p2))
highestBitMask :: Nat -> Nat
highestBitMask x0
= case (x0 .|. shiftRL x0 1) of
x1 -> case (x1 .|. shiftRL x1 2) of
x2 -> case (x2 .|. shiftRL x2 4) of
x3 -> case (x3 .|. shiftRL x3 8) of
x4 -> case (x4 .|. shiftRL x4 16) of
x5 -> case (x5 .|. shiftRL x5 32) of
x6 -> (x6 `xor` (shiftRL x6 1))
foldlStrict :: (a -> b -> a) -> a -> [b] -> a
foldlStrict f z xs
= case xs of
[] -> z
(x:xx) -> let z' = f z x in seq z' (foldlStrict f z' xx)