sinatras/pmpp-eval huggingface dataset:
Task: ch13-radix-multiradix-coarsened-fullsort-single
Target file: student_kernel.cu

Here is the current skeleton you must complete:

```cuda
// student_kernel.cu
#include <cuda_runtime.h>
#include <stdint.h>
#include <cstdio>

static inline void CK(cudaError_t e,const char* m){
    if(e!=cudaSuccess){ std::fprintf(stderr,"CUDA %s: %s\n", m, cudaGetErrorString(e)); std::exit(2); }
}

#ifndef RADIX_BITS
#define RADIX_BITS 4
#endif
#ifndef COARSENING_FACTOR
#define COARSENING_FACTOR 8
#endif
#ifndef BLOCK
#define BLOCK 256
#endif

// TODO: Implement a stable 4-bit LSD radix sort with thread coarsening (COARSENING_FACTOR).
// Sort must be in-place on `data` (you may use a temp buffer internally).

// Choose your radix size and coarsening factor
#define RADIX_SIZE (1 << RADIX_BITS)  // 2^RADIX_BITS buckets
#define RADIX_MASK (RADIX_SIZE - 1)

extern "C" __global__
void radix_sort_coarsened_kernel(unsigned int* __restrict__ data,
                                unsigned int* __restrict__ temp,
                                int n,
                                int shift)
{
    // TODO: Implement coarsened multi-radix sort pass
    // 1. Each thread loads COARSENING_FACTOR elements
    // 2. Count elements for each bucket using coarsened loading
    // 3. Compute prefix sums to find output positions for each bucket
    // 4. Scatter elements to correct positions based on radix value
    // 5. Ensure stable sorting and efficient memory access patterns
}

extern "C"
void radix_sort_coarsened_host(unsigned int* data, int n)
{
    if(n <= 1) return;
    // Placeholder: shallow copy (intentionally insufficient so tests fail until implemented)
    unsigned int* tmp=nullptr;
    CK(cudaMalloc(&tmp, n*sizeof(unsigned int)), "malloc tmp");
    CK(cudaMemcpy(tmp, data, n*sizeof(unsigned int), cudaMemcpyDeviceToDevice), "copy to tmp");
    CK(cudaMemcpy(data, tmp, n*sizeof(unsigned int), cudaMemcpyDeviceToDevice), "copy back");
    cudaFree(tmp);
}
```
Task: ch13-radix-multiradix-fullsort-single
Target file: student_kernel.cu

Here is the current skeleton you must complete:

```cuda
// student_kernel.cu
#include <cuda_runtime.h>
#include <stdint.h>
#include <cstdio>

static inline void CK(cudaError_t e, const char* m){
    if(e != cudaSuccess){
        std::fprintf(stderr, "CUDA %s: %s\n", m, cudaGetErrorString(e));
        std::exit(2);
    }
}

// TODO: Implement a multiradix (RADIX_BITS=4) stable radix sort in-place.
// Contract:
//   extern "C" void radix_sort_multiradix_host(unsigned int* data, int n);
// Requirements:
//   - Sort ascending, stable per pass (4-bit buckets, 8 passes total).
//   - Arbitrary n (including 0 / non-multiples of block size).
//   - No OOB writes (tests use guarded buffers).
//   - In-place on `data` (you may use an internal device temp buffer).

// Choose your radix size (recommended: 2-bit or 4-bit)
#define RADIX_BITS 2
#define RADIX_SIZE (1 << RADIX_BITS)  // 2^RADIX_BITS buckets
#define RADIX_MASK (RADIX_SIZE - 1)

extern "C" __global__
void radix_sort_multiradix_kernel(unsigned int* __restrict__ data,
                                 unsigned int* __restrict__ temp,
                                 int n,
                                 int shift)
{
    // TODO: Implement multi-radix sort pass
    // 1. Count elements for each bucket (RADIX_SIZE buckets) using shared memory
    // 2. Compute prefix sums to find output positions for each bucket
    // 3. Scatter elements to correct positions based on radix value
    // 4. Ensure stable sorting (preserve relative order for equal keys)
}

extern "C"
void radix_sort_multiradix_host(unsigned int* data, int n)
{
    if (n <= 1) return;
    // Starter behavior: shallow copy  will fail non-trivial tests.
    unsigned int* tmp = nullptr;
    CK(cudaMalloc(&tmp, n*sizeof(unsigned int)), "malloc tmp");
    CK(cudaMemcpy(tmp, data, n*sizeof(unsigned int), cudaMemcpyDeviceToDevice), "copy tmp");
    CK(cudaMemcpy(data, tmp, n*sizeof(unsigned int), cudaMemcpyDeviceToDevice), "copy back");
    cudaFree(tmp);
}
```
Task: ch13-radix-naive-1bit-fullsort-single
Target file: student_kernel.cu

