{-# LANGUAGE BangPatterns #-} {-# LANGUAGE CPP #-} {-# LANGUAGE RecordWildCards #-} module Heap ( PriorityQueue(..) , Precedence(..) , newPrecedence , empty , isEmpty , enqueue , dequeue , delete ) where #if __GLASGOW_HASKELL__ < 709 import Control.Applicative ((<$>)) import Data.Word (Word) #endif import Data.Array (Array, listArray, (!)) import Data.Heap (Heap) import qualified Data.Heap as H ---------------------------------------------------------------- type Key = Int type Weight = Int type Deficit = Word -- Deficit can be overflowed data Precedence = Precedence { deficit :: {-# UNPACK #-} !Deficit , weight :: {-# UNPACK #-} !Weight -- stream dependency, used by the upper layer , dependency :: {-# UNPACK #-} !Key } deriving Show -- | For test only newPrecedence :: Weight -> Precedence newPrecedence w = Precedence 0 w 0 instance Eq Precedence where Precedence d1 _ _ == Precedence d2 _ _ = d1 == d2 instance Ord Precedence where -- This is correct even if one of them is overflowed Precedence d1 _ _ < Precedence d2 _ _ = d1 /= d2 && d2 - d1 <= deficitStepsW Precedence d1 _ _ <= Precedence d2 _ _ = d2 - d1 <= deficitStepsW data Entry a = Entry Key Precedence a instance Eq (Entry a) where Entry _ p1 _ == Entry _ p2 _ = p1 == p2 instance Ord (Entry a) where Entry _ p1 _ < Entry _ p2 _ = p1 < p2 Entry _ p1 _ <= Entry _ p2 _ = p1 <= p2 -- FIXME: The base (Word64) would be overflowed. -- In that case, the heap must be re-constructed. data PriorityQueue a = PriorityQueue { baseDeficit :: {-# UNPACK #-} !Deficit , queue :: Heap (Entry a) } ---------------------------------------------------------------- magicDeficit :: Deficit magicDeficit = 0 deficitSteps :: Int deficitSteps = 65536 deficitStepsW :: Word deficitStepsW = fromIntegral deficitSteps deficitList :: [Deficit] deficitList = map calc idxs where idxs = [1..256] :: [Double] calc w = round (fromIntegral deficitSteps / w) deficitTable :: Array Int Deficit deficitTable = listArray (1,256) deficitList weightToDeficit :: Weight -> Deficit weightToDeficit w = deficitTable ! w ---------------------------------------------------------------- empty :: PriorityQueue a empty = PriorityQueue 0 H.empty isEmpty :: PriorityQueue a -> Bool isEmpty (PriorityQueue _ h) = H.null h enqueue :: Key -> Precedence -> a -> PriorityQueue a -> PriorityQueue a enqueue k p v PriorityQueue{..} = PriorityQueue b queue' where !d = weightToDeficit (weight p) !b = if deficit p == magicDeficit then baseDeficit else deficit p !deficit' = max (b + d) baseDeficit !p' = p { deficit = deficit' } !queue' = H.insert (Entry k p' v) queue dequeue :: PriorityQueue a -> Maybe (Key, Precedence, a, PriorityQueue a) dequeue (PriorityQueue _ heap) = case H.uncons heap of Nothing -> Nothing Just (Entry k p v, heap') -> Just (k, p, v, PriorityQueue (deficit p) heap') delete :: Key -> PriorityQueue a -> (Maybe a, PriorityQueue a) delete k (PriorityQueue base heap) = (mv, PriorityQueue base' heap') where !(h,heap') = H.partition (\(Entry k' _ _) -> k' == k) heap mv = case H.viewMin h of Nothing -> Nothing Just (Entry _ _ v, _) -> Just v base' = case H.viewMin heap of Nothing -> base Just (Entry k' p _, _) | k == k' -> deficit p | otherwise -> base