module Data.Vector.Dynamic
( DynamicVector
, newDyn
, newDyn'
, readDyn
, writeDyn
, pushDyn
, popDyn
, shiftDyn
, unshiftDyn
, sizeDyn
, toListDyn
) where
import Control.Imperative.Vector.Base (VectorElem, VectorEntity)
import Control.Monad
import Control.Monad.Primitive (PrimMonad, PrimState)
import Data.Bits
import Data.Int
import Data.Primitive.MutVar
import qualified Data.Vector.Generic.Mutable as GMV
import qualified Data.Vector.Mutable as MV
import qualified Data.Vector.Unboxed as UV
import Data.Word
data DynamicVector m a = Dynamic
{ logSizeVar :: MutVar (PrimState m) Int
, frontVar :: MutVar (PrimState m) Int
, rearVar :: MutVar (PrimState m) Int
, vecVar :: MutVar (PrimState m) (VectorEntity a (PrimState m) a)
}
toPow2 :: Int -> Int
toPow2 n
| n <= 0 = 1
| otherwise = go (n1) 1
where
go 0 !c = c
go n !c = go (shiftR n 1) (shiftL c 1)
newDyn :: (PrimMonad m, VectorElem a) => Int -> m (DynamicVector m a)
newDyn logSize = liftM4 Dynamic
(newMutVar logSize)
(newMutVar 0)
(newMutVar (max 0 (logSize1)))
(GMV.unsafeNew (toPow2 logSize) >>= newMutVar)
newDyn' :: (PrimMonad m, VectorElem a) => Int -> a -> m (DynamicVector m a)
newDyn' logSize x = do
d@(Dynamic {vecVar}) <- newDyn logSize
v <- readMutVar vecVar
w <- GMV.replicate logSize x
unsafeMemCopy 0 v 0 (max 0 logSize) w
return d
unsafeMemCopy
:: (PrimMonad m, GMV.MVector v a)
=> Int
-> v (PrimState m) a
-> Int
-> Int
-> v (PrimState m) a
-> m ()
unsafeMemCopy i dst j n src = do
let sdst = GMV.unsafeSlice i n dst
ssrc = GMV.unsafeSlice j n src
GMV.unsafeCopy sdst ssrc
readDyn :: (PrimMonad m, VectorElem a) => DynamicVector m a -> Int -> m a
readDyn d@(Dynamic {logSizeVar, frontVar, rearVar, vecVar}) logIndex = do
logSize <- readMutVar logSizeVar
when (logSize <= logIndex || logIndex < 0) $
error $ "index out of bounds " ++ show (logIndex, logSize)
phyIndex <- log2phy d logIndex
vec <- readMutVar vecVar
GMV.unsafeRead vec phyIndex
log2phy :: (PrimMonad m, VectorElem a) => DynamicVector m a -> Int -> m Int
log2phy (Dynamic {frontVar, vecVar}) i = do
front <- readMutVar frontVar
vec <- readMutVar vecVar
let realCap = GMV.length vec
return $ (front + i) .&. (realCap 1)
writeDyn :: (PrimMonad m, VectorElem a) => DynamicVector m a -> Int -> a -> m ()
writeDyn d@(Dynamic {logSizeVar, rearVar, vecVar}) logIndex x = do
logSize <- readMutVar logSizeVar
when (logIndex < 0) $
error $ "index out of bounds " ++ show (logIndex, logSize)
when (logSize <= logIndex) $ do
resizeDyn d (logIndex + 1)
log2phy d logIndex >>= writeMutVar rearVar
phyIndex <- log2phy d logIndex
vec <- readMutVar vecVar
GMV.unsafeWrite vec phyIndex x
resizeDyn :: (PrimMonad m, VectorElem a) => DynamicVector m a -> Int -> m ()
resizeDyn (Dynamic {logSizeVar, frontVar, rearVar, vecVar}) newLogSize = do
vec <- readMutVar vecVar
let realCap = GMV.length vec
when (realCap < newLogSize) $ do
let newRealCap = until (newLogSize <=) (*2) realCap
diff = newRealCap realCap
front <- readMutVar frontVar
rear <- readMutVar rearVar
newVec <- if front > rear
then do
let n = realCap front
nv <- GMV.unsafeNew newRealCap
unsafeMemCopy 0 nv 0 (rear+1) vec
unsafeMemCopy (front + diff) nv front n vec
writeMutVar frontVar (front + diff)
return nv
else GMV.unsafeGrow vec diff
writeMutVar vecVar newVec
writeMutVar logSizeVar newLogSize
pushDyn :: (PrimMonad m, VectorElem a) => DynamicVector m a -> a -> m ()
pushDyn d@(Dynamic {logSizeVar, rearVar}) x = do
logSize <- readMutVar logSizeVar
writeDyn d logSize x
popDyn :: (PrimMonad m, VectorElem a) => DynamicVector m a -> m a
popDyn d@(Dynamic {logSizeVar, frontVar, rearVar}) = do
logSize <- readMutVar logSizeVar
when (logSize < 1) $ error "Couldn't pop on an empty vector"
x <- readDyn d (logSize1)
when (logSize > 1) $
log2phy d (logSize2) >>= writeMutVar rearVar
modifyMutVar' logSizeVar pred
return x
shiftDyn :: (PrimMonad m, VectorElem a) => DynamicVector m a -> m a
shiftDyn d@(Dynamic {logSizeVar, frontVar, rearVar, vecVar}) = do
logSize <- readMutVar logSizeVar
when (logSize < 1) $ error "Couldn't shift on an empty vector"
x <- readDyn d 0
when (logSize > 1) $
log2phy d 1 >>= writeMutVar frontVar
modifyMutVar' logSizeVar pred
return x
unshiftDyn :: (PrimMonad m, VectorElem a) => DynamicVector m a -> a -> m ()
unshiftDyn d@(Dynamic {logSizeVar, frontVar, rearVar, vecVar}) x = do
logSize <- readMutVar logSizeVar
realCap <- liftM GMV.length $ readMutVar vecVar
if logSize == realCap
then resizeDyn d (logSize+1)
else modifyMutVar' logSizeVar succ
phyNewHead <- log2phy d (1)
vec <- readMutVar vecVar
GMV.unsafeWrite vec phyNewHead x
writeMutVar frontVar phyNewHead
when (logSize==0) $ writeMutVar rearVar phyNewHead
toListDyn :: (PrimMonad m, VectorElem a) => DynamicVector m a -> m [a]
toListDyn (Dynamic {logSizeVar, frontVar, rearVar, vecVar}) = do
logSize <- readMutVar logSizeVar
if logSize == 0
then return []
else do
front <- readMutVar frontVar
rear <- readMutVar rearVar
vec <- readMutVar vecVar
let realCap = GMV.length vec
indicies = if front <= rear then [front..rear] else [front..realCap1] ++ [0..rear]
forM indicies $ GMV.unsafeRead vec
sizeDyn :: PrimMonad m => DynamicVector m a -> m Int
sizeDyn (Dynamic {logSizeVar}) = readMutVar logSizeVar