module Data.Struct.Internal.Order where
import Control.Exception
import Control.Monad
import Control.Monad.Primitive
import Control.Monad.ST
import Data.Bits
import Data.Struct.Internal
import Data.Struct.Internal.Label (Label, Key)
import qualified Data.Struct.Label as Label
import qualified Data.Struct.Internal.Label as Label (key)
import Data.Word
newtype Order a s = Order { runOrder :: Object s }
instance Eq (Order a s) where (==) = eqStruct
instance Struct (Order a)
key :: Field (Order a) Key
key = field 0
value :: Field (Order a) a
value = field 1
next :: Slot (Order a) (Order a)
next = slot 2
prev :: Slot (Order a) (Order a)
prev = slot 3
parent :: Slot (Order a) Label
parent = slot 4
makeOrder :: PrimMonad m => Label (PrimState m) -> Key -> a -> Order a (PrimState m) -> Order a (PrimState m) -> m (Order a (PrimState m))
makeOrder mom a v p n = st $ do
this <- alloc 5
set parent this mom
setField key this a
setField value this v
set prev this p
set next this n
return this
compareM :: PrimMonad m => Order a (PrimState m) -> Order a (PrimState m) -> m Ordering
compareM i j
| isNil i || isNil j = throw NullPointerException
| otherwise = st $ do
ui <- get parent i
uj <- get parent j
xs <- Label.compareM ui uj
case xs of
EQ -> compare <$> getField key i <*> getField key j
x -> return x
insertAfter :: PrimMonad m => Order a (PrimState m) -> a -> m (Order a (PrimState m))
insertAfter n0 a1 = st $ do
when (isNil n0) $ throw NullPointerException
mom <- get parent n0
k0 <- getField key n0
n2 <- get next n0
k2 <- if isNil n2 then return maxBound else getField key n2
let !k1 = k0 + unsafeShiftR (k2 k0) 1
n1 <- makeOrder mom k1 a1 n0 n2
unless (isNil n2) $ set prev n2 n1
set next n0 n1
when (k0 + 1 == k2) $ rewind mom n0
return n1
where
rewind :: Label s -> Order a s -> ST s ()
rewind mom this = do
p <- get prev this
if isNil p then rebalance mom mom this 0 64
else do
dad <- get parent p
if mom == dad then rewind mom p
else rebalance mom mom p 0 64
rebalance :: Label s -> Label s -> Order a s -> Word64 -> Int -> ST s ()
rebalance mom dad this k j = unless (isNil this) $ do
guardian <- get parent this
when (mom == guardian) $ do
setField key this k
set parent this dad
n <- get next this
if j > 0 then rebalance mom dad n (k + deltaU) (j1)
else do
stepdad <- Label.insertAfter dad
rebalance mom stepdad n deltaU logU
delete :: PrimMonad m => Order a (PrimState m) -> m ()
delete this = st $ do
when (isNil this) $ throw NullPointerException
mom <- get parent this
p <- get prev this
n <- get next this
set prev this Nil
set next this Nil
x <- if isNil p then return False
else do
set next p n
pmom <- get parent p
return (mom == pmom)
y <- if isNil n then return False
else do
set prev n p
nmom <- get parent n
return (mom == nmom)
unless (x || y) $ Label.delete mom
logU :: Int
logU = 64
loglogU :: Int
loglogU = 6
deltaU :: Key
deltaU = unsafeShiftR maxBound loglogU
new :: PrimMonad m => a -> m (Order a (PrimState m))
new a = st $ do
l <- Label.new
makeOrder l (unsafeShiftR maxBound 1) a Nil Nil
keys :: PrimMonad m => Order a (PrimState m) -> m (Key, Key)
keys this = st $ do
mom <- get parent this
(,) <$> getField Label.key mom <*> getField key this