cudaDotProductF :: (Vector Float, Vector Float) -- ^ vectors we're running dot product on
                -> CIO (Scalar Float)           -- ^ result of the dot product
cudaDotProductF (v1, v2) = do
    let n = arraySize (arrayShape v1)

    -- allocate result scalar
    o <- allocScalar

    -- get device pointers on the GPU memory 
    -- for the two vectors and the result
    -- see Data.Array.Accelerate.BLAS.Common
    v1ptr  <- devVF v1
    v2ptr  <- devVF v2
    outPtr <- devSF o

    -- TODO: avoid doing the init/setPointerMode/cleanup on every call
    --       see the unsafePerformIO trick in accelerate-fft
    liftIO $ BL.withCublas $ \handle -> execute handle n v1ptr v2ptr outPtr

    -- return the output array, that now contains the result
    return o

    where
      execute h n v1ptr v2ptr optr
        = BL.sdot h n v1ptr 1 v2ptr 1 optr
cudaDotProductF :: (Vector Float, Vector Float) -- ^ vectors we're running dot product on
                -> CIO (Scalar Float)
cudaDotProductF (v1, v2) = do
    let n = arraySize (arrayShape v1)

    -- allocate result scalar
    o <- allocScalar

    -- get device pointers on the GPU memory 
    -- for the two vectors and the result
    -- see Data.Array.Accelerate.BLAS.Common
    v1ptr  <- devVF v1
    v2ptr  <- devVF v2
    outPtr <- devSF o

    -- TODO: avoid doing the init/setPointerMode/cleanup on every call
    --       see the unsafePerformIO trick in accelerate-fft
    liftIO $ BL.withCublas $ \handle -> execute handle n v1ptr v2ptr outPtr

    -- return the output array, that now contains the result
    return o

    where
      execute h n v1ptr v2ptr optr
        = BL.sdot h n v1ptr 1 v2ptr 1 optr
cudaDotProductF (v1, v2) = do
    let n = arraySize (arrayShape v1)

    -- allocate result scalar
    o <- allocScalar

    -- get device pointers on the GPU memory 
    -- for the two vectors and the result
    -- see Data.Array.Accelerate.BLAS.Common
    v1ptr  <- devVF v1
    v2ptr  <- devVF v2
    outPtr <- devSF o

    -- TODO: avoid doing the init/setPointerMode/cleanup on every call
    --       see the unsafePerformIO trick in accelerate-fft
    liftIO $ BL.withCublas $ \handle -> execute handle n v1ptr v2ptr outPtr

    -- return the output array, that now contains the result
    return o

    where
      execute h n v1ptr v2ptr optr
        = BL.sdot h n v1ptr 1 v2ptr 1 optr