packages feed

stable-heap 0.1.0.0 → 0.2.1.0

raw patch · 4 files changed

+620/−96 lines, 4 filesdep +QuickCheckdep +tastydep +tasty-quickcheckdep ~basePVP ok

version bump matches the API change (PVP)

Dependencies added: QuickCheck, tasty, tasty-quickcheck, transformers

Dependency ranges changed: base

API changes (from Hackage documentation)

- Data.Heap.Stable: instance (Eq k, Eq a) => Eq (Heap k a)
- Data.Heap.Stable: instance (Monoid k, Ord k) => Alternative (Heap k)
- Data.Heap.Stable: instance (Monoid k, Ord k) => Applicative (Heap k)
- Data.Heap.Stable: instance (Monoid k, Ord k) => Monad (Heap k)
- Data.Heap.Stable: instance (Monoid k, Ord k) => MonadPlus (Heap k)
- Data.Heap.Stable: instance (Ord k, Ord a) => Ord (Heap k a)
- Data.Heap.Stable: instance (Ord k, Read k, Read a) => Read (Heap k a)
- Data.Heap.Stable: instance (Show k, Show a) => Show (Heap k a)
- Data.Heap.Stable: instance Foldable (Heap k)
- Data.Heap.Stable: instance Functor (Heap k)
- Data.Heap.Stable: instance Ord k => IsList (Heap k a)
- Data.Heap.Stable: instance Ord k => Monoid (Heap k a)
- Data.Heap.Stable: instance Traversable (Heap k)
- Data.Heap.Stable: minViewWithKey :: Ord k => Heap k a -> Maybe (Heap k a, (k, a), Heap k a)
- Data.Heap.Stable: toListAsc :: Ord k => Heap k a -> [(k, a)]
- Data.Heap.Stable: union :: Ord k => Heap k a -> Heap k a -> Heap k a
+ Data.Heap.Stable: EmptyView :: MinView k v
+ Data.Heap.Stable: MinView :: Heap k v -> k -> v -> Heap k v -> MinView k v
+ Data.Heap.Stable: append :: Ord k => Heap k a -> Heap k a -> Heap k a
+ Data.Heap.Stable: appends :: Ord k => [Heap k a] -> Heap k a
+ Data.Heap.Stable: data MinView k v
+ Data.Heap.Stable: foldMapWithKey :: Monoid b => (k -> a -> b) -> Heap k a -> b
+ Data.Heap.Stable: instance (GHC.Base.Monoid k, GHC.Classes.Ord k) => GHC.Base.Alternative (Data.Heap.Stable.Heap k)
+ Data.Heap.Stable: instance (GHC.Base.Monoid k, GHC.Classes.Ord k) => GHC.Base.Applicative (Data.Heap.Stable.Heap k)
+ Data.Heap.Stable: instance (GHC.Base.Monoid k, GHC.Classes.Ord k) => GHC.Base.Monad (Data.Heap.Stable.Heap k)
+ Data.Heap.Stable: instance (GHC.Base.Monoid k, GHC.Classes.Ord k) => GHC.Base.MonadPlus (Data.Heap.Stable.Heap k)
+ Data.Heap.Stable: instance (GHC.Classes.Eq k, GHC.Classes.Eq a) => GHC.Classes.Eq (Data.Heap.Stable.Heap k a)
+ Data.Heap.Stable: instance (GHC.Classes.Eq k, GHC.Classes.Eq v) => GHC.Classes.Eq (Data.Heap.Stable.MinView k v)
+ Data.Heap.Stable: instance (GHC.Classes.Ord k, GHC.Classes.Ord a) => GHC.Classes.Ord (Data.Heap.Stable.Heap k a)
+ Data.Heap.Stable: instance (GHC.Classes.Ord k, GHC.Read.Read k, GHC.Read.Read a) => GHC.Read.Read (Data.Heap.Stable.Heap k a)
+ Data.Heap.Stable: instance (GHC.Show.Show k, GHC.Show.Show a) => GHC.Show.Show (Data.Heap.Stable.Heap k a)
+ Data.Heap.Stable: instance (GHC.Show.Show k, GHC.Show.Show v) => GHC.Show.Show (Data.Heap.Stable.MinView k v)
+ Data.Heap.Stable: instance Data.Foldable.Foldable (Data.Heap.Stable.Heap k)
+ Data.Heap.Stable: instance Data.Traversable.Traversable (Data.Heap.Stable.Heap k)
+ Data.Heap.Stable: instance GHC.Base.Functor (Data.Heap.Stable.Heap k)
+ Data.Heap.Stable: instance GHC.Classes.Ord k => GHC.Base.Monoid (Data.Heap.Stable.Heap k a)
+ Data.Heap.Stable: instance GHC.Classes.Ord k => GHC.Base.Semigroup (Data.Heap.Stable.Heap k a)
+ Data.Heap.Stable: instance GHC.Classes.Ord k => GHC.IsList.IsList (Data.Heap.Stable.Heap k a)
+ Data.Heap.Stable: mapWithKey :: (k -> a -> b) -> Heap k a -> Heap k b
+ Data.Heap.Stable: minView :: Ord k => Heap k a -> MinView k a
+ Data.Heap.Stable: null :: Heap k a -> Bool
+ Data.Heap.Stable: size :: Heap k a -> Int
+ Data.Heap.Stable: toAscList :: Ord k => Heap k a -> [(k, a)]
+ Data.Heap.Stable: traverseKeys :: (Applicative f, Ord k2) => (k1 -> f k2) -> Heap k1 a -> f (Heap k2 a)
+ Data.Heap.Stable: traverseWithKey :: Applicative f => (k -> a -> f b) -> Heap k a -> f (Heap k b)

