// rl_deterministic_checksum.cu — provably deterministic sum-of-squares.
//
// Phase 1 of the determinism foundation
// (`docs/superpowers/specs/2026-06-02-determinism-foundation.md` §1.1).
//
// Computes `Σ data[i]^2` over `n` elements with construction-guaranteed
// determinism. Launch geometry is hard-coded (1,1,1)/(1,1,1) — a single
// thread iterates sequentially. The accumulator is double precision; no
// intermediate fp32 round-off varies with launch geometry or block count.
//
// Trade-off vs throughput:
//   * RTX 3050 / L40S sum-of-squares throughput at single-thread is on the
//     order of 10^9 elements/s. The largest tensor in foxhunt's RL train
//     loop is ~24k floats (Q-head grad_w). Cost per launch is therefore
//     dominated by launch overhead (~5-20 μs) — 15 launches/step add
//     ~150-300 μs total, well within the dev-mode budget per §4 of the
//     spec.
//   * The deliberately slow accumulation order is the entire point. Any
//     parallel reduction tree introduces order-of-addition variance that
//     would invalidate the diagnostic.
//
// Per `feedback_no_atomicadd`: zero atomics — pure sequential accumulation.
// Per `feedback_no_stubs`: kernel always executes, no early-exit; n=0
// writes 0.0 (defined behavior).
// Per `feedback_no_nvrtc`: registered in `crates/ml-alpha/build.rs` for
// AOT compilation to per-arch cubin.
//
// Output is f64 (one element). Reading it from the host is the only CPU
// roundtrip; the value itself is computed entirely on device, satisfying
// `feedback_cpu_is_read_only`.

#include <cuda_runtime.h>
#include <stdint.h>

// `n` is passed as int (32-bit) to match the kernel arg-passing
// convention used throughout foxhunt (raw_launch + RawArgs::push_i32).
// All checksum-able tensors in the RL trainer are < 2 billion elements
// (the largest, Q-head weights with HIDDEN_DIM * N_ACTIONS * Q_N_ATOMS,
// is on the order of 1e5 floats), so int is safe.

extern "C" __global__ void rl_deterministic_checksum_f32(
    const float* __restrict__ data,
    int                       n,
    double*      __restrict__ out
) {
    // Single block, single thread — only thread 0 does anything.
    if (threadIdx.x != 0 || blockIdx.x != 0) {
        return;
    }
    double acc = 0.0;
    for (int i = 0; i < n; ++i) {
        const double v = static_cast<double>(data[i]);
        acc += v * v;
    }
    out[0] = acc;
}

// Integer variant for replay indices and other u32/i32 tensors. Same
// accumulation discipline; the input is cast through int64 to avoid
// signed-overflow UB on the sum-of-squares accumulation.
extern "C" __global__ void rl_deterministic_checksum_i32(
    const int*   __restrict__ data,
    int                       n,
    double*      __restrict__ out
) {
    if (threadIdx.x != 0 || blockIdx.x != 0) {
        return;
    }
    double acc = 0.0;
    for (int i = 0; i < n; ++i) {
        // Cast to int64 first to widen — int32^2 fits in int64 exactly;
        // double's 53-bit mantissa may round large sums but the
        // accumulation ORDER is still deterministic, which is the
        // contract.
        const int64_t v = static_cast<int64_t>(data[i]);
        acc += static_cast<double>(v) * static_cast<double>(v);
    }
    out[0] = acc;
}
