{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TupleSections #-}
module Data.MinMaxQueue (
MinMaxQueue
, empty
, singleton
, fromList
, fromListWith
, fromMap
, null
, notNull
, size
, withMaxSize
, maxSize
, insert
, peekMin
, peekMax
, deleteMin
, deleteMax
, pollMin
, pollMax
, takeMin
, takeMax
, dropMin
, dropMax
, map
, mapWithPriority
, foldr
, foldl
, foldrWithPriority
, foldlWithPriority
, foldMapWithPriority
, foldr'
, foldl'
, foldrWithPriority'
, foldlWithPriority'
, elems
, toList
, toAscList
, toDescList
, toMap
) where
import Data.Data (Data)
import qualified Data.Foldable as Foldable
import Data.Functor.Classes
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.List.NonEmpty (NonEmpty(..), (<|))
import qualified Data.List.NonEmpty as Nel
import Prelude hiding (drop, foldl, foldr, lookup, map, null, take)
import qualified Prelude
type Size = Int
type MaxSize = Maybe Int
data MinMaxQueue prio a = MinMaxQueue {-# UNPACK #-} !Size !MaxSize !(Map prio (NonEmpty a))
deriving (Eq, Ord, Data)
instance Eq prio => Eq1 (MinMaxQueue prio) where
liftEq = liftEq2 (==)
instance Eq2 MinMaxQueue where
liftEq2 eqk eqv q1 q2 =
Map.size (toMap q1) == Map.size (toMap q2)
&& liftEq (liftEq2 eqk eqv) (toList q1) (toList q2)
instance Ord prio => Ord1 (MinMaxQueue prio) where
liftCompare = liftCompare2 compare
instance Ord2 MinMaxQueue where
liftCompare2 cmpk cmpv q1 q2 =
liftCompare (liftCompare2 cmpk cmpv) (toList q1) (toList q2)
instance (Show prio, Show a) => Show (MinMaxQueue prio a) where
showsPrec d q = showParen (d > 10) $
showString "fromList " . shows (toList q)
instance Show prio => Show1 (MinMaxQueue prio) where
liftShowsPrec = liftShowsPrec2 showsPrec showList
instance Show2 MinMaxQueue where
liftShowsPrec2 spk slk spv slv d m =
showsUnaryWith (liftShowsPrec sp sl) "fromList" d (toList m)
where
sp = liftShowsPrec2 spk slk spv slv
sl = liftShowList2 spk slk spv slv
instance (Ord prio, Read prio, Read a) => Read (MinMaxQueue prio a) where
readsPrec p = readParen (p > 10) $ \r -> do
("fromList",s) <- lex r
(xs,t) <- reads s
pure (fromList xs,t)
instance (Ord prio, Read prio) => Read1 (MinMaxQueue prio) where
liftReadsPrec rp rl = readsData $
readsUnaryWith (liftReadsPrec rp' rl') "fromList" fromList
where
rp' = liftReadsPrec rp rl
rl' = liftReadList rp rl
instance Functor (MinMaxQueue prio) where
fmap = map
instance Foldable.Foldable (MinMaxQueue prio) where
foldMap = foldMapWithPriority . const
empty :: MinMaxQueue prio a
empty = MinMaxQueue 0 Nothing Map.empty
singleton :: (a -> prio) -> a -> MinMaxQueue prio a
singleton f a = MinMaxQueue 1 Nothing (Map.singleton (f a) (pure a))
fromList :: Ord prio => [(prio, a)] -> MinMaxQueue prio a
fromList = Foldable.foldr (uncurry (insert . const)) empty
fromListWith :: Ord prio => (a -> prio) -> [a] -> MinMaxQueue prio a
fromListWith f = Foldable.foldr (insert f) empty
fromMap :: Map prio (NonEmpty a) -> MinMaxQueue prio a
fromMap m = MinMaxQueue (sum (fmap length m)) Nothing m
null :: MinMaxQueue prio a -> Bool
null = (== 0) . size
notNull :: MinMaxQueue prio a -> Bool
notNull = not . null
size :: MinMaxQueue prio a -> Int
size (MinMaxQueue sz _ _) = sz
withMaxSize :: Ord prio => MinMaxQueue prio a -> Int -> MinMaxQueue prio a
withMaxSize q ms = MinMaxQueue sz (Just ms) m
where (MinMaxQueue sz _ m) = takeMin ms q
maxSize :: MinMaxQueue prio a -> Maybe Int
maxSize (MinMaxQueue _ ms _) = max 0 <$> ms
insert :: Ord prio => (a -> prio) -> a -> MinMaxQueue prio a -> MinMaxQueue prio a
insert f a q@(MinMaxQueue sz ms _) = case ms of
Just ms' | sz >= ms' -> deleteMax (insert' f a q)
_ -> insert' f a q
insert' :: Ord prio => (a -> prio) -> a -> MinMaxQueue prio a -> MinMaxQueue prio a
insert' f a (MinMaxQueue sz ms m) = MinMaxQueue (sz+1) ms (Map.alter g (f a) m)
where
g Nothing = Just (pure a)
g (Just as) = Just (a <| as)
peekMin :: Ord prio => MinMaxQueue prio a -> Maybe a
peekMin (MinMaxQueue _ _ m) = Nel.head . snd <$> Map.lookupMin m
peekMax :: Ord prio => MinMaxQueue prio a -> Maybe a
peekMax (MinMaxQueue _ _ m) = Nel.head . snd <$> Map.lookupMax m
deleteMin :: Ord prio => MinMaxQueue prio a -> MinMaxQueue prio a
deleteMin q@(MinMaxQueue sz ms m)
| Just (prio,_) <- Map.lookupMin m = MinMaxQueue (sz-1) ms (Map.update (Nel.nonEmpty . Nel.tail) prio m)
| otherwise = q
deleteMax :: Ord prio => MinMaxQueue prio a -> MinMaxQueue prio a
deleteMax q@(MinMaxQueue sz ms m)
| Just (prio,_) <- Map.lookupMax m = MinMaxQueue (sz-1) ms (Map.update (Nel.nonEmpty . Nel.tail) prio m)
| otherwise = q
pollMin :: Ord prio => MinMaxQueue prio a -> Maybe (a, MinMaxQueue prio a)
pollMin q = (,) <$> peekMin q <*> pure (deleteMin q)
pollMax :: Ord prio => MinMaxQueue prio a -> Maybe (a, MinMaxQueue prio a)
pollMax q = (,) <$> peekMax q <*> pure (deleteMax q)
takeMin :: Ord prio => Int -> MinMaxQueue prio a -> MinMaxQueue prio a
takeMin n q@(MinMaxQueue sz ms m)
| newSz >= sz = q
| newSz * 2 <= sz = MinMaxQueue newSz ms (take Map.lookupMin newSz m)
| otherwise = MinMaxQueue newSz ms (drop Map.lookupMax (sz - newSz) m)
where newSz = max 0 (min sz n)
takeMax :: Ord prio => Int -> MinMaxQueue prio a -> MinMaxQueue prio a
takeMax n q@(MinMaxQueue sz ms m)
| newSz >= sz = q
| newSz * 2 <= sz = MinMaxQueue newSz ms (take Map.lookupMax newSz m)
| otherwise = MinMaxQueue newSz ms (drop Map.lookupMin (sz - newSz) m)
where newSz = max 0 (min sz n)
dropMin :: Ord prio => Int -> MinMaxQueue prio a -> MinMaxQueue prio a
dropMin n q@(MinMaxQueue sz ms m)
| newSz >= sz = q
| newSz * 2 > sz = MinMaxQueue newSz ms (drop Map.lookupMin (sz - newSz) m)
| otherwise = MinMaxQueue newSz ms (take Map.lookupMax newSz m)
where newSz = max 0 (min sz (sz - n))
dropMax :: Ord prio => Int -> MinMaxQueue prio a -> MinMaxQueue prio a
dropMax n q@(MinMaxQueue sz ms m)
| newSz >= sz = q
| newSz * 2 > sz = MinMaxQueue newSz ms (drop Map.lookupMax (sz - newSz) m)
| otherwise = MinMaxQueue newSz ms (take Map.lookupMin newSz m)
where newSz = max 0 (min sz (sz - n))
take
:: Ord prio
=> (forall b. Map prio b -> Maybe (prio, b))
-> Int -> Map prio (NonEmpty a) -> Map prio (NonEmpty a)
take lookup n m = go 0 m Map.empty
where
go sz mIn mOut
| sz >= n = mOut
| Just (prio, hd :| tl) <- lookup mIn =
let as = hd :| Prelude.take (n - sz - 1) tl
len = Nel.length as
mOut' = Map.insert prio as mOut
mIn' = Map.delete prio mIn
in go (sz + len) mIn' mOut'
| otherwise = mOut
drop
:: Ord prio
=> (forall b. Map prio b -> Maybe (prio, b))
-> Int -> Map prio (NonEmpty a) -> Map prio (NonEmpty a)
drop lookup n = go 0
where
go sz mOut
| sz >= n = mOut
| Just (prio, hd :| tl) <- lookup mOut =
let len = length tl + 1
in if sz + len <= n
then go (sz + len) (Map.delete prio mOut)
else Map.insert prio (hd :| Prelude.drop (n - sz) tl) mOut
| otherwise = mOut
map :: (a -> b) -> MinMaxQueue prio a -> MinMaxQueue prio b
map = mapWithPriority . const
mapWithPriority :: (prio -> a -> b) -> MinMaxQueue prio a -> MinMaxQueue prio b
mapWithPriority f (MinMaxQueue sz ms m) =
MinMaxQueue sz ms (Map.mapWithKey (fmap . f) m)
foldr :: (a -> b -> b) -> b -> MinMaxQueue prio a -> b
foldr = foldrWithPriority . const
foldl :: (a -> b -> a) -> a -> MinMaxQueue prio b -> a
foldl = foldlWithPriority . (const .)
foldrWithPriority :: (prio -> a -> b -> b) -> b -> MinMaxQueue prio a -> b
foldrWithPriority f b (MinMaxQueue _ _ m) = Map.foldrWithKey f' b m
where
f' = flip . Foldable.foldr . f
foldlWithPriority :: (a -> prio -> b -> a) -> a -> MinMaxQueue prio b -> a
foldlWithPriority f a (MinMaxQueue _ _ m) = Map.foldlWithKey f' a m
where
f' = flip (Foldable.foldl . flip f)
foldr' :: (a -> b -> b) -> b -> MinMaxQueue prio a -> b
foldr' = foldrWithPriority' . const
foldl' :: (a -> b -> a) -> a -> MinMaxQueue prio b -> a
foldl' = foldlWithPriority' . (const .)
foldrWithPriority' :: (prio -> a -> b -> b) -> b -> MinMaxQueue prio a -> b
foldrWithPriority' f b (MinMaxQueue _ _ m) = Map.foldrWithKey' f' b m
where
f' = flip . Foldable.foldr . f
foldlWithPriority' :: (a -> prio -> b -> a) -> a -> MinMaxQueue prio b -> a
foldlWithPriority' f a (MinMaxQueue _ _ m) = Map.foldlWithKey' f' a m
where
f' = flip (Foldable.foldl' . flip f)
foldMapWithPriority :: Monoid m => (prio -> a -> m) -> MinMaxQueue prio a -> m
foldMapWithPriority f (MinMaxQueue _ _ m) =
Map.foldMapWithKey (Foldable.foldMap . f) m
elems :: MinMaxQueue prio a -> [a]
elems (MinMaxQueue _ _ m) = Foldable.foldMap Nel.toList m
toList :: MinMaxQueue prio a -> [(prio, a)]
toList = toAscList
toAscList :: MinMaxQueue prio a -> [(prio, a)]
toAscList (MinMaxQueue _ _ m) =
Map.toAscList m >>= uncurry (\prio -> fmap (prio,) . Nel.toList)
toDescList :: MinMaxQueue prio a -> [(prio, a)]
toDescList (MinMaxQueue _ _ m) =
Map.toDescList m >>= uncurry (\prio -> fmap (prio,) . Nel.toList)
toMap :: MinMaxQueue prio a -> Map prio (NonEmpty a)
toMap (MinMaxQueue _ _ m) = m