    {-# LANGUAGE UndecidableInstances #-}

    -- | Low level interface to parallel array filling operators.
    module Data.Array.Repa.Eval
            ( -- * Element types
              Elt       (..)

            -- * Parallel array filling
            , Target    (..)
            , Load      (..)
            , LoadRange (..)
            , fromList

            -- * Converting between representations
            , computeS, computeP, suspendedComputeP
            , copyS,    copyP,    suspendedCopyP
            , now

            -- * Chunked filling
            , fillLinearS
            , fillChunkedP
            , fillChunkedIOP

            -- * Interleaved filling
            , fillInterleavedP

            -- * Blockwise filling
            , fillBlock2P
            , fillBlock2S

            -- * Cursored blockwise filling
            , fillCursoredBlock2S
            , fillCursoredBlock2P

            -- * Chunked selection
            , selectChunkedS
            , selectChunkedP)
    where
    import Data.Array.Repa.Eval.Elt
    import Data.Array.Repa.Eval.Target
    import Data.Array.Repa.Eval.Load
    import Data.Array.Repa.Eval.Chunked
    import Data.Array.Repa.Eval.Interleaved
    import Data.Array.Repa.Eval.Cursored
    import Data.Array.Repa.Eval.Selection
    import Data.Array.Repa.Repr.Delayed
    import Data.Array.Repa.Base
    import Data.Array.Repa.Shape
    import System.IO.Unsafe

    -- | Parallel computation of array elements.
    --
    --   * The source array must have a delayed representation like `D`, `C` or `P`,
    --     and the result a manifest representation like `U` or `F`.
    --
    --   * If you want to copy data between manifest representations then use
    --    `copyP` instead.
    --
    --   * If you want to convert a manifest array back to a delayed representation
    --     then use `delay` instead.
    --
    computeP
            :: ( Load r1 sh e
               , Target r2 e, Source r2 e, Monad m)
            => Array r1 sh e -> m (Array r2 sh e)
    computeP arr = now $ suspendedComputeP arr
    {-# INLINE [4] computeP #-}

    -- | Sequential computation of array elements.
    computeS
            :: (Load r1 sh e, Target r2 e)
            => Array r1 sh e -> Array r2 sh e
    computeS arr1
     = arr1 `deepSeqArray`
       unsafePerformIO
     $ do   mvec2   <- newMVec (size $ extent arr1)
            loadS arr1 mvec2
            unsafeFreezeMVec (extent arr1) mvec2
    {-# INLINE [4] computeS #-}

    -- | Suspended parallel computation of array elements.
    --
    --   This version creates a thunk that will evaluate the array on demand.
    --   If you force it when another parallel computation is already running
    --   then you  will get a runtime warning and evaluation will be sequential.
    --   Use `deepSeqArray` and `now` to ensure that each array is evaluated
    --   before proceeding to the next one.
    --
    --   If unsure then just use the monadic version `computeP`. This one ensures
    --   that each array is fully evaluated before continuing.
    --
    suspendedComputeP
            :: (Load r1 sh e, Target r2 e)
            => Array r1 sh e -> Array r2 sh e
    suspendedComputeP arr1
     = arr1 `deepSeqArray`
       unsafePerformIO
     $ do   mvec2    <- newMVec (size $ extent arr1)
            loadP arr1 mvec2
            unsafeFreezeMVec (extent arr1) mvec2
    {-# INLINE [4] suspendedComputeP #-}

    -- | Parallel copying of arrays.
    --
    --   * This is a wrapper that delays an array before calling `computeP`.
    --
    --   * You can use it to copy manifest arrays between representations.
    --
    copyP  :: ( Source r1 e, Source r2 e
              , Load D sh e, Target r2 e
              , Monad m)
            => Array r1 sh e -> m (Array r2 sh e)
    copyP arr = now $ suspendedCopyP arr
    {-# INLINE [4] copyP #-}

    -- | Sequential copying of arrays.
    copyS   :: ( Source r1 e
               , Load D sh e, Target r2 e)
            => Array r1 sh e -> Array r2 sh e
    copyS arr1  = computeS $ delay arr1
    {-# INLINE [4] copyS #-}

    -- | Suspended parallel copy of array elements.
    suspendedCopyP
            :: ( Source r1 e
               , Load D sh e, Target r2 e)
            => Array r1 sh e -> Array r2 sh e
    suspendedCopyP arr1  = suspendedComputeP $ delay arr1
    {-# INLINE [4] suspendedCopyP #-}

    -- | Monadic version of `deepSeqArray`.
    --
    --   Forces an suspended array computation to be completed at this point
    --   in a monadic computation.
    --
    -- @ do  let arr2 = suspendedComputeP arr1
    --     ...
    --     arr3 <- now $ arr2
    --     ...
    -- @
    --
    now     :: (Shape sh, Source r e, Monad m)
            => Array r sh e -> m (Array r sh e)
    now arr
     = do   arr `deepSeqArray` return ()
            return arr
    {-# INLINE [4] now #-}