Here is the current skeleton you must complete:

```cuda
// student_kernel.cu
// TODO: Implement a naive 1-bit parallel radix sort that fully sorts 32-bit keys
// across 32 passes (LSB -> MSB), stable per pass.
//
// Requirements:
//  - API: extern "C" void radix_sort_1bit_host(unsigned int* data, int n)
//  - In-place: modify data in-place
//  - Stability: within each bit-partition, preserve relative order (use zerosBefore(i) / onesBefore(i))
//  - Multi-pass orchestration: 32 passes, swap ping-pong buffers every pass
//  - Correct for arbitrary n (n can be 0)
//  - No OOB writes: only write within [0..n)
//
// Hints (not mandatory, but aligned with the reference):
//  - kFlagZeros: flagsZero[i] = 1 if ((x >> bit)&1)==0 else 0
//  - kBlockExclusiveScan + host scan over block sums for robustness
//  - kAddBlockOffsets to turn per-block exclusive scans into global exclusive scan
//  - kScatter uses stable positions:
//        if bit==0: pos = zerosBefore(i)
//        else      : pos = totalZeros + (i - zerosBefore(i))

#include <cuda_runtime.h>

// TODO: Implement naive 1-bit radix sort using LSD approach.
// Contract summary:
//  - Stable: equal elements maintain relative order
//  - Process 1 bit per pass, 32 passes total for uint32_t
//  - Use parallel counting, prefix sum, and scattering
//  - Sort in ascending order

extern "C" __global__
void radix_sort_1bit_kernel(unsigned int* __restrict__ data,
                           unsigned int* __restrict__ temp,
                           int n,
                           int bit)
{
    // TODO: Implement single-bit radix sort pass
    // 1. Count elements with bit=0 and bit=1 using shared memory
    // 2. Compute prefix sums to find output positions
    // 3. Scatter elements to correct positions based on bit value
    // 4. Ensure stable sorting (preserve relative order for equal keys)
}

extern "C"
void radix_sort_1bit_host(unsigned int* data, int n)
{
    // TODO: Implement host function that orchestrates 32 sorting passes
    // 1. Allocate temporary buffer
    // 2. For each bit position (0 to 31):
    //    - Launch radix_sort_1bit_kernel
    //    - Swap data and temp pointers
    // 3. Ensure final result is in original data array
    // 4. Clean up temporary buffer
}
```
Task: ch13-radix-onepass-multiradix-single
Target file: student_kernel.cu

Here is the current skeleton you must complete:

