diff --git a/Data/PQueue.hs b/Data/PQueue.hs new file mode 100644 index 0000000..e4ed1bb --- /dev/null +++ b/Data/PQueue.hs @@ -0,0 +1,395 @@ +{-# LANGUAGE CPP #-} + +----------------------------------------------------------------------------- +-- | +-- Module : Data.PQueue +-- Copyright : (c) Henning Thielemann 2017 +-- (c) Louis Wasserman 2010 +-- License : BSD-style +-- Maintainer : libraries@haskell.org +-- Stability : experimental +-- Portability : portable +-- +-- General purpose priority queue, supporting view-top operations. +-- +-- An amortized running time is given for each operation, with /n/ referring +-- to the length of the sequence and /k/ being the integral index used by +-- some operations. These bounds hold even in a persistent (shared) setting. +-- +-- This implementation is based on a binomial heap augmented with a global root. +-- The spine of the heap is maintained lazily. To force the spine of the heap, +-- use 'seqSpine'. +-- +-- This implementation does not guarantee stable behavior. +-- +-- This implementation offers a number of methods of the form @xxxU@, where @U@ stands for +-- unordered. No guarantees whatsoever are made on the execution or traversal order of +-- these functions. +----------------------------------------------------------------------------- +module Data.PQueue ( + Queue, + MinQueue, + MaxQueue, + -- * Basic operations + empty, + null, + size, + -- * Query operations + findTop, + getTop, + deleteTop, + deleteFindTop, + delete, + topView, + -- * Construction operations + singleton, + insert, + insertBehind, + union, + unions, + -- * Subsets + -- ** Extracting subsets + (!!), + take, + drop, + splitAt, + -- ** Predicates + takeWhile, + dropWhile, + span, + break, + -- * Filter/Map + filter, + partition, + mapMaybe, + mapEither, + -- * Fold\/Functor\/Traversable variations + map, + foldrAsc, + foldlAsc, + foldrDesc, + foldlDesc, + -- * List operations + toList, + toAscList, + toDescList, + fromList, + fromAscList, + fromDescList, + fromOrderedList, + -- * Unordered operations + mapU, + foldrU, + foldlU, + elemsU, + toListU, + -- * Miscellaneous operations + keysQueue, + seqSpine) where + +import qualified Data.PQueue.Min as Min +import qualified Data.PQueue.Top as Top +import Data.PQueue.Prio.Private (PQueue(PQ)) +import Data.PQueue.Top (Top, Wrap(Wrap, unwrap)) + +import Control.DeepSeq (NFData(rnf)) + +import Data.Functor ((<$>)) +import Data.Monoid (Monoid(mempty, mappend)) +import Data.Maybe (fromMaybe) +import Data.Traversable (traverse) +import Data.Foldable (foldl, foldr) + +import Prelude hiding (null, foldr, foldl, take, drop, takeWhile, dropWhile, splitAt, span, break, (!!), filter) + +#ifdef __GLASGOW_HASKELL__ +import Text.Read (Lexeme(Ident), lexP, parens, prec, + readPrec, readListPrec, readListPrecDefault) +import Data.Data +#else +build :: ((a -> [a] -> [a]) -> [a] -> [a]) -> [a] +build f = f (:) [] +#endif + +-- | A priority queue with elements of type @a@. Supports extracting the top element. +-- Implemented as a wrapper around 'Min.MinQueue'. +newtype Queue top a = Q (Min.MinQueue (Wrap top a)) +# if __GLASGOW_HASKELL__ + deriving (Eq, Ord, Data, Typeable) +# else + deriving (Eq, Ord) +# endif + +type MinQueue = Queue Top.Min +type MaxQueue = Queue Top.Max + +instance NFData a => NFData (Queue top a) where + rnf (Q q) = rnf q + +instance (Top top, Ord a, Show a) => Show (Queue top a) where + showsPrec p xs = showParen (p > 10) $ + showString "fromOrderedList " . shows (toList xs) + +instance Read a => Read (Queue top a) where +#ifdef __GLASGOW_HASKELL__ + readPrec = parens $ prec 10 $ do + Ident "fromOrderedList" <- lexP + xs <- readPrec + return (fromOrderedList xs) + + readListPrec = readListPrecDefault +#else + readsPrec p = readParen (p > 10) $ \ r -> do + ("fromOrderedList",s) <- lex r + (xs,t) <- reads s + return (fromOrderedList xs,t) +#endif + +instance (Top top, Ord a) => Monoid (Queue top a) where + mempty = empty + mappend = union + +-- | /O(1)/. The empty priority queue. +empty :: Queue top a +empty = Q Min.empty + +-- | /O(1)/. Is this the empty priority queue? +null :: Queue top a -> Bool +null (Q q) = Min.null q + +-- | /O(1)/. The number of elements in the queue. +size :: Queue top a -> Int +size (Q q) = Min.size q + +-- | /O(1)/. Returns the top element of the queue. Throws an error on an empty queue. +findTop :: Queue top a -> a +findTop = fromMaybe (error "Error: findTop called on empty queue") . getTop + +-- | /O(1)/. The top element of the queue, if there is one. +getTop :: Queue top a -> Maybe a +getTop (Q q) = unwrap <$> Min.getMin q + +-- | /O(log n)/. Deletes the top element of the queue. Does nothing on an empty queue. +deleteTop :: (Top top, Ord a) => Queue top a -> Queue top a +deleteTop (Q q) = Q (Min.deleteMin q) + +-- | /O(log n)/. Extracts the top element of the queue. Throws an error on an empty queue. +deleteFindTop :: (Top top, Ord a) => Queue top a -> (a, Queue top a) +deleteFindTop = fromMaybe (error "Error: deleteFindTop called on empty queue") . topView + +-- | /O(log n)/. Extract the top element of the sequence, if there is one. +topView :: (Top top, Ord a) => Queue top a -> Maybe (a, Queue top a) +topView (Q q) = case Min.minView q of + Nothing -> Nothing + Just (Wrap x, q') + -> Just (x, Q q') + +-- | /O(log n)/. Delete the top element of the sequence, if there is one. +delete :: (Top top, Ord a) => Queue top a -> Maybe (Queue top a) +delete = fmap snd . topView + +-- | /O(1)/. Construct a priority queue with a single element. +singleton :: a -> Queue top a +singleton = Q . Min.singleton . Wrap + +-- | /O(1)/. Insert an element into the priority queue. +insert :: (Top top, Ord a) => a -> Queue top a -> Queue top a +x `insert` Q q = Q (Wrap x `Min.insert` q) + +-- | Amortized /O(1)/, worst-case /O(log n)/. Insert an element into the priority queue, +-- putting it behind elements that compare equal to the inserted one. +insertBehind :: (Top top, Ord a) => a -> Queue top a -> Queue top a +x `insertBehind` Q q = Q (Wrap x `Min.insertBehind` q) + +-- | /O(log (min(n1,n2)))/. Take the union of two priority queues. +union :: (Top top, Ord a) => Queue top a -> Queue top a -> Queue top a +Q q1 `union` Q q2 = Q (q1 `Min.union` q2) + +-- | Takes the union of a list of priority queues. Equivalent to @'foldl' 'union' 'empty'@. +unions :: (Top top, Ord a) => [Queue top a] -> Queue top a +unions qs = Q (Min.unions [q | Q q <- qs]) + +-- | /O(k log n)/. Returns the @(k+1)@th top element of the queue. +(!!) :: (Top top, Ord a) => Queue top a -> Int -> a +Q q !! n = unwrap ((Min.!!) q n) + +{-# INLINE take #-} +-- | /O(k log n)/. Returns the list of the @k@ top elements of the queue, in natural order, or +-- all elements of the queue, if @k >= n@. +take :: (Top top, Ord a) => Int -> Queue top a -> [a] +take k (Q q) = map unwrap $ Min.take k q + +-- | /O(k log n)/. Returns the queue with the @k@ top elements deleted, or the empty queue if @k >= n@. +drop :: (Top top, Ord a) => Int -> Queue top a -> Queue top a +drop k (Q q) = Q (Min.drop k q) + +-- | /O(k log n)/. Equivalent to @(take k queue, drop k queue)@. +splitAt :: (Top top, Ord a) => Int -> Queue top a -> ([a], Queue top a) +splitAt k (Q q) = (map unwrap xs, Q q') where + (xs, q') = Min.splitAt k q + +-- | 'takeWhile', applied to a predicate @p@ and a queue @queue@, returns the +-- longest prefix (possibly empty) of @queue@ of elements that satisfy @p@. +takeWhile :: (Top top, Ord a) => (a -> Bool) -> Queue top a -> [a] +takeWhile p (Q q) = map unwrap (Min.takeWhile (p . unwrap) q) + +-- | 'dropWhile' @p queue@ returns the queue remaining after 'takeWhile' @p queue@. +dropWhile :: (Top top, Ord a) => (a -> Bool) -> Queue top a -> Queue top a +dropWhile p (Q q) = Q (Min.dropWhile (p . unwrap) q) + +-- | 'span', applied to a predicate @p@ and a queue @queue@, returns a tuple where +-- first element is longest prefix (possibly empty) of @queue@ of elements that +-- satisfy @p@ and second element is the remainder of the queue. +-- +span :: (Top top, Ord a) => (a -> Bool) -> Queue top a -> ([a], Queue top a) +span p (Q q) = (map unwrap xs, Q q') where + (xs, q') = Min.span (p . unwrap) q + +-- | 'break', applied to a predicate @p@ and a queue @queue@, returns a tuple where +-- first element is longest prefix (possibly empty) of @queue@ of elements that +-- /do not satisfy/ @p@ and second element is the remainder of the queue. +break :: (Top top, Ord a) => (a -> Bool) -> Queue top a -> ([a], Queue top a) +break p = span (not . p) + +-- | /O(n)/. Returns a queue of those elements which satisfy the predicate. +filter :: (Top top, Ord a) => (a -> Bool) -> Queue top a -> Queue top a +filter p (Q q) = Q (Min.filter (p . unwrap) q) + +-- | /O(n)/. Returns a pair of queues, where the left queue contains those elements that satisfy the predicate, +-- and the right queue contains those that do not. +partition :: (Top top, Ord a) => (a -> Bool) -> Queue top a -> (Queue top a, Queue top a) +partition p (Q q) = (Q q0, Q q1) + where (q0, q1) = Min.partition (p . unwrap) q + +-- | /O(n)/. Maps a function over the elements of the queue, and collects the 'Just' values. +mapMaybe :: (Top top, Ord b) => (a -> Maybe b) -> Queue top a -> Queue top b +mapMaybe f (Q q) = Q (Min.mapMaybe (traverse f) q) + +-- | /O(n)/. Maps a function over the elements of the queue, and separates the 'Left' and 'Right' values. +mapEither :: (Top top, Ord b, Ord c) => (a -> Either b c) -> Queue top a -> (Queue top b, Queue top c) +mapEither f (Q q) = (Q q0, Q q1) + where (q0, q1) = Min.mapEither (either (Left . Wrap) (Right . Wrap) . f . unwrap) q + +-- | /O(n)/. Assumes that the function it is given is monotonic, and applies this function to every element of the priority queue. +-- /Does not check the precondition/. +mapU :: (a -> b) -> Queue top a -> Queue top b +mapU f (Q q) = Q (Min.mapU (fmap f) q) + +-- | /O(n)/. Unordered right fold on a priority queue. +foldrU :: (a -> b -> b) -> b -> Queue top a -> b +foldrU f z (Q q) = Min.foldrU (flip (foldr f)) z q + +-- | /O(n)/. Unordered left fold on a priority queue. +foldlU :: (b -> a -> b) -> b -> Queue top a -> b +foldlU f z (Q q) = Min.foldlU (foldl f) z q + +{-# INLINE elemsU #-} +-- | Equivalent to 'toListU'. +elemsU :: Queue top a -> [a] +elemsU = toListU + +{-# INLINE toListU #-} +-- | /O(n)/. Returns a list of the elements of the priority queue, in no particular order. +toListU :: Queue top a -> [a] +toListU (Q q) = map unwrap (Min.toListU q) + +-- | /O(n log n)/. Performs a right-fold on the elements of a priority queue in ascending order. +-- @'foldrAsc' f z q == 'foldlDesc' (flip f) z q@. +foldrAsc :: (Top top, Ord a) => (a -> b -> b) -> b -> Queue top a -> b +foldrAsc = foldlDesc . flip + +-- | /O(n log n)/. Performs a left-fold on the elements of a priority queue in descending order. +-- @'foldlAsc' f z q == 'foldrDesc' (flip f) z q@. +foldlAsc :: (Top top, Ord a) => (b -> a -> b) -> b -> Queue top a -> b +foldlAsc = foldrDesc . flip + +newtype + Foldr b a top = + Foldr { + runFoldr :: (Wrap top a -> b -> b) -> b -> Min.MinQueue (Wrap top a) -> b + } + +-- | /O(n log n)/. Performs a right-fold on the elements of a priority queue in descending order. +foldrDesc :: (Top top, Ord a) => (a -> b -> b) -> b -> Queue top a -> b +foldrDesc f z (Q q) = + runFoldr + (Top.switch (Foldr Min.foldrDesc) (Foldr Min.foldrAsc)) + (flip (foldr f)) z q + +newtype + Foldl b a top = + Foldl { + runFoldl :: (b -> Wrap top a -> b) -> b -> Min.MinQueue (Wrap top a) -> b + } + +-- | /O(n log n)/. Performs a left-fold on the elements of a priority queue in descending order. +foldlDesc :: (Top top, Ord a) => (b -> a -> b) -> b -> Queue top a -> b +foldlDesc f z (Q q) = + runFoldl + (Top.switch (Foldl Min.foldlDesc) (Foldl Min.foldlAsc)) + (foldl f) z q + +newtype + ToList a top = + ToList {runToList :: Min.MinQueue (Wrap top a) -> [Wrap top a]} + +{-# INLINE toAscList #-} +-- | /O(n log n)/. Extracts the elements of the priority queue in ascending order. +toAscList :: (Top top, Ord a) => Queue top a -> [a] +toAscList (Q q) = + fmap unwrap $ + runToList (Top.switch (ToList Min.toAscList) (ToList Min.toDescList)) q + +{-# INLINE toDescList #-} +-- | /O(n log n)/. Extracts the elements of the priority queue in descending order. +toDescList :: (Top top, Ord a) => Queue top a -> [a] +toDescList (Q q) = + fmap unwrap $ + runToList (Top.switch (ToList Min.toDescList) (ToList Min.toAscList)) q + +{-# INLINE toList #-} +-- | /O(n log n)/. Returns the elements of the priority queue with top keys first. +-- +-- If the order of the elements is irrelevant, consider using 'toListU'. +toList :: (Top top, Ord a) => Queue top a -> [a] +toList (Q q) = map unwrap (Min.toList q) + +newtype + FromList a top = + FromList {runFromList :: [Wrap top a] -> Min.MinQueue (Wrap top a)} + +{-# INLINE fromAscList #-} +-- | /O(n)/. Constructs a priority queue from an ascending list. /Warning/: Does not check the precondition. +fromAscList :: (Top top) => [a] -> Queue top a +fromAscList = + Q . + runFromList + (Top.switch (FromList Min.fromAscList) (FromList Min.fromDescList)) . + map Wrap + +{-# INLINE fromDescList #-} +-- | /O(n)/. Constructs a priority queue from a descending list. /Warning/: Does not check the precondition. +fromDescList :: (Top top) => [a] -> Queue top a +fromDescList = + Q . + runFromList + (Top.switch (FromList Min.fromDescList) (FromList Min.fromAscList)) . + map Wrap + +{-# INLINE fromOrderedList #-} +-- | /O(n)/. Constructs a priority queue from a list with top keys first. /Warning/: Does not check the precondition. +fromOrderedList :: [a] -> Queue top a +fromOrderedList = Q . Min.fromAscList . map Wrap + +{-# INLINE fromList #-} +-- | /O(n log n)/. Constructs a priority queue from an unordered list. +fromList :: (Top top, Ord a) => [a] -> Queue top a +fromList = foldr insert empty + +-- | /O(n)/. Constructs a priority queue from the keys of a 'Prio.PQueue'. +keysQueue :: PQueue top k a -> Queue top k +keysQueue (PQ q) = Q (Min.keysQueue q) + +-- | /O(log n)/. Forces the spine of the heap. +seqSpine :: Queue top a -> b -> b +seqSpine (Q q) = Min.seqSpine q diff --git a/Data/PQueue/Prio.hs b/Data/PQueue/Prio.hs new file mode 100644 index 0000000..7c6ed8a --- /dev/null +++ b/Data/PQueue/Prio.hs @@ -0,0 +1,433 @@ +----------------------------------------------------------------------------- +-- | +-- Module : Data.PQueue.Prio +-- Copyright : (c) Henning Thielemann 2017 +-- (c) Louis Wasserman 2010 +-- License : BSD-style +-- Maintainer : libraries@haskell.org +-- Stability : experimental +-- Portability : portable +-- +-- General purpose priority queue. +-- Each element is associated with a /key/, and the priority queue supports +-- viewing and extracting the element with the top key. +-- +-- A worst-case bound is given for each operation. In some cases, an amortized +-- bound is also specified; these bounds do not hold in a persistent context. +-- +-- This implementation is based on a binomial heap augmented with a global root. +-- The spine of the heap is maintained lazily. To force the spine of the heap, +-- use 'seqSpine'. +-- +-- We do not guarantee stable behavior. +-- Ties are broken arbitrarily -- that is, if @k1 <= k2@ and @k2 <= k1@, then there +-- are no guarantees about the relative order in which @k1@, @k2@, and their associated +-- elements are returned. (Unlike Data.Map, we allow multiple elements with the +-- same key.) +-- +-- This implementation offers a number of methods of the form @xxxU@, where @U@ stands for +-- unordered. No guarantees whatsoever are made on the execution or traversal order of +-- these functions. +----------------------------------------------------------------------------- +module Data.PQueue.Prio ( + PQueue, + MinPQueue, + MaxPQueue, + -- * Construction + empty, + singleton, + insert, + insertBehind, + union, + unions, + -- * Query + null, + size, + -- ** Top view + findTop, + getTop, + deleteTop, + deleteFindTop, + adjustTop, + adjustTopWithKey, + updateTop, + updateTopWithKey, + topView, + topViewWithKey, + -- * Traversal + -- ** Map + map, + mapWithKey, + mapKeys, + mapKeysMonotonic, + -- ** Fold + foldrWithKey, + foldlWithKey, + -- ** Traverse + traverseWithKey, + -- * Subsets + -- ** Indexed + take, + drop, + splitAt, + -- ** Predicates + takeWhile, + takeWhileWithKey, + dropWhile, + dropWhileWithKey, + span, + spanWithKey, + break, + breakWithKey, + -- *** Filter + filter, + filterWithKey, + partition, + partitionWithKey, + mapMaybe, + mapMaybeWithKey, + mapEither, + mapEitherWithKey, + -- * List operations + -- ** Conversion from lists + fromList, + fromAscList, + fromDescList, + fromOrderedList, + -- ** Conversion to lists + keys, + elems, + assocs, + toAscList, + toDescList, + toList, + -- * Unordered operations + foldrU, + foldrWithKeyU, + foldlU, + foldlWithKeyU, + traverseU, + traverseWithKeyU, + keysU, + elemsU, + assocsU, + toListU, + -- * Helper methods + seqSpine + ) + where + +import qualified Data.PQueue.Prio.Min as Q +import qualified Data.PQueue.Top as Top +import Data.PQueue.Prio.Private + (PQueue(PQ), toList, fromOrderedList, empty, union, unions) +import Data.PQueue.Top (Top, Wrap(Wrap, unwrap)) + +import Control.Applicative (Applicative, (<$>)) +import Data.Maybe (fromMaybe) + +import Prelude hiding (map, filter, break, span, takeWhile, dropWhile, splitAt, take, drop, (!!), null, foldr, foldl) + + +first' :: (a -> b) -> (a, c) -> (b, c) +first' f (a, c) = (f a, c) + + +type MinPQueue = PQueue Top.Min +type MaxPQueue = PQueue Top.Max + + +-- | /O(1)/. Constructs a singleton priority queue. +singleton :: k -> a -> PQueue top k a +singleton k a = PQ (Q.singleton (Wrap k) a) + +-- | Amortized /O(1)/, worst-case /O(log n)/. Inserts +-- an element with the specified key into the queue. +insert :: (Top top, Ord k) => k -> a -> PQueue top k a -> PQueue top k a +insert k a (PQ q) = PQ (Q.insert (Wrap k) a q) + +-- | Amortized /O(1)/, worst-case /O(log n)/. Insert an element into the priority queue, +-- putting it behind elements that compare equal to the inserted one. +insertBehind :: (Top top, Ord k) => k -> a -> PQueue top k a -> PQueue top k a +insertBehind k a (PQ q) = PQ (Q.insertBehind (Wrap k) a q) + +-- | /O(1)/. Checks if this priority queue is empty. +null :: PQueue top k a -> Bool +null (PQ q) = Q.null q + +-- | /O(1)/. Returns the size of this priority queue. +size :: PQueue top k a -> Int +size (PQ q) = Q.size q + +-- | /O(1)/. The top (key, element) in the queue. Calls 'error' if empty. +findTop :: PQueue top k a -> (k, a) +findTop = fromMaybe (error "Error: findTop called on an empty queue") . getTop + +-- | /O(1)/. The top (key, element) in the queue, if the queue is nonempty. +getTop :: PQueue top k a -> Maybe (k, a) +getTop (PQ q) = do + (Wrap k, a) <- Q.getMin q + return (k, a) + +-- | /O(log n)/. Delete and find the element with the top key. Calls 'error' if empty. +deleteTop :: (Top top, Ord k) => PQueue top k a -> PQueue top k a +deleteTop (PQ q) = PQ (Q.deleteMin q) + +-- | /O(log n)/. Delete and find the element with the top key. Calls 'error' if empty. +deleteFindTop :: (Top top, Ord k) => PQueue top k a -> ((k, a), PQueue top k a) +deleteFindTop = fromMaybe (error "Error: deleteFindTop called on an empty queue") . topViewWithKey + +-- | /O(1)/. Alter the value at the top key. If the queue is empty, does nothing. +adjustTop :: (a -> a) -> PQueue top k a -> PQueue top k a +adjustTop = adjustTopWithKey . const + +-- | /O(1)/. Alter the value at the top key. If the queue is empty, does nothing. +adjustTopWithKey :: (k -> a -> a) -> PQueue top k a -> PQueue top k a +adjustTopWithKey f (PQ q) = PQ (Q.adjustMinWithKey (f . unwrap) q) + +-- | /O(log n)/. (Actually /O(1)/ if there's no deletion.) Update the value at the top key. +-- If the queue is empty, does nothing. +updateTop :: (Top top, Ord k) => (a -> Maybe a) -> PQueue top k a -> PQueue top k a +updateTop = updateTopWithKey . const + +-- | /O(log n)/. (Actually /O(1)/ if there's no deletion.) Update the value at the top key. +-- If the queue is empty, does nothing. +updateTopWithKey :: (Top top, Ord k) => (k -> a -> Maybe a) -> PQueue top k a -> PQueue top k a +updateTopWithKey f (PQ q) = PQ (Q.updateMinWithKey (f . unwrap) q) + +-- | /O(log n)/. Retrieves the value associated with the top key of the queue, and the queue +-- stripped of that element, or 'Nothing' if passed an empty queue. +topView :: (Top top, Ord k) => PQueue top k a -> Maybe (a, PQueue top k a) +topView q = do + ((_, a), q') <- topViewWithKey q + return (a, q') + +-- | /O(log n)/. Retrieves the top (key, value) pair of the map, and the map stripped of that +-- element, or 'Nothing' if passed an empty map. +topViewWithKey :: (Top top, Ord k) => PQueue top k a -> Maybe ((k, a), PQueue top k a) +topViewWithKey (PQ q) = do + ((Wrap k, a), q') <- Q.minViewWithKey q + return ((k, a), PQ q') + +-- | /O(n)/. Map a function over all values in the queue. +map :: (a -> b) -> PQueue top k a -> PQueue top k b +map = mapWithKey . const + +-- | /O(n)/. Map a function over all values in the queue. +mapWithKey :: (k -> a -> b) -> PQueue top k a -> PQueue top k b +mapWithKey f (PQ q) = PQ (Q.mapWithKey (f . unwrap) q) + +-- | /O(n)/. Map a function over all values in the queue. +mapKeys :: (Top top, Ord k') => (k -> k') -> PQueue top k a -> PQueue top k' a +mapKeys f (PQ q) = PQ (Q.mapKeys (fmap f) q) + +-- | /O(n)/. @'mapKeysMonotonic' f q == 'mapKeys' f q@, but only works when @f@ is strictly +-- monotonic. /The precondition is not checked./ This function has better performance than +-- 'mapKeys'. +mapKeysMonotonic :: (k -> k') -> PQueue top k a -> PQueue top k' a +mapKeysMonotonic f (PQ q) = PQ (Q.mapKeysMonotonic (fmap f) q) + +-- | /O(n log n)/. Fold the keys and values in the map, such that +-- @'foldrWithKey' f z q == 'List.foldr' ('uncurry' f) z ('toList' q)@. +-- +-- If you do not care about the traversal order, consider using 'foldrWithKeyU'. +foldrWithKey :: (Top top, Ord k) => (k -> a -> b -> b) -> b -> PQueue top k a -> b +foldrWithKey f z (PQ q) = Q.foldrWithKey (f . unwrap) z q + +-- | /O(n log n)/. Fold the keys and values in the map, such that +-- @'foldlWithKey' f z q == 'List.foldl' ('uncurry' . f) z ('toList' q)@. +-- +-- If you do not care about the traversal order, consider using 'foldlWithKeyU'. +foldlWithKey :: (Top top, Ord k) => (b -> k -> a -> b) -> b -> PQueue top k a -> b +foldlWithKey f z0 (PQ q) = Q.foldlWithKey (\ z -> f z . unwrap) z0 q + +-- | /O(n log n)/. Traverses the elements of the queue in natural order by key. +-- (@'traverseWithKey' f q == 'fromOrderedList' <$> 'traverse' ('uncurry' f) ('toList' q)@) +-- +-- If you do not care about the /order/ of the traversal, consider using 'traverseWithKeyU'. +traverseWithKey :: (Top top, Ord k, Applicative f) => (k -> a -> f b) -> PQueue top k a -> f (PQueue top k b) +traverseWithKey f (PQ q) = PQ <$> Q.traverseWithKey (f . unwrap) q + +-- | /O(k log n)/. Takes the first @k@ (key, value) pairs in the queue, or the first @n@ if @k >= n@. +-- (@'take' k q == 'List.take' k ('toList' q)@) +take :: (Top top, Ord k) => Int -> PQueue top k a -> [(k, a)] +take k (PQ q) = fmap (first' unwrap) (Q.take k q) + +-- | /O(k log n)/. Deletes the first @k@ (key, value) pairs in the queue, or returns an empty queue if @k >= n@. +drop :: (Top top, Ord k) => Int -> PQueue top k a -> PQueue top k a +drop k (PQ q) = PQ (Q.drop k q) + +-- | /O(k log n)/. Equivalent to @('take' k q, 'drop' k q)@. +splitAt :: (Top top, Ord k) => Int -> PQueue top k a -> ([(k, a)], PQueue top k a) +splitAt k (PQ q) = case Q.splitAt k q of + (xs, q') -> (fmap (first' unwrap) xs, PQ q') + +-- | Takes the longest possible prefix of elements satisfying the predicate. +-- (@'takeWhile' p q == 'List.takeWhile' (p . 'snd') ('toList' q)@) +takeWhile :: (Top top, Ord k) => (a -> Bool) -> PQueue top k a -> [(k, a)] +takeWhile = takeWhileWithKey . const + +-- | Takes the longest possible prefix of elements satisfying the predicate. +-- (@'takeWhile' p q == 'List.takeWhile' (uncurry p) ('toList' q)@) +takeWhileWithKey :: (Top top, Ord k) => (k -> a -> Bool) -> PQueue top k a -> [(k, a)] +takeWhileWithKey p (PQ q) = fmap (first' unwrap) (Q.takeWhileWithKey (p . unwrap) q) + +-- | Removes the longest possible prefix of elements satisfying the predicate. +dropWhile :: (Top top, Ord k) => (a -> Bool) -> PQueue top k a -> PQueue top k a +dropWhile = dropWhileWithKey . const + +-- | Removes the longest possible prefix of elements satisfying the predicate. +dropWhileWithKey :: (Top top, Ord k) => (k -> a -> Bool) -> PQueue top k a -> PQueue top k a +dropWhileWithKey p (PQ q) = PQ (Q.dropWhileWithKey (p . unwrap) q) + +-- | Equivalent to @('takeWhile' p q, 'dropWhile' p q)@. +span :: (Top top, Ord k) => (a -> Bool) -> PQueue top k a -> ([(k, a)], PQueue top k a) +span = spanWithKey . const + +-- | Equivalent to @'span' ('not' . p)@. +break :: (Top top, Ord k) => (a -> Bool) -> PQueue top k a -> ([(k, a)], PQueue top k a) +break = breakWithKey . const + +-- | Equivalent to @'spanWithKey' (\ k a -> 'not' (p k a)) q@. +spanWithKey :: (Top top, Ord k) => (k -> a -> Bool) -> PQueue top k a -> ([(k, a)], PQueue top k a) +spanWithKey p (PQ q) = case Q.spanWithKey (p . unwrap) q of + (xs, q') -> (fmap (first' unwrap) xs, PQ q') + +-- | Equivalent to @'spanWithKey' (\ k a -> 'not' (p k a)) q@. +breakWithKey :: (Top top, Ord k) => (k -> a -> Bool) -> PQueue top k a -> ([(k, a)], PQueue top k a) +breakWithKey p (PQ q) = case Q.breakWithKey (p . unwrap) q of + (xs, q') -> (fmap (first' unwrap) xs, PQ q') + +-- | /O(n)/. Filter all values that satisfy the predicate. +filter :: (Top top, Ord k) => (a -> Bool) -> PQueue top k a -> PQueue top k a +filter = filterWithKey . const + +-- | /O(n)/. Filter all values that satisfy the predicate. +filterWithKey :: (Top top, Ord k) => (k -> a -> Bool) -> PQueue top k a -> PQueue top k a +filterWithKey p (PQ q) = PQ (Q.filterWithKey (p . unwrap) q) + +-- | /O(n)/. Partition the queue according to a predicate. The first queue contains all elements +-- which satisfy the predicate, the second all elements that fail the predicate. +partition :: (Top top, Ord k) => (a -> Bool) -> PQueue top k a -> (PQueue top k a, PQueue top k a) +partition = partitionWithKey . const + +-- | /O(n)/. Partition the queue according to a predicate. The first queue contains all elements +-- which satisfy the predicate, the second all elements that fail the predicate. +partitionWithKey :: (Top top, Ord k) => (k -> a -> Bool) -> PQueue top k a -> (PQueue top k a, PQueue top k a) +partitionWithKey p (PQ q) = case Q.partitionWithKey (p . unwrap) q of + (q1, q0) -> (PQ q1, PQ q0) + +-- | /O(n)/. Map values and collect the 'Just' results. +mapMaybe :: (Top top, Ord k) => (a -> Maybe b) -> PQueue top k a -> PQueue top k b +mapMaybe = mapMaybeWithKey . const + +-- | /O(n)/. Map values and collect the 'Just' results. +mapMaybeWithKey :: (Top top, Ord k) => (k -> a -> Maybe b) -> PQueue top k a -> PQueue top k b +mapMaybeWithKey f (PQ q) = PQ (Q.mapMaybeWithKey (f . unwrap) q) + +-- | /O(n)/. Map values and separate the 'Left' and 'Right' results. +mapEither :: (Top top, Ord k) => (a -> Either b c) -> PQueue top k a -> (PQueue top k b, PQueue top k c) +mapEither = mapEitherWithKey . const + +-- | /O(n)/. Map values and separate the 'Left' and 'Right' results. +mapEitherWithKey :: (Top top, Ord k) => (k -> a -> Either b c) -> PQueue top k a -> (PQueue top k b, PQueue top k c) +mapEitherWithKey f (PQ q) = case Q.mapEitherWithKey (f . unwrap) q of + (qL, qR) -> (PQ qL, PQ qR) + +-- | /O(n)/. Build a priority queue from the list of (key, value) pairs. +fromList :: (Top top, Ord k) => [(k, a)] -> PQueue top k a +fromList = PQ . Q.fromList . fmap (first' Wrap) + +newtype + FromList k a top = + FromList {runFromList :: [(Wrap top k, a)] -> Q.MinPQueue (Wrap top k) a} + +-- | /O(n)/. Build a priority queue from an ascending list of (key, value) pairs. /The precondition is not checked./ +fromAscList :: (Top top) => [(k, a)] -> PQueue top k a +fromAscList = + PQ . + runFromList (Top.switch (FromList Q.fromAscList) (FromList Q.fromDescList)) . + fmap (first' Wrap) + +-- | /O(n)/. Build a priority queue from a descending list of (key, value) pairs. /The precondition is not checked./ +fromDescList :: (Top top) => [(k, a)] -> PQueue top k a +fromDescList = + PQ . + runFromList (Top.switch (FromList Q.fromDescList) (FromList Q.fromAscList)) . + fmap (first' Wrap) + +-- | /O(n log n)/. Return all keys of the queue in natural order, that is, top keys first. +keys :: (Top top, Ord k) => PQueue top k a -> [k] +keys = fmap fst . toList + +-- | /O(n log n)/. Return all elements of the queue in natural order by key. +elems :: (Top top, Ord k) => PQueue top k a -> [a] +elems = fmap snd . toList + +-- | /O(n log n)/. Equivalent to 'toList'. +assocs :: (Top top, Ord k) => PQueue top k a -> [(k, a)] +assocs = toList + +newtype + ToList k a top = + ToList {runToList :: Q.MinPQueue (Wrap top k) a -> [(Wrap top k, a)]} + +-- | /O(n log n)/. Return all (key, value) pairs in ascending order by key. +toAscList :: (Top top, Ord k) => PQueue top k a -> [(k, a)] +toAscList (PQ q) = + fmap (first' unwrap) $ + runToList (Top.switch (ToList Q.toAscList) (ToList Q.toDescList)) q + +-- | /O(n log n)/. Return all (key, value) pairs in descending order by key. +toDescList :: (Top top, Ord k) => PQueue top k a -> [(k, a)] +toDescList (PQ q) = + fmap (first' unwrap) $ + runToList (Top.switch (ToList Q.toDescList) (ToList Q.toAscList)) q + +-- | /O(n)/. An unordered right fold over the elements of the queue, in no particular order. +foldrU :: (a -> b -> b) -> b -> PQueue top k a -> b +foldrU = foldrWithKeyU . const + +-- | /O(n)/. An unordered right fold over the elements of the queue, in no particular order. +foldrWithKeyU :: (k -> a -> b -> b) -> b -> PQueue top k a -> b +foldrWithKeyU f z (PQ q) = Q.foldrWithKeyU (f . unwrap) z q + +-- | /O(n)/. An unordered left fold over the elements of the queue, in no particular order. +foldlU :: (b -> a -> b) -> b -> PQueue top k a -> b +foldlU f = foldlWithKeyU (const . f) + +-- | /O(n)/. An unordered left fold over the elements of the queue, in no particular order. +foldlWithKeyU :: (b -> k -> a -> b) -> b -> PQueue top k a -> b +foldlWithKeyU f z0 (PQ q) = Q.foldlWithKeyU (\ z -> f z . unwrap) z0 q + +-- | /O(n)/. An unordered traversal over a priority queue, in no particular order. +-- While there is no guarantee in which order the elements are traversed, the resulting +-- priority queue will be perfectly valid. +traverseU :: (Applicative f) => (a -> f b) -> PQueue top k a -> f (PQueue top k b) +traverseU = traverseWithKeyU . const + +-- | /O(n)/. An unordered traversal over a priority queue, in no particular order. +-- While there is no guarantee in which order the elements are traversed, the resulting +-- priority queue will be perfectly valid. +traverseWithKeyU :: (Applicative f) => (k -> a -> f b) -> PQueue top k a -> f (PQueue top k b) +traverseWithKeyU f (PQ q) = PQ <$> Q.traverseWithKeyU (f . unwrap) q + +-- | /O(n)/. Return all keys of the queue in no particular order. +keysU :: PQueue top k a -> [k] +keysU = fmap fst . toListU + +-- | /O(n)/. Return all elements of the queue in no particular order. +elemsU :: PQueue top k a -> [a] +elemsU = fmap snd . toListU + +-- | /O(n)/. Equivalent to 'toListU'. +assocsU :: PQueue top k a -> [(k, a)] +assocsU = toListU + +-- | /O(n)/. Returns all (key, value) pairs in the queue in no particular order. +toListU :: PQueue top k a -> [(k, a)] +toListU (PQ q) = fmap (first' unwrap) (Q.toListU q) + +-- | /O(log n)/. Analogous to @deepseq@ in the @deepseq@ package, but only forces the spine of the binomial heap. +seqSpine :: PQueue top k a -> b -> b +seqSpine (PQ q) = Q.seqSpine q diff --git a/Data/PQueue/Prio/Private.hs b/Data/PQueue/Prio/Private.hs new file mode 100644 index 0000000..94a6162 --- /dev/null +++ b/Data/PQueue/Prio/Private.hs @@ -0,0 +1,99 @@ +{-# LANGUAGE CPP #-} +module Data.PQueue.Prio.Private ( + PQueue(PQ), toList, fromOrderedList, empty, union, unions, + ) where + +import qualified Data.PQueue.Prio.Min as Q +import Data.PQueue.Top (Top, Wrap(Wrap, unwrap)) + +import Control.DeepSeq (NFData (rnf)) +import Control.Applicative ((<$>)) +import Data.Monoid (Monoid(mempty, mappend, mconcat)) +import Data.Traversable (Traversable(traverse)) +import Data.Foldable (Foldable, foldr, foldl) + +import Prelude hiding (map, filter, break, span, takeWhile, dropWhile, splitAt, take, drop, (!!), null, foldr, foldl) + + +#ifdef __GLASGOW_HASKELL__ +import Data.Data (Data, Typeable) +import Text.Read (Lexeme(Ident), lexP, parens, prec, + readPrec, readListPrec, readListPrecDefault) +#else +build :: ((a -> [a] -> [a]) -> [a] -> [a]) -> [a] +build f = f (:) [] +#endif + +first' :: (a -> b) -> (a, c) -> (b, c) +first' f (a, c) = (f a, c) + + +-- | A priority queue where values of type @a@ are annotated with keys of type @k@. +-- The queue supports extracting the element with top key. +newtype PQueue top k a = PQ (Q.MinPQueue (Wrap top k) a) +# if __GLASGOW_HASKELL__ + deriving (Eq, Ord, Data, Typeable) +# else + deriving (Eq, Ord) +# endif + +instance (NFData k, NFData a) => NFData (PQueue top k a) where + rnf (PQ q) = rnf q + + +instance (Top top, Ord k) => Monoid (PQueue top k a) where + mempty = empty + mappend = union + mconcat = unions + +instance (Top top, Ord k, Show k, Show a) => Show (PQueue top k a) where + showsPrec p xs = showParen (p > 10) $ + showString "fromOrderedList " . shows (toList xs) + +instance (Read k, Read a) => Read (PQueue top k a) where +#ifdef __GLASGOW_HASKELL__ + readPrec = parens $ prec 10 $ do + Ident "fromOrderedList" <- lexP + xs <- readPrec + return (fromOrderedList xs) + + readListPrec = readListPrecDefault +#else + readsPrec p = readParen (p > 10) $ \ r -> do + ("fromOrderedList",s) <- lex r + (xs,t) <- reads s + return (fromOrderedList xs,t) +#endif + +instance Functor (PQueue top k) where + fmap f (PQ q) = PQ (fmap f q) + +instance (Top top, Ord k) => Foldable (PQueue top k) where + foldr f z (PQ q) = foldr f z q + foldl f z (PQ q) = foldl f z q + +instance (Top top, Ord k) => Traversable (PQueue top k) where + traverse f (PQ q) = PQ <$> traverse f q + +-- | /O(1)/. Returns the empty priority queue. +empty :: PQueue top k a +empty = PQ Q.empty + +-- | Amortized /O(log(min(n1, n2)))/, worst-case /O(log(max(n1, n2)))/. Returns the union +-- of the two specified queues. +union :: (Top top, Ord k) => PQueue top k a -> PQueue top k a -> PQueue top k a +PQ q1 `union` PQ q2 = PQ (q1 `Q.union` q2) + +-- | The union of a list of queues: (@'unions' == 'List.foldl' 'union' 'empty'@). +unions :: (Top top, Ord k) => [PQueue top k a] -> PQueue top k a +unions qs = PQ (Q.unions [q | PQ q <- qs]) + +-- | /O(n)/. Build a priority queue from a list of (key, value) pairs where every suffix contains the top element at the list head. /The precondition is not checked./ +fromOrderedList :: [(k, a)] -> PQueue top k a +fromOrderedList = PQ . Q.fromAscList . fmap (first' Wrap) + +-- | /O(n log n)/. Return all (key, value) pairs in natural order by key. +-- +-- If the traversal order is irrelevant, consider using 'toListU'. +toList :: (Top top, Ord k) => PQueue top k a -> [(k, a)] +toList (PQ q) = fmap (first' unwrap) (Q.toAscList q) diff --git a/Data/PQueue/Top.hs b/Data/PQueue/Top.hs new file mode 100644 index 0000000..7b72b3b --- /dev/null +++ b/Data/PQueue/Top.hs @@ -0,0 +1,51 @@ +{-# LANGUAGE CPP #-} +module Data.PQueue.Top where + +import Control.DeepSeq (NFData(rnf)) + +import qualified Data.Traversable as Trav +import qualified Data.Foldable as Fold + +#ifdef __GLASGOW_HASKELL__ +import Data.Data (Data, Typeable) +#endif + +class Top top where + switch :: f Min -> f Max -> f top + +newtype Wrap top a = Wrap {unwrap :: a} + deriving (Data, Typeable) + +data Min = Min +data Max = Max + +instance Top Min where switch f _ = f +instance Top Max where switch _ f = f + + +instance (Top top, Eq a) => Eq (Wrap top a) where + Wrap x == Wrap y = x==y + +newtype + Compare a top = Compare {runCompare :: Wrap top a -> Wrap top a -> Ordering} + +instance (Top top, Ord a) => Ord (Wrap top a) where + compare = + runCompare $ + switch + (Compare $ \(Wrap x) (Wrap y) -> compare x y) + (Compare $ \(Wrap x) (Wrap y) -> compare y x) + +instance NFData a => NFData (Wrap top a) where + rnf (Wrap a) = rnf a + +instance Functor (Wrap top) where + fmap f (Wrap a) = Wrap (f a) + +instance Fold.Foldable (Wrap top) where + foldMap f (Wrap a) = f a + foldr f z (Wrap a) = a `f` z + foldl f z (Wrap a) = z `f` a + +instance Trav.Traversable (Wrap top) where + traverse f (Wrap a) = fmap Wrap $ f a diff --git a/pqueue.cabal b/pqueue.cabal index b6ddd9f..afebfdd 100644 --- a/pqueue.cabal +++ b/pqueue.cabal @@ -1,5 +1,5 @@ Name: pqueue -Version: 1.3.2.2 +Version: 1.3.3 Category: Data Structures Author: Louis Wasserman License: BSD3 @@ -30,14 +30,18 @@ Library { , deepseq >= 1.3 && < 1.5 } exposed-modules: + Data.PQueue.Prio Data.PQueue.Prio.Min Data.PQueue.Prio.Max + Data.PQueue Data.PQueue.Min Data.PQueue.Max other-modules: - Data.PQueue.Prio.Internals Data.PQueue.Internals + Data.PQueue.Prio.Private + Data.PQueue.Prio.Internals Data.PQueue.Prio.Max.Internals + Data.PQueue.Top Control.Applicative.Identity if impl(ghc) { default-extensions: DeriveDataTypeable