module MOO.List (
MOOList
, length
, null
, (!)
, head
, slice
, tail
, splitAt
, empty
, snoc
, concat
, forM_
, elem
, findIndex
, elemIndex
, foldM
, toList
, fromList
, storageBytes
, equal
, assocLens
, set
, insert
, delete
) where
import Control.Applicative ((<$>))
import Data.Function (on)
import Data.HashMap.Lazy (HashMap)
import Data.Monoid (Monoid(mempty, mappend, mconcat), (<>))
import Data.Typeable (Typeable)
import Data.Vector (Vector)
import Database.VCache (VCacheable(put, get))
import Prelude hiding (concat, head, length, null, tail, elem, splitAt, (++))
import qualified Data.HashMap.Lazy as HM
import qualified Data.Vector as V
import qualified Data.Vector.Mutable as VM
import MOO.Types (Value(Lst, Str), StrT)
import MOO.Util (VVector(..))
import qualified MOO.Types as Value
type AssocMap = HashMap StrT (Int, Value)
data MOOList = MOOList {
toVector :: Vector Value
, toAssocMap :: Maybe AssocMap
} deriving Typeable
instance Eq MOOList where
(==) = (==) `on` toVector
instance Monoid MOOList where
mempty = empty
mappend = (++)
mconcat = concat
instance Show MOOList where
show = show . toList
instance VCacheable MOOList where
put = put . VVector . toVector
get = fromVector . unVVector <$> get
fromVector :: Vector Value -> MOOList
fromVector vec = MOOList {
toVector = vec
, toAssocMap = vectorToAssocMap vec
}
fromList :: [Value] -> MOOList
fromList = fromVector . V.fromList
toList :: MOOList -> [Value]
toList = V.toList . toVector
storageBytes :: MOOList -> Int
storageBytes = V.sum . V.map Value.storageBytes . toVector
equal :: MOOList -> MOOList -> Bool
equal = vectorEqual `on` toVector
where x `vectorEqual` y = V.length x == V.length y &&
V.and (V.zipWith Value.equal x y)
empty :: MOOList
empty = fromVector V.empty
(!) :: MOOList -> Int -> Value
lst ! i = toVector lst V.! i
head :: MOOList -> Value
head = V.head . toVector
tail :: MOOList -> MOOList
tail = fromVector . V.tail . toVector
slice :: Int -> Int -> MOOList -> MOOList
slice i n = fromVector . V.slice i n . toVector
splitAt :: Int -> MOOList -> (MOOList, MOOList)
splitAt n lst = let (b, a) = V.splitAt n (toVector lst)
in (fromVector b, fromVector a)
snoc :: MOOList -> Value -> MOOList
snoc lst = fromVector . V.snoc (toVector lst)
(++) :: MOOList -> MOOList -> MOOList
x ++ y = fromVector $ toVector x <> toVector y
concat :: [MOOList] -> MOOList
concat = fromVector . V.concat . map toVector
length :: MOOList -> Int
length = V.length . toVector
null :: MOOList -> Bool
null = V.null . toVector
elem :: Value -> MOOList -> Bool
elem x = V.elem x . toVector
elemIndex :: Value -> MOOList -> Maybe Int
elemIndex x = V.elemIndex x . toVector
findIndex :: (Value -> Bool) -> MOOList -> Maybe Int
findIndex p = V.findIndex p . toVector
foldM :: Monad m => (a -> Value -> m a) -> a -> MOOList -> m a
foldM f acc = V.foldM f acc . toVector
forM_ :: Monad m => MOOList -> (Value -> m b) -> m ()
forM_ = V.forM_ . toVector
vectorToAssocMap :: Vector Value -> Maybe AssocMap
vectorToAssocMap = fmap snd . V.foldM mkAssocMap (0, HM.empty)
where mkAssocMap :: (Int, AssocMap) -> Value -> Maybe (Int, AssocMap)
mkAssocMap (i, map) (Lst lst) = case toList lst of
[Str k, value] -> let map' = assocMapInsert k (i, value) map
in Just (succ $! i, map')
[_ , _ ] -> Just (succ $! i, map)
_ -> Nothing
mkAssocMap _ _ = Nothing
assocMapInsert :: StrT -> (Int, Value) -> AssocMap -> AssocMap
assocMapInsert = HM.insertWith (flip const)
assocLens :: StrT -> MOOList -> Maybe (Maybe Value, Maybe Value -> MOOList)
assocLens key lst = mkLens <$> toAssocMap lst
where mkLens :: AssocMap -> (Maybe Value, Maybe Value -> MOOList)
mkLens map = (snd <$> current, setValue)
where current :: Maybe (Int, Value)
current = HM.lookup key map
setValue :: Maybe Value -> MOOList
setValue (Just newValue) =
let vec = toVector lst
assoc = Lst $ fromList [Str key, newValue]
map' i = HM.insert key (i, newValue) map
in case current of
Nothing -> lst {
toVector = V.snoc vec assoc
, toAssocMap = Just $ map' (V.length vec)
}
Just (i, _) -> lst {
toVector = vectorSet vec assoc i
, toAssocMap = Just $ map' i
}
setValue Nothing = maybe lst (delete lst . fst) current
set :: MOOList -> Value -> Int -> MOOList
set lst value = fromVector . vectorSet (toVector lst) value
vectorSet :: Vector Value -> Value -> Int -> Vector Value
vectorSet vec value i = V.modify (\vec' -> VM.write vec' i value) vec
insert :: MOOList -> Int -> Value -> MOOList
insert lst i = fromVector . vectorInsert (toVector lst) i
where vectorInsert :: Vector Value -> Int -> Value -> Vector Value
vectorInsert vec index value
| index <= 0 = V.cons value vec
| index >= vecLen = V.snoc vec value
| otherwise = V.create $ do
vec' <- flip VM.grow 1 =<< V.thaw vec
let moveLen = vecLen index
s = VM.slice index moveLen vec'
t = VM.slice (index + 1) moveLen vec'
VM.move t s
VM.write vec' index value
return vec'
where vecLen = V.length vec
delete :: MOOList -> Int -> MOOList
delete lst i = fromVector $ vectorDelete (toVector lst) i
where vectorDelete :: Vector Value -> Int -> Vector Value
vectorDelete vec index
| index == 0 = V.tail vec
| index == vecLen 1 = V.init vec
| index * 2 < vecLen = V.tail $ (`V.modify` vec) $ \vec' ->
let s = VM.slice 0 index vec'
t = VM.slice 1 index vec'
in VM.move t s
| otherwise = V.init $ (`V.modify` vec) $ \vec' ->
let moveLen = vecLen index 1
s = VM.slice (index + 1) moveLen vec'
t = VM.slice index moveLen vec'
in VM.move t s
where vecLen = V.length vec