packages feed

co-feldspar-0.1.0.1: src/Feldspar/Array/Buffered.hs

{-# language TypeFamilies     #-}
{-# language FlexibleContexts #-}
{-# language RecordWildCards  #-}
{-# language MultiParamTypeClasses #-}
{-# language ScopedTypeVariables #-}

module Feldspar.Array.Buffered where

import Feldspar.Representation
import Feldspar.Frontend
import Feldspar.Array.Vector

import Prelude hiding (length)
import qualified Prelude as P

--------------------------------------------------------------------------------
-- *
--------------------------------------------------------------------------------

-- | Double-buffered storage.
data Store m a = Store
    { activeBuf :: Array m a
    , freeBuf   :: Array m a
    }

--------------------------------------------------------------------------------

class ArraysEq vec1 vec2
  where
    unsafeArrEq :: vec1 a -> vec2 a -> Bool

class Arrays m => ArraysSwap m
  where
    unsafeArrSwap :: Array m a -> Array m a -> m ()

--------------------------------------------------------------------------------

-- | Create a new double-buffered 'Store', initialized to a 0x0 matrix.
newStore :: (SyntaxM m a, MonadComp m) => Expr m Length -> m (Store m a)
newStore l = Store <$> newArr l <*> newArr l

-- | Create a new 'Store' from a single array.
newInPlaceStore
  :: ( SyntaxM m a
     , MonadComp m
     , Finite (Expr m) (IArray m a)
     , Finite (Expr m) (Array m a)
     )
  => Expr m Length -> m (Store m a)
newInPlaceStore l = do
  arr <- newArr l
  brr <- unsafeFreezeArr arr >>= unsafeThawArr
  return (Store arr brr)

-- | Read the contents of a 'Store' without making a copy. This is generally
--   only safe if the the 'Store' is not updated as long as the resulting vector
--   is alive.
unsafeFreezeStore
  :: ( SyntaxM m a
     , MonadComp m
     , Finite   (Expr m) (Array  m a)
     , Slicable (Expr m) (IArray m a)
     , Num (Expr m Index)
     )
  => Expr m Length -> Store m a -> m (Manifest m a)
unsafeFreezeStore l st = M <$> unsafeFreezeSlice l (activeBuf st)

-- | Cheap swapping of the two buffers in a 'Store'.
swapStore
  :: ( SyntaxM    m a
     -- hmm..
     , ArraysSwap m
     )
  => Store m a -> m ()
swapStore Store {..} = unsafeArrSwap activeBuf freeBuf

replaceFreeStore :: (SyntaxM m a, Arrays m) => Expr m Length -> Store m a -> m (Store m a)
replaceFreeStore l Store {..} = Store activeBuf <$> newArr l

-- | Write a vector to a 'Store'. The operation may become a no-op
--   if the vector is already in the 'Store'.
setStore
  :: forall m a vec .
     ( Manifestable m vec a
     , Finite (Expr m) vec
     , SyntaxM m a
     --
     , ArraysSwap m
     , ArraysEq (Array m) (IArray m)
     )
  => Store m a -> vec -> m ()
setStore st@Store {..} vec = case viewManifest vec of
    Just (M iarr :: Manifest m a) | unsafeArrEq activeBuf iarr
      -> return ()
    _ -> manifestStore freeBuf vec >> swapStore st
-- todo: Should check for offset and length differences as well.

-- | Write the contents of a vector to a 'Store' and get it back as a
--   'Manifest' vector.
store
  :: ( Manifestable m vec a
     , Finite (Expr m) vec
     , SyntaxM m a
     , MonadComp m
     --
     , Finite   (Expr m) (Array  m a)
     , Slicable (Expr m) (IArray m a)
     , Num (Expr m Index)
     --
     , ArraysSwap m
     , ArraysEq (Array m) (IArray m)
     )
  => Store m a -> vec -> m (Manifest m a)
store st vec = setStore st vec >> unsafeFreezeStore (length vec) st

loopStore
  :: ( Manifestable m vec1 a
     , Finite (Expr m) vec1
     , Manifestable m vec2 a
     , Finite (Expr m) vec2
     , SyntaxM m a
     , MonadComp m
     , Loop m
     --
     , Integral i
     , SyntaxM' m (Expr m i)
     , SyntaxM' m (Expr m Length)
     --
     , Finite   (Expr m) (Array  m a)
     , Slicable (Expr m) (IArray m a)
     , Num (Expr m Index)
     --
     , ArraysSwap m
     , ArraysEq (Array m) (IArray m)
     )
  => Store m a
  -> Expr m i  -- ^ Lower bound.
  -> Int       -- ^ Step.
  -> Expr m i  -- ^ Upper bound.
  -> (Expr m i -> Manifest m a -> m vec1)
  -> vec2
  -> m (Manifest m a)
loopStore store low step high body init = do
  setStore store init
  ilen <- initRef $ length init
  for low step high $ \ix -> do
    len  <- unsafeFreezeRef ilen
    next <- body ix =<< unsafeFreezeStore len store
    setStore store next
    setRef ilen $ length next
  len <- unsafeFreezeRef ilen
  unsafeFreezeStore len store

--------------------------------------------------------------------------------