module Data.Repa.Array.Material.Unboxed
        ( U      (..)
        , Name   (..)
        , Array  (..)
        , Buffer (..)
        , U.Unbox

        -- * Conversions
        , fromUnboxed,  toUnboxed)

where
import Data.Repa.Array.Meta.Window                      as A
import Data.Repa.Array.Meta.Delayed                     as A
import Data.Repa.Array.Generic.Index                    as A
import Data.Repa.Array.Internals.Bulk                   as A
import Data.Repa.Array.Internals.Target                 as A
import Control.Monad
import Data.Word
import qualified Data.Vector.Unboxed                    as U
import qualified Data.Vector.Unboxed.Mutable            as UM
#include "repa-array.h"


-- | Layout an array as a flat vector of unboxed elements.
--
--   This is the most efficient representation for numerical data.
--
--   The implementation uses @Data.Vector.Unboxed@ which picks an efficient,
--   specialised representation for every element type. In particular,
--   unboxed vectors of pairs are represented as pairs of unboxed vectors.
--
--   UNSAFE: Indexing into raw material arrays is not bounds checked.
--   You may want to wrap this with a Checked layout as well.
--
data U = Unboxed { unboxedLength :: !Int }
  deriving (Show, Eq)


---------------------------------------------------------------------------------------------------
-- | Unboxed arrays.
instance Layout U where
 data Name  U                   = U
 type Index U                   = Int
 name                           = U
 create U len                   = Unboxed len
 extent (Unboxed len)           = len
 toIndex   _ ix                 = ix
 fromIndex _ ix                 = ix
 {-# INLINE_ARRAY name      #-}
 {-# INLINE_ARRAY create    #-}
 {-# INLINE_ARRAY extent    #-}
 {-# INLINE_ARRAY toIndex   #-}
 {-# INLINE_ARRAY fromIndex #-}

deriving instance Eq   (Name U)
deriving instance Show (Name U)


---------------------------------------------------------------------------------------------------
-- | Unboxed arrays.
instance U.Unbox a => Bulk U a where
 data Array U a                 = UArray !(U.Vector a)
 layout (UArray vec)            = Unboxed (U.length vec)
 index  (UArray vec) ix         = U.unsafeIndex vec ix
 {-# INLINE_ARRAY layout #-}
 {-# INLINE_ARRAY index  #-}
 {-# SPECIALIZE instance Bulk U ()      #-}
 {-# SPECIALIZE instance Bulk U Bool    #-}
 {-# SPECIALIZE instance Bulk U Char    #-}
 {-# SPECIALIZE instance Bulk U Int     #-}
 {-# SPECIALIZE instance Bulk U Float   #-}
 {-# SPECIALIZE instance Bulk U Double  #-}
 {-# SPECIALIZE instance Bulk U Word8   #-}
 {-# SPECIALIZE instance Bulk U Word16  #-}
 {-# SPECIALIZE instance Bulk U Word32  #-}
 {-# SPECIALIZE instance Bulk U Word64  #-}

deriving instance (Show a, U.Unbox a) => Show (Array U a)


instance (UM.Unbox a, Eq a) => Eq (Array U a) where
 (==) (UArray arr1) (UArray arr2) = arr1 == arr2
 {-# INLINE_ARRAY (==) #-}


---------------------------------------------------------------------------------------------------
-- | Windowing Unboxed arrays.
instance U.Unbox a => Windowable U a where
 window st len (UArray vec)
        = UArray (U.unsafeSlice st len vec)
 {-# INLINE_ARRAY window #-}
 {-# SPECIALIZE instance Windowable U Int     #-}
 {-# SPECIALIZE instance Windowable U Float   #-}
 {-# SPECIALIZE instance Windowable U Double  #-}
 {-# SPECIALIZE instance Windowable U Word8   #-}
 {-# SPECIALIZE instance Windowable U Word16  #-}
 {-# SPECIALIZE instance Windowable U Word32  #-}
 {-# SPECIALIZE instance Windowable U Word64  #-}


---------------------------------------------------------------------------------------------------
-- | Unboxed buffers.
instance U.Unbox a => Target U a where
 data Buffer U a
  = UBuffer !(UM.IOVector a)

 unsafeNewBuffer (Unboxed len)
  = liftM UBuffer (UM.unsafeNew len)
 {-# INLINE_ARRAY unsafeNewBuffer #-}

 unsafeReadBuffer (UBuffer mvec) ix
  = UM.unsafeRead mvec ix
 {-# INLINE_ARRAY unsafeReadBuffer #-}

 unsafeWriteBuffer (UBuffer mvec) ix
  = UM.unsafeWrite mvec ix
 {-# INLINE_ARRAY unsafeWriteBuffer #-}

 unsafeGrowBuffer (UBuffer mvec) bump
  = do  mvec'   <- UM.unsafeGrow mvec bump
        return  $ UBuffer mvec'
 {-# INLINE_ARRAY unsafeGrowBuffer #-}

 unsafeFreezeBuffer (UBuffer mvec)
  = do  vec     <- U.unsafeFreeze mvec
        return  $  UArray vec
 {-# INLINE_ARRAY unsafeFreezeBuffer #-}

 unsafeThawBuffer (UArray mvec)
  = liftM UBuffer (U.unsafeThaw mvec)
 {-# INLINE_ARRAY unsafeThawBuffer #-}

 unsafeSliceBuffer st len (UBuffer mvec)
  = do  let mvec'  = UM.unsafeSlice st len mvec
        return $ UBuffer mvec'
 {-# INLINE_ARRAY unsafeSliceBuffer #-}

 touchBuffer _
  = return ()
 {-# INLINE_ARRAY touchBuffer #-}

 bufferLayout (UBuffer mvec)
   = Unboxed (UM.length mvec)

 {-# SPECIALIZE instance Target U Int    #-}
 {-# SPECIALIZE instance Target U Float  #-}
 {-# SPECIALIZE instance Target U Double #-}
 {-# SPECIALIZE instance Target U Word8  #-}
 {-# SPECIALIZE instance Target U Word16 #-}
 {-# SPECIALIZE instance Target U Word32 #-}
 {-# SPECIALIZE instance Target U Word64 #-}


---------------------------------------------------------------------------------------------------
-- | O(1). Wrap an unboxed vector as an array.
fromUnboxed :: U.Vector a -> Array U a
fromUnboxed vec = UArray vec
{-# INLINE_ARRAY fromUnboxed #-}


-- | O(1). Unwrap an unboxed vector from an array.
toUnboxed   :: Array U a -> U.Vector a
toUnboxed (UArray vec) = vec
{-# INLINE_ARRAY toUnboxed #-}