module GHC.Event.PSQ
    (
    
    Elem(..)
    , Key
    , Prio
    
    , PSQ
    
    , size
    , null
    , lookup
    
    , empty
    , singleton
    
    , insert
    
    , delete
    , adjust
    
    , toList
    , toAscList
    , toDescList
    , fromList
    
    , findMin
    , deleteMin
    , minView
    , atMost
    ) where
import Data.Maybe (Maybe(..))
import GHC.Base
import GHC.Num (Num(..))
import GHC.Show (Show(showsPrec))
import GHC.Event.Unique (Unique)
data Elem a = E
    { key   ::  !Key
    , prio  ::  !Prio
    , value :: a
    } deriving (Eq, Show)
type Prio = Double
type Key = Unique
data PSQ a = Void
           | Winner  !(Elem a)
                    !(LTree a)
                     !Key  
           deriving (Eq, Show)
size :: PSQ a -> Int
size Void            = 0
size (Winner _ lt _) = 1 + size' lt
null :: PSQ a -> Bool
null Void           = True
null (Winner _ _ _) = False
lookup :: Key -> PSQ a -> Maybe (Prio, a)
lookup k q = case tourView q of
    Null -> Nothing
    Single (E k' p v)
        | k == k'   -> Just (p, v)
        | otherwise -> Nothing
    tl `Play` tr
        | k <= maxKey tl -> lookup k tl
        | otherwise      -> lookup k tr
empty :: PSQ a
empty = Void
singleton :: Key -> Prio -> a -> PSQ a
singleton k p v = Winner (E k p v) Start k
insert :: Key -> Prio -> a -> PSQ a -> PSQ a
insert k p v q = case q of
    Void -> singleton k p v
    Winner (E k' p' v') Start _ -> case compare k k' of
        LT -> singleton k  p  v  `play` singleton k' p' v'
        EQ -> singleton k  p  v
        GT -> singleton k' p' v' `play` singleton k  p  v
    Winner e (RLoser _ e' tl m tr) m'
        | k <= m    -> insert k p v (Winner e tl m) `play` (Winner e' tr m')
        | otherwise -> (Winner e tl m) `play` insert k p v (Winner e' tr m')
    Winner e (LLoser _ e' tl m tr) m'
        | k <= m    -> insert k p v (Winner e' tl m) `play` (Winner e tr m')
        | otherwise -> (Winner e' tl m) `play` insert k p v (Winner e tr m')
delete :: Key -> PSQ a -> PSQ a
delete k q = case q of
    Void -> empty
    Winner (E k' p v) Start _
        | k == k'   -> empty
        | otherwise -> singleton k' p v
    Winner e (RLoser _ e' tl m tr) m'
        | k <= m    -> delete k (Winner e tl m) `play` (Winner e' tr m')
        | otherwise -> (Winner e tl m) `play` delete k (Winner e' tr m')
    Winner e (LLoser _ e' tl m tr) m'
        | k <= m    -> delete k (Winner e' tl m) `play` (Winner e tr m')
        | otherwise -> (Winner e' tl m) `play` delete k (Winner e tr m')
adjust :: (Prio -> Prio) -> Key -> PSQ a -> PSQ a
adjust f k q0 =  go q0
  where
    go q = case q of
        Void -> empty
        Winner (E k' p v) Start _
            | k == k'   -> singleton k' (f p) v
            | otherwise -> singleton k' p v
        Winner e (RLoser _ e' tl m tr) m'
            | k <= m    -> go (Winner e tl m) `unsafePlay` (Winner e' tr m')
            | otherwise -> (Winner e tl m) `unsafePlay` go (Winner e' tr m')
        Winner e (LLoser _ e' tl m tr) m'
            | k <= m    -> go (Winner e' tl m) `unsafePlay` (Winner e tr m')
            | otherwise -> (Winner e' tl m) `unsafePlay` go (Winner e tr m')
fromList :: [Elem a] -> PSQ a
fromList = foldr (\(E k p v) q -> insert k p v q) empty
toList :: PSQ a -> [Elem a]
toList = toAscList
toAscList :: PSQ a -> [Elem a]
toAscList q  = seqToList (toAscLists q)
toAscLists :: PSQ a -> Sequ (Elem a)
toAscLists q = case tourView q of
    Null         -> emptySequ
    Single e     -> singleSequ e
    tl `Play` tr -> toAscLists tl <> toAscLists tr
toDescList :: PSQ a -> [ Elem a ]
toDescList q = seqToList (toDescLists q)
toDescLists :: PSQ a -> Sequ (Elem a)
toDescLists q = case tourView q of
    Null         -> emptySequ
    Single e     -> singleSequ e
    tl `Play` tr -> toDescLists tr <> toDescLists tl
findMin :: PSQ a -> Maybe (Elem a)
findMin Void           = Nothing
findMin (Winner e _ _) = Just e
deleteMin :: PSQ a -> PSQ a
deleteMin Void           = Void
deleteMin (Winner _ t m) = secondBest t m
minView :: PSQ a -> Maybe (Elem a, PSQ a)
minView Void           = Nothing
minView (Winner e t m) = Just (e, secondBest t m)
secondBest :: LTree a -> Key -> PSQ a
secondBest Start _                 = Void
secondBest (LLoser _ e tl m tr) m' = Winner e tl m `play` secondBest tr m'
secondBest (RLoser _ e tl m tr) m' = secondBest tl m `play` Winner e tr m'
atMost :: Prio -> PSQ a -> ([Elem a], PSQ a)
atMost pt q = let (sequ, q') = atMosts pt q
              in (seqToList sequ, q')
atMosts :: Prio -> PSQ a -> (Sequ (Elem a), PSQ a)
atMosts !pt q = case q of
    (Winner e _ _)
        | prio e > pt -> (emptySequ, q)
    Void              -> (emptySequ, Void)
    Winner e Start _  -> (singleSequ e, Void)
    Winner e (RLoser _ e' tl m tr) m' ->
        let (sequ, q')   = atMosts pt (Winner e tl m)
            (sequ', q'') = atMosts pt (Winner e' tr m')
        in (sequ <> sequ', q' `play` q'')
    Winner e (LLoser _ e' tl m tr) m' ->
        let (sequ, q')   = atMosts pt (Winner e' tl m)
            (sequ', q'') = atMosts pt (Winner e tr m')
        in (sequ <> sequ', q' `play` q'')
type Size = Int
data LTree a = Start
             | LLoser  !Size
                       !(Elem a)
                      !(LTree a)
                       !Key  
                      !(LTree a)
             | RLoser  !Size
                       !(Elem a)
                      !(LTree a)
                       !Key  
                      !(LTree a)
             deriving (Eq, Show)
size' :: LTree a -> Size
size' Start              = 0
size' (LLoser s _ _ _ _) = s
size' (RLoser s _ _ _ _) = s
left, right :: LTree a -> LTree a
left Start                = moduleError "left" "empty loser tree"
left (LLoser _ _ tl _ _ ) = tl
left (RLoser _ _ tl _ _ ) = tl
right Start                = moduleError "right" "empty loser tree"
right (LLoser _ _ _  _ tr) = tr
right (RLoser _ _ _  _ tr) = tr
maxKey :: PSQ a -> Key
maxKey Void           = moduleError "maxKey" "empty queue"
maxKey (Winner _ _ m) = m
lloser, rloser :: Key -> Prio -> a -> LTree a -> Key -> LTree a -> LTree a
lloser k p v tl m tr = LLoser (1 + size' tl + size' tr) (E k p v) tl m tr
rloser k p v tl m tr = RLoser (1 + size' tl + size' tr) (E k p v) tl m tr
omega :: Int
omega = 4
lbalance, rbalance :: Key -> Prio -> a -> LTree a -> Key -> LTree a -> LTree a
lbalance k p v l m r
    | size' l + size' r < 2     = lloser        k p v l m r
    | size' r > omega * size' l = lbalanceLeft  k p v l m r
    | size' l > omega * size' r = lbalanceRight k p v l m r
    | otherwise                 = lloser        k p v l m r
rbalance k p v l m r
    | size' l + size' r < 2     = rloser        k p v l m r
    | size' r > omega * size' l = rbalanceLeft  k p v l m r
    | size' l > omega * size' r = rbalanceRight k p v l m r
    | otherwise                 = rloser        k p v l m r
lbalanceLeft :: Key -> Prio -> a -> LTree a -> Key -> LTree a -> LTree a
lbalanceLeft  k p v l m r
    | size' (left r) < size' (right r) = lsingleLeft  k p v l m r
    | otherwise                        = ldoubleLeft  k p v l m r
lbalanceRight :: Key -> Prio -> a -> LTree a -> Key -> LTree a -> LTree a
lbalanceRight k p v l m r
    | size' (left l) > size' (right l) = lsingleRight k p v l m r
    | otherwise                        = ldoubleRight k p v l m r
rbalanceLeft :: Key -> Prio -> a -> LTree a -> Key -> LTree a -> LTree a
rbalanceLeft  k p v l m r
    | size' (left r) < size' (right r) = rsingleLeft  k p v l m r
    | otherwise                        = rdoubleLeft  k p v l m r
rbalanceRight :: Key -> Prio -> a -> LTree a -> Key -> LTree a -> LTree a
rbalanceRight k p v l m r
    | size' (left l) > size' (right l) = rsingleRight k p v l m r
    | otherwise                        = rdoubleRight k p v l m r
lsingleLeft :: Key -> Prio -> a -> LTree a -> Key -> LTree a -> LTree a
lsingleLeft k1 p1 v1 t1 m1 (LLoser _ (E k2 p2 v2) t2 m2 t3)
    | p1 <= p2  = lloser k1 p1 v1 (rloser k2 p2 v2 t1 m1 t2) m2 t3
    | otherwise = lloser k2 p2 v2 (lloser k1 p1 v1 t1 m1 t2) m2 t3
lsingleLeft k1 p1 v1 t1 m1 (RLoser _ (E k2 p2 v2) t2 m2 t3) =
    rloser k2 p2 v2 (lloser k1 p1 v1 t1 m1 t2) m2 t3
lsingleLeft _ _ _ _ _ _ = moduleError "lsingleLeft" "malformed tree"
rsingleLeft :: Key -> Prio -> a -> LTree a -> Key -> LTree a -> LTree a
rsingleLeft k1 p1 v1 t1 m1 (LLoser _ (E k2 p2 v2) t2 m2 t3) =
    rloser k1 p1 v1 (rloser k2 p2 v2 t1 m1 t2) m2 t3
rsingleLeft k1 p1 v1 t1 m1 (RLoser _ (E k2 p2 v2) t2 m2 t3) =
    rloser k2 p2 v2 (rloser k1 p1 v1 t1 m1 t2) m2 t3
rsingleLeft _ _ _ _ _ _ = moduleError "rsingleLeft" "malformed tree"
lsingleRight :: Key -> Prio -> a -> LTree a -> Key -> LTree a -> LTree a
lsingleRight k1 p1 v1 (LLoser _ (E k2 p2 v2) t1 m1 t2) m2 t3 =
    lloser k2 p2 v2 t1 m1 (lloser k1 p1 v1 t2 m2 t3)
lsingleRight k1 p1 v1 (RLoser _ (E k2 p2 v2) t1 m1 t2) m2 t3 =
    lloser k1 p1 v1 t1 m1 (lloser k2 p2 v2 t2 m2 t3)
lsingleRight _ _ _ _ _ _ = moduleError "lsingleRight" "malformed tree"
rsingleRight :: Key -> Prio -> a -> LTree a -> Key -> LTree a -> LTree a
rsingleRight k1 p1 v1 (LLoser _ (E k2 p2 v2) t1 m1 t2) m2 t3 =
    lloser k2 p2 v2 t1 m1 (rloser k1 p1 v1 t2 m2 t3)
rsingleRight k1 p1 v1 (RLoser _ (E k2 p2 v2) t1 m1 t2) m2 t3
    | p1 <= p2  = rloser k1 p1 v1 t1 m1 (lloser k2 p2 v2 t2 m2 t3)
    | otherwise = rloser k2 p2 v2 t1 m1 (rloser k1 p1 v1 t2 m2 t3)
rsingleRight _ _ _ _ _ _ = moduleError "rsingleRight" "malformed tree"
ldoubleLeft :: Key -> Prio -> a -> LTree a -> Key -> LTree a -> LTree a
ldoubleLeft k1 p1 v1 t1 m1 (LLoser _ (E k2 p2 v2) t2 m2 t3) =
    lsingleLeft k1 p1 v1 t1 m1 (lsingleRight k2 p2 v2 t2 m2 t3)
ldoubleLeft k1 p1 v1 t1 m1 (RLoser _ (E k2 p2 v2) t2 m2 t3) =
    lsingleLeft k1 p1 v1 t1 m1 (rsingleRight k2 p2 v2 t2 m2 t3)
ldoubleLeft _ _ _ _ _ _ = moduleError "ldoubleLeft" "malformed tree"
ldoubleRight :: Key -> Prio -> a -> LTree a -> Key -> LTree a -> LTree a
ldoubleRight k1 p1 v1 (LLoser _ (E k2 p2 v2) t1 m1 t2) m2 t3 =
    lsingleRight k1 p1 v1 (lsingleLeft k2 p2 v2 t1 m1 t2) m2 t3
ldoubleRight k1 p1 v1 (RLoser _ (E k2 p2 v2) t1 m1 t2) m2 t3 =
    lsingleRight k1 p1 v1 (rsingleLeft k2 p2 v2 t1 m1 t2) m2 t3
ldoubleRight _ _ _ _ _ _ = moduleError "ldoubleRight" "malformed tree"
rdoubleLeft :: Key -> Prio -> a -> LTree a -> Key -> LTree a -> LTree a
rdoubleLeft k1 p1 v1 t1 m1 (LLoser _ (E k2 p2 v2) t2 m2 t3) =
    rsingleLeft k1 p1 v1 t1 m1 (lsingleRight k2 p2 v2 t2 m2 t3)
rdoubleLeft k1 p1 v1 t1 m1 (RLoser _ (E k2 p2 v2) t2 m2 t3) =
    rsingleLeft k1 p1 v1 t1 m1 (rsingleRight k2 p2 v2 t2 m2 t3)
rdoubleLeft _ _ _ _ _ _ = moduleError "rdoubleLeft" "malformed tree"
rdoubleRight :: Key -> Prio -> a -> LTree a -> Key -> LTree a -> LTree a
rdoubleRight k1 p1 v1 (LLoser _ (E k2 p2 v2) t1 m1 t2) m2 t3 =
    rsingleRight k1 p1 v1 (lsingleLeft k2 p2 v2 t1 m1 t2) m2 t3
rdoubleRight k1 p1 v1 (RLoser _ (E k2 p2 v2) t1 m1 t2) m2 t3 =
    rsingleRight k1 p1 v1 (rsingleLeft k2 p2 v2 t1 m1 t2) m2 t3
rdoubleRight _ _ _ _ _ _ = moduleError "rdoubleRight" "malformed tree"
play :: PSQ a -> PSQ a -> PSQ a
Void `play` t' = t'
t `play` Void  = t
Winner e@(E k p v) t m `play` Winner e'@(E k' p' v') t' m'
    | p <= p'   = Winner e (rbalance k' p' v' t m t') m'
    | otherwise = Winner e' (lbalance k p v t m t') m'
unsafePlay :: PSQ a -> PSQ a -> PSQ a
Void `unsafePlay` t' =  t'
t `unsafePlay` Void  =  t
Winner e@(E k p v) t m `unsafePlay` Winner e'@(E k' p' v') t' m'
    | p <= p'   = Winner e (rloser k' p' v' t m t') m'
    | otherwise = Winner e' (lloser k p v t m t') m'
data TourView a = Null
                | Single  !(Elem a)
                | (PSQ a) `Play` (PSQ a)
tourView :: PSQ a -> TourView a
tourView Void               = Null
tourView (Winner e Start _) = Single e
tourView (Winner e (RLoser _ e' tl m tr) m') =
    Winner e tl m `Play` Winner e' tr m'
tourView (Winner e (LLoser _ e' tl m tr) m') =
    Winner e' tl m `Play` Winner e tr m'
moduleError :: String -> String -> a
moduleError fun msg = error ("GHC.Event.PSQ." ++ fun ++ ':' : ' ' : msg)
newtype Sequ a = Sequ ([a] -> [a])
emptySequ :: Sequ a
emptySequ = Sequ (\as -> as)
singleSequ :: a -> Sequ a
singleSequ a = Sequ (\as -> a : as)
(<>) :: Sequ a -> Sequ a -> Sequ a
Sequ x1 <> Sequ x2 = Sequ (\as -> x1 (x2 as))
infixr 5 <>
seqToList :: Sequ a -> [a]
seqToList (Sequ x) = x []
instance Show a => Show (Sequ a) where
    showsPrec d a = showsPrec d (seqToList a)