{-# LANGUAGE CPP #-}
{-# LANGUAGE MultiParamTypeClasses #-}
#if __GLASGOW_HASKELL__ >= 702
{-# LANGUAGE Safe #-}
#endif
#if __GLASGOW_HASKELL__ >= 710
{-# LANGUAGE AutoDeriveTypeable #-}
#endif
module HaskellWorks.Data.PriorityQueue.Strict
( PQueue
, empty
, singleton
, union
, insert
, add
, fromList
, null
, minView
, minViewWithKey
, takeWithKeys
, take
) where
import Control.Arrow ((***))
import Control.DeepSeq (NFData)
import Data.Foldable (Foldable (foldMap))
import Data.Monoid
import GHC.Generics (Generic)
import HaskellWorks.Data.FingerTree.Strict (FingerTree, Measured (..), ViewL (..), (<|), (><), (|>))
import Prelude hiding (null, take)
import qualified Data.Semigroup as S
import qualified HaskellWorks.Data.FingerTree.Strict as FT
data Entry k v = Entry k v
instance Functor (Entry k) where
fmap f (Entry k v) = Entry k (f v)
instance Foldable (Entry k) where
foldMap f (Entry _ v) = f v
data Prio k v = NoPrio | Prio k v
appendPrio :: Ord k => Prio k v -> Prio k v -> Prio k v
appendPrio x NoPrio = x
appendPrio NoPrio y = y
appendPrio x@(Prio kx _) y@(Prio ky _) = if kx <= ky then x else y
{-# INLINE appendPrio #-}
instance Ord k => S.Semigroup (Prio k v) where
(<>) = appendPrio
{-# INLINE (<>) #-}
instance Ord k => Monoid (Prio k v) where
mempty = NoPrio
{-# INLINE mempty #-}
mappend = appendPrio
{-# INLINE mappend #-}
instance Ord k => Measured (Prio k v) (Entry k v) where
measure (Entry k v) = Prio k v
newtype PQueue k v = PQueue (FingerTree (Prio k v) (Entry k v))
instance Ord k => Functor (PQueue k) where
fmap f (PQueue xs) = PQueue (FT.fmap' (fmap f) xs)
instance Ord k => Foldable (PQueue k) where
foldMap f q = case minView q of
Nothing -> mempty
Just (v, q') -> f v `mappend` foldMap f q'
instance Ord k => S.Semigroup (PQueue k v) where
(<>) = union
{-# INLINE (<>) #-}
instance Ord k => Monoid (PQueue k v) where
mempty = empty
{-# INLINE mempty #-}
mappend = (<>)
{-# INLINE mappend #-}
empty :: PQueue k v
empty = PQueue FT.empty
singleton :: Ord k => k -> v -> PQueue k v
singleton k v = PQueue (FT.singleton (Entry k v))
insert :: Ord k => k -> v -> PQueue k v -> PQueue k v
insert k v (PQueue q) = PQueue (Entry k v <| q)
add :: Ord k => k -> v -> PQueue k v -> PQueue k v
add k v (PQueue q) = PQueue (q |> Entry k v)
union :: Ord k => PQueue k v -> PQueue k v -> PQueue k v
union (PQueue xs) (PQueue ys) = PQueue (xs >< ys)
fromList :: Ord k => [(k, v)] -> PQueue k v
fromList = foldr (uncurry insert) empty
null :: PQueue k v -> Bool
null (PQueue q) = FT.null q
minView :: Ord k => PQueue k v -> Maybe (v, PQueue k v)
minView q = fmap (snd *** id) (minViewWithKey q)
takeWithKeys :: Ord k => Int -> PQueue k v -> ([(k, v)], PQueue k v)
takeWithKeys = go []
where go :: Ord k => [(k, v)] -> Int -> PQueue k v -> ([(k, v)], PQueue k v)
go as n q | n > 0 = case minViewWithKey q of
Just (a, r) -> go (a:as) (n - 1) r
_ -> (reverse as, q)
go as _ q = (reverse as, q)
take :: Ord k => Int -> PQueue k v -> ([v], PQueue k v)
take = go []
where go :: Ord k => [v] -> Int -> PQueue k v -> ([v], PQueue k v)
go as n q | n > 0 = case minView q of
Just (a, r) -> go (a:as) (n - 1) r
_ -> (reverse as, q)
go as _ q = (reverse as, q)
minViewWithKey :: Ord k => PQueue k v -> Maybe ((k, v), PQueue k v)
minViewWithKey (PQueue q)
| FT.null q = Nothing
| otherwise = Just ((k, v), case FT.viewl r of
_ :< r' -> PQueue (l >< r')
_ -> error "can't happen")
where
Prio k v = measure q
(l, r) = FT.split (below k) q
below :: Ord k => k -> Prio k v -> Bool
below _ NoPrio = False
below k (Prio k' _) = k' <= k