```cuda
// ch13-radix-onepass-multiradix-single / student_kernel.cu
#include <cuda_runtime.h>
#include <stdint.h>

// CONTRACT:
// Implement one *stable* multiradix pass over keys.
// - keys_d:  input keys (length n)
// - out_d:   output keys (length n)
// - n:       number of elements
// - r:       bits per pass (1, 2, or 4)
// - shift:   bit shift for the digit (e.g., 0, r, 2r, ...)
// Approach expected (typical):
//   1) extract digits (0..(2^r - 1))
//   2) per-block histogram -> global array [grid x buckets]
//   3) host exclusive scan to get global bases & per-block bucket bases
//   4) stable scatter into out_d using digit, globalBase[b], blockBase[block,b], and local offset within block
// NOTE: Stability means equal digits preserve the original order.

extern "C" void radix_onepass_multiradix(
    const uint32_t* keys_d, uint32_t* out_d,
    int n, int r, int shift);

// TODO: provide your implementation
extern "C" void radix_onepass_multiradix(
    const uint32_t* keys_d, uint32_t* out_d,
    int n, int r, int shift)
{
    // Implement kernels + host prefix here.
    // You may choose blockDim=256 and compute grid from n.
    // (Any correct stable implementation passes.)
    (void)keys_d; (void)out_d; (void)n; (void)r; (void)shift;
}
```
Task: ch14-spmv-csr-thread-per-row-single
            Target file: student_kernel.cu

            Implementation requirements:
- Assign one thread per row using a grid-stride loop.
- Accumulate vals[j] * x[colIdx[j]] for j in [rowPtr[row], rowPtr[row+1]) and write y[row].
- Avoid atomics and guard rows >= m before reading rowPtr[row+1].

            Here is the current student_kernel.cu:

            ```cuda
            // ch14-spmv-csr-thread-per-row-single / student_kernel.cu
#include <cuda_runtime.h>

extern "C" __global__
void spmv_csr_kernel(const int* __restrict__ rowPtr,
                     const int* __restrict__ colIdx,
                     const float* __restrict__ vals,
                     const float* __restrict__ x,
                     float* __restrict__ y,
                     int m)
{
    // TODO: Assign one thread per row, iterate over the row's nonzeros, and
    // write y[row] without using atomics.

    int row = blockIdx.x * blockDim.x + threadIdx.x;
    if (row < m) {
        int start = rowPtr[row];
        int end   = rowPtr[row + 1];
        float scratch = 0.0f;
        for (int j = start; j < end; ++j) {
            (void)colIdx[j];
            (void)vals[j];
        }
        if (y) {
            y[row] = scratch; // placeholder leaves output unchanged (all zeros)
        }
    }
}
            ```
Task: ch14-spmv-coo-single
            Target file: student_kernel.cu

            Implementation requirements:
- Launch a grid-stride loop over nnz entries.
- For each entry, accumulate with atomicAdd(&y[row], vals[k] * x[col]) after bounds checks.
- Do not modify the input arrays.

            Here is the current student_kernel.cu:

            ```cuda
            // ch14-spmv-coo-single / student_kernel.cu
#include <cuda_runtime.h>

extern "C" __global__
void spmv_coo_kernel(const int* __restrict__ row_idx,
                     const int* __restrict__ col_idx,
                     const float* __restrict__ vals,
                     const float* __restrict__ x,
                     float* __restrict__ y,
                     int nnz)
{
    // TODO: Implement COO-format SpMV using a grid-stride loop and atomicAdd on
    // y[row] to handle duplicate entries.

    int k = blockIdx.x * blockDim.x + threadIdx.x;
    if (k < nnz) {
        int r = row_idx[k];
        int c = col_idx[k];
        (void)r;
        (void)c;
        (void)vals[k];
        (void)x;
        (void)y;
    }
}
            ```
Task: ch14-spmv-ell-single
            Target file: student_kernel.cu

            Implementation requirements:
- Assign one thread per row using a grid-stride loop.
- Skip padded slots with colIdx < 0 and accumulate remaining entries into y[row].
- Initialize each row result before accumulation.

            Here is the current student_kernel.cu:

            ```cuda
            // ch14-spmv-ell-single / student_kernel.cu
#include <cuda_runtime.h>

extern "C" __global__
void spmv_ell_kernel(const int* __restrict__ colIdx,
                     const float* __restrict__ vals,
                     const float* __restrict__ x,
                     float* __restrict__ y,
                     int m, int K)
{
    // TODO: Assign one thread per row, iterate across the K padded entries,
    // skip negative column indices, and accumulate into y[row].
    //
    // DATA LAYOUT: Row-major ELL storage
    // For row i, the K entries are stored at indices [i*K, i*K+1, ..., i*K+K-1]
    // Example:
    //   int base = row * K;
    //   for (int t = 0; t < K; ++t) {
    //       int col = colIdx[base + t];
    //       if (col >= 0) {
    //           // accumulate vals[base + t] * x[col]
    //       }
    //   }

    int row = blockIdx.x * blockDim.x + threadIdx.x;
    if (row < m) {
        (void)colIdx;
        (void)vals;
        (void)x;
        if (y) {
            y[row] = 0.0f;
        }
    }

    (void)K;
}
            ```
