module Data.SparseArray
( SparseArray
, Hashable, Hash, hash
, Level, down, up, lastLevel
, emptyArray, mkSingleton, mkPair
, arrayLookup, arrayInsert, arrayUpdate, arrayDelete
, arrayMapM, arrayToMaybe
) where
import Control.Monad.ST
import Data.Bits
import Data.Hashable (Hashable)
import qualified Data.Hashable as H
import Data.Primitive.Array
import Data.Word
import Prelude hiding (lookup, mapM)
data SparseArray a = SparseArray !Bitmap !(Array a)
type Bitmap = Word
type Hash = Word
type Level = Int
emptyArray :: SparseArray a
emptyArray = SparseArray 0 arr
where
arr = runST $ do
marr <- newArray 0 undefined
unsafeFreezeArray marr
mkSingleton :: Level -> Hash -> a -> SparseArray a
mkSingleton level h a = SparseArray bmp arr
where
i = index level h
bmp = unsafeShiftL 1 i
arr = runST $ do
marr <- newArray 1 a
unsafeFreezeArray marr
mkPair :: Level -> Hash -> a -> Hash -> a -> Maybe (SparseArray a)
mkPair level h1 a1 h2 a2 =
case compare i1 i2 of
LT -> Just $ SparseArray bmp (pair a1 a2)
GT -> Just $ SparseArray bmp (pair a2 a1)
EQ -> Nothing
where
i1 = index level h1
i2 = index level h2
bmp = (unsafeShiftL 1 i1) .|. (unsafeShiftL 1 i2)
pair x y = runST $ do
marr <- newArray 2 undefined
writeArray marr 0 x
writeArray marr 1 y
unsafeFreezeArray marr
arrayLookup :: Level -> Hash -> SparseArray a -> Maybe a
arrayLookup level h (SparseArray bmp arr)
| bmp .&. m == 0 = Nothing
| otherwise = Just (indexArray arr i)
where
m = mask level h
i = sparseIndex bmp m
arrayInsert :: Level -> Hash -> a -> SparseArray a -> SparseArray a
arrayInsert level h a (SparseArray bmp arr) = SparseArray bmp' arr'
where
n = popCount bmp
m = mask level h
i = sparseIndex bmp m
bmp' = bmp .|. m
arr' = runST $ do
marr <- newArray (n+1) undefined
copyArray marr 0 arr 0 i
writeArray marr i a
copyArray marr (i+1) arr i (ni)
unsafeFreezeArray marr
arrayUpdate :: Level -> Hash -> a -> SparseArray a -> SparseArray a
arrayUpdate level h a (SparseArray bmp arr) = SparseArray bmp arr'
where
n = popCount bmp
m = mask level h
i = sparseIndex bmp m
arr' = runST $ do
marr <- newArray n undefined
copyArray marr 0 arr 0 n
writeArray marr i a
unsafeFreezeArray marr
arrayDelete :: Level -> Hash -> SparseArray a -> SparseArray a
arrayDelete level h (SparseArray bmp arr) = SparseArray bmp' arr'
where
n = popCount bmp
m = mask level h
i = sparseIndex bmp m
bmp' = bmp `xor` m
arr' = runST $ do
marr <- newArray (n1) undefined
copyArray marr 0 arr 0 i
copyArray marr i arr (i+1) (n(i+1))
unsafeFreezeArray marr
arrayMapM :: (a -> IO a) -> SparseArray a -> IO (SparseArray a)
arrayMapM f = \(SparseArray bmp arr) -> do
let n = popCount bmp
marr <- newArray n undefined
go n arr marr 0
arr' <- unsafeFreezeArray marr
return (SparseArray bmp arr')
where
go n arr marr i
| i >= n = return ()
| otherwise = do
x <- indexArrayM arr i
writeArray marr i =<< f x
go n arr marr (i+1)
arrayToMaybe :: SparseArray a -> Maybe a
arrayToMaybe (SparseArray bmp arr) =
case popCount bmp of
1 -> Just $ indexArray arr 0
_ -> Nothing
hash :: Hashable a => a -> Hash
hash = fromIntegral . H.hash
hashLength :: Int
hashLength = finiteBitSize (undefined :: Word)
bitsPerSubkey :: Int
bitsPerSubkey = floor . logBase (2 :: Float) . fromIntegral $ hashLength
subkeyMask :: Bitmap
subkeyMask = 1 `unsafeShiftL` bitsPerSubkey 1
down :: Level -> Level
down = (+) bitsPerSubkey
up :: Level -> Level
up = subtract bitsPerSubkey
lastLevel :: Level
lastLevel = hashLength
index :: Level -> Hash -> Int
index level h = fromIntegral $ (h `unsafeShiftR` level) .&. subkeyMask
mask :: Level -> Hash -> Bitmap
mask level h = 1 `unsafeShiftL` index level h
sparseIndex :: Bitmap -> Bitmap -> Int
sparseIndex bmp m = popCount ((m 1) .&. bmp)