/* update_adaptive_clip — GPU-only winsorized adaptive grad-clip update.
 *
 * SP4 Layer C close-out C4 (2026-05-01). Replaces the host-side scalar-
 * reduction + EMA arithmetic chain in `GpuDqnTrainer::update_adaptive_clip`
 * (gpu_dqn_trainer.rs ~line 22660-22744) per `feedback_no_cpu_compute_strict`.
 * Host code previously ran:
 *   1. winsor: `clamped = min(observed, K × max(prev_clip, 1e-3))`
 *   2. cold-start sentinel: `if ema ≤ 0.0 { ema = clamped }`
 *   3. EMA: `ema = β × ema + (1 - β) × clamped`
 *   4. scalar reduction: `new_clip = max(ema × CLIP_MULTIPLIER, MIN_CLIP)`
 *   5. ISV upper bound: `new_clip = min(new_clip, max(ISV[GRAD_CLIP_BOUND_INDEX], EPS_CLAMP_FLOOR))`
 *   6. mapped-pinned write: `adaptive_clip_pinned[0] = new_clip`
 * — every step of which is compute on GPU-produced inputs (raw_grad_norm,
 * prev_clip, ISV slot are all mapped-pinned scalars). The whole chain now
 * runs in a single-thread kernel.
 *
 * Reads:  observed_grad_norm     — host-passed scalar (the caller has
 *                                  already verified `is_finite() && > 0.0`
 *                                  per the host-side early-return guard).
 *         clip_dev[0]            — previous adaptive_clip (mapped-pinned).
 *         ema_dev[0]             — previous winsorized grad-norm EMA
 *                                  (mapped-pinned scalar).
 *         isv[grad_clip_bound_idx] — ISV[GRAD_CLIP_BOUND_INDEX=168] upper
 *                                  bound from `launch_sp4_grad_norm_p99`
 *                                  (Pearl D-smoothed p99 of grad_norm).
 *         outlier_diag_dev[0]    — diagnostic flag (mapped-pinned scalar):
 *                                  set to `observed - clamped` if winsor
 *                                  triggered, else 0.0. Host reads it
 *                                  post-launch to drive the GRAD_CLIP_OUTLIER
 *                                  warn log without cross-stream sync.
 * Writes: ema_dev[0]             — updated EMA.
 *         clip_dev[0]            — updated adaptive_clip (final value).
 *         outlier_diag_dev[0]    — winsor diagnostic delta.
 *
 * Cold-start sentinel: if `ema_dev[0] <= 0.0`, treat as bootstrap (cold-
 * start for this run): assign `clamped` directly. Otherwise apply the
 * fixed-α EMA recurrence (β=0.95, mirroring the deleted host constant).
 *
 * Per `feedback_no_cpu_compute_strict.md`: the entire chain is compute on
 * mapped-pinned-resident inputs — belongs on GPU. Per `feedback_no_partial_refactor`:
 * we migrate the COMPLETE chain (winsor + EMA + 2 clamps + ISV bound) in
 * one kernel rather than a partial split that would leave host scalar
 * reductions interleaved with the EMA arithmetic.
 *
 * Per `feedback_no_atomicadd.md`: a single thread reads four scalars and
 * writes three — no atomics needed.
 *
 * The `__threadfence_system()` ensures the writes are PCIe-visible to the
 * mapped-pinned host_ptr accessors (`grad_norm_ema_value()` HEALTH_DIAG +
 * the host-side outlier-diagnostic warn check after this launch returns)
 * and to other-stream kernel reads via dev_ptr (`adaptive_clip_pinned` is
 * read by the Adam clip kernel during the next training step's replay).
 */

extern "C" __global__ void update_adaptive_clip_kernel(
    float        observed_grad_norm,                /* host-passed scalar */
    float        ema_beta,                          /* fixed α=0.95 (legacy const) */
    float        clip_multiplier,                   /* fixed 2.0 (legacy const) */
    float        min_clip,                          /* fixed 1.0 (legacy const) */
    float        outlier_k,                         /* fixed 100.0 (legacy const) */
    float        eps_clamp_floor,                   /* SP4 EPS_CLAMP_FLOOR */
    float*       __restrict__ clip_dev,             /* [1] mapped-pinned adaptive_clip */
    float*       __restrict__ ema_dev,              /* [1] mapped-pinned grad_norm_ema */
    const float* __restrict__ isv,                  /* ISV bus */
    int          grad_clip_bound_idx,               /* GRAD_CLIP_BOUND_INDEX = 168 */
    float*       __restrict__ outlier_diag_dev      /* [1] winsor-delta diagnostic */
) {
    if (blockIdx.x != 0 || threadIdx.x != 0) return;

    /* 1. Winsor: clamp observation against K × prev_clip_safe. The
     *    `prev_clip_safe = max(prev_clip, 1e-3)` ε floor guards against
     *    cold-start where prev_clip might be 0; matches deleted host code. */
    const float prev_clip      = clip_dev[0];
    const float prev_clip_safe = fmaxf(prev_clip, 1e-3f);
    const float upper_winsor   = outlier_k * prev_clip_safe;
    const float clamped        = fminf(observed_grad_norm, upper_winsor);

    /* 2. Outlier diagnostic — host reads this slot post-launch to decide
     *    whether to emit the GRAD_CLIP_OUTLIER warn log. Encoded as the
     *    delta `observed - clamped` so the host can recover the original
     *    observed value (`clamped + delta`) for the log message. */
    outlier_diag_dev[0] = observed_grad_norm - clamped;

    /* 3. Cold-start sentinel + EMA. */
    const float prev_ema = ema_dev[0];
    float new_ema;
    if (prev_ema <= 0.0f) {
        new_ema = clamped;
    } else {
        new_ema = ema_beta * prev_ema + (1.0f - ema_beta) * clamped;
    }
    ema_dev[0] = new_ema;

    /* 4. Scalar reductions: clip = max(EMA × multiplier, MIN_CLIP),
     *    bounded by ISV[GRAD_CLIP_BOUND_INDEX].max(EPS_CLAMP_FLOOR). */
    const float new_clip_raw = fmaxf(new_ema * clip_multiplier, min_clip);
    const float upper_bound  = fmaxf(isv[grad_clip_bound_idx], eps_clamp_floor);
    const float new_clip     = fminf(new_clip_raw, upper_bound);

    clip_dev[0] = new_clip;

    __threadfence_system();  /* PCIe-visible to mapped-pinned host_ptr +
                              * other-stream kernel reads via dev_ptr. */
}