Task: ch14-spmv-jds-single
            Target file: student_kernel.cu

            Implementation requirements:
- Implement spmv_jds_kernel to walk jagged diagonals, checking if the current row participates before accumulating.
- Write results back to original row order using the permutation array.
- Implement spmv_jds host wrapper to configure and launch the kernel.

            Here is the current student_kernel.cu:

            ```cuda
            // ch14-spmv-jds-single / student_kernel.cu
#include <cuda_runtime.h>

extern "C" __global__
void spmv_jds_kernel(const int* __restrict__ colJds,
                     const float* __restrict__ valJds,
                     const int* __restrict__ permute,
                     const int* __restrict__ jdPtr,
                     const float* __restrict__ x,
                     float* __restrict__ y,
                     int m, int maxJ)
{
    // TODO: Traverse the jagged diagonals, accumulate per permuted row, and
    // write results back to the original ordering via permute[]
    if (colJds && valJds && permute && jdPtr && x && y) {
        (void)m;
        (void)maxJ;
    }
}

extern "C" void spmv_jds(const int* colJds, const float* valJds,
                         const int* permute, const int* jdPtr,
                         const float* x, float* y, int m, int maxJ)
{
    (void)colJds; (void)valJds; (void)permute; (void)jdPtr;
    (void)x; (void)y; (void)m; (void)maxJ;

    // TODO: Configure launch dimensions and invoke spmv_jds_kernel.
}
            ```
Task: ch14-spmv-hyb-single
            Target file: student_kernel.cu

            Implementation requirements:
- Implement spmv_ell_rows_kernel to compute ELL rows without atomics.
- Implement spmv_coo_accum_kernel as a grid-stride loop that atomicAdds COO overflow contributions.
- In spmv_hyb, launch the ELL kernel, synchronize, then launch the COO accumulation.

            Here is the current student_kernel.cu:

            ```cuda
            // ch14-spmv-hyb-single / student_kernel.cu
#include <cuda_runtime.h>

extern "C" __global__
void spmv_ell_rows_kernel(const int* __restrict__ colEll,
                          const float* __restrict__ valEll,
                          const float* __restrict__ x,
                          float* __restrict__ y,
                          int m, int K)
{
    // TODO: Compute the ELL portion of the HYB product row-by-row.
    //
    // DATA LAYOUT: Row-major ELL storage
    // For row i, the K entries are stored at indices [i*K, i*K+1, ..., i*K+K-1]
    // Example:
    //   int row = blockIdx.x * blockDim.x + threadIdx.x;
    //   if (row < m) {
    //       float sum = 0.0f;
    //       int base = row * K;
    //       for (int t = 0; t < K; ++t) {
    //           int col = colEll[base + t];
    //           if (col >= 0) sum += valEll[base + t] * x[col];
    //       }
    //       y[row] = sum;
    //   }
    if (colEll && valEll && x && y) {
        (void)m;
        (void)K;
    }
}

extern "C" __global__
void spmv_coo_accum_kernel(const int* __restrict__ rowCoo,
                           const int* __restrict__ colCoo,
                           const float* __restrict__ valCoo,
                           const float* __restrict__ x,
                           float* __restrict__ y,
                           int nnzC)
{
    // TODO: Accumulate the COO tail using atomicAdd.
    if (rowCoo && colCoo && valCoo && x && y) {
        (void)nnzC;
    }
}

extern "C" void spmv_hyb(const int* colEll, const float* valEll, int m, int K,
                         const int* rowCoo, const int* colCoo, const float* valCoo, int nnzC,
                         const float* x, float* y)
{
    (void)colEll; (void)valEll; (void)m; (void)K;
    (void)rowCoo; (void)colCoo; (void)valCoo; (void)nnzC;
    (void)x; (void)y;

    // TODO: Launch spmv_ell_rows_kernel followed by spmv_coo_accum_kernel to
    // form the full HYB SpMV result.
}
            ```
