/*  Full Mode — C reference kernels.
 *
 *  Framework-free, portable, ~200 lines. Compiles for:
 *    - x86-64 / arm64 Linux, macOS, Windows  (CPython ctypes backend)
 *    - ESP32-S3 (Heltec V4, RadioBox-Nano S3)   via esp-idf / arduino-esp32
 *    - ESP32-C5 gateway                         via esp-idf / arduino-esp32
 *  No libm dependency beyond expf/logf (fdlibm-free compatible with newlib-nano).
 */

#include "fullmode_kernels.h"

#include <math.h>
#include <string.h>

/* ── fp32 dense ─────────────────────────────────────────────────────────── */
void cactus_fullmode_gemm_fp32(
    const float *x, const float *w, const float *b,
    float *y, int N, int K, int M)
{
    for (int n = 0; n < N; ++n) {
        const float *xrow = x + n * K;
        float *yrow = y + n * M;
        /* Seed with bias */
        for (int m = 0; m < M; ++m) yrow[m] = b[m];
        /* Outer product accumulation — cache-friendly over W's rows */
        for (int k = 0; k < K; ++k) {
            const float xv = xrow[k];
            const float *wrow = w + k * M;
            for (int m = 0; m < M; ++m) {
                yrow[m] += xv * wrow[m];
            }
        }
    }
}

/* ── fp32 ReLU ─────────────────────────────────────────────────────────── */
void cactus_fullmode_relu_fp32(float *x, int n)
{
    for (int i = 0; i < n; ++i) {
        if (x[i] < 0.0f) x[i] = 0.0f;
    }
}

/* ── fp32 Softmax ──────────────────────────────────────────────────────── */
void cactus_fullmode_softmax_fp32(
    const float *x, float *y, int N, int M)
{
    for (int n = 0; n < N; ++n) {
        const float *xrow = x + n * M;
        float *yrow = y + n * M;
        float mx = xrow[0];
        for (int m = 1; m < M; ++m) if (xrow[m] > mx) mx = xrow[m];
        float sum = 0.0f;
        for (int m = 0; m < M; ++m) {
            yrow[m] = expf(xrow[m] - mx);
            sum += yrow[m];
        }
        float inv = 1.0f / sum;
        for (int m = 0; m < M; ++m) yrow[m] *= inv;
    }
}

/* ── int8-quantized dense (symmetric per-tensor, zero_point = 0) ──────── */
void cactus_fullmode_gemm_int8(
    const float *x, const int8_t *w, float w_scale, const float *b,
    float *y, int N, int K, int M)
{
    for (int n = 0; n < N; ++n) {
        const float *xrow = x + n * K;
        float *yrow = y + n * M;
        for (int m = 0; m < M; ++m) yrow[m] = b[m];
        for (int k = 0; k < K; ++k) {
            const float xv = xrow[k];
            const int8_t *wrow = w + k * M;
            for (int m = 0; m < M; ++m) {
                yrow[m] += xv * ((float)wrow[m] * w_scale);
            }
        }
    }
}

/* ── Argmax ────────────────────────────────────────────────────────────── */
void cactus_fullmode_argmax(const float *x, int32_t *out, int N, int M)
{
    for (int n = 0; n < N; ++n) {
        const float *xrow = x + n * M;
        int best = 0;
        float bestv = xrow[0];
        for (int m = 1; m < M; ++m) {
            if (xrow[m] > bestv) { bestv = xrow[m]; best = m; }
        }
        out[n] = best;
    }
}
