From 89f95cbcb82c8112c4c75f07f7a4c58b762ea472 Mon Sep 17 00:00:00 2001 From: Henning Thielemann Date: Sat, 11 Mar 2017 18:56:05 +0100 Subject: [PATCH 1/9] PQueue.Prio: unification of MinPQueue and MaxPQueue It is based on the implementation of MaxPQueue but uses new data type (Wrap top) instead of Down. PQueue.Top: generalization of Down data type. Bump version to 1.3.3 --- Data/PQueue/Prio.hs | 478 ++++++++++++++++++++++++++++++++++++++++++++ Data/PQueue/Top.hs | 34 ++++ pqueue.cabal | 4 +- 3 files changed, 515 insertions(+), 1 deletion(-) create mode 100644 Data/PQueue/Prio.hs create mode 100644 Data/PQueue/Top.hs diff --git a/Data/PQueue/Prio.hs b/Data/PQueue/Prio.hs new file mode 100644 index 0000000..70690ea --- /dev/null +++ b/Data/PQueue/Prio.hs @@ -0,0 +1,478 @@ +{-# LANGUAGE CPP #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} + +----------------------------------------------------------------------------- +-- | +-- 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, + fromOrderedList, + -- ** Conversion to lists + keys, + elems, + assocs, + 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.Top (Top, Wrap(Wrap, unwrap)) + +import Control.DeepSeq (NFData (rnf)) +import Control.Applicative (Applicative, (<$>)) +import Data.Monoid (Monoid(mempty, mappend, mconcat)) +import Data.Traversable (Traversable(traverse)) +import Data.Foldable (Foldable, foldr, foldl) +import Data.Maybe (fromMaybe) + +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 + + +type MinPQueue = PQueue Top.Min +type MaxPQueue = PQueue Top.Max + +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 + +-- | /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) + +-- | 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(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) + +-- | /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 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 + +-- | /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) + +-- | /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/Top.hs b/Data/PQueue/Top.hs new file mode 100644 index 0000000..43cbb33 --- /dev/null +++ b/Data/PQueue/Top.hs @@ -0,0 +1,34 @@ +{-# LANGUAGE CPP #-} +module Data.PQueue.Top where + +import Control.DeepSeq (NFData(rnf)) +import qualified Data.Ord as Ord +import Prelude hiding (compare) + +#ifdef __GLASGOW_HASKELL__ +import Data.Data (Data, Typeable) +#endif + +class Top top where + compare :: (Ord a) => Wrap top a -> Wrap top a -> Ordering + +newtype Wrap top a = Wrap {unwrap :: a} + deriving (Data, Typeable) + +data Min = Min +data Max = Max + +instance Top Min where compare (Wrap x) (Wrap y) = Ord.compare x y +instance Top Max where compare (Wrap x) (Wrap y) = Ord.compare y x + +instance (Top top, Eq a) => Eq (Wrap top a) where + Wrap x == Wrap y = x==y + +instance (Top top, Ord a) => Ord (Wrap top a) where + compare = compare + +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) diff --git a/pqueue.cabal b/pqueue.cabal index b6ddd9f..71e151d 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,6 +30,7 @@ Library { , deepseq >= 1.3 && < 1.5 } exposed-modules: + Data.PQueue.Prio Data.PQueue.Prio.Min Data.PQueue.Prio.Max Data.PQueue.Min @@ -38,6 +39,7 @@ Library { Data.PQueue.Prio.Internals Data.PQueue.Internals Data.PQueue.Prio.Max.Internals + Data.PQueue.Top Control.Applicative.Identity if impl(ghc) { default-extensions: DeriveDataTypeable From 63291863913790e77a2db528cbbdff982c1cff9d Mon Sep 17 00:00:00 2001 From: Henning Thielemann Date: Sat, 11 Mar 2017 19:33:54 +0100 Subject: [PATCH 2/9] PQueue.Top: turn into closed-world class --- Data/PQueue/Top.hs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Data/PQueue/Top.hs b/Data/PQueue/Top.hs index 43cbb33..b82ee96 100644 --- a/Data/PQueue/Top.hs +++ b/Data/PQueue/Top.hs @@ -2,15 +2,13 @@ module Data.PQueue.Top where import Control.DeepSeq (NFData(rnf)) -import qualified Data.Ord as Ord -import Prelude hiding (compare) #ifdef __GLASGOW_HASKELL__ import Data.Data (Data, Typeable) #endif class Top top where - compare :: (Ord a) => Wrap top a -> Wrap top a -> Ordering + switch :: f Min -> f Max -> f top newtype Wrap top a = Wrap {unwrap :: a} deriving (Data, Typeable) @@ -18,14 +16,22 @@ newtype Wrap top a = Wrap {unwrap :: a} data Min = Min data Max = Max -instance Top Min where compare (Wrap x) (Wrap y) = Ord.compare x y -instance Top Max where compare (Wrap x) (Wrap y) = Ord.compare y x +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 = compare + 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 From 2329991358d842b88069c9d11f64983bde092138 Mon Sep 17 00:00:00 2001 From: Henning Thielemann Date: Sat, 11 Mar 2017 19:42:35 +0100 Subject: [PATCH 3/9] PQueue.Prio.fromAscList, fromDescList, toAscList, toDescList: implement using Top.switch --- Data/PQueue/Prio.hs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/Data/PQueue/Prio.hs b/Data/PQueue/Prio.hs index 70690ea..253a7c5 100644 --- a/Data/PQueue/Prio.hs +++ b/Data/PQueue/Prio.hs @@ -94,11 +94,15 @@ module Data.PQueue.Prio ( -- * List operations -- ** Conversion from lists fromList, + fromAscList, + fromDescList, fromOrderedList, -- ** Conversion to lists keys, elems, assocs, + toAscList, + toDescList, toList, -- * Unordered operations foldrU, @@ -407,6 +411,24 @@ mapEitherWithKey f (PQ q) = case Q.mapEitherWithKey (f . unwrap) q of 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)/. 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) @@ -423,6 +445,22 @@ elems = fmap snd . 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 log n)/. Return all (key, value) pairs in natural order by key. -- -- If the traversal order is irrelevant, consider using 'toListU'. From 8aa38d45f6ee735ada24c8817ba205c43ca49469 Mon Sep 17 00:00:00 2001 From: Henning Thielemann Date: Sat, 11 Mar 2017 21:18:35 +0100 Subject: [PATCH 4/9] PQueue.Top: instance Foldable Wrap --- Data/PQueue/Top.hs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Data/PQueue/Top.hs b/Data/PQueue/Top.hs index b82ee96..d73b892 100644 --- a/Data/PQueue/Top.hs +++ b/Data/PQueue/Top.hs @@ -3,6 +3,8 @@ module Data.PQueue.Top where import Control.DeepSeq (NFData(rnf)) +import qualified Data.Foldable as Fold + #ifdef __GLASGOW_HASKELL__ import Data.Data (Data, Typeable) #endif @@ -38,3 +40,8 @@ instance NFData a => NFData (Wrap top a) where 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 From 063c81e17a1208072a291fede3b1aea6af410bbe Mon Sep 17 00:00:00 2001 From: Henning Thielemann Date: Sat, 11 Mar 2017 21:26:10 +0100 Subject: [PATCH 5/9] PQueue: unification of MinQueue and MaxQueue PQueue.Prio.PQ: export data constructor temporarily It is based on the implementation of MaxQueue but uses Wrap instead of Down. --- Data/PQueue.hs | 394 ++++++++++++++++++++++++++++++++++++++++++++ Data/PQueue/Prio.hs | 2 +- pqueue.cabal | 1 + 3 files changed, 396 insertions(+), 1 deletion(-) create mode 100644 Data/PQueue.hs diff --git a/Data/PQueue.hs b/Data/PQueue.hs new file mode 100644 index 0000000..85492d3 --- /dev/null +++ b/Data/PQueue.hs @@ -0,0 +1,394 @@ +{-# 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 qualified Data.PQueue.Prio as Prio +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.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) = [a | Wrap a <- 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 (\ (Wrap x) -> Wrap <$> f x) 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 (\ (Wrap a) -> Wrap (f a)) 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 :: Prio.PQueue top k a -> Queue top k +keysQueue (Prio.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 index 253a7c5..edfb929 100644 --- a/Data/PQueue/Prio.hs +++ b/Data/PQueue/Prio.hs @@ -33,7 +33,7 @@ -- these functions. ----------------------------------------------------------------------------- module Data.PQueue.Prio ( - PQueue, + PQueue(PQ), MinPQueue, MaxPQueue, -- * Construction diff --git a/pqueue.cabal b/pqueue.cabal index 71e151d..f938465 100644 --- a/pqueue.cabal +++ b/pqueue.cabal @@ -33,6 +33,7 @@ Library { Data.PQueue.Prio Data.PQueue.Prio.Min Data.PQueue.Prio.Max + Data.PQueue Data.PQueue.Min Data.PQueue.Max other-modules: From dc6187bb83c2a46bf3fd9faee44c910afc69f2e0 Mon Sep 17 00:00:00 2001 From: Henning Thielemann Date: Sat, 11 Mar 2017 21:34:45 +0100 Subject: [PATCH 6/9] PQueue.mapU: simplify using fmap on Wrap --- Data/PQueue.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Data/PQueue.hs b/Data/PQueue.hs index 85492d3..a70e3c3 100644 --- a/Data/PQueue.hs +++ b/Data/PQueue.hs @@ -272,7 +272,7 @@ mapEither f (Q q) = (Q q0, Q q1) -- | /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 (\ (Wrap a) -> Wrap (f a)) q) +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 From a18fbcd981084e0e5a82fd7118eb5e190f676a20 Mon Sep 17 00:00:00 2001 From: Henning Thielemann Date: Sat, 11 Mar 2017 21:35:20 +0100 Subject: [PATCH 7/9] PQueue.take: simplify using 'unwrap' --- Data/PQueue.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Data/PQueue.hs b/Data/PQueue.hs index a70e3c3..d692013 100644 --- a/Data/PQueue.hs +++ b/Data/PQueue.hs @@ -216,7 +216,7 @@ Q q !! n = unwrap ((Min.!!) q n) -- | /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) = [a | Wrap a <- Min.take k q] +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 From 1276837628a010d357f3f9b5326a95bd76e7f2d4 Mon Sep 17 00:00:00 2001 From: Henning Thielemann Date: Sat, 11 Mar 2017 21:42:16 +0100 Subject: [PATCH 8/9] PQueue.Top: instance Traversable Wrap PQueue.mapMaybe: use it here --- Data/PQueue.hs | 3 ++- Data/PQueue/Top.hs | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Data/PQueue.hs b/Data/PQueue.hs index d692013..81eec63 100644 --- a/Data/PQueue.hs +++ b/Data/PQueue.hs @@ -97,6 +97,7 @@ 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) @@ -262,7 +263,7 @@ partition p (Q q) = (Q q0, Q q1) -- | /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 (\ (Wrap x) -> Wrap <$> f x) q) +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) diff --git a/Data/PQueue/Top.hs b/Data/PQueue/Top.hs index d73b892..7b72b3b 100644 --- a/Data/PQueue/Top.hs +++ b/Data/PQueue/Top.hs @@ -3,6 +3,7 @@ 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__ @@ -45,3 +46,6 @@ 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 From 6258cf9210fe8a856c2e2cc941efa9f4eea5123a Mon Sep 17 00:00:00 2001 From: Henning Thielemann Date: Sat, 11 Mar 2017 22:01:41 +0100 Subject: [PATCH 9/9] PQueue.Prio.Private: extracted data type and instance definitions from PQueue.Prio This way, we do no longer need to expose the PQ constructor. --- Data/PQueue.hs | 6 +-- Data/PQueue/Prio.hs | 89 ++------------------------------- Data/PQueue/Prio/Private.hs | 99 +++++++++++++++++++++++++++++++++++++ pqueue.cabal | 3 +- 4 files changed, 107 insertions(+), 90 deletions(-) create mode 100644 Data/PQueue/Prio/Private.hs diff --git a/Data/PQueue.hs b/Data/PQueue.hs index 81eec63..e4ed1bb 100644 --- a/Data/PQueue.hs +++ b/Data/PQueue.hs @@ -89,7 +89,7 @@ module Data.PQueue ( import qualified Data.PQueue.Min as Min import qualified Data.PQueue.Top as Top -import qualified Data.PQueue.Prio as Prio +import Data.PQueue.Prio.Private (PQueue(PQ)) import Data.PQueue.Top (Top, Wrap(Wrap, unwrap)) import Control.DeepSeq (NFData(rnf)) @@ -387,8 +387,8 @@ 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 :: Prio.PQueue top k a -> Queue top k -keysQueue (Prio.PQ q) = Q (Min.keysQueue q) +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 diff --git a/Data/PQueue/Prio.hs b/Data/PQueue/Prio.hs index edfb929..7c6ed8a 100644 --- a/Data/PQueue/Prio.hs +++ b/Data/PQueue/Prio.hs @@ -1,6 +1,3 @@ -{-# LANGUAGE CPP #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} - ----------------------------------------------------------------------------- -- | -- Module : Data.PQueue.Prio @@ -33,7 +30,7 @@ -- these functions. ----------------------------------------------------------------------------- module Data.PQueue.Prio ( - PQueue(PQ), + PQueue, MinPQueue, MaxPQueue, -- * Construction @@ -122,84 +119,23 @@ module Data.PQueue.Prio ( 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.DeepSeq (NFData (rnf)) import Control.Applicative (Applicative, (<$>)) -import Data.Monoid (Monoid(mempty, mappend, mconcat)) -import Data.Traversable (Traversable(traverse)) -import Data.Foldable (Foldable, foldr, foldl) import Data.Maybe (fromMaybe) 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 - - type MinPQueue = PQueue Top.Min type MaxPQueue = PQueue Top.Max -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 -- | /O(1)/. Constructs a singleton priority queue. singleton :: k -> a -> PQueue top k a @@ -215,15 +151,6 @@ insert k a (PQ q) = PQ (Q.insert (Wrap k) a q) 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) --- | 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(1)/. Checks if this priority queue is empty. null :: PQueue top k a -> Bool null (PQ q) = Q.null q @@ -429,10 +356,6 @@ fromDescList = runFromList (Top.switch (FromList Q.fromDescList) (FromList Q.fromAscList)) . fmap (first' Wrap) --- | /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 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 @@ -461,12 +384,6 @@ toDescList (PQ q) = fmap (first' unwrap) $ runToList (Top.switch (ToList Q.toDescList) (ToList Q.toAscList)) q --- | /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) - -- | /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 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/pqueue.cabal b/pqueue.cabal index f938465..afebfdd 100644 --- a/pqueue.cabal +++ b/pqueue.cabal @@ -37,8 +37,9 @@ Library { 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