queuelike-1.0.6: Data/Queue/TrieQueue/MonoidQueue2.hs
{-# LANGUAGE TypeFamilies, TypeOperators, PatternGuards #-}
module Data.Queue.TrieQueue.MonoidQueue2 (MQueue, extractSingle, replace) where
import Data.Queue.PQueue
import Data.Queue.Class
import Data.Queue.QueueHelpers (fusing, fusing')
import Data.Queue.TrieQueue.Edge
import Data.Maybe
import Data.Monoid
import Prelude hiding (null)
data MQueue k m = Nil | MQ {-# UNPACK #-} !(k :- m) {-# UNPACK #-} !(PQueue (k :- m)) deriving (Show)
instance (Ord k, Monoid m) => Monoid (MQueue k m) where
mempty = Nil
mappend = mergeMQ
{-
Here we organize on the queues themselves. We require that fromList not be defined in terms of mergeAll, of course -- in fact, if it is defined using toQueue as it is below,
this gives us the best of both worlds -- balanced merging both on the values associated with the minimum key (provided by fromList), and balanced merging on the remaining queues.
We lose some partial merging opportunities, but this implementation does none, so that's not an issue.
-}
mconcat qs = case mkMQueue [k :- (m, q) | MQ (k :- m) q <- qs] of
Nil -> Nil
MQ (k :- (m, pq)) qOfMQs ->
let q' = pq `merge` mergeAll
[(k :- m) `insert` q | k :- (m, q) <- toList_ qOfMQs]
in MQ (k :- m) q'
where mkMQueue :: (Ord k, Monoid m) => [k :- m] -> MQueue k m
mkMQueue = fromList
-- mconcat = fromMaybe Nil . fusing' mergeMQ
mergeMQ' :: Ord k => (k -> k -> Ordering) -> (m -> m -> m) -> MQueue k m -> MQueue k m -> MQueue k m
mergeMQ' cmp (><) (MQ x1@(k1 :- m1) q1) (MQ x2@(k2 :- m2) q2) = case cmp k1 k2 of
LT -> MQ x1 (x2 `insert` q)
EQ -> MQ (k1 :- (m1 >< m2)) q
GT -> MQ x2 (x1 `insert` q)
where q = q1 `merge` q2
mergeMQ' _ _ q Nil = q
mergeMQ' _ _ Nil q = q
mergeMQ :: (Ord k, Monoid m) => MQueue k m -> MQueue k m -> MQueue k m
mergeMQ = mergeMQ' compare mappend
instance (Ord k, Monoid m) => IQueue (MQueue k m) where
type QueueKey (MQueue k m) = (k :- m)
empty = Nil
singleton = (`MQ` empty)
fromList = toQueue . fromList
merge = mappend
mergeAll = mconcat
null Nil = True
null _ = False
extract Nil = Nothing
extract (MQ x q) = Just (x, toQueue q)
toList_ Nil = []
toList_ (MQ x q) = x:toList_ q
-- This version ignores balancing and does a totally simple merge on values with the minimal key in common.
toQueue :: (Ord k, Monoid m) => PQueue (k :- m) -> MQueue k m
toQueue q = case extract q of
Nothing -> Nil
Just (k :- m, q') -> let toQueue' ms q = case extract q of Just (k' :- m', q')
| k == k' -> toQueue' (m':ms) q'
-- _ -> MQ (k :- maybe m (mappend m) (fusing ms)) q
_ | [] <- ms -> MQ (k :- m) q
| otherwise -> MQ (k :- (m `mappend` mconcat ms)) q
in toQueue' [] q'
extractSingle :: Ord k => MQueue k m -> Maybe (k :- m)
extractSingle (MQ x q)
| null q = Just x
extractSingle _ = Nothing
replace :: (Ord k, Monoid m) => (k :- m) -> MQueue k m -> MQueue k m
replace (k :- m) (MQ _ q) = MQ (k :- m) q
replace _ Nil = error "Attempt to modify head of an empty queue"