stable-heap-0.2.1.0: src/Data/Heap/Stable.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE Trustworthy #-}
-- |
--
-- Module : Data.Heap.Stable
-- Copyright : (C) 2015-2016 Jake McArthur
-- License : MIT
-- Maintainer : Jake.McArthur@gmail.com
-- Stability : experimental
--
-- 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.
--
-- 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
( -- $setup
Heap ()
-- * Query
, Data.Heap.Stable.null
, size
-- * Construction
, empty
, singleton
, append
, appends
, cons
, snoc
-- * 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 qualified Data.List
import Data.Foldable (Foldable)
import Data.Traversable (Traversable)
import qualified GHC.Exts
#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 !Int !(Heap k a) (Heap k a) !k a (Heap k a) !(Heap k a)
| Empty
deriving (Functor, Foldable, Traversable)
-- |
--
-- '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
-- |
--
-- 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 1 empty empty k v empty empty
-- |
--
-- 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 (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 (sx+sy) xs ls2 k2 v2 rs2 r2
Heap {} -> Heap (sx+sy) Empty ((xs `append` l2) `append` ls2) k2 v2 rs2 r2
-- |
--
-- Sequentially append an arbitrary number of heaps.
--
-- /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
-- |
--
-- 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
#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
-- |
--
-- 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 `append`)
-- |
--
-- 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 `append` singleton k v
-- |
--
-- 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))))
-- |
--
-- 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.
--
-- /O(n log n)/ when the spine of the result is evaluated fully.
--
-- >>> 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 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.
--
-- /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
-- |
--
-- >>> 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 `append` go ls `append` singleton (f k) (g v) `append` go rs `append` go r
-- |
--
-- >>> 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
-- |
--
-- 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
= (fl <*> xs)
`append` (fls <*> xs)
`append` bimap (fk `mappend`) f xs
`append` (frs <*> xs)
`append` (fr <*> xs)
-- |
--
-- 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
= (xl >>= 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)
instance (Ord k, Read k, Read a) => Read (Heap k a) where
readsPrec p = readParen (p > 10) $ \ r -> do
("fromList", s) <- lex r
(xs, t) <- reads s
return (fromList xs, t)
instance Ord k => GHC.Exts.IsList (Heap k a) where
type Item (Heap k a) = (k, a)
fromList = fromList
toList = toList
-- |
--
-- prop> (xs == ys) == (toList xs == toList ys)
instance (Eq k, Eq a) => Eq (Heap k a) where
xs == ys = 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)
-- |
--
-- Formed from 'empty' and 'append'
instance (Monoid k, Ord k) => Applicative.Alternative (Heap k) where
empty = mempty
(<|>) = mappend
-- |
--
-- 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)]