
module Data.Repa.Array.Material.Boxed
        ( B      (..)
        , Name   (..)
        , Array  (..)
        , Buffer (..)

        -- * Conversions
        , fromBoxed,    toBoxed

        -- * Utils
        , decimate)
where
import Data.Repa.Array.Meta.Window                      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 Data.Word
import Control.Monad
import qualified Data.Vector                            as V
import qualified Data.Vector.Mutable                    as VM
#include "repa-array.h"


-- | Layout an array as flat vector of boxed elements.
--
--   UNSAFE: Indexing into raw material arrays is not bounds checked.
--   You may want to wrap this with a Checked layout as well.
--
data B = Boxed { boxedLength :: !Int }
  deriving (Show, Eq)

------------------------------------------------------------------------------
-- | Boxed arrays.
instance Layout B where
 data Name  B                   = B
 type Index B                   = Int
 name                           = B
 create B len                   = Boxed len
 extent (Boxed 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 B)
deriving instance Show (Name B)


------------------------------------------------------------------------------
-- | Boxed arrays.
instance Bulk B a where
 data Array B a                  = BArray !(V.Vector a)
 layout (BArray vec)             = Boxed (V.length vec)
 index  (BArray vec) ix          = V.unsafeIndex vec ix
# INLINE_ARRAY layout   #
# INLINE_ARRAY index    #

deriving instance Show a => Show (Array B a)


instance Eq a => Eq (Array B a) where
 (==) (BArray arr1) (BArray arr2) = arr1 == arr2
# INLINE_ARRAY (= =) #


-------------------------------------------------------------------------------
-- | Boxed windows.
instance Windowable B a where
 window st len (BArray vec)
        = BArray (V.unsafeSlice st len vec)
# INLINE_ARRAY window #