Task: ch14-coo-to-csr-single
            Target file: student_kernel.cu

            Implementation requirements:
- Build rowCounts on the device via a grid-stride histogram of row indices.
- Perform an exclusive scan of rowCounts to obtain rowPtr and ensure rowPtr[m] == nnz.
- Use a per-row cursor (rowNext) to scatter COO entries into CSR while preserving input order.
- Handle empty rows and invalid rows defensively; do not combine duplicate entries.
- Keep the input COO arrays unchanged and avoid out-of-bounds writes.

            Here is the current student_kernel.cu:

            ```cuda
            // ch14-coo-to-csr-single / student_kernel.cu
#include <cuda_runtime.h>
#include <cstdio>

// Convert an unsorted COO triple (row/col/val) into CSR form.  The reference
// shows one correct solution; this stub leaves the heavy lifting to the model.

extern "C" __global__
void k_hist_rows(const int* __restrict__ row, int nnz, int m, int* __restrict__ rowCounts)
{
    // TODO: Grid-stride histogram of row indices into rowCounts without
    // accessing out-of-range rows.

    if (blockIdx.x == 0 && threadIdx.x == 0 && nnz > 0 && m > 0) {
        atomicAdd(&rowCounts[0], 0);
    }
}

extern "C" __global__
void k_stable_scatter_single(const int* __restrict__ row,
                             const int* __restrict__ col,
                             const float* __restrict__ val,
                             int nnz, int m,
                             int* __restrict__ rowNext,
                             int* __restrict__ colCSR,
                             float* __restrict__ valCSR)
{
    // TODO: Walk COO entries in order, writing to CSR using per-row cursors.
    if (blockIdx.x == 0 && threadIdx.x == 0 && nnz > 0 && m > 0) {
        rowNext[0] += 0;
        colCSR[0] += 0;
        valCSR[0] += 0.0f;
    }
}

extern "C" void coo_to_csr(const int* d_row, const int* d_col, const float* d_val,
                           int nnz, int m, int /*n*/,
                           int* d_rowPtr, int* d_colCSR, float* d_valCSR)
{
    // TODO: Allocate row counts, launch k_hist_rows, perform an exclusive scan
    // on the host (or device), then launch k_stable_scatter_single to emit the
    // CSR structure.  Ensure rowPtr[m] == nnz and input ordering within each
    // row is preserved.

    if (m <= 0) {
        int zero = 0;
        cudaMemcpy(d_rowPtr, &zero, sizeof(int), cudaMemcpyHostToDevice);
        return;
    }

    // Placeholder touch so the stub compiles when invoked by tests.
    cudaMemset(d_colCSR, 0, nnz * sizeof(int));
    cudaMemset(d_valCSR, 0, nnz * sizeof(float));
}
            ```
Task: ch15-bfs-push-single
            Target file: student_kernel.cu

            Implementation requirements:
