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