-------------------------------------------------------------------------------
-- | Boxed buffers.
instance Target B a where
 data Buffer B a
  = BBuffer !(VM.IOVector a)

 unsafeNewBuffer (Boxed len)
  = liftM BBuffer (VM.unsafeNew len)
 {-# INLINE_ARRAY unsafeNewBuffer #-}

 unsafeReadBuffer (BBuffer mvec) ix
  = VM.unsafeRead mvec ix
# INLINE_ARRAY unsafeReadBuffer #

 unsafeWriteBuffer (BBuffer mvec) ix
  = VM.unsafeWrite mvec ix
# INLINE_ARRAY unsafeWriteBuffer #

 unsafeGrowBuffer (BBuffer mvec) bump
  = liftM BBuffer (VM.unsafeGrow mvec bump)
# INLINE_ARRAY unsafeGrowBuffer #

 unsafeFreezeBuffer (BBuffer mvec)
  = liftM BArray (V.unsafeFreeze mvec)
# INLINE_ARRAY unsafeFreezeBuffer #

 unsafeThawBuffer (BArray vec)
  = liftM BBuffer (V.unsafeThaw vec)
 {-# INLINE_ARRAY unsafeThawBuffer #-}

 unsafeSliceBuffer start len (BBuffer mvec)
  = let mvec'  = VM.unsafeSlice start len mvec
    in  return $ BBuffer mvec'
 {-# INLINE_ARRAY unsafeSliceBuffer #-}

 touchBuffer _
  = return ()
# INLINE_ARRAY touchBuffer #

 bufferLayout (BBuffer mvec)
  = Boxed (VM.length mvec)
 {-# INLINE_ARRAY bufferLayout #-}

# SPECIALIZE instance Target B Int     #
# SPECIALIZE instance Target B Float   #
# SPECIALIZE instance Target B Double #
# SPECIALIZE instance Target B Word8   #
# SPECIALIZE instance Target B Word16 #
# SPECIALIZE instance Target B Word32 #
# SPECIALIZE instance Target B Word64 #


-------------------------------------------------------------------------------
-- | O(1). Wrap a boxed vector as an array.
fromBoxed :: V.Vector a -> Array B a
fromBoxed vec = BArray vec
# INLINE_ARRAY fromBoxed #


-- | O(1). Unwrap a boxed vector from an array.
toBoxed   :: Array B a -> V.Vector a
toBoxed (BArray vec) = vec
# INLINE_ARRAY toBoxed #



-- | Scan through an array from front to back.
    For pairs of successive elements , drop the second one when the given
--   predicate returns true.
--
--   This function can be used to remove duplicates from a sorted array.
--
    TODO : generalise to other array types .
decimate
        :: (a -> a -> Bool)
        -> Array B a -> Array B a

decimate f arr
        | A.length arr == 0        
        = A.fromList B []

        | otherwise
        = fromBoxed
        $ V.cons (arr `A.index` 0)
                 (V.map  snd
                        $ V.filter (\(prev,  here) -> not $ f prev here)
                        $ V.zip (toBoxed arr) (V.tail $ toBoxed arr))


 * Conversions
 * Utils
 | Layout an array as flat vector of boxed elements.

   UNSAFE: Indexing into raw material arrays is not bounds checked.
   You may want to wrap this with a Checked layout as well.

----------------------------------------------------------------------------
 | Boxed arrays.
----------------------------------------------------------------------------
 | Boxed arrays.
-----------------------------------------------------------------------------
 | Boxed windows.
-----------------------------------------------------------------------------
 | Boxed buffers.
# INLINE_ARRAY unsafeNewBuffer #
# INLINE_ARRAY unsafeThawBuffer #
# INLINE_ARRAY unsafeSliceBuffer #
# INLINE_ARRAY bufferLayout #
-----------------------------------------------------------------------------
 | O(1). Wrap a boxed vector as an array.
 | O(1). Unwrap a boxed vector from an array.
 | Scan through an array from front to back.
   predicate returns true.

   This function can be used to remove duplicates from a sorted array.


module Data.Repa.Array.Material.Boxed
        ( B      (..)
        , Name   (..)
        , Array  (..)
        , Buffer (..)

        , fromBoxed,    toBoxed

        , decimate)
where
import Data.Repa.Array.Meta.Window                      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 Data.Word
import Control.Monad
import qualified Data.Vector                            as V
import qualified Data.Vector.Mutable                    as VM
#include "repa-array.h"


data B = Boxed { boxedLength :: !Int }
  deriving (Show, Eq)

instance Layout B where
 data Name  B                   = B
 type Index B                   = Int
 name                           = B
 create B len                   = Boxed len
 extent (Boxed 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 B)
deriving instance Show (Name B)


instance Bulk B a where
 data Array B a                  = BArray !(V.Vector a)
 layout (BArray vec)             = Boxed (V.length vec)
 index  (BArray vec) ix          = V.unsafeIndex vec ix
# INLINE_ARRAY layout   #
# INLINE_ARRAY index    #

deriving instance Show a => Show (Array B a)


instance Eq a => Eq (Array B a) where
 (==) (BArray arr1) (BArray arr2) = arr1 == arr2
# INLINE_ARRAY (= =) #


instance Windowable B a where
 window st len (BArray vec)
        = BArray (V.unsafeSlice st len vec)
# INLINE_ARRAY window #


instance Target B a where
 data Buffer B a
  = BBuffer !(VM.IOVector a)

 unsafeNewBuffer (Boxed len)
  = liftM BBuffer (VM.unsafeNew len)

 unsafeReadBuffer (BBuffer mvec) ix
  = VM.unsafeRead mvec ix
# INLINE_ARRAY unsafeReadBuffer #

 unsafeWriteBuffer (BBuffer mvec) ix
  = VM.unsafeWrite mvec ix
# INLINE_ARRAY unsafeWriteBuffer #

 unsafeGrowBuffer (BBuffer mvec) bump
  = liftM BBuffer (VM.unsafeGrow mvec bump)
# INLINE_ARRAY unsafeGrowBuffer #

 unsafeFreezeBuffer (BBuffer mvec)
  = liftM BArray (V.unsafeFreeze mvec)
# INLINE_ARRAY unsafeFreezeBuffer #

 unsafeThawBuffer (BArray vec)
  = liftM BBuffer (V.unsafeThaw vec)

 unsafeSliceBuffer start len (BBuffer mvec)
  = let mvec'  = VM.unsafeSlice start len mvec
    in  return $ BBuffer mvec'

 touchBuffer _
  = return ()
# INLINE_ARRAY touchBuffer #

 bufferLayout (BBuffer mvec)
  = Boxed (VM.length mvec)

# SPECIALIZE instance Target B Int     #
# SPECIALIZE instance Target B Float   #
# SPECIALIZE instance Target B Double #
# SPECIALIZE instance Target B Word8   #
# SPECIALIZE instance Target B Word16 #
# SPECIALIZE instance Target B Word32 #
# SPECIALIZE instance Target B Word64 #


fromBoxed :: V.Vector a -> Array B a
fromBoxed vec = BArray vec
# INLINE_ARRAY fromBoxed #


toBoxed   :: Array B a -> V.Vector a
toBoxed (BArray vec) = vec
# INLINE_ARRAY toBoxed #



    For pairs of successive elements , drop the second one when the given
    TODO : generalise to other array types .
decimate
        :: (a -> a -> Bool)
        -> Array B a -> Array B a

decimate f arr
        | A.length arr == 0        
        = A.fromList B []

        | otherwise
        = fromBoxed
        $ V.cons (arr `A.index` 0)
                 (V.map  snd
                        $ V.filter (\(prev,  here) -> not $ f prev here)
                        $ V.zip (toBoxed arr) (V.tail $ toBoxed arr))

