// rl_l2_norm.cu — `‖x‖₂ = sqrt(Σ x²)` over n floats → scalar.
//
// Single-buffer L2 norm reduction. Used by the LR controller's input
// path to compute per-head gradient norms (Q, π, V) for the
// signal-modulated learning-rate target.
//
// Same shape as rl_l2_diff_norm (single block, grid-stride loop, tree
// reduce, no atomics) but operating on one buffer rather than two.
// The trainer allocates one launch per head (Q grad_w 24k floats, π
// grad_w 1k floats, V grad_w 128 floats) and feeds the scalar output
// to ema_update_per_step at the corresponding ISV slot
// (RL_Q_GRAD_NORM_EMA_INDEX = 424, π = 425, V = 426).

extern "C" __global__ void rl_l2_norm(
    const float* __restrict__ x,
    float*       __restrict__ out,
    int                       n
) {
    extern __shared__ float s[];
    const int tid = threadIdx.x;

    float acc = 0.0f;
    for (int i = tid; i < n; i += blockDim.x) {
        const float v = x[i];
        acc += v * v;
    }
    s[tid] = acc;
    __syncthreads();

    for (int stride = blockDim.x / 2; stride > 0; stride >>= 1) {
        if (tid < stride) {
            s[tid] += s[tid + stride];
        }
        __syncthreads();
    }

    if (tid == 0) {
        out[0] = sqrtf(s[0]);
    }
}
