    {-# LANGUAGE MagicHash #-}
    module Data.Array.Repa.Repr.Delayed
            ( D, Array(..)
            , fromFunction, toFunction
            , delay)
    where
    import Data.Array.Repa.Eval.Load
    import Data.Array.Repa.Eval.Target
    import Data.Array.Repa.Eval.Chunked
    import Data.Array.Repa.Eval.Cursored
    import Data.Array.Repa.Eval.Elt
    import Data.Array.Repa.Index
    import Data.Array.Repa.Shape
    import Data.Array.Repa.Base
    import Debug.Trace
    import GHC.Exts

    -- | Delayed arrays are represented as functions from the index to element value.
    --
    --   Every time you index into a delayed array the element at that position
    --   is recomputed.
    data D

    -- | Compute elements of a delayed array.
    instance Source D a where
     data Array D sh a
            = ADelayed
                    !sh
                    (sh -> a)

     index       (ADelayed _  f) ix  = f ix
     {-# INLINE index #-}

     linearIndex (ADelayed sh f) ix  = f (fromIndex sh ix)
     {-# INLINE linearIndex #-}

     extent (ADelayed sh _)
            = sh
     {-# INLINE extent #-}

     deepSeqArray (ADelayed sh f) y
            = sh `deepSeq` f `seq` y
     {-# INLINE deepSeqArray #-}

    -- Load -----------------------------------------------------------------------
    -- | Compute all elements in an array.
    instance Shape sh => Load D sh e where
     loadP (ADelayed sh getElem) mvec
      = mvec `deepSeqMVec`
        do  traceEventIO "Repa.loadP[Delayed]: start"
            fillChunkedP (size sh) (unsafeWriteMVec mvec) (getElem . fromIndex sh)
            touchMVec mvec
            traceEventIO "Repa.loadP[Delayed]: end"
     {-# INLINE [4] loadP #-}

     loadS (ADelayed sh getElem) mvec
      = mvec `deepSeqMVec`
        do  traceEventIO "Repa.loadS[Delayed]: start"
            fillLinearS (size sh) (unsafeWriteMVec mvec) (getElem . fromIndex sh)
            touchMVec mvec
            traceEventIO "Repa.loadS[Delayed]: end"
     {-# INLINE [4] loadS #-}

    -- | Compute a range of elements in a rank-2 array.
    instance Elt e => LoadRange D DIM2 e where
     loadRangeP  (ADelayed (Z :. _h :. (I# w)) getElem) mvec
                 (Z :. (I# y0) :. (I# x0)) (Z :. (I# h0) :. (I# w0))
      = mvec `deepSeqMVec`
        do  traceEventIO "Repa.loadRangeP[Delayed]: start"
            fillBlock2P (unsafeWriteMVec mvec)
                            getElem
                            w x0 y0 w0 h0
            touchMVec mvec
            traceEventIO "Repa.loadRangeP[Delayed]: end"
     {-# INLINE [1] loadRangeP #-}

     loadRangeS  (ADelayed (Z :. _h :. (I# w)) getElem) mvec
                 (Z :. (I# y0) :. (I# x0)) (Z :. (I# h0) :. (I# w0))
      = mvec `deepSeqMVec`
        do  traceEventIO "Repa.loadRangeS[Delayed]: start"
            fillBlock2S (unsafeWriteMVec mvec)
                    getElem
                    w x0 y0 w0 h0
            touchMVec mvec
            traceEventIO "Repa.loadRangeS[Delayed]: end"
     {-# INLINE [1] loadRangeS #-}

    -- Conversions ----------------------------------------------------------------
    -- | O(1). Wrap a function as a delayed array.
    fromFunction :: sh -> (sh -> a) -> Array D sh a
    fromFunction sh f
            = ADelayed sh f
    {-# INLINE fromFunction #-}

    -- | O(1). Produce the extent of an array, and a function to retrieve an
    --   arbitrary element.
    toFunction
            :: (Shape sh, Source r1 a)
            => Array r1 sh a -> (sh, sh -> a)
    toFunction arr
     = case delay arr of
            ADelayed sh f -> (sh, f)
    {-# INLINE toFunction #-}

    -- | O(1). Delay an array.
    --   This wraps the internal representation to be a function from
    --   indices to elements, so consumers don't need to worry about
    --   what the previous representation was.
    --
    delay   :: Shape sh => Source r e
            => Array r sh e -> Array D sh e
    delay arr = ADelayed (extent arr) (unsafeIndex arr)
    {-# INLINE delay #-}