packages feed

pvector-0.1.0.0: src/Data/Vector/Persistent/Internal/Array.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE UnboxedSums #-}
{-# LANGUAGE UnboxedTuples #-}

module Data.Vector.Persistent.Internal.Array
  ( Array,
    MArray,
    nullSmallArray,
    lastSmallArray,
    singletonSmallArray,
    twoSmallArray,
    updateSmallArray,
    modifySmallArray,
    modifySmallArrayF,
    modifySmallArray',
    updateResizeSmallArray,
    popSmallArray,
    undefinedElem,
    ifoldrStepSmallArray,
    ifoldlStepSmallArray,
    ifoldrStepSmallArray',
    ifoldlStepSmallArray',
    imapStepSmallArray,
    imapStepSmallArray',
    itraverseStepSmallArray,
    modifySmallArray#,
    mapSmallArray#,
    shrinkSmallMutableArray_,
  )
where

import Control.Applicative (liftA2)
import Control.Monad (when)
import Control.Monad.Primitive (PrimMonad, PrimState)
import Control.Monad.ST (ST, runST)
import Data.Coerce (coerce)
import Data.Functor (($>))
import Data.Functor.Identity (Identity (..))
import qualified Data.Primitive as Primitive
import Data.Primitive.SmallArray
import GHC.Exts (SmallMutableArray#)

type Array = SmallArray

type MArray = SmallMutableArray

-- | Used to support older ghcs.
shrinkSmallMutableArray_ :: PrimMonad m => MArray (PrimState m) a -> Int -> m (MArray (PrimState m) a)
#if __GLASGOW_HASKELL__ >= 810
shrinkSmallMutableArray_ marr n = Primitive.shrinkSmallMutableArray marr n $> marr
#else
shrinkSmallMutableArray_ mary n = Primitive.cloneSmallMutableArray mary 0 n
#endif 
{-# INLINE shrinkSmallMutableArray_ #-}

mapSmallArray# :: (a -> (# b #)) -> SmallArray a -> SmallArray b
mapSmallArray# f sa = createSmallArray (length sa) (error "mapSmallArray#") $ \smb -> do
  let go i =
        when (i < length sa) $ do
          x <- indexSmallArrayM sa i
          let !(# y #) = f x
          writeSmallArray smb i y *> go (i + 1)
  go 0
{-# INLINE mapSmallArray# #-}

nullSmallArray :: SmallArray a -> Bool
nullSmallArray arr = sizeofSmallArray arr == 0
{-# INLINE nullSmallArray #-}

lastSmallArray :: SmallArray a -> a
lastSmallArray arr = indexSmallArray arr $ sizeofSmallArray arr

singletonSmallArray :: a -> Array a
singletonSmallArray a = runSmallArray $ newSmallArray 1 a
{-# INLINE singletonSmallArray #-}

twoSmallArray :: a -> a -> Array a
twoSmallArray x y = runSmallArray $ do
  marr <- newSmallArray 2 x
  writeSmallArray marr 1 y
  pure marr
{-# INLINE twoSmallArray #-}

updateSmallArray :: Array a -> Int -> a -> Array a
updateSmallArray arr i x = modifySmallArray# arr i $ \_ -> (# x #)
{-# INLINE updateSmallArray #-}

modifySmallArray :: Array a -> Int -> (a -> a) -> Array a
modifySmallArray arr i f = modifySmallArray# arr i $ \x -> (# f x #)
{-# INLINE modifySmallArray #-}

modifySmallArrayF :: Functor f => Array a -> Int -> (a -> f a) -> f (Array a)
modifySmallArrayF arr i f | (# x #) <- indexSmallArray## arr i = updateSmallArray arr i <$> f x
{-# INLINE modifySmallArrayF #-}

modifySmallArray' :: Array a -> Int -> (a -> a) -> Array a
modifySmallArray' arr i f = modifySmallArray# arr i $ \x -> let !x' = f x in (# x' #)
{-# INLINE modifySmallArray' #-}

modifySmallArray# :: Array a -> Int -> (a -> (# a #)) -> Array a
modifySmallArray# arr i f = runSmallArray $ do
  marr <- thawSmallArray arr 0 $ sizeofSmallArray arr
  x <- indexSmallArrayM arr i
  let !(# x' #) = f x
  writeSmallArray marr i x'
  pure marr
{-# INLINE modifySmallArray# #-}

updateResizeSmallArray :: Array a -> Int -> a -> Array a
updateResizeSmallArray arr i a = runSmallArray $ do
  marr <- thawSmallArray arr 0 (max len (i + 1))
  writeSmallArray marr i a
  pure marr
  where
    len = sizeofSmallArray arr
{-# INLINE updateResizeSmallArray #-}

popSmallArray :: Array a -> Array a
popSmallArray arr = runSmallArray $ thawSmallArray arr 0 (sizeofSmallArray arr - 1)
{-# INLINE popSmallArray #-}

undefinedElem :: forall a. a
undefinedElem = error "undefined element"
{-# NOINLINE undefinedElem #-}

ifoldrStepSmallArray :: Int -> Int -> (Int -> a -> b -> b) -> b -> SmallArray a -> b
ifoldrStepSmallArray i0 step f z arr = do
  let len = sizeofSmallArray arr
      go i j
        | i == len = z
        | (# x #) <- indexSmallArray## arr i = f j x (go (i + 1) $! j + step)
  go 0 i0
{-# INLINE ifoldrStepSmallArray #-}

ifoldlStepSmallArray :: Int -> Int -> (Int -> b -> a -> b) -> b -> SmallArray a -> b
ifoldlStepSmallArray i0 step f z arr = do
  let len = sizeofSmallArray arr
      go i j
        | i < 0 = z
        | (# x #) <- indexSmallArray## arr i = f j (go (i - 1) $! j - step) x
  go (len - 1) i0
{-# INLINE ifoldlStepSmallArray #-}

ifoldrStepSmallArray' :: Int -> Int -> (Int -> a -> b -> b) -> b -> SmallArray a -> b
ifoldrStepSmallArray' i0 step f z arr = do
  let go i j acc
        | i < 0 = acc
        | (# x #) <- indexSmallArray## arr i = (go (i - 1) $! (j - step)) $! f j x acc
  go (sizeofSmallArray arr) i0 z
{-# INLINE ifoldrStepSmallArray' #-}

ifoldlStepSmallArray' :: Int -> Int -> (Int -> b -> a -> b) -> b -> SmallArray a -> b
ifoldlStepSmallArray' i0 step f z arr = do
  let go i j acc
        | i == sizeofSmallArray arr = acc
        | (# x #) <- indexSmallArray## arr i = (go (i + 1) $! (j + step)) $! f j acc x
  go 0 i0 z
{-# INLINE ifoldlStepSmallArray' #-}

imapStepSmallArray :: Int -> Int -> (Int -> a -> b) -> SmallArray a -> SmallArray b
imapStepSmallArray i0 step f arr = createSmallArray len undefinedElem $ \marr -> do
  let go i k = when (i < len) $ do
        x <- indexSmallArrayM arr i
        writeSmallArray marr i (f k x)
        go (i + 1) $! k + step
  go 0 i0
  where
    len = sizeofSmallArray arr
{-# INLINE imapStepSmallArray #-}

imapStepSmallArray' :: Int -> (a -> Int) -> (Int -> a -> b) -> SmallArray a -> SmallArray b
imapStepSmallArray' i0 step f arr = createSmallArray len undefinedElem $ \marr -> do
  let go i k = when (i < len) $ do
        x <- indexSmallArrayM arr i
        writeSmallArray marr i $! f k x
        go (i + 1) $! k + step x
  go 0 i0
  where
    len = sizeofSmallArray arr
{-# INLINE imapStepSmallArray' #-}

newtype STA a = STA {_runSTA :: forall s. SmallMutableArray# s a -> ST s (SmallArray a)}

runSTA :: Int -> STA a -> SmallArray a
runSTA !sz = \(STA m) ->
  runST $
    newSmallArray sz undefinedElem
      >>= \(SmallMutableArray ar#) -> m ar#

itraverseStepSmallArray :: Applicative f => Int -> Int -> (Int -> a -> f b) -> SmallArray a -> f (SmallArray b)
itraverseStepSmallArray i0 step f = \ !arr -> do
  let len = sizeofSmallArray arr
      go i k
        | i == len =
            pure $ STA $ \marr -> unsafeFreezeSmallArray (SmallMutableArray marr)
        | (# x #) <- indexSmallArray## arr i =
            liftA2
              (\b (STA m) -> STA $ \marr -> writeSmallArray (SmallMutableArray marr) i b >> m marr)
              (f k x)
              (go (i + 1) $! k + step)
  if len == 0
    then pure emptySmallArray
    else runSTA len <$> go 0 i0
{-# INLINE [1] itraverseStepSmallArray #-}

{-# RULES
"itraverseStepSmallArray/ST" forall i0 step (f :: Int -> a -> ST s b).
  itraverseStepSmallArray i0 step f =
    itraverseStepSmallArrayP i0 step f
"itraverseStepSmallArray/IO" forall i0 step (f :: Int -> a -> IO b).
  itraverseStepSmallArray i0 step f =
    itraverseStepSmallArrayP i0 step f
"itraverseStepSmallArray/Id" forall i0 step (f :: Int -> a -> Identity b).
  itraverseStepSmallArray i0 step f =
    ( coerce ::
        (SmallArray a -> SmallArray (Identity b)) ->
        SmallArray a ->
        Identity (SmallArray b)
    )
      (imapStepSmallArray i0 step f)
  #-}

-- | This is the fastest, most straightforward way to traverse
-- an array, but it only works correctly with a sufficiently
-- "affine" 'PrimMonad' instance. In particular, it must only produce
-- /one/ result array. 'Control.Monad.Trans.List.ListT'-transformed
-- monads, for example, will not work right at all.
itraverseStepSmallArrayP :: PrimMonad m => Int -> Int -> (Int -> a -> m b) -> SmallArray a -> m (SmallArray b)
itraverseStepSmallArrayP i0 step f = \ !ary -> do
  let len = sizeofSmallArray ary
      go i k marr
        | i == len = unsafeFreezeSmallArray marr
        | otherwise = do
            a <- indexSmallArrayM ary i
            b <- f k a
            writeSmallArray marr i b
            (go (i + 1) $! k + step) marr
  marr <- newSmallArray len undefinedElem
  go 0 i0 marr
{-# INLINE itraverseStepSmallArrayP #-}