module Data.STM.PriorityQueue.Internal.LLSLPQ(
LLSLPQ,
new'
) where
import Control.Monad.STM
import Control.Concurrent.STM
import System.IO.Unsafe
import System.Random.PCG.Fast (createSystemRandom, uniform, GenIO)
import Data.STM.PriorityQueue.Class
data Node k v
= Nil
| Node
{ _getKey :: k
, _getVal :: TVar v
, _getUp :: TNode k v
, _getNext :: TNode k v
, _getDown :: TNode k v
}
type TNode k v = TVar (Node k v)
data LLSLPQ k v
= PQ
{ _getTop :: TNode k v
, _getBottom :: TNode k v
, _getHeight :: TVar Int
, _getNil :: TNode k v
, _getGenIO :: TVar GenIO
}
buildHeads
:: Ord k
=> TNode k v
-> Int
-> STM (TNode k v, TNode k v)
buildHeads up' 0 = do
nil' <- newTVar Nil
return (nil', up')
buildHeads up' h = do
curr' <- newTVar Nil
next' <- newTVar Nil
(down', bottom') <- buildHeads curr' (h1)
writeTVar curr' $ Node undefined undefined up' next' down'
return (curr', bottom')
new' :: Ord k => Int -> STM (LLSLPQ k v)
new' height = do
nil' <- newTVar Nil
(top', bottom') <- buildHeads nil' height
height' <- newTVar height
gio' <- newTVar $ unsafePerformIO createSystemRandom
return $ PQ top' bottom' height' nil' gio'
pqNew :: Ord k => STM (LLSLPQ k v)
pqNew = new' 16
logHalf :: Float
logHalf = log 0.5
chooseLvl :: GenIO -> Int -> Int
chooseLvl g h =
min h $ 1 + truncate (log x / logHalf)
where x = unsafePerformIO (uniform g :: IO Float)
pqInsert :: Ord k => LLSLPQ k v -> k -> v -> STM ()
pqInsert (PQ top' _ height' nil' gio') k v = do
top <- readTVar top'
case top of
Nil -> error "Illegal state: top must not be Nil"
_ -> do
prevs <- buildPrevs top' []
height <- readTVar height'
v' <- newTVar v
gio <- readTVar gio'
let lvl = chooseLvl gio height
insertNode v' nil' prevs lvl
where
buildPrevs curr' prevs = do
curr <- readTVar curr'
case curr of
Nil -> return prevs
(Node _ _ _ next' down') -> do
next <- readTVar next'
let goDown = buildPrevs down' $ curr':prevs
goNext = buildPrevs next' prevs
case next of
Nil -> goDown
(Node kk _ _ _ _) -> if kk > k then goDown else goNext
insertNode _ _ _ 0 = return ()
insertNode v' down' (prev':prevs) h = do
curr' <- newTVar Nil
up' <- newTVar Nil
(Node _ _ _ next' _) <- readTVar prev'
next <- readTVar next'
nextnext' <- newTVar next
let curr = Node k v' up' nextnext' down'
down <- readTVar down'
case down of
Nil -> return ()
(Node _ _ downUp' _ _) -> writeTVar downUp' curr
writeTVar next' curr
writeTVar curr' curr
insertNode v' curr' prevs (h1)
insertNode _ _ [] _ = error "Illegal state: main layout must be not lower than new column"
pqPeekMin :: Ord k => LLSLPQ k v -> STM v
pqPeekMin (PQ _ bottom' _ _ _) = do
bottom <- readTVar bottom'
case bottom of
Nil -> error "Illegal state: bottom must not be Nil"
(Node _ _ _ next' _) -> do
next <- readTVar next'
case next of
Nil -> retry
(Node _ v' _ _ _) -> readTVar v'
pqDeleteMin :: Ord k => LLSLPQ k v -> STM v
pqDeleteMin (PQ _ bottom' _ _ _) = do
bottom <- readTVar bottom'
case bottom of
Nil -> error "Illegal state: bottom must not be Nil"
(Node _ _ _ next' _) -> do
next <- readTVar next'
case next of
Nil -> retry
(Node _ v' _ _ _) -> do
recDel bottom next
readTVar v'
where
recDel _ Nil = return ()
recDel (Node _ _ headUp' next'' _)
(Node _ _ up' nextnext' _) = do
nextnext <- readTVar nextnext'
up <- readTVar up'
headUp <- readTVar headUp'
writeTVar next'' nextnext
recDel headUp up
recDel _ _ = error "Illegal state"
instance PriorityQueue LLSLPQ where
new = pqNew
insert = pqInsert
peekMin = pqPeekMin
deleteMin = pqDeleteMin