{-# LANGUAGE CPP, TypeFamilies, MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, UndecidableInstances #-} -- | -- Module : Simulation.Aivika.Trans.Vector.Unboxed -- Copyright : Copyright (c) 2009-2014, David Sorokin -- License : BSD3 -- Maintainer : David Sorokin -- Stability : experimental -- Tested with: GHC 7.8.3 -- -- It defines a prototype of mutable unboxed vectors. -- module Simulation.Aivika.Trans.Vector.Unboxed (Vector, newVector, copyVector, vectorCount, appendVector, readVector, writeVector, vectorBinarySearch, vectorInsert, vectorDeleteAt, vectorIndex, freezeVector) where import Data.Array import Control.Monad import Simulation.Aivika.Trans.Session import Simulation.Aivika.Trans.ProtoRef import Simulation.Aivika.Trans.ProtoArray.Unboxed -- | A prototype of mutable unboxed vector. data Vector m a = Vector { vectorSession :: Session m, vectorArrayRef :: ProtoRef m (ProtoArray m a), vectorCountRef :: ProtoRef m Int, vectorCapacityRef :: ProtoRef m Int } -- | Create a new vector within the specified simulation session. newVector :: ProtoArrayMonad m a => Session m -> m (Vector m a) newVector session = do array <- newProtoArray_ session 4 arrayRef <- newProtoRef session array countRef <- newProtoRef session 0 capacityRef <- newProtoRef session 4 return Vector { vectorSession = session, vectorArrayRef = arrayRef, vectorCountRef = countRef, vectorCapacityRef = capacityRef } -- | Copy the vector. copyVector :: ProtoArrayMonad m a => Vector m a -> m (Vector m a) copyVector vector = do let session = vectorSession vector array <- readProtoRef (vectorArrayRef vector) count <- readProtoRef (vectorCountRef vector) array' <- newProtoArray_ session count arrayRef' <- newProtoRef session array' countRef' <- newProtoRef session count capacityRef' <- newProtoRef session count forM_ [0 .. count - 1] $ \i -> do x <- readProtoArray array i writeProtoArray array' i x return Vector { vectorSession = session, vectorArrayRef = arrayRef', vectorCountRef = countRef', vectorCapacityRef = capacityRef' } -- | Ensure that the vector has the specified capacity. vectorEnsureCapacity :: ProtoArrayMonad m a => Vector m a -> Int -> m () vectorEnsureCapacity vector capacity = do capacity' <- readProtoRef (vectorCapacityRef vector) when (capacity' < capacity) $ do array' <- readProtoRef (vectorArrayRef vector) count' <- readProtoRef (vectorCountRef vector) let capacity'' = max (2 * capacity') capacity session = vectorSession vector array'' <- newProtoArray_ session capacity'' forM_ [0 .. count' - 1] $ \i -> do x <- readProtoArray array' i writeProtoArray array'' i x writeProtoRef (vectorArrayRef vector) array'' writeProtoRef (vectorCapacityRef vector) capacity'' -- | Return the element count. vectorCount :: ProtoArrayMonad m a => Vector m a -> m Int vectorCount vector = readProtoRef (vectorCountRef vector) -- | Add the specified element to the end of the vector. appendVector :: ProtoArrayMonad m a => Vector m a -> a -> m () appendVector vector item = do count <- readProtoRef (vectorCountRef vector) vectorEnsureCapacity vector (count + 1) array <- readProtoRef (vectorArrayRef vector) writeProtoArray array count item writeProtoRef (vectorCountRef vector) (count + 1) -- | Read a value from the vector, where indices are started from 0. readVector :: ProtoArrayMonad m a => Vector m a -> Int -> m a readVector vector index = do array <- readProtoRef (vectorArrayRef vector) readProtoArray array index -- | Set an array item at the specified index which is started from 0. writeVector :: ProtoArrayMonad m a => Vector m a -> Int -> a -> m () writeVector vector index item = do array <- readProtoRef (vectorArrayRef vector) writeProtoArray array index item -- | Return the index of the specified element using binary search; otherwise, -- a negated insertion index minus one: 0 -> -0 - 1, ..., i -> -i - 1, .... vectorBinarySearch :: (ProtoArrayMonad m a, Ord a) => Vector m a -> a -> m Int vectorBinarySearch vector item = do array <- readProtoRef (vectorArrayRef vector) count <- readProtoRef (vectorCountRef vector) vectorBinarySearch' array item 0 (count - 1) -- | Return the index of the specified element using binary search -- within the specified range; otherwise, a negated insertion index minus one. vectorBinarySearchWithin :: (ProtoArrayMonad m a, Ord a) => Vector m a -> a -> Int -> Int -> m Int vectorBinarySearchWithin vector item left right = do array <- readProtoRef (vectorArrayRef vector) vectorBinarySearch' array item left right -- | Return the elements of the vector in an immutable array. freezeVector :: ProtoArrayMonad m a => Vector m a -> m (Array Int a) freezeVector vector = do array <- readProtoRef (vectorArrayRef vector) freezeProtoArray array -- | Insert the element in the vector at the specified index. vectorInsert :: ProtoArrayMonad m a => Vector m a -> Int -> a -> m () vectorInsert vector index item = do count <- readProtoRef (vectorCountRef vector) when (index < 0) $ error $ "Index cannot be " ++ "negative: vectorInsert." when (index > count) $ error $ "Index cannot be greater " ++ "than the count: vectorInsert." vectorEnsureCapacity vector (count + 1) array <- readProtoRef (vectorArrayRef vector) forM_ [count, count - 1 .. index + 1] $ \i -> do x <- readProtoArray array (i - 1) writeProtoArray array i x writeProtoArray array index item writeProtoRef (vectorCountRef vector) (count + 1) -- | Delete the element at the specified index. vectorDeleteAt :: ProtoArrayMonad m a => Vector m a -> Int -> m () vectorDeleteAt vector index = do count <- readProtoRef (vectorCountRef vector) when (index < 0) $ error $ "Index cannot be " ++ "negative: vectorDeleteAt." when (index >= count) $ error $ "Index must be less " ++ "than the count: vectorDeleteAt." array <- readProtoRef (vectorArrayRef vector) forM_ [index, index + 1 .. count - 2] $ \i -> do x <- readProtoArray array (i + 1) writeProtoArray array i x writeProtoArray array (count - 1) undefined writeProtoRef (vectorCountRef vector) (count - 1) -- | Return the index of the item or -1. vectorIndex :: (ProtoArrayMonad m a, Eq a) => Vector m a -> a -> m Int vectorIndex vector item = do count <- readProtoRef (vectorCountRef vector) array <- readProtoRef (vectorArrayRef vector) let loop index = if index >= count then return $ -1 else do x <- readProtoArray array index if item == x then return index else loop $ index + 1 loop 0 vectorBinarySearch' :: (ProtoArrayMonad m a, Ord a) => ProtoArray m a -> a -> Int -> Int -> m Int vectorBinarySearch' array item left right = if left > right then return $ - (right + 1) - 1 else do let index = (left + right) `div` 2 curr <- readProtoArray array index if item < curr then vectorBinarySearch' array item left (index - 1) else if item == curr then return index else vectorBinarySearch' array item (index + 1) right