module General.Heap
(
Heap
, Entry(..)
, empty
, insert
, uncons
) where
import Data.Monoid
import Data.Foldable hiding (minimum, concatMap)
import Data.Function (on)
import Prelude hiding (foldr)
data Heap a
= Empty
| Heap {-# UNPACK #-} !Int (a -> a -> Bool) {-# UNPACK #-} !(Tree a)
instance Show a => Show (Heap a) where
showsPrec _ Empty = showString "fromList []"
showsPrec d (Heap _ _ t) = showParen (d > 10) $
showString "fromList " . showsPrec 11 (toList t)
empty :: Heap a
empty = Empty
{-# INLINE empty #-}
singletonWith :: (a -> a -> Bool) -> a -> Heap a
singletonWith f a = Heap 1 f (Node 0 a Nil)
{-# INLINE singletonWith #-}
insert :: Ord a => a -> Heap a -> Heap a
insert = insertWith (<=)
{-# INLINE insert #-}
insertWith :: (a -> a -> Bool) -> a -> Heap a -> Heap a
insertWith leq x Empty = singletonWith leq x
insertWith leq x (Heap s _ t@(Node _ y f))
| leq x y = Heap (s+1) leq (Node 0 x (t `Cons` Nil))
| otherwise = Heap (s+1) leq (Node 0 y (skewInsert leq (Node 0 x Nil) f))
{-# INLINE insertWith #-}
uncons :: Heap a -> Maybe (a, Heap a)
uncons Empty = Nothing
uncons l@(Heap _ _ t) = Just (root t, deleteMin l)
{-# INLINE uncons #-}
trees :: Forest a -> [Tree a]
trees (a `Cons` as) = a : trees as
trees Nil = []
deleteMin :: Heap a -> Heap a
deleteMin Empty = Empty
deleteMin (Heap _ _ (Node _ _ Nil)) = Empty
deleteMin (Heap s leq (Node _ _ f0)) = Heap (s - 1) leq (Node 0 x f3)
where
(Node r x cf, ts2) = getMin leq f0
(zs, ts1, f1) = splitForest r Nil Nil cf
f2 = skewMeld leq (skewMeld leq ts1 ts2) f1
f3 = foldr (skewInsert leq) f2 (trees zs)
{-# INLINE deleteMin #-}
data Tree a = Node
{ rank :: {-# UNPACK #-} !Int
, root :: a
, _forest :: !(Forest a)
} deriving (Show)
data Forest a = !(Tree a) `Cons` !(Forest a) | Nil
deriving (Show)
infixr 5 `Cons`
instance Functor Tree where
fmap f (Node r a as) = Node r (f a) (fmap f as)
instance Functor Forest where
fmap f (a `Cons` as) = fmap f a `Cons` fmap f as
fmap _ Nil = Nil
instance Foldable Tree where
foldMap f (Node _ a as) = f a `mappend` foldMap f as
instance Foldable Forest where
foldMap f (a `Cons` as) = foldMap f a `mappend` foldMap f as
foldMap _ Nil = mempty
link :: (a -> a -> Bool) -> Tree a -> Tree a -> Tree a
link f t1@(Node r1 x1 cf1) t2@(Node r2 x2 cf2)
| f x1 x2 = Node (r1+1) x1 (t2 `Cons` cf1)
| otherwise = Node (r2+1) x2 (t1 `Cons` cf2)
skewLink :: (a -> a -> Bool) -> Tree a -> Tree a -> Tree a -> Tree a
skewLink f t0@(Node _ x0 cf0) t1@(Node r1 x1 cf1) t2@(Node r2 x2 cf2)
| f x1 x0 && f x1 x2 = Node (r1+1) x1 (t0 `Cons` t2 `Cons` cf1)
| f x2 x0 && f x2 x1 = Node (r2+1) x2 (t0 `Cons` t1 `Cons` cf2)
| otherwise = Node (r1+1) x0 (t1 `Cons` t2 `Cons` cf0)
ins :: (a -> a -> Bool) -> Tree a -> Forest a -> Forest a
ins _ t Nil = t `Cons` Nil
ins f t (t' `Cons` ts)
| rank t < rank t' = t `Cons` t' `Cons` ts
| otherwise = ins f (link f t t') ts
uniqify :: (a -> a -> Bool) -> Forest a -> Forest a
uniqify _ Nil = Nil
uniqify f (t `Cons` ts) = ins f t ts
unionUniq :: (a -> a -> Bool) -> Forest a -> Forest a -> Forest a
unionUniq _ Nil ts = ts
unionUniq _ ts Nil = ts
unionUniq f tts1@(t1 `Cons` ts1) tts2@(t2 `Cons` ts2) = case compare (rank t1) (rank t2) of
LT -> t1 `Cons` unionUniq f ts1 tts2
EQ -> ins f (link f t1 t2) (unionUniq f ts1 ts2)
GT -> t2 `Cons` unionUniq f tts1 ts2
skewInsert :: (a -> a -> Bool) -> Tree a -> Forest a -> Forest a
skewInsert f t ts@(t1 `Cons` t2 `Cons`rest)
| rank t1 == rank t2 = skewLink f t t1 t2 `Cons` rest
| otherwise = t `Cons` ts
skewInsert _ t ts = t `Cons` ts
{-# INLINE skewInsert #-}
skewMeld :: (a -> a -> Bool) -> Forest a -> Forest a -> Forest a
skewMeld f ts ts' = unionUniq f (uniqify f ts) (uniqify f ts')
{-# INLINE skewMeld #-}
getMin :: (a -> a -> Bool) -> Forest a -> (Tree a, Forest a)
getMin _ (t `Cons` Nil) = (t, Nil)
getMin f (t `Cons` ts)
| f (root t) (root t') = (t, ts)
| otherwise = (t', t `Cons` ts')
where (t',ts') = getMin f ts
getMin _ Nil = error "Heap.getMin: empty forest"
splitForest :: Int -> Forest a -> Forest a -> Forest a -> (Forest a, Forest a, Forest a)
splitForest a b c d | a `seq` b `seq` c `seq` d `seq` False = undefined
splitForest 0 zs ts f = (zs, ts, f)
splitForest 1 zs ts (t `Cons` Nil) = (zs, t `Cons` ts, Nil)
splitForest 1 zs ts (t1 `Cons` t2 `Cons` f)
| rank t2 == 0 = (t1 `Cons` zs, t2 `Cons` ts, f)
| otherwise = (zs, t1 `Cons` ts, t2 `Cons` f)
splitForest r zs ts (t1 `Cons` t2 `Cons` cf)
| r1 == r2 = (zs, t1 `Cons` t2 `Cons` ts, cf)
| r1 == 0 = splitForest (r-1) (t1 `Cons` zs) (t2 `Cons` ts) cf
| otherwise = splitForest (r-1) zs (t1 `Cons` ts) (t2 `Cons` cf)
where
r1 = rank t1
r2 = rank t2
splitForest _ _ _ _ = error "Heap.splitForest: invalid arguments"
data Entry p a = Entry { priority :: p, payload :: a }
deriving (Show)
instance Eq p => Eq (Entry p a) where
(==) = (==) `on` priority
{-# INLINE (==) #-}
instance Ord p => Ord (Entry p a) where
compare = compare `on` priority
{-# INLINE compare #-}