{-# LANGUAGE GADTs, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables, UndecidableInstances #-}
module Data.PriorityQueue (PQueue, Branching, Pruned,
branchable, prune, pruneAbove, pruneAlternativesAbove, mapWithCost, filter, foldPeers,
canonical, pruneSubsets, strip, stripCommon,
cost, leastCost, withCost) where
import Control.Applicative (Applicative(..), Alternative(..))
import Data.Coerce (coerce)
import Data.Foldable (Foldable(fold))
import Data.Monoid (Monoid(mempty, mappend), Alt(Alt, getAlt))
import Data.Semigroup (Semigroup((<>)))
import Prelude hiding (filter)
data Branching
data Pruned
data PQueue t c a = Costly !c (PQueue t c a)
| Free !(Ground a) (PQueue t c a)
| Empty
deriving Show
data Ground a = Leaf a
| Peer !(Ground a) !(Ground a)
deriving Show
instance Foldable Ground where
foldMap f (Leaf a) = f a
foldMap f (Peer g h) = foldMap f g <> foldMap f h
instance Functor Ground where
fmap f (Leaf a) = Leaf (f a)
fmap f (Peer g h) = Peer (fmap f g) (fmap f h)
instance Applicative Ground where
Leaf f <*> g = f <$> g
Peer g1 g2 <*> h = Peer (g1 <*> h) (g2 <*> h)
pure = Leaf
instance Foldable (PQueue t c) where
foldMap f (Costly _ q) = foldMap f q
foldMap f (Free a q) = foldMap f a <> foldMap f q
foldMap f Empty = mempty
instance Functor (PQueue t c) where
fmap f (Costly c q) = Costly c (fmap f q)
fmap f (Free a q) = Free (fmap f a) (fmap f q)
fmap _ Empty = Empty
instance (Alternative (PQueue t c), Semigroup c) => Applicative (PQueue t c) where
Costly c1 q1 <*> Costly c2 q2 = Costly (c1 <> c2) (q1 <*> q2)
Costly c q1 <*> q2 = Costly c (q1 <*> q2)
q1 <*> Costly c q2 = Costly c (q1 <*> q2)
Free f q1 <*> Free a q2 = Free (f <*> a) (mapPeers (f <*>) q2 <|> mapPeers (<*> a) q1 <|> q1 <*> q2)
where mapPeers f (Free g q) = Free (f g) (mapPeers f q)
mapPeers f (Costly c q) = Costly c (mapPeers f q)
mapPeers f Empty = Empty
Empty <*> _ = Empty
_ <*> Empty = Empty
pure a = Free (Leaf a) Empty
{-# INLINABLE (<*>) #-}
instance (Num c, Ord c, Semigroup c) => Alternative (PQueue Branching c) where
Costly c1 q1 <|> Costly c2 q2 = {-# SCC "AltB.compare" #-}
case compare c1 c2
of LT -> Costly c1 (q1 <|> Costly (c2 - c1) q2)
GT -> Costly c2 (Costly (c1 - c2) q1 <|> q2)
EQ -> Costly c1 (q1 <|> q2)
Free a q1 <|> Free b q2 = Free (Peer a b) (q1 <|> q2)
Free a q1 <|> q2 = Free a (q1 <|> q2)
q1 <|> Free a q2 = Free a (q1 <|> q2)
Empty <|> pq = pq
pq <|> Empty = pq
empty = Empty
{-# INLINABLE (<|>) #-}
instance (Num c, Ord c, Semigroup c) => Alternative (PQueue Pruned c) where
Costly c1 q1 <|> Costly c2 q2 = {-# SCC "AltP.compare" #-}
case compare c1 c2
of LT -> Costly c1 (q1 <|> Costly (c2 - c1) q2)
GT -> Costly c2 (Costly (c1 - c2) q1 <|> q2)
EQ -> Costly c1 (q1 <|> q2)
Free a _ <|> _ = Free a Empty
_ <|> Free a _ = Free a Empty
Empty <|> pq = pq
pq <|> Empty = pq
empty = Empty
{-# INLINABLE (<|>) #-}
instance (Semigroup c, Alternative (PQueue t c)) => Monad (PQueue t c) where
Costly c q >>= f = Costly c (q >>= f)
Free a q >>= f = getAlt (foldMap (Alt . f) a) <|> (q >>= f)
Empty >>= _ = Empty
{-# INLINABLE (>>=) #-}
withCost :: (Semigroup c, Num c, Ord c) => c -> PQueue t c a -> PQueue t c a
withCost 0 q = q
withCost c q | c <= 0 = error "The cost must be non-negative!"
| otherwise = Costly c q
{-# INLINE withCost #-}
foldPeers :: (a -> a -> a) -> PQueue t c a -> PQueue t c a
foldPeers _ Empty = Empty
foldPeers f (Costly c q) = Costly c (foldPeers f q)
foldPeers f (Free g q) = Free (Leaf a'') q''
where (a'', q'') = case foldPeers f q
of Free (Leaf b) q' -> (f a' b, q')
q' -> (a', q')
a' = foldGroundPeers f g
foldGroundPeers :: (a -> a -> a) -> Ground a -> a
foldGroundPeers _ (Leaf a) = a
foldGroundPeers f (Peer l r) = f (foldGroundPeers f l) (foldGroundPeers f r)
cost :: (Semigroup c, Num c, Ord c) => c -> PQueue Branching c ()
cost 0 = pure ()
cost k | k > 0 = Costly k (pure ())
branchable :: PQueue Pruned c a -> PQueue t c a
branchable = coerce
pruneAbove :: (Semigroup c, Num c, Ord c) => c -> PQueue t c a -> PQueue t c a
pruneAbove k _
| k < 0 = Empty
pruneAbove k (Costly c q)
| k' < 0 = Empty
| otherwise = Costly c (pruneAbove k' q)
where k' = k - c
pruneAbove k (Free a q) = Free a (pruneAbove k q)
pruneAbove _ Empty = Empty
{-# INLINABLE pruneAbove #-}
pruneAlternativesAbove :: (Semigroup c, Num c, Ord c) => c -> PQueue t c a -> PQueue t c a
pruneAlternativesAbove k q
| k <= 0 = q
pruneAlternativesAbove k (Costly c q) = Costly c (pruneAlternativesAbove (k - c) q)
pruneAlternativesAbove k (Free a q) = Free a (pruneAbove k q)
pruneAlternativesAbove _ Empty = Empty
{-# INLINABLE pruneAlternativesAbove #-}
prune :: PQueue t c a -> PQueue Pruned c a
prune (Costly c q) = Costly c (prune q)
prune (Free a q) = Free (Leaf $ leftmost a) Empty
where leftmost :: Ground a -> a
leftmost (Leaf a) = a
leftmost (Peer l r) = leftmost l
prune Empty = Empty
canonical :: Semigroup c => PQueue t c a -> PQueue t c a
canonical (Costly c1 (Costly c2 q)) = canonical (Costly (c1 <> c2) q)
canonical (Costly c q) = Costly c (canonical q)
canonical (Free a q) = Free a (canonical q)
canonical Empty = Empty
filter :: (a -> Bool) -> PQueue t c a -> PQueue t c a
filter f (Costly c q) = Costly c (filter f q)
filter f (Free g q) = maybe id Free (filterGround g) (filter f q)
where filterGround g@(Leaf a) = if f a then Just g else Nothing
filterGround (Peer g1 g2) = case (filterGround g1, filterGround g2)
of (Just g1', Just g2') -> Just (Peer g1' g2')
(Just g', Nothing) -> Just g'
(Nothing, Just g') -> Just g'
(Nothing, Nothing) -> Nothing
filter _ Empty = Empty
pruneSubsets :: (a -> b -> Maybe (a, b)) -> a -> PQueue t c b -> PQueue t c b
pruneSubsets unionDiff set (Costly c q) = Costly c (pruneSubsets unionDiff set q)
pruneSubsets unionDiff set (Free g q) =
case pruneGroundSubsets unionDiff set g
of Nothing -> pruneSubsets unionDiff set q
Just (set', g') -> Free g' (pruneSubsets unionDiff set' q)
pruneSubsets _ _ Empty = Empty
pruneGroundSubsets :: (a -> b -> Maybe (a, b)) -> a -> Ground b -> Maybe (a, Ground b)
pruneGroundSubsets unionDiff set (Leaf l) = case unionDiff set l
of Nothing -> Nothing
Just (set', l') -> Just (set', Leaf l')
pruneGroundSubsets unionDiff set (Peer g1 g2) =
case pruneGroundSubsets unionDiff set g1
of Nothing -> pruneGroundSubsets unionDiff set g2
Just (set', g1') -> case pruneGroundSubsets unionDiff set' g2
of Nothing -> Just (set', g1')
Just (set'', g2') -> Just (set'', Peer g1' g2')
stripCommon :: (Ord c, Num c, Functor f, Foldable f, Alternative (PQueue t c)) =>
f (PQueue t c a) -> (PQueue Pruned c (a -> a), f (PQueue t c a))
stripCommon f = (common, strip common <$> f)
where common = const id <$> prune (getAlt $ foldMap Alt f)
strip :: (Ord c, Num c) => PQueue Pruned c a -> PQueue t c b -> PQueue t c b
strip (Costly c q1) q2 = stripCost c (strip q1 q2)
strip _ q = q
stripCost :: (Ord c, Num c) => c -> PQueue t c a -> PQueue t c a
stripCost c (Costly c' q)
| c < c' = Costly (c' - c) q
| c > c' = stripCost (c - c') q
| otherwise = q
stripCost _ Empty = Empty
leastCost :: Monoid c => PQueue t c a -> Maybe c
leastCost (Costly c q) = (c <>) <$> leastCost q
leastCost Free{} = Just mempty
leastCost Empty = Nothing
mapWithCost :: Monoid c => (c -> a -> b) -> PQueue t c a -> PQueue t c b
mapWithCost f (Costly c q) = Costly c (mapWithCost (f . (c <>)) q)
mapWithCost f (Free a q) = Free (f mempty <$> a) (mapWithCost f q)
mapWithCost _ Empty = Empty