- Initialize levels to INF_LVL, set level[src] = 0, and seed the frontier array.
- In each iteration, run a push kernel that traverses neighbors with atomicCAS discovery and atomicAdd enqueue.
- Swap frontier buffers between iterations until the frontier is empty, then free temporary buffers.

            Here is the current student_kernel.cu:

            ```cuda
            // ch15-bfs-push-single / student_kernel.cu
#include <cuda_runtime.h>
#include <limits.h>

#ifndef INF_LVL
#define INF_LVL 0x3f3f3f3f
#endif

__global__ void _init_levels(int* __restrict__ level, int V, int src){
    int i = blockIdx.x * blockDim.x + threadIdx.x;
    if (i < V) {
        level[i] = INF_LVL;
    }
    if (src >= 0 && src < V && i == src) {
        level[i] = 0;
    }
}

__global__ void bfs_push_kernel(const int* __restrict__ row_ptr,
                                const int* __restrict__ col_idx,
                                const int* __restrict__ frontier,
                                int frontier_size,
                                int* __restrict__ next_frontier,
                                int* __restrict__ next_frontier_size,
                                int* __restrict__ level,
                                int cur_level)
{
    // TODO: Visit outgoing edges of the frontier, atomically claim newly
    // discovered vertices, and append them to next_frontier.
    if (row_ptr && col_idx && frontier && next_frontier && next_frontier_size && level) {
        (void)frontier_size;
        (void)cur_level;
    }
}

extern "C" void bfs_push_gpu(const int* d_row_ptr,
                             const int* d_col_idx,
                             int V, int E,
                             int src,
                             int* d_level)
{
    (void)d_row_ptr;
    (void)d_col_idx;
    (void)E;

    if (V <= 0) {
        return;
    }

    dim3 b(256), g((V + b.x - 1) / b.x);
    _init_levels<<<g, b>>>(d_level, V, src);
    // TODO: Allocate frontier buffers, iterate until the frontier is empty, and
    // free any resources allocated on the device.
}
            ```
Task: ch15-bfs-pull-single
            Target file: student_kernel.cu

            Implementation requirements:
- Initialize levels to INF_LVL, set level[src] = 0, and seed the in_frontier bitmap.
- Each iteration clears out_frontier, runs a pull kernel that scans neighbors of undiscovered vertices, and counts discoveries.
- Swap bitmaps when progress occurs and terminate when no new vertices are found.
- Release all temporary device allocations at exit.

            Here is the current student_kernel.cu:

            ```cuda
            // ch15-bfs-pull-single / student_kernel.cu
#include <cuda_runtime.h>
#include <limits.h>

#ifndef INF_LVL
#define INF_LVL 0x3f3f3f3f
#endif

__global__ void _init_levels_pull(int* __restrict__ level, int V, int src){
    // TODO: Initialize the level array and set the source distance to zero.
    int i = blockIdx.x * blockDim.x + threadIdx.x;
    if (i < V) {
        level[i] = INF_LVL;
    }
    if (src >= 0 && src < V && i == src) {
        level[i] = 0;
    }
}

__global__ void _clear_bitmap(unsigned char* __restrict__ bm, int V){
    int i = blockIdx.x * blockDim.x + threadIdx.x;
    if (i < V) {
        bm[i] = 0;
    }
}

__global__ void _set_single(unsigned char* __restrict__ bm, int idx){
    if (threadIdx.x == 0 && blockIdx.x == 0 && idx >= 0) {
        bm[idx] = 1;
    }
}

__global__ void bfs_pull_kernel(const int* __restrict__ row_ptr,
                                const int* __restrict__ col_idx,
                                const unsigned char* __restrict__ in_frontier,
                                unsigned char* __restrict__ out_frontier,
                                int* __restrict__ level,
                                int cur_level,
                                int V,
                                int* __restrict__ next_count)
{
    // TODO: For undiscovered vertices, scan neighbors to detect if any reside
    // in the current frontier bitmap.
    int v = blockIdx.x * blockDim.x + threadIdx.x;
    if (v < V) {
        (void)row_ptr;
        (void)col_idx;
        (void)in_frontier;
        (void)out_frontier;
        (void)level;
        (void)cur_level;
        (void)next_count;
    }
}

extern "C" void bfs_pull_gpu(const int* d_row_ptr,
                             const int* d_col_idx,
                             int V, int E,
                             int src,
                             int* d_level)
{
    (void)d_row_ptr;
    (void)d_col_idx;
    (void)E;

    if (V <= 0) {
        return;
    }

    dim3 b(256), g((V + b.x - 1) / b.x);
    _init_levels_pull<<<g, b>>>(d_level, V, src);
    // TODO: Allocate bitmaps, iterate levels until no new vertices are found.
}
            ```