// crates/ml/src/cuda_pipeline/novelty_simhash_proj_init_kernel.cu
//
// SP11 Fix 39 Task A2 (2026-05-04) — one-shot GPU init for the SimHash
// projection matrix.
//
// Per `feedback_no_cpu_forwards.md` ("CPU is read-only") and
// `feedback_no_cpu_compute_strict.md`, the projection matrix MUST be
// populated by a GPU kernel using the project's deterministic Philox-like
// hash (see `philox_uniform` in `common_device_functions.cuh`). Host-side
// `StdRng` writes through `host_slice_mut()` would violate the rule.
//
// The matrix is 42 × 16 = 672 floats with sign-quantized entries ∈ {-1, +1}.
// Generated once at trainer construct from a u64 seed (config-derived +
// SP11_PROJ_SEED_SALT salt) and never re-initialised; the same seed always
// produces the same projection so reproducible runs share their hash
// function.
//
// `philox_uniform(id, counter, seed)` returns a uniform float in [0, 1)
// derived from a stateless Philox-like hash; we map the low bit of an
// integer derivation to the {-1, +1} sign. Concretely we read
// `philox_uniform(i, 0, seed) * (1 << 24)` as a scaled u32, take the low
// bit, and emit ±1.
//
// Spec: docs/superpowers/specs/2026-05-04-sp11-reward-as-controlled-subsystem.md §3.5.2
// Plan: docs/superpowers/plans/2026-05-04-sp11-reward-as-controlled-subsystem.md A2.2 Step 6

#include <cuda_runtime.h>

extern "C" __global__ void novelty_simhash_proj_init_kernel(
    /* Output: 42 × 16 = 672 floats. */
    float* __restrict__ proj,
    /* Seed (host-passed, derived from config + SP11_PROJ_SEED_SALT). */
    const int seed,
    /* Element count (42 × 16 = 672). Passed explicitly so the kernel binary
     * is independent of state_dim/code_bits choices. */
    const int n_floats)
{
    const int i = blockIdx.x * blockDim.x + threadIdx.x;
    if (i >= n_floats) return;

    /* Derive a deterministic uniform in [0,1) from (i, seed). Map to {-1, +1}
     * via the low bit of the scaled-u32 representation.
     *
     * The Philox-like hash in `common_device_functions.cuh` mixes (id,
     * counter, seed) so different proj matrices (different seeds) get
     * statistically independent ±1 patterns. Same seed always reproduces. */
    const float u    = philox_uniform(i, /*counter=*/0, seed);
    const unsigned int u_scaled = (unsigned int)(u * 16777216.0f); // 2^24 = uniform's bit width
    proj[i] = (u_scaled & 1u) ? 1.0f : -1.0f;
}
