{-# LANGUAGE UnboxedTuples, MagicHash #-}
{- |
   Module     : GHC.ArrBZ
   Copyright  : Copyright (C) 2006 Bulat Ziganshin
   License    : BSD3

   Maintainer : Bulat Ziganshin <Bulat.Ziganshin@gmail.com>
   Stability  : experimental
   Portability: GHC

Vectors of boxed values

-}

module GHC.ArrBZ where

import GHC.Base

import Control.Monad.STorIO

-- ---------------------------------------------------------------------------
-- | Immutable and mutable vectors of boxed values

data  Vec   a  =   Vec (Array# a)
data MVec s a  =  MVec (MutableArray# s a)

-- | Alloc the mutable vector
allocBoxed :: (STorIO m s, Integral elems) => elems -> a -> m (MVec s a)
-- | Mutable->immutable vector on-place conversion
unsafeFreezeBoxed :: (STorIO m s) => MVec s a -> m (Vec a)
-- | Immutable->mutable vector on-place conversion
unsafeThawBoxed :: (STorIO m s) => Vec a -> m (MVec s a)
-- | Mutable->immutable vector conversion which takes a copy of contents
freezeBoxed :: (STorIO m s) => MVec s a -> Int -> a -> m (Vec a)
-- | Immutable->mutable vector conversion which takes a copy of contents
thawBoxed :: (STorIO m s) => Vec a -> Int -> a -> m (MVec s a)


allocBoxed elems initial = mLift ( \s ->
  case newArray# (fromI# elems) initial s of
    (# q, arr #) -> (# q, MVec arr #) )

{-# INLINE unsafeFreezeBoxed #-}
unsafeFreezeBoxed (MVec mvec) = mLift ( \s ->
  case unsafeFreezeArray# mvec s of
    (# q, vec #) -> (# q, Vec vec #) )

{-# INLINE unsafeThawBoxed #-}
unsafeThawBoxed (Vec arr#) = mLift ( \s ->
  case unsafeThawArray# arr# s of
    (# q, marr# #) -> (# q, MVec marr# #))

freezeBoxed (MVec marr#) (I# n#) initial = mLift ( \s1# ->
    case newArray# n# initial                s1#  of { (# s2#, tmparr# #) ->

    let copy i# s01# | i# ==# n# =        s01#
                     | otherwise =
            case readArray#  marr#   i#   s01# of { (# s02#, e #) ->
            case writeArray# tmparr# i# e s02# of { s03# ->
            copy (i# +# 1#)               s03# }} in

    case copy 0#                          s2#  of { s3# ->
    case unsafeFreezeArray# tmparr#       s3#  of { (# s4#, arr# #) ->
    (# s4#, Vec arr# #) }}} )

thawBoxed (Vec vec#) (I# n#) initial = mLift ( \s1# ->
    case newArray# n# initial s1#               of { (# s2#, mvec# #) ->

    let copy i# s01# | i# ==# n# =      s01#
                     | otherwise =
            case indexArray# vec#  i#        of { (# e #) ->
            case writeArray# mvec# i# e s01# of { s02# ->
            copy (i# +# 1#)             s02# }} in

    case copy 0#                        s2#  of { s3# ->
    (# s3#, MVec mvec# #) }} )

-- Implementation helper function that converts any integral value to the Int#
{-# INLINE fromI# #-}
fromI# :: (Integral n) => n -> Int#
fromI# n = n#   where I# n# = fromIntegral n

-- ---------------------------------------------------------------------------
-- Read/write/index vector elements

-- | Read the value from mutable vector at given `index`
readBoxed    :: (STorIO m s, Integral index) => MVec s value -> index -> m value
-- | Write the value to mutable vector at given `index`
writeBoxed   :: (STorIO m s, Integral index) => MVec s value -> index -> value -> m ()
-- | Read the value from immutable vector at given `index`
indexBoxed   :: (Integral index) => Vec value -> index -> value


{-# INLINE readBoxed #-}
readBoxed (MVec vec) index  =  mLift (readArray# vec (fromI# index))

{-# INLINE writeBoxed #-}
writeBoxed (MVec vec) index value  =  mLift ( \s ->
    case writeArray# vec (fromI# index) value s of
      q -> (# q, () #))

{-# INLINE indexBoxed #-}
indexBoxed (Vec vec) index  =
    case indexArray# vec (fromI# index) of { (# s #) -> s }