diff --git a/Foreign/CUDA.hs b/Foreign/CUDA.hs
new file mode 100644
--- /dev/null
+++ b/Foreign/CUDA.hs
@@ -0,0 +1,19 @@
+--------------------------------------------------------------------------------
+-- |
+-- Module    : Foreign.CUDA
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Top level bindings. By default, expose the C-for-CUDA runtime API bindings,
+-- as they are slightly more user friendly.
+--
+--------------------------------------------------------------------------------
+
+module Foreign.CUDA
+  (
+    module Foreign.CUDA.Runtime
+  )
+  where
+
+import Foreign.CUDA.Runtime
+
diff --git a/Foreign/CUDA/Driver.hs b/Foreign/CUDA/Driver.hs
new file mode 100644
--- /dev/null
+++ b/Foreign/CUDA/Driver.hs
@@ -0,0 +1,30 @@
+--------------------------------------------------------------------------------
+-- |
+-- Module    : Foreign.CUDA.Driver
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Top level bindings to CUDA driver API
+--
+--------------------------------------------------------------------------------
+
+module Foreign.CUDA.Driver
+  (
+    module Foreign.CUDA.Driver.Context,
+    module Foreign.CUDA.Driver.Device,
+    module Foreign.CUDA.Driver.Error,
+    module Foreign.CUDA.Driver.Exec,
+    module Foreign.CUDA.Driver.Marshal,
+    module Foreign.CUDA.Driver.Module,
+    module Foreign.CUDA.Driver.Utils
+  )
+  where
+
+import Foreign.CUDA.Driver.Context
+import Foreign.CUDA.Driver.Device
+import Foreign.CUDA.Driver.Error
+import Foreign.CUDA.Driver.Exec
+import Foreign.CUDA.Driver.Marshal
+import Foreign.CUDA.Driver.Module
+import Foreign.CUDA.Driver.Utils
+
diff --git a/Foreign/CUDA/Driver/Context.chs b/Foreign/CUDA/Driver/Context.chs
new file mode 100644
--- /dev/null
+++ b/Foreign/CUDA/Driver/Context.chs
@@ -0,0 +1,145 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+--------------------------------------------------------------------------------
+-- |
+-- Module    : Foreign.CUDA.Driver.Context
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Context management for low-level driver interface
+--
+--------------------------------------------------------------------------------
+
+module Foreign.CUDA.Driver.Context
+  (
+    Context, ContextFlag(..),
+    create, attach, detach, destroy, current, pop, push, sync
+  )
+  where
+
+#include <cuda.h>
+{# context lib="cuda" #}
+
+-- Friends
+import Foreign.CUDA.Driver.Device
+import Foreign.CUDA.Driver.Error
+import Foreign.CUDA.Internal.C2HS
+
+-- System
+import Foreign
+import Foreign.C
+import Control.Monad                    (liftM)
+
+
+--------------------------------------------------------------------------------
+-- Data Types
+--------------------------------------------------------------------------------
+
+-- |
+-- A device context
+--
+newtype Context = Context { useContext :: {# type CUcontext #}}
+
+
+-- |
+-- Context creation flags
+--
+{# enum CUctx_flags as ContextFlag
+    { underscoreToCase }
+    with prefix="CU_CTX" deriving (Eq, Show) #}
+
+
+--------------------------------------------------------------------------------
+-- Context management
+--------------------------------------------------------------------------------
+
+-- |
+-- Create a new CUDA context and associate it with the calling thread
+--
+create :: Device -> [ContextFlag] -> IO Context
+create dev flags = resultIfOk =<< cuCtxCreate flags dev
+
+{# fun unsafe cuCtxCreate
+  { alloca-         `Context'       peekCtx*
+  , combineBitMasks `[ContextFlag]'
+  , useDevice       `Device'                 } -> `Status' cToEnum #}
+  where peekCtx = liftM Context . peek
+
+
+-- |
+-- Increments the usage count of the context. API: no context flags are
+-- currently supported, so this parameter must be empty.
+--
+attach :: Context -> [ContextFlag] -> IO ()
+attach ctx flags = nothingIfOk =<< cuCtxAttach ctx flags
+
+{# fun unsafe cuCtxAttach
+  { withCtx*        `Context'
+  , combineBitMasks `[ContextFlag]' } -> `Status' cToEnum #}
+  where withCtx = with . useContext
+
+
+-- |
+-- Detach the context, and destroy if no longer used
+--
+detach :: Context -> IO ()
+detach ctx = nothingIfOk =<< cuCtxDetach ctx
+
+{# fun unsafe cuCtxDetach
+  { useContext `Context' } -> `Status' cToEnum #}
+
+
+-- |
+-- Destroy the specified context. This fails if the context is more than a
+-- single attachment (including that from initial creation).
+--
+destroy :: Context -> IO ()
+destroy ctx = nothingIfOk =<< cuCtxDestroy ctx
+
+{# fun unsafe cuCtxDestroy
+  { useContext `Context' } -> `Status' cToEnum #}
+
+
+-- |
+-- Return the device of the currently active context
+--
+current :: IO Device
+current = resultIfOk =<< cuCtxGetDevice
+
+{# fun unsafe cuCtxGetDevice
+  { alloca- `Device' dev* } -> `Status' cToEnum #}
+  where dev = liftM Device . peekIntConv
+
+
+-- |
+-- Pop the current CUDA context from the CPU thread. The context must have a
+-- single usage count (matching calls to attach/detach). If successful, the new
+-- context is returned, and the old may be attached to a different CPU.
+--
+pop :: IO Context
+pop = resultIfOk =<< cuCtxPopCurrent
+
+{# fun unsafe cuCtxPopCurrent
+  { alloca- `Context' peekCtx* } -> `Status' cToEnum #}
+  where peekCtx = liftM Context . peek
+
+
+-- |
+-- Push the given context onto the CPU's thread stack of current contexts. The
+-- context must be floating (via `pop'), i.e. not attached to any thread.
+--
+push :: Context -> IO ()
+push ctx = nothingIfOk =<< cuCtxPushCurrent ctx
+
+{# fun unsafe cuCtxPushCurrent
+  { useContext `Context' } -> `Status' cToEnum #}
+
+
+-- |
+-- Block until the device has completed all preceding requests
+--
+sync :: IO ()
+sync = nothingIfOk =<< cuCtxSynchronize
+
+{# fun unsafe cuCtxSynchronize
+  { } -> `Status' cToEnum #}
+
diff --git a/Foreign/CUDA/Driver/Device.chs b/Foreign/CUDA/Driver/Device.chs
new file mode 100644
--- /dev/null
+++ b/Foreign/CUDA/Driver/Device.chs
@@ -0,0 +1,215 @@
+{-# LANGUAGE ForeignFunctionInterface, EmptyDataDecls #-}
+--------------------------------------------------------------------------------
+-- |
+-- Module    : Foreign.CUDA.Driver.Device
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Device management for low-level driver interface
+--
+--------------------------------------------------------------------------------
+
+module Foreign.CUDA.Driver.Device
+  (
+    Device(..), -- should be exported abstractly
+    DeviceProperties(..), DeviceAttribute(..), InitFlag,
+
+    initialise, capability, device, attribute, count, name, props, totalMem
+  )
+  where
+
+#include <cuda.h>
+{# context lib="cuda" #}
+
+-- Friends
+import Foreign.CUDA.Driver.Error
+import Foreign.CUDA.Internal.C2HS
+import Foreign.CUDA.Internal.Offsets
+
+-- System
+import Foreign
+import Foreign.C
+import Control.Monad            (liftM)
+
+
+--------------------------------------------------------------------------------
+-- Data Types
+--------------------------------------------------------------------------------
+
+newtype Device = Device { useDevice :: {# type CUdevice #}}
+
+
+-- |
+-- Device attributes
+--
+{# enum CUdevice_attribute as DeviceAttribute
+    { underscoreToCase }
+    with prefix="CU_DEVICE_ATTRIBUTE" deriving (Eq, Show) #}
+
+{# pointer *CUdevprop as ^ foreign -> DeviceProperties nocode #}
+
+
+-- |
+-- Properties of the compute device
+--
+data DeviceProperties = DeviceProperties
+  {
+    maxThreadsPerBlock  :: Int,           -- ^ Maximum number of threads per block
+    maxThreadsDim       :: (Int,Int,Int), -- ^ Maximum size of each dimension of a block
+    maxGridSize         :: (Int,Int,Int), -- ^ Maximum size of each dimension of a grid
+    sharedMemPerBlock   :: Int,           -- ^ Shared memory available per block in bytes
+    totalConstantMemory :: Int,           -- ^ Constant memory available on device in bytes
+    warpSize            :: Int,           -- ^ Warp size in threads (SIMD width)
+    memPitch            :: Int,           -- ^ Maximum pitch in bytes allowed by memory copies
+    regsPerBlock        :: Int,           -- ^ 32-bit registers available per block
+    clockRate           :: Int,           -- ^ Clock frequency in kilohertz
+    textureAlign        :: Int            -- ^ Alignment requirement for textures
+  }
+  deriving (Show)
+
+instance Storable DeviceProperties where
+  sizeOf _    = {#sizeof CUdevprop#}
+  alignment _ = alignment (undefined :: Ptr ())
+
+  peek p      = do
+    tb <- cIntConv `fmap` {#get CUdevprop.maxThreadsPerBlock#} p
+    sm <- cIntConv `fmap` {#get CUdevprop.sharedMemPerBlock#} p
+    cm <- cIntConv `fmap` {#get CUdevprop.totalConstantMemory#} p
+    ws <- cIntConv `fmap` {#get CUdevprop.SIMDWidth#} p
+    mp <- cIntConv `fmap` {#get CUdevprop.memPitch#} p
+    rb <- cIntConv `fmap` {#get CUdevprop.regsPerBlock#} p
+    cl <- cIntConv `fmap` {#get CUdevprop.clockRate#} p
+    ta <- cIntConv `fmap` {#get CUdevprop.textureAlign#} p
+
+    (t1:t2:t3:_) <- map cIntConv `fmap` peekArray 3 (p `plusPtr` devMaxThreadDimOffset' :: Ptr CInt)
+    (g1:g2:g3:_) <- map cIntConv `fmap` peekArray 3 (p `plusPtr` devMaxGridSizeOffset'  :: Ptr CInt)
+
+    return DeviceProperties
+      {
+        maxThreadsPerBlock  = tb,
+        maxThreadsDim       = (t1,t2,t3),
+        maxGridSize         = (g1,g2,g3),
+        sharedMemPerBlock   = sm,
+        totalConstantMemory = cm,
+        warpSize            = ws,
+        memPitch            = mp,
+        regsPerBlock        = rb,
+        clockRate           = cl,
+        textureAlign        = ta
+      }
+
+-- |
+-- Possible option flags for CUDA initialisation. Dummy instance until the API
+-- exports actual option values.
+--
+data InitFlag
+
+instance Enum InitFlag where
+
+
+--------------------------------------------------------------------------------
+-- Initialisation
+--------------------------------------------------------------------------------
+
+-- |
+-- Initialise the CUDA driver API. Must be called before any other driver
+-- function.
+--
+initialise :: [InitFlag] -> IO ()
+initialise flags = nothingIfOk =<< cuInit flags
+
+{# fun unsafe cuInit
+  { combineBitMasks `[InitFlag]' } -> `Status' cToEnum #}
+
+
+--------------------------------------------------------------------------------
+-- Device Management
+--------------------------------------------------------------------------------
+
+-- |
+-- Return the compute compatibility revision supported by the device
+--
+capability :: Device -> IO Double
+capability dev =
+  (\(s,a,b) -> resultIfOk (s,cap a b)) =<< cuDeviceComputeCapability dev
+  where
+    cap a b = let a' = fromIntegral a in
+              let b' = fromIntegral b in
+              a' + b' / max 10 (10^ ((ceiling . logBase 10) b' :: Int))
+
+{# fun unsafe cuDeviceComputeCapability
+  { alloca-   `Int'    peekIntConv*
+  , alloca-   `Int'    peekIntConv*
+  , useDevice `Device'              } -> `Status' cToEnum #}
+
+
+-- |
+-- Return a device handle
+--
+device :: Int -> IO Device
+device d = resultIfOk =<< cuDeviceGet d
+
+{# fun unsafe cuDeviceGet
+  { alloca-  `Device' dev*
+  , cIntConv `Int'           } -> `Status' cToEnum #}
+  where dev = liftM Device . peek
+
+
+-- |
+-- Return the selected attribute for the given device
+--
+attribute :: Device -> DeviceAttribute -> IO Int
+attribute d a = resultIfOk =<< cuDeviceGetAttribute a d
+
+{# fun unsafe cuDeviceGetAttribute
+  { alloca-   `Int'             peekIntConv*
+  , cFromEnum `DeviceAttribute'
+  , useDevice `Device'                       } -> `Status' cToEnum #}
+
+
+-- |
+-- Return the number of device with compute capability > 1.0
+--
+count :: IO Int
+count = resultIfOk =<< cuDeviceGetCount
+
+{# fun unsafe cuDeviceGetCount
+  { alloca- `Int' peekIntConv* } -> `Status' cToEnum #}
+
+
+-- |
+-- Name of the device
+--
+name :: Device -> IO String
+name d = resultIfOk =<< cuDeviceGetName d
+
+{# fun unsafe cuDeviceGetName
+  { allocaS-  `String'& peekS*
+  , useDevice `Device'         } -> `Status' cToEnum #}
+  where
+    len            = 512
+    allocaS a      = allocaBytes len $ \p -> a (p, cIntConv len)
+    peekS s _      = peekCString s
+
+
+-- |
+-- Return the properties of the selected device
+--
+props :: Device -> IO DeviceProperties
+props d = resultIfOk =<< cuDeviceGetProperties d
+
+{# fun unsafe cuDeviceGetProperties
+  { alloca-   `DeviceProperties' peek*
+  , useDevice `Device'                 } -> `Status' cToEnum #}
+
+
+-- |
+-- Total memory available on the device (bytes)
+--
+totalMem :: Device -> IO Int
+totalMem d = resultIfOk =<< cuDeviceTotalMem d
+
+{# fun unsafe cuDeviceTotalMem
+  { alloca-   `Int' peekIntConv*
+  , useDevice `Device'           } -> `Status' cToEnum #}
+
diff --git a/Foreign/CUDA/Driver/Error.chs b/Foreign/CUDA/Driver/Error.chs
new file mode 100644
--- /dev/null
+++ b/Foreign/CUDA/Driver/Error.chs
@@ -0,0 +1,129 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+--------------------------------------------------------------------------------
+-- |
+-- Module    : Foreign.CUDA.Driver.Error
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Error handling
+--
+--------------------------------------------------------------------------------
+
+module Foreign.CUDA.Driver.Error
+  where
+
+
+-- System
+import Data.Typeable
+import Control.Exception.Extensible
+
+#include <cuda.h>
+{# context lib="cuda" #}
+
+
+--------------------------------------------------------------------------------
+-- Return Status
+--------------------------------------------------------------------------------
+
+--
+-- Error Codes
+--
+{# enum CUresult as Status
+    { underscoreToCase
+    , CUDA_SUCCESS as Success
+    , CUDA_ERROR_NO_BINARY_FOR_GPU as NoBinaryForGPU }
+    with prefix="CUDA_ERROR" deriving (Eq, Show) #}
+
+
+-- |
+-- Return a descriptive error string associated with a particular error code
+--
+describe :: Status -> String
+describe Success                     = "no error"
+describe InvalidValue                = "invalid argument"
+describe OutOfMemory                 = "out of memory"
+describe NotInitialized              = "driver not initialised"
+describe Deinitialized               = "driver deinitialised"
+describe NoDevice                    = "no CUDA-capable device is available"
+describe InvalidDevice               = "invalid device ordinal"
+describe InvalidImage                = "invalid kernel image"
+describe InvalidContext              = "invalid context handle"
+describe ContextAlreadyCurrent       = "context already current"
+describe MapFailed                   = "map failed"
+describe UnmapFailed                 = "unmap failed"
+describe ArrayIsMapped               = "array is mapped"
+describe AlreadyMapped               = "already mapped"
+describe NoBinaryForGPU              = "no binary available for this GPU"
+describe AlreadyAcquired             = "resource already acquired"
+describe NotMapped                   = "not mapped"
+describe InvalidSource               = "invalid source"
+describe FileNotFound                = "file not found"
+describe InvalidHandle               = "invalid handle"
+describe NotFound                    = "not found"
+describe NotReady                    = "device not ready"
+describe LaunchFailed                = "unspecified launch failure"
+describe LaunchOutOfResources        = "too many resources requested for launch"
+describe LaunchTimeout               = "the launch timed out and was terminated"
+describe LaunchIncompatibleTexturing = "launch with incompatible texturing"
+describe Unknown                     = "unknown error"
+
+
+--------------------------------------------------------------------------------
+-- Exceptions
+--------------------------------------------------------------------------------
+
+data CUDAException
+  = ExitCode Status
+  | UserError String
+  deriving Typeable
+
+instance Exception CUDAException
+
+instance Show CUDAException where
+  showsPrec _ (ExitCode  s) = showString ("CUDA Exception: " ++ describe s)
+  showsPrec _ (UserError s) = showString ("CUDA Exception: " ++ s)
+
+
+-- |
+-- Raise a CUDAException in the IO Monad
+--
+cudaError :: String -> IO a
+cudaError s = throwIO (UserError s)
+
+
+-- |
+-- Run a CUDA computation
+--
+{-
+runCUDA f = runEMT $ do
+  f `catchWithSrcLoc` \l e -> lift (handle l e)
+  where
+    handle :: CallTrace -> CUDAException -> IO ()
+    handle l e = putStrLn $ showExceptionWithTrace l e
+-}
+
+--------------------------------------------------------------------------------
+-- Helper Functions
+--------------------------------------------------------------------------------
+
+-- |
+-- Return the results of a function on successful execution, otherwise throw an
+-- exception with an error string associated with the return code
+--
+resultIfOk :: (Status, a) -> IO a
+resultIfOk (status,result) =
+    case status of
+        Success -> return  result
+        _       -> throwIO (ExitCode status)
+
+
+-- |
+-- Throw an exception with an error string associated with an unsuccessful
+-- return code, otherwise return unit.
+--
+nothingIfOk :: Status -> IO ()
+nothingIfOk status =
+    case status of
+        Success -> return  ()
+        _       -> throwIO (ExitCode status)
+
diff --git a/Foreign/CUDA/Driver/Event.chs b/Foreign/CUDA/Driver/Event.chs
new file mode 100644
--- /dev/null
+++ b/Foreign/CUDA/Driver/Event.chs
@@ -0,0 +1,127 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+--------------------------------------------------------------------------------
+-- |
+-- Module    : Foreign.CUDA.Driver.Event
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Event management for low-level driver interface
+--
+--------------------------------------------------------------------------------
+
+module Foreign.CUDA.Driver.Event
+  (
+    Event, EventFlag(..),
+    create, destroy, elapsedTime, query, record, block
+  )
+  where
+
+#include <cuda.h>
+{# context lib="cuda" #}
+
+-- Friends
+import Foreign.CUDA.Internal.C2HS
+import Foreign.CUDA.Driver.Error
+import Foreign.CUDA.Driver.Stream               (Stream(..))
+
+-- System
+import Foreign
+import Foreign.C
+import Control.Monad                            (liftM)
+
+
+--------------------------------------------------------------------------------
+-- Data Types
+--------------------------------------------------------------------------------
+
+-- |
+-- Events
+--
+newtype Event = Event { useEvent :: {# type CUevent #}}
+
+
+-- |
+-- Event creation flags
+--
+{# enum CUevent_flags as EventFlag
+    { underscoreToCase }
+    with prefix="CU_EVENT" deriving (Eq, Show) #}
+
+
+--------------------------------------------------------------------------------
+-- Event management
+--------------------------------------------------------------------------------
+
+-- |
+-- Create a new event
+--
+create :: [EventFlag] -> IO Event
+create flags = resultIfOk =<< cuEventCreate flags
+
+{# fun unsafe cuEventCreate
+  { alloca-         `Event'       peekEvt*
+  , combineBitMasks `[EventFlag]'          } -> `Status' cToEnum #}
+  where peekEvt = liftM Event . peek
+
+
+-- |
+-- Destroy an event
+--
+destroy :: Event -> IO ()
+destroy ev = nothingIfOk =<< cuEventDestroy ev
+
+{# fun unsafe cuEventDestroy
+  { useEvent `Event' } -> `Status' cToEnum #}
+
+
+-- |
+-- Determine the elapsed time (in milliseconds) between two events
+--
+elapsedTime :: Event -> Event -> IO Float
+elapsedTime ev1 ev2 = resultIfOk =<< cuEventElapsedTime ev1 ev2
+
+{# fun unsafe cuEventElapsedTime
+  { alloca-  `Float' peekFloatConv*
+  , useEvent `Event'
+  , useEvent `Event'                } -> `Status' cToEnum #}
+
+
+-- |
+-- Determines if a event has actually been recorded
+--
+query :: Event -> IO Bool
+query ev =
+  cuEventQuery ev >>= \rv ->
+  case rv of
+    Success  -> return True
+    NotReady -> return False
+    _        -> resultIfOk (rv,undefined)
+
+{# fun unsafe cuEventQuery
+  { useEvent `Event' } -> `Status' cToEnum #}
+
+
+-- |
+-- Record an event once all operations in the current context (or optionally
+-- specified stream) have completed. This operation is asynchronous.
+--
+record :: Event -> Maybe Stream -> IO ()
+record ev mst =
+  nothingIfOk =<< case mst of
+    Just st -> cuEventRecord ev st
+    Nothing -> cuEventRecord ev (Stream nullPtr)
+
+{# fun unsafe cuEventRecord
+  { useEvent  `Event'
+  , useStream `Stream' } -> `Status' cToEnum #}
+
+
+-- |
+-- Wait until the event has been recorded
+--
+block :: Event -> IO ()
+block ev = nothingIfOk =<< cuEventSynchronize ev
+
+{# fun unsafe cuEventSynchronize
+  { useEvent `Event' } -> `Status' cToEnum #}
+
diff --git a/Foreign/CUDA/Driver/Exec.chs b/Foreign/CUDA/Driver/Exec.chs
new file mode 100644
--- /dev/null
+++ b/Foreign/CUDA/Driver/Exec.chs
@@ -0,0 +1,165 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+--------------------------------------------------------------------------------
+-- |
+-- Module    : Foreign.CUDA.Driver.Exec
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Kernel execution control for low-level driver interface
+--
+--------------------------------------------------------------------------------
+
+module Foreign.CUDA.Driver.Exec
+  (
+    Fun(Fun),  -- need to export the data constructor for use by Module )=
+    FunParam(..), FunAttribute(..),
+    requires, setBlockShape, setSharedSize, setParams, launch
+  )
+  where
+
+#include <cuda.h>
+{# context lib="cuda" #}
+
+-- Friends
+import Foreign.CUDA.Internal.C2HS
+import Foreign.CUDA.Driver.Error
+import Foreign.CUDA.Driver.Stream               (Stream(..))
+
+-- System
+import Foreign
+import Foreign.C
+import Control.Monad                            (zipWithM_)
+
+
+--------------------------------------------------------------------------------
+-- Data Types
+--------------------------------------------------------------------------------
+
+-- |
+-- A @__global__@ device function
+--
+newtype Fun = Fun { useFun :: {# type CUfunction #}}
+
+
+-- |
+-- Function attributes
+--
+{# enum CUfunction_attribute as FunAttribute
+    { underscoreToCase
+    , MAX_THREADS_PER_BLOCK as MaxKernelThreadsPerBlock }
+    with prefix="CU_FUNC_ATTRIBUTE" deriving (Eq, Show) #}
+
+
+-- |
+-- Kernel function parameters
+--
+data Storable a => FunParam a
+    = IArg Int
+    | FArg Float
+    | VArg a
+--  | TArg Texture
+
+--------------------------------------------------------------------------------
+-- Execution Control
+--------------------------------------------------------------------------------
+
+-- |
+-- Returns the value of the selected attribute requirement for the given kernel
+--
+requires :: Fun -> FunAttribute -> IO Int
+requires fn att = resultIfOk =<< cuFuncGetAttribute att fn
+
+{# fun unsafe cuFuncGetAttribute
+  { alloca-   `Int'          peekIntConv*
+  , cFromEnum `FunAttribute'
+  , useFun    `Fun'                       } -> `Status' cToEnum #}
+
+
+-- |
+-- Specify the (x,y,z) dimensions of the thread blocks that are created when the
+-- given kernel function is lanched
+--
+setBlockShape :: Fun -> (Int,Int,Int) -> IO ()
+setBlockShape fn (x,y,z) = nothingIfOk =<< cuFuncSetBlockShape fn x y z
+
+{# fun unsafe cuFuncSetBlockShape
+  { useFun `Fun'
+  ,        `Int'
+  ,        `Int'
+  ,        `Int' } -> `Status' cToEnum #}
+
+
+-- |
+-- Set the number of bytes of dynamic shared memory to be available to each
+-- thread block when the function is launched
+--
+setSharedSize :: Fun -> Integer -> IO ()
+setSharedSize fn bytes = nothingIfOk =<< cuFuncSetSharedSize fn bytes
+
+{# fun unsafe cuFuncSetSharedSize
+  { useFun   `Fun'
+  , cIntConv `Integer' } -> `Status' cToEnum #}
+
+
+-- |
+-- Invoke the kernel on a size (w,h) grid of blocks. Each block contains the
+-- number of threads specified by a previous call to `setBlockShape'. The launch
+-- may also be associated with a specific `Stream'.
+--
+launch :: Fun -> (Int,Int) -> Maybe Stream -> IO ()
+launch fn (w,h) mst =
+  nothingIfOk =<< case mst of
+    Nothing -> cuLaunchGridAsync fn w h (Stream nullPtr)
+    Just st -> cuLaunchGridAsync fn w h st
+
+{# fun unsafe cuLaunchGridAsync
+  { useFun    `Fun'
+  ,           `Int'
+  ,           `Int'
+  , useStream `Stream' } -> `Status' cToEnum #}
+
+
+--------------------------------------------------------------------------------
+-- Kernel function parameters
+--------------------------------------------------------------------------------
+
+-- |
+-- Set the parameters that will specified next time the kernel is invoked
+--
+setParams :: Storable a => Fun -> [FunParam a] -> IO ()
+setParams fn prs = do
+  zipWithM_ (set fn) offsets prs
+  nothingIfOk =<< cuParamSetSize fn (last offsets)
+  where
+    offsets = scanl (\a b -> a + size b) 0 prs
+
+    size (IArg _)    = sizeOf (undefined::CUInt)
+    size (FArg _)    = sizeOf (undefined::CFloat)
+    size (VArg v)    = sizeOf v
+
+    set f o (IArg v) = nothingIfOk =<< cuParamSeti f o v
+    set f o (FArg v) = nothingIfOk =<< cuParamSetf f o v
+    set f o (VArg v) = with v $ \p -> (nothingIfOk =<< cuParamSetv f o p (sizeOf v))
+
+
+{# fun unsafe cuParamSetSize
+  { useFun `Fun'
+  ,        `Int' } -> `Status' cToEnum #}
+
+{# fun unsafe cuParamSeti
+  { useFun `Fun'
+  ,        `Int'
+  ,        `Int' } -> `Status' cToEnum #}
+
+{# fun unsafe cuParamSetf
+  { useFun `Fun'
+  ,        `Int'
+  ,        `Float' } -> `Status' cToEnum #}
+
+{# fun unsafe cuParamSetv
+  `Storable a' =>
+  { useFun  `Fun'
+  ,         `Int'
+  , castPtr `Ptr a'
+  ,         `Int'   } -> `Status' cToEnum #}
+
diff --git a/Foreign/CUDA/Driver/Marshal.chs b/Foreign/CUDA/Driver/Marshal.chs
new file mode 100644
--- /dev/null
+++ b/Foreign/CUDA/Driver/Marshal.chs
@@ -0,0 +1,419 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+--------------------------------------------------------------------------------
+-- |
+-- Module    : Foreign.CUDA.Driver.Marshal
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Memory management for low-level driver interface
+--
+--------------------------------------------------------------------------------
+
+module Foreign.CUDA.Driver.Marshal
+  (
+    -- * Host Allocation
+    HostPtr(..), AllocFlag(..),
+    withHostPtr, mallocHostArray, freeHost, nullHostPtr,
+
+    -- * Device Allocation
+    DevicePtr,
+    mallocArray, allocaArray, free, nullDevPtr,
+
+    -- * Marshalling
+    peekArray, peekArrayAsync, peekListArray,
+    pokeArray, pokeArrayAsync, pokeListArray,
+               copyArrayAsync,
+
+    -- * Combined Allocation and Marshalling
+    newListArray, withListArray, withListArrayLen,
+
+    -- * Utility
+    memset, getDevicePtr
+  )
+  where
+
+#include <cuda.h>
+{# context lib="cuda" #}
+
+-- Friends
+import Foreign.CUDA.Internal.C2HS
+import Foreign.CUDA.Driver.Error
+import Foreign.CUDA.Driver.Stream               (Stream(..))
+
+-- System
+import Unsafe.Coerce
+import Control.Monad                            (liftM)
+import Control.Exception.Extensible
+
+import Foreign.C
+import Foreign.Ptr
+import Foreign.Storable
+import qualified Foreign.Marshal as F
+
+#c
+typedef enum CUmemhostalloc_option_enum {
+    CU_MEMHOSTALLOC_OPTION_PORTABLE       = CU_MEMHOSTALLOC_PORTABLE,
+    CU_MEMHOSTALLOC_OPTION_DEVICE_MAPPED  = CU_MEMHOSTALLOC_DEVICEMAP,
+    CU_MEMHOSTALLOC_OPTION_WRITE_COMBINED = CU_MEMHOSTALLOC_WRITECOMBINED
+} CUmemhostalloc_option;
+#endc
+
+--------------------------------------------------------------------------------
+-- Data Types
+--------------------------------------------------------------------------------
+
+-- |
+-- A reference to memory allocated on the device
+--
+newtype DevicePtr a = DevicePtr { useDevicePtr :: {# type CUdeviceptr #}}
+
+instance Storable (DevicePtr a) where
+  sizeOf _      = sizeOf    (undefined :: {# type CUdeviceptr #})
+  alignment _   = alignment (undefined :: {# type CUdeviceptr #})
+  peek p        = DevicePtr `fmap` peek (castPtr p)
+  poke p v      = poke (castPtr p) (useDevicePtr v)
+
+
+-- |
+-- A reference to memory on the host that is page-locked and directly-accessible
+-- from the device. Since the memory can be accessed directly, it can be read or
+-- written at a much higher bandwidth than pageable memory from the traditional
+-- malloc.
+--
+-- The driver automatically accelerates calls to functions such as `memcpy'
+-- which reference page-locked memory.
+--
+newtype HostPtr a = HostPtr { useHostPtr :: Ptr a }
+
+-- |
+-- Unwrap a host pointer and execute a computation using the base pointer object
+--
+withHostPtr :: HostPtr a -> (Ptr a -> IO b) -> IO b
+withHostPtr p f = f (useHostPtr p)
+
+-- |
+-- Options for host allocation
+--
+{# enum CUmemhostalloc_option as AllocFlag
+    { underscoreToCase }
+    with prefix="CU_MEMHOSTALLOC_OPTION" deriving (Eq, Show) #}
+
+--------------------------------------------------------------------------------
+-- Host Allocation
+--------------------------------------------------------------------------------
+
+-- |
+-- Allocate a section of linear memory on the host which is page-locked and
+-- directly accessible from the device. The storage is sufficient to hold the
+-- given number of elements of a storable type.
+--
+-- Note that since the amount of pageable memory is thusly reduced, overall
+-- system performance may suffer. This is best used sparingly to allocate
+-- staging areas for data exchange.
+--
+mallocHostArray :: Storable a => [AllocFlag] -> Int -> IO (HostPtr a)
+mallocHostArray flags = doMalloc undefined
+  where
+    doMalloc :: Storable a' => a' -> Int -> IO (HostPtr a')
+    doMalloc x n = resultIfOk =<< cuMemHostAlloc (n * sizeOf x) flags
+
+{# fun unsafe cuMemHostAlloc
+  { alloca'-        `HostPtr a'   peekHP*
+  ,                 `Int'
+  , combineBitMasks `[AllocFlag]'         } -> `Status' cToEnum #}
+  where
+    alloca'  = F.alloca
+    peekHP p = (HostPtr . castPtr) `fmap` peek p
+
+
+-- |
+-- Free a section of page-locked host memory
+--
+freeHost :: HostPtr a -> IO ()
+freeHost p = nothingIfOk =<< cuMemFreeHost p
+
+{# fun unsafe cuMemFreeHost
+  { useHP `HostPtr a' } -> `Status' cToEnum #}
+  where
+    useHP = castPtr . useHostPtr
+
+
+-- |
+-- The constant 'nullHostPtr' contains the distinguished memory location that is
+-- not associated with a valid memory location
+--
+nullHostPtr :: HostPtr a
+nullHostPtr =  HostPtr nullPtr
+
+
+--------------------------------------------------------------------------------
+-- Device Allocation
+--------------------------------------------------------------------------------
+
+-- |
+-- Allocate a section of linear memory on the device, and return a reference to
+-- it. The memory is sufficient to hold the given number of elements of storable
+-- type. It is suitably aligned for any type, and is not cleared.
+--
+mallocArray :: Storable a => Int -> IO (DevicePtr a)
+mallocArray = doMalloc undefined
+  where
+    doMalloc :: Storable a' => a' -> Int -> IO (DevicePtr a')
+    doMalloc x n = resultIfOk =<< cuMemAlloc (n * sizeOf x)
+
+{# fun unsafe cuMemAlloc
+  { alloca'- `DevicePtr a' peekDP*
+  ,          `Int'               } -> `Status' cToEnum #}
+  where
+    alloca' = F.alloca
+    peekDP  = liftM DevicePtr . peek
+
+
+-- |
+-- Execute a computation on the device, passing a pointer to a temporarily
+-- allocated block of memory sufficient to hold the given number of elements of
+-- storable type. The memory is freed when the computation terminates (normally
+-- or via an exception), so the pointer must not be used after this.
+--
+-- Note that kernel launches can be asynchronous, so you may want to add a
+-- synchronisation point using 'sync' as part of the computation.
+--
+allocaArray :: Storable a => Int -> (DevicePtr a -> IO b) -> IO b
+allocaArray n = bracket (mallocArray n) free
+
+
+-- |
+-- Release a section of device memory
+--
+free :: DevicePtr a -> IO ()
+free dp = nothingIfOk =<< cuMemFree dp
+
+{# fun unsafe cuMemFree
+  { useDevicePtr `DevicePtr a' } -> `Status' cToEnum #}
+
+
+-- |
+-- The constant 'nullDevPtr' contains the distinguished memory location that is
+-- not associated with a valid memory location
+--
+nullDevPtr :: DevicePtr a
+nullDevPtr =  DevicePtr 0
+
+
+--------------------------------------------------------------------------------
+-- Marshalling
+--------------------------------------------------------------------------------
+
+-- |
+-- Copy a number of elements from the device to host memory. This is a
+-- synchronous operation
+--
+peekArray :: Storable a => Int -> DevicePtr a -> Ptr a -> IO ()
+peekArray n dptr hptr = doPeek undefined dptr
+  where
+    doPeek :: Storable a' => a' -> DevicePtr a' -> IO ()
+    doPeek x _ = nothingIfOk =<< cuMemcpyDtoH hptr dptr (n * sizeOf x)
+
+{# fun unsafe cuMemcpyDtoH
+  { castPtr      `Ptr a'
+  , useDevicePtr `DevicePtr a'
+  ,              `Int'         } -> `Status' cToEnum #}
+
+
+-- |
+-- Copy memory from the device asynchronously, possibly associated with a
+-- particular stream. The destination host memory must be page-locked.
+--
+peekArrayAsync :: Storable a => Int -> DevicePtr a -> HostPtr a -> Maybe Stream -> IO ()
+peekArrayAsync n dptr hptr mst = doPeek undefined dptr
+  where
+    doPeek :: Storable a' => a' -> DevicePtr a' -> IO ()
+    doPeek x _ =
+      nothingIfOk =<< case mst of
+        Nothing -> cuMemcpyDtoHAsync hptr dptr (n * sizeOf x) (Stream nullPtr)
+        Just st -> cuMemcpyDtoHAsync hptr dptr (n * sizeOf x) st
+
+{# fun unsafe cuMemcpyDtoHAsync
+  { useHP        `HostPtr a'
+  , useDevicePtr `DevicePtr a'
+  ,              `Int'
+  , useStream    `Stream'  } -> `Status' cToEnum #}
+  where
+    useHP = castPtr . useHostPtr
+
+
+-- |
+-- Copy a number of elements from the device into a new Haskell list. Note that
+-- this requires two memory copies: firstly from the device into a heap
+-- allocated array, and from there marshalled into a list.
+--
+peekListArray :: Storable a => Int -> DevicePtr a -> IO [a]
+peekListArray n dptr =
+  F.allocaArray n $ \p -> do
+    peekArray   n dptr p
+    F.peekArray n p
+
+
+-- |
+-- Copy a number of elements onto the device. This is a synchronous operation
+--
+pokeArray :: Storable a => Int -> Ptr a -> DevicePtr a -> IO ()
+pokeArray n hptr dptr = doPoke undefined dptr
+  where
+    doPoke :: Storable a' => a' -> DevicePtr a' -> IO ()
+    doPoke x _ = nothingIfOk =<< cuMemcpyHtoD dptr hptr (n * sizeOf x)
+
+{# fun unsafe cuMemcpyHtoD
+  { useDevicePtr `DevicePtr a'
+  , castPtr      `Ptr a'
+  ,              `Int'         } -> `Status' cToEnum #}
+
+
+-- |
+-- Copy memory onto the device asynchronously, possibly associated with a
+-- particular stream. The source host memory must be page-locked.
+--
+pokeArrayAsync :: Storable a => Int -> HostPtr a -> DevicePtr a -> Maybe Stream -> IO ()
+pokeArrayAsync n hptr dptr mst = dopoke undefined dptr
+  where
+    dopoke :: Storable a' => a' -> DevicePtr a' -> IO ()
+    dopoke x _ =
+      nothingIfOk =<< case mst of
+        Nothing -> cuMemcpyHtoDAsync dptr hptr (n * sizeOf x) (Stream nullPtr)
+        Just st -> cuMemcpyHtoDAsync dptr hptr (n * sizeOf x) st
+
+{# fun unsafe cuMemcpyHtoDAsync
+  { useDevicePtr `DevicePtr a'
+  , useHP        `HostPtr a'
+  ,              `Int'
+  , useStream    `Stream'      } -> `Status' cToEnum #}
+  where
+    useHP = castPtr . useHostPtr
+
+
+-- |
+-- Write a list of storable elements into a device array. The device array must
+-- be sufficiently large to hold the entire list. This requires two marshalling
+-- operations.
+--
+pokeListArray :: Storable a => [a] -> DevicePtr a -> IO ()
+pokeListArray xs dptr = F.withArrayLen xs $ \len p -> pokeArray len p dptr
+
+
+-- |
+-- Copy the given number of elements from the first device array (source) to the
+-- second (destination). The copied areas may not overlap. This operation is
+-- asynchronous with respect to the host, but will never overlap with kernel
+-- execution.
+--
+copyArrayAsync :: Storable a => Int -> DevicePtr a -> DevicePtr a -> IO ()
+copyArrayAsync n = docopy undefined
+  where
+    docopy :: Storable a' => a' -> DevicePtr a' -> DevicePtr a' -> IO ()
+    docopy x src dst = nothingIfOk =<< cuMemcpyDtoD dst src (n * sizeOf x)
+
+{# fun unsafe cuMemcpyDtoD
+  { useDevicePtr `DevicePtr a'
+  , useDevicePtr `DevicePtr a'
+  ,              `Int'         } -> `Status' cToEnum #}
+
+
+--------------------------------------------------------------------------------
+-- Combined Allocation and Marshalling
+--------------------------------------------------------------------------------
+
+-- |
+-- Write a list of storable elements into a newly allocated device array. Note
+-- that this requires two memory copies: firstly from a Haskell list to a heap
+-- allocated array, and from there onto the graphics device. The memory should
+-- be 'free'd when no longer required.
+--
+newListArray :: Storable a => [a] -> IO (DevicePtr a)
+newListArray xs =
+  F.withArrayLen xs                     $ \len p ->
+  bracketOnError (mallocArray len) free $ \d_xs  -> do
+    pokeArray len p d_xs
+    return d_xs
+
+
+-- |
+-- Temporarily store a list of elements into a newly allocated device array. An
+-- IO action is applied to to the array, the result of which is returned.
+-- Similar to 'newListArray', this requires copying the data twice.
+--
+-- As with 'allocaArray', the memory is freed once the action completes, so you
+-- should not return the pointer from the action, and be wary of asynchronous
+-- kernel execution.
+--
+withListArray :: Storable a => [a] -> (DevicePtr a -> IO b) -> IO b
+withListArray xs = withListArrayLen xs . const
+
+
+-- |
+-- A variant of 'withListArray' which also supplies the number of elements in
+-- the array to the applied function
+--
+withListArrayLen :: Storable a => [a] -> (Int -> DevicePtr a -> IO b) -> IO b
+withListArrayLen xs f =
+  allocaArray   len $ \d_xs -> do
+  F.allocaArray len $ \h_xs -> do
+    F.pokeArray h_xs xs
+    pokeArray len h_xs d_xs
+  f len d_xs
+  where
+    len = length xs
+
+
+--------------------------------------------------------------------------------
+-- Utility
+--------------------------------------------------------------------------------
+
+-- |
+-- Set a number of data elements to the specified value, which may be either 8-,
+-- 16-, or 32-bits wide.
+--
+memset :: Storable a => DevicePtr a -> Int -> a -> IO ()
+memset dptr n val = case sizeOf val of
+    1 -> nothingIfOk =<< cuMemsetD8  dptr val n
+    2 -> nothingIfOk =<< cuMemsetD16 dptr val n
+    4 -> nothingIfOk =<< cuMemsetD32 dptr val n
+    _ -> cudaError "can only memset 8-, 16-, and 32-bit values"
+
+--
+-- We use unsafe coerce below to reinterpret the bits of the value to memset as,
+-- into the integer type required by the setting functions.
+--
+{# fun unsafe cuMemsetD8
+  { useDevicePtr `DevicePtr a'
+  , unsafeCoerce `a'
+  ,              `Int'         } -> `Status' cToEnum #}
+
+{# fun unsafe cuMemsetD16
+  { useDevicePtr `DevicePtr a'
+  , unsafeCoerce `a'
+  ,              `Int'         } -> `Status' cToEnum #}
+
+{# fun unsafe cuMemsetD32
+  { useDevicePtr `DevicePtr a'
+  , unsafeCoerce `a'
+  ,              `Int'         } -> `Status' cToEnum #}
+
+
+-- |
+-- Return the device pointer associated with a mapped, pinned host buffer, which
+-- was allocated with the 'DeviceMapped' option by 'mallocHostArray'.
+--
+-- Currently, no options are supported and this must be empty.
+--
+getDevicePtr :: [AllocFlag] -> HostPtr a -> IO (DevicePtr a)
+getDevicePtr flags hp = resultIfOk =<< cuMemHostGetDevicePointer hp flags
+
+{# fun unsafe cuMemHostGetDevicePointer
+  { alloca'-        `DevicePtr a' peekDP*
+  , useHP           `HostPtr a'
+  , combineBitMasks `[AllocFlag]'         } -> `Status' cToEnum #}
+  where
+    alloca' = F.alloca
+    useHP   = castPtr . useHostPtr
+    peekDP  = liftM DevicePtr . peek
+
diff --git a/Foreign/CUDA/Driver/Module.chs b/Foreign/CUDA/Driver/Module.chs
new file mode 100644
--- /dev/null
+++ b/Foreign/CUDA/Driver/Module.chs
@@ -0,0 +1,182 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+--------------------------------------------------------------------------------
+-- |
+-- Module    : Foreign.CUDA.Driver.Module
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Module management for low-level driver interface
+--
+--------------------------------------------------------------------------------
+
+module Foreign.CUDA.Driver.Module
+  (
+    Module,
+    JITOption(..), JITTarget(..), JITResult(..),
+    getFun, loadFile, loadData, loadDataEx, unload
+  )
+  where
+
+#include <cuda.h>
+{# context lib="cuda" #}
+
+-- Friends
+import Foreign.CUDA.Internal.C2HS
+import Foreign.CUDA.Driver.Error
+import Foreign.CUDA.Driver.Exec
+
+-- System
+import Foreign
+import Foreign.C
+import Unsafe.Coerce
+
+import Control.Monad                            (liftM)
+import Data.ByteString.Char8                    (ByteString)
+import qualified Data.ByteString.Char8 as B
+
+
+--------------------------------------------------------------------------------
+-- Data Types
+--------------------------------------------------------------------------------
+
+-- |
+-- A reference to a Module object, containing collections of device functions
+--
+newtype Module = Module { useModule :: {# type CUmodule #}}
+
+
+-- |
+-- Just-in-time compilation options
+--
+data JITOption
+  = MaxRegisters       Int       -- ^ maximum number of registers per thread
+  | ThreadsPerBlock    Int       -- ^ number of threads per block to target for
+  | OptimisationLevel  Int       -- ^ level of optimisation to apply (1-4, default 4)
+  | Target             JITTarget -- ^ compilation target, otherwise determined from context
+--  | FallbackStrategy   JITFallback
+  deriving (Show)
+
+-- |
+-- Results of online compilation
+--
+data JITResult = JITResult
+  {
+    jitTime     :: Float,       -- ^ milliseconds spent compiling PTX
+    jitInfoLog  :: ByteString,  -- ^ information about PTX asembly
+    jitErrorLog :: ByteString   -- ^ compilation errors
+  }
+  deriving (Show)
+
+
+{# enum CUjit_option as JITOptionInternal
+    { }
+    with prefix="CU" deriving (Eq, Show) #}
+
+{# enum CUjit_target as JITTarget
+    { underscoreToCase }
+    with prefix="CU_TARGET" deriving (Eq, Show) #}
+
+{# enum CUjit_fallback as JITFallback
+    { underscoreToCase }
+    with prefix="CU_PREFER" deriving (Eq, Show) #}
+
+
+--------------------------------------------------------------------------------
+-- Module management
+--------------------------------------------------------------------------------
+
+-- |
+-- Returns a function handle
+--
+getFun :: Module -> String -> IO Fun
+getFun mdl fn = resultIfOk =<< cuModuleGetFunction mdl fn
+
+{# fun unsafe cuModuleGetFunction
+  { alloca-      `Fun'    peekFun*
+  , useModule    `Module'
+  , withCString* `String'          } -> `Status' cToEnum #}
+  where peekFun = liftM Fun . peek
+
+
+-- |
+-- Load the contents of the specified file (either a ptx or cubin file) to
+-- create a new module, and load that module into the current context
+--
+loadFile :: String -> IO Module
+loadFile ptx = resultIfOk =<< cuModuleLoad ptx
+
+{# fun unsafe cuModuleLoad
+  { alloca-      `Module' peekMod*
+  , withCString* `String'          } -> `Status' cToEnum #}
+  where peekMod = liftM Module . peek
+
+
+-- |
+-- Load the contents of the given image into a new module, and load that module
+-- into the current context. The image (typically) is the contents of a cubin or
+-- ptx file as a NULL-terminated string.
+--
+loadData :: ByteString -> IO Module
+loadData img = resultIfOk =<< cuModuleLoadData img
+
+{# fun unsafe cuModuleLoadData
+  { alloca- `Module'     peekMod*
+  , useBS*  `ByteString'          } -> ` Status' cToEnum #}
+  where
+    peekMod      = liftM Module . peek
+    useBS bs act = B.useAsCString bs $ \p -> act (castPtr p)
+
+
+-- |
+-- Load a module with online compiler options. The actual attributes of the
+-- compiled kernel can be probed using `requirements'.
+--
+loadDataEx :: ByteString -> [JITOption] -> IO (Module, JITResult)
+loadDataEx img options =
+  allocaArray logSize $ \p_ilog ->
+  allocaArray logSize $ \p_elog ->
+  let (opt,val) = unzip $
+        [ (JIT_WALL_TIME, 0) -- must be first
+        , (JIT_INFO_LOG_BUFFER_SIZE_BYTES,  logSize)
+        , (JIT_ERROR_LOG_BUFFER_SIZE_BYTES, logSize)
+        , (JIT_INFO_LOG_BUFFER,  unsafeCoerce (p_ilog :: CString))
+        , (JIT_ERROR_LOG_BUFFER, unsafeCoerce (p_elog :: CString)) ] ++ map unpack options in
+
+  withArray (map cFromEnum opt)    $ \p_opts ->
+  withArray (map unsafeCoerce val) $ \p_vals -> do
+
+  (s,mdl) <- cuModuleLoadDataEx img (length opt) p_opts p_vals
+  infoLog <- B.packCString p_ilog
+  errLog  <- B.packCString p_elog
+  time    <- peek (castPtr p_vals)
+  resultIfOk (s, (mdl, JITResult time infoLog errLog))
+
+  where
+    logSize = 2048
+
+    unpack (MaxRegisters x)      = (JIT_MAX_REGISTERS, x)
+    unpack (ThreadsPerBlock x)   = (JIT_THREADS_PER_BLOCK, x)
+    unpack (OptimisationLevel x) = (JIT_OPTIMIZATION_LEVEL, x)
+    unpack (Target x)            = (JIT_TARGET, fromEnum x)
+
+
+{# fun unsafe cuModuleLoadDataEx
+  { alloca- `Module'       peekMod*
+  , useBS*  `ByteString'
+  ,         `Int'
+  , id      `Ptr CInt'
+  , id      `Ptr (Ptr ())'          } -> `Status' cToEnum #}
+  where
+    peekMod      = liftM Module . peek
+    useBS bs act = B.useAsCString bs $ \p -> act (castPtr p)
+
+
+-- |
+-- Unload a module from the current context
+--
+unload :: Module -> IO ()
+unload m = nothingIfOk =<< cuModuleUnload m
+
+{# fun unsafe cuModuleUnload
+  { useModule `Module' } -> `Status' cToEnum #}
+
diff --git a/Foreign/CUDA/Driver/Stream.chs b/Foreign/CUDA/Driver/Stream.chs
new file mode 100644
--- /dev/null
+++ b/Foreign/CUDA/Driver/Stream.chs
@@ -0,0 +1,98 @@
+{-# LANGUAGE ForeignFunctionInterface, EmptyDataDecls #-}
+--------------------------------------------------------------------------------
+-- |
+-- Module    : Foreign.CUDA.Driver.Stream
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Stream management for low-level driver interface
+--
+--------------------------------------------------------------------------------
+
+module Foreign.CUDA.Driver.Stream
+  (
+    Stream(..), StreamFlag,
+    create, destroy, finished, block
+  )
+  where
+
+#include <cuda.h>
+{# context lib="cuda" #}
+
+-- Friends
+import Foreign.CUDA.Driver.Error
+import Foreign.CUDA.Internal.C2HS
+
+-- System
+import Foreign
+import Foreign.C
+import Control.Monad                            (liftM)
+
+
+--------------------------------------------------------------------------------
+-- Data Types
+--------------------------------------------------------------------------------
+
+-- |
+-- A processing stream
+--
+newtype Stream = Stream { useStream :: {# type CUstream #}}
+
+
+-- |
+-- Possible option flags for stream initialisation. Dummy instance until the API
+-- exports actual option values.
+--
+data StreamFlag
+
+instance Enum StreamFlag where
+
+--------------------------------------------------------------------------------
+-- Stream management
+--------------------------------------------------------------------------------
+
+-- |
+-- Create a new stream
+--
+create :: [StreamFlag] -> IO Stream
+create flags = resultIfOk =<< cuStreamCreate flags
+
+{# fun unsafe cuStreamCreate
+  { alloca-         `Stream'       peekStream*
+  , combineBitMasks `[StreamFlag]'             } -> `Status' cToEnum #}
+  where peekStream = liftM Stream . peek
+
+-- |
+-- Destroy a stream
+--
+destroy :: Stream -> IO ()
+destroy st = nothingIfOk =<< cuStreamDestroy st
+
+{# fun unsafe cuStreamDestroy
+  { useStream `Stream' } -> `Status' cToEnum #}
+
+
+-- |
+-- Check if all operations in the stream have completed
+--
+finished :: Stream -> IO Bool
+finished st =
+  cuStreamQuery st >>= \rv ->
+  case rv of
+    Success  -> return True
+    NotReady -> return False
+    _        -> resultIfOk (rv,undefined)
+
+{# fun unsafe cuStreamQuery
+  { useStream `Stream' } -> `Status' cToEnum #}
+
+
+-- |
+-- Wait until the device has completed all operations in the Stream
+--
+block :: Stream -> IO ()
+block st = nothingIfOk =<< cuStreamSynchronize st
+
+{# fun unsafe cuStreamSynchronize
+  { useStream `Stream' } -> `Status' cToEnum #}
+
diff --git a/Foreign/CUDA/Driver/Utils.chs b/Foreign/CUDA/Driver/Utils.chs
new file mode 100644
--- /dev/null
+++ b/Foreign/CUDA/Driver/Utils.chs
@@ -0,0 +1,39 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+--------------------------------------------------------------------------------
+-- |
+-- Module    : Foreign.CUDA.Driver.Utils
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Utility functions
+--
+--------------------------------------------------------------------------------
+
+module Foreign.CUDA.Driver.Utils
+  (
+    driverVersion
+  )
+  where
+
+#include <cuda.h>
+{# context lib="cuda" #}
+
+
+-- Friends
+import Foreign.CUDA.Driver.Error
+import Foreign.CUDA.Internal.C2HS
+
+-- System
+import Foreign
+import Foreign.C
+
+
+-- |
+-- Return the version number of the installed CUDA driver
+--
+driverVersion :: IO Int
+driverVersion =  resultIfOk =<< cuDriverGetVersion
+
+{# fun unsafe cuDriverGetVersion
+  { alloca- `Int' peekIntConv* } -> `Status' cToEnum #}
+
diff --git a/Foreign/CUDA/Internal/C2HS.hs b/Foreign/CUDA/Internal/C2HS.hs
new file mode 100644
--- /dev/null
+++ b/Foreign/CUDA/Internal/C2HS.hs
@@ -0,0 +1,225 @@
+--  C->Haskell Compiler: Marshalling library
+--
+--  Copyright (c) [1999...2005] Manuel M T Chakravarty
+--
+--  Redistribution and use in source and binary forms, with or without
+--  modification, are permitted provided that the following conditions are met:
+-- 
+--  1. Redistributions of source code must retain the above copyright notice,
+--     this list of conditions and the following disclaimer. 
+--  2. Redistributions in binary form must reproduce the above copyright
+--     notice, this list of conditions and the following disclaimer in the
+--     documentation and/or other materials provided with the distribution. 
+--  3. The name of the author may not be used to endorse or promote products
+--     derived from this software without specific prior written permission. 
+--
+--  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+--  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+--  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+--  NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+--  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+--  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+--  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+--  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+--  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+--  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+--
+--- Description ---------------------------------------------------------------
+--
+--  Language: Haskell 98
+--
+--  This module provides the marshaling routines for Haskell files produced by 
+--  C->Haskell for binding to C library interfaces.  It exports all of the
+--  low-level FFI (language-independent plus the C-specific parts) together
+--  with the C->HS-specific higher-level marshalling routines.
+--
+
+module Foreign.CUDA.Internal.C2HS (
+
+--  -- * Re-export the language-independent component of the FFI 
+--  module Foreign,
+--
+--  -- * Re-export the C language component of the FFI
+--  module CForeign,
+
+  -- * Composite marshalling functions
+  withCStringLenIntConv, peekCStringLenIntConv, withIntConv, withFloatConv,
+  peekIntConv, peekFloatConv, withBool, peekBool, withEnum, peekEnum,
+
+  -- * Conditional results using 'Maybe'
+  nothingIf, nothingIfNull,
+
+  -- * Bit masks
+  combineBitMasks, containsBitMask, extractBitMasks,
+
+  -- * Conversion between C and Haskell types
+  cIntConv, cFloatConv, cToBool, cFromBool, cToEnum, cFromEnum
+) where 
+
+
+import Foreign
+       hiding       (Word)
+		    -- Should also hide the Foreign.Marshal.Pool exports in
+		    -- compilers that export them
+import CForeign
+
+import Monad        (liftM)
+
+
+-- Composite marshalling functions
+-- -------------------------------
+
+-- Strings with explicit length
+--
+withCStringLenIntConv     :: String -> (CStringLen -> IO a) -> IO a
+withCStringLenIntConv s f  = withCStringLen s $ \(p, n) -> f (p, cIntConv n)
+
+peekCStringLenIntConv        :: CStringLen -> IO String
+peekCStringLenIntConv (s, n)  = peekCStringLen (s, cIntConv n)
+
+-- Marshalling of numerals
+--
+
+withIntConv   :: (Storable b, Integral a, Integral b) 
+	      => a -> (Ptr b -> IO c) -> IO c
+withIntConv    = with . cIntConv
+
+withFloatConv :: (Storable b, RealFloat a, RealFloat b) 
+	      => a -> (Ptr b -> IO c) -> IO c
+withFloatConv  = with . cFloatConv
+
+peekIntConv   :: (Storable a, Integral a, Integral b) 
+	      => Ptr a -> IO b
+peekIntConv    = liftM cIntConv . peek
+
+peekFloatConv :: (Storable a, RealFloat a, RealFloat b) 
+	      => Ptr a -> IO b
+peekFloatConv  = liftM cFloatConv . peek
+
+-- Passing Booleans by reference
+--
+
+withBool :: (Integral a, Storable a) => Bool -> (Ptr a -> IO b) -> IO b
+withBool  = with . fromBool
+
+peekBool :: (Integral a, Storable a) => Ptr a -> IO Bool
+peekBool  = liftM toBool . peek
+
+
+-- Passing enums by reference
+--
+
+withEnum :: (Enum a, Integral b, Storable b) => a -> (Ptr b -> IO c) -> IO c
+withEnum  = with . cFromEnum
+
+peekEnum :: (Enum a, Integral b, Storable b) => Ptr b -> IO a
+peekEnum  = liftM cToEnum . peek
+
+
+{-
+-- Storing of 'Maybe' values
+-- -------------------------
+
+instance Storable a => Storable (Maybe a) where
+  sizeOf    _ = sizeOf    (undefined :: Ptr ())
+  alignment _ = alignment (undefined :: Ptr ())
+
+  peek p = do
+	     ptr <- peek (castPtr p)
+	     if ptr == nullPtr
+	       then return Nothing
+	       else liftM Just $ peek ptr
+
+  poke p v = do
+	       ptr <- case v of
+		        Nothing -> return nullPtr
+			Just v' -> new v'
+               poke (castPtr p) ptr
+-}
+
+
+-- Conditional results using 'Maybe'
+-- ---------------------------------
+
+-- Wrap the result into a 'Maybe' type.
+--
+-- * the predicate determines when the result is considered to be non-existing,
+--   ie, it is represented by `Nothing'
+--
+-- * the second argument allows to map a result wrapped into `Just' to some
+--   other domain
+--
+nothingIf       :: (a -> Bool) -> (a -> b) -> a -> Maybe b
+nothingIf p f x  = if p x then Nothing else Just $ f x
+
+-- |Instance for special casing null pointers.
+--
+nothingIfNull :: (Ptr a -> b) -> Ptr a -> Maybe b
+nothingIfNull  = nothingIf (== nullPtr)
+
+
+-- Support for bit masks
+-- ---------------------
+
+-- Given a list of enumeration values that represent bit masks, combine these
+-- masks using bitwise disjunction.
+--
+combineBitMasks :: (Enum a, Bits b) => [a] -> b
+combineBitMasks = foldl (.|.) 0 . map (fromIntegral . fromEnum)
+
+-- Tests whether the given bit mask is contained in the given bit pattern
+-- (i.e., all bits set in the mask are also set in the pattern).
+--
+containsBitMask :: (Bits a, Enum b) => a -> b -> Bool
+bits `containsBitMask` bm = let bm' = fromIntegral . fromEnum $ bm
+			    in
+			    bm' .&. bits == bm'
+
+-- |Given a bit pattern, yield all bit masks that it contains.
+--
+-- * This does *not* attempt to compute a minimal set of bit masks that when
+--   combined yield the bit pattern, instead all contained bit masks are
+--   produced.
+--
+extractBitMasks :: (Bits a, Enum b, Bounded b) => a -> [b]
+extractBitMasks bits = 
+  [bm | bm <- [minBound..maxBound], bits `containsBitMask` bm]
+
+
+-- Conversion routines
+-- -------------------
+
+-- |Integral conversion
+--
+cIntConv :: (Integral a, Integral b) => a -> b
+cIntConv  = fromIntegral
+
+-- |Floating conversion
+--
+cFloatConv :: (RealFloat a, RealFloat b) => a -> b
+cFloatConv  = realToFrac
+-- As this conversion by default goes via `Rational', it can be very slow...
+{-# RULES 
+  "cFloatConv/Float->Float"   forall (x::Float).  cFloatConv x = x;
+  "cFloatConv/Double->Double" forall (x::Double). cFloatConv x = x
+ #-}
+
+-- |Obtain C value from Haskell 'Bool'.
+--
+cFromBool :: Num a => Bool -> a
+cFromBool  = fromBool
+
+-- |Obtain Haskell 'Bool' from C value.
+--
+cToBool :: Num a => a -> Bool
+cToBool  = toBool
+
+-- |Convert a C enumeration to Haskell.
+--
+cToEnum :: (Integral i, Enum e) => i -> e
+cToEnum  = toEnum . cIntConv
+
+-- |Convert a Haskell enumeration to C.
+--
+cFromEnum :: (Enum e, Integral i) => e -> i
+cFromEnum  = cIntConv . fromEnum
diff --git a/Foreign/CUDA/Internal/Offsets.hsc b/Foreign/CUDA/Internal/Offsets.hsc
new file mode 100644
--- /dev/null
+++ b/Foreign/CUDA/Internal/Offsets.hsc
@@ -0,0 +1,33 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+{-
+ - Structure field offset constants.
+ - Too difficult to extract using C->Haskell )=
+ -}
+
+module Foreign.CUDA.Internal.Offsets where
+
+
+--------------------------------------------------------------------------------
+-- Runtime API
+--------------------------------------------------------------------------------
+
+#include <cuda_runtime_api.h>
+
+devNameOffset, devMaxThreadDimOffset, devMaxGridSizeOffset :: Int
+
+devNameOffset         = #{offset struct cudaDeviceProp, name}
+devMaxThreadDimOffset = #{offset struct cudaDeviceProp, maxThreadsDim}
+devMaxGridSizeOffset  = #{offset struct cudaDeviceProp, maxGridSize}
+
+
+--------------------------------------------------------------------------------
+-- Driver API
+--------------------------------------------------------------------------------
+
+#include <cuda.h>
+
+devMaxThreadDimOffset', devMaxGridSizeOffset' :: Int
+
+devMaxThreadDimOffset' = #{offset struct CUdevprop_st, maxThreadsDim}
+devMaxGridSizeOffset'  = #{offset struct CUdevprop_st, maxGridSize}
+
diff --git a/Foreign/CUDA/Runtime.hs b/Foreign/CUDA/Runtime.hs
new file mode 100644
--- /dev/null
+++ b/Foreign/CUDA/Runtime.hs
@@ -0,0 +1,30 @@
+--------------------------------------------------------------------------------
+-- |
+-- Module    : Foreign.CUDA.Runtime
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Top level bindings to the C-for-CUDA runtime API
+--
+--------------------------------------------------------------------------------
+
+module Foreign.CUDA.Runtime
+  (
+    module Foreign.CUDA.Runtime.Device,
+    module Foreign.CUDA.Runtime.Error,
+    module Foreign.CUDA.Runtime.Exec,
+    module Foreign.CUDA.Runtime.Marshal,
+    module Foreign.CUDA.Runtime.Ptr,
+    module Foreign.CUDA.Runtime.Thread,
+    module Foreign.CUDA.Runtime.Utils
+  )
+  where
+
+import Foreign.CUDA.Runtime.Device
+import Foreign.CUDA.Runtime.Error
+import Foreign.CUDA.Runtime.Exec
+import Foreign.CUDA.Runtime.Marshal
+import Foreign.CUDA.Runtime.Ptr
+import Foreign.CUDA.Runtime.Thread
+import Foreign.CUDA.Runtime.Utils
+
diff --git a/Foreign/CUDA/Runtime/Device.chs b/Foreign/CUDA/Runtime/Device.chs
new file mode 100644
--- /dev/null
+++ b/Foreign/CUDA/Runtime/Device.chs
@@ -0,0 +1,229 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+--------------------------------------------------------------------------------
+-- |
+-- Module    : Foreign.CUDA.Runtime.Device
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Device management routines
+--
+--------------------------------------------------------------------------------
+
+module Foreign.CUDA.Runtime.Device
+  (
+    ComputeMode(..), DeviceFlag(..), DeviceProperties(..),
+
+    -- ** Device management
+    choose, get, count, props, set, setFlags, setOrder
+  )
+  where
+
+#include <cuda_runtime_api.h>
+{# context lib="cudart" #}
+
+-- Friends
+import Foreign.CUDA.Runtime.Error
+import Foreign.CUDA.Internal.C2HS
+import Foreign.CUDA.Internal.Offsets
+
+-- System
+import Foreign
+import Foreign.C
+
+#c
+typedef struct cudaDeviceProp   cudaDeviceProp;
+
+typedef enum
+{
+    cudaDeviceFlagScheduleAuto  = cudaDeviceScheduleAuto,
+    cudaDeviceFlagScheduleSpin  = cudaDeviceScheduleSpin,
+    cudaDeviceFlagScheduleYield = cudaDeviceScheduleYield,
+    cudaDeviceFlagBlockingSync  = cudaDeviceBlockingSync,
+    cudaDeviceFlagMapHost       = cudaDeviceMapHost
+} cudaDeviceFlags;
+#endc
+
+
+--------------------------------------------------------------------------------
+-- Data Types
+--------------------------------------------------------------------------------
+
+{# pointer *cudaDeviceProp as ^ foreign -> DeviceProperties nocode #}
+
+-- |
+-- The compute mode the device is currently in
+--
+{# enum cudaComputeMode as ComputeMode { }
+    with prefix="cudaComputeMode" deriving (Eq, Show) #}
+
+-- |
+-- Device execution flags
+--
+{# enum cudaDeviceFlags as DeviceFlag { }
+    with prefix="cudaDeviceFlag" deriving (Eq, Show) #}
+
+-- |
+-- The properties of a compute device
+--
+data DeviceProperties = DeviceProperties
+  {
+    deviceName               :: String,         -- ^ Identifier
+    computeCapability        :: Double,         -- ^ Supported compute capability
+    totalGlobalMem           :: Int64,          -- ^ Available global memory on the device in bytes
+    totalConstMem            :: Int64,          -- ^ Available constant memory on the device in bytes
+    sharedMemPerBlock        :: Int64,          -- ^ Available shared memory per block in bytes
+    regsPerBlock             :: Int,            -- ^ 32-bit registers per block
+    warpSize                 :: Int,            -- ^ Warp size in threads
+    maxThreadsPerBlock       :: Int,            -- ^ Max number of threads per block
+    maxThreadsDim            :: (Int,Int,Int),  -- ^ Max size of each dimension of a block
+    maxGridSize              :: (Int,Int,Int),  -- ^ Max size of each dimension of a grid
+    clockRate                :: Int,            -- ^ Clock frequency in kilohertz
+    multiProcessorCount      :: Int,            -- ^ Number of multiprocessors on the device
+    memPitch                 :: Int64,          -- ^ Max pitch in bytes allowed by memory copies
+    textureAlignment         :: Int64,          -- ^ Alignment requirement for textures
+    computeMode              :: ComputeMode,
+    deviceOverlap            :: Bool,           -- ^ Device can concurrently copy memory and execute a kernel
+    kernelExecTimeoutEnabled :: Bool,           -- ^ Whether there is a runtime limit on kernels
+    integrated               :: Bool,           -- ^ As opposed to discrete
+    canMapHostMemory         :: Bool            -- ^ Device can use pinned memory
+  }
+  deriving (Show)
+
+
+instance Storable DeviceProperties where
+  sizeOf _    = {#sizeof cudaDeviceProp#}
+  alignment _ = alignment (undefined :: Ptr ())
+
+  peek p      = do
+    gm <- cIntConv     `fmap` {#get cudaDeviceProp.totalGlobalMem#} p
+    sm <- cIntConv     `fmap` {#get cudaDeviceProp.sharedMemPerBlock#} p
+    rb <- cIntConv     `fmap` {#get cudaDeviceProp.regsPerBlock#} p
+    ws <- cIntConv     `fmap` {#get cudaDeviceProp.warpSize#} p
+    mp <- cIntConv     `fmap` {#get cudaDeviceProp.memPitch#} p
+    tb <- cIntConv     `fmap` {#get cudaDeviceProp.maxThreadsPerBlock#} p
+    cl <- cIntConv     `fmap` {#get cudaDeviceProp.clockRate#} p
+    cm <- cIntConv     `fmap` {#get cudaDeviceProp.totalConstMem#} p
+    v1 <- fromIntegral `fmap` {#get cudaDeviceProp.major#} p
+    v2 <- fromIntegral `fmap` {#get cudaDeviceProp.minor#} p
+    ta <- cIntConv     `fmap` {#get cudaDeviceProp.textureAlignment#} p
+    ov <- cToBool      `fmap` {#get cudaDeviceProp.deviceOverlap#} p
+    pc <- cIntConv     `fmap` {#get cudaDeviceProp.multiProcessorCount#} p
+    ke <- cToBool      `fmap` {#get cudaDeviceProp.kernelExecTimeoutEnabled#} p
+    tg <- cToBool      `fmap` {#get cudaDeviceProp.integrated#} p
+    hm <- cToBool      `fmap` {#get cudaDeviceProp.canMapHostMemory#} p
+    md <- cToEnum      `fmap` {#get cudaDeviceProp.computeMode#} p
+
+    --
+    -- C->Haskell returns the wrong type when accessing static arrays in
+    -- structs, returning the dereferenced element but with a Ptr type. Work
+    -- around this with manual pointer arithmetic...
+    --
+    n            <- peekCString (p `plusPtr` devNameOffset)
+    (t1:t2:t3:_) <- map cIntConv `fmap` peekArray 3 (p `plusPtr` devMaxThreadDimOffset :: Ptr CInt)
+    (g1:g2:g3:_) <- map cIntConv `fmap` peekArray 3 (p `plusPtr` devMaxGridSizeOffset  :: Ptr CInt)
+
+    return DeviceProperties
+      {
+        deviceName               = n,
+        computeCapability        = v1 + v2 / max 10 (10^ ((ceiling . logBase 10) v2 :: Int)),
+        totalGlobalMem           = gm,
+        totalConstMem            = cm,
+        sharedMemPerBlock        = sm,
+        regsPerBlock             = rb,
+        warpSize                 = ws,
+        maxThreadsPerBlock       = tb,
+        maxThreadsDim            = (t1,t2,t3),
+        maxGridSize              = (g1,g2,g3),
+        clockRate                = cl,
+        multiProcessorCount      = pc,
+        memPitch                 = mp,
+        textureAlignment         = ta,
+        computeMode              = md,
+        deviceOverlap            = ov,
+        kernelExecTimeoutEnabled = ke,
+        integrated               = tg,
+        canMapHostMemory         = hm
+      }
+
+
+--------------------------------------------------------------------------------
+-- Functions
+--------------------------------------------------------------------------------
+
+-- |
+-- Select the compute device which best matches the given criteria
+--
+choose     :: DeviceProperties -> IO Int
+choose dev =  resultIfOk =<< cudaChooseDevice dev
+
+{# fun unsafe cudaChooseDevice
+  { alloca-      `Int'              peekIntConv*
+  , withDevProp* `DeviceProperties'              } -> `Status' cToEnum #}
+  where
+      withDevProp = with
+
+
+-- |
+-- Returns which device is currently being used
+--
+get :: IO Int
+get =  resultIfOk =<< cudaGetDevice
+
+{# fun unsafe cudaGetDevice
+  { alloca- `Int' peekIntConv* } -> `Status' cToEnum #}
+
+
+-- |
+-- Returns the number of devices available for execution, with compute
+-- capability >= 1.0
+--
+count :: IO Int
+count =  resultIfOk =<< cudaGetDeviceCount
+
+{# fun unsafe cudaGetDeviceCount
+  { alloca- `Int' peekIntConv* } -> `Status' cToEnum #}
+
+
+-- |
+-- Return information about the selected compute device
+--
+props   :: Int -> IO DeviceProperties
+props n =  resultIfOk =<< cudaGetDeviceProperties n
+
+{# fun unsafe cudaGetDeviceProperties
+  { alloca- `DeviceProperties' peek*
+  ,         `Int'                    } -> `Status' cToEnum #}
+
+
+-- |
+-- Set device to be used for GPU execution
+--
+set   :: Int -> IO ()
+set n =  nothingIfOk =<< cudaSetDevice n
+
+{# fun unsafe cudaSetDevice
+  { `Int' } -> `Status' cToEnum #}
+
+
+-- |
+-- Set flags to be used for device executions
+--
+setFlags   :: [DeviceFlag] -> IO ()
+setFlags f =  nothingIfOk =<< cudaSetDeviceFlags (combineBitMasks f)
+
+{# fun unsafe cudaSetDeviceFlags
+  { `Int' } -> `Status' cToEnum #}
+
+
+-- |
+-- Set list of devices for CUDA execution in priority order
+--
+setOrder   :: [Int] -> IO ()
+setOrder l =  nothingIfOk =<< cudaSetValidDevices l (length l)
+
+{# fun unsafe cudaSetValidDevices
+  { withArrayIntConv* `[Int]'
+  ,                   `Int'   } -> `Status' cToEnum #}
+  where
+      withArrayIntConv = withArray . map cIntConv
+
diff --git a/Foreign/CUDA/Runtime/Error.chs b/Foreign/CUDA/Runtime/Error.chs
new file mode 100644
--- /dev/null
+++ b/Foreign/CUDA/Runtime/Error.chs
@@ -0,0 +1,103 @@
+{-# LANGUAGE ForeignFunctionInterface, DeriveDataTypeable #-}
+--------------------------------------------------------------------------------
+-- |
+-- Module    : Foreign.CUDA.Runtime.Error
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Error handling functions
+--
+--------------------------------------------------------------------------------
+
+module Foreign.CUDA.Runtime.Error
+  (
+    Status(..), CUDAException(..),
+    cudaError, describe,
+    resultIfOk, nothingIfOk
+  )
+  where
+
+
+-- Friends
+import Foreign.CUDA.Internal.C2HS
+
+-- System
+import Foreign
+import Foreign.C
+import Data.Typeable
+import Control.Exception.Extensible
+
+#include <cuda_runtime_api.h>
+{# context lib="cudart" #}
+
+
+--------------------------------------------------------------------------------
+-- Return Status
+--------------------------------------------------------------------------------
+
+-- |
+-- Return codes from API functions
+--
+{# enum cudaError as Status
+    { cudaSuccess as Success }
+    with prefix="cudaError" deriving (Eq, Show) #}
+
+--------------------------------------------------------------------------------
+-- Exceptions
+--------------------------------------------------------------------------------
+
+data CUDAException
+  = ExitCode Status
+  | UserError String
+  deriving Typeable
+
+instance Exception CUDAException
+
+instance Show CUDAException where
+  showsPrec _ (ExitCode  s) = showString ("CUDA Exception: " ++ describe s)
+  showsPrec _ (UserError s) = showString ("CUDA Exception: " ++ s)
+
+
+-- |
+-- Raise a 'CUDAException' in the IO Monad
+--
+cudaError :: String -> IO a
+cudaError s = throwIO (UserError s)
+
+
+--------------------------------------------------------------------------------
+-- Helper Functions
+--------------------------------------------------------------------------------
+
+-- |
+-- Return the descriptive string associated with a particular error code
+--
+{# fun pure unsafe cudaGetErrorString as describe
+    { cFromEnum `Status' } -> `String' #}
+--
+-- Logically, this must be a pure function, returning a pointer to a statically
+-- defined string constant.
+--
+
+
+-- |
+-- Return the results of a function on successful execution, otherwise return
+-- the error string associated with the return code
+--
+resultIfOk :: (Status, a) -> IO a
+resultIfOk (status,result) =
+    case status of
+        Success -> return  result
+        _       -> throwIO (ExitCode status)
+
+
+-- |
+-- Return the error string associated with an unsuccessful return code,
+-- otherwise Nothing
+--
+nothingIfOk :: Status -> IO ()
+nothingIfOk status =
+    case status of
+        Success -> return  ()
+        _       -> throwIO (ExitCode status)
+
diff --git a/Foreign/CUDA/Runtime/Event.chs b/Foreign/CUDA/Runtime/Event.chs
new file mode 100644
--- /dev/null
+++ b/Foreign/CUDA/Runtime/Event.chs
@@ -0,0 +1,133 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+--------------------------------------------------------------------------------
+-- |
+-- Module    : Foreign.CUDA.Driver.Event
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Event management for C-for-CUDA runtime environment
+--
+--------------------------------------------------------------------------------
+
+module Foreign.CUDA.Runtime.Event
+  (
+    Event, EventFlag(..),
+    create, destroy, elapsedTime, query, record, block
+  )
+  where
+
+#include <cuda_runtime_api.h>
+{# context lib="cudart" #}
+
+-- Friends
+import Foreign.CUDA.Runtime.Error
+import Foreign.CUDA.Runtime.Stream                      (Stream(..))
+import Foreign.CUDA.Internal.C2HS
+
+-- System
+import Foreign
+import Foreign.C
+import Control.Monad                                    (liftM)
+
+
+#c
+typedef enum cudaEvent_option_enum {
+//  CUDA_EVENT_OPTION_DEFAULT       = cuduEventDefault,
+    CUDA_EVENT_OPTION_BLOCKING_SYNC = cudaEventBlockingSync
+} cudaEvent_option;
+#endc
+
+--------------------------------------------------------------------------------
+-- Data Types
+--------------------------------------------------------------------------------
+
+-- |
+-- Events
+--
+newtype Event = Event { useEvent :: {# type cudaEvent_t #}}
+
+-- |
+-- Event creation flags
+--
+{# enum cudaEvent_option_enum as EventFlag
+    { underscoreToCase }
+    with prefix="CUDA_EVENT_OPTION" deriving (Eq,Show) #}
+
+
+--------------------------------------------------------------------------------
+-- Event management
+--------------------------------------------------------------------------------
+
+-- |
+-- Create a new event
+--
+create :: [EventFlag] -> IO Event
+create flags = resultIfOk =<< cudaEventCreateWithFlags flags
+
+{# fun unsafe cudaEventCreateWithFlags
+  { alloca-         `Event'       peekEvt*
+  , combineBitMasks `[EventFlag]'          } -> `Status' cToEnum #}
+  where peekEvt = liftM Event . peek
+
+
+-- |
+-- Destroy an event
+--
+destroy :: Event -> IO ()
+destroy ev = nothingIfOk =<< cudaEventDestroy ev
+
+{# fun unsafe cudaEventDestroy
+  { useEvent `Event' } -> `Status' cToEnum #}
+
+
+-- |
+-- Determine the elapsed time (in milliseconds) between two events
+--
+elapsedTime :: Event -> Event -> IO Float
+elapsedTime ev1 ev2 = resultIfOk =<< cudaEventElapsedTime ev1 ev2
+
+{# fun unsafe cudaEventElapsedTime
+  { alloca-  `Float' peekFloatConv*
+  , useEvent `Event'
+  , useEvent `Event'                } -> `Status' cToEnum #}
+
+
+-- |
+-- Determines if a event has actually been recorded
+--
+query :: Event -> IO Bool
+query ev =
+  cudaEventQuery ev >>= \rv ->
+  case rv of
+    Success  -> return True
+    NotReady -> return False
+    _        -> resultIfOk (rv,undefined)
+
+{# fun unsafe cudaEventQuery
+  { useEvent `Event' } -> `Status' cToEnum #}
+
+
+-- |
+-- Record an event once all operations in the current context (or optionally
+-- specified stream) have completed. This operation is asynchronous.
+--
+record :: Event -> Maybe Stream -> IO ()
+record ev mst =
+  nothingIfOk =<< case mst of
+    Just st -> cudaEventRecord ev st
+    Nothing -> cudaEventRecord ev (Stream 0)
+
+{# fun unsafe cudaEventRecord
+  { useEvent  `Event'
+  , useStream `Stream' } -> `Status' cToEnum #}
+
+
+-- |
+-- Wait until the event has been recorded
+--
+block :: Event -> IO ()
+block ev = nothingIfOk =<< cudaEventSynchronize ev
+
+{# fun unsafe cudaEventSynchronize
+  { useEvent `Event' } -> `Status' cToEnum #}
+
diff --git a/Foreign/CUDA/Runtime/Exec.chs b/Foreign/CUDA/Runtime/Exec.chs
new file mode 100644
--- /dev/null
+++ b/Foreign/CUDA/Runtime/Exec.chs
@@ -0,0 +1,180 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+--------------------------------------------------------------------------------
+-- |
+-- Module    : Foreign.CUDA.Runtime.Exec
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Kernel execution control for C-for-CUDA runtime interface
+--
+--------------------------------------------------------------------------------
+
+module Foreign.CUDA.Runtime.Exec
+  (
+    FunAttributes(..), FunParam(..),
+    attributes, setConfig, setParams, launch
+  )
+  where
+
+#include "cbits/stubs.h"
+#include <cuda_runtime_api.h>
+{# context lib="cudart" #}
+
+-- Friends
+import Foreign.CUDA.Runtime.Stream
+import Foreign.CUDA.Runtime.Error
+import Foreign.CUDA.Internal.C2HS
+
+-- System
+import Foreign
+import Foreign.C
+import Control.Monad
+
+#c
+typedef struct cudaFuncAttributes cudaFuncAttributes;
+#endc
+
+
+--------------------------------------------------------------------------------
+-- Data Types
+--------------------------------------------------------------------------------
+
+--
+-- Function Attributes
+--
+{# pointer *cudaFuncAttributes as ^ foreign -> FunAttributes nocode #}
+
+data FunAttributes = FunAttributes
+  {
+    constSizeBytes           :: Int64,
+    localSizeBytes           :: Int64,
+    sharedSizeBytes          :: Int64,
+    maxKernelThreadsPerBlock :: Int,	-- ^ maximum block size that can be successively launched (based on register usage)
+    numRegs                  :: Int     -- ^ number of registers required for each thread
+  }
+  deriving (Show)
+
+instance Storable FunAttributes where
+  sizeOf _    = {# sizeof cudaFuncAttributes #}
+  alignment _ = alignment (undefined :: Ptr ())
+
+  peek p      = do
+    cs <- cIntConv `fmap` {#get cudaFuncAttributes.constSizeBytes#} p
+    ls <- cIntConv `fmap` {#get cudaFuncAttributes.localSizeBytes#} p
+    ss <- cIntConv `fmap` {#get cudaFuncAttributes.sharedSizeBytes#} p
+    tb <- cIntConv `fmap` {#get cudaFuncAttributes.maxThreadsPerBlock#} p
+    nr <- cIntConv `fmap` {#get cudaFuncAttributes.numRegs#} p
+
+    return FunAttributes
+      {
+        constSizeBytes           = cs,
+        localSizeBytes           = ls,
+        sharedSizeBytes          = ss,
+        maxKernelThreadsPerBlock = tb,
+        numRegs                  = nr
+      }
+
+-- |
+-- Kernel function parameters. Doubles will be converted to an internal float
+-- representation on devices that do not support doubles natively.
+--
+data Storable a => FunParam a
+    = IArg Int
+    | FArg Float
+    | DArg Double
+    | VArg a
+
+--------------------------------------------------------------------------------
+-- Execution Control
+--------------------------------------------------------------------------------
+
+-- |
+-- Obtain the attributes of the named @__global__@ device function. This
+-- itemises the requirements to successfully launch the given kernel.
+--
+attributes :: String -> IO FunAttributes
+attributes fn = resultIfOk =<< cudaFuncGetAttributes fn
+
+{# fun unsafe cudaFuncGetAttributes
+  { alloca-      `FunAttributes' peek*
+  , withCString* `String'              } -> `Status' cToEnum #}
+
+
+-- |
+-- Specify the grid and block dimensions for a device call. Used in conjunction
+-- with 'setParams', this pushes data onto the execution stack that will be
+-- popped when a function is 'launch'ed.
+--
+setConfig :: (Int,Int)		-- ^ grid dimensions
+	  -> (Int,Int,Int)	-- ^ block dimensions
+	  -> Int64		-- ^ shared memory per block (bytes)
+	  -> Maybe Stream	-- ^ associated processing stream
+	  -> IO ()
+setConfig (gx,gy) (bx,by,bz) sharedMem mst =
+  nothingIfOk =<< case mst of
+    Nothing -> cudaConfigureCallSimple gx gy bx by bz sharedMem (Stream 0)
+    Just st -> cudaConfigureCallSimple gx gy bx by bz sharedMem st
+
+--
+-- The FFI does not support passing deferenced structures to C functions, as
+-- this is highly platform/compiler dependent. Wrap our own function stub
+-- accepting plain integers.
+--
+{# fun unsafe cudaConfigureCallSimple
+  {	      `Int', `Int'
+  ,           `Int', `Int', `Int'
+  , cIntConv  `Int64'
+  , useStream `Stream'            } -> `Status' cToEnum #}
+
+
+-- |
+-- Set the argument parameters that will be passed to the next kernel
+-- invocation. This is used in conjunction with 'setConfig' to control kernel
+-- execution.
+setParams :: Storable a => [FunParam a] -> IO ()
+setParams = foldM_ k 0
+  where
+    k offset arg = do
+      let s = size arg
+      set arg s offset >>= nothingIfOk
+      return (offset + s)
+
+    size (IArg _) = sizeOf (undefined :: Int)
+    size (FArg _) = sizeOf (undefined :: Float)
+    size (DArg _) = sizeOf (undefined :: Double)
+    size (VArg a) = sizeOf a
+
+    set (IArg v) s o = cudaSetupArgument v s o
+    set (FArg v) s o = cudaSetupArgument v s o
+    set (VArg v) s o = cudaSetupArgument v s o
+    set (DArg v) s o =
+      cudaSetDoubleForDevice v >>= resultIfOk >>= \d ->
+      cudaSetupArgument d s o
+
+
+{# fun unsafe cudaSetupArgument
+  `Storable a' =>
+  { with'* `a'
+  ,        `Int'
+  ,        `Int'   } -> `Status' cToEnum #}
+  where
+    with' v a = with v $ \p -> a (castPtr p)
+
+{# fun unsafe cudaSetDoubleForDevice
+  { with'* `Double' peek'* } -> `Status' cToEnum #}
+  where
+    with' v a = with v $ \p -> a (castPtr p)
+    peek'     = peek . castPtr
+
+
+-- |
+-- Invoke the named kernel on the device, which must have been declared
+-- @__global__@. This must be preceded by a call to 'setConfig' and (if
+-- appropriate) 'setParams'.
+--
+launch :: String -> IO ()
+launch fn = nothingIfOk =<< cudaLaunch fn
+
+{# fun unsafe cudaLaunch
+  { withCString* `String' } -> `Status' cToEnum #}
+
diff --git a/Foreign/CUDA/Runtime/Marshal.chs b/Foreign/CUDA/Runtime/Marshal.chs
new file mode 100644
--- /dev/null
+++ b/Foreign/CUDA/Runtime/Marshal.chs
@@ -0,0 +1,359 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+--------------------------------------------------------------------------------
+-- |
+-- Module    : Foreign.CUDA.Runtime.Marshal
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Memory management for CUDA devices
+--
+--------------------------------------------------------------------------------
+
+module Foreign.CUDA.Runtime.Marshal
+  (
+    -- * Host Allocation
+    AllocFlag(..),
+    mallocHostArray, freeHost,
+
+    -- * Device Allocation
+    mallocArray, allocaArray, free,
+
+    -- * Marshalling
+    peekArray, peekArrayAsync, peekListArray,
+    pokeArray, pokeArrayAsync, pokeListArray,
+    copyArray, copyArrayAsync,
+
+    -- * Combined Allocation and Marshalling
+    newListArray, withListArray, withListArrayLen,
+
+    -- * Utility
+    memset
+  )
+  where
+
+#include <cuda_runtime_api.h>
+{# context lib="cudart" #}
+
+-- Friends
+import Foreign.CUDA.Runtime.Ptr
+import Foreign.CUDA.Runtime.Error
+import Foreign.CUDA.Runtime.Stream
+import Foreign.CUDA.Internal.C2HS
+
+-- System
+import Data.Int
+import Control.Exception.Extensible
+
+import Foreign.C
+import Foreign.Ptr
+import Foreign.Storable
+import qualified Foreign.Marshal as F
+
+#c
+typedef enum cudaMemHostAlloc_option_enum {
+//  CUDA_MEMHOSTALLOC_OPTION_DEFAULT        = cudaHostAllocDefault,
+    CUDA_MEMHOSTALLOC_OPTION_DEVICE_MAPPED  = cudaHostAllocMapped,
+    CUDA_MEMHOSTALLOC_OPTION_PORTABLE       = cudaHostAllocPortable,
+    CUDA_MEMHOSTALLOC_OPTION_WRITE_COMBINED = cudaHostAllocWriteCombined
+} cudaMemHostAlloc_option;
+#endc
+
+
+--------------------------------------------------------------------------------
+-- Host Allocation
+--------------------------------------------------------------------------------
+
+-- |
+-- Options for host allocation
+--
+{# enum cudaMemHostAlloc_option as AllocFlag
+    { underscoreToCase }
+    with prefix="CUDA_MEMHOSTALLOC_OPTION" deriving (Eq, Show) #}
+
+
+-- |
+-- Allocate a section of linear memory on the host which is page-locked and
+-- directly accessible from the device. The storage is sufficient to hold the
+-- given number of elements of a storable type. The runtime system automatically
+-- accelerates calls to functions such as 'memcpy' to page-locked memory.
+--
+-- Note that since the amount of pageable memory is thusly reduced, overall
+-- system performance may suffer. This is best used sparingly to allocate
+-- staging areas for data exchange
+--
+mallocHostArray :: Storable a => [AllocFlag] -> Int -> IO (HostPtr a)
+mallocHostArray flags = doMalloc undefined
+  where
+    doMalloc :: Storable a' => a' -> Int -> IO (HostPtr a')
+    doMalloc x n = resultIfOk =<< cudaHostAlloc (fromIntegral n * fromIntegral (sizeOf x)) flags
+
+{# fun unsafe cudaHostAlloc
+  { alloca'-        `HostPtr a' hptr*
+  , cIntConv        `Int64'
+  , combineBitMasks `[AllocFlag]'     } -> `Status' cToEnum #}
+  where
+    alloca' = F.alloca
+    hptr p  = (HostPtr . castPtr) `fmap` peek p
+
+
+-- |
+-- Free page-locked host memory previously allocated with 'mallecHost'
+--
+freeHost :: HostPtr a -> IO ()
+freeHost p = nothingIfOk =<< cudaFreeHost p
+
+{# fun unsafe cudaFreeHost
+  { hptr `HostPtr a' } -> `Status' cToEnum #}
+  where hptr = castPtr . useHostPtr
+
+
+--------------------------------------------------------------------------------
+-- Device Allocation
+--------------------------------------------------------------------------------
+
+-- |
+-- Allocate a section of linear memory on the device, and return a reference to
+-- it. The memory is sufficient to hold the given number of elements of storable
+-- type. It is suitable aligned, and not cleared.
+--
+mallocArray :: Storable a => Int -> IO (DevicePtr a)
+mallocArray = doMalloc undefined
+  where
+    doMalloc :: Storable a' => a' -> Int -> IO (DevicePtr a')
+    doMalloc x n = resultIfOk =<< cudaMalloc (fromIntegral n * fromIntegral (sizeOf x))
+
+{# fun unsafe cudaMalloc
+  { alloca'- `DevicePtr a' dptr*
+  , cIntConv `Int64'             } -> `Status' cToEnum #}
+  where
+    -- C-> Haskell doesn't like qualified imports in marshaller specifications
+    alloca' = F.alloca
+    dptr p  = (castDevPtr . DevicePtr) `fmap` peek p
+
+
+-- |
+-- Execute a computation, passing a pointer to a temporarily allocated block of
+-- memory sufficient to hold the given number of elements of storable type. The
+-- memory is freed when the computation terminates (normally or via an
+-- exception), so the pointer must not be used after this.
+--
+-- Note that kernel launches can be asynchronous, so you may need to add a
+-- synchronisation point at the end of the computation.
+--
+allocaArray :: Storable a => Int -> (DevicePtr a -> IO b) -> IO b
+allocaArray n = bracket (mallocArray n) free
+
+
+-- |
+-- Free previously allocated memory on the device
+--
+free :: DevicePtr a -> IO ()
+free p = nothingIfOk =<< cudaFree p
+
+{# fun unsafe cudaFree
+  { dptr `DevicePtr a' } -> `Status' cToEnum #}
+  where
+    dptr = useDevicePtr . castDevPtr
+
+
+--------------------------------------------------------------------------------
+-- Marshalling
+--------------------------------------------------------------------------------
+
+-- |
+-- Copy a number of elements from the device to host memory. This is a
+-- synchronous operation.
+--
+peekArray :: Storable a => Int -> DevicePtr a -> Ptr a -> IO ()
+peekArray n dptr hptr = memcpy hptr (useDevicePtr dptr) n DeviceToHost
+
+
+-- |
+-- Copy memory from the device asynchronously, possibly associated with a
+-- particular stream. The destination memory must be page locked.
+--
+peekArrayAsync :: Storable a => Int -> DevicePtr a -> HostPtr a -> Maybe Stream -> IO ()
+peekArrayAsync n dptr hptr mst =
+  memcpyAsync (useHostPtr hptr) (useDevicePtr dptr) n DeviceToHost mst
+
+
+-- |
+-- Copy a number of elements from the device into a new Haskell list. Note that
+-- this requires two memory copies: firstly from the device into a heap
+-- allocated array, and from there marshalled into a list
+--
+peekListArray :: Storable a => Int -> DevicePtr a -> IO [a]
+peekListArray n dptr =
+  F.allocaArray n $ \p -> do
+    peekArray   n dptr p
+    F.peekArray n p
+
+
+-- |
+-- Copy a number of elements onto the device. This is a synchronous operation.
+--
+pokeArray :: Storable a => Int -> Ptr a -> DevicePtr a -> IO ()
+pokeArray n hptr dptr = memcpy (useDevicePtr dptr) hptr n HostToDevice
+
+
+-- |
+-- Copy memory onto the device asynchronously, possibly associated with a
+-- particular stream. The source memory must be page-locked.
+--
+pokeArrayAsync :: Storable a => Int -> HostPtr a -> DevicePtr a -> Maybe Stream -> IO ()
+pokeArrayAsync n hptr dptr mst =
+  memcpyAsync (useDevicePtr dptr) (useHostPtr hptr) n HostToDevice mst
+
+
+-- |
+-- Write a list of storable elements into a device array. The array must be
+-- sufficiently large to hold the entire list. This requires two marshalling
+-- operations
+--
+pokeListArray :: Storable a => [a] -> DevicePtr a -> IO ()
+pokeListArray xs dptr = F.withArrayLen xs $ \len p -> pokeArray len p dptr
+
+
+-- |
+-- Copy the given number of elements from the first device array (source) to the
+-- second (destination). The copied areas may not overlap. This is a synchronous
+-- operation.
+--
+copyArray :: Storable a => Int -> DevicePtr a -> DevicePtr a -> IO ()
+copyArray n src dst = memcpy (useDevicePtr dst) (useDevicePtr src) n DeviceToDevice
+
+
+-- |
+-- Copy the given number of elements from the first device array (source) to the
+-- second (destination). The copied areas may not overlap. This operation is
+-- asynchronous with respect to host, but will never overlap with kernel
+-- execution.
+--
+copyArrayAsync :: Storable a => Int -> DevicePtr a -> DevicePtr a -> Maybe Stream -> IO ()
+copyArrayAsync n src dst mst =
+  memcpyAsync (useDevicePtr dst) (useDevicePtr src) n DeviceToDevice mst
+
+
+--
+-- Memory copy kind
+--
+{# enum cudaMemcpyKind as CopyDirection {}
+    with prefix="cudaMemcpy" deriving (Eq, Show) #}
+
+-- |
+-- Copy data between host and device. This is a synchronous operation.
+--
+memcpy :: Storable a
+       => Ptr a                 -- ^ destination
+       -> Ptr a                 -- ^ source
+       -> Int                   -- ^ number of elements
+       -> CopyDirection
+       -> IO ()
+memcpy dst src n dir = doMemcpy undefined dst
+  where
+    doMemcpy :: Storable a' => a' -> Ptr a' -> IO ()
+    doMemcpy x _ =
+      nothingIfOk =<< cudaMemcpy dst src (fromIntegral n * fromIntegral (sizeOf x)) dir
+
+{# fun unsafe cudaMemcpy
+  { castPtr   `Ptr a'
+  , castPtr   `Ptr a'
+  , cIntConv  `Int64'
+  , cFromEnum `CopyDirection' } -> `Status' cToEnum #}
+
+
+-- |
+-- Copy data between the host and device asynchronously, possibly associated
+-- with a particular stream. The host-side memory must be page-locked (allocated
+-- with 'mallocHostArray').
+--
+memcpyAsync :: Storable a
+            => Ptr a            -- ^ destination
+            -> Ptr a            -- ^ source
+            -> Int              -- ^ number of elements
+            -> CopyDirection
+            -> Maybe Stream
+            -> IO ()
+memcpyAsync dst src n kind mst = doMemcpy undefined dst
+  where
+    doMemcpy :: Storable a' => a' -> Ptr a' -> IO ()
+    doMemcpy x _ =
+      let bytes = fromIntegral n * fromIntegral (sizeOf x) in
+      nothingIfOk =<< case mst of
+        Nothing -> cudaMemcpyAsync dst src bytes kind (Stream 0)
+        Just st -> cudaMemcpyAsync dst src bytes kind st
+
+{# fun unsafe cudaMemcpyAsync
+  { castPtr   `Ptr a'
+  , castPtr   `Ptr a'
+  , cIntConv  `Int64'
+  , cFromEnum `CopyDirection'
+  , useStream `Stream'        } -> `Status' cToEnum #}
+
+
+--------------------------------------------------------------------------------
+-- Combined Allocation and Marshalling
+--------------------------------------------------------------------------------
+
+-- |
+-- Write a list of storable elements into a newly allocated device array. Note
+-- that this requires two copy operations: firstly from a Haskell list into a
+-- heap-allocated array, and from there into device memory. The array should be
+-- 'free'd when no longer required.
+--
+newListArray :: Storable a => [a] -> IO (DevicePtr a)
+newListArray xs =
+  F.withArrayLen xs                     $ \len p ->
+  bracketOnError (mallocArray len) free $ \d_xs  -> do
+    pokeArray len p d_xs
+    return d_xs
+
+
+-- |
+-- Temporarily store a list of elements into a newly allocated device array. An
+-- IO action is applied to the array, the result of which is returned. Similar
+-- to 'newListArray', this requires two marshalling operations of the data.
+--
+-- As with 'allocaArray', the memory is freed once the action completes, so you
+-- should not return the pointer from the action, and be sure that any
+-- asynchronous operations (such as kernel execution) have completed.
+--
+withListArray :: Storable a => [a] -> (DevicePtr a -> IO b) -> IO b
+withListArray xs = withListArrayLen xs . const
+
+
+-- |
+-- A variant of 'withListArray' which also supplies the number of elements in
+-- the array to the applied function
+--
+withListArrayLen :: Storable a => [a] -> (Int -> DevicePtr a -> IO b) -> IO b
+withListArrayLen xs f =
+  allocaArray   len $ \d_xs -> do
+  F.allocaArray len $ \h_xs -> do
+    F.pokeArray h_xs xs
+    pokeArray len h_xs d_xs
+  f len d_xs
+  where
+    len = length xs
+
+
+--------------------------------------------------------------------------------
+-- Utility
+--------------------------------------------------------------------------------
+
+-- |
+-- Initialise device memory to a given 8-bit value
+--
+memset :: DevicePtr a                   -- ^ The device memory
+       -> Int64                         -- ^ Number of bytes
+       -> Int8                          -- ^ Value to set for each byte
+       -> IO ()
+memset dptr bytes symbol = nothingIfOk =<< cudaMemset dptr symbol bytes
+
+{# fun unsafe cudaMemset
+  { dptr     `DevicePtr a'
+  , cIntConv `Int8'
+  , cIntConv `Int64'       } -> `Status' cToEnum #}
+  where
+    dptr = useDevicePtr . castDevPtr
+
diff --git a/Foreign/CUDA/Runtime/Ptr.hs b/Foreign/CUDA/Runtime/Ptr.hs
new file mode 100644
--- /dev/null
+++ b/Foreign/CUDA/Runtime/Ptr.hs
@@ -0,0 +1,161 @@
+--------------------------------------------------------------------------------
+-- |
+-- Module    : Foreign.CUDA.Runtime.Ptr
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- References to objects stored on the CUDA devices
+--
+--------------------------------------------------------------------------------
+
+module Foreign.CUDA.Runtime.Ptr
+  where
+
+-- System
+import Foreign.Ptr
+import Foreign.Storable
+
+
+--------------------------------------------------------------------------------
+-- Device Pointer
+--------------------------------------------------------------------------------
+
+-- |
+-- A reference to data stored on the device
+--
+data DevicePtr a = DevicePtr { useDevicePtr :: Ptr a }
+  deriving (Eq,Ord)
+
+instance Show (DevicePtr a) where
+  showsPrec n (DevicePtr p) = showsPrec n p
+
+instance Storable (DevicePtr a) where
+  sizeOf _    = sizeOf    (undefined :: Ptr a)
+  alignment _ = alignment (undefined :: Ptr a)
+  peek p      = DevicePtr `fmap` peek (castPtr p)
+  poke p v    = poke (castPtr p) (useDevicePtr v)
+
+
+-- |
+-- Look at the contents of device memory. This takes an IO action that will be
+-- applied to that pointer, the result of which is returned. It would be silly
+-- to return the pointer from the action.
+--
+withDevicePtr :: DevicePtr a -> (Ptr a -> IO b) -> IO b
+withDevicePtr p f = f (useDevicePtr p)
+
+
+-- |
+-- The constant 'nullDevPtr' contains the distinguished memory location that is
+-- not associated with a valid memory location
+--
+nullDevPtr :: DevicePtr a
+nullDevPtr =  DevicePtr nullPtr
+
+-- |
+-- Cast a device pointer from one type to another
+--
+castDevPtr :: DevicePtr a -> DevicePtr b
+castDevPtr (DevicePtr p) = DevicePtr (castPtr p)
+
+-- |
+-- Advance the pointer address by the given offset in bytes.
+--
+plusDevPtr :: DevicePtr a -> Int -> DevicePtr a
+plusDevPtr (DevicePtr p) d = DevicePtr (p `plusPtr` d)
+
+-- |
+-- Given an alignment constraint, align the device pointer to the next highest
+-- address satisfying the constraint
+--
+alignDevPtr :: DevicePtr a -> Int -> DevicePtr a
+alignDevPtr (DevicePtr p) i = DevicePtr (p `alignPtr` i)
+
+-- |
+-- Compute the difference between the second and first argument. This fulfils
+-- the relation
+--
+-- > p2 == p1 `plusDevPtr` (p2 `minusDevPtr` p1)
+--
+minusDevPtr :: DevicePtr a -> DevicePtr a -> Int
+minusDevPtr (DevicePtr a) (DevicePtr b) = a `minusPtr` b
+
+-- |
+-- Advance a pointer into a device array by the given number of elements
+--
+advanceDevPtr :: Storable a => DevicePtr a -> Int -> DevicePtr a
+advanceDevPtr  = doAdvance undefined
+  where
+    doAdvance :: Storable a' => a' -> DevicePtr a' -> Int -> DevicePtr a'
+    doAdvance x p i = p `plusDevPtr` (i * sizeOf x)
+
+
+--------------------------------------------------------------------------------
+-- Host Pointer
+--------------------------------------------------------------------------------
+
+-- |
+-- A reference to page-locked host memory
+--
+data HostPtr a = HostPtr { useHostPtr :: Ptr a }
+  deriving (Eq,Ord)
+
+instance Show (HostPtr a) where
+  showsPrec n (HostPtr p) = showsPrec n p
+
+instance Storable (HostPtr a) where
+  sizeOf _    = sizeOf    (undefined :: Ptr a)
+  alignment _ = alignment (undefined :: Ptr a)
+  peek p      = HostPtr `fmap` peek (castPtr p)
+  poke p v    = poke (castPtr p) (useHostPtr v)
+
+
+-- |
+-- Apply an IO action to the memory reference living inside the host pointer
+-- object. All uses of the pointer should be inside the 'withHostPtr' bracket.
+--
+withHostPtr :: HostPtr a -> (Ptr a -> IO b) -> IO b
+withHostPtr p f = f (useHostPtr p)
+
+
+-- |
+-- The constant 'nullHostPtr' contains the distinguished memory location that is
+-- not associated with a valid memory location
+--
+nullHostPtr :: HostPtr a
+nullHostPtr =  HostPtr nullPtr
+
+-- |
+-- Cast a host pointer from one type to another
+--
+castHostPtr :: HostPtr a -> HostPtr b
+castHostPtr (HostPtr p) = HostPtr (castPtr p)
+
+-- |
+-- Advance the pointer address by the given offset in bytes
+--
+plusHostPtr :: HostPtr a -> Int -> HostPtr a
+plusHostPtr (HostPtr p) d = HostPtr (p `plusPtr` d)
+
+-- |
+-- Given an alignment constraint, align the host pointer to the next highest
+-- address satisfying the constraint
+--
+alignHostPtr :: HostPtr a -> Int -> HostPtr a
+alignHostPtr (HostPtr p) i = HostPtr (p `alignPtr` i)
+
+-- |
+-- Compute the difference between the second and first argument
+--
+minusHostPtr :: HostPtr a -> HostPtr a -> Int
+minusHostPtr (HostPtr a) (HostPtr b) = a `minusPtr` b
+
+-- |
+-- Advance a pointer into a host array by a given number of elements
+--
+advanceHostPtr :: Storable a => HostPtr a -> Int -> HostPtr a
+advanceHostPtr  = doAdvance undefined
+  where
+    doAdvance :: Storable a' => a' -> HostPtr a' -> Int -> HostPtr a'
+    doAdvance x p i = p `plusHostPtr` (i * sizeOf x)
+
diff --git a/Foreign/CUDA/Runtime/Stream.chs b/Foreign/CUDA/Runtime/Stream.chs
new file mode 100644
--- /dev/null
+++ b/Foreign/CUDA/Runtime/Stream.chs
@@ -0,0 +1,94 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+--------------------------------------------------------------------------------
+-- |
+-- Module    : Foreign.CUDA.Runtime.Stream
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Stream management routines
+--
+--------------------------------------------------------------------------------
+
+
+module Foreign.CUDA.Runtime.Stream
+  (
+    Stream(..),
+
+    -- ** Stream management
+    create, destroy, finished, block
+  )
+  where
+
+#include <cuda_runtime_api.h>
+{# context lib="cudart" #}
+
+-- Friends
+import Foreign.CUDA.Runtime.Error
+import Foreign.CUDA.Internal.C2HS
+
+-- System
+import Foreign
+import Foreign.C
+import Control.Monad                                    (liftM)
+
+
+--------------------------------------------------------------------------------
+-- Data Types
+--------------------------------------------------------------------------------
+
+-- |
+-- A processing stream
+--
+newtype Stream = Stream { useStream :: {# type cudaStream_t #}}
+  deriving (Show)
+
+
+--------------------------------------------------------------------------------
+-- Functions
+--------------------------------------------------------------------------------
+
+-- |
+-- Create a new asynchronous stream
+--
+create :: IO Stream
+create = resultIfOk =<< cudaStreamCreate
+
+{# fun unsafe cudaStreamCreate
+  { alloca- `Stream' peekStream* } -> `Status' cToEnum #}
+  where peekStream = liftM Stream . peekIntConv
+
+
+-- |
+-- Destroy and clean up an asynchronous stream
+--
+destroy :: Stream -> IO ()
+destroy s = nothingIfOk =<< cudaStreamDestroy s
+
+{# fun unsafe cudaStreamDestroy
+  { useStream `Stream' } -> `Status' cToEnum #}
+
+
+-- |
+-- Determine if all operations in a stream have completed
+--
+finished   :: Stream -> IO Bool
+finished s =
+  cudaStreamQuery s >>= \rv -> do
+  case rv of
+      Success  -> return True
+      NotReady -> return False
+      _        -> resultIfOk (rv,undefined)
+
+{# fun unsafe cudaStreamQuery
+  { useStream `Stream' } -> `Status' cToEnum #}
+
+
+-- |
+-- Block until all operations in a Stream have been completed
+--
+block :: Stream -> IO ()
+block s = nothingIfOk =<< cudaStreamSynchronize s
+
+{# fun unsafe cudaStreamSynchronize
+  { useStream `Stream' } -> `Status' cToEnum #}
+
diff --git a/Foreign/CUDA/Runtime/Thread.chs b/Foreign/CUDA/Runtime/Thread.chs
new file mode 100644
--- /dev/null
+++ b/Foreign/CUDA/Runtime/Thread.chs
@@ -0,0 +1,55 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+--------------------------------------------------------------------------------
+-- |
+-- Module    : Foreign.CUDA.Runtime.Thread
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Thread management routines
+--
+--------------------------------------------------------------------------------
+
+
+module Foreign.CUDA.Runtime.Thread
+  (
+    sync, exit
+  )
+  where
+
+#include <cuda_runtime_api.h>
+{# context lib="cudart" #}
+
+-- Friends
+import Foreign.CUDA.Runtime.Error
+import Foreign.CUDA.Internal.C2HS
+
+-- System
+import Foreign.C
+
+
+--------------------------------------------------------------------------------
+-- Thread management
+--------------------------------------------------------------------------------
+
+-- |
+-- Block until the device has completed all preceding requested tasks. Returns
+-- an error if one of the tasks fails.
+--
+sync :: IO ()
+sync =  nothingIfOk =<< cudaThreadSynchronize
+
+{# fun unsafe cudaThreadSynchronize
+  { } -> `Status' cToEnum #}
+
+
+-- |
+-- Explicitly clean up all runtime related resources associated with the calling
+-- host thread. Any subsequent API call re-initialised the environment.
+-- Implicitly called on thread exit.
+--
+exit :: IO ()
+exit =  nothingIfOk =<< cudaThreadExit
+
+{# fun unsafe cudaThreadExit
+  { } -> `Status' cToEnum #}
+
diff --git a/Foreign/CUDA/Runtime/Utils.chs b/Foreign/CUDA/Runtime/Utils.chs
new file mode 100644
--- /dev/null
+++ b/Foreign/CUDA/Runtime/Utils.chs
@@ -0,0 +1,48 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+--------------------------------------------------------------------------------
+-- |
+-- Module    : Foreign.CUDA.Runtime.Utils
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Utility functions
+--
+--------------------------------------------------------------------------------
+
+module Foreign.CUDA.Runtime.Utils
+  (
+    runtimeVersion, driverVersion
+  )
+  where
+
+#include <cuda_runtime_api.h>
+{# context lib="cudart" #}
+
+-- Friends
+import Foreign.CUDA.Runtime.Error
+import Foreign.CUDA.Internal.C2HS
+
+-- System
+import Foreign
+import Foreign.C
+
+
+-- |
+-- Return the version number of the installed CUDA driver
+--
+runtimeVersion :: IO Int
+runtimeVersion =  resultIfOk =<< cudaRuntimeGetVersion
+
+{# fun unsafe cudaRuntimeGetVersion
+  { alloca- `Int' peekIntConv* } -> `Status' cToEnum #}
+
+
+-- |
+-- Return the version number of the installed CUDA runtime
+--
+driverVersion :: IO Int
+driverVersion =  resultIfOk =<< cudaDriverGetVersion
+
+{# fun unsafe cudaDriverGetVersion
+  { alloca- `Int' peekIntConv* } -> `Status' cToEnum #}
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2009 Trevor L. McDonell, University of New South Wales.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of the University of New South Wales nor the
+      names of its contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,4 @@
+import Distribution.Simple
+
+main :: IO ()
+main =  defaultMain
diff --git a/cbits/stubs.c b/cbits/stubs.c
new file mode 100644
--- /dev/null
+++ b/cbits/stubs.c
@@ -0,0 +1,16 @@
+/*
+ * Extra bits for CUDA binding
+ */
+
+#include "cbits/stubs.h"
+
+
+cudaError_t
+cudaConfigureCallSimple(int gx, int gy, int bx, int by, int bz, size_t sharedMem, cudaStream_t stream)
+{
+    dim3 gridDim  = {gx,gy,1};
+    dim3 blockDim = {bx,by,bz};
+
+    return cudaConfigureCall(gridDim, blockDim, sharedMem, stream);
+}
+
diff --git a/cbits/stubs.h b/cbits/stubs.h
new file mode 100644
--- /dev/null
+++ b/cbits/stubs.h
@@ -0,0 +1,21 @@
+/*
+ * Extra bits for CUDA bindings
+ */
+
+#ifndef C_STUBS_H
+#define C_STUBS_H
+
+#include <cuda_runtime_api.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+cudaError_t
+cudaConfigureCallSimple(int gx, int gy, int bx, int by, int bz, size_t sharedMem, cudaStream_t stream);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/configure b/configure
new file mode 100644
--- /dev/null
+++ b/configure
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+# substitute standard header path variables
+if test -n "$CPPFLAGS" ; then
+    echo "Found CPPFLAGS in environment: '$CPPFLAGS'"
+    sed 's,@CPPFLAGS@,'"$CPPFLAGS"',g;s,@LDFLAGS@,'"$LDFLAGS"',g'  \
+        < cuda.buildinfo.in > cuda.buildinfo
+fi
+
diff --git a/cuda.buildinfo.in b/cuda.buildinfo.in
new file mode 100644
--- /dev/null
+++ b/cuda.buildinfo.in
@@ -0,0 +1,3 @@
+ghc-options: -optc@CPPFLAGS@
+cc-options:  @CPPFLAGS@
+ld-options:  @LDFLAGS@
diff --git a/cuda.cabal b/cuda.cabal
new file mode 100644
--- /dev/null
+++ b/cuda.cabal
@@ -0,0 +1,67 @@
+Name:                   cuda
+Version:                0.1
+Synopsis:               A binding to the CUDA interface for programming NVIDIA GPUs
+Description:    
+    The CUDA library provides a direct, general purpose C-like SPMD programming
+    model for NVIDIA graphics cards (G8x series onwards). This is a collection
+    of bindings to allow you to call and control, although not write, such
+    functions from Haskell land. You will need to install the CUDA driver and
+    developer toolkit (tested with v2.3).
+    .
+    <http://developer.nvidia.com/object/cuda.html>
+    .
+    Note that on Snow Leopard, the c2hs preprocessor is confused by the notation
+    for Apple's Blocks extension, so to work around this:
+    .
+    > cabal install --c2hs-option=-ccpp-4.0
+    .
+
+License:                BSD3
+License-file:           LICENSE
+Copyright:              Copyright (c) 2009-10. Trevor L. McDonell <tmcdonell@cse.unsw.edu.au>
+Author:                 Trevor L. McDonell <tmcdonell@cse.unsw.edu.au>
+Maintainer:             Trevor L. McDonell <tmcdonell@cse.unsw.edu.au>
+Category:               Foreign
+Cabal-version:          >=1.2
+
+Build-type:             Configure
+Extra-tmp-files:        cuda.buildinfo
+Extra-source-files:     configure
+                        cuda.buildinfo.in
+
+Library
+  Exposed-Modules:      Foreign.CUDA
+                        Foreign.CUDA.Runtime
+                        Foreign.CUDA.Runtime.Device
+                        Foreign.CUDA.Runtime.Error
+                        Foreign.CUDA.Runtime.Event
+                        Foreign.CUDA.Runtime.Exec
+                        Foreign.CUDA.Runtime.Marshal
+                        Foreign.CUDA.Runtime.Ptr
+                        Foreign.CUDA.Runtime.Stream
+                        Foreign.CUDA.Runtime.Thread
+                        Foreign.CUDA.Runtime.Utils
+
+                        Foreign.CUDA.Driver
+                        Foreign.CUDA.Driver.Context
+                        Foreign.CUDA.Driver.Device
+                        Foreign.CUDA.Driver.Error
+                        Foreign.CUDA.Driver.Event
+                        Foreign.CUDA.Driver.Exec
+                        Foreign.CUDA.Driver.Marshal
+                        Foreign.CUDA.Driver.Module
+                        Foreign.CUDA.Driver.Stream
+                        Foreign.CUDA.Driver.Utils
+
+  Other-modules:        Foreign.CUDA.Internal.C2HS
+                        Foreign.CUDA.Internal.Offsets
+
+  Include-dirs:         .
+  C-sources:            cbits/stubs.c
+
+  Build-tools:          c2hs, hsc2hs
+  Build-depends:        base >= 3 && < 5, haskell98, bytestring, extensible-exceptions
+  Extensions:
+  ghc-options:          -Wall
+  extra-libraries:      cuda cudart
+
diff --git a/examples/Makefile b/examples/Makefile
new file mode 100644
--- /dev/null
+++ b/examples/Makefile
@@ -0,0 +1,18 @@
+ifneq ($(emu),1)
+    PROJECTS := $(shell find src -name Makefile)
+else
+    PROJECTS := $(shell find src -name Makefile | xargs grep -L 'USEDRVAPI')
+endif
+
+
+%.do :
+	$(MAKE) -C $(dir $*) $(MAKECMDGOALS)
+
+all : $(addsuffix .do,$(PROJECTS))
+	@echo "Finished building all"
+
+clean : $(addsuffix .do,$(PROJECTS))
+	@echo "Finished cleaning all"
+
+clobber : $(addsuffix .do,$(PROJECTS))
+	@echo "Finished cleaning all"
diff --git a/examples/common/common.mk b/examples/common/common.mk
new file mode 100644
--- /dev/null
+++ b/examples/common/common.mk
@@ -0,0 +1,413 @@
+# ------------------------------------------------------------------------------
+#
+# Copyright 1993-2009 NVIDIA Corporation.  All rights reserved.
+#
+# NVIDIA Corporation and its licensors retain all intellectual property and 
+# proprietary rights in and to this software and related documentation. 
+# Any use, reproduction, disclosure, or distribution of this software 
+# and related documentation without an express license agreement from
+# NVIDIA Corporation is strictly prohibited.
+#
+# Please refer to the applicable NVIDIA end user license agreement (EULA) 
+# associated with this source code for terms and conditions that govern 
+# your use of this NVIDIA software.
+#
+# ------------------------------------------------------------------------------
+
+# ------------------------------------------------------------------------------
+# Common Haskell/CUDA build system
+# ------------------------------------------------------------------------------
+
+.SUFFIXES : .cu .cu_dbg.o .c_dbg.o .cpp_dbg.o .cu_rel.o .c_rel.o .cpp_rel.o .cubin .ptx
+
+# No CUDA compatible device present
+# MAIN_DEVICE     := $(shell ghc -e "Control.Monad.liftM (Data.Either.either id Foreign.CUDA.deviceName) (Foreign.CUDA.props 0)")
+# ifeq ($(MAIN_DEVICE),"Device Emulation (CPU)")
+#     emu		:= 1
+# endif
+
+# Add new SM Versions here as devices with new Compute Capability are released
+SM_VERSIONS     := sm_10 sm_11 sm_12 sm_13
+
+# detect OS
+OSUPPER         := $(shell uname -s 2>/dev/null | tr [:lower:] [:upper:])
+OSLOWER         := $(shell uname -s 2>/dev/null | tr [:upper:] [:lower:])
+DARWIN          := $(strip $(findstring DARWIN, $(OSUPPER)))
+ifneq ($(DARWIN),)
+    SNOWLEOPARD := $(strip $(findstring 10.6, $(shell egrep "<string>10\.6.*</string>" /System/Library/CoreServices/SystemVersion.plist)))
+endif
+
+# detect if 32 bit or 64 bit system
+HP_64           := $(strip $(shell uname -m | grep 64))
+
+# Basic directory setup
+CUDA_INSTALL_PATH ?= /usr/local/cuda
+CUDA_SDK_PATH     ?= /Developer/GPU\ Computing/C
+
+SRCDIR          ?= .
+ROOTDIR         ?= ..
+ROOTBINDIR      := $(ROOTDIR)/../bin
+BINDIR		:= $(ROOTBINDIR)/$(OSLOWER)
+ROOTOBJDIR      := obj
+LIBDIR          := $(ROOTDIR)/../lib
+COMMONDIR	:= $(ROOTDIR)/../common
+
+# Compilers
+NVCC            := $(CUDA_INSTALL_PATH)/bin/nvcc
+GHC             := ghc
+C2HS            := c2hs
+HSC2HS          := hsc2hs
+CXX             := g++
+CC              := gcc
+LINK            := g++ -fPIC
+
+# Includes
+INCLUDES        += -I. -I$(CUDA_INSTALL_PATH)/include -I$(COMMONDIR)/include -I$(COMMONDIR)/src
+
+# architecture flag for cubin build
+CUBIN_ARCH_FLAG :=
+
+# Warning flags
+CXXWARN_FLAGS := \
+        -W -Wall \
+        -Wimplicit \
+        -Wswitch \
+        -Wformat \
+        -Wchar-subscripts \
+        -Wparentheses \
+        -Wmultichar \
+        -Wtrigraphs \
+        -Wpointer-arith \
+        -Wcast-align \
+        -Wreturn-type \
+        -Wno-unused-function
+
+CWARN_FLAGS := $(CXXWARN_FLAGS) \
+        -Wstrict-prototypes \
+        -Wmissing-prototypes \
+        -Wmissing-declarations \
+        -Wnested-externs \
+        -Wmain
+
+GHCWARN_FLAGS := \
+        -Wall
+
+# cross-compilation flags
+ifeq ($(x86_64),1)
+    NVCCFLAGS	+= -m64
+    ifneq ($(DARWIN),)
+        CXX_ARCH_FLAGS 	+= -arch x86_64
+    else
+        CXX_ARCH_FLAGS	+= -m64
+    endif
+else
+ifeq ($(i386),1)
+    NVCCFLAGS	+= -m32
+    ifneq ($(DARWIN),)
+        CXX_ARCH_FLAGS	+= -arch i386
+    else
+        CXX_ARCH_FLAGS	+= -m32
+    endif
+else
+    ifneq ($(SNOWLEOPARD),)
+        NVCCFLAGS	+= -m32
+        CXX_ARCH_FLAGS	+= -arch i386 -m32
+    endif
+endif
+endif
+
+# Compiler-specific flags
+NVCCFLAGS       +=
+GHCFLAGS        += $(GHCWARN_FLAGS) -i$(SRCDIR) -i$(COMMONDIR)/src -i$(OBJDIR) -odir $(OBJDIR) -hidir $(OBJDIR) --make
+CXXFLAGS        += $(CXXWARN_FLAGS) $(CXX_ARCH_FLAGS)
+CFLAGS          += $(CWARN_FLAGS) $(CXX_ARCH_FLAGS)
+LINK		+= $(CXX_ARCH_FLAGS)
+
+# Common flags
+COMMONFLAGS     += $(INCLUDES) -DUNIX
+
+# Debug/release configuration
+ifeq ($(dbg),1)
+    COMMONFLAGS += -g
+    GHCFLAGS	+= -prof -auto-all -fhpc
+    NVCCFLAGS   += -D_DEBUG
+    CXXFLAGS    += -D_DEBUG
+    CFLAGS      += -D_DEBUG
+    BINSUBDIR   := debug
+    LIBSUFFIX   := D
+else
+    COMMONFLAGS += -O2
+    GHCFLAGS	+= -O2
+    BINSUBDIR   := release
+    LIBSUFFIX   :=
+    NVCCFLAGS   += --compiler-options -fno-strict-aliasing
+    CXXFLAGS    += -fno-strict-aliasing
+    CFLAGS      += -fno-strict-aliasing
+endif
+
+# Device emulation configuration
+ifeq ($(emu),1)
+    NVCCFLAGS   += -deviceemu
+    CUDACCFLAGS +=
+    BINSUBDIR   := emu$(BINSUBDIR)
+    LIBSUFFIX   := $(LIBSUFFIX)_emu
+    # consistency, makes developing easier
+    C2HSFLAGS   += --cppopts=-D__DEVICE_EMULATION__
+    GHCFLAGS    += -D__DEVICE_EMULATION__
+    CXXFLAGS    += -D__DEVICE_EMULATION__
+    CFLAGS      += -D__DEVICE_EMULATION__
+endif
+
+# architecture flag for cubin build
+CUBIN_ARCH_FLAG :=
+
+# Libraries
+LIB             += -L$(LIBDIR) $(addprefix -l,$(EXTRALIBS))
+ifeq ($(HP_64),)
+   LIB          += -L$(CUDA_INSTALL_PATH)/lib
+else
+   LIB          += -L$(CUDA_INSTALL_PATH)/lib64
+endif
+
+# If dynamically linking to CUDA and CUDART, we exclude the libraries from the LIB
+ifeq ($(USECUDADYNLIB),1)
+    LIB         += -ldl -rdynamic
+else
+    # static linking, we will statically link against CUDA and CUDART
+    ifeq ($(USEDRVAPI),1)
+        LIB     += -lcuda
+    else
+        LIB     += -lcudart
+    endif
+endif
+
+ifeq ($(USECUFFT),1)
+    ifeq ($(emu),1)
+        LIB     += -lcufftemu
+    else
+        LIB     += -lcufft
+    endif
+endif
+
+ifeq ($(USECUBLAS),1)
+    ifeq ($(emu),1)
+        LIB     += -lcublasemu
+    else
+        LIB     += -lcublas
+    endif
+endif
+
+ifeq ($(USECUDPP),1)
+    CUDPPLIB := cudpp
+
+    ifneq ($(HP_64),)
+        CUDPPLIB := $(CUDPPLIB)64
+    endif
+
+    ifeq ($(emu),1)
+        CUDPPLIB := $(CUDPPLIB)_emu
+    endif
+
+    LIB     += -l$(CUDPPLIB)
+endif
+
+# Library/executable configuration
+ifneq ($(STATIC_LIB),)
+    TARGETDIR   := $(LIBDIR)
+    TARGET      := $(LIBDIR)/$(basename $(STATIC_LIB))$(LIBSUFFIX)$(suffix $(STATIC_LIB))
+    LINKLINE     = ar rucv $(TARGET) $(OBJS); ranlib $(TARGET)
+else
+ifneq ($(DYNAMIC_LIB),)
+    TARGETDIR   := $(LIBDIR)
+    TARGET      := $(LIBDIR)/$(basename $(DYNAMIC_LIB))$(LIBSUFFIX)$(suffix $(DYNAMIC_LIB))
+    CFLAGS      += -fPIC
+    CXXFLAGS    += -fPIC
+    NVCCFLAGS   += -Xcompiler -fPIC
+    ifneq ($(DARWIN),)
+    LINKLINE     = $(LINK) -dynamiclib -o $(TARGET) -install_name "@rpath/$(notdir $(TARGET))" $(OBJS) $(LIB)
+    else
+    LINKLINE     = $(LINK) -shared -o $(TARGET) -Wl,-rpath,$(notdir $(TARGET)) $(OBJS) $(LIB)
+    endif
+else
+    TARGETDIR   := $(BINDIR)/$(BINSUBDIR)
+    TARGET      := $(TARGETDIR)/$(EXECUTABLE)
+    ifneq ($(HSMAIN),)
+        ifeq ($(suffix $(HSMAIN)),.chs)
+            FOO      := $(HSMAIN)
+            CHSFILES += $(FOO)
+            HSMAIN    = $(OBJDIR)/$(basename $(FOO)).hs
+        endif
+
+        ifeq ($(dbg),1)
+#            OBJS     += $(OBJDIR)/ptxvars.cu.o
+            LINKLINE  = $(GHC) -o $(TARGET) $(LIB) $(OBJS) $(GHCFLAGS) $(HSMAIN)
+        else
+            LINKLINE  = $(GHC) -o $(TARGET) $(LIB) $(OBJS) $(GHCFLAGS) $(HSMAIN)
+        endif
+    else
+        LINKLINE = $(LINK) -o $(TARGET) $(OBJS) $(LIB)
+    endif
+endif
+endif
+
+# check if verbose
+ifeq ($(verbose),1)
+    VERBOSE     :=
+else
+    VERBOSE     := @
+endif
+
+
+# ------------------------------------------------------------------------------
+# Check for input flags and set compiler flags appropriately
+# ------------------------------------------------------------------------------
+ifeq ($(fastmath),1)
+    NVCCFLAGS   += -use_fast_math
+endif
+
+ifeq ($(keep),1)
+    NVCCFLAGS       += -keep
+    NVCC_KEEP_CLEAN := *.i* *.cubin *.cu.c *.cudafe* *.fatbin.c *.ptx
+endif
+
+ifdef maxregisters
+    NVCCFLAGS   += -maxrregcount $(maxregisters)
+endif
+
+# Add cudacc flags
+NVCCFLAGS += $(CUDACCFLAGS)
+
+# Add common flags
+NVCCFLAGS += $(COMMONFLAGS)
+CXXFLAGS  += $(COMMONFLAGS)
+CFLAGS    += $(COMMONFLAGS)
+
+ifeq ($(nvcc_warn_verbose),1)
+    NVCCFLAGS += $(addprefix --compiler-options ,$(CXXWARN_FLAGS))
+    NVCCFLAGS += --compiler-options -fno-strict-aliasing
+endif
+
+
+# ------------------------------------------------------------------------------
+# Set up object files
+# ------------------------------------------------------------------------------
+OBJDIR  := $(ROOTOBJDIR)/$(BINSUBDIR)
+OBJS    += $(patsubst %.cpp,$(OBJDIR)/%.cpp.o,$(notdir $(CCFILES)))
+OBJS    += $(patsubst %.c,$(OBJDIR)/%.c.o,$(notdir $(CFILES)))
+OBJS    += $(patsubst %.cu,$(OBJDIR)/%.cu.o,$(notdir $(CUFILES)))
+
+# ------------------------------------------------------------------------------
+# Set up preprocessed Haskell files
+# ------------------------------------------------------------------------------
+DEPS	+= $(patsubst %.chs,$(OBJDIR)/%.hs,$(notdir $(CHSFILES)))
+DEPS	+= $(patsubst %.hsc,$(OBJDIR)/%.hs,$(notdir $(HSCFILES)))
+
+# ------------------------------------------------------------------------------
+# Set up cubin output files
+# ------------------------------------------------------------------------------
+CUBINDIR := $(SRCDIR)/data
+CUBINS   += $(patsubst %.cu,$(CUBINDIR)/%.cubin,$(notdir $(CUBINFILES)))
+
+# ------------------------------------------------------------------------------
+# Set up PTX output files
+# ------------------------------------------------------------------------------
+PTXDIR  := $(SRCDIR)/data
+PTXBINS += $(patsubst %.cu,$(PTXDIR)/%.ptx,$(notdir $(PTXFILES)))
+
+
+# ------------------------------------------------------------------------------
+# Rules
+# ------------------------------------------------------------------------------
+default: $(TARGET)
+
+%.subdir :
+	$(VERBOSE)$(MAKE) -C $* $(MAKECMDGOALS)
+
+$(OBJDIR)/%.c.o : $(SRCDIR)/%.c $(C_DEPS)
+	$(VERBOSE)$(CC) $(CFLAGS) -o $@ -c $<
+
+$(OBJDIR)/%.cpp.o : $(SRCDIR)/%.cpp $(C_DEPS)
+	$(VERBOSE)$(CXX) $(CXXFLAGS) -o $@ -c $<
+
+$(OBJDIR)/ptxvars.cu.o: makedirectories
+	$(VERBOSE)$(NVCC) -g -G --host-compilation=C -D__DEVICE_LAUNCH_PARAMETERS_H__ -Xptxas -fext -o $@ -c $(CUDA_INSTALL_PATH)/bin/ptxvars.cu
+
+$(OBJDIR)/%.cu.o : $(SRCDIR)/%.cu $(CU_DEPS)
+	$(VERBOSE)$(NVCC) $(NVCCFLAGS) $(SMVERSIONFLAGS) -o $@ -c $<
+
+$(OBJDIR)/%.hs : $(SRCDIR)/%.chs
+	$(VERBOSE)$(C2HS) $(C2HSFLAGS) --include=$(OBJDIR) $(addprefix --cppopts=,$(INCLUDES)) --output-dir=$(OBJDIR) --output=$(notdir $@) $<
+
+$(OBJDIR)/%.hs : $(SRCDIR)/%.hsc
+	$(VERBOSE)$(HSC2HS) $(INCLUDES) -o $@ $<
+
+$(CUBINDIR)/%.cubin : $(SRCDIR)/%.cu cubindirectory
+	$(VERBOSE)$(NVCC) $(CUBIN_ARCH_FLAG) $(NVCCFLAGS) $(SMVERSIONFLAGS) -o $@ -cubin $<
+
+$(PTXDIR)/%.ptx : $(SRCDIR)/%.cu ptxdirectory
+	$(VERBOSE)$(NVCC) $(CUBIN_ARCH_FLAG) $(NVCCFLAGS) $(SMVERSIONFLAGS) -o $@ -ptx $<
+
+#
+# The following definition is a template that gets instantiated for each SM
+# version (sm_10, sm_13, etc.) stored in SMVERSIONS.  It does 2 things:
+# 1. It adds to OBJS a .cu_sm_XX.o for each .cu file it finds in CUFILES_sm_XX.
+# 2. It generates a rule for building .cu_sm_XX.o files from the corresponding
+#    .cu file.
+#
+# The intended use for this is to allow Makefiles that use common.mk to compile
+# files to different Compute Capability targets (aka SM arch version).  To do
+# so, in the Makefile, list files for each SM arch separately, like so:
+#
+# CUFILES_sm_10 := mycudakernel_sm10.cu app.cu
+# CUFILES_sm_12 := anothercudakernel_sm12.cu
+#
+define SMVERSION_template
+OBJS += $(patsubst %.cu,$(OBJDIR)/%.cu_$(1).o,$(notdir $(CUFILES_$(1))))
+$(OBJDIR)/%.cu_$(1).o : $(SRCDIR)/%.cu $(CU_DEPS)
+	$(VERBOSE)$(NVCC) -o $$@ -c $$< $(NVCCFLAGS) -arch $(1)
+endef
+
+# This line invokes the above template for each arch version stored in
+# SM_VERSIONS.  The call funtion invokes the template, and the eval
+# function interprets it as make commands.
+$(foreach smver,$(SM_VERSIONS),$(eval $(call SMVERSION_template,$(smver))))
+
+$(TARGET): makedirectories $(DEPS) $(OBJS) $(CUBINS) $(PTXBINS) Makefile $(addsuffix .subdir,$(SUBDIRS))
+	$(VERBOSE)$(LINKLINE)
+
+cubindirectory:
+	$(VERBOSE)mkdir -p $(CUBINDIR)
+
+ptxdirectory:
+	$(VERBOSE)mkdir -p $(PTXDIR)
+
+makedirectories:
+	$(VERBOSE)mkdir -p $(LIBDIR)
+	$(VERBOSE)mkdir -p $(OBJDIR)
+	$(VERBOSE)mkdir -p $(TARGETDIR)
+
+
+tidy : $(addsuffix .subdir,$(SUBDIRS))
+	$(VERBOSE)find . | egrep "#"  | xargs rm -f
+	$(VERBOSE)find . | egrep "\~" | xargs rm -f
+
+clean : tidy
+	$(VERBOSE)rm -f $(OBJS)
+	$(VERBOSE)rm -f $(CUBINS)
+	$(VERBOSE)rm -f $(PTXBINS)
+	$(VERBOSE)rm -f $(TARGET)
+	$(VERBOSE)rm -f $(NVCC_KEEP_CLEAN)
+	$(VERBOSE)rm -f $(ROOTBINDIR)/$(OSLOWER)/$(BINSUBDIR)/*.ppm
+	$(VERBOSE)rm -f $(ROOTBINDIR)/$(OSLOWER)/$(BINSUBDIR)/*.pgm
+	$(VERBOSE)rm -f $(ROOTBINDIR)/$(OSLOWER)/$(BINSUBDIR)/*.bin
+	$(VERBOSE)rm -f $(ROOTBINDIR)/$(OSLOWER)/$(BINSUBDIR)/*.bmp
+
+clobber : clean
+	$(VERBOSE)rm -rf $(ROOTOBJDIR)
+
+spotless:
+	$(VERBOSE)rm -rf .hpc
+	$(VERBOSE)rm -f $(EXECUTABLE).{aux,hp,prof,ps}
+	$(VERBOSE)rm -f *.html
+	$(VERBOSE)find . -name "*.tix" -print0 | xargs -0 rm -f
+
diff --git a/examples/common/include/cudpp/LICENSE b/examples/common/include/cudpp/LICENSE
new file mode 100644
--- /dev/null
+++ b/examples/common/include/cudpp/LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2007-2009 The Regents of the University of California, Davis
+campus ("The Regents") and NVIDIA Corporation ("NVIDIA"). All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, 
+are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, 
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice, 
+      this list of conditions and the following disclaimer in the documentation 
+      and/or other materials provided with the distribution.
+    * Neither the name of the The Regents, nor NVIDIA, nor the names of its 
+      contributors may be used to endorse or promote products derived from this 
+      software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/examples/common/include/cudpp/cudpp_globals.h b/examples/common/include/cudpp/cudpp_globals.h
new file mode 100644
--- /dev/null
+++ b/examples/common/include/cudpp/cudpp_globals.h
@@ -0,0 +1,56 @@
+// -------------------------------------------------------------
+// cuDPP -- CUDA Data Parallel Primitives library
+// -------------------------------------------------------------
+// $Revision: 5632 $
+// $Date: 2009-07-01 14:36:01 +1000 (Wed, 01 Jul 2009) $
+// -------------------------------------------------------------
+// This source code is distributed under the terms of license.txt in
+// the root directory of this source distribution.
+// -------------------------------------------------------------
+
+/**
+ * @file
+ * cudpp_globals.h
+ *
+ * @brief Global declarations defining machine characteristics of GPU target
+ * These are currently set for best performance on G8X GPUs.  The optimal
+ * parameters may change on future GPUs. In the future, we hope to make
+ * CUDPP a self-tuning library.
+ */
+
+#ifndef __CUDPP_GLOBALS_H__
+#define __CUDPP_GLOBALS_H__
+
+const int NUM_BANKS = 16;                        /**< Number of shared memory banks */
+const int LOG_NUM_BANKS = 4;                     /**< log_2(NUM_BANKS) */
+const int CTA_SIZE = 128;                        /**< Number of threads in a CTA */
+const int WARP_SIZE = 32;                        /**< Number of threads in a warp */
+const int LOG_CTA_SIZE = 7;                      /**< log_2(CTA_SIZE) */
+const int LOG_WARP_SIZE = 5;                     /**< log_2(WARP_SIZE) */
+const int LOG_SIZEOF_FLOAT = 2;                  /**< log_2(sizeof(float)) */
+const int SCAN_ELTS_PER_THREAD = 8;              /**< Number of elements per scan thread */
+const int SEGSCAN_ELTS_PER_THREAD = 8;     /**< Number of elements per segmented scan thread */
+
+const int maxSharedMemoryPerBlock = 16384; /**< Number of bytes of shared
+                                              memory in each block */
+const int maxThreadsPerBlock = CTA_SIZE;   /**< Maximum number of
+                                             * threads in a CTA */
+
+#define AVOID_BANK_CONFLICTS /**< Set if by default, we want our
+                              * shared memory allocation to perform
+                              * additional computation to avoid bank
+                              * conflicts */
+
+#ifdef AVOID_BANK_CONFLICTS
+#define CONFLICT_FREE_OFFSET(index) ((index) >> LOG_NUM_BANKS)
+#else
+#define CONFLICT_FREE_OFFSET(index) (0)
+#endif
+
+#endif // __CUDPP_GLOBALS_H__
+
+// Leave this at the end of the file
+// Local Variables:
+// mode:c++
+// c-file-style: "NVIDIA"
+// End:
diff --git a/examples/common/include/cudpp/shared_mem.h b/examples/common/include/cudpp/shared_mem.h
new file mode 100644
--- /dev/null
+++ b/examples/common/include/cudpp/shared_mem.h
@@ -0,0 +1,115 @@
+// -------------------------------------------------------------
+// cuDPP -- CUDA Data Parallel Primitives library
+// -------------------------------------------------------------
+// $Revision: 5633 $
+// $Date: 2009-07-01 15:02:51 +1000 (Wed, 01 Jul 2009) $
+// -------------------------------------------------------------
+// This source code is distributed under the terms of license.txt
+// in the root directory of this source distribution.
+// -------------------------------------------------------------
+
+/**
+ * @file
+ * sharedmem.h
+ *
+ * @brief Shared memory declaration struct for templatized types.
+ *
+ * Because dynamically sized shared memory arrays are declared "extern" in CUDA,
+ * we can't templatize their types directly.  To get around this, we declare a
+ * simple wrapper struct that will declare the extern array with a different
+ * name depending on the type.  This avoids linker errors about multiple
+ * definitions.
+ *
+ * To use dynamically allocated shared memory in a templatized __global__ or
+ * __device__ function, just replace code like this:
+ *
+ * <pre>
+ *  template<class T>
+ *  __global__ void
+ *  foo( T* d_out, T* d_in)
+ *  {
+ *      // Shared mem size is determined by the host app at run time
+ *      extern __shared__  T sdata[];
+ *      ...
+ *      doStuff(sdata);
+ *      ...
+ *  }
+ * </pre>
+ *
+ *  With this
+ * <pre>
+ *  template<class T>
+ *  __global__ void
+ *  foo( T* d_out, T* d_in)
+ *  {
+ *      // Shared mem size is determined by the host app at run time
+ *      SharedMemory<T> smem;
+ *      T* sdata = smem.getPointer();
+ *      ...
+ *      doStuff(sdata);
+ *      ...
+ *  }
+ * </pre>
+ */
+
+#ifndef __SHARED_MEM_H__
+#define __SHARED_MEM_H__
+
+
+/** @brief Wrapper class for templatized dynamic shared memory arrays.
+  *
+  * This struct uses template specialization on the type \a T to declare
+  * a differently named dynamic shared memory array for each type
+  * (\code extern __shared__ T s_type[] \endcode).
+  *
+  * Currently there are specializations for the following types:
+  * \c int, \c uint, \c char, \c uchar, \c short, \c ushort, \c long,
+  * \c unsigned long, \c bool, \c float, and \c double. One can also specialize it
+  * for user defined types.
+  */
+template <typename T>
+struct SharedMemory
+{
+    /** Return a pointer to the runtime-sized shared memory array. **/
+    __device__ T* getPointer()
+    {
+        extern __device__ void Error_UnsupportedType(); // Ensure that we won't compile any un-specialized types
+        Error_UnsupportedType();
+        return (T*)0;
+    }
+    // TODO: Use operator overloading to make this class look like a regular array
+};
+
+// Following are the specializations for the following types.
+// int, uint, char, uchar, short, ushort, long, ulong, bool, float, and double
+// One could also specialize it for user-defined types.
+
+#define SPEC_SHAREDMEM(T, name)                                                         \
+    template <> struct SharedMemory <T>                                                 \
+    {                                                                                   \
+        __device__ T* getPointer() { extern __shared__ T s_##name[]; return s_##name; } \
+    }
+
+SPEC_SHAREDMEM(int,    int);
+SPEC_SHAREDMEM(char,   char);
+SPEC_SHAREDMEM(long,   long);
+SPEC_SHAREDMEM(short,  short);
+SPEC_SHAREDMEM(bool,   bool);
+SPEC_SHAREDMEM(float,  float);
+SPEC_SHAREDMEM(double, double);
+
+SPEC_SHAREDMEM(unsigned int,   uint);
+SPEC_SHAREDMEM(unsigned char,  uchar);
+SPEC_SHAREDMEM(unsigned long,  ulong);
+SPEC_SHAREDMEM(unsigned short, ushort);
+
+SPEC_SHAREDMEM(uchar4, uchar4);
+
+#undef SPEC_SHAREDMEM
+#endif // __SHARED_MEM_H__
+
+// Leave this at the end of the file
+// Local Variables:
+// mode:c++
+// c-file-style: "NVIDIA"
+// End:
diff --git a/examples/common/include/cudpp/type_vector.h b/examples/common/include/cudpp/type_vector.h
new file mode 100644
--- /dev/null
+++ b/examples/common/include/cudpp/type_vector.h
@@ -0,0 +1,51 @@
+// -------------------------------------------------------------
+// cuDPP -- CUDA Data Parallel Primitives library
+// -------------------------------------------------------------
+// $Revision: 5632 $
+// $Date: 2009-07-01 14:36:01 +1000 (Wed, 01 Jul 2009) $
+// -------------------------------------------------------------
+// This source code is distributed under the terms of license.txt in
+// the root directory of this source distribution.
+// -------------------------------------------------------------
+
+#ifndef __TYPE_VECTOR_H__
+#define __TYPE_VECTOR_H__
+
+/** @brief Utility template struct for generating small vector types from scalar types
+  *
+  * Given a base scalar type (\c int, \c float, etc.) and a vector length (1 through 4) as
+  * template parameters, this struct defines a vector type (\c float3, \c int4, etc.) of the
+  * specified length and base type.  For example:
+  * \code
+  * template <class T>
+  * __device__ void myKernel(T *data)
+  * {
+  *     typeToVector<T,4>::Result myVec4;             // create a vec4 of type T
+  *     myVec4 = (typeToVector<T,4>::Result*)data[0]; // load first element of data as a vec4
+  * }
+  * \endcode
+  *
+  * This functionality is implemented using template specialization.  Currently specializations
+  * for int, float, and unsigned int vectors of lengths 2-4 are defined.  Note that this results
+  * in types being generated at compile time -- there is no runtime cost.  typeToVector is used by
+  * the optimized scan \c __device__ functions in scan_cta.cu.
+  */
+template <typename T, int N>
+struct typeToVector
+{
+    typedef T Result;
+};
+
+#define TYPE_VECTOR(type, name)                                                 \
+    template <> struct typeToVector<type, 2> { typedef name##2 Result; };       \
+    template <> struct typeToVector<type, 3> { typedef name##3 Result; };       \
+    template <> struct typeToVector<type, 4> { typedef name##4 Result; }
+
+TYPE_VECTOR(int,         int);
+TYPE_VECTOR(float,       float);
+TYPE_VECTOR(unsigned int, uint);
+
+
+#undef TYPE_VECTOR
+#endif
+
diff --git a/examples/common/include/operator.h b/examples/common/include/operator.h
new file mode 100644
--- /dev/null
+++ b/examples/common/include/operator.h
@@ -0,0 +1,114 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Module    : Operator
+ * Copyright : (c) 2009 Trevor L. McDonell
+ * License   : BSD
+ *
+ * A template class for unary/binary kernel operations
+ *
+ * ---------------------------------------------------------------------------*/
+
+
+#ifndef __OPERATOR_H__
+#define __OPERATOR_H__
+
+#include <float.h>
+#include <limits.h>
+
+
+/*
+ * Template class for an operation that can be mapped over an array.
+ */
+template <typename Ta, typename Tb=Ta>
+class Functor
+{
+public:
+    /*
+     * Apply the operation to the given operand.
+     */
+    static __device__ Tb apply(const Ta &x);
+};
+
+template <typename Ta, typename Tb>
+class fromIntegral : Functor<Ta, Tb>
+{
+public:
+    static __device__ Tb apply(const Ta &x) { return (Tb) x; }
+};
+
+
+/*
+ * Template class for binary operators. Certain algorithms may require the
+ * operator to be associative (that is, Ta == Tb), such as parallel scan and
+ * reduction.
+ *
+ * As this is template code, it should compile down to something efficient...
+ */
+template <typename Ta, typename Tb=Ta, typename Tc=Ta>
+class BinaryOp
+{
+public:
+    /*
+     * Apply the operation to the given operands.
+     */
+    static __device__ Tc apply(const Ta &a, const Tb &b);
+
+    /*
+     * Return an identity element for the type Tc.
+     *
+     * This may have special meaning for a given implementation, for example a
+     * `max' operation over integers may want to return INT_MIN.
+     */
+    static __device__ Tc identity();
+};
+
+/*
+ * Return the minimum or maximum value of a type
+ */
+template <typename T> inline __device__ T getMin();
+template <typename T> inline __device__ T getMax();
+
+#define SPEC_MINMAX(type,vmin,vmax)                                            \
+    template <> inline __device__ type getMin() { return vmin; };              \
+    template <> inline __device__ type getMax() { return vmax; }               \
+
+SPEC_MINMAX(float,        -FLT_MAX,  FLT_MAX);
+SPEC_MINMAX(int,           INT_MIN,  INT_MAX);
+SPEC_MINMAX(char,          CHAR_MIN, CHAR_MAX);
+SPEC_MINMAX(unsigned int,  0,        UINT_MAX);
+SPEC_MINMAX(unsigned char, 0,        UCHAR_MAX);
+
+
+/*
+ * Basic binary arithmetic operations. We take advantage of automatic type
+ * promotion to keep the parameters general.
+ */
+#define BASIC_OP(name,expr,id)                                                 \
+    template <typename Ta, typename Tb=Ta, typename Tc=Ta>                     \
+    class name : BinaryOp<Ta, Tb, Tc>                                          \
+    {                                                                          \
+    public:                                                                    \
+        static __device__ Tc apply(const Ta &a, const Tb &b) { return expr; }  \
+        static __device__ Tc identity() { return id; }                         \
+    }
+
+#define LOGICAL_OP(name,expr,id)                                               \
+    template <typename Ta, typename Tb=Ta>                                     \
+    class name : BinaryOp<Ta, Tb, bool>                                        \
+    {                                                                          \
+    public:                                                                    \
+        static __device__ bool apply(const Ta &a, const Tb &b) { return expr; }\
+        static __device__ bool identity() { return id; }                       \
+    }
+
+BASIC_OP(Plus,  a + b,    0);
+BASIC_OP(Times, a * b,    1);
+BASIC_OP(Min,   min(a,b), getMax<Ta>());
+BASIC_OP(Max,   max(a,b), getMin<Ta>());
+
+LOGICAL_OP(Eq,  a == b,   false);
+
+#undef SPEC_MINMAX
+#undef BASIC_OP
+#endif
+
diff --git a/examples/common/include/utils.h b/examples/common/include/utils.h
new file mode 100644
--- /dev/null
+++ b/examples/common/include/utils.h
@@ -0,0 +1,146 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Module    : Utils
+ * Copyright : (c) 2009 Trevor L. McDonell
+ * License   : BSD
+ *
+ * ---------------------------------------------------------------------------*/
+
+
+#ifndef __UTILS_H__
+#define __UTILS_H__
+
+#include <math.h>
+#include <stdio.h>
+
+
+/*
+ * Core assert function. Don't let this escape...
+ */
+#if defined(__CUDACC__) || !defined(__DEVICE_EMULATION__)
+#define __assert(e, file, line) ((void)0)
+#else
+#define __assert(e, file, line) \
+    ((void) fprintf (stderr, "%s:%u: failed assertion `%s'\n", file, line, e), abort())
+#endif
+
+/*
+ * Test the given expression, and abort the program if it evaluates to false.
+ * Only available in debug mode.
+ */
+#ifndef _DEBUG
+#define assert(e)               ((void)0)
+#else
+#define assert(e)  \
+    ((void) ((e) ? (void(0)) : __assert (#e, __FILE__, __LINE__)))
+#endif
+
+
+/*
+ * Macro to insert __syncthreads() in device emulation mode
+ */
+#ifdef __DEVICE_EMULATION__
+#define __EMUSYNC               __syncthreads()
+#else
+#define __EMUSYNC
+#endif
+
+
+/*
+ * Check the return status of CUDA API calls, and abort with an appropriate
+ * error string on failure.
+ */
+#define CUDA_SAFE_CALL_NO_SYNC(call)                                           \
+    do {                                                                       \
+        cudaError err = call;                                                  \
+        if(cudaSuccess != err) {                                               \
+            const char *str = cudaGetErrorString(err);                         \
+            __assert(str, __FILE__, __LINE__);                                 \
+        }                                                                      \
+    } while (0)
+
+#define CUDA_SAFE_CALL(call)                                                   \
+    do {                                                                       \
+        CUDA_SAFE_CALL_NO_SYNC(call);                                          \
+        CUDA_SAFE_CALL_NO_SYNC(cudaThreadSynchronize());                       \
+    } while (0)
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Determine if the input is a power of two
+ */
+inline bool
+isPow2(unsigned int x)
+{
+    return ((x&(x-1)) == 0);
+}
+
+
+/*
+ * Compute the next highest power of two
+ */
+inline unsigned int
+ceilPow2(unsigned int x)
+{
+#if 0
+    --x;
+    x |= x >> 1;
+    x |= x >> 2;
+    x |= x >> 4;
+    x |= x >> 8;
+    x |= x >> 16;
+    return ++x;
+#endif
+
+    return (isPow2(x)) ? x : 1u << (int) ceil(log2((double)x));
+}
+
+
+/*
+ * Compute the next lowest power of two
+ */
+inline unsigned int
+floorPow2(unsigned int x)
+{
+#if 0
+    float nf = (float) n;
+    return 1 << (((*(int*)&nf) >> 23) - 127);
+#endif
+
+    int exp;
+    frexp(x, &exp);
+    return 1 << (exp - 1);
+}
+
+
+/*
+ * computes next highest multiple of f from x
+ */
+inline unsigned int
+multiple(unsigned int x, unsigned int f)
+{
+    return ((x + (f-1)) / f);
+}
+
+
+/*
+ * MS Excel-style CEIL() function. Rounds x up to nearest multiple of f
+ */
+inline unsigned int
+ceiling(unsigned int x, unsigned int f)
+{
+    return multiple(x, f) * f;
+}
+
+
+#undef __asert
+
+#ifdef __cplusplus
+}
+#endif
+#endif
+
diff --git a/examples/common/src/C2HS.hs b/examples/common/src/C2HS.hs
new file mode 100644
--- /dev/null
+++ b/examples/common/src/C2HS.hs
@@ -0,0 +1,224 @@
+--  C->Haskell Compiler: Marshalling library
+--
+--  Copyright (c) [1999...2005] Manuel M T Chakravarty
+--
+--  Redistribution and use in source and binary forms, with or without
+--  modification, are permitted provided that the following conditions are met:
+-- 
+--  1. Redistributions of source code must retain the above copyright notice,
+--     this list of conditions and the following disclaimer. 
+--  2. Redistributions in binary form must reproduce the above copyright
+--     notice, this list of conditions and the following disclaimer in the
+--     documentation and/or other materials provided with the distribution. 
+--  3. The name of the author may not be used to endorse or promote products
+--     derived from this software without specific prior written permission. 
+--
+--  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+--  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+--  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+--  NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+--  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+--  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+--  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+--  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+--  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+--  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+--
+--- Description ---------------------------------------------------------------
+--
+--  Language: Haskell 98
+--
+--  This module provides the marshaling routines for Haskell files produced by 
+--  C->Haskell for binding to C library interfaces.  It exports all of the
+--  low-level FFI (language-independent plus the C-specific parts) together
+--  with the C->HS-specific higher-level marshalling routines.
+--
+
+module C2HS (
+
+  -- * Re-export the language-independent component of the FFI 
+  module Foreign,
+
+  -- * Re-export the C language component of the FFI
+  module Foreign.C,
+
+  -- * Composite marshalling functions
+  withCStringLenIntConv, peekCStringLenIntConv, withIntConv, withFloatConv,
+  peekIntConv, peekFloatConv, withBool, peekBool, withEnum, peekEnum,
+
+  -- * Conditional results using 'Maybe'
+  nothingIf, nothingIfNull,
+
+  -- * Bit masks
+  combineBitMasks, containsBitMask, extractBitMasks,
+
+  -- * Conversion between C and Haskell types
+  cIntConv, cFloatConv, cToBool, cFromBool, cToEnum, cFromEnum
+) where 
+
+
+import Foreign
+       hiding       (Word)
+		    -- Should also hide the Foreign.Marshal.Pool exports in
+		    -- compilers that export them
+import Foreign.C
+
+import Monad        (liftM)
+
+
+-- Composite marshalling functions
+-- -------------------------------
+
+-- Strings with explicit length
+--
+withCStringLenIntConv    :: String -> (CStringLen -> IO a) -> IO a
+withCStringLenIntConv s f = withCStringLen s $ \(p, n) -> f (p, cIntConv n)
+
+peekCStringLenIntConv      :: CStringLen -> IO String
+peekCStringLenIntConv (s,n) = peekCStringLen (s, cIntConv n)
+
+-- Marshalling of numerals
+--
+
+withIntConv   :: (Storable b, Integral a, Integral b) 
+	      => a -> (Ptr b -> IO c) -> IO c
+withIntConv    = with . cIntConv
+
+withFloatConv :: (Storable b, RealFloat a, RealFloat b) 
+	      => a -> (Ptr b -> IO c) -> IO c
+withFloatConv  = with . cFloatConv
+
+peekIntConv   :: (Storable a, Integral a, Integral b) 
+	      => Ptr a -> IO b
+peekIntConv    = liftM cIntConv . peek
+
+peekFloatConv :: (Storable a, RealFloat a, RealFloat b) 
+	      => Ptr a -> IO b
+peekFloatConv  = liftM cFloatConv . peek
+
+-- Passing Booleans by reference
+--
+
+withBool :: (Integral a, Storable a) => Bool -> (Ptr a -> IO b) -> IO b
+withBool  = with . fromBool
+
+peekBool :: (Integral a, Storable a) => Ptr a -> IO Bool
+peekBool  = liftM toBool . peek
+
+
+-- Passing enums by reference
+--
+
+withEnum :: (Enum a, Integral b, Storable b) => a -> (Ptr b -> IO c) -> IO c
+withEnum  = with . cFromEnum
+
+peekEnum :: (Enum a, Integral b, Storable b) => Ptr b -> IO a
+peekEnum  = liftM cToEnum . peek
+
+
+{-
+-- Storing of 'Maybe' values
+-- -------------------------
+
+instance Storable a => Storable (Maybe a) where
+  sizeOf    _ = sizeOf    (undefined :: Ptr ())
+  alignment _ = alignment (undefined :: Ptr ())
+
+  peek p = do
+	     ptr <- peek (castPtr p)
+	     if ptr == nullPtr
+	       then return Nothing
+	       else liftM Just $ peek ptr
+
+  poke p v = do
+	       ptr <- case v of
+		        Nothing -> return nullPtr
+			Just v' -> new v'
+               poke (castPtr p) ptr
+-}
+
+-- Conditional results using 'Maybe'
+-- ---------------------------------
+
+-- Wrap the result into a 'Maybe' type.
+--
+-- * the predicate determines when the result is considered to be non-existing,
+--   ie, it is represented by `Nothing'
+--
+-- * the second argument allows to map a result wrapped into `Just' to some
+--   other domain
+--
+nothingIf       :: (a -> Bool) -> (a -> b) -> a -> Maybe b
+nothingIf p f x  = if p x then Nothing else Just $ f x
+
+-- |Instance for special casing null pointers.
+--
+nothingIfNull :: (Ptr a -> b) -> Ptr a -> Maybe b
+nothingIfNull  = nothingIf (== nullPtr)
+
+
+-- Support for bit masks
+-- ---------------------
+
+-- Given a list of enumeration values that represent bit masks, combine these
+-- masks using bitwise disjunction.
+--
+combineBitMasks :: (Enum a, Bits b) => [a] -> b
+combineBitMasks = foldl (.|.) 0 . map (fromIntegral . fromEnum)
+
+-- Tests whether the given bit mask is contained in the given bit pattern
+-- (i.e., all bits set in the mask are also set in the pattern).
+--
+containsBitMask :: (Bits a, Enum b) => a -> b -> Bool
+bits `containsBitMask` bm = let bm' = fromIntegral . fromEnum $ bm
+			    in
+			    bm' .&. bits == bm'
+
+-- |Given a bit pattern, yield all bit masks that it contains.
+--
+-- * This does *not* attempt to compute a minimal set of bit masks that when
+--   combined yield the bit pattern, instead all contained bit masks are
+--   produced.
+--
+extractBitMasks :: (Bits a, Enum b, Bounded b) => a -> [b]
+extractBitMasks bits = 
+  [bm | bm <- [minBound..maxBound], bits `containsBitMask` bm]
+
+
+-- Conversion routines
+-- -------------------
+
+-- |Integral conversion
+--
+cIntConv :: (Integral a, Integral b) => a -> b
+cIntConv  = fromIntegral
+
+-- |Floating conversion
+--
+cFloatConv :: (RealFloat a, RealFloat b) => a -> b
+cFloatConv  = realToFrac
+-- As this conversion by default goes via `Rational', it can be very slow...
+{-# RULES 
+  "cFloatConv/Float->Float"   forall (x::Float).  cFloatConv x = x;
+  "cFloatConv/Double->Double" forall (x::Double). cFloatConv x = x
+ #-}
+
+-- |Obtain C value from Haskell 'Bool'.
+--
+cFromBool :: Num a => Bool -> a
+cFromBool  = fromBool
+
+-- |Obtain Haskell 'Bool' from C value.
+--
+cToBool :: Num a => a -> Bool
+cToBool  = toBool
+
+-- |Convert a C enumeration to Haskell.
+--
+cToEnum :: (Integral i, Enum e) => i -> e
+cToEnum  = toEnum . cIntConv
+
+-- |Convert a Haskell enumeration to C.
+--
+cFromEnum :: (Enum e, Integral i) => e -> i
+cFromEnum  = cIntConv . fromEnum
diff --git a/examples/common/src/PrettyPrint.hs b/examples/common/src/PrettyPrint.hs
new file mode 100644
--- /dev/null
+++ b/examples/common/src/PrettyPrint.hs
@@ -0,0 +1,61 @@
+--------------------------------------------------------------------------------
+-- |
+-- Module    : PrettyPrint
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Simple layout and pretty printing
+--
+--------------------------------------------------------------------------------
+
+module PrettyPrint where
+
+import Data.List
+import Text.PrettyPrint
+import System.IO
+
+
+--------------------------------------------------------------------------------
+-- Printing
+--------------------------------------------------------------------------------
+
+printDoc :: Doc -> IO ()
+printDoc =  putStrLn . flip (++) "\n" . render
+
+--
+-- stolen from $fptools/ghc/compiler/utils/Pretty.lhs
+--
+-- This code has a BSD-style license
+--
+printDocFull :: Mode -> Handle -> Doc -> IO ()
+printDocFull m hdl doc = do
+  fullRender m cols 1.5 put done doc
+  hFlush hdl
+  where
+    put (Chr c) next  = hPutChar hdl c >> next
+    put (Str s) next  = hPutStr  hdl s >> next
+    put (PStr s) next = hPutStr  hdl s >> next
+
+    done = hPutChar hdl '\n'
+    cols = 80
+
+
+--------------------------------------------------------------------------------
+-- Layout
+--------------------------------------------------------------------------------
+
+--
+-- Display the given grid of renderable data, given as either a list of rows or
+-- columns, using the minimum size required for each column. An additional
+-- parameter specifies extra space to be inserted between each column.
+--
+ppAsRows      :: Int -> [[Doc]] -> Doc
+ppAsRows q    =  ppAsColumns q . transpose
+
+ppAsColumns   :: Int -> [[Doc]] -> Doc
+ppAsColumns q =  vcat . map hsep . transpose . map (\col -> pad (width col) col)
+  where
+    len   = length . render
+    width = maximum . map len
+    pad w = map (\x -> x <> (hcat $ replicate (w - (len x) + q) space))
+
diff --git a/examples/common/src/RandomVector.hs b/examples/common/src/RandomVector.hs
new file mode 100644
--- /dev/null
+++ b/examples/common/src/RandomVector.hs
@@ -0,0 +1,101 @@
+{-# LANGUAGE FlexibleContexts, ParallelListComp #-}
+--------------------------------------------------------------------------------
+--
+-- Module    : RandomVector
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Storable multi-dimensional arrays and lists of random numbers
+--
+--------------------------------------------------------------------------------
+
+module RandomVector
+  (
+    Storable,
+    module RandomVector,
+    module Data.Array.Storable
+  )
+  where
+
+import Foreign                                          (Ptr, Storable)
+import Control.Monad                                    (join)
+import Control.Exception                                (evaluate)
+import Data.Array.Storable
+import System.Random
+
+
+--------------------------------------------------------------------------------
+-- Arrays
+--------------------------------------------------------------------------------
+
+type Vector e = StorableArray Int e
+type Matrix e = StorableArray (Int,Int) e
+
+withVector :: Vector e -> (Ptr e -> IO a) -> IO a
+withVector = withStorableArray
+
+withMatrix :: Matrix e -> (Ptr e -> IO a) -> IO a
+withMatrix = withStorableArray
+
+
+--
+-- To ensure the array is fully evaluated, force one element
+--
+evaluateArr :: (Ix i, MArray StorableArray e IO)
+            => i -> StorableArray i e -> IO (StorableArray i e)
+evaluateArr l arr = (join $ evaluate (arr `readArray` l)) >> return arr
+
+
+--
+-- Generate a new random array
+--
+randomArrR :: (Ix i, Num e, Storable e, Random e, MArray StorableArray e IO)
+           => (i,i) -> (e,e) -> IO (StorableArray i e)
+randomArrR (l,u) bnds = do
+  rg <- newStdGen
+  let -- The standard random number generator is too slow to generate really
+      -- large vectors. Instead, we generate a short vector and repeat that.
+      k     = 1000
+      rands = take k (randomRs bnds rg)
+
+  newListArray (l,u) [rands !! (index (l,u) i`mod`k) | i <- range (l,u)] >>= evaluateArr l
+
+
+randomArr :: (Ix i, Num e, Storable e, Random e, MArray StorableArray e IO)
+          => (i,i) -> IO (StorableArray i e)
+randomArr (l,u) = randomArrR (l,u) (-1,1)
+
+
+--
+-- Verify similarity of two arrays
+--
+verify :: (Ix i, Ord e, Fractional e, Storable e)
+       => StorableArray i e -> StorableArray i e -> IO (Bool)
+verify ref arr = do
+  as <- getElems arr
+  bs <- getElems ref
+  return (verifyList as bs)
+
+
+--------------------------------------------------------------------------------
+-- Lists
+--------------------------------------------------------------------------------
+
+randomListR :: (Num e, Random e, Storable e) => Int -> (e,e) -> IO [e]
+randomListR len bnds = do
+  rg <- newStdGen
+  let -- The standard random number generator is too slow to generate really
+      -- large vectors. Instead, we generate a short vector and repeat that.
+      k     = 1000
+      rands = take k (randomRs bnds rg)
+
+  evaluate [rands !! (i`mod`k) | i <- [0..len-1]]
+
+randomList :: (Num e, Random e, Storable e) => Int -> IO [e]
+randomList len = randomListR len (-1,1)
+
+
+verifyList :: (Ord e, Fractional e) => [e] -> [e] -> Bool
+verifyList xs ys = all (< epsilon) [abs ((x-y)/(x+y+epsilon)) | x <- xs | y <- ys]
+  where epsilon = 0.0005
+
diff --git a/examples/common/src/Time.hs b/examples/common/src/Time.hs
new file mode 100644
--- /dev/null
+++ b/examples/common/src/Time.hs
@@ -0,0 +1,52 @@
+--------------------------------------------------------------------------------
+--
+-- Module    : Time
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Simple timing benchmarks
+--
+--------------------------------------------------------------------------------
+
+module Time where
+
+import System.CPUTime
+import Control.Monad
+
+
+-- Timing
+--
+data Time = Time { cpu_time :: Integer }
+
+type TimeUnit = Integer -> Integer
+
+picosecond, millisecond, second :: TimeUnit
+picosecond  n = n
+millisecond n = n `div` 1000000000
+second      n = n `div` 1000000000000
+
+getTime :: IO Time
+getTime = Time `fmap` getCPUTime
+
+timeIn :: TimeUnit -> Time -> Integer
+timeIn u (Time t) = u t
+
+elapsedTime :: Time -> Time -> Time
+elapsedTime (Time t1) (Time t2) = Time (t2 - t1)
+
+
+-- Simple benchmarking
+--
+{-# NOINLINE benchmark #-}
+benchmark
+  :: Int                -- Number of times to repeat test
+  -> IO a               -- Test to run
+  -> IO b               -- Finaliser to before measuring elapsed time
+  -> IO (Time,a)
+benchmark n testee finaliser = do
+  t1    <- getTime
+  (r:_) <- replicateM n testee
+  _     <- finaliser
+  t2    <- getTime
+  return (elapsedTime t1 t2, r)
+
diff --git a/examples/common/src/cudpp/LICENSE b/examples/common/src/cudpp/LICENSE
new file mode 100644
--- /dev/null
+++ b/examples/common/src/cudpp/LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2007-2009 The Regents of the University of California, Davis
+campus ("The Regents") and NVIDIA Corporation ("NVIDIA"). All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, 
+are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, 
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice, 
+      this list of conditions and the following disclaimer in the documentation 
+      and/or other materials provided with the distribution.
+    * Neither the name of the The Regents, nor NVIDIA, nor the names of its 
+      contributors may be used to endorse or promote products derived from this 
+      software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/examples/common/src/cudpp/scan_cta.cu b/examples/common/src/cudpp/scan_cta.cu
new file mode 100644
--- /dev/null
+++ b/examples/common/src/cudpp/scan_cta.cu
@@ -0,0 +1,617 @@
+// -------------------------------------------------------------
+//  cuDPP -- CUDA Data Parallel Primitives library
+//  -------------------------------------------------------------
+//  $Revision: 5633 $
+//  $Date: 2009-07-01 15:02:51 +1000 (Wed, 01 Jul 2009) $
+// -------------------------------------------------------------
+// This source code is distributed under the terms of license.txt
+// in the root directory of this source distribution.
+// -------------------------------------------------------------
+
+/**
+ * @file
+ * scan_cta.cu
+ *
+ * @brief CUDPP CTA-level scan routines
+ */
+
+/** \defgroup cudpp_cta CUDPP CTA-Level API
+  * The CUDPP CTA-Level API contains functions that run on the GPU
+  * device.  These are CUDA \c __device__ functions that are called
+  * from within other CUDA device functions (typically
+  * \link cudpp_kernel CUDPP Kernel-Level API\endlink functions).
+  * They are called CTA-level functions because they typically process
+  * s_data "owned" by each CTA within shared memory, and are agnostic of
+  * any other CTAs that may be running (or how many CTAs are running),
+  * other than to compute appropriate global memory addresses.
+  * @{
+  */
+
+/** @name Scan Functions
+* @{
+*/
+
+#include "cudpp/cudpp_globals.h"
+#include "cudpp/type_vector.h"
+// #include <cudpp_util.h>
+#include <math.h>
+// #include <cudpp.h>
+
+/**
+ * @brief Macro to insert necessary __syncthreads() in device emulation mode
+ */
+#ifdef __DEVICE_EMULATION__
+#define __EMUSYNC __syncthreads()
+#else
+#define __EMUSYNC
+#endif
+
+/**
+  * @brief Template class containing compile-time parameters to the scan functions
+  *
+  * ScanTraits is passed as a template parameter to all scan functions.  By
+  * using these compile-time functions we can enable generic code while
+  * maintaining the highest performance.  This is crucial for the performance
+  * of low-level workhorse algorithms like scan.
+  *
+  * @param T The datatype of the scan
+  * @param oper The ::CUDPPOperator to use for the scan (add, max, etc.)
+  * @param multiRow True if this is a multi-row scan
+  * @param unroll True if scan inner loops should be unrolled
+  * @param sums True if each block should write it's sum to the d_blockSums array (false for single-block scans)
+  * @param backward True if this is a backward scan
+  * @param fullBlock True if all blocks in this scan are full (CTA_SIZE * SCAN_ELEMENTS_PER_THREAD elements)
+  * @param exclusive True for exclusive scans, false for inclusive scans
+  */
+template <class T, class oper, bool backward, bool exclusive,
+          bool multiRow, bool sums, bool fullBlock>
+class ScanTraits
+{
+public:
+
+    //! Returns true if this is a backward scan
+    static __device__ bool isBackward()    { return backward; };
+    //! Returns true if this is an exclusive scan
+    static __device__ bool isExclusive()  { return exclusive; };
+    //! Returns true if this a multi-row scan.
+    static __device__ bool isMultiRow()    { return multiRow; };
+    //! Returns true if this scan writes the sum of each block to the d_blockSums array (multi-block scans)
+    static __device__ bool writeSums()     { return sums; };
+    //! Returns true if this is a full scan -- all blocks process CTA_SIZE * SCAN_ELEMENTS_PER_THREAD elements
+    static __device__ bool isFullBlock()   { return fullBlock; };
+
+
+    //! The operator function used for the scan
+    static __device__ T op(const T &a, const T &b) { return oper::apply(a, b); }
+
+    //! The identity value used by the scan
+    static __device__ T identity() { return oper::identity(); }
+};
+
+//! This is used to insert syncthreads to avoid perf loss caused by 128-bit
+//! load overlap that happens on G80.  This gives about a 15% boost on scans on
+//! G80.
+//! @todo Parameterize this in case this perf detail changes on future GPUs.
+#define DISALLOW_LOADSTORE_OVERLAP 1
+
+/**
+* @brief Handles loading input s_data from global memory to shared memory
+* (vec4 version)
+*
+* Load a chunk of 8*blockDim.x elements from global memory into a
+* shared memory array.  Each thread loads two T4 elements (where
+* T4 is, e.g. int4 or float4), computes the scan of those two vec4s in
+* thread local arrays (in registers), and writes the two total sums of the
+* vec4s into shared memory, where they will be cooperatively scanned with
+* the other partial sums by all threads in the CTA.
+*
+* @param[out] s_out The output (shared) memory array
+* @param[out] threadScan0 Intermediate per-thread partial sums array 1
+* @param[out] threadScan1 Intermediate per-thread partial sums array 2
+* @param[in] d_in The input (device) memory array
+* @param[in] numElements The number of elements in the array being scanned
+* @param[in] iDataOffset the offset of the input array in global memory for this
+* thread block
+* @param[out] ai The shared memory address for the thread's first element
+* (returned for reuse)
+* @param[out] bi The shared memory address for the thread's second element
+* (returned for reuse)
+* @param[out] aiDev The device memory address for this thread's first element
+* (returned for reuse)
+* @param[out] biDev The device memory address for this thread's second element
+* (returned for reuse)
+*/
+template <class T, class traits>
+__device__ void loadSharedChunkFromMem4(T        *s_out,
+                                        T        threadScan0[4],
+                                        T        threadScan1[4],
+                                        const T  *d_in,
+                                        int      numElements,
+                                        int      iDataOffset,
+                                        int      &ai,
+                                        int      &bi,
+                                        int      &aiDev,
+                                        int      &biDev)
+{
+    int thid = threadIdx.x;
+    aiDev = iDataOffset + thid;
+    biDev = aiDev + blockDim.x;
+
+    // convert to 4-vector
+    typename typeToVector<T,4>::Result  tempData;
+    typename typeToVector<T,4>::Result* inData = (typename typeToVector<T,4>::Result*)d_in;
+
+    ai = thid;
+    bi = thid + blockDim.x;
+
+    // read into tempData;
+    if (traits::isBackward())
+    {
+        int i = aiDev * 4;
+        if (traits::isFullBlock() || i + 3 < numElements)
+        {
+            tempData       = inData[aiDev];
+            threadScan0[3] = tempData.w;
+            threadScan0[2] = traits::op(tempData.z, threadScan0[3]);
+            threadScan0[1] = traits::op(tempData.y, threadScan0[2]);
+            threadScan0[0] = s_out[ai]
+                           = traits::op(tempData.x, threadScan0[1]);
+        }
+        else
+        {
+            threadScan0[3] = traits::identity();
+            threadScan0[2] = traits::op(((i+2) < numElements) ? d_in[i+2] : traits::identity(), threadScan0[3]);
+            threadScan0[1] = traits::op(((i+1) < numElements) ? d_in[i+1] : traits::identity(), threadScan0[2]);
+            threadScan0[0] = s_out[ai]
+                           = traits::op((i     < numElements) ? d_in[i]   : traits::identity(), threadScan0[1]);
+        }
+
+#ifdef DISALLOW_LOADSTORE_OVERLAP
+        __syncthreads();
+#endif
+
+        i = biDev * 4;
+        if (traits::isFullBlock() || i + 3 < numElements)
+        {
+            tempData       = inData[biDev];
+            threadScan1[3] = tempData.w;
+            threadScan1[2] = traits::op(tempData.z, threadScan1[3]);
+            threadScan1[1] = traits::op(tempData.y, threadScan1[2]);
+            threadScan1[0] = s_out[bi]
+                           = traits::op(tempData.x, threadScan1[1]);
+        }
+        else
+        {
+            threadScan1[3] = traits::identity();
+            threadScan1[2] = traits::op(((i+2) < numElements) ? d_in[i+2] : traits::identity(), threadScan1[3]);
+            threadScan1[1] = traits::op(((i+1) < numElements) ? d_in[i+1] : traits::identity(), threadScan1[2]);
+            threadScan1[0] = s_out[bi]
+                           = traits::op((i     < numElements) ? d_in[i]   : traits::identity(), threadScan1[1]);
+        }
+        __syncthreads();
+
+        // reverse s_data in shared memory
+        if (ai < CTA_SIZE)
+        {
+            unsigned int leftIdx = ai;
+            unsigned int rightIdx = (2 * CTA_SIZE - 1) - ai;
+
+            if (leftIdx < rightIdx)
+            {
+                T tmp           = s_out[leftIdx];
+                s_out[leftIdx]  = s_out[rightIdx];
+                s_out[rightIdx] = tmp;
+            }
+        }
+        __syncthreads();
+    }
+    else
+    {
+        int i = aiDev * 4;
+        if (traits::isFullBlock() || i + 3 < numElements)
+        {
+            tempData       = inData[aiDev];
+            threadScan0[0] = tempData.x;
+            threadScan0[1] = traits::op(tempData.y, threadScan0[0]);
+            threadScan0[2] = traits::op(tempData.z, threadScan0[1]);
+            threadScan0[3] = s_out[ai]
+                           = traits::op(tempData.w, threadScan0[2]);
+        }
+        else
+        {
+            threadScan0[0] = (i < numElements) ? d_in[i] : traits::identity();
+            threadScan0[1] = traits::op(((i+1) < numElements) ? d_in[i+1] : traits::identity(), threadScan0[0]);
+            threadScan0[2] = traits::op(((i+2) < numElements) ? d_in[i+2] : traits::identity(), threadScan0[1]);
+            threadScan0[3] = s_out[ai]
+                           = traits::op(((i+3) < numElements) ? d_in[i+3] : traits::identity(), threadScan0[2]);
+        }
+
+
+#ifdef DISALLOW_LOADSTORE_OVERLAP
+        __syncthreads();
+#endif
+
+        i = biDev * 4;
+        if (traits::isFullBlock() || i + 3 < numElements)
+        {
+            tempData       = inData[biDev];
+            threadScan1[0] = tempData.x;
+            threadScan1[1] = traits::op(tempData.y, threadScan1[0]);
+            threadScan1[2] = traits::op(tempData.z, threadScan1[1]);
+            threadScan1[3] = s_out[bi]
+                           = traits::op(tempData.w, threadScan1[2]);
+        }
+        else
+        {
+            threadScan1[0] = (i < numElements) ? d_in[i] : traits::identity();
+            threadScan1[1] = traits::op(((i+1) < numElements) ? d_in[i+1] : traits::identity(), threadScan1[0]);
+            threadScan1[2] = traits::op(((i+2) < numElements) ? d_in[i+2] : traits::identity(), threadScan1[1]);
+            threadScan1[3] = s_out[bi]
+                           = traits::op(((i+3) < numElements) ? d_in[i+3] : traits::identity(), threadScan1[2]);
+        }
+        __syncthreads();
+    }
+}
+
+
+/**
+* @brief Handles storing result s_data from shared memory to global memory
+* (vec4 version)
+*
+* Store a chunk of SCAN_ELTS_PER_THREAD*blockDim.x elements from shared memory
+* into a device memory array.  Each thread stores reads two elements from shared
+* memory, adds them to the intermediate sums computed in
+* loadSharedChunkFromMem4(), and writes two T4 elements (where
+* T4 is, e.g. int4 or float4) to global memory.
+*
+* @param[out] d_out The output (device) memory array
+* @param[in] threadScan0 Intermediate per-thread partial sums array 1
+* (contents computed in loadSharedChunkFromMem4())
+* @param[in] threadScan1 Intermediate per-thread partial sums array 2
+* (contents computed in loadSharedChunkFromMem4())
+* @param[in] s_in The input (shared) memory array
+* @param[in] numElements The number of elements in the array being scanned
+* @param[in] oDataOffset the offset of the output array in global memory
+* for this thread block
+* @param[in] ai The shared memory address for the thread's first element
+* (computed in loadSharedChunkFromMem4())
+* @param[in] bi The shared memory address for the thread's second element
+* (computed in loadSharedChunkFromMem4())
+* @param[in] aiDev The device memory address for this thread's first element
+* (computed in loadSharedChunkFromMem4())
+* @param[in] biDev The device memory address for this thread's second element
+* (computed in loadSharedChunkFromMem4())
+*/
+template <class T, class traits>
+__device__ void storeSharedChunkToMem4(T   *d_out,
+                                       T   threadScan0[4],
+                                       T   threadScan1[4],
+                                       T   *s_in,
+                                       int numElements,
+                                       int oDataOffset,
+                                       int ai,
+                                       int bi,
+                                       int aiDev,
+                                       int biDev)
+{
+    // Convert to 4-vector
+    typename typeToVector<T,4>::Result tempData;
+    typename typeToVector<T,4>::Result* outData = (typename typeToVector<T,4>::Result*)d_out;
+
+    // write results to global memory
+    if (traits::isBackward())
+    {
+        if (ai < CTA_SIZE)
+        {
+
+            unsigned int leftIdx = ai;
+            unsigned int rightIdx = (2 * CTA_SIZE - 1) - ai;
+
+            if (leftIdx < rightIdx)
+            {
+                T tmp = s_in[leftIdx];
+                s_in[leftIdx] = s_in[rightIdx];
+                s_in[rightIdx] = tmp;
+            }
+        }
+        __syncthreads();
+
+        T temp = s_in[ai];
+
+        if (traits::isExclusive())
+        {
+            tempData.w = temp;
+            tempData.z = traits::op(temp, threadScan0[3]);
+            tempData.y = traits::op(temp, threadScan0[2]);
+            tempData.x = traits::op(temp, threadScan0[1]);
+        }
+        else
+        {
+            tempData.w = traits::op(temp, threadScan0[3]);
+            tempData.z = traits::op(temp, threadScan0[2]);
+            tempData.y = traits::op(temp, threadScan0[1]);
+            tempData.x = traits::op(temp, threadScan0[0]);
+        }
+
+        int i = aiDev * 4;
+        if (traits::isFullBlock() || i + 3 < numElements)
+        {
+            outData[aiDev] = tempData;
+        }
+        else
+        {
+            if (i   < numElements) { d_out[i]   = tempData.x;
+            if (i+1 < numElements) { d_out[i+1] = tempData.y;
+            if (i+2 < numElements) { d_out[i+2] = tempData.z; }}}
+        }
+
+#ifdef DISALLOW_LOADSTORE_OVERLAP
+        __syncthreads();
+#endif
+
+        temp = s_in[bi];
+
+        if (traits::isExclusive())
+        {
+            tempData.w = temp;
+            tempData.z = traits::op(temp, threadScan1[3]);
+            tempData.y = traits::op(temp, threadScan1[2]);
+            tempData.x = traits::op(temp, threadScan1[1]);
+        }
+        else
+        {
+            tempData.w = traits::op(temp, threadScan1[3]);
+            tempData.z = traits::op(temp, threadScan1[2]);
+            tempData.y = traits::op(temp, threadScan1[1]);
+            tempData.x = traits::op(temp, threadScan1[0]);
+        }
+
+        i = biDev * 4;
+        if (traits::isFullBlock() || i + 3 < numElements)
+        {
+            outData[biDev] = tempData;
+        }
+        else
+        {
+            if (i   < numElements) { d_out[i]   = tempData.x;
+            if (i+1 < numElements) { d_out[i+1] = tempData.y;
+            if (i+2 < numElements) { d_out[i+2] = tempData.z; }}}
+        }
+    }
+    else
+    {
+        T temp;
+        temp = s_in[ai];
+
+        if (traits::isExclusive())
+        {
+            tempData.x = temp;
+            tempData.y = traits::op(temp, threadScan0[0]);
+            tempData.z = traits::op(temp, threadScan0[1]);
+            tempData.w = traits::op(temp, threadScan0[2]);
+        }
+        else
+        {
+            tempData.x = traits::op(temp, threadScan0[0]);
+            tempData.y = traits::op(temp, threadScan0[1]);
+            tempData.z = traits::op(temp, threadScan0[2]);
+            tempData.w = traits::op(temp, threadScan0[3]);
+        }
+
+        int i = aiDev * 4;
+        if (traits::isFullBlock() || i + 3 < numElements)
+        {
+            outData[aiDev] = tempData;
+        }
+        else
+        {
+            // we can't use vec4 because the original array isn't a multiple of
+            // 4 elements
+            if ( i    < numElements) { d_out[i]   = tempData.x;
+            if ((i+1) < numElements) { d_out[i+1] = tempData.y;
+            if ((i+2) < numElements) { d_out[i+2] = tempData.z; } } }
+        }
+
+#ifdef DISALLOW_LOADSTORE_OVERLAP
+        __syncthreads();
+#endif
+
+        temp       = s_in[bi];
+
+        if (traits::isExclusive())
+        {
+            tempData.x = temp;
+            tempData.y = traits::op(temp, threadScan1[0]);
+            tempData.z = traits::op(temp, threadScan1[1]);
+            tempData.w = traits::op(temp, threadScan1[2]);
+        }
+        else
+        {
+            tempData.x = traits::op(temp, threadScan1[0]);
+            tempData.y = traits::op(temp, threadScan1[1]);
+            tempData.z = traits::op(temp, threadScan1[2]);
+            tempData.w = traits::op(temp, threadScan1[3]);
+        }
+
+        i = biDev * 4;
+        if (traits::isFullBlock() || i + 3 < numElements)
+        {
+            outData[biDev] = tempData;
+        }
+        else
+        {
+            // we can't use vec4 because the original array isn't a multiple of
+            // 4 elements
+            if ( i    < numElements) { d_out[i]   = tempData.x;
+            if ((i+1) < numElements) { d_out[i+1] = tempData.y;
+            if ((i+2) < numElements) { d_out[i+2] = tempData.z; } } }
+        }
+    }
+}
+
+/** @brief Scan all warps of a CTA without synchronization
+  *
+  * The warp-scan algorithm breaks a block of data into warp-sized chunks, and
+  * scans the chunks independently with a warp of threads each.  Because warps
+  * execute instructions in SIMD fashion, there is no need to synchronize in
+  * order to share data within a warp (only across warps).  Also, in SIMD the
+  * most efficient algorithm is a step-efficient algorithm.  Therefore, within
+  * each warp we use a Hillis-and-Steele-style scan that takes log2(N) steps
+  * to scan the warp [Daniel Hillis and Guy Steele 1986], rather than the
+  * work-efficient tree-based algorithm described by Guy Blelloch [1990] that
+  * takes 2 * log(N) steps and is in general more complex to implement.
+  * Previous versions of CUDPP used the Blelloch algorithm.  For current GPUs,
+  * the warp size is 32, so this takes five steps per warp.
+  *
+  * Each thread is responsible for a single element of the array to be scanned.
+  * Each thread inputs a single value to the scan via \a val and returns
+  * its own scanned result element.  The threads of each warp cooperate
+  * via the shared memory array \a s_data to scan WARP_SIZE elements.
+  *
+  * Template parameter \a maxlevel allows this warpscan to be performed on
+  * partial warps.  For example, if only the first 8 elements of each warp need
+  * to be scanned, then warpscan only performs log2(8)=3 steps rather than 5.
+  *
+  * The computation uses 2 * WARP_SIZE elements of shared memory per warp to
+  * enable warps to offset beyond their input data and receive the identity
+  * element without using any branch instructions.
+  *
+  * \note s_data is declared volatile here to prevent the compiler from
+  * optimizing away writes to shared memory, and ensure correct intrawarp
+  * communication in the absence of __syncthreads.
+  *
+  * @return The result of the warp scan for the current thread
+  * @param[in] val The current threads's input to the scan
+  * @param[in,out] s_data A pointer to a temporary shared array of 2*CTA_SIZE
+  * elements used to compute the warp scans
+  */
+template<class T, class traits,int maxlevel>
+__device__ T warpscan(T val, volatile T* s_data)
+{
+    // The following is the same as 2 * 32 * warpId + threadInWarp =
+    // 64*(threadIdx.x >> 5) + (threadIdx.x & (WARP_SIZE-1))
+    int idx = 2 * threadIdx.x - (threadIdx.x & (WARP_SIZE-1));
+    s_data[idx] = traits::identity();
+    idx += WARP_SIZE;
+    s_data[idx] = val;                                 __EMUSYNC;
+
+        // This code is needed because the warp size of device emulation
+        // is only 1 thread, so sync-less cooperation within a warp doesn't
+        // work.
+#ifdef __DEVICE_EMULATION__
+    T t = s_data[idx -  1]; __EMUSYNC;
+    s_data[idx] = traits::op((const T&)s_data[idx],t); __EMUSYNC;
+    t = s_data[idx -  2]; __EMUSYNC;
+    s_data[idx] = traits::op((const T&)s_data[idx],t); __EMUSYNC;
+    t = s_data[idx -  4]; __EMUSYNC;
+    s_data[idx] = traits::op((const T&)s_data[idx],t); __EMUSYNC;
+    t = s_data[idx -  8]; __EMUSYNC;
+    s_data[idx] = traits::op((const T&)s_data[idx],t); __EMUSYNC;
+    t = s_data[idx - 16]; __EMUSYNC;
+    s_data[idx] = traits::op((const T&)s_data[idx],t); __EMUSYNC;
+#else
+    if (0 <= maxlevel) { s_data[idx] = traits::op((const T&)s_data[idx], (const T&)s_data[idx - 1]); }
+    if (1 <= maxlevel) { s_data[idx] = traits::op((const T&)s_data[idx], (const T&)s_data[idx - 2]); }
+    if (2 <= maxlevel) { s_data[idx] = traits::op((const T&)s_data[idx], (const T&)s_data[idx - 4]); }
+    if (3 <= maxlevel) { s_data[idx] = traits::op((const T&)s_data[idx], (const T&)s_data[idx - 8]); }
+    if (4 <= maxlevel) { s_data[idx] = traits::op((const T&)s_data[idx], (const T&)s_data[idx -16]); }
+#endif
+
+    return s_data[idx-1];      // convert inclusive -> exclusive
+}
+
+/** @brief Perform a full CTA scan using the warp-scan algorithm
+  *
+  * As described in the comment for warpscan(), the warp-scan algorithm breaks
+  * a block of data into warp-sized chunks, and scans the chunks independently
+  * with a warp of threads each.  To complete the scan, each warp <i>j</i> then
+  * writes its last element to element <i>j</i> of a temporary shared array.
+  * Then a single warp exclusive-scans these "warp sums".  Finally, each thread
+  * adds the result of the warp sum scan to the result of the scan from the
+  * first pass.
+  *
+  * Because we scan 2*CTA_SIZE elements per thread, we have to call warpscan
+  * twice.
+  *
+  * @param x The first input value for the current thread
+  * @param y The second input value for the current thread
+  * @param s_data Temporary shared memory space of 2*CTA_SIZE elements for
+  * performing the scan
+  */
+template <class T, class traits>
+__device__ void scanWarps(T x, T y,
+                          T *s_data)
+{
+    T val  = warpscan<T, traits, 4>(x, s_data);
+    __syncthreads();
+    T val2 = warpscan<T, traits, 4>(y, s_data);
+
+    int idx = threadIdx.x;
+
+    if ((idx & 31)==31)
+    {
+        s_data[idx >> 5]                = traits::op(val, x);
+        s_data[(idx + blockDim.x) >> 5] = traits::op(val2, y);
+    }
+    __syncthreads();
+
+#ifndef __DEVICE_EMULATION__
+    if (idx < 32)
+#endif
+    {
+        s_data[idx] = warpscan<T,traits,(LOG_CTA_SIZE-LOG_WARP_SIZE+1)>(s_data[idx], s_data);
+    }
+    __syncthreads();
+
+    val  = traits::op(val, s_data[idx >> 5]);
+
+    val2 = traits::op(val2, s_data[(idx + blockDim.x) >> 5]);
+
+    __syncthreads();
+
+    s_data[idx] = val;
+    s_data[idx+blockDim.x] = val2;
+}
+
+/**
+* @brief CTA-level scan routine; scans s_data in shared memory in each thread block
+*
+* This function is the main CTA-level scan function.  It may be called by other
+* CUDA __global__ or __device__ functions. This function scans 2 * CTA_SIZE elements.
+* Each thread is responsible for one element in each half of the input array.
+* \note This code is intended to be run on a CTA of 128 threads.  Other sizes are
+* untested.
+*
+* @param[in] s_data The array to be scanned in shared memory
+* @param[out] d_blockSums Array of per-block sums
+* @param[in] blockSumIndex Location in \a d_blockSums to which to write this block's sum
+*/
+template <class T, class traits>
+__device__ void scanCTA(T            *s_data,
+                        T            *d_blockSums,
+                        unsigned int blockSumIndex)
+{
+    T val  = s_data[threadIdx.x];
+    T val2 = s_data[threadIdx.x + blockDim.x];
+    __syncthreads();
+
+    scanWarps<T,traits>(val, val2, s_data);
+    __syncthreads();
+
+    if (traits::writeSums() && threadIdx.x == blockDim.x - 1)
+    {
+        d_blockSums[blockSumIndex] = traits::op(val2, s_data[threadIdx.x + blockDim.x]);
+    }
+
+
+#ifdef __DEVICE_EMULATION__
+    // must sync in emulation mode when doing backward scans, because otherwise the
+    // shared memory array will get reversed before the block sums are read!
+    if (traits::isBackward())
+        __syncthreads();
+#endif
+}
+
+
+/** @} */ // end scan functions
+/** @} */ // end cudpp_cta
diff --git a/examples/common/src/cudpp/scan_kernel.cu b/examples/common/src/cudpp/scan_kernel.cu
new file mode 100644
--- /dev/null
+++ b/examples/common/src/cudpp/scan_kernel.cu
@@ -0,0 +1,113 @@
+// -------------------------------------------------------------
+// cuDPP -- CUDA Data Parallel Primitives library
+// -------------------------------------------------------------
+//  $Revision: 5633 $
+//  $Date: 2009-07-01 15:02:51 +1000 (Wed, 01 Jul 2009) $
+// -------------------------------------------------------------
+// This source code is distributed under the terms of license.txt
+// in the root directory of this source distribution.
+// -------------------------------------------------------------
+
+/**
+ * @file
+ * scan_kernel.cu
+ *
+ * @brief CUDPP kernel-level scan routines
+ */
+
+/** \defgroup cudpp_kernel CUDPP Kernel-Level API
+  * The CUDPP Kernel-Level API contains functions that run on the GPU
+  * device across a grid of Cooperative Thread Array (CTA, aka Thread
+  * Block).  These kernels are declared \c __global__ so that they
+  * must be invoked from host (CPU) code.  They generally invoke GPU
+  * \c __device__ routines in the CUDPP \link cudpp_cta CTA-Level API\endlink.
+  * Kernel-Level API functions are used by CUDPP
+  * \link cudpp_app Application-Level\endlink functions to implement their
+  * functionality.
+  * @{
+  */
+
+/** @name Scan Functions
+* @{
+*/
+
+#include "cudpp/cudpp_globals.h"
+#include "cudpp/scan_cta.cu"
+#include "cudpp/shared_mem.h"
+
+/**
+  * @brief Main scan kernel
+  *
+  * This __global__ device function performs one level of a multiblock scan on
+  * an arbitrary-dimensioned array in \a d_in, returning the result in \a d_out
+  * (which may point to the same array).  The same function may be used for
+  * single or multi-row scans.  To perform a multirow scan, pass the width of
+  * each row of the input row (in elements) in \a dataRowPitch, and the width of
+  * the rows of \a d_blockSums (in elements) in \a blockSumRowPitch, and invoke
+  * with a thread block grid with height greater than 1.
+  *
+  * This function peforms one level of a recursive, multiblock scan.  At the
+  * app level, this function is called by cudppScan and cudppMultiScan and used
+  * in combination with vectorAddUniform4() to produce a complete scan.
+  *
+  * Template parameter \a T is the datatype of the array to be scanned.
+  * Template parameter \a traits is the ScanTraits struct containing
+  * compile-time options for the scan, such as whether it is forward or
+  * backward, exclusive or inclusive, multi- or single-row, etc.
+  *
+  * @param[out] d_out The output (scanned) array
+  * @param[in]  d_in The input array to be scanned
+  * @param[out] d_blockSums The array of per-block sums
+  * @param[in]  numElements The number of elements to scan
+  * @param[in]  dataRowPitch The width of each row of \a d_in in elements
+  * (for multi-row scans)
+  * @param[in]  blockSumRowPitch The with of each row of \a d_blockSums in elements
+  * (for multi-row scans)
+  */
+template<class T, class traits>
+__global__ void scan4(T            *d_out,
+                      const T      *d_in,
+                      T            *d_blockSums,
+                      int          numElements,
+                      unsigned int dataRowPitch,
+                      unsigned int blockSumRowPitch)
+{
+    SharedMemory<T> smem;
+    T* temp = smem.getPointer();
+
+    int devOffset, ai, bi, aiDev, biDev;
+    T threadScan0[4], threadScan1[4];
+
+    unsigned int blockN = numElements;
+    unsigned int blockSumIndex = blockIdx.x;
+
+    if (traits::isMultiRow())
+    {
+        //int width = __mul24(gridDim.x, blockDim.x) << 1;
+        int yIndex     = __umul24(blockDim.y, blockIdx.y) + threadIdx.y;
+        devOffset      = __umul24(dataRowPitch, yIndex);
+        blockN        += (devOffset << 2);
+        devOffset     += __umul24(blockIdx.x, blockDim.x << 1);
+        blockSumIndex += __umul24(blockSumRowPitch << 2, yIndex) ;
+    }
+    else
+    {
+        devOffset = __umul24(blockIdx.x, (blockDim.x << 1));
+    }
+
+    // load data into shared memory
+    loadSharedChunkFromMem4<T, traits>
+        (temp, threadScan0, threadScan1, d_in,
+         blockN, devOffset, ai, bi, aiDev, biDev);
+
+    scanCTA<T, traits>(temp, d_blockSums, blockSumIndex);
+
+    // write results to device memory
+    storeSharedChunkToMem4<T, traits>
+        (d_out, threadScan0, threadScan1, temp,
+         blockN, devOffset, ai, bi, aiDev, biDev);
+
+}
+
+/** @} */ // end scan functions
+/** @} */ // end cudpp_kernel
diff --git a/examples/common/src/cudpp/vector_kernel.cu b/examples/common/src/cudpp/vector_kernel.cu
new file mode 100644
--- /dev/null
+++ b/examples/common/src/cudpp/vector_kernel.cu
@@ -0,0 +1,444 @@
+// -------------------------------------------------------------
+// CUDPP -- CUDA Data Parallel Primitives library
+// -------------------------------------------------------------
+//  $Revision: 5632 $
+//  $Date: 2009-07-01 14:36:01 +1000 (Wed, 01 Jul 2009) $
+// -------------------------------------------------------------
+// This source code is distributed under the terms of license.txt in
+// the root directory of this source distribution.
+// -------------------------------------------------------------
+
+/**
+ * @file
+ * vector_kernel.cu
+ *
+ * @brief CUDA kernel methods for basic operations on vectors.
+ *
+ * CUDA kernel methods for basic operations on vectors.
+ *
+ * Examples:
+ * - vectorAddConstant(): d_vector + constant
+ * - vectorAddUniform():  d_vector + uniform (per-block constants)
+ * - vectorAddVectorVector(): d_vector + d_vector
+ */
+
+// MJH: these functions assume there are 2N elements for N threads.
+// Is this always going to be a good idea?  There may be cases where
+// we have as many threads as elements, but for large problems
+// we are probably limited by max CTA size for simple kernels like
+// this so we should process multiple elements per thread.
+// we may want to extend these with looping versions that process
+// many elements per thread.
+
+// #include "cudpp_util.h"
+// #include "sharedmem.h"
+// #include "cudpp.h"
+
+/** \addtogroup cudpp_kernel
+  * @{
+  */
+
+/** @name Vector Functions
+ * CUDA kernel methods for basic operations on vectors.
+ * @{
+ */
+
+#if 0
+/** @brief Adds a constant value to all values in the input d_vector
+ *
+ * Each thread adds two pairs of elements.
+ * @todo Test this function -- it is currently not yet used.
+ *
+ * @param[in,out] d_vector The array of elements to be modified
+ * @param[in] constant The constant value to be added to elements of
+ * \a d_vector
+ * @param[in] n The number of elements in the d_vector to be modified
+ * @param[in] baseIndex An optional offset to the beginning of the
+ * elements in the input array to be processed
+ */
+template <class T>
+__global__  void vectorAddConstant(T   *d_vector,
+                                   T   constant,
+                                   int n,
+                                   int baseIndex)
+{
+    // Compute this thread's output address
+    unsigned int address = baseIndex + threadIdx.x +
+        __mul24(blockIdx.x, (blockDim.x << 1));
+
+    // note two adds per thread: one in first half of the block, one in last
+    d_vector[address]              += constant;
+    d_vector[address + blockDim.x] += (threadIdx.x + blockDim.x < n) * constant;
+}
+#endif
+#if 0
+ /** @brief Add a uniform value to each data element of an array
+  *
+  * This function reads one value per CTA from \a d_uniforms into shared
+  * memory and adds that value to all values "owned" by the CTA in \a
+  * d_vector.  Each thread adds two pairs of values.
+  *
+  * @param[out] d_vector The d_vector whose values will have the uniform added
+  * @param[in] d_uniforms The array of uniform values (one per CTA)
+  * @param[in] numElements The number of elements in \a d_vector to process
+  * @param[in] blockOffset an optional offset to the beginning of this block's
+  * data.
+  * @param[in] baseIndex an optional offset to the beginning of the array
+  * within \a d_vector.
+  */
+template <class T>
+__global__ void vectorAddUniform(T       *d_vector,
+                                 const T *d_uniforms,
+                                 int     numElements,
+                                 int     blockOffset,
+                                 int     baseIndex)
+{
+    __shared__ T uni;
+    // Get this block's uniform value from the uniform array in device memory
+    // We store it in shared memory so that the hardware's shared memory
+    // broadcast capability can be used to share among all threads in each warp
+    // in a single cycle
+    if (threadIdx.x == 0)
+    {
+        uni = d_uniforms[blockIdx.x + __mul24(gridDim.x, blockIdx.y) + blockOffset];
+    }
+
+    // Compute this thread's output address
+    int width = __mul24(gridDim.x,(blockDim.x << 1));
+
+    unsigned int address = baseIndex + __mul24(width, blockIdx.y)
+        + threadIdx.x + __mul24(blockIdx.x, (blockDim.x << 1));
+
+    __syncthreads();
+
+    // note two adds per thread: one in first half of the block, one in last
+    d_vector[address]              += uni;
+    if (threadIdx.x + blockDim.x < numElements) d_vector[address + blockDim.x] += uni;
+}
+#endif
+
+/** @brief Add a uniform value to each data element of an array (vec4 version)
+  *
+  * This function reads one value per CTA from \a d_uniforms into shared
+  * memory and adds that value to all values "owned" by the CTA in \a d_vector.
+  * Each thread adds the uniform value to eight values in \a d_vector.
+  *
+  * @param[out] d_vector The d_vector whose values will have the uniform added
+  * @param[in] d_uniforms The array of uniform values (one per CTA)
+  * @param[in] numElements The number of elements in \a d_vector to process
+  * @param[in] vectorRowPitch For 2D arrays, the pitch (in elements) of the
+  * rows of \a d_vector.
+  * @param[in] uniformRowPitch For 2D arrays, the pitch (in elements) of the
+  * rows of \a d_uniforms.
+  * @param[in] blockOffset an optional offset to the beginning of this block's
+  * data.
+  * @param[in] baseIndex an optional offset to the beginning of the array
+  * within \a d_vector.
+  */
+template <class T, class op, int elementsPerThread>
+__global__ void vectorAddUniform4(T       *d_vector,
+                                  const T *d_uniforms,
+                                  int      numElements,
+                                  int      vectorRowPitch,     // width of input array in elements
+                                  int      uniformRowPitch,    // width of uniform array in elements
+                                  int      blockOffset,
+                                  int      baseIndex)
+{
+    __shared__ T uni;
+    // Get this block's uniform value from the uniform array in device memory
+    // We store it in shared memory so that the hardware's shared memory
+    // broadcast capability can be used to share among all threads in each warp
+    // in a single cycle
+    if (threadIdx.x == 0)
+    {
+        uni = d_uniforms[blockIdx.x + __umul24(uniformRowPitch, blockIdx.y) + blockOffset];
+    }
+
+    // Compute this thread's output address
+    //int width = __mul24(gridDim.x,(blockDim.x << 1));
+
+    unsigned int address = baseIndex + __umul24(vectorRowPitch, blockIdx.y)
+        + threadIdx.x + __umul24(blockIdx.x, (blockDim.x * elementsPerThread));
+    numElements += __umul24(vectorRowPitch, blockIdx.y);
+
+    __syncthreads();
+
+    for (int i = 0; i < elementsPerThread && address < numElements; i++)
+    {
+        d_vector[address] = op::apply(d_vector[address], uni);
+        address += blockDim.x;
+    }
+}
+
+#if 0
+/** @brief Adds together two vectors
+ *
+ * Each thread adds two pairs of elements.
+ * @todo Test this function -- it is currently not yet used.
+ *
+ * @param[out] d_vectorA The left operand array and the result
+ * @param[in] d_vectorB The right operand array
+ * @param[in] numElements The number of elements in the vectors to be added.
+ * @param[in] baseIndex An optional offset to the beginning of the
+ * elements in the input arrays to be processed
+ */
+template <class T>
+__global__ void vectorAddVector(T       *d_vectorA,        // A += B
+                                const T *d_vectorB,
+                                int     numElements,
+                                int     baseIndex)
+{
+    // Compute this thread's output address
+    unsigned int address = baseIndex + threadIdx.x +
+        __mul24(blockIdx.x, (blockDim.x << 1));
+
+    // note two adds per thread: one in first half of the block, one in last
+    d_vectorA[address]              += d_vectorB[address];
+    d_vectorA[address + blockDim.x] +=
+        (threadIdx.x + blockDim.x < numElements) * d_vectorB[address];
+}
+#endif
+
+/** @brief Add a uniform value to data elements of an array (vec4 version)
+  *
+  * This function reads one value per CTA from \a d_uniforms into shared
+  * memory and adds that value to values "owned" by the CTA in \a d_vector.
+  * The uniform value is added to only those values "owned" by the CTA which
+  * have an index less than d_maxIndex. If d_maxIndex for that CTA is UINT_MAX
+  * it adds the uniform to all values "owned" by the CTA.
+  * Each thread adds the uniform value to eight values in \a d_vector.
+  *
+  * @param[out] d_vector The d_vector whose values will have the uniform added
+  * @param[in] d_uniforms The array of uniform values (one per CTA)
+  * @param[in] d_maxIndices The array of maximum indices (one per CTA). This is
+  *            index upto which the uniform would be added. If this is UINT_MAX
+  *            the uniform is added to all elements of the CTA. This index is
+  *            1-based.
+  * @param[in] numElements The number of elements in \a d_vector to process
+  * @param[in] blockOffset an optional offset to the beginning of this block's
+  * data.
+  * @param[in] baseIndex an optional offset to the beginning of the array
+  * within \a d_vector.
+  */
+template <class T, class op, bool isLastBlockFull>
+__global__ void vectorSegmentedAddUniform4(T                  *d_vector,
+                                           const T            *d_uniforms,
+                                           const unsigned int *d_maxIndices,
+                                           unsigned int       numElements,
+                                           int                blockOffset,
+                                           int                baseIndex)
+{
+    __shared__ T uni[2];
+
+    unsigned int blockAddress =
+        blockIdx.x + __mul24(gridDim.x, blockIdx.y) + blockOffset;
+
+    // Get this block's uniform value from the uniform array in device memory
+    // We store it in shared memory so that the hardware's shared memory
+    // broadcast capability can be used to share among all threads in each warp
+    // in a single cycle
+
+    if (threadIdx.x == 0)
+    {
+        if (blockAddress > 0)
+            uni[0] = d_uniforms[blockAddress-1];
+        else
+            uni[0] = op::identity();
+
+        // Tacit assumption that T is four-byte wide
+        uni[1] = (T)(d_maxIndices[blockAddress]);
+    }
+
+    // Compute this thread's output address
+    int width = __mul24(gridDim.x,(blockDim.x << 1));
+
+    unsigned int address = baseIndex + __mul24(width, blockIdx.y)
+                           + threadIdx.x + __mul24(blockIdx.x, (blockDim.x << 3));
+
+    __syncthreads();
+
+    unsigned int maxIndex = (unsigned int)(uni[1]);
+
+    bool isLastBlock = (blockIdx.x == (gridDim.x-1));
+
+    if (maxIndex < UINT_MAX)
+    {
+        // Since maxIndex is a 1 based index
+        --maxIndex;
+        bool leftLess = address < maxIndex;
+        bool rightLess = (address + 7 * blockDim.x) < maxIndex;
+
+        if (leftLess)
+        {
+            if (rightLess)
+            {
+                for (unsigned int i = 0; i < 8; ++i)
+                    d_vector[address + i * blockDim.x] =
+                        op::apply(d_vector[address + i * blockDim.x], uni[0]);
+            }
+            else
+            {
+                for (unsigned int i=0; i < 8; ++i)
+                {
+                    if (address < maxIndex)
+                        d_vector[address] =
+                            op::apply(d_vector[address], uni[0]);
+
+                    address += blockDim.x;
+                }
+            }
+        }
+    }
+    else
+    {
+        if (!isLastBlockFull && isLastBlock)
+        {
+            for (unsigned int i = 0; i < 8; ++i)
+            {
+                if (address < numElements)
+                    d_vector[address] =
+                        op::apply(d_vector[address], uni[0]);
+
+                address += blockDim.x;
+            }
+        }
+        else
+        {
+            for (unsigned int i=0; i<8; ++i)
+            {
+                d_vector[address] =
+                    op::apply(d_vector[address], uni[0]);
+
+                address += blockDim.x;
+            }
+        }
+    }
+}
+
+
+/** @brief Add a uniform value to data elements of an array (vec4 version)
+  *
+  * This function reads one value per CTA from \a d_uniforms into shared
+  * memory and adds that value to values "owned" by the CTA in \a d_vector.
+  * The uniform value is added to only those values "owned" by the CTA which
+  * have an index greater than d_minIndex. If d_minIndex for that CTA is 0
+  * it adds the uniform to all values "owned" by the CTA.
+  * Each thread adds the uniform value to eight values in \a d_vector.
+  *
+  * @param[out] d_vector The d_vector whose values will have the uniform added
+  * @param[in] d_uniforms The array of uniform values (one per CTA)
+  * @param[in] d_minIndices The array of minimum indices (one per CTA). The
+  *            uniform is added to the right of this index (that is, to every index
+  *            that is greater than this index). If this is 0, the uniform is
+  *            added to all elements of the CTA. This index is 1-based to
+  *            prevent overloading of what 0 means. In our case it means
+  *            absence of a flag. But if the first element of a CTA has
+  *            flag the index will also be 0. Hence we use 1-based indices
+  *            so the index is 1 in the latter case.
+  * @param[in] numElements The number of elements in \a d_vector to process
+  * @param[in] blockOffset an optional offset to the beginning of this block's
+  * data.
+  * @param[in] baseIndex an optional offset to the beginning of the array
+  * within \a d_vector.
+  *
+  */
+template <class T, class op, bool isLastBlockFull>
+__global__ void vectorSegmentedAddUniformToRight4(T                  *d_vector,
+                                                  const T            *d_uniforms,
+                                                  const unsigned int *d_minIndices,
+                                                  unsigned int       numElements,
+                                                  int                blockOffset,
+                                                  int                baseIndex)
+{
+    __shared__ T uni[2];
+
+    unsigned int blockAddress =
+        blockIdx.x + __mul24(gridDim.x, blockIdx.y) + blockOffset;
+
+    // Get this block's uniform value from the uniform array in device memory
+    // We store it in shared memory so that the hardware's shared memory
+    // broadcast capability can be used to share among all threads in each warp
+    // in a single cycle
+
+    if (threadIdx.x == 0)
+    {
+        // FIXME - blockAddress test here is incompatible with how it is calculated
+        // above
+        if (blockAddress < (gridDim.x-1))
+            uni[0] = d_uniforms[blockAddress+1];
+        else
+            uni[0] = op::identity();
+
+        // Tacit assumption that T is four-byte wide
+        uni[1] = (T)(d_minIndices[blockAddress]);
+    }
+
+    // Compute this thread's output address
+    int width = __mul24(gridDim.x,(blockDim.x << 1));
+
+    unsigned int address = baseIndex + __mul24(width, blockIdx.y)
+                           + threadIdx.x + __mul24(blockIdx.x, (blockDim.x << 3));
+
+    __syncthreads();
+
+    unsigned int minIndex = (unsigned int)(uni[1]);
+
+    bool isLastBlock = (blockIdx.x == (gridDim.x-1));
+
+    if (minIndex > 0)
+    {
+        // Since minIndex is a 1 based index
+        --minIndex;
+        bool leftInRange = address > minIndex;
+        bool rightInRange = (address + 7 * blockDim.x) > minIndex;
+
+        if (rightInRange)
+        {
+            if (leftInRange)
+            {
+                for (unsigned int i = 0; i < 8; ++i)
+                    d_vector[address + i * blockDim.x] =
+                        op::apply(d_vector[address + i * blockDim.x], uni[0]);
+            }
+            else
+            {
+                for (unsigned int i=0; i < 8; ++i)
+                {
+                    if (address > minIndex)
+                        d_vector[address] =
+                            op::apply(d_vector[address], uni[0]);
+
+                    address += blockDim.x;
+                }
+            }
+        }
+    }
+    else
+    {
+        if (!isLastBlockFull && isLastBlock)
+        {
+            for (unsigned int i = 0; i < 8; ++i)
+            {
+                if (address < numElements)
+                    d_vector[address] =
+                        op::apply(d_vector[address], uni[0]);
+
+                address += blockDim.x;
+            }
+        }
+        else
+        {
+            for (unsigned int i=0; i<8; ++i)
+            {
+                d_vector[address] =
+                    op::apply(d_vector[address], uni[0]);
+
+                address += blockDim.x;
+            }
+        }
+    }
+}
+
+
+/** @} */ // end d_vector functions
+/** @} */ // end cudpp_kernel
diff --git a/examples/src/bandwidthTest/BandwidthTest.hs b/examples/src/bandwidthTest/BandwidthTest.hs
new file mode 100644
--- /dev/null
+++ b/examples/src/bandwidthTest/BandwidthTest.hs
@@ -0,0 +1,221 @@
+--------------------------------------------------------------------------------
+--
+-- Module    : Fold
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Reduce a vector to a single value
+--
+--------------------------------------------------------------------------------
+
+module Main where
+
+-- Friends
+import PrettyPrint
+
+-- System
+import Numeric
+import Data.List
+import Control.Monad
+import Control.Exception
+import System.Exit
+import System.Environment
+import System.Console.GetOpt
+import Text.PrettyPrint
+
+import Foreign
+import qualified Foreign.CUDA               as CUDA
+import qualified Foreign.CUDA.Runtime.Event as CUDA
+
+
+--------------------------------------------------------------------------------
+-- Options
+--------------------------------------------------------------------------------
+
+data TestMode = Quick | Range | Shmoo
+  deriving (Eq,Show,Read)
+
+data MemoryMode = List | Pageable | Pinned | WriteCombined
+  deriving (Eq,Show,Read,Ord,Enum)
+
+data CopyMode = HostToDevice | DeviceToHost | DeviceToDevice
+  deriving (Eq,Show,Read,Ord,Enum)
+
+data Options = Options
+  { testMode   :: TestMode
+--  , memoryMode :: [MemoryMode]
+--  , copyMode   :: [CopyMode]
+  , device     :: Int
+  , range      :: (Int,Int,Int)
+  }
+  deriving (Show)
+
+defaultOptions :: Options
+defaultOptions = Options
+  { device     = 0
+  , testMode   = Quick
+--  , memoryMode = []
+--  , copyMode   = []
+  , range      = (kilobyte, kilobyte, 10*kilobyte)
+  }
+
+kilobyte, megabyte :: Int
+kilobyte = 1 `shift` 10
+megabyte = 1 `shift` 20
+
+options :: [OptDescr (Options -> Options)]
+options =
+  [ Option ['d'] ["device"]    (ReqArg (\d opts -> opts { device = read d }) "ID")                           "numeral of device to test"
+  , Option ['t'] ["test"]      (ReqArg (\t opts -> opts { testMode = read t}) "MODE")                        "testing mode: Quick | Range | Shmoo"
+--  , Option ['m'] ["memory"]    (ReqArg (\m opts -> opts { memoryMode = read m : memoryMode opts}) "MODE")    "memory copy mode: List | Pageable | Pinned | WriteCombined"
+--  , Option ['c'] ["copy"]      (ReqArg (\c opts -> opts { copyMode = read c : copyMode opts}) "DIRECTION")   "memory copy direction: DeviceToHost | HostToDevice | DeviceToDevice"
+  , Option ['s'] ["start"]     (ReqArg (\s opts -> opts { range = let (_,i,e) = range opts in (read s,i,e)}) "BYTES") "starting transfer size"
+  , Option ['i'] ["increment"] (ReqArg (\i opts -> opts { range = let (s,_,e) = range opts in (s,read i,e)}) "BYTES") "transfer test size increment"
+  , Option ['e'] ["end"]       (ReqArg (\e opts -> opts { range = let (s,i,_) = range opts in (s,i,read e)}) "BYTES") "ending transfer size"
+  ]
+
+
+--------------------------------------------------------------------------------
+-- Testing
+--------------------------------------------------------------------------------
+
+-- Benchmarking
+--
+bench :: Int -> IO a -> IO Double
+bench n testee =
+  let iter = 10
+      size = fromIntegral (iter * n) * fromIntegral (sizeOf (undefined::Int))
+  in
+  bracket (CUDA.create []) CUDA.destroy $ \start ->
+  bracket (CUDA.create []) CUDA.destroy $ \stop  -> do
+    CUDA.record start Nothing
+    replicateM_ iter testee
+    CUDA.record stop  Nothing
+    CUDA.sync
+    ms <- realToFrac `fmap` CUDA.elapsedTime start stop
+    return $ 1E3 * size / (fromIntegral megabyte * ms)
+
+
+-- Bandwidth testing for the various copy modes
+--
+bandwidth :: CopyMode -> MemoryMode -> Int -> IO Double
+
+bandwidth HostToDevice List     n =
+  bench n (CUDA.withListArray [1..n] (\_ -> return ()))
+
+bandwidth HostToDevice Pageable n =
+  CUDA.allocaArray n $ \d_ptr ->
+  withArray [1..n]   $ \h_ptr ->
+  bench n (CUDA.pokeArray n h_ptr d_ptr)
+
+bandwidth HostToDevice x n        =
+  CUDA.allocaArray n $ \d_ptr ->
+  let f = if x == WriteCombined then [CUDA.WriteCombined] else [] in
+  bracket (CUDA.mallocHostArray f n) (CUDA.freeHost) $ \h_ptr -> do
+  pokeArray (CUDA.useHostPtr h_ptr) [1..n]
+  bench n (CUDA.pokeArrayAsync n h_ptr d_ptr Nothing)
+
+bandwidth DeviceToHost List     n =
+  CUDA.withListArray [1..n] $ \d_ptr ->
+  bench n (CUDA.peekListArray n d_ptr)
+
+bandwidth DeviceToHost Pageable n =
+  allocaArray n             $ \h_ptr ->
+  CUDA.withListArray [1..n] $ \d_ptr ->
+  bench n (CUDA.peekArray n d_ptr h_ptr)
+
+bandwidth DeviceToHost x n        =
+  let f = if x == WriteCombined then [CUDA.WriteCombined] else [] in
+  bracket (CUDA.mallocHostArray f n) (CUDA.freeHost) $ \h_ptr ->
+  CUDA.withListArray [1..n]                          $ \d_ptr ->
+  bench n (CUDA.peekArrayAsync n d_ptr h_ptr Nothing)
+
+bandwidth DeviceToDevice _ n      =
+  CUDA.withListArray [1..n] $ \d_src ->
+  CUDA.allocaArray n        $ \d_dst ->
+  (\x->2*x) `fmap` bench n (CUDA.copyArray n d_src d_dst)
+
+
+-- Testing modes
+--
+runTests :: [Int] -> IO [(String,[Double])]
+runTests bytes = sequence $
+  [ run m c | m <- [List ..], c <- [HostToDevice,DeviceToHost]] ++ [ run Pageable DeviceToDevice ]
+  where
+    run m c =
+      mapM (\b -> bandwidth c m (b `div` sizeOf (undefined::Int))) bytes >>= \t ->
+      return (sc c ++ sm m, t)
+
+    sc DeviceToHost   = "D->H"
+    sc HostToDevice   = "H->D"
+    sc DeviceToDevice = "D->D"
+    sm List           = " (List)"
+    sm Pinned         = " (Pinned)"
+    sm WriteCombined  = " (WC)"
+    sm Pageable       = ""
+
+
+--------------------------------------------------------------------------------
+-- Main
+--------------------------------------------------------------------------------
+
+printQuick :: [(String,[Double])] -> IO ()
+printQuick = printDoc . ppAsRows 1 . (++) header . map k
+  where
+    k (x,y) = text x : map float' y
+    float'  = text . flip (showFFloat (Just 2)) ""
+    header  = map (map text) [["Mode", "Bandwidth (MB/s)"]
+                             ,["----", "----------------"]]
+
+printMany :: [Int] -> [(String,[Double])] -> IO ()
+printMany xs tests =
+  printDoc . ppAsRows 1 . (++) header . zipWith k xs . transpose . snd . unzip $ tests
+  where
+    k b ys  = int b : map float' ys
+    float'  = text . flip (showFFloat (Just 2)) ""
+
+    titles    = "Size (bytes)" : fst (unzip tests)
+    seperator = map (flip replicate '-' . length) $ titles
+    header    = map (map text) [titles,seperator]
+
+
+parseOptions :: [String] -> IO (Options, [String])
+parseOptions argv =
+  case getOpt Permute options argv of
+    (o,n,[])   -> return (foldl (flip id) defaultOptions o, n)
+    (_,_,errs) -> do putStrLn $ concat errs ++ usageInfo header options
+                     exitFailure
+  where
+    header = "Usage: bandwidthTest [OPTION...]"
+
+shmooBytes :: [Int]
+shmooBytes =
+  [    kilobyte,   2*kilobyte  ..  20*kilobyte ] ++
+  [ 22*kilobyte,  24*kilobyte  ..  50*kilobyte ] ++
+  [ 60*kilobyte,  70*kilobyte  .. 100*kilobyte ] ++
+  [ 200*kilobyte, 300*kilobyte ..   1*megabyte ] ++
+  [   2*megabyte,   3*megabyte ..  16*megabyte ] ++
+  [  18*megabyte,  20*megabyte ..  32*megabyte ] ++
+  [  36*megabyte,  40*megabyte ..  64*megabyte ]
+
+-- Main
+--
+main :: IO ()
+main = do
+  (opts,_) <- parseOptions =<< getArgs
+  props    <- CUDA.props (device opts)
+  putStrLn $  "Device " ++ show (device opts) ++ ": " ++ (CUDA.deviceName props)
+
+  let (s,i,e) = range opts
+      bytes   = [s,(s+i)..e]
+
+  case testMode opts of
+    Quick -> putStrLn "Quick mode: Bandwidth of 32MB transfer"
+    _     -> putStrLn "Bandwidth measured in MB/s"
+
+  putStrLn "Please wait...\n"
+  case testMode opts of
+    Range -> runTests bytes         >>= printMany bytes
+    Shmoo -> runTests shmooBytes    >>= printMany shmooBytes
+    Quick -> runTests [32*megabyte] >>= printQuick
+
diff --git a/examples/src/bandwidthTest/Makefile b/examples/src/bandwidthTest/Makefile
new file mode 100644
--- /dev/null
+++ b/examples/src/bandwidthTest/Makefile
@@ -0,0 +1,18 @@
+#
+# Baking!
+#
+
+# ------------------------------------------------------------------------------
+# Input files
+# ------------------------------------------------------------------------------
+EXECUTABLE      := bandwidthTest
+
+HSMAIN          := BandwidthTest.hs
+CUFILES         :=
+
+EXTRALIBS       :=
+
+# ------------------------------------------------------------------------------
+# Haskell/CUDA build system
+# ------------------------------------------------------------------------------
+include ../../common/common.mk
diff --git a/examples/src/bandwidthTest/results/GT120.pdf b/examples/src/bandwidthTest/results/GT120.pdf
new file mode 100644
Binary files /dev/null and b/examples/src/bandwidthTest/results/GT120.pdf differ
diff --git a/examples/src/bandwidthTest/results/Tesla.pdf b/examples/src/bandwidthTest/results/Tesla.pdf
new file mode 100644
Binary files /dev/null and b/examples/src/bandwidthTest/results/Tesla.pdf differ
diff --git a/examples/src/fold/Fold.chs b/examples/src/fold/Fold.chs
new file mode 100644
--- /dev/null
+++ b/examples/src/fold/Fold.chs
@@ -0,0 +1,79 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+--------------------------------------------------------------------------------
+--
+-- Module    : Fold
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Reduce a vector to a single value
+--
+--------------------------------------------------------------------------------
+
+module Main where
+
+#include "fold.h"
+
+-- Friends
+import C2HS
+import Time
+import RandomVector
+
+-- System
+import Control.Exception
+import qualified Foreign.CUDA.Runtime as CUDA
+
+
+--------------------------------------------------------------------------------
+-- Reference
+--------------------------------------------------------------------------------
+
+foldRef :: Num e => [e] -> IO e
+foldRef xs = do
+  (t,r) <- benchmark 100 (evaluate (foldl (+) 0 xs)) (return ())
+  putStrLn $ "== Reference: " ++ shows (fromInteger (timeIn millisecond t)/100::Float) " ms"
+  return r
+
+--------------------------------------------------------------------------------
+-- CUDA
+--------------------------------------------------------------------------------
+
+--
+-- Note that this requires two memory copies: once from a Haskell list to the C
+-- heap, and from there into the graphics card memory. See the `bandwidthTest'
+-- example for the atrocious performance of this operation.
+--
+-- For this test, cheat a little and just time the pure computation.
+--
+foldCUDA :: [Float] -> IO Float
+foldCUDA xs = do
+  let len = length xs
+  CUDA.withListArray xs $ \d_xs -> do
+    (t,r) <- benchmark 100 (fold_plusf d_xs len) CUDA.sync
+    putStrLn $ "== CUDA: " ++ shows (fromInteger (timeIn millisecond t)/100::Float) " ms"
+    return r
+
+{# fun unsafe fold_plusf
+  { withDP* `CUDA.DevicePtr Float'
+  ,         `Int'
+  }
+  -> `Float' cFloatConv #}
+  where
+    withDP p a = CUDA.withDevicePtr p $ \p' -> a (castPtr p')
+
+
+--------------------------------------------------------------------------------
+-- Main
+--------------------------------------------------------------------------------
+
+main :: IO ()
+main = do
+  dev   <- CUDA.get
+  props <- CUDA.props dev
+  putStrLn $ "Using device " ++ show dev ++ ": " ++ CUDA.deviceName props
+
+  xs   <- randomList 30000
+  ref  <- foldRef xs
+  cuda <- foldCUDA xs
+
+  putStrLn $ "== Validating: " ++ if ((ref-cuda)/ref) < 0.0001 then "Ok!" else "INVALID!"
+
diff --git a/examples/src/fold/Makefile b/examples/src/fold/Makefile
new file mode 100644
--- /dev/null
+++ b/examples/src/fold/Makefile
@@ -0,0 +1,18 @@
+#
+# Baking!
+#
+
+# ------------------------------------------------------------------------------
+# Input files
+# ------------------------------------------------------------------------------
+EXECUTABLE      := fold
+
+HSMAIN          := Fold.chs
+CUFILES         := fold.cu
+
+EXTRALIBS       := stdc++
+
+# ------------------------------------------------------------------------------
+# Haskell/CUDA build system
+# ------------------------------------------------------------------------------
+include ../../common/common.mk
diff --git a/examples/src/fold/fold.cu b/examples/src/fold/fold.cu
new file mode 100644
--- /dev/null
+++ b/examples/src/fold/fold.cu
@@ -0,0 +1,223 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Module    : Fold
+ * Copyright : (c) 2009 Trevor L. McDonell
+ * License   : BSD
+ *
+ * ---------------------------------------------------------------------------*/
+
+#include "fold.h"
+
+#include "utils.h"
+#include "operator.h"
+#include "cudpp/shared_mem.h"
+
+
+/*
+ * Compute multiple elements per thread sequentially. This reduces the overall
+ * cost of the algorithm while keeping the work complexity O(n) and the step
+ * complexity O(log n). c.f. Brent's Theorem optimisation.
+ *
+ * Stolen from the CUDA SDK examples
+ */
+template <unsigned int blockSize, bool lengthIsPow2, class op, typename T>
+__global__ static void
+fold_recursive
+(
+    const T     *d_xs,
+    T           *d_ys,
+    int         length
+)
+{
+    SharedMemory<T> smem;
+    T *scratch = smem.getPointer();
+
+    /*
+     * Calculate first level of reduction reading into shared memory
+     */
+    unsigned int i;
+    unsigned int tid      = threadIdx.x;
+    unsigned int gridSize = blockSize * 2 * gridDim.x;
+
+    scratch[tid] = op::identity();
+
+    /*
+     * Reduce multiple elements per thread. The number is determined by the
+     * number of active thread blocks (via gridDim). More blocks will result in
+     * a larger `gridSize', and hence fewer elements per thread
+     *
+     * The loop stride of `gridSize' is used to maintain coalescing.
+     */
+    for (i =  blockIdx.x * blockSize * 2 + tid; i <  length; i += gridSize)
+    {
+        scratch[tid] = op::apply(scratch[tid], d_xs[i]);
+
+        /*
+         * Ensure we don't read out of bounds. This is optimised away if the
+         * input length is a power of two
+         */
+        if (lengthIsPow2 || i + blockSize < length)
+            scratch[tid] = op::apply(scratch[tid], d_xs[i+blockSize]);
+    }
+    __syncthreads();
+
+    /*
+     * Now, calculate the reduction in shared memory
+     */
+    if (blockSize >= 512) { if (tid < 256) { scratch[tid] = op::apply(scratch[tid], scratch[tid+256]); } __syncthreads(); }
+    if (blockSize >= 256) { if (tid < 128) { scratch[tid] = op::apply(scratch[tid], scratch[tid+128]); } __syncthreads(); }
+    if (blockSize >= 128) { if (tid <  64) { scratch[tid] = op::apply(scratch[tid], scratch[tid+ 64]); } __syncthreads(); }
+
+#ifndef __DEVICE_EMULATION__
+    if (tid < 32)
+#endif
+    {
+        if (blockSize >= 64) { scratch[tid] = op::apply(scratch[tid], scratch[tid+32]);  __EMUSYNC; }
+        if (blockSize >= 32) { scratch[tid] = op::apply(scratch[tid], scratch[tid+16]);  __EMUSYNC; }
+        if (blockSize >= 16) { scratch[tid] = op::apply(scratch[tid], scratch[tid+ 8]);  __EMUSYNC; }
+        if (blockSize >=  8) { scratch[tid] = op::apply(scratch[tid], scratch[tid+ 4]);  __EMUSYNC; }
+        if (blockSize >=  4) { scratch[tid] = op::apply(scratch[tid], scratch[tid+ 2]);  __EMUSYNC; }
+        if (blockSize >=  2) { scratch[tid] = op::apply(scratch[tid], scratch[tid+ 1]);  __EMUSYNC; }
+    }
+
+    /*
+     * Write the results of this block back to global memory
+     */
+    if (tid == 0)
+        d_ys[blockIdx.x] = scratch[0];
+}
+
+
+/*
+ * Wrapper function for kernel launch
+ */
+template <class op, typename T>
+static void
+fold_dispatch
+(
+    const T     *d_xs,
+    T           *d_ys,
+    int         length,
+    int         blocks,
+    int         threads
+)
+{
+    unsigned int smem = threads * sizeof(T);
+
+    if (isPow2(length))
+    {
+        switch (threads)
+        {
+        case 512: fold_recursive<512,true,op,T><<<blocks,threads,smem>>>(d_xs, d_ys, length); break;
+        case 256: fold_recursive<256,true,op,T><<<blocks,threads,smem>>>(d_xs, d_ys, length); break;
+        case 128: fold_recursive<128,true,op,T><<<blocks,threads,smem>>>(d_xs, d_ys, length); break;
+        case  64: fold_recursive< 64,true,op,T><<<blocks,threads,smem>>>(d_xs, d_ys, length); break;
+        case  32: fold_recursive< 32,true,op,T><<<blocks,threads,smem>>>(d_xs, d_ys, length); break;
+        case  16: fold_recursive< 16,true,op,T><<<blocks,threads,smem>>>(d_xs, d_ys, length); break;
+        case   8: fold_recursive<  8,true,op,T><<<blocks,threads,smem>>>(d_xs, d_ys, length); break;
+        case   4: fold_recursive<  4,true,op,T><<<blocks,threads,smem>>>(d_xs, d_ys, length); break;
+        case   2: fold_recursive<  2,true,op,T><<<blocks,threads,smem>>>(d_xs, d_ys, length); break;
+        case   1: fold_recursive<  1,true,op,T><<<blocks,threads,smem>>>(d_xs, d_ys, length); break;
+        default:
+            assert(!"Non-exhaustive patterns in match");
+        }
+    }
+    else
+    {
+        switch (threads)
+        {
+        case 512: fold_recursive<512,false,op,T><<<blocks,threads,smem>>>(d_xs, d_ys, length); break;
+        case 256: fold_recursive<256,false,op,T><<<blocks,threads,smem>>>(d_xs, d_ys, length); break;
+        case 128: fold_recursive<128,false,op,T><<<blocks,threads,smem>>>(d_xs, d_ys, length); break;
+        case  64: fold_recursive< 64,false,op,T><<<blocks,threads,smem>>>(d_xs, d_ys, length); break;
+        case  32: fold_recursive< 32,false,op,T><<<blocks,threads,smem>>>(d_xs, d_ys, length); break;
+        case  16: fold_recursive< 16,false,op,T><<<blocks,threads,smem>>>(d_xs, d_ys, length); break;
+        case   8: fold_recursive<  8,false,op,T><<<blocks,threads,smem>>>(d_xs, d_ys, length); break;
+        case   4: fold_recursive<  4,false,op,T><<<blocks,threads,smem>>>(d_xs, d_ys, length); break;
+        case   2: fold_recursive<  2,false,op,T><<<blocks,threads,smem>>>(d_xs, d_ys, length); break;
+        case   1: fold_recursive<  1,false,op,T><<<blocks,threads,smem>>>(d_xs, d_ys, length); break;
+        default:
+            assert(!"Non-exhaustive patterns in match");
+        }
+    }
+}
+
+
+/*
+ * Compute the number of blocks and threads to use for the reduction kernel
+ */
+static void
+fold_control
+(
+    int         n,
+    int         &blocks,
+    int         &threads,
+    int         maxThreads = MAX_THREADS,
+    int         maxBlocks  = MAX_BLOCKS
+)
+{
+    threads = (n < maxThreads*2) ? ceilPow2((n+1)/2) : maxThreads;
+    blocks  = (n + threads * 2 - 1) / (threads * 2);
+    blocks  = min(blocks, maxBlocks);
+}
+
+
+/*
+ * Apply a binary operator to an array, reducing the array to a single value.
+ * The reduction will take place in parallel, so the operator must be
+ * associative.
+ */
+template <class op, typename T>
+T
+fold
+(
+    const T     *d_xs,
+    int         n
+)
+{
+    int blocks;
+    int threads;
+    T   gpu_result;
+    T*  d_data          = NULL;
+
+    /*
+     * Allocate temporary storage for the block-level reduction
+     */
+    fold_control(n, blocks, threads);
+    cudaMalloc((void **) &d_data, sizeof(T) * blocks);
+
+    /*
+     * Recursively fold the partial block sums to a single value
+     */
+    fold_dispatch<op,T>(d_xs, d_data, n, blocks, threads);
+
+    n = blocks;
+    while (n > 1)
+    {
+        fold_control(n, blocks, threads);
+        fold_dispatch<op,T>(d_data, d_data, n, blocks, threads);
+
+        n = (n + threads * 2 - 1) / (threads * 2);
+    }
+    assert(n == 1);
+
+    /*
+     * Read back the final result
+     */
+    cudaMemcpy(&gpu_result, d_data, sizeof(T), cudaMemcpyDeviceToHost);
+    cudaFree(d_data);
+
+    return gpu_result;
+}
+
+
+// -----------------------------------------------------------------------------
+// Instances
+// -----------------------------------------------------------------------------
+
+float fold_plusf(float *xs, int N)
+{
+    float  result = fold< Plus<float> >(xs, N);
+    return result;
+}
+
diff --git a/examples/src/fold/fold.h b/examples/src/fold/fold.h
new file mode 100644
--- /dev/null
+++ b/examples/src/fold/fold.h
@@ -0,0 +1,32 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Module    : Fold
+ * Copyright : (c) 2009 Trevor L. McDonell
+ * License   : BSD
+ *
+ * ---------------------------------------------------------------------------*/
+
+#ifndef __FOLD_H__
+#define __FOLD_H__
+
+/*
+ * Optimised for Tesla.
+ * Maximum thread occupancy for your card may be achieved with different values.
+ */
+#define MAX_THREADS     128
+#define MAX_BLOCKS      64
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Instances
+ */
+float fold_plusf(float *xs, int N);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/examples/src/matrixMul/LICENSE b/examples/src/matrixMul/LICENSE
new file mode 100644
--- /dev/null
+++ b/examples/src/matrixMul/LICENSE
@@ -0,0 +1,187 @@
+NVIDIA Corporation GPU COMPUTING SDK END USER LICENSE AGREEMENT ("Agreement")
+
+BY DOWNLOADING THE SOFTWARE AND OTHER AVAILABLE MATERIALS, YOU  ("DEVELOPER")
+AGREE TO BE BOUND BY THE FOLLOWING TERMS AND CONDITIONS OF THIS AGREEMENT.  IF
+DEVELOPER DOES NOT AGREE TO THE TERMS AND CONDITION OF THIS AGREEMENT, THEN DO
+NOT DOWNLOAD THE SOFTWARE AND MATERIALS.
+
+The materials available for download to Developers may include software in both
+sample source ("Source Code") and object code ("Object Code") versions,
+documentation ("Documentation"), certain art work ("Art Assets") and other
+materials (collectively, these materials referred to herein as "Materials").
+Except as expressly indicated herein, all terms and conditions of this Agreement
+apply to all of the Materials.
+
+Except as expressly set forth herein, NVIDIA owns all of the Materials and makes
+them available to Developer only under the terms and conditions set forth in
+this Agreement.
+
+License:  Subject to the terms of this Agreement, NVIDIA hereby grants to
+Developer a royalty-free, non-exclusive license to possess and to use the
+Materials.  Developer may install and use multiple copies of the Materials on a
+shared computer or concurrently on different computers, and make multiple
+back-up copies of the Materials, solely for Licensee's use within Licensee's
+Enterprise. "Enterprise" shall mean individual use by Licensee or any legal
+entity (such as a corporation or university) and the subsidiaries it owns by
+more than 50 percent.  The following terms apply to the specified type of
+Material:
+
+Source Code:  Developer shall have the right to modify and create derivative
+works with the Source Code.  Developer shall own any derivative works
+("Derivatives") it creates to the Source Code, provided that Developer uses the
+Materials in accordance with the terms and conditions of this Agreement.
+Developer may distribute the Derivatives, provided that all NVIDIA copyright
+notices and trademarks are used properly and the Derivatives include the
+following statement: "This software contains source code provided by NVIDIA
+Corporation."  
+
+Object Code:  Developer agrees not to disassemble, decompile or reverse engineer
+the Object Code versions of any of the Materials.  Developer acknowledges that
+certain of the Materials provided in Object Code version may contain third party
+components that may be subject to restrictions, and expressly agrees not to
+attempt to modify or distribute such Materials without first receiving consent
+from NVIDIA.
+
+Art Assets:  Developer shall have the right to modify and create Derivatives of
+the Art Assets, but may not distribute any of the Art Assets or Derivatives
+created therefrom without NVIDIA's prior written consent.
+
+No Other License: No rights or licenses are granted by NVIDIA to Developer under
+this Agreement, expressly or by implication, with respect to any proprietary
+information or patent, copyright, trade secret or other intellectual property
+right owned or controlled by NVIDIA, except as expressly provided in this
+Agreement.
+
+Intellectual Property Ownership: All rights, title, interest and copyrights in
+and to the Materials (including but not limited to all images, photographs,
+animations, video, audio, music, text, and other information incorporated into
+the Materials), are owned by NVIDIA, or its suppliers. The Materials are
+protected by copyright laws and international treaty provisions. Accordingly,
+Developer is required to treat the Materials like any other copyrighted
+material, except as otherwise allowed pursuant to this Agreement. 
+
+
+Term of Agreement:  This Agreement is effective until (i) automatically
+terminated if Developer fails to comply with any of the terms and conditions of
+this Agreement; or (ii) terminated by NVIDIA.  NVIDIA may terminate this
+Agreement (and with it, all of Developer's right to the Materials) immediately
+upon written notice (which may include email) to Developer, with or without
+cause.
+
+Defensive Suspension: If Developer commences or participates in any legal
+proceeding against NVIDIA, then NVIDIA may, in its sole discretion, suspend or
+terminate all license grants and any other rights provided under this Agreement
+during the pendency of such legal proceedings. 
+
+
+No Support:  NVIDIA has no obligation to support or to continue providing or
+updating any of the Materials.
+
+No Warranty:  THE SOFTWARE AND ANY OTHER MATERIALS PROVIDED BY NVIDIA TO
+DEVELOPER HEREUNDER ARE PROVIDED "AS IS."  NVIDIA DISCLAIMS ALL WARRANTIES,
+EXPRESS, IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT.
+
+Limitation of Liability: NVIDIA SHALL NOT BE LIABLE TO DEVELOPER, DEVELOPER'S
+CUSTOMERS, OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH OR UNDER DEVELOPER FOR
+ANY LOSS OF PROFITS, INCOME, SAVINGS, OR ANY OTHER CONSEQUENTIAL, INCIDENTAL,
+SPECIAL, PUNITIVE, DIRECT OR INDIRECT DAMAGES (WHETHER IN AN ACTION IN CONTRACT,
+TORT OR BASED ON A WARRANTY), EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY
+OF SUCH DAMAGES.  THESE LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF
+THE ESSENTIAL PURPOSE OF ANY LIMITED REMEDY.  IN NO EVENT SHALL NVIDIA'S
+AGGREGATE LIABILITY TO DEVELOPER OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH
+OR UNDER DEVELOPER EXCEED THE AMOUNT OF MONEY ACTUALLY PAID BY DEVELOPER TO
+NVIDIA FOR THE SOFTWARE OR ANY OTHER MATERIALS.
+
+Applicable Law: This Agreement shall be deemed to have been made in, and shall
+be construed pursuant to, the laws of the State of Delaware. The United Nations
+Convention on Contracts for the International Sale of Goods is specifically
+disclaimed.
+
+Feedback: In the event Developer contacts NVIDIA to request Feedback (as defined
+below) on how to optimize Developer's product for use with the Materials, the
+following terms and conditions apply the Feedback:
+
+1.	Exchange of Feedback. Both parties agree that neither party has an
+obligation to give the other party any suggestions, comments or other feedback,
+whether verbally or in code form ("Feedback"), relating to (i) the Materials;
+(ii) Developer's products; (iii) Developer's use of the Materials; or (iv)
+optimization of Developer's product with NVIDIA product(s).  In the event either
+party provides Feedback to the other party, the party receiving the Feedback may
+use and include any Feedback that the other party voluntarily provides to
+improve the (i) Materials or other related NVIDIA technologies, respectively for
+the benefit of NVIDIA; or (ii) Developer's product or other related Developer
+technologies, respectively for the benefit of Developer.  Accordingly, if either
+party provides Feedback to the other party, both parties agree that the other
+party and its respective Developers may freely use, reproduce, license,
+distribute, and otherwise commercialize the Feedback in the (i) Materials or
+other related technologies; or (ii) Developer's products or other related
+technologies, respectively, without the payment of any royalties or fees.
+  
+2.	Residual Rights. Developer agrees that NVIDIA shall be free to use any
+general knowledge, skills and experience, (including, but not limited to, ideas,
+concepts, know-how, or techniques) ("Residuals"), contained in the (i) Feedback
+provided by Developer to NVIDIA; (ii) Developer's products, in source or object
+code form, shared or disclosed to NVIDIA in connection with the Feedback; or (c)
+Developer's confidential information voluntarily provided to NVIDIA in
+connection with the Feedback, which are retained in the memories of NVIDIA's
+employees, agents, or contractors who have had access to such (i) Feedback
+provided by Developer to NVIDIA; (ii) Developer's products; or (c) Developer's
+confidential information voluntarily provided to NVIDIA, in connection with the
+Feedback.  Subject to the terms and conditions of this Agreement, NVIDIA's
+employees, agents, or contractors shall not be prevented from using Residuals as
+part of such employee's, agent's or contractor's general knowledge, skills,
+experience, talent, and/or expertise. NVIDIA shall not have any obligation to
+limit or restrict the assignment of such employees, agents or contractors or to
+pay royalties for any work resulting from the use of Residuals.
+
+3.	Disclaimer of Warranty. FEEDBACK FROM EITHER PARTY IS PROVIDED FOR THE
+OTHER PARTY'S USE "AS IS" AND BOTH PARTIES DISCLAIM ALL WARRANTIES, EXPRESS,
+IMPLIED AND STATUTORY INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  BOTH PARTIES DO NOT
+REPRESENT OR WARRANT THAT THE FEEDBACK WILL MEET THE OTHER PARTY'S REQUIREMENTS
+OR THAT THE OPERATION OR IMPLEMENTATION OF THE FEEDBACK WILL BE UNINTERRUPTED OR
+ERROR-FREE.
+
+4.	No Liability for Consequential Damages. TO THE MAXIMUM EXTENT PERMITTED
+BY APPLICABLE LAW, IN NO EVENT SHALL EITHER PARTY OR ITS SUPPLIERS BE LIABLE FOR
+ANY SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER
+(INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS
+INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY LOSS) ARISING
+OUT OF THE USE OF OR INABILITY TO USE THE FEEDBACK, EVEN IF THE OTHER PARTY HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+5.	Freedom of Action.  Developer agrees that this Agreement is nonexclusive
+and NVIDIA may currently or in the future be developing software, other
+technology or confidential information internally, or receiving confidential
+information from other parties that maybe similar to the Feedback and
+Developer's confidential information (as provided in subsection 2 above), which
+may be provided to NVIDIA in connection with Feedback by Developer. Accordingly,
+Developer agrees that nothing in this Agreement will be construed as a
+representation or inference that NVIDIA will not develop, design, manufacture,
+acquire, market products, or have products developed, designed, manufactured,
+acquired, or marketed for NVIDIA, that compete with the Developer's products or
+confidential information.
+
+RESTRICTED RIGHTS NOTICE: Materials has been developed entirely at private
+expense and is commercial computer software provided with RESTRICTED RIGHTS.
+Use, duplication or disclosure by the U.S. Government or a U.S. Government
+subcontractor is subject to the restrictions set forth in the license agreement
+under which Materials was obtained pursuant to DFARS 227.7202-3(a) or as set
+forth in subparagraphs (c)(1) and (2) of the Commercial Computer Software -
+Restricted Rights clause at FAR 52.227-19, as applicable.
+Contractor/manufacturer is NVIDIA, 2701 San Tomas Expressway, Santa Clara, CA
+95050.
+
+Miscellaneous: If any provision of this Agreement is inconsistent with, or
+cannot be fully enforced under, the law, such provision will be construed as
+limited to the extent necessary to be consistent with and fully enforceable
+under the law. This Agreement is the final, complete and exclusive agreement
+between the parties relating to the subject matter hereof, and supersedes all
+prior or contemporaneous understandings and agreements relating to such subject
+matter, whether oral or written. This Agreement may only be modified in writing
+signed by an authorized officer of NVIDIA. Developer agrees that it will not
+ship, transfer or export the Materials into any country, or use the Materials in
+any manner, prohibited by the United States Bureau of Industry and Security or
+any export laws, restrictions or regulations. 
diff --git a/examples/src/matrixMul/Makefile b/examples/src/matrixMul/Makefile
new file mode 100644
--- /dev/null
+++ b/examples/src/matrixMul/Makefile
@@ -0,0 +1,18 @@
+#
+# Baking!
+#
+
+# ------------------------------------------------------------------------------
+# Input files
+# ------------------------------------------------------------------------------
+EXECUTABLE      := matrixMul
+
+HSMAIN          := MatrixMul.hs
+CUFILES         := matrix_mul.cu
+
+EXTRALIBS       := stdc++
+
+# ------------------------------------------------------------------------------
+# Haskell/CUDA build system
+# ------------------------------------------------------------------------------
+include ../../common/common.mk
diff --git a/examples/src/matrixMul/MatrixMul.hs b/examples/src/matrixMul/MatrixMul.hs
new file mode 100644
--- /dev/null
+++ b/examples/src/matrixMul/MatrixMul.hs
@@ -0,0 +1,114 @@
+{-# LANGUAGE CPP #-}
+--------------------------------------------------------------------------------
+--
+-- Module    : MatrixMul
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Matrix multiplication using runtime interface and execution control instead
+-- of calling C functions via the FFI.
+--
+--------------------------------------------------------------------------------
+
+module Main where
+
+#include "matrix_mul.h"
+
+-- Friends
+import Time
+import RandomVector
+
+-- System
+import Data.Array
+import System.IO
+import Foreign
+import qualified Foreign.CUDA as CUDA
+
+
+--------------------------------------------------------------------------------
+-- Reference
+--------------------------------------------------------------------------------
+
+matMult :: (Num e, Storable e) => Matrix e -> Matrix e -> IO (Matrix e)
+matMult mx my = do
+  x <- unsafeFreeze mx
+  y <- unsafeFreeze my
+  let ((li, lj), (ui, uj))  = bounds x
+      ((li',lj'),(ui',uj')) = bounds y
+      resBnds | (lj,uj) == (li',ui') = ((li,lj'),(ui,uj'))
+              | otherwise            = error "matrix dimensions must agree"
+
+  newListArray resBnds [sum [x!(i,k) * y!(k,j) | k <- range (lj,uj)]
+                         | i <- range (li,ui)
+                         , j <- range (lj',uj') ]
+
+
+--------------------------------------------------------------------------------
+-- CUDA
+--------------------------------------------------------------------------------
+
+matMultCUDA :: (Num e, Storable e) => Matrix e -> Matrix e -> IO (Matrix e)
+matMultCUDA xs' ys' = doMult undefined xs' ys'
+  where
+    doMult :: (Num e', Storable e') => e' -> Matrix e' -> Matrix e' -> IO (Matrix e')
+    doMult dummy xs ys = do
+
+      -- Setup matrix parameters
+      --
+      ((li, lj), (ui, uj))  <- getBounds xs
+      ((li',lj'),(ui',uj')) <- getBounds ys
+      let wx = rangeSize (lj,uj)
+          hx = rangeSize (li,ui)
+          wy = rangeSize (lj',uj')
+          hy = rangeSize (li',ui')
+          resBnds | wx == hy  = ((li,lj'),(ui,uj'))
+                  | otherwise = error "matrix dimensions must agree"
+
+      -- Allocate memory and copy test data
+      --
+      CUDA.allocaArray (wx*hx) $ \d_xs -> do
+      CUDA.allocaArray (wy*hy) $ \d_ys -> do
+      CUDA.allocaArray (wy*hx) $ \d_zs -> do
+      withMatrix xs $ \p -> CUDA.pokeArray (wx*hx) p d_xs
+      withMatrix ys $ \p -> CUDA.pokeArray (wy*hy) p d_ys
+
+      -- Launch the kernel
+      --
+      let gridDim   = (wy`div`BLOCK_SIZE, hx`div`BLOCK_SIZE)
+          blockDim  = (BLOCK_SIZE,BLOCK_SIZE,1)
+          sharedMem = 2 * BLOCK_SIZE * BLOCK_SIZE * fromIntegral (sizeOf dummy)
+
+      CUDA.setConfig gridDim blockDim sharedMem Nothing
+      CUDA.setParams [CUDA.VArg d_xs, CUDA.VArg d_ys, CUDA.VArg d_zs, CUDA.IArg wx, CUDA.IArg wy]
+      CUDA.launch "matrixMul"
+
+      -- Copy back result
+      zs <- newArray_ resBnds
+      withMatrix zs $ \p -> CUDA.peekArray (wy*hx) d_zs p
+      return zs
+
+
+--------------------------------------------------------------------------------
+-- Main
+--------------------------------------------------------------------------------
+
+main :: IO ()
+main = do
+  dev   <- CUDA.get
+  props <- CUDA.props dev
+  putStrLn $ "Using device " ++ show dev ++ ": " ++ CUDA.deviceName props
+
+  xs <- randomArr ((1,1),(8*BLOCK_SIZE, 4*BLOCK_SIZE)) :: IO (Matrix Float)
+  ys <- randomArr ((1,1),(4*BLOCK_SIZE,12*BLOCK_SIZE)) :: IO (Matrix Float)
+
+  putStr   "== Reference: " >> hFlush stdout
+  (tr,ref) <- benchmark 100 (matMult xs ys) (return ())
+  putStrLn $  shows (fromInteger (timeIn millisecond tr) / 100::Float) " ms"
+
+  putStr   "== CUDA: " >> hFlush stdout
+  (tc,mat) <- benchmark 100 (matMultCUDA xs ys) (CUDA.sync)
+  putStrLn $  shows (fromInteger (timeIn millisecond tc) / 100::Float) " ms"
+
+  putStr "== Validating: "
+  verify ref mat >>= \rv -> putStrLn $ if rv then "Ok!" else "INVALID!"
+
diff --git a/examples/src/matrixMul/matrix_mul.cu b/examples/src/matrixMul/matrix_mul.cu
new file mode 100644
--- /dev/null
+++ b/examples/src/matrixMul/matrix_mul.cu
@@ -0,0 +1,110 @@
+/*
+ * Copyright 1993-2009 NVIDIA Corporation.  All rights reserved.
+ *
+ * NVIDIA Corporation and its licensors retain all intellectual property and 
+ * proprietary rights in and to this software and related documentation. 
+ * Any use, reproduction, disclosure, or distribution of this software 
+ * and related documentation without an express license agreement from
+ * NVIDIA Corporation is strictly prohibited.
+ *
+ * Please refer to the applicable NVIDIA end user license agreement (EULA) 
+ * associated with this source code for terms and conditions that govern 
+ * your use of this NVIDIA software.
+ * 
+ */
+
+/* Matrix multiplication: C = A * B.
+ * Device code.
+ */
+
+#ifndef _MATRIXMUL_KERNEL_H_
+#define _MATRIXMUL_KERNEL_H_
+
+#include <stdio.h>
+#include "matrix_mul.h"
+
+#define CHECK_BANK_CONFLICTS 0
+#if CHECK_BANK_CONFLICTS
+#define AS(i, j) cutilBankChecker(((float*)&As[0][0]), (BLOCK_SIZE * i + j))
+#define BS(i, j) cutilBankChecker(((float*)&Bs[0][0]), (BLOCK_SIZE * i + j))
+#else
+#define AS(i, j) As[i][j]
+#define BS(i, j) Bs[i][j]
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+//! Matrix multiplication on the device: C = A * B
+//! wA is A's width and wB is B's width
+////////////////////////////////////////////////////////////////////////////////
+extern "C" __global__ void
+matrixMul(float* A, float* B, float* C, int wA, int wB)
+{
+    // Block index
+    int bx = blockIdx.x;
+    int by = blockIdx.y;
+
+    // Thread index
+    int tx = threadIdx.x;
+    int ty = threadIdx.y;
+
+    // Index of the first sub-matrix of A processed by the block
+    int aBegin = wA * BLOCK_SIZE * by;
+
+    // Index of the last sub-matrix of A processed by the block
+    int aEnd   = aBegin + wA - 1;
+
+    // Step size used to iterate through the sub-matrices of A
+    int aStep  = BLOCK_SIZE;
+
+    // Index of the first sub-matrix of B processed by the block
+    int bBegin = BLOCK_SIZE * bx;
+
+    // Step size used to iterate through the sub-matrices of B
+    int bStep  = BLOCK_SIZE * wB;
+
+    // Csub is used to store the element of the block sub-matrix
+    // that is computed by the thread
+    float Csub = 0;
+
+    // Loop over all the sub-matrices of A and B
+    // required to compute the block sub-matrix
+    for (int a = aBegin, b = bBegin;
+             a <= aEnd;
+             a += aStep, b += bStep) {
+
+        // Declaration of the shared memory array As used to
+        // store the sub-matrix of A
+        __shared__ float As[BLOCK_SIZE][BLOCK_SIZE];
+
+        // Declaration of the shared memory array Bs used to
+        // store the sub-matrix of B
+        __shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];
+
+        // Load the matrices from device memory
+        // to shared memory; each thread loads
+        // one element of each matrix
+        AS(ty, tx) = A[a + wA * ty + tx];
+        BS(ty, tx) = B[b + wB * ty + tx];
+
+        // Synchronize to make sure the matrices are loaded
+        __syncthreads();
+
+        // Multiply the two matrices together;
+        // each thread computes one element
+        // of the block sub-matrix
+        for (int k = 0; k < BLOCK_SIZE; ++k)
+            Csub += AS(ty, k) * BS(k, tx);
+
+        // Synchronize to make sure that the preceding
+        // computation is done before loading two new
+        // sub-matrices of A and B in the next iteration
+        __syncthreads();
+    }
+
+    // Write the block sub-matrix to device memory;
+    // each thread writes one element
+    int c = wB * BLOCK_SIZE * by + BLOCK_SIZE * bx;
+    C[c + wB * ty + tx] = Csub;
+}
+
+#endif // #ifndef _MATRIXMUL_KERNEL_H_
diff --git a/examples/src/matrixMul/matrix_mul.h b/examples/src/matrixMul/matrix_mul.h
new file mode 100644
--- /dev/null
+++ b/examples/src/matrixMul/matrix_mul.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1993-2009 NVIDIA Corporation.  All rights reserved.
+ *
+ * NVIDIA Corporation and its licensors retain all intellectual property and 
+ * proprietary rights in and to this software and related documentation. 
+ * Any use, reproduction, disclosure, or distribution of this software 
+ * and related documentation without an express license agreement from
+ * NVIDIA Corporation is strictly prohibited.
+ *
+ * Please refer to the applicable NVIDIA end user license agreement (EULA) 
+ * associated with this source code for terms and conditions that govern 
+ * your use of this NVIDIA software.
+ * 
+ */
+
+#ifndef _MATRIXMUL_H_
+#define _MATRIXMUL_H_
+
+/* Thread block size */
+#define BLOCK_SIZE 16
+
+/* Matrix dimensions
+ * (chosen as multiples of the thread block size for simplicity)
+ */
+#define WA (3 * BLOCK_SIZE) /* Matrix A width  */
+#define HA (5 * BLOCK_SIZE) /* Matrix A height */
+#define WB (8 * BLOCK_SIZE) /* Matrix B width  */
+#define HB WA  /* Matrix B height */
+#define WC WB  /* Matrix C width  */
+#define HC HA  /* Matrix C height */
+
+#endif /* _MATRIXMUL_H_ */
+
diff --git a/examples/src/matrixMulDrv/LICENSE b/examples/src/matrixMulDrv/LICENSE
new file mode 100644
--- /dev/null
+++ b/examples/src/matrixMulDrv/LICENSE
@@ -0,0 +1,187 @@
+NVIDIA Corporation GPU COMPUTING SDK END USER LICENSE AGREEMENT ("Agreement")
+
+BY DOWNLOADING THE SOFTWARE AND OTHER AVAILABLE MATERIALS, YOU  ("DEVELOPER")
+AGREE TO BE BOUND BY THE FOLLOWING TERMS AND CONDITIONS OF THIS AGREEMENT.  IF
+DEVELOPER DOES NOT AGREE TO THE TERMS AND CONDITION OF THIS AGREEMENT, THEN DO
+NOT DOWNLOAD THE SOFTWARE AND MATERIALS.
+
+The materials available for download to Developers may include software in both
+sample source ("Source Code") and object code ("Object Code") versions,
+documentation ("Documentation"), certain art work ("Art Assets") and other
+materials (collectively, these materials referred to herein as "Materials").
+Except as expressly indicated herein, all terms and conditions of this Agreement
+apply to all of the Materials.
+
+Except as expressly set forth herein, NVIDIA owns all of the Materials and makes
+them available to Developer only under the terms and conditions set forth in
+this Agreement.
+
+License:  Subject to the terms of this Agreement, NVIDIA hereby grants to
+Developer a royalty-free, non-exclusive license to possess and to use the
+Materials.  Developer may install and use multiple copies of the Materials on a
+shared computer or concurrently on different computers, and make multiple
+back-up copies of the Materials, solely for Licensee's use within Licensee's
+Enterprise. "Enterprise" shall mean individual use by Licensee or any legal
+entity (such as a corporation or university) and the subsidiaries it owns by
+more than 50 percent.  The following terms apply to the specified type of
+Material:
+
+Source Code:  Developer shall have the right to modify and create derivative
+works with the Source Code.  Developer shall own any derivative works
+("Derivatives") it creates to the Source Code, provided that Developer uses the
+Materials in accordance with the terms and conditions of this Agreement.
+Developer may distribute the Derivatives, provided that all NVIDIA copyright
+notices and trademarks are used properly and the Derivatives include the
+following statement: "This software contains source code provided by NVIDIA
+Corporation."  
+
+Object Code:  Developer agrees not to disassemble, decompile or reverse engineer
+the Object Code versions of any of the Materials.  Developer acknowledges that
+certain of the Materials provided in Object Code version may contain third party
+components that may be subject to restrictions, and expressly agrees not to
+attempt to modify or distribute such Materials without first receiving consent
+from NVIDIA.
+
+Art Assets:  Developer shall have the right to modify and create Derivatives of
+the Art Assets, but may not distribute any of the Art Assets or Derivatives
+created therefrom without NVIDIA's prior written consent.
+
+No Other License: No rights or licenses are granted by NVIDIA to Developer under
+this Agreement, expressly or by implication, with respect to any proprietary
+information or patent, copyright, trade secret or other intellectual property
+right owned or controlled by NVIDIA, except as expressly provided in this
+Agreement.
+
+Intellectual Property Ownership: All rights, title, interest and copyrights in
+and to the Materials (including but not limited to all images, photographs,
+animations, video, audio, music, text, and other information incorporated into
+the Materials), are owned by NVIDIA, or its suppliers. The Materials are
+protected by copyright laws and international treaty provisions. Accordingly,
+Developer is required to treat the Materials like any other copyrighted
+material, except as otherwise allowed pursuant to this Agreement. 
+
+
+Term of Agreement:  This Agreement is effective until (i) automatically
+terminated if Developer fails to comply with any of the terms and conditions of
+this Agreement; or (ii) terminated by NVIDIA.  NVIDIA may terminate this
+Agreement (and with it, all of Developer's right to the Materials) immediately
+upon written notice (which may include email) to Developer, with or without
+cause.
+
+Defensive Suspension: If Developer commences or participates in any legal
+proceeding against NVIDIA, then NVIDIA may, in its sole discretion, suspend or
+terminate all license grants and any other rights provided under this Agreement
+during the pendency of such legal proceedings. 
+
+
+No Support:  NVIDIA has no obligation to support or to continue providing or
+updating any of the Materials.
+
+No Warranty:  THE SOFTWARE AND ANY OTHER MATERIALS PROVIDED BY NVIDIA TO
+DEVELOPER HEREUNDER ARE PROVIDED "AS IS."  NVIDIA DISCLAIMS ALL WARRANTIES,
+EXPRESS, IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT.
+
+Limitation of Liability: NVIDIA SHALL NOT BE LIABLE TO DEVELOPER, DEVELOPER'S
+CUSTOMERS, OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH OR UNDER DEVELOPER FOR
+ANY LOSS OF PROFITS, INCOME, SAVINGS, OR ANY OTHER CONSEQUENTIAL, INCIDENTAL,
+SPECIAL, PUNITIVE, DIRECT OR INDIRECT DAMAGES (WHETHER IN AN ACTION IN CONTRACT,
+TORT OR BASED ON A WARRANTY), EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY
+OF SUCH DAMAGES.  THESE LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF
+THE ESSENTIAL PURPOSE OF ANY LIMITED REMEDY.  IN NO EVENT SHALL NVIDIA'S
+AGGREGATE LIABILITY TO DEVELOPER OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH
+OR UNDER DEVELOPER EXCEED THE AMOUNT OF MONEY ACTUALLY PAID BY DEVELOPER TO
+NVIDIA FOR THE SOFTWARE OR ANY OTHER MATERIALS.
+
+Applicable Law: This Agreement shall be deemed to have been made in, and shall
+be construed pursuant to, the laws of the State of Delaware. The United Nations
+Convention on Contracts for the International Sale of Goods is specifically
+disclaimed.
+
+Feedback: In the event Developer contacts NVIDIA to request Feedback (as defined
+below) on how to optimize Developer's product for use with the Materials, the
+following terms and conditions apply the Feedback:
+
+1.	Exchange of Feedback. Both parties agree that neither party has an
+obligation to give the other party any suggestions, comments or other feedback,
+whether verbally or in code form ("Feedback"), relating to (i) the Materials;
+(ii) Developer's products; (iii) Developer's use of the Materials; or (iv)
+optimization of Developer's product with NVIDIA product(s).  In the event either
+party provides Feedback to the other party, the party receiving the Feedback may
+use and include any Feedback that the other party voluntarily provides to
+improve the (i) Materials or other related NVIDIA technologies, respectively for
+the benefit of NVIDIA; or (ii) Developer's product or other related Developer
+technologies, respectively for the benefit of Developer.  Accordingly, if either
+party provides Feedback to the other party, both parties agree that the other
+party and its respective Developers may freely use, reproduce, license,
+distribute, and otherwise commercialize the Feedback in the (i) Materials or
+other related technologies; or (ii) Developer's products or other related
+technologies, respectively, without the payment of any royalties or fees.
+  
+2.	Residual Rights. Developer agrees that NVIDIA shall be free to use any
+general knowledge, skills and experience, (including, but not limited to, ideas,
+concepts, know-how, or techniques) ("Residuals"), contained in the (i) Feedback
+provided by Developer to NVIDIA; (ii) Developer's products, in source or object
+code form, shared or disclosed to NVIDIA in connection with the Feedback; or (c)
+Developer's confidential information voluntarily provided to NVIDIA in
+connection with the Feedback, which are retained in the memories of NVIDIA's
+employees, agents, or contractors who have had access to such (i) Feedback
+provided by Developer to NVIDIA; (ii) Developer's products; or (c) Developer's
+confidential information voluntarily provided to NVIDIA, in connection with the
+Feedback.  Subject to the terms and conditions of this Agreement, NVIDIA's
+employees, agents, or contractors shall not be prevented from using Residuals as
+part of such employee's, agent's or contractor's general knowledge, skills,
+experience, talent, and/or expertise. NVIDIA shall not have any obligation to
+limit or restrict the assignment of such employees, agents or contractors or to
+pay royalties for any work resulting from the use of Residuals.
+
+3.	Disclaimer of Warranty. FEEDBACK FROM EITHER PARTY IS PROVIDED FOR THE
+OTHER PARTY'S USE "AS IS" AND BOTH PARTIES DISCLAIM ALL WARRANTIES, EXPRESS,
+IMPLIED AND STATUTORY INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  BOTH PARTIES DO NOT
+REPRESENT OR WARRANT THAT THE FEEDBACK WILL MEET THE OTHER PARTY'S REQUIREMENTS
+OR THAT THE OPERATION OR IMPLEMENTATION OF THE FEEDBACK WILL BE UNINTERRUPTED OR
+ERROR-FREE.
+
+4.	No Liability for Consequential Damages. TO THE MAXIMUM EXTENT PERMITTED
+BY APPLICABLE LAW, IN NO EVENT SHALL EITHER PARTY OR ITS SUPPLIERS BE LIABLE FOR
+ANY SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER
+(INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS
+INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY LOSS) ARISING
+OUT OF THE USE OF OR INABILITY TO USE THE FEEDBACK, EVEN IF THE OTHER PARTY HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+5.	Freedom of Action.  Developer agrees that this Agreement is nonexclusive
+and NVIDIA may currently or in the future be developing software, other
+technology or confidential information internally, or receiving confidential
+information from other parties that maybe similar to the Feedback and
+Developer's confidential information (as provided in subsection 2 above), which
+may be provided to NVIDIA in connection with Feedback by Developer. Accordingly,
+Developer agrees that nothing in this Agreement will be construed as a
+representation or inference that NVIDIA will not develop, design, manufacture,
+acquire, market products, or have products developed, designed, manufactured,
+acquired, or marketed for NVIDIA, that compete with the Developer's products or
+confidential information.
+
+RESTRICTED RIGHTS NOTICE: Materials has been developed entirely at private
+expense and is commercial computer software provided with RESTRICTED RIGHTS.
+Use, duplication or disclosure by the U.S. Government or a U.S. Government
+subcontractor is subject to the restrictions set forth in the license agreement
+under which Materials was obtained pursuant to DFARS 227.7202-3(a) or as set
+forth in subparagraphs (c)(1) and (2) of the Commercial Computer Software -
+Restricted Rights clause at FAR 52.227-19, as applicable.
+Contractor/manufacturer is NVIDIA, 2701 San Tomas Expressway, Santa Clara, CA
+95050.
+
+Miscellaneous: If any provision of this Agreement is inconsistent with, or
+cannot be fully enforced under, the law, such provision will be construed as
+limited to the extent necessary to be consistent with and fully enforceable
+under the law. This Agreement is the final, complete and exclusive agreement
+between the parties relating to the subject matter hereof, and supersedes all
+prior or contemporaneous understandings and agreements relating to such subject
+matter, whether oral or written. This Agreement may only be modified in writing
+signed by an authorized officer of NVIDIA. Developer agrees that it will not
+ship, transfer or export the Materials into any country, or use the Materials in
+any manner, prohibited by the United States Bureau of Industry and Security or
+any export laws, restrictions or regulations. 
diff --git a/examples/src/matrixMulDrv/Makefile b/examples/src/matrixMulDrv/Makefile
new file mode 100644
--- /dev/null
+++ b/examples/src/matrixMulDrv/Makefile
@@ -0,0 +1,18 @@
+#
+# Baking!
+#
+
+# ------------------------------------------------------------------------------
+# Input files
+# ------------------------------------------------------------------------------
+EXECUTABLE	:= matrixMulDrv
+
+HSMAIN		:= MatrixMul.hs
+PTXFILES	:= matrix_mul.cu
+
+USEDRVAPI	:= 1
+
+# ------------------------------------------------------------------------------
+# Haskell/CUDA build system
+# ------------------------------------------------------------------------------
+include ../../common/common.mk
diff --git a/examples/src/matrixMulDrv/MatrixMul.hs b/examples/src/matrixMulDrv/MatrixMul.hs
new file mode 100644
--- /dev/null
+++ b/examples/src/matrixMulDrv/MatrixMul.hs
@@ -0,0 +1,155 @@
+{-# LANGUAGE CPP #-}
+--------------------------------------------------------------------------------
+--
+-- Module    : MatrixMul
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Matrix multiplication using driver interface
+--
+--------------------------------------------------------------------------------
+
+module Main where
+
+#include "matrix_mul.h"
+
+-- Friends
+import RandomVector
+
+-- System
+import Numeric
+import Data.Array
+import Control.Exception
+import Data.Array.Storable
+import Foreign.Storable
+import qualified Data.ByteString.Char8 as B
+import qualified Foreign.CUDA.Driver   as CUDA
+
+
+-- Return the (width,height) of a matrix
+--
+getSize :: Storable e => Matrix e -> IO (Int,Int)
+getSize mat = do
+  ((li,lj),(ui,uj)) <- getBounds mat
+  return (rangeSize (lj,uj), rangeSize (li,ui))
+
+--------------------------------------------------------------------------------
+-- Reference implementation
+--------------------------------------------------------------------------------
+
+matMult :: (Num e, Storable e) => Matrix e -> Matrix e -> IO (Matrix e)
+matMult mx my = do
+  x <- unsafeFreeze mx
+  y <- unsafeFreeze my
+  let ((li, lj), (ui, uj))  = bounds x
+      ((li',lj'),(ui',uj')) = bounds y
+      resBnds | (lj,uj) == (li',ui') = ((li,lj'),(ui,uj'))
+              | otherwise            = error "matrix dimensions must agree"
+
+  newListArray resBnds [sum [x!(i,k) * y!(k,j) | k <- range (lj,uj)]
+                         | i <- range (li,ui)
+                         , j <- range (lj',uj') ]
+
+
+--------------------------------------------------------------------------------
+-- CUDA
+--------------------------------------------------------------------------------
+
+--
+-- Initialise the device and context. Load the PTX source code, and return a
+-- reference to the kernel function.
+--
+initCUDA :: IO (CUDA.Context, CUDA.Fun)
+initCUDA = do
+  CUDA.initialise []
+  dev     <- CUDA.device 0
+  ctx     <- CUDA.create dev []
+  ptx     <- B.readFile "data/matrix_mul.ptx"
+  (mdl,r) <- CUDA.loadDataEx ptx [CUDA.ThreadsPerBlock (BLOCK_SIZE*BLOCK_SIZE)]
+  fun     <- CUDA.getFun mdl "matrixMul"
+
+  putStrLn $ ">> PTX JIT compilation (" ++ showFFloat (Just 2) (CUDA.jitTime r) " ms)"
+  B.putStrLn (CUDA.jitInfoLog r)
+  return (ctx,fun)
+
+
+--
+-- Allocate some memory, and copy over the input data to the device. Should
+-- probably catch allocation exceptions individually...
+--
+initData :: (Num e, Storable e)
+         => Matrix e -> Matrix e -> IO (CUDA.DevicePtr e, CUDA.DevicePtr e, CUDA.DevicePtr e)
+initData xs ys = do
+  (wx,hx) <- getSize xs
+  (wy,hy) <- getSize ys
+  dxs     <- CUDA.mallocArray (wx*hx)
+  dys     <- CUDA.mallocArray (wy*hy)
+  res     <- CUDA.mallocArray (wy*hx)
+
+  flip onException (mapM_ CUDA.free [dxs,dys,res]) $ do
+  withMatrix xs $ \p -> CUDA.pokeArray (wx*hx) p dxs
+  withMatrix ys $ \p -> CUDA.pokeArray (wy*hy) p dys
+  return (dxs, dys, res)
+
+
+--
+-- Run the test
+--
+testCUDA :: (Num e, Storable e) => Matrix e -> Matrix e -> IO (Matrix e)
+testCUDA xs' ys' = doTest undefined xs' ys'
+  where
+    doTest :: (Num e', Storable e') => e' -> Matrix e' -> Matrix e' -> IO (Matrix e')
+    doTest dummy xs ys = do
+      (widthX,heightX)      <- getSize xs
+      (widthY,_)            <- getSize ys
+      ((li, lj), (ui, uj))  <- getBounds xs
+      ((li',lj'),(ui',uj')) <- getBounds ys
+      let resBnds | (lj,uj) == (li',ui') = ((li,lj'),(ui,uj'))
+                  | otherwise            = error "matrix dimensions must agree"
+
+      -- Initialise environment and copy over test data
+      --
+      putStrLn ">> Initialising"
+      bracket initCUDA (\(ctx,_) -> CUDA.destroy ctx) $ \(_,matMul) -> do
+
+      -- Ensure we release the memory, even if there was an error
+      --
+      putStrLn ">> Executing"
+      bracket
+        (initData xs ys)
+        (\(dx,dy,dz) -> mapM_ CUDA.free [dx,dy,dz]) $
+         \(dx,dy,dz) -> do
+          -- Repeat test many times...
+          --
+          CUDA.setParams     matMul [CUDA.VArg dx, CUDA.VArg dy, CUDA.VArg dz, CUDA.IArg widthX, CUDA.IArg widthY]
+          CUDA.setBlockShape matMul (BLOCK_SIZE,BLOCK_SIZE,1)
+          CUDA.setSharedSize matMul (fromIntegral (2 * BLOCK_SIZE * BLOCK_SIZE * sizeOf dummy))
+          CUDA.launch        matMul (widthY `div` BLOCK_SIZE, heightX `div` BLOCK_SIZE) Nothing
+          CUDA.sync
+
+          -- Copy back result
+          --
+          zs <- newArray_ resBnds
+          withMatrix zs $ \p -> CUDA.peekArray (widthY*heightX) dz p
+          return zs
+
+
+--------------------------------------------------------------------------------
+-- Test & Verify
+--------------------------------------------------------------------------------
+
+main :: IO ()
+main = do
+  putStrLn "== Generating random matrices"
+  xs <- randomArr ((1,1),(8*BLOCK_SIZE, 4*BLOCK_SIZE)) :: IO (Matrix Float)
+  ys <- randomArr ((1,1),(4*BLOCK_SIZE,12*BLOCK_SIZE)) :: IO (Matrix Float)
+
+  putStrLn "== Generating reference solution"
+  ref <- matMult xs ys
+
+  putStrLn "== Testing CUDA"
+  mat <- testCUDA xs ys
+
+  putStr "== Validating: "
+  verify ref mat >>= \rv -> putStrLn $ if rv then "Ok!" else "INVALID!"
+
diff --git a/examples/src/matrixMulDrv/matrix_mul.cu b/examples/src/matrixMulDrv/matrix_mul.cu
new file mode 100644
--- /dev/null
+++ b/examples/src/matrixMulDrv/matrix_mul.cu
@@ -0,0 +1,110 @@
+/*
+ * Copyright 1993-2009 NVIDIA Corporation.  All rights reserved.
+ *
+ * NVIDIA Corporation and its licensors retain all intellectual property and 
+ * proprietary rights in and to this software and related documentation. 
+ * Any use, reproduction, disclosure, or distribution of this software 
+ * and related documentation without an express license agreement from
+ * NVIDIA Corporation is strictly prohibited.
+ *
+ * Please refer to the applicable NVIDIA end user license agreement (EULA) 
+ * associated with this source code for terms and conditions that govern 
+ * your use of this NVIDIA software.
+ * 
+ */
+
+/* Matrix multiplication: C = A * B.
+ * Device code.
+ */
+
+#ifndef _MATRIXMUL_KERNEL_H_
+#define _MATRIXMUL_KERNEL_H_
+
+#include <stdio.h>
+#include "matrix_mul.h"
+
+#define CHECK_BANK_CONFLICTS 0
+#if CHECK_BANK_CONFLICTS
+#define AS(i, j) cutilBankChecker(((float*)&As[0][0]), (BLOCK_SIZE * i + j))
+#define BS(i, j) cutilBankChecker(((float*)&Bs[0][0]), (BLOCK_SIZE * i + j))
+#else
+#define AS(i, j) As[i][j]
+#define BS(i, j) Bs[i][j]
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+//! Matrix multiplication on the device: C = A * B
+//! wA is A's width and wB is B's width
+////////////////////////////////////////////////////////////////////////////////
+extern "C" __global__ void
+matrixMul(float* A, float* B, float* C, int wA, int wB)
+{
+    // Block index
+    int bx = blockIdx.x;
+    int by = blockIdx.y;
+
+    // Thread index
+    int tx = threadIdx.x;
+    int ty = threadIdx.y;
+
+    // Index of the first sub-matrix of A processed by the block
+    int aBegin = wA * BLOCK_SIZE * by;
+
+    // Index of the last sub-matrix of A processed by the block
+    int aEnd   = aBegin + wA - 1;
+
+    // Step size used to iterate through the sub-matrices of A
+    int aStep  = BLOCK_SIZE;
+
+    // Index of the first sub-matrix of B processed by the block
+    int bBegin = BLOCK_SIZE * bx;
+
+    // Step size used to iterate through the sub-matrices of B
+    int bStep  = BLOCK_SIZE * wB;
+
+    // Csub is used to store the element of the block sub-matrix
+    // that is computed by the thread
+    float Csub = 0;
+
+    // Loop over all the sub-matrices of A and B
+    // required to compute the block sub-matrix
+    for (int a = aBegin, b = bBegin;
+             a <= aEnd;
+             a += aStep, b += bStep) {
+
+        // Declaration of the shared memory array As used to
+        // store the sub-matrix of A
+        __shared__ float As[BLOCK_SIZE][BLOCK_SIZE];
+
+        // Declaration of the shared memory array Bs used to
+        // store the sub-matrix of B
+        __shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];
+
+        // Load the matrices from device memory
+        // to shared memory; each thread loads
+        // one element of each matrix
+        AS(ty, tx) = A[a + wA * ty + tx];
+        BS(ty, tx) = B[b + wB * ty + tx];
+
+        // Synchronize to make sure the matrices are loaded
+        __syncthreads();
+
+        // Multiply the two matrices together;
+        // each thread computes one element
+        // of the block sub-matrix
+        for (int k = 0; k < BLOCK_SIZE; ++k)
+            Csub += AS(ty, k) * BS(k, tx);
+
+        // Synchronize to make sure that the preceding
+        // computation is done before loading two new
+        // sub-matrices of A and B in the next iteration
+        __syncthreads();
+    }
+
+    // Write the block sub-matrix to device memory;
+    // each thread writes one element
+    int c = wB * BLOCK_SIZE * by + BLOCK_SIZE * bx;
+    C[c + wB * ty + tx] = Csub;
+}
+
+#endif // #ifndef _MATRIXMUL_KERNEL_H_
diff --git a/examples/src/matrixMulDrv/matrix_mul.h b/examples/src/matrixMulDrv/matrix_mul.h
new file mode 100644
--- /dev/null
+++ b/examples/src/matrixMulDrv/matrix_mul.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1993-2009 NVIDIA Corporation.  All rights reserved.
+ *
+ * NVIDIA Corporation and its licensors retain all intellectual property and 
+ * proprietary rights in and to this software and related documentation. 
+ * Any use, reproduction, disclosure, or distribution of this software 
+ * and related documentation without an express license agreement from
+ * NVIDIA Corporation is strictly prohibited.
+ *
+ * Please refer to the applicable NVIDIA end user license agreement (EULA) 
+ * associated with this source code for terms and conditions that govern 
+ * your use of this NVIDIA software.
+ * 
+ */
+
+#ifndef _MATRIXMUL_H_
+#define _MATRIXMUL_H_
+
+/* Thread block size */
+#define BLOCK_SIZE 16
+
+/* Matrix dimensions
+ * (chosen as multiples of the thread block size for simplicity)
+ */
+#define WA (3 * BLOCK_SIZE) /* Matrix A width  */
+#define HA (5 * BLOCK_SIZE) /* Matrix A height */
+#define WB (8 * BLOCK_SIZE) /* Matrix B width  */
+#define HB WA  /* Matrix B height */
+#define WC WB  /* Matrix C width  */
+#define HC HA  /* Matrix C height */
+
+#endif /* _MATRIXMUL_H_ */
+
diff --git a/examples/src/scan/Makefile b/examples/src/scan/Makefile
new file mode 100644
--- /dev/null
+++ b/examples/src/scan/Makefile
@@ -0,0 +1,18 @@
+#
+# Baking!
+#
+
+# ------------------------------------------------------------------------------
+# Input files
+# ------------------------------------------------------------------------------
+EXECUTABLE      := scan
+
+HSMAIN          := Scan.chs
+CUFILES         := scan.cu
+
+EXTRALIBS       := stdc++
+
+# ------------------------------------------------------------------------------
+# Haskell/CUDA build system
+# ------------------------------------------------------------------------------
+include ../../common/common.mk
diff --git a/examples/src/scan/Scan.chs b/examples/src/scan/Scan.chs
new file mode 100644
--- /dev/null
+++ b/examples/src/scan/Scan.chs
@@ -0,0 +1,112 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+--------------------------------------------------------------------------------
+--
+-- Module    : Scan
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Apply a binary operator to an array similar to 'fold', but return a
+-- successive list of values reduced from the left (or right).
+--
+--------------------------------------------------------------------------------
+
+module Main where
+
+#include "scan.h"
+
+-- Friends
+import C2HS                                     hiding (newArray)
+import Time
+import RandomVector
+
+-- System
+import Control.Monad
+import Control.Exception
+import qualified Foreign.CUDA as CUDA
+
+
+--------------------------------------------------------------------------------
+-- Reference
+--------------------------------------------------------------------------------
+
+scanList :: (Num e, Storable e) => Vector e -> IO (Vector e)
+scanList xs = do
+  bnds    <- getBounds xs
+  xs'     <- getElems  xs
+  (t,zs') <- benchmark 100 (return (scanl1 (+) xs')) (return ())
+  putStrLn $ "List: " ++ shows (fromInteger (timeIn millisecond t`div`100)::Float) " ms"
+  newListArray bnds zs'
+
+
+scanArr :: (Num e, Storable e) => Vector e -> IO (Vector e)
+scanArr xs = do
+  bnds  <- getBounds xs
+  zs    <- newArray_ bnds
+  let idx = range bnds
+  (t,_) <- benchmark 100 (foldM_ (k zs) 0 idx) (return ())
+  putStrLn $ "Array: " ++ shows (fromInteger (timeIn millisecond t)/100::Float) " ms"
+  return zs
+  where
+    k zs a i = do
+      x <- readArray xs i
+      let z = x+a
+      writeArray zs i z
+      return z
+
+
+--------------------------------------------------------------------------------
+-- CUDA
+--------------------------------------------------------------------------------
+
+--
+-- Include the time to copy the data to/from the storable array (significantly
+-- faster than from a Haskell list)
+--
+scanCUDA :: Vector Float -> IO (Vector Float)
+scanCUDA xs = do
+  bnds  <- getBounds xs
+  zs    <- newArray_ bnds
+  let len = rangeSize bnds
+  CUDA.allocaArray len $ \d_xs -> do
+  CUDA.allocaArray len $ \d_zs -> do
+  (t,_) <- flip (benchmark 100) CUDA.sync $ do
+    withVector xs $ \p -> CUDA.pokeArray len p d_xs
+    scanl1_plusf d_xs d_zs len
+    withVector zs $ \p -> CUDA.peekArray len d_zs p
+  putStrLn $ "CUDA: " ++ shows (fromInteger (timeIn millisecond t)/100::Float) " ms (with copy)"
+
+  (t',_) <- benchmark 100 (scanl1_plusf d_xs d_zs len) CUDA.sync
+  putStrLn $ "CUDA: " ++ shows (fromInteger (timeIn millisecond t')/100::Float) " ms (compute only)"
+
+  return zs
+
+{# fun unsafe scanl1_plusf
+  { withDP* `CUDA.DevicePtr Float'
+  , withDP* `CUDA.DevicePtr Float'
+  ,         `Int'
+  } -> `()' #}
+  where
+    withDP p a = CUDA.withDevicePtr p $ \p' -> a (castPtr p')
+
+
+--------------------------------------------------------------------------------
+-- Main
+--------------------------------------------------------------------------------
+
+main :: IO ()
+main = do
+  dev   <- CUDA.get
+  props <- CUDA.props dev
+  putStrLn $ "Using device " ++ show dev ++ ": " ++ CUDA.deviceName props
+
+  arr  <- randomArr (1,100000) :: IO (Vector Float)
+  ref  <- scanList arr
+  ref' <- scanArr  arr
+  cuda <- scanCUDA arr
+
+  return ()
+
+  putStr   "== Validating: "
+  verify ref ref' >>= \rv -> assert rv (return ())
+  verify ref cuda >>= \rv -> putStrLn $ if rv then "Ok!" else "INVALID!"
+
diff --git a/examples/src/scan/scan.cu b/examples/src/scan/scan.cu
new file mode 100644
--- /dev/null
+++ b/examples/src/scan/scan.cu
@@ -0,0 +1,204 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Module    : Scan
+ * Copyright : (c) 2009 Trevor L. McDonell
+ * License   : BSD
+ *
+ * ---------------------------------------------------------------------------*/
+
+#include "scan.h"
+
+#include "utils.h"
+#include "operator.h"
+#include "cudpp/cudpp_globals.h"
+#include "cudpp/scan_kernel.cu"
+#include "cudpp/vector_kernel.cu"
+
+template <typename T>
+struct scan_plan
+{
+    T           **block_sums;
+    size_t      num_levels;
+};
+
+static inline unsigned int
+calc_num_blocks(unsigned int N)
+{
+    return max(1u, (unsigned int)ceil((double)N / (SCAN_ELTS_PER_THREAD * CTA_SIZE)));
+}
+
+
+/*
+ * This is the CPU-side workhorse of the scan operation, invoking the kernel on
+ * each of the reduction blocks.
+ */
+template <class op, typename T, bool backward, bool exclusive>
+static void
+scan_recursive
+(
+    const T             *in,
+    T                   *out,
+    scan_plan<T>        *plan,
+    int                 N,
+    int                 level
+)
+{
+    size_t      num_blocks = calc_num_blocks(N);
+    bool        is_full    = N == num_blocks * SCAN_ELTS_PER_THREAD * CTA_SIZE;
+
+    dim3        grid(num_blocks, 1, 1);
+    dim3        block(CTA_SIZE, 1, 1);
+    size_t      smem = sizeof(T) * CTA_SIZE * 2;
+
+#define MULTIBLOCK      0x01
+#define FULLBLOCK       0x04
+    int traits = 0;
+    if (num_blocks > 1) traits |= MULTIBLOCK;
+    if (is_full)        traits |= FULLBLOCK;
+
+    /*
+     * Set up execution parameters, and execute the scan
+     */
+    switch (traits)
+    {
+    case 0:
+        scan4
+            < T, ScanTraits<T, op, backward, exclusive, false, false, false> >
+            <<<grid, block, smem>>>(out, in, NULL, N, 1, 1);
+        break;
+
+    case MULTIBLOCK:
+        scan4
+            < T, ScanTraits<T, op, backward, exclusive, false, true, false> >
+            <<<grid, block, smem>>>(out, in, plan->block_sums[level], N, 1, 1);
+        break;
+
+    case FULLBLOCK:
+        scan4
+            < T, ScanTraits<T, op, backward, exclusive, false, false, true> >
+            <<<grid, block, smem>>>(out, in, NULL, N, 1, 1);
+        break;
+
+    case MULTIBLOCK | FULLBLOCK:
+        scan4
+            < T, ScanTraits<T, op, backward, exclusive, false, true, true> >
+            <<<grid, block, smem>>>(out, in, plan->block_sums[level], N, 1, 1);
+        break;
+
+    default:
+        assert(!"Non-exhaustive patterns in match");
+    }
+
+    /*
+     * After scanning the sub-blocks, we now need to combine those results by
+     * taking the last value from each sub-block, and adding that to each of the
+     * successive blocks (i.e. scan across the sub-computations)
+     */
+    if (num_blocks > 1)
+    {
+        T *sums = plan->block_sums[level];
+
+        scan_recursive
+            <op, T, backward, true>
+            (sums, sums, plan, num_blocks, level+1);
+
+        vectorAddUniform4
+            <T, op, SCAN_ELTS_PER_THREAD>
+            <<<grid,block>>>
+            (out, sums, N, 4, 4, 0, 0);
+    }
+
+#undef MULTIBLOCK
+#undef FULLBLOCK
+}
+
+
+/*
+ * Allocate temporary memory used by the scan.
+ */
+template <typename T>
+static void
+scan_init(int N, scan_plan<T> *plan)
+{
+    size_t level        = 0;
+    size_t elements     = N;
+    size_t num_blocks;
+
+    /*
+     * Determine how many intermediate block-level summations will be required
+     */
+    for (elements = N; elements > 1; elements = num_blocks)
+    {
+        num_blocks = calc_num_blocks(elements);
+
+        if (num_blocks > 1)
+            ++level;
+    }
+
+    plan->block_sums = (T**) malloc(level * sizeof(T*));
+    plan->num_levels = level;
+
+    /*
+     * Now, allocate the necessary storage at each level
+     */
+    for (elements = N, level = 0; elements > 1; elements = num_blocks, level++)
+    {
+        num_blocks = calc_num_blocks(elements);
+
+        if (num_blocks > 1)
+            cudaMalloc((void**) &plan->block_sums[level], num_blocks * sizeof(T));
+    }
+}
+
+
+/*
+ * Clean up temporary memory used by the scan
+ */
+template <typename T>
+static void
+scan_finalise(scan_plan<T> *p)
+{
+    for (size_t l = 0; l < p->num_levels; ++l)
+        cudaFree(p->block_sums[l]);
+
+    free(p->block_sums);
+}
+
+
+/*
+ * Apply a binary operator to an array similar to `fold', but return a
+ * successive list of values reduced from the left. The reduction will take
+ * place in parallel, so the operator must be associative.
+ */
+template <class op, typename T, bool backward, bool exclusive>
+void
+scan
+(
+    const T     *in,
+    T           *out,
+    int         length
+)
+{
+    scan_plan<T> plan;
+    scan_init<T>(length, &plan);
+
+    scan_recursive<op, T, backward, exclusive>(in, out, &plan, length, 0);
+
+    scan_finalise<T>(&plan);
+}
+
+
+// -----------------------------------------------------------------------------
+// Instances
+// -----------------------------------------------------------------------------
+
+void scanl_plusf(float *in, float *out, int N)
+{
+    scan< Plus<float>, float, false, true >(in, out, N);
+}
+
+void scanl1_plusf(float *in, float *out, int N)
+{
+    scan< Plus<float>, float, false, false >(in, out, N);
+}
+
diff --git a/examples/src/scan/scan.h b/examples/src/scan/scan.h
new file mode 100644
--- /dev/null
+++ b/examples/src/scan/scan.h
@@ -0,0 +1,26 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Module    : Scan
+ * Copyright : (c) 2009 Trevor L. McDonell
+ * License   : BSD
+ *
+ * ---------------------------------------------------------------------------*/
+
+#ifndef __SCAN_H__
+#define __SCAN_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Instances
+ */
+void scanl_plusf(float *in, float *out, int N);
+void scanl1_plusf(float *in, float *out, int N);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/examples/src/smvm/Makefile b/examples/src/smvm/Makefile
new file mode 100644
--- /dev/null
+++ b/examples/src/smvm/Makefile
@@ -0,0 +1,20 @@
+#
+# Baking!
+#
+
+# ------------------------------------------------------------------------------
+# Input files
+# ------------------------------------------------------------------------------
+EXECUTABLE      := smvm
+
+HSMAIN          := SMVM.chs
+CUFILES         := smvm-csr.cu \
+                   smvm-cudpp.cu
+
+USECUDPP        := 1
+EXTRALIBS       := stdc++
+
+# ------------------------------------------------------------------------------
+# Haskell/CUDA build system
+# ------------------------------------------------------------------------------
+include ../../common/common.mk
diff --git a/examples/src/smvm/SMVM.chs b/examples/src/smvm/SMVM.chs
new file mode 100644
--- /dev/null
+++ b/examples/src/smvm/SMVM.chs
@@ -0,0 +1,201 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+--------------------------------------------------------------------------------
+--
+-- Module    : SMVM
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Sparse-matrix dense-vector multiplication
+--
+--------------------------------------------------------------------------------
+
+module Main where
+
+#include "smvm.h"
+
+-- Friends
+import Time
+import C2HS
+import RandomVector                             (randomList,randomListR,verifyList)
+
+-- System
+import Numeric
+import Data.List
+import Control.Monad
+import Control.Applicative
+import System.Random
+import Foreign.CUDA                             (withDevicePtr)
+import qualified Foreign.CUDA as CUDA
+
+--
+-- A very simple sparse-matrix / vector representation
+-- (confusingly, different from that in RandomVector and used elsewhere)
+--
+type Vector e       = [e]
+type SparseVector e = [(Int,e)]
+type SparseMatrix e = [SparseVector e]
+
+--------------------------------------------------------------------------------
+-- Reference
+--------------------------------------------------------------------------------
+
+smvm :: Num e => SparseMatrix e -> Vector e -> Vector e
+smvm sm v = [ sum [ x * (v!!col) | (col,x) <- sv ]  | sv <- sm ]
+
+--------------------------------------------------------------------------------
+-- CUDA
+--------------------------------------------------------------------------------
+
+--
+-- Sparse-matrix vector multiplication, using compressed-sparse row format.
+--
+-- Lots of boilerplate to copy data to the device. Our simple list
+-- representation has atrocious copy performance (see the `bandwidthTest'
+-- example), so don't include that in the benchmarking
+--
+smvm_csr :: SparseMatrix Float -> Vector Float -> IO (Float, Vector Float)
+smvm_csr sm v =
+  let matData = concatMap (map cFloatConv . snd . unzip) sm
+      colIdx  = concatMap (map cIntConv   . fst . unzip) sm
+      rowPtr  = scanl (+) 0 (map (cIntConv . length) sm)
+      v'      = map cFloatConv v
+#ifdef __DEVICE_EMULATION__
+      iters   = 1
+#else
+      iters   = 100
+#endif
+  in
+  CUDA.withListArray    matData  $ \d_data       ->
+  CUDA.withListArray    rowPtr   $ \d_ptr        ->
+  CUDA.withListArray    colIdx   $ \d_indices    ->
+  CUDA.withListArrayLen v'       $ \num_rows d_x ->
+  CUDA.allocaArray      num_rows $ \d_y          -> do
+    (t,_) <- benchmark iters (smvm_csr_f d_y d_x d_data d_ptr d_indices num_rows) CUDA.sync
+    y     <- map cFloatConv <$> CUDA.peekListArray num_rows d_y
+    return (fromInteger (timeIn millisecond t) / fromIntegral iters, y)
+
+
+{# fun unsafe smvm_csr_f
+  { withDevicePtr* `CUDA.DevicePtr CFloat'
+  , withDevicePtr* `CUDA.DevicePtr CFloat'
+  , withDevicePtr* `CUDA.DevicePtr CFloat'
+  , withDevicePtr* `CUDA.DevicePtr CUInt'
+  , withDevicePtr* `CUDA.DevicePtr CUInt'
+  ,                `Int'                   } -> `()' #}
+
+
+--
+-- Sparse-matrix vector multiplication from CUDPP
+--
+smvm_cudpp :: SparseMatrix Float -> Vector Float -> IO (Float, Vector Float)
+smvm_cudpp sm v =
+  let matData = concatMap (map cFloatConv . snd . unzip) sm
+      colIdx  = concatMap (map cIntConv   . fst . unzip) sm
+      rowPtr  = scanl (+) 0 (map (cIntConv . length) sm)
+      v'      = map cFloatConv v
+#ifdef __DEVICE_EMULATION__
+      iters   = 1
+#else
+      iters   = 100
+#endif
+  in
+  CUDA.withListArrayLen v'       $ \num_rows     d_x    ->
+  CUDA.allocaArray      num_rows $ \d_y                 ->
+  withArrayLen          matData  $ \num_nonzeros h_data ->
+  withArray             rowPtr   $ \h_rowPtr            ->
+  withArray             colIdx   $ \h_colIdx            -> do
+    (t,_) <- benchmark iters (smvm_cudpp_f d_y d_x h_data h_rowPtr h_colIdx num_rows num_nonzeros) CUDA.sync
+    y     <- map cFloatConv <$> CUDA.peekListArray num_rows d_y
+    return (fromInteger (timeIn millisecond t) / fromIntegral iters, y)
+
+{# fun unsafe smvm_cudpp_f
+  { withDevicePtr* `CUDA.DevicePtr CFloat'
+  , withDevicePtr* `CUDA.DevicePtr CFloat'
+  , id             `Ptr CFloat'
+  , id             `Ptr CUInt'
+  , id             `Ptr CUInt'
+  ,                `Int'
+  ,                `Int'                   } -> `()' #}
+
+
+--------------------------------------------------------------------------------
+-- Main
+--------------------------------------------------------------------------------
+
+--
+-- Generate random matrices
+--
+sparseMat :: (Num e, Random e, Storable e) => (Int,Int) -> (Int,Int) -> IO (SparseMatrix e)
+sparseMat bnds (h,w) = replicateM h sparseVec
+  where
+    sparseVec = do
+      nz  <- randomRIO bnds                         -- number of non-zero elements
+      idx <- nub . sort <$> randomListR nz (0,w-1)  -- remove duplicate column indices
+      zip idx <$> randomList (length idx)           -- (column indices don't actually need to be sorted)
+
+denseMat :: (Num e, Random e, Storable e) => (Int,Int) -> IO (SparseMatrix e)
+denseMat (h,w) = replicateM h (zip [0..] <$> randomList w)
+
+--
+-- Some test-harness utilities
+--
+stats :: (Floating a, Ord a) => [a] -> (a,a,a,a,a,a)
+stats (x:xs) = finish . foldl' stats' (x,x,x,x*x,1) $ xs
+  where
+    stats' (mn,mx,s,ss,n) v = (min v mn, max v mx, s+v, ss+v*v, n+1)
+    finish (mn,mx,s,ss,n)   = (mn, mx, av, var, stdev, n)
+      where av    = s/n
+            var   = (1/(n-1))*ss - (n/(n-1))*av*av
+            stdev = sqrt var
+
+
+testAlgorithm :: (Num e, Ord e, Floating e)
+             => String                                                  -- name of the algorithm
+             -> (SparseMatrix e -> Vector e -> IO (Float, Vector e))    -- return time (ms), and result
+             -> SparseMatrix e                                          -- input matrix
+             -> Vector e                                                -- input vector
+             -> Vector e                                                -- reference solution
+             -> IO ()
+testAlgorithm name f m v ref = do
+    putStr name
+    (t,y) <- f m v
+    putStr   $ if verifyList ref y then "Ok!      " else "INVALID! "
+    putStr   $ "( " ++ showFFloat (Just 2) t " ms, "
+    putStrLn $ showFFloat (Just 2) (fromIntegral (2 * 1000 * sum (map length m)) / (t * 1E9)) " GFLOPS )"
+
+
+testMatrix :: String -> SparseMatrix Float -> Vector Float -> IO ()
+testMatrix name sm v = do
+  let w                  = length v
+      (_,_,av,_,stdev,h) = stats (map (fromIntegral . length) sm)
+      ref                = smvm sm v
+
+  putStr   $ name ++ ": " ++ show w ++ "x" ++ show (round h)
+  putStr   $ ", " ++ shows (round (av*h)) " non-zero elements "
+  putStrLn $ "( " ++ showFFloat (Just 2) av " +/- " ++ showFFloat (Just 2) stdev " )"
+
+  testAlgorithm "  smvm-csr:     " smvm_csr   sm v ref
+  testAlgorithm "  smvm-cudpp:   " smvm_cudpp sm v ref
+  putStrLn ""
+
+
+--
+-- Finally, the main function
+--
+main :: IO ()
+main = do
+  dev   <- CUDA.get
+  props <- CUDA.props dev
+  putStrLn $ "Using device " ++ show dev ++ ": \"" ++ CUDA.deviceName props ++ "\""
+  putStrLn $ "  Compute capability:  " ++ show (CUDA.computeCapability props)
+  putStrLn $ "  Total global memory: " ++
+    showFFloat (Just 2) (fromIntegral (CUDA.totalGlobalMem props) / (1024*1024) :: Double) " GB\n"
+
+  v1 <- randomList 512
+  v2 <- randomList 2048
+  m1 <- denseMat  (512,512)
+  m2 <- sparseMat (20,200) (20 * 2048,2048)
+
+  testMatrix "Dense Matrix"  m1 v1
+  testMatrix "Sparse Matrix" m2 v2
+
diff --git a/examples/src/smvm/smvm-csr.cu b/examples/src/smvm/smvm-csr.cu
new file mode 100644
--- /dev/null
+++ b/examples/src/smvm/smvm-csr.cu
@@ -0,0 +1,246 @@
+/*
+ *  Copyright 2008-2009 NVIDIA Corporation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+
+#include "smvm.h"
+#include "utils.h"
+#include "texture.h"
+
+
+/* -----------------------------------------------------------------------------
+ * Sparse-matrix dense-vector multiplication, compressed-sparse row format
+ * -----------------------------------------------------------------------------
+ *
+ * Each row of the CSR matrix is assigned to a warp, which computes the dot
+ * product of the i-th row of A with the x vector, in parallel.
+ *
+ *   y[i] = A[i,:] * x
+ *
+ * This division of work implies that the CSR index and data arrays (Aj and Ax)
+ * are accessed in a contiguous manner (but generally not aligned). On the GT200
+ * these accesses are coalesced, unlike kernels based on the one-row-per-thread
+ * division of work. Since an entire 32-thread warp is assigned to each row,
+ * many threads will remain idle when their row contains a small number of
+ * elements. This code relies on implicit synchronization among threads in a
+ * warp.
+ *
+ * Optionally, the texture cache may be used for accessing the x vector. This
+ * generally shows good improvements.
+ *
+ * References:
+ *
+ *  [1] N. Bell and M. Garland. "Efficient sparse matrix-vector multiplication on
+ *      CUDA." NVIDIA Technical Report NVR-2008-004, NVIDIA Corporation, Dec. 2008.
+ *
+ *  [2] N. Bell and M. Garland. "Implementing sparse matrix-vector
+ *      multiplication on throughput-oriented processors." In Supercomputing
+ *      `09: Proceedings of the 2009 Conference on High Performance Computing
+ *      Networking, Storage and Analysis, pages 1-11, 2009.
+ */
+template <unsigned int BlockSize, typename T, bool UseCache>
+__global__ static void
+smvm_k
+(
+    T                   *d_y,
+    const T             *d_x,
+    const T             *d_Ax,
+    const unsigned int  *d_Ap,
+    const unsigned int  *d_Aj,
+    const unsigned int  num_rows
+)
+{
+    /*
+     * Require at least a full warp for each row. This could be relaxed by
+     * modifying the cooperative reduction step
+     */
+    assert(BlockSize % WARP_SIZE == 0);
+
+    const unsigned int vectorsPerBlock = BlockSize / WARP_SIZE;
+    const unsigned int num_vectors     = vectorsPerBlock * gridDim.x;
+    const unsigned int thread_id       = BlockSize * blockIdx.x + threadIdx.x;
+    const unsigned int vector_id       = thread_id / WARP_SIZE;
+    const unsigned int thread_lane     = threadIdx.x & (WARP_SIZE-1);
+    const unsigned int vector_lane     = threadIdx.x / WARP_SIZE;
+
+    __shared__ volatile T            s_data[(vectorsPerBlock+1) * WARP_SIZE];
+    __shared__ volatile unsigned int s_ptrs[vectorsPerBlock][2];
+
+    for (unsigned int row = vector_id; row < num_rows; row += num_vectors)
+    {
+        /*
+         * Use two threads to fetch the indices of the start and end of this
+         * segment. This is a single coalesced (although unaligned) global read
+         * rather than two, and hence considerably faster.
+         */
+        if (thread_lane < 2)
+            s_ptrs[vector_lane][thread_lane] = d_Ap[row + thread_lane];
+
+        __EMUSYNC;
+        const unsigned int row_start = s_ptrs[vector_lane][0];
+        const unsigned int row_end   = s_ptrs[vector_lane][1];
+
+        /*
+         * Have the threads read in all values for this row, accumulating local
+         * dot-product sums. Then, reduce this cooperatively in shared memory.
+         */
+        T sum = 0;
+        for (unsigned int j = row_start + thread_lane; j < row_end; j += WARP_SIZE)
+            sum += d_Ax[j] * fetch_x<UseCache>(d_Aj[j], d_x);
+
+        s_data[threadIdx.x] = sum;                                  __EMUSYNC;
+        s_data[threadIdx.x] = sum = sum + s_data[threadIdx.x + 16]; __EMUSYNC;
+        s_data[threadIdx.x] = sum = sum + s_data[threadIdx.x +  8]; __EMUSYNC;
+        s_data[threadIdx.x] = sum = sum + s_data[threadIdx.x +  4]; __EMUSYNC;
+        s_data[threadIdx.x] = sum = sum + s_data[threadIdx.x +  2]; __EMUSYNC;
+        s_data[threadIdx.x] = sum = sum + s_data[threadIdx.x +  1]; __EMUSYNC;
+
+#if 0
+        /*
+         * Alternative method (slightly slower, due to bank conflicts?)
+         */
+        s_data[threadIdx.x] += s_data[threadIdx.x + 16];
+        s_data[threadIdx.x] += s_data[threadIdx.x +  8];
+        s_data[threadIdx.x] += s_data[threadIdx.x +  4];
+        s_data[threadIdx.x] += s_data[threadIdx.x +  2];
+        s_data[threadIdx.x] += s_data[threadIdx.x +  1];
+#endif
+
+        /*
+         * Finally, first thread writes the result for this row
+         */
+        if (thread_lane == 0)
+            d_y[row] = s_data[threadIdx.x];
+    }
+}
+
+
+template <typename T, bool UseCache>
+static void
+smvm_dispatch
+(
+    T                   *d_y,
+    const T             *d_x,
+    const T             *d_data,
+    const unsigned int  *d_ptr,
+    const unsigned int  *d_indices,
+    const unsigned int  num_rows,
+    const unsigned int  blocks,
+    const unsigned int  threads
+)
+{
+    const unsigned int smem = 0;
+
+    switch (threads)
+    {
+    case 512: smvm_k<512,T,UseCache><<<blocks,threads,smem>>>(d_y, d_x, d_data, d_ptr, d_indices, num_rows); break;
+    case 256: smvm_k<256,T,UseCache><<<blocks,threads,smem>>>(d_y, d_x, d_data, d_ptr, d_indices, num_rows); break;
+    case 128: smvm_k<128,T,UseCache><<<blocks,threads,smem>>>(d_y, d_x, d_data, d_ptr, d_indices, num_rows); break;
+    case  64: smvm_k< 64,T,UseCache><<<blocks,threads,smem>>>(d_y, d_x, d_data, d_ptr, d_indices, num_rows); break;
+    case  32: smvm_k< 32,T,UseCache><<<blocks,threads,smem>>>(d_y, d_x, d_data, d_ptr, d_indices, num_rows); break;
+    default:
+        assert(!"Non-exhaustive patterns in match");
+    }
+}
+
+
+/*
+ * Select an "optimal" number of threads and blocks for the problem size. This
+ * is an act of balancing resource usage: shared memory, registers, in-flight
+ * threads and blocks per multiprocessor. Ultimately, this requires some
+ * experimentation for every kernel, device and problem set, but we choose some
+ * sensible default values.
+ *
+ * Additionally, each block will have at least one full warp, as required by the
+ * core kernel.
+ */
+static void
+smvm_control
+(
+    unsigned int        n,
+    unsigned int        &blocks,
+    unsigned int        &threads,
+    unsigned int        maxThreads = MAX_THREADS,
+    unsigned int        maxBlocks  = MAX_BLOCKS
+
+)
+{
+    threads = (n < maxThreads) ? max(WARP_SIZE, ceilPow2(n)) : maxThreads;
+    blocks  = (n + threads - 1) / threads;
+    blocks  = min(blocks, maxBlocks);
+}
+
+
+/*
+ * Sparse matrix multiplication:
+ *   y = A * x
+ *
+ * The CSR format explicitly stores column indices (indices) and non-zero values
+ * (data) in row-major order, together with a third array of row pointers (ptr).
+ * For an M-by-N matrix, ptr has length (M+1) and stores the offset to the start
+ * of the i-th row in ptr[i]. The last entry then, corresponding to the (M+1)-st
+ * row, contains the number of non-zero elements in the matrix.
+ *
+ * Example:
+ *            | 1 7 0 0 |
+ *        A = | 0 2 8 0 |
+ *            | 5 0 3 9 |
+ *            | 0 6 0 4 |
+ *
+ *      ptr = [ 0 2 4 7 9 ]
+ *  indices = [ 0 1 1 2 0 2 3 1 3 ]
+ *     data = [ 1 7 2 8 5 3 9 6 4 ]
+ *
+ * d_y          The output vector
+ * d_x          The input (dense) vector to multiply against
+ * d_data       The non-zero elements of the sparse, stored row-major order
+ * d_ptr        Row offsets
+ * d_indices    Column indices
+ */
+template <typename T, bool UseCache>
+static void
+smvm_csr
+(
+    T                   *d_y,
+    const T             *d_x,
+    const T             *d_data,
+    const unsigned int  *d_ptr,
+    const unsigned int  *d_indices,
+    const unsigned int  num_rows
+)
+{
+    unsigned int blocks;
+    unsigned int threads;
+
+    if (UseCache)
+        bind_x(d_x);
+
+    smvm_control(num_rows, blocks, threads);
+    smvm_dispatch<T,UseCache>(d_y, d_x, d_data, d_ptr, d_indices, num_rows, blocks, threads);
+
+    if (UseCache)
+        unbind_x(d_x);
+}
+
+/* -----------------------------------------------------------------------------
+ * Instances
+ * ---------------------------------------------------------------------------*/
+
+void
+smvm_csr_f(float *d_y, float *d_x, float *d_data, unsigned int *d_rowPtr, unsigned int *d_colIdx, unsigned int num_rows)
+{
+    smvm_csr<float,true>(d_y, d_x, d_data, d_rowPtr, d_colIdx, num_rows);
+}
+
diff --git a/examples/src/smvm/smvm-cudpp.cu b/examples/src/smvm/smvm-cudpp.cu
new file mode 100644
--- /dev/null
+++ b/examples/src/smvm/smvm-cudpp.cu
@@ -0,0 +1,56 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Module    : SMVM
+ * Copyright : (c) 2009 Trevor L. McDonell
+ * License   : BSD
+ *
+ * ---------------------------------------------------------------------------*/
+
+
+#include "smvm.h"
+#include <cudpp.h>
+
+template <typename T> CUDPPDatatype getType();
+template <> CUDPPDatatype getType<float>() { return CUDPP_FLOAT; }
+template <> CUDPPDatatype getType<unsigned int>() { return CUDPP_UINT; }
+
+
+/*
+ * Sparse matrix-dense vector multiply. Hook directly into the CUDPP
+ * implementation.
+ */
+template <typename T>
+void smvm_cudpp
+(
+    float               *d_y,
+    const float         *d_x,
+    const float         *h_data,
+    const unsigned int  *h_rowPtr,
+    const unsigned int  *h_colIdx,
+    const unsigned int  num_rows,
+    const unsigned int  num_nonzeros
+)
+{
+    CUDPPConfiguration cp;
+    CUDPPHandle        sm;
+
+    cp.datatype  = getType<T>();
+    cp.options   = 0;
+    cp.algorithm = CUDPP_SPMVMULT;
+
+    cudppSparseMatrix(&sm, cp, num_nonzeros, num_rows, h_data, h_rowPtr, h_colIdx);
+    cudppSparseMatrixVectorMultiply(sm, d_y, d_x);
+
+    cudppDestroySparseMatrix(sm);
+}
+
+
+// -----------------------------------------------------------------------------
+// Instances
+// -----------------------------------------------------------------------------
+
+void smvm_cudpp_f(float *d_y, float *d_x, float *h_data, unsigned int *h_rowPtr, unsigned int *h_colIdx, unsigned int num_rows, unsigned int num_nonzeros)
+{
+    smvm_cudpp<float>(d_y, d_x, h_data, h_rowPtr, h_colIdx, num_rows, num_nonzeros);
+}
+
diff --git a/examples/src/smvm/smvm.h b/examples/src/smvm/smvm.h
new file mode 100644
--- /dev/null
+++ b/examples/src/smvm/smvm.h
@@ -0,0 +1,33 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Module    : SMVM
+ * Copyright : (c) 2009 Trevor L. McDonell
+ * License   : BSD
+ *
+ * ---------------------------------------------------------------------------*/
+
+#ifndef __SMVM_H__
+#define __SMVM_H__
+
+/*
+ * Optimised for Tesla C1060 (compute 1.3)
+ * Maximum performance for your card may be achieved with different values.
+ *
+ * http://developer.download.nvidia.com/compute/cuda/CUDA_Occupancy_calculator.xls
+ */
+#define MAX_THREADS             128
+#define MAX_BLOCKS_PER_SM       8
+#define MAX_BLOCKS              (MAX_BLOCKS_PER_SM * 30)
+#define WARP_SIZE               32
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void smvm_csr_f(float *d_y, float *d_x, float *d_data, unsigned int *d_rowPtr, unsigned int *d_colIdx, unsigned int num_rows);
+void smvm_cudpp_f(float *d_y, float *d_x, float *h_data, unsigned int *h_rowPtr, unsigned int *h_colIdx, unsigned int num_rows, unsigned int num_nonzeros);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/examples/src/smvm/texture.h b/examples/src/smvm/texture.h
new file mode 100644
--- /dev/null
+++ b/examples/src/smvm/texture.h
@@ -0,0 +1,94 @@
+/*
+ *  Copyright 2008-2009 NVIDIA Corporation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+
+#ifndef __TEXTURE_H__
+#define __TEXTURE_H__
+
+#include "utils.h"
+#include <cuda_runtime_api.h>
+
+/*
+ * These textures are (optionally) used to cache the 'x' vector in y += A*x
+ * Use int2 to pull doubles through texture cache.
+ */
+texture<float,1> tex_x_float;
+texture<int2,1>  tex_x_double;
+
+inline void
+bind_x(const float * x)
+{
+    size_t offset = size_t(-1);
+
+    CUDA_SAFE_CALL(cudaBindTexture(&offset, tex_x_float, x));
+    if (offset != 0)
+        assert(!"memory is not aligned, refusing to use texture cache");
+}
+
+inline void
+bind_x(const double * x)
+{
+    size_t offset = size_t(-1);
+
+    CUDA_SAFE_CALL(cudaBindTexture(&offset, tex_x_double, x));
+    if (offset != 0)
+        assert(!"memory is not aligned, refusing to use texture cache");
+}
+
+/*
+ * NOTE: the parameter is unused only to distinguish the two unbind functions
+ */
+inline void
+unbind_x(const float *)
+{
+    CUDA_SAFE_CALL(cudaUnbindTexture(tex_x_float));
+}
+
+inline void
+unbind_x(const double *)
+{
+    CUDA_SAFE_CALL(cudaUnbindTexture(tex_x_double));
+}
+
+template <bool UseCache>
+__inline__ __device__ float
+fetch_x(const int& i, const float * x)
+{
+    if (UseCache) return tex1Dfetch(tex_x_float, i);
+    else          return x[i];
+}
+
+#ifndef CUDA_NO_SM_13_DOUBLE_INTRINSICS
+template <bool UseCache>
+__inline__ __device__ double fetch_x(const int& i, const double * x)
+{
+#if __CUDA_ARCH__ < 130
+#error "double precision require Compute Compatibility 1.3 or greater"
+#endif
+    if (UseCache)
+    {
+        int2 v = tex1Dfetch(tex_x_double, i);
+        return __hiloint2double(v.y, v.x);
+    }
+    else
+    {
+        return x[i];
+    }
+}
+#endif
+
+#endif  // __TEXTURE_H__
+
diff --git a/examples/src/sort/Makefile b/examples/src/sort/Makefile
new file mode 100644
--- /dev/null
+++ b/examples/src/sort/Makefile
@@ -0,0 +1,19 @@
+#
+# Baking!
+#
+
+# ------------------------------------------------------------------------------
+# Input files
+# ------------------------------------------------------------------------------
+EXECUTABLE      := sort
+
+HSMAIN          := Sort.chs
+CUFILES         := radix_sort.cu
+
+USECUDPP        := 1
+EXTRALIBS       := stdc++
+
+# ------------------------------------------------------------------------------
+# Haskell/CUDA build system
+# ------------------------------------------------------------------------------
+include ../../common/common.mk
diff --git a/examples/src/sort/Sort.chs b/examples/src/sort/Sort.chs
new file mode 100644
--- /dev/null
+++ b/examples/src/sort/Sort.chs
@@ -0,0 +1,98 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+--------------------------------------------------------------------------------
+--
+-- Module    : Sort
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Reduce a vector of (key,value) pairs
+--
+--------------------------------------------------------------------------------
+
+module Main where
+
+#include "sort.h"
+
+import C2HS
+import RandomVector
+
+import Data.Ord
+import Data.List
+import Control.Monad
+import qualified Foreign.CUDA as C
+
+
+
+--------------------------------------------------------------------------------
+-- CUDA
+
+test_f :: (Storable a, Eq a) => [(Float,a)] -> IO Bool
+test_f kv =
+  let l     = length kv
+      (k,v) = unzip kv
+  in
+  C.withListArray k $ \d_k ->
+  C.withListArray v $ \d_v -> do
+
+    sort_f d_k d_v (length kv)
+    res <- liftM2 zip (C.peekListArray l d_k) (C.peekListArray l d_v)
+    return (res == sortBy (comparing fst) kv)
+
+
+test_i :: (Storable a, Eq a) => [(Int,a)] -> IO Bool
+test_i kv =
+  let l     = length kv
+      (k,v) = unzip kv
+  in
+  C.withListArray k $ \d_k ->
+  C.withListArray v $ \d_v -> do
+
+    sort_ui d_k d_v (length kv)
+    res <- liftM2 zip (C.peekListArray l d_k) (C.peekListArray l d_v)
+    return (res == sortBy (comparing fst) kv)
+
+
+{# fun unsafe sort_f
+  { withDP* `C.DevicePtr Float'
+  , withDP* `C.DevicePtr a'
+  ,         `Int'
+  } -> `()' #}
+  where
+    withDP p a = C.withDevicePtr p $ \p' -> a (castPtr p')
+
+{# fun unsafe sort_ui
+  { withDP* `C.DevicePtr Int'
+  , withDP* `C.DevicePtr a'
+  ,         `Int'
+  } -> `()' #}
+  where
+    withDP p a = C.withDevicePtr p $ \p' -> a (castPtr p')
+
+--
+-- I don't need to learn template haskell or quick check... nah, not at all...
+--
+main :: IO ()
+main = do
+  f <- randomList  10000
+  i <- randomListR 10000 (0,1000)
+
+  putStr "Test (float,int): "
+  test_f (zip f i) >>= \r -> case r of
+    True -> putStrLn "Ok!"
+    _    -> putStrLn "INVALID!"
+
+  putStr "Test (float,float): "
+  test_f (zip f f) >>= \r -> case r of
+    True -> putStrLn "Ok!"
+    _    -> putStrLn "INVALID!"
+
+  putStr "Test (int,int): "
+  test_i (zip i i) >>= \r -> case r of
+    True -> putStrLn "Ok!"
+    _    -> putStrLn "INVALID!"
+
+  putStr "Test (int,float): "
+  test_i (zip i f) >>= \r -> case r of
+    True -> putStrLn "Ok!"
+    _    -> putStrLn "INVALID!"
+
diff --git a/examples/src/sort/radix_sort.cu b/examples/src/sort/radix_sort.cu
new file mode 100644
--- /dev/null
+++ b/examples/src/sort/radix_sort.cu
@@ -0,0 +1,61 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Module    : Sort
+ * Copyright : (c) 2009 Trevor L. McDonell
+ * License   : BSD
+ *
+ *----------------------------------------------------------------------------*/
+
+#include <cudpp.h>
+
+#include "sort.h"
+
+
+template <typename T> CUDPPDatatype static getType();
+template <> CUDPPDatatype getType<float>()        { return CUDPP_FLOAT; }
+template <> CUDPPDatatype getType<unsigned int>() { return CUDPP_UINT;  }
+
+
+/*
+ * In-place radix sort of values or key-value pairs. Values can be any 32-bit
+ * type, as their payload is never inspected or manipulated.
+ */
+template <typename T>
+static void
+radix_sort
+(
+    unsigned int        length,
+    T                   *d_keys,
+    void                *d_vals = NULL,
+    int                 bits    = 8 * sizeof(T)
+)
+{
+    CUDPPHandle         plan;
+    CUDPPConfiguration  cp;
+
+    cp.datatype  = getType<T>();
+    cp.algorithm = CUDPP_SORT_RADIX;
+    cp.options   = (d_vals != NULL) ? CUDPP_OPTION_KEY_VALUE_PAIRS
+                                    : CUDPP_OPTION_KEYS_ONLY;
+
+    cudppPlan(&plan, cp, length, 1, 0);
+    cudppSort(plan, d_keys, d_vals, bits, length);
+
+    cudppDestroyPlan(plan);
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Instances
+ * ---------------------------------------------------------------------------*/
+
+void sort_f(float *d_keys, void *d_vals, unsigned int length)
+{
+    radix_sort<float>(length, d_keys, d_vals);
+}
+
+void sort_ui(unsigned int *d_keys, void *d_vals, unsigned int length)
+{
+    radix_sort<unsigned int>(length, d_keys, d_vals);
+}
+
diff --git a/examples/src/sort/sort.h b/examples/src/sort/sort.h
new file mode 100644
--- /dev/null
+++ b/examples/src/sort/sort.h
@@ -0,0 +1,23 @@
+/* -----------------------------------------------------------------------------
+ *
+ * Module    : Sort
+ * Copyright : (c) 2009 Trevor L. McDonell
+ * License   : BSD
+ *
+ * ---------------------------------------------------------------------------*/
+
+#ifndef __SORT_PRIV_H__
+#define __SORT_PRIV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void sort_f(float *d_keys, void *d_vals, unsigned int length);
+void sort_ui(unsigned int *d_keys, void *d_vals, unsigned int length);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
+
diff --git a/examples/src/vectorAddDrv/Makefile b/examples/src/vectorAddDrv/Makefile
new file mode 100644
--- /dev/null
+++ b/examples/src/vectorAddDrv/Makefile
@@ -0,0 +1,18 @@
+#
+# Baking!
+#
+
+# ------------------------------------------------------------------------------
+# Input files
+# ------------------------------------------------------------------------------
+EXECUTABLE	:= vectorAddDrv
+
+HSMAIN		:= VectorAdd.hs
+PTXFILES	:= vector_add.cu
+
+USEDRVAPI	:= 1
+
+# ------------------------------------------------------------------------------
+# Haskell/CUDA build system
+# ------------------------------------------------------------------------------
+include ../../common/common.mk
diff --git a/examples/src/vectorAddDrv/VectorAdd.hs b/examples/src/vectorAddDrv/VectorAdd.hs
new file mode 100644
--- /dev/null
+++ b/examples/src/vectorAddDrv/VectorAdd.hs
@@ -0,0 +1,121 @@
+--------------------------------------------------------------------------------
+--
+-- Module    : VectorAdd
+-- Copyright : (c) 2009 Trevor L. McDonell
+-- License   : BSD
+--
+-- Element-wise addition of two vectors
+--
+--------------------------------------------------------------------------------
+
+module Main where
+
+-- Friends
+import RandomVector
+
+-- System
+import Numeric
+import Control.Monad
+import Control.Exception
+import Data.Array.Storable
+import qualified Data.ByteString.Char8 as B
+import qualified Foreign.CUDA.Driver   as CUDA
+
+
+--------------------------------------------------------------------------------
+-- Reference implementation
+--------------------------------------------------------------------------------
+
+testRef :: (Num e, Storable e) => Vector e -> Vector e -> IO (Vector e)
+testRef xs ys = do
+  (i,j) <- getBounds xs
+  res   <- newArray_ (i,j)
+  forM_ [i..j] (add res)
+  return res
+  where
+    add res idx = do
+      a <- readArray xs idx
+      b <- readArray ys idx
+      writeArray res idx (a+b)
+
+--------------------------------------------------------------------------------
+-- CUDA
+--------------------------------------------------------------------------------
+
+--
+-- Initialise the device and context. Load the PTX source code, and return a
+-- reference to the kernel function.
+--
+initCUDA :: IO (CUDA.Context, CUDA.Fun)
+initCUDA = do
+  CUDA.initialise []
+  dev     <- CUDA.device 0
+  ctx     <- CUDA.create dev []
+  ptx     <- B.readFile "data/vector_add.ptx"
+  (mdl,r) <- CUDA.loadDataEx ptx [CUDA.MaxRegisters 32]
+  fun     <- CUDA.getFun mdl "VecAdd"
+
+  putStrLn $ ">> PTX JIT compilation (" ++ showFFloat (Just 2) (CUDA.jitTime r) " ms)"
+  B.putStrLn (CUDA.jitInfoLog r)
+  return (ctx,fun)
+
+
+--
+-- Run the test
+--
+testCUDA :: (Num e, Storable e) => Vector e -> Vector e -> IO (Vector e)
+testCUDA xs ys = do
+  (m,n)   <- getBounds xs
+  let len = (n-m+1)
+
+  -- Initialise environment and copy over test data
+  --
+  putStrLn ">> Initialising"
+  bracket initCUDA (\(ctx,_) -> CUDA.destroy ctx) $ \(_,addVec) -> do
+
+  -- Allocate some device memory. This will be freed once the computation
+  -- terminates, either normally or by exception.
+  --
+  putStrLn ">> Executing"
+  CUDA.allocaArray len $ \dx -> do
+  CUDA.allocaArray len $ \dy -> do
+  CUDA.allocaArray len $ \dz -> do
+
+  -- Copy over the data
+  --
+  withVector xs $ \p -> CUDA.pokeArray len p dx
+  withVector ys $ \p -> CUDA.pokeArray len p dy
+
+  -- Setup and execute the kernel (repeat test many times...)
+  --
+  CUDA.setParams     addVec [CUDA.VArg dx, CUDA.VArg dy, CUDA.VArg dz, CUDA.IArg len]
+  CUDA.setBlockShape addVec (128,1,1)
+  CUDA.launch        addVec ((len+128-1) `div` 128, 1) Nothing
+  CUDA.sync
+
+  -- Copy back result
+  --
+  zs <- newArray_ (m,n)
+  withVector zs $ \p -> CUDA.peekArray len dz p
+  return zs
+
+
+--------------------------------------------------------------------------------
+-- Test & Verify
+--------------------------------------------------------------------------------
+
+main :: IO ()
+main = do
+  putStrLn "== Generating random vectors"
+  xs  <- randomArr (1,10000) :: IO (Vector Float)
+  ys  <- randomArr (1,10000) :: IO (Vector Float)
+
+  putStrLn "== Generating reference solution"
+  ref <- testRef  xs ys
+
+  putStrLn "== Testing CUDA"
+  arr <- testCUDA xs ys
+
+  putStr   "== Validating: "
+  verify ref arr >>= \rv -> putStrLn $ if rv then "Ok!" else "INVALID!"
+
diff --git a/examples/src/vectorAddDrv/vector_add.cu b/examples/src/vectorAddDrv/vector_add.cu
new file mode 100644
--- /dev/null
+++ b/examples/src/vectorAddDrv/vector_add.cu
@@ -0,0 +1,18 @@
+/*
+ * Name      : VectorAdd
+ * Copyright : (c) 2009 Trevor L. McDonell
+ * License   : BSD
+ *
+ * Element-wise addition of two (floating-point) vectors
+ */
+
+
+extern "C"
+__global__ void VecAdd(const float *xs, const float *ys, float *out, const unsigned int N)
+{
+    unsigned int idx = blockDim.x * blockIdx.x + threadIdx.x;
+
+    if (idx < N)
+        out[idx] = xs[idx] + ys[idx];
+}
+
