#version 450

#include "types.glsl"

// Fused snake activation: y = x + sin(b * x)^2 * c
//   data_a [ne0, ne1]   per element activation x    (A_TYPE)
//   data_b [1,   ne1]   per channel multiplier      (float)
//   data_c [1,   ne1]   per channel inverse scale   (float, precomputed as 1 / freq)
//   data_d [ne0, ne1]   output y                    (D_TYPE)
layout (binding = 0) readonly  buffer A {A_TYPE data_a[];};
layout (binding = 1) readonly  buffer B {float  data_b[];};
layout (binding = 2) readonly  buffer C {float  data_c[];};
layout (binding = 3) writeonly buffer D {D_TYPE data_d[];};

layout(local_size_x = 256, local_size_y = 1, local_size_z = 1) in;

layout (push_constant) uniform parameter {
    uint32_t ne0;
    uint32_t ne1;
} p;

// Load A_TYPE to float
float load_val(uint32_t idx) {
#if defined(DATA_A_BF16)
    return bf16_to_fp32(uint32_t(data_a[idx]));
#else
    return float(data_a[idx]);
#endif
}

// Store float as D_TYPE
void store_val(uint32_t idx, float v) {
#if defined(DATA_D_BF16)
    data_d[idx] = D_TYPE(fp32_to_bf16(v));
#else
    data_d[idx] = D_TYPE(v);
#endif
}

void main() {
    const uint32_t i0 = gl_GlobalInvocationID.x;
    const uint32_t i1 = gl_GlobalInvocationID.y;
    if (i0 >= p.ne0 || i1 >= p.ne1) return;

    const uint32_t idx = i0 + i1 * p.ne0;
    const float xi = load_val(idx);
    const float s  = sin(data_b[i1] * xi);
    store_val(idx, xi + s * s * data_c[i1]);
}
