seqn-0.1.0.0: src/Data/Seqn/Internal/PQueue.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_HADDOCK not-home #-}
-- |
-- This is an internal module. You probably don't need to import this. Use
-- "Data.Seqn.PQueue" instead.
--
-- = WARNING
--
-- Definitions in this module allow violating invariants that would otherwise be
-- guaranteed by "Data.Seqn.PQueue". Use at your own risk!
--
module Data.Seqn.Internal.PQueue
(
-- * PQueue
PQueue(..)
, Elem(..)
, Min(..)
, empty
, singleton
, fromList
, concatMap
, insert
, min
, minView
, toSortedList
-- * Entry
, Entry(..)
, entryPrio
, entryValue
) where
import Prelude hiding (concatMap, min)
import Data.Coerce (coerce)
import Control.DeepSeq (NFData(..), NFData1(..))
import qualified Data.Foldable as F
import qualified Data.Foldable.WithIndex as IFo
import Data.Functor.Classes (Eq1(..), Ord1(..), Show1(..))
import qualified GHC.Exts as X
import Data.Seqn.MSeq (Measured(..))
import Data.Seqn.Internal.MSeq (MSeq(..))
import qualified Data.Seqn.Internal.MSeq as MSeq
import Data.Seqn.Internal.MTree (MTree(..))
import qualified Data.Seqn.Internal.MTree as T
import qualified Data.Seqn.Internal.Util as U
newtype Min a = Min a
-- Note: We do not use Data.Semigroup.Min because we need a left-biased (<>)
-- for the FIFO property. Data.Semigroup.Min simply delegates to the min
-- function of the underlying type, which is not required to be left-biased.
instance Ord a => Semigroup (Min a) where
x@(Min x') <> y@(Min y') = if x' <= y' then x else y
{-# INLINE (<>) #-}
instance NFData a => NFData (Min a) where
rnf = (coerce :: (a -> ()) -> Min a -> ()) rnf
{-# INLINABLE rnf #-}
instance NFData1 Min where
liftRnf = coerce
newtype Elem a = Elem a
deriving newtype (Eq, Ord, Show, Read, NFData)
instance Ord a => Measured (Elem a) where
type Measure (Elem a) = Min a
measure = coerce
-- | A minimum priority queue.
--
-- @PQueue@ can be used as a maximum priority queue by wrapping its elements
-- with t'Data.Ord.Down'.
newtype PQueue a = PQueue (MSeq (Elem a))
deriving newtype
(
-- | Insertion order.
Eq
-- | Lexicographical ordering, in insertion order.
, Ord
-- |
-- [@(<>)@]: \(O(\left| \log n_1 - \log n_2 \right|)\). Concatenate
-- two @PQueue@s.
, Semigroup
-- |
-- [@mempty@]: The empty queue.
, Monoid
, Show
, Read
)
instance Eq1 PQueue where
liftEq =
(coerce :: ((Elem a -> Elem b -> Bool) -> MSeq (Elem a) -> MSeq (Elem b) -> Bool)
-> (a -> b -> Bool) -> PQueue a -> PQueue b -> Bool)
liftEq
{-# INLINE liftEq #-}
instance Ord1 PQueue where
liftCompare =
(coerce :: ((Elem a -> Elem b -> Ordering) -> MSeq (Elem a) -> MSeq (Elem b) -> Ordering)
-> (a -> b -> Ordering) -> PQueue a -> PQueue b -> Ordering)
liftCompare
{-# INLINE liftCompare #-}
instance Show1 PQueue where
liftShowsPrec _ sl _ s = sl (F.toList s)
{-# INLINE liftShowsPrec #-}
-- |
-- [length]: \(O(1)\).
--
-- Folds in insertion order.
instance Foldable PQueue where
foldMap =
(coerce :: ((Elem a -> m) -> MSeq (Elem a) -> m)
-> (a -> m) -> PQueue a -> m)
foldMap
{-# INLINE foldMap #-}
foldr =
(coerce :: ((Elem a -> b -> b) -> b -> MSeq (Elem a) -> b)
-> (a -> b -> b) -> b -> PQueue a -> b)
foldr
{-# INLINE foldr #-}
foldl' =
(coerce :: ((b -> Elem a -> b) -> b -> MSeq (Elem a) -> b)
-> (b -> a -> b) -> b -> PQueue a -> b)
F.foldl'
{-# INLINE foldl' #-}
foldl =
(coerce :: ((b -> Elem a -> b) -> b -> MSeq (Elem a) -> b)
-> (b -> a -> b) -> b -> PQueue a -> b)
F.foldl
{-# INLINE foldl #-}
foldr' =
(coerce :: ((Elem a -> b -> b) -> b -> MSeq (Elem a) -> b)
-> (a -> b -> b) -> b -> PQueue a -> b)
F.foldr'
{-# INLINE foldr' #-}
null = coerce (null @MSeq)
length = coerce (length @MSeq)
-- | Folds in insertion order.
instance IFo.FoldableWithIndex Int PQueue where
ifoldMap =
(coerce :: ((Int -> Elem a -> m) -> MSeq (Elem a) -> m)
-> (Int -> a -> m) -> PQueue a -> m)
IFo.ifoldMap
{-# INLINE ifoldMap #-}
ifoldr =
(coerce :: ((Int -> Elem a -> b -> b) -> b -> MSeq (Elem a) -> b)
-> (Int -> a -> b -> b) -> b -> PQueue a -> b)
IFo.ifoldr
{-# INLINE ifoldr #-}
ifoldr' =
(coerce :: ((Int -> Elem a -> b -> b) -> b -> MSeq (Elem a) -> b)
-> (Int -> a -> b -> b) -> b -> PQueue a -> b)
IFo.ifoldr'
{-# INLINE ifoldr' #-}
ifoldl' =
(coerce :: ((Int -> b -> Elem a -> b) -> b -> MSeq (Elem a) -> b)
-> (Int -> b -> a -> b) -> b -> PQueue a -> b)
IFo.ifoldl'
{-# INLINE ifoldl' #-}
ifoldl =
(coerce :: ((Int -> b -> Elem a -> b) -> b -> MSeq (Elem a) -> b)
-> (Int -> b -> a -> b) -> b -> PQueue a -> b)
IFo.ifoldl
{-# INLINE ifoldl #-}
instance Ord a => X.IsList (PQueue a) where
type Item (PQueue a) = a
fromList = fromList
{-# INLINE fromList #-}
toList = F.toList
{-# INLINE toList #-}
instance NFData a => NFData (PQueue a) where
rnf = (coerce :: (MSeq (Elem a) -> ()) -> PQueue a -> ()) rnf
{-# INLINABLE rnf #-}
instance NFData1 PQueue where
liftRnf f (PQueue t) = MSeq.liftRnf2 (liftRnf f) (coerce f) t
{-# INLINE liftRnf #-}
---------------
-- Operations
---------------
-- | The empty queue.
empty :: PQueue a
empty = PQueue MSeq.empty
-- | A singleton queue.
singleton :: a -> PQueue a
singleton = coerce MSeq.singleton
-- | \(O(n)\). Create a queue from a list.
fromList :: Ord a => [a] -> PQueue a
fromList = coerce MSeq.fromList
{-# INLINE fromList #-}
-- | \(O \left(\sum_i \log n_i \right)\).
-- Map over a @Foldable@ and concatenate the results.
concatMap :: (Ord b, Foldable f) => (a -> PQueue b) -> f a -> PQueue b
concatMap =
(coerce :: ((a -> MSeq (Elem b)) -> f a -> MSeq (Elem b))
-> (a -> PQueue b) -> f a -> PQueue b)
MSeq.concatMap
{-# INLINE concatMap #-}
-- | \(O(\log n)\). Insert an element into the queue.
--
-- Note: When inserting multiple elements, it is more efficient to concatenate
-- a fresh queue rather than repeatedly insert elements.
--
-- @
-- q <> fromList xs -- Good
-- foldl' (flip insert) q xs -- Worse
-- @
insert :: Ord a => a -> PQueue a -> PQueue a
insert = coerce (flip MSeq.snoc)
{-# INLINABLE insert #-}
-- | \(O(1)\). The minimum element in the queue.
min :: Ord a => PQueue a -> Maybe a
min =
(coerce :: (MSeq (Elem a) -> Maybe (Min a)) -> PQueue a -> Maybe a)
MSeq.summaryMay
{-# INLINE min #-}
-- | \(O(\log n)\). The minimum element in the queue, with the rest of the
-- queue.
minView :: Ord a => PQueue a -> Maybe (a, PQueue a)
minView (PQueue t) = case t of
MEmpty -> Nothing
MTree x xs -> case minViewSure x xs of
U.S2 y t' -> Just (y, PQueue t')
{-# INLINE minView #-}
minViewSure :: Ord a => Elem a -> MTree (Elem a) -> U.S2 a (MSeq (Elem a))
minViewSure x@(Elem !x1) xs = case xs of
MTip -> U.S2 x1 MSeq.empty
MBin _ (Min v) _ _ _
| x1 <= v -> U.S2 x1 (MSeq.fromMTree xs)
| otherwise -> U.S2 v (MTree x (deleteSure v xs))
{-# INLINABLE minViewSure #-}
deleteSure :: Ord a => a -> MTree (Elem a) -> MTree (Elem a)
deleteSure !k = \case
MBin _ _ x@(Elem x1) l r -> case l of
MTip
| x1 <= k -> r
| otherwise -> T.cons x (deleteSure k r)
MBin _ (Min v) _ _ _
| v <= k -> T.balanceR x (deleteSure k l) r
| x1 <= k -> T.glue l r
| otherwise -> T.balanceL x l (deleteSure k r)
MTip -> error "PQueue.deleteSure: impossible"
{-# INLINABLE deleteSure #-}
-- | \(O(n \log n)\). Convert to a sorted list.
toSortedList :: Ord a => PQueue a -> [a]
toSortedList q0 = X.build $ \lcons lnil ->
let go q = case minView q of
Nothing -> lnil
Just (x,q') -> lcons x (go q')
in go q0
{-# INLINE toSortedList #-}
----------
-- Entry
----------
-- | A priority associated with a value. A @PQueue (Entry k a)@ may be used
-- when the priority is separate from the value.
data Entry k a = Entry !k a
deriving (Show, Read, Functor)
-- | Compares by @k@ only.
instance Eq k => Eq (Entry k a) where
Entry k1 _ == Entry k2 _ = k1 == k2
{-# INLINABLE (==) #-}
-- | Compares by @k@ only.
instance Ord k => Ord (Entry k a) where
compare (Entry k1 _) (Entry k2 _) = compare k1 k2
{-# INLINABLE compare #-}
Entry k1 _ <= Entry k2 _ = k1 <= k2
{-# INLINABLE (<=) #-}
instance (NFData k, NFData a) => NFData (Entry k a) where
rnf (Entry k x) = rnf k `seq` rnf x
{-# INLINABLE rnf #-}
-- | The priority.
entryPrio :: Entry k a -> k
entryPrio (Entry k _) = k
-- | The value.
entryValue :: Entry k a -> a
entryValue (Entry _ x) = x