Files

bench/Bench.hs view
@@ -27,7 +27,7 @@       , create "heap" (map (Heap.priority &&& Heap.payload) . Foldable.toList) (\h k v -> Heap.insert (Heap.Entry k v) h) Heap.empty       ]     , bgroup "stable"-      [ create "stable-heap" Stable.toListAsc Stable.snoc Stable.empty+      [ create "stable-heap" Stable.toAscList Stable.snoc Stable.empty       , create "fingertree" (unfoldr FingerTree.minViewWithKey) (\q k v -> FingerTree.add k v q) FingerTree.empty       ]     ]
src/Data/Heap/Stable.hs view
@@ -1,177 +1,509 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DeriveFoldable #-} {-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE Trustworthy #-}  -- |+-- -- Module      :  Data.Heap.Stable--- Copyright   :  (C) Jake McArthur 2015+-- Copyright   :  (C) 2015-2016 Jake McArthur -- License     :  MIT -- Maintainer  :  Jake.McArthur@gmail.com -- Stability   :  experimental ----- This module provides an implementation of stable heaps, or fair--- priority queues. The data structure is a fairly simple tweak to add--- stability to the lazy pairing heaps described in--- /Purely Functional Data Structures/, by Chris Okasaki.+-- A simple implementation of stable heaps (fair priority queues), modeled as a+-- sequence of key-value pairs, allowing duplicates, with efficient access to+-- the leftmost key-value pair having the smallest key. ----- Unless stated otherwise, the documented asymptotic efficiencies of--- functions on 'Heap' assume that arguments are already in WHNF and--- that the result is to be evaluated to WHNF.+-- The data structure is a modification of the lazy pairing heaps described in+-- Chris Okasaki's /Purely Functional Data Structures/.+--+-- A 'Heap' has both heap-like and sequence-like properties. Most of the+-- traversals defined in this module work in sequence order; those that work in+-- key order are explicitly documented as such.+--+-- Unless stated otherwise, the documented asymptotic efficiencies of functions+-- on 'Heap' assume that arguments are already in WHNF and that the result is to+-- be evaluated to WHNF. module Data.Heap.Stable-       ( Heap ()+       ( -- $setup+         Heap ()+         -- * Query+       , Data.Heap.Stable.null+       , size+         -- * Construction        , empty        , singleton-       , union-       , minViewWithKey+       , append+       , appends        , cons        , snoc-       , foldrWithKey-       , toList-       , toListAsc-       , fromList+         -- * Minimum view+       , MinView (..)+       , minView+         -- * Traversal+         -- ** Map        , bimap        , mapKeys+       , mapWithKey+       , traverseKeys+       , traverseWithKey+         -- ** Fold+       , foldrWithKey+       , foldMapWithKey+         -- * List operations+         -- ** Conversion from lists+       , fromList+         -- ** Conversion to lists+       , toList+       , toAscList        ) where +import Prelude hiding (null)+ import qualified Control.Applicative as Applicative+import Control.Applicative hiding (Alternative (..)) import Control.Monad import Data.List (foldl', unfoldr)-import Data.Monoid+import qualified Data.List+import Data.Foldable (Foldable)+import Data.Traversable (Traversable)  import qualified GHC.Exts --- | Semantically, @Heap k a@ is equivalent to @[(k, a)]@, but its--- operations have different efficiencies.+#if MIN_VERSION_base(4,9,0)+-- Data.Semigroup was added in base-4.9+import Data.Semigroup as Sem+#endif+#if !(MIN_VERSION_base(4,8,0))+-- starting with base-4.8, Monoid is rexported from Prelude+import Data.Monoid+#endif++-- |+--+-- @Heap k a@ is equivalent to @[(k, a)]@, but its operations have different+-- efficiencies. data Heap k a-  = Heap !(Heap k a) (Heap k a) !k a (Heap k a) !(Heap k a)+  = Heap !Int !(Heap k a) (Heap k a) !k a (Heap k a) !(Heap k a)   | Empty   deriving (Functor, Foldable, Traversable) --- | @toList empty = []@+-- |+--+-- 'True' if the 'Heap' is empty and 'False' otherwise.+--+-- /O(1)/.+--+-- >>> any null [the, quick, brown, fox]+-- False+--+-- >>> null empty+-- True+--+-- prop> null xs == Data.List.null (toList xs)+null :: Heap k a -> Bool+null Empty = True+null Heap {} = False++-- |+--+-- The number of key-value pairs in the heap.+--+-- /O(1)/.+--+-- >>> map size [the, quick, brown, fox]+-- [3,5,5,3]+--+-- >>> size empty+-- 0+--+-- prop> size xs == length (toList xs)+size :: Heap k a -> Int+size Empty = 0+size (Heap s _ _ _ _ _ _) = s++-- |+-- An empty heap.+--+-- >>> empty+-- fromList [] empty :: Heap k a empty = Empty --- | /O(1)/.+-- | ----- > toList (singleton k v) = [(k, v)]+-- Construct a heap containing a single key-value pair.+--+-- /O(1)/.+--+-- >>> singleton "foo" 42+-- fromList [("foo",42)]+--+-- prop> toList (singleton k v) == [(k, v)] singleton :: k -> a -> Heap k a-singleton k v = Heap empty empty k v empty empty+singleton k v = Heap 1 empty empty k v empty empty --- | /O(1)/.+-- | ----- > toList (xs `union` ys) = toList xs ++ toList ys-union :: Ord k => Heap k a -> Heap k a -> Heap k a-Empty `union` ys = ys-xs `union` Empty = xs-xs@(Heap l1 ls1 k1 v1 rs1 r1) `union` ys@(Heap l2 ls2 k2 v2 rs2 r2)+-- Append two heaps, preserving sequential ordering.+--+-- /O(1)/.+--+-- >>> append empty the+-- fromList [('t',0),('h',1),('e',2)]+--+-- >>> append the empty+-- fromList [('t',0),('h',1),('e',2)]+--+-- >>> append the fox+-- fromList [('t',0),('h',1),('e',2),('f',0),('o',1),('x',2)]+--+-- prop> toList (xs `append` ys) == toList xs ++ toList ys+append :: Ord k => Heap k a -> Heap k a -> Heap k a+Empty `append` ys = ys+xs `append` Empty = xs+xs@(Heap sx l1 ls1 k1 v1 rs1 r1) `append` ys@(Heap sy l2 ls2 k2 v2 rs2 r2)   | k1 <= k2 =       case r1 of-        Empty            -> Heap l1 ls1 k1 v1  rs1                     ys-        Heap _ _ _ _ _ _ -> Heap l1 ls1 k1 v1 (rs1 `union` (r1 `union` ys)) Empty+        Empty   -> Heap (sx+sy) l1 ls1 k1 v1  rs1                     ys+        Heap {} -> Heap (sx+sy) l1 ls1 k1 v1 (rs1 `append` (r1 `append` ys)) Empty   | otherwise =       case l2 of-        Empty            -> Heap         xs                     ls2  k2 v2 rs2 r2-        Heap _ _ _ _ _ _ -> Heap Empty ((xs `union` l2) `union` ls2) k2 v2 rs2 r2+        Empty   -> Heap (sx+sy)        xs                        ls2  k2 v2 rs2 r2+        Heap {} -> Heap (sx+sy) Empty ((xs `append` l2) `append` ls2) k2 v2 rs2 r2 --- | Split the 'Heap' at the leftmost occurrence of the smallest key--- contained in the 'Heap'.+-- | ----- When the 'Heap' is empty, /O(1)/. When the 'Heap' is not empty,--- finding the key and value is /O(1)/, and evaluating the remainder--- of the heap to the left or right of the key-value pair is amortized--- /O(log n)/.+-- Sequentially append an arbitrary number of heaps. ----- > toList xs =--- > case minViewWithKey xs of--- >   Nothing -> []--- >   Just (l, kv, r) -> toList l ++ [kv] ++ toList r-minViewWithKey :: Ord k => Heap k a -> Maybe (Heap k a, (k, a), Heap k a)-minViewWithKey Empty = Nothing-minViewWithKey (Heap l ls k v rs r) = Just (l `union` ls, (k, v), rs `union` r)+-- /O(m)/, where /m/ is the length of the input list.+--+-- >>> appends [the, quick, fox]+-- fromList [('t',0),('h',1),('e',2),('q',0),('u',1),('i',2),('c',3),('k',4),('f',0),('o',1),('x',2)]+--+-- prop> toList (appends xss) == concatMap toList xss+appends :: Ord k => [Heap k a] -> Heap k a+appends = foldl' append empty  -- |--- > mempty  = empty--- > mappend = union+--+-- View of the minimum key of a heap, split out from everything occurring to its+-- left and to its right in the sequence.+data MinView k v+  = EmptyView+  | MinView (Heap k v) k v (Heap k v)+  deriving (Eq, Show)++-- |+--+-- Split the 'Heap' at the /leftmost/ occurrence of the smallest key contained+-- in the 'Heap'.+--+-- When the 'Heap' is empty, /O(1)/. When the 'Heap' is not empty, finding the+-- key and value is /O(1)/, and evaluating the remainder of the heap to the left+-- or right of the key-value pair is amortized /O(log n)/.+--+-- >>> minView empty+-- EmptyView+--+-- >>> minView the+-- MinView (fromList [('t',0),('h',1)]) 'e' 2 (fromList [])+--+-- >>> minView (append the the)+-- MinView (fromList [('t',0),('h',1)]) 'e' 2 (fromList [('t',0),('h',1),('e',2)])+--+-- >>> minView quick+-- MinView (fromList [('q',0),('u',1),('i',2)]) 'c' 3 (fromList [('k',4)])+--+-- >>> minView brown+-- MinView (fromList []) 'b' 0 (fromList [('r',1),('o',2),('w',3),('n',4)])+--+-- >>> minView fox+-- MinView (fromList []) 'f' 0 (fromList [('o',1),('x',2)])+--+-- Here is a model implementation of 'minView':+--+-- >>> :{+-- let { minViewModel xs =+--         case toList xs of+--           []        -> EmptyView+--           keyValues ->+--             let minKey          = minimum (map fst keyValues)+--                 (l, (k, v) : r) = break (\(key, _) -> key == minKey) keyValues+--             in MinView (fromList l) k v (fromList r)+--     }+-- :}+--+-- The following property looks different from the others in this module due to+-- working around a limitation of doctest.+--+-- >>> quickCheck $ \xs -> minView (xs :: Heap Integer Integer) == minViewModel xs+-- +++ OK, passed 100 tests.+minView :: Ord k => Heap k a -> MinView k a+minView Empty = EmptyView+minView (Heap _ l ls k v rs r) = MinView (l `append` ls) k v (rs `append` r)++#if MIN_VERSION_base(4,9,0)+instance Ord k => Sem.Semigroup (Heap k a) where+  (<>) = append+#endif++-- |+--+-- Formed from 'empty' and 'append' instance Ord k => Monoid (Heap k a) where   mempty = empty-  mappend = union --- | /O(1)/.+#if MIN_VERSION_base(4,11,0)+  -- starting with base-4.11, mappend definitions are redundant;+  -- at some point `mappend` will be removed from `Monoid`+#elif MIN_VERSION_base(4,9,0)+  mappend = (Sem.<>)+#else+  -- prior to GHC 8.0 / base-4.9 where no `Semigroup` class existed+  mappend = append+#endif++-- | ----- > toList (cons k v xs) = (k, v) : toList xs+-- Prepend a key-value pair to the beginning of a 'Heap'.+--+-- /O(1)/.+--+-- >>> cons 'a' 0 fox+-- fromList [('a',0),('f',0),('o',1),('x',2)]+--+-- prop> toList (cons k v xs) == (k, v) : toList xs cons :: Ord k => k -> a -> Heap k a -> Heap k a-cons k v = (singleton k v <>)+cons k v = (singleton k v `append`) --- | /O(1)/.+-- | ----- > toList (snoc xs k v) = toList xs ++ [(k, v)]+-- Append a key-value pair to the end of a 'Heap'.+--+-- /O(1)/.+--+-- >>> snoc fox 'y' 0+-- fromList [('f',0),('o',1),('x',2),('y',0)]+--+-- prop> toList (snoc xs k v) == toList xs ++ [(k, v)] snoc :: Ord k => Heap k a -> k -> a -> Heap k a-snoc xs k v = xs <> singleton k v+snoc xs k v = xs `append` singleton k v --- | > foldrWithKey f z xs = foldr (uncurry f) z (toList xs)+-- |+--+-- Like 'foldr', but provides access to the key for each value in the folding+-- function.+--+-- >>> foldrWithKey (\k v kvs -> (k, v) : kvs) [] fox+-- [('f',0),('o',1),('x',2)]+--+-- prop> let f k v acc = g `apply` k `apply` v `apply` acc in foldrWithKey f z xs == foldr (uncurry f) z (toList xs) foldrWithKey :: (k -> a -> b -> b) -> b -> Heap k a -> b foldrWithKey f = flip go   where     go Empty z = z-    go (Heap l ls k v rs r) z = go l (go ls (f k v (go rs (go r z))))+    go (Heap _ l ls k v rs r) z = go l (go ls (f k v (go rs (go r z)))) --- | List the key-value pairs in a 'Heap' in occurrence order. This is the semantic+-- |+--+-- List the key-value pairs in a 'Heap' in sequence order. This is the semantic -- function for 'Heap'. --+-- >>> toList empty+-- []+--+-- >>> toList the+-- [('t',0),('h',1),('e',2)]+--+-- >>> toList quick+-- [('q',0),('u',1),('i',2),('c',3),('k',4)]+--+-- >>> toList brown+-- [('b',0),('r',1),('o',2),('w',3),('n',4)]+--+-- >>> toList fox+-- [('f',0),('o',1),('x',2)]+-- -- /O(n)/ when the spine of the result is evaluated fully.+--+-- prop> toList (fromList xs) == xs+-- prop> fromList (toList xs) == xs toList :: Heap k a -> [(k, a)] toList = foldrWithKey (\k v xs -> (k, v) : xs) [] --- | List the key-value pairs in a 'Heap' in key order.+-- | --+-- List the key-value pairs in a 'Heap' in key order.+-- -- /O(n log n)/ when the spine of the result is evaluated fully.-toListAsc :: Ord k => Heap k a -> [(k, a)]-toListAsc = unfoldr f+--+-- >>> toAscList empty+-- []+--+-- >>> toAscList the+-- [('e',2),('h',1),('t',0)]+--+-- >>> toAscList quick+-- [('c',3),('i',2),('k',4),('q',0),('u',1)]+--+-- >>> toAscList brown+-- [('b',0),('n',4),('o',2),('r',1),('w',3)]+--+-- >>> toAscList fox+-- [('f',0),('o',1),('x',2)]+--+-- prop> toAscList xs == Data.List.sortOn fst (toList xs)+toAscList :: Ord k => Heap k a -> [(k, a)]+toAscList = unfoldr f   where     f xs =-      case minViewWithKey xs of-        Nothing -> Nothing-        Just (l, kv, r) -> Just (kv, l <> r)+      case minView xs of+        EmptyView -> Nothing+        MinView l k v r -> Just ((k, v), l `append` r) --- | Construct a 'Heap' from a list of key-value pairs.+-- | --+-- Construct a 'Heap' from a list of key-value pairs.+-- -- /O(n)/.+--+-- >>> fromList (zip [0..3] [4..])+-- fromList [(0,4),(1,5),(2,6),(3,7)]+--+-- prop> toList (fromList xs) == xs+-- prop> fromList (toList xs) == xs fromList :: Ord k => [(k, a)] -> Heap k a fromList = foldl' (\acc (k, v) -> snoc acc k v) empty --- | > toList (bimap f g xs) = map (f *** g) (toList xs)+-- |+--+-- >>> bimap succ (*10) fox+-- fromList [('g',0),('p',10),('y',20)]+--+-- prop> toList (bimap (apply f) (apply g) xs) == map (\(k, v) -> (apply f k, apply g v)) (toList xs) bimap :: Ord k2 => (k1 -> k2) -> (a -> b) -> Heap k1 a -> Heap k2 b bimap f g = go   where     go Empty = Empty-    go (Heap l ls k v rs r) = go l <> go ls <> singleton (f k) (g v) <> go rs <> go r+    go (Heap _ l ls k v rs r) =+      go l `append` go ls `append` singleton (f k) (g v) `append` go rs `append` go r --- | > toList (mapKeys f xs) = map (first f) (toList xs)+-- |+--+-- >>> mapKeys succ fox+-- fromList [('g',0),('p',1),('y',2)]+--+-- prop> toList (mapKeys (apply f) xs) == map (\(k, v) -> (apply f k, v)) (toList xs) mapKeys :: Ord k2 => (k1 -> k2) -> Heap k1 a -> Heap k2 a mapKeys f = bimap f id --- | Same semantics as @WriterT k []@+-- |+--+-- Map a function over all values in a heap.+--+-- /O(1)/ when evaluating to WHNF. /O(n)/ when evaluating to NF.+--+-- >>> mapWithKey (\k v -> (k,v)) fox+-- fromList [('f',('f',0)),('o',('o',1)),('x',('x',2))]+--+-- prop> let f k v = g `apply` k `apply` v in mapWithKey f xs == fromList (map (\(k, v) -> (k, f k v)) (toList xs))+mapWithKey :: (k -> a -> b) -> Heap k a -> Heap k b+mapWithKey f = go+  where+    go Empty = Empty+    go (Heap n l ls k v rs r) = Heap n (go l) (go ls) k (f k v) (go rs) (go r)++-- |+--+-- Fold the keys and values in the heap using the given monoid, such that+--+-- /O(n)/.+--+-- >>> foldMapWithKey (\k v -> [(k,v)]) fox+-- [('f',0),('o',1),('x',2)]+--+-- prop> let f k v = g `apply` k `apply` v :: [Integer] in foldMapWithKey f xs == Data.Foldable.fold (mapWithKey f xs)+foldMapWithKey :: Monoid b => (k -> a -> b) -> Heap k a -> b+foldMapWithKey f = go+  where+    go Empty = mempty+    go (Heap _ l ls k v rs r) =+      go l `mappend` go ls `mappend` f k v `mappend` go rs `mappend` go r++-- |+--+-- Behaves exactly like a regular traverse except that the traversing function+-- also has access to the key associated with a value, such that+--+-- /O(n)/.+--+-- >>> traverseWithKey (\k v -> print (k, v) >> return (succ k, v)) fox+-- ('f',0)+-- ('o',1)+-- ('x',2)+-- fromList [('f',('g',0)),('o',('p',1)),('x',('y',2))]+--+-- prop> let f k v = g `apply` k `apply` v :: ([Integer], Integer) in traverseWithKey f xs == (fromList <$> traverse (\(k, v) -> (,) k <$> f k v) (toList xs))++-- > traverseWithKey f = fromList . traverse ((k, v) -> (,) k $ f k v) . toList+traverseWithKey :: Applicative f => (k -> a -> f b) -> Heap k a -> f (Heap k b)+traverseWithKey f = go+  where+    go Empty = pure Empty+    go (Heap n l ls k v rs r) = Heap n <$> go l <*> go ls <*> pure k <*> f k v <*> go rs <*> go r++-- |+--+-- Behaves exactly like a regular traverse except that it's over the keys+-- instead of the values.+--+-- /O(n)/.+--+-- >>> traverseKeys (\k -> print k >> return (succ k)) fox+-- 'f'+-- 'o'+-- 'x'+-- fromList [('g',0),('p',1),('y',2)]+--+-- prop> traverseKeys (apply f) xs == (fromList <$> traverse (\(k, v) -> flip (,) v <$> (apply f k :: ([Integer], Integer))) (toList xs))+traverseKeys :: (Applicative f, Ord k2) => (k1 -> f k2) -> Heap k1 a -> f (Heap k2 a)+traverseKeys f = go+  where+    go Empty = pure Empty+    go (Heap _ l ls k v rs r) = go l <.> go ls <.> ((`singleton` v) <$> f k) <.> go rs <.> go r+    (<.>) = liftA2 append++-- |+--+-- Equivalent to @WriterT k []@ instance (Monoid k, Ord k) => Applicative (Heap k) where   pure = singleton mempty   Empty <*> _ = Empty   _ <*> Empty = Empty-  (Heap fl fls fk f frs fr) <*> xs+  Heap _ fl fls fk f frs fr <*> xs     =  (fl  <*>         xs)-    <> (fls <*>         xs)-    <> (bimap (fk <>) f xs)-    <> (frs <*>         xs)-    <> (fr  <*>         xs)+    `append` (fls <*> xs)+    `append` bimap (fk `mappend`) f xs+    `append` (frs <*> xs)+    `append` (fr  <*> xs) --- | Same semantics as @WriterT k []@+-- |+--+-- Equivalent to @WriterT k []@ instance (Monoid k, Ord k) => Monad (Heap k) where   return = pure   Empty >>= _ = Empty-  Heap xl xls xk x xrs xr >>= f+  Heap _ xl xls xk x xrs xr >>= f     =  (xl  >>= f)-    <> (xls >>= f)-    <> (mapKeys (xk <>) (f x))-    <> (xrs >>= f)-    <> (xr  >>= f)+    `append` (xls >>= f)+    `append` mapKeys (xk `mappend`) (f x)+    `append` (xrs >>= f)+    `append` (xr  >>= f)  instance (Show k, Show a) => Show (Heap k a) where   showsPrec d h = showParen (d > 10) $ showString "fromList " . shows (toList h)@@ -187,24 +519,60 @@   fromList = fromList   toList   = toList --- | > xs == ys = toList xs == toList ys+-- |+--+-- prop> (xs == ys) == (toList xs == toList ys) instance (Eq k, Eq a) => Eq (Heap k a) where   xs == ys = toList xs == toList ys --- | > compare xs ys = compare (toList xs) (toList ys)+-- |+--+-- prop> compare xs ys == compare (toList xs) (toList ys) instance (Ord k, Ord a) => Ord (Heap k a) where   compare xs ys = compare (toList xs) (toList ys)  -- |--- > empty = empty--- > (<|>) = union+--+-- Formed from 'empty' and 'append' instance (Monoid k, Ord k) => Applicative.Alternative (Heap k) where   empty = mempty   (<|>) = mappend  -- |--- > mzero = empty--- > mplus = union+--+-- Formed from 'empty' and 'append' instance (Monoid k, Ord k) => MonadPlus (Heap k) where   mzero = mempty   mplus = mappend++-- $setup+--+-- We use QuickCheck to verify the properties given in this documentation. Here+-- is the necessary setup code:+--+-- >>> import Test.QuickCheck+-- >>> import Test.QuickCheck.Function+-- >>> :{+-- instance (Arbitrary k, Arbitrary v, Ord k) => Arbitrary (Heap k v) where+--   arbitrary = fromList <$> arbitrary+--   shrink = map fromList . shrink . toList+-- :}+--+-- Here are some example values used in the documentation for this module:+--+-- >>> let the   = fromList (zip "the"   [0..])+-- >>> let quick = fromList (zip "quick" [0..])+-- >>> let brown = fromList (zip "brown" [0..])+-- >>> let fox   = fromList (zip "fox"   [0..])+--+-- >>> the+-- fromList [('t',0),('h',1),('e',2)]+--+-- >>> quick+-- fromList [('q',0),('u',1),('i',2),('c',3),('k',4)]+--+-- >>> brown+-- fromList [('b',0),('r',1),('o',2),('w',3),('n',4)]+--+-- >>> fox+-- fromList [('f',0),('o',1),('x',2)]
stable-heap.cabal view
@@ -1,5 +1,5 @@ name:                stable-heap-version:             0.1.0.0+version:             0.2.1.0 synopsis:            Purely functional stable heaps (fair priority queues) description:         This library provides a purely functional implementation of@@ -15,25 +15,49 @@ license-file:        LICENSE author:              Jake McArthur maintainer:          Jake.McArthur@gmail.com-copyright:           Copyright (C) 2015 Jake McArthur+copyright:           Copyright (C) 2015-2016 Jake McArthur homepage:            http://hub.darcs.net/jmcarthur/stable-heap bug-reports:         http://hub.darcs.net/jmcarthur/stable-heap/issues category:            Data Structures build-type:          Simple cabal-version:       >=1.10 stability:           experimental+tested-with:         GHC ==7.8.4,+                     GHC ==7.10.3,+                     GHC ==8.0.2,+                     GHC ==8.2.2,+                     GHC ==8.4.4,+                     GHC ==8.6.5,+                     GHC ==8.8.4,+                     GHC ==8.10.7,+                     GHC ==9.0.2,+                     GHC ==9.2.7,+                     GHC ==9.4.4,+                     GHC ==9.6.1  library   exposed-modules:     Data.Heap.Stable-  build-depends:       base >=4.8 && <4.9+  build-depends:       base >=4.7 && <4.19   hs-source-dirs:      src   default-language:    Haskell2010   other-extensions:    DeriveTraversable, Trustworthy, TypeFamilies +test-suite test+  type:                exitcode-stdio-1.0+  hs-source-dirs:      test+  build-depends:       base,+                       QuickCheck,+                       stable-heap,+                       tasty,+                       tasty-quickcheck,+                       transformers+  main-is:             Test.hs+  default-language:    Haskell2010+ benchmark bench   type:                exitcode-stdio-1.0   hs-source-dirs:      bench-  build-depends:       base >=4.8 && <4.9,+  build-depends:       base >=4.7 && <4.19,                        criterion >= 1.1,                        fingertree >= 0.1,                        heaps >= 0.3,@@ -45,10 +69,10 @@   default-language:    Haskell2010  source-repository head-  type:     darcs-  location: http://hub.darcs.net/jmcarthur/stable-heap+  type:     git+  location: https://github.com/jmcarthur/stable-heap.git  source-repository this-  type:     darcs-  location: http://hub.darcs.net/jmcarthur/stable-heap-  tag:      v0.1.0.0+  type:     git+  location: https://github.com/jmcarthur/stable-heap.git+  tag:      v0.2.1.0
+ test/Test.hs view
@@ -0,0 +1,132 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE ViewPatterns #-}++import Control.Arrow+import Control.Applicative+import Control.Monad.Trans.Writer+import Data.Heap.Stable (Heap)+import qualified Data.Heap.Stable as Heap+import Data.Foldable (foldMap)+import Data.List+import Data.Ord+import Data.Traversable (traverse)+import Data.Tuple+import Test.QuickCheck.Function+import Test.Tasty+import Test.Tasty.QuickCheck++main :: IO ()+main = defaultMain tests++-- TODO Many of these tests are now redundant with doctests.+tests :: TestTree+tests =+  testGroup "toList"+  [ testProperty "null" $+    \(H h :: H Int Int) ->+      Heap.null h == (null . Heap.toList) h+  , testProperty "size" $+    \(H h :: H Int Int) ->+      Heap.size h == (length . Heap.toList) h+  , testProperty "empty" $+    (null . Heap.toList) Heap.empty+  , testProperty "singleton" $+    \(k :: Int) (v :: Int) ->+      Heap.toList (Heap.singleton k v) == [(k, v)]+  , testProperty "append" $+    \(H a :: H Int Int) (H b) ->+      Heap.toList (Heap.append a b) == Heap.toList a ++ Heap.toList b+  , testProperty "appends" $+    \(map toHeap -> hs :: [Heap Int Int]) ->+      (Heap.toList . Heap.appends) hs == concatMap Heap.toList hs+  , testProperty "minView" $+    \(H h :: H Int Int) ->+      (minViewToSemantics . Heap.minView) h+      == (minViewSemantics . Heap.toList) h+  , testProperty "cons" $+    \k v (H h :: H Int Int) ->+      (Heap.toList . Heap.cons k v) h == (k, v) : Heap.toList h+  , testProperty "snoc" $+    \(H h :: H Int Int) k v ->+      Heap.toList (Heap.snoc h k v) == Heap.toList h ++ [(k, v)]+  , testProperty "foldrWithKey" $+    \f (z :: Int) (H h :: H Int Int) ->+      let+        g k v acc = f `apply` k `apply` v `apply` acc+      in+        Heap.foldrWithKey g z h == foldr (\(k, v) acc -> g k v acc) z (Heap.toList h)+  , testProperty "toAscList" $+    \(H h :: H Int Int) ->+      Heap.toAscList h == sortBy (comparing fst) (Heap.toList h)+  , testProperty "fromList" $+    \(kvs :: [(Int, Int)]) ->+      (Heap.toList . Heap.fromList) kvs == kvs+  , testProperty "bimap" $+    \(f :: Fun Int Int) (g :: Fun Int Int) (H h) ->+      (Heap.toList . Heap.bimap (apply f) (apply g)) h+      == (map (apply f *** apply g) . Heap.toList) h+  , testProperty "mapKeys" $+    \(f :: Fun Int Int) (H h :: H Int Int) ->+      (Heap.toList . Heap.mapKeys (apply f)) h+      == ((map . first . apply) f . Heap.toList) h+  , testProperty "mapWithKey" $+    \f (H h :: H Int Int) ->+      let+        g k v = f `apply` k `apply` v :: Int+      in+        (Heap.toList . Heap.mapWithKey g) h+        == (map (\(k, v) -> (k, g k v)) . Heap.toList) h+  , testProperty "foldMapWithKey" $+    \f (H h :: H Int Int) ->+      let+        g k v = f `apply` k `apply` v :: [Int]+      in+        Heap.foldMapWithKey g h == ((foldMap . uncurry) g . Heap.toList) h+  , testProperty "traverseWithKey" $+    \f (H h :: H Int Int) ->+      let+        g k v = f `apply` k `apply` v :: ([Int], Int)+      in+        (fmap Heap.toList . Heap.traverseWithKey g) h+        == (traverse (\(k, v) -> (,) k <$> g k v) . Heap.toList) h+  , testProperty "traverseKeys" $+    \f (H h :: H Int Int) ->+      let+        g k = f `apply` k :: ([Int], Int)+      in+        (fmap Heap.toList . Heap.traverseKeys g) h+        == (traverse (\(k, v) -> flip (,) v <$> g k) . Heap.toList) h+  , testProperty "pure" $+    \(x :: Int) ->+      ((Heap.toList . pure) x :: [([Int], Int)]) == (fromWriterT . pure) x+  , testProperty "(<*>)" $+    \(H (fmap apply -> a) :: H [Int] (Fun Int Int)) (H b :: H [Int] Int) ->+      Heap.toList (a <*> b) == fromWriterT (toWriterT a <*> toWriterT b)+  , testProperty "(>>=)" $+    \(H a :: H [Int] Int) (fmap toHeap . apply -> f :: Int -> Heap [Int] Int) ->+      Heap.toList (a >>= f) == fromWriterT (toWriterT a >>= toWriterT . f)+  ]++minViewToSemantics :: Heap.MinView k a -> Maybe ([(k, a)], (k, a), [(k, a)])+minViewToSemantics Heap.EmptyView = Nothing+minViewToSemantics (Heap.MinView l k v r) = Just (Heap.toList l, (k, v), Heap.toList r)++minViewSemantics :: Ord k => [(k, a)] -> Maybe ([(k, a)], (k, a), [(k, a)])+minViewSemantics [] = Nothing+minViewSemantics kvs = Just (l, kv, r)+  where+    minKey = (minimum . map fst) kvs+    (l, kv : r) = break ((== minKey) . fst) kvs++fromWriterT :: WriterT k [] v -> [(k, v)]+fromWriterT = map swap . runWriterT++toWriterT :: Heap k v -> WriterT k [] v+toWriterT = WriterT . map swap . Heap.toList++newtype H k v = H { toHeap :: Heap k v }+  deriving Show++instance (Arbitrary k, Arbitrary v, Ord k) => Arbitrary (H k v) where+  arbitrary = H . Heap.fromList <$> arbitrary+  shrink = map (H . Heap.fromList . shrink) . Heap.toList . toHeap