// rl_step_counter_update.cu — per-batch trade-duration counter +
// done-gated emit, for the rl_gamma controller's
// `mean_trade_duration_ema` input.
//
// The rl_gamma controller adapts γ so
// `γ^(mean_trade_duration) ≈ 0.5` — half the value remains by the
// time the average trade closes. The input EMA must be in event-count
// units (number of steps a position was open). This kernel maintains
// a per-batch counter that increments on every step and resets on
// done; the value AT the done step is the trade's duration.
//
// Per-thread state machine (one thread per batch index):
//   if dones[b] > 0.5:        // trade closed this step
//       duration[b] = counter[b]   // emit count for EMA consumer
//       counter[b]  = 0            // reset for next trade
//   else:                          // trade still open (or no position)
//       duration[b] = 0.0          // zero is the gate input — ema_update_on_done skips it
//       counter[b] += 1
//
// The duration_out buffer is then passed to `ema_update_on_done`
// gated on dones_d: gate skips zeros so only real trade-close
// durations contribute to the EMA.
//
// Element-wise, trivially parallel. No atomics.

extern "C" __global__ void rl_step_counter_update(
    const float* __restrict__ dones,        // [b_size]
    int*         __restrict__ counter,      // [b_size] state (mutated)
    float*       __restrict__ duration_out, // [b_size] OUT
    int                       b_size
) {
    const int b = blockIdx.x * blockDim.x + threadIdx.x;
    if (b >= b_size) return;

    const int cur = counter[b];
    if (dones[b] > 0.5f) {
        // Trade close: emit count, reset counter.
        duration_out[b] = (float)cur;
        counter[b]      = 0;
    } else {
        // Trade still open (or no position): no emit, increment.
        duration_out[b] = 0.0f;
        counter[b]      = cur + 1;
    }
}
