/* AVM ISA v1 — C Port (Strict Functional Execution) */

#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

/* ── Constants ─────────────────────────────────────────────── */
#define AVM_CLAMP_MIN (-2147483647)
#define AVM_CLAMP_MAX 2147483647
#define AVM_Q0_MIN (-32767)
#define AVM_Q0_MAX 32767
#define Q16_SCALE 65536
#define AVM_MAX_STACK 1024
#define AVM_MAX_LOCALS 256

/* ── Clamp ─────────────────────────────────────────────────── */
static inline int32_t avm_clamp(int64_t x) {
    if (x > AVM_CLAMP_MAX) return AVM_CLAMP_MAX;
    if (x < AVM_CLAMP_MIN) return AVM_CLAMP_MIN;
    return (int32_t)x;
}

static inline int32_t avm_q0_clamp(int64_t x) {
    if (x > AVM_Q0_MAX) return AVM_Q0_MAX;
    if (x < AVM_Q0_MIN) return AVM_Q0_MIN;
    return (int32_t)x;
}

/* Floor division matching Lean Int.ediv (rounds toward -inf) */
static inline int32_t floor_div(int64_t a, int64_t b) {
    if (b == 0) return 0;
    int64_t q = a / b;
    int64_t r = a % b;
    if (r != 0 && ((a ^ b) < 0)) q--;
    return (int32_t)q;
}

static inline bool lt_q16_v6(int32_t a, int32_t b) {
    bool sa = a < 0, sb = b < 0;
    return (sa != sb) ? sa : (a < b);
}

/* ── Types ─────────────────────────────────────────────────── */
typedef enum { TY_Q0, TY_Q16, TY_BOOL } AvmTy;

typedef struct {
    AvmTy ty;
    union { int32_t i; bool b; } val;
} AnyVal;

/* ── Instructions ──────────────────────────────────────────── */
typedef enum {
    OP_PUSH_Q16, OP_PUSH_BOOL, OP_PUSH_Q0,
    OP_POP, OP_DUP, OP_SWAP, OP_LOAD, OP_STORE,
    OP_JUMP, OP_JUMP_IF, OP_PRIM, OP_HALT
} OpCode;

typedef enum {
    PRIM_ADD_Q0, PRIM_SUB_Q0, PRIM_ADD_Q16, PRIM_SUB_Q16,
    PRIM_MUL_Q16, PRIM_DIV_Q16, PRIM_LT_Q16, PRIM_EQ_Q16,
    PRIM_AND, PRIM_OR, PRIM_NOT
} PrimCode;

typedef struct { OpCode op; int32_t arg; bool arg2; } Instr;

/* ── State ─────────────────────────────────────────────────── */
typedef struct {
    int pc;
    AnyVal stack[AVM_MAX_STACK];
    int sp;
    AnyVal locals[AVM_MAX_LOCALS];
    bool local_set[AVM_MAX_LOCALS];
    bool halted;
} State;

void init_state(State *s, int n_locals) {
    s->pc = 0; s->sp = 0; s->halted = false;
    for (int i = 0; i < n_locals && i < AVM_MAX_LOCALS; i++) s->local_set[i] = false;
}

/* ── Primitive execution ───────────────────────────────────── */
AnyVal eval_prim(PrimCode p, AnyVal a, AnyVal b) {
    AnyVal r = { .ty = TY_Q0, .val.i = 0 };
    if (p == PRIM_ADD_Q0) {
        if (a.ty != TY_Q0 || b.ty != TY_Q0) return r;
        r.ty = TY_Q0; r.val.i = avm_q0_clamp((int64_t)a.val.i + b.val.i);
    } else if (p == PRIM_SUB_Q0) {
        if (a.ty != TY_Q0 || b.ty != TY_Q0) return r;
        r.ty = TY_Q0; r.val.i = avm_q0_clamp((int64_t)a.val.i - b.val.i);
    } else if (p == PRIM_ADD_Q16) {
        if (a.ty != TY_Q16 || b.ty != TY_Q16) return r;
        r.ty = TY_Q16; r.val.i = avm_clamp((int64_t)a.val.i + b.val.i);
    } else if (p == PRIM_SUB_Q16) {
        if (a.ty != TY_Q16 || b.ty != TY_Q16) return r;
        r.ty = TY_Q16; r.val.i = avm_clamp((int64_t)a.val.i - b.val.i);
    } else if (p == PRIM_MUL_Q16) {
        if (a.ty != TY_Q16 || b.ty != TY_Q16) return r;
        r.ty = TY_Q16;
        r.val.i = avm_clamp(floor_div((int64_t)a.val.i * b.val.i, Q16_SCALE));
    } else if (p == PRIM_DIV_Q16) {
        if (a.ty != TY_Q16 || b.ty != TY_Q16 || b.val.i == 0) return r;
        r.ty = TY_Q16;
        r.val.i = avm_clamp(floor_div((int64_t)a.val.i * Q16_SCALE, b.val.i));
    } else if (p == PRIM_LT_Q16) {
        if (a.ty != TY_Q16 || b.ty != TY_Q16) return r;
        r.ty = TY_BOOL; r.val.b = lt_q16_v6(a.val.i, b.val.i);
    } else if (p == PRIM_EQ_Q16) {
        if (a.ty != TY_Q16 || b.ty != TY_Q16) return r;
        r.ty = TY_BOOL; r.val.b = (a.val.i == b.val.i);
    } else if (p == PRIM_AND) {
        if (a.ty != TY_BOOL || b.ty != TY_BOOL) return r;
        r.ty = TY_BOOL; r.val.b = a.val.b && b.val.b;
    } else if (p == PRIM_OR) {
        if (a.ty != TY_BOOL || b.ty != TY_BOOL) return r;
        r.ty = TY_BOOL; r.val.b = a.val.b || b.val.b;
    } else if (p == PRIM_NOT) {
        if (a.ty != TY_BOOL) return r;
        r.ty = TY_BOOL; r.val.b = !a.val.b;
    }
    return r;
}

/* ── Step ──────────────────────────────────────────────────── */
int step(State *s, const Instr *prog, int prog_len) {
    if (s->halted) return -1;
    if (s->pc < 0 || s->pc >= prog_len) { s->halted = true; return 0; }

    Instr instr = prog[s->pc];
    int npc = s->pc + 1;

    /* Check stack depth for growing ops */
    if (instr.op <= OP_PUSH_Q0 || instr.op == OP_DUP || instr.op == OP_LOAD) {
        if (s->sp >= AVM_MAX_STACK) return -2; /* stack overflow */
    }

    if (instr.op == OP_PUSH_Q16) {
        s->stack[s->sp].ty = TY_Q16; s->stack[s->sp].val.i = avm_clamp(instr.arg); s->sp++;
    } else if (instr.op == OP_PUSH_BOOL) {
        s->stack[s->sp].ty = TY_BOOL; s->stack[s->sp].val.b = instr.arg2; s->sp++;
    } else if (instr.op == OP_PUSH_Q0) {
        s->stack[s->sp].ty = TY_Q0; s->stack[s->sp].val.i = avm_q0_clamp(instr.arg); s->sp++;
    } else if (instr.op == OP_POP) {
        if (s->sp <= 0) return -3; /* empty stack */
        s->sp--;
    } else if (instr.op == OP_DUP) {
        if (s->sp <= 0) return -3;
        s->stack[s->sp] = s->stack[s->sp - 1]; s->sp++;
    } else if (instr.op == OP_SWAP) {
        if (s->sp < 2) return -4; /* underflow */
        AnyVal tmp = s->stack[s->sp - 1];
        s->stack[s->sp - 1] = s->stack[s->sp - 2];
        s->stack[s->sp - 2] = tmp;
    } else if (instr.op == OP_LOAD) {
        int i = instr.arg;
        if (i < 0 || i >= AVM_MAX_LOCALS || !s->local_set[i]) return -5; /* missing local */
        s->stack[s->sp] = s->locals[i]; s->sp++;
    } else if (instr.op == OP_STORE) {
        if (s->sp <= 0) return -3;
        int i = instr.arg;
        if (i < 0 || i >= AVM_MAX_LOCALS) return -5;
        s->locals[i] = s->stack[--s->sp]; s->local_set[i] = true;
    } else if (instr.op == OP_JUMP) {
        if (instr.arg < 0 || instr.arg >= prog_len) return -6; /* jump OOB */
        npc = instr.arg;
    } else if (instr.op == OP_JUMP_IF) {
        if (s->sp <= 0) return -3;
        AnyVal v = s->stack[--s->sp];
        if (v.ty != TY_BOOL) return -7; /* type mismatch */
        if (v.val.b) {
            if (instr.arg < 0 || instr.arg >= prog_len) return -6;
            npc = instr.arg;
        }
    } else if (instr.op == OP_PRIM) {
        PrimCode p = (PrimCode)instr.arg;
        int arity = (p == PRIM_NOT) ? 1 : 2;
        if (s->sp < arity) return -4;
        AnyVal b = (arity >= 2) ? s->stack[--s->sp] : (AnyVal){0};
        AnyVal a = s->stack[--s->sp];
        /* Check division by zero before calling eval_prim */
        if (p == PRIM_DIV_Q16 && b.val.i == 0) return -8;
        s->stack[s->sp++] = eval_prim(p, a, b);
    } else if (instr.op == OP_HALT) {
        s->halted = true;
    }

    s->pc = npc;
    return 0;
}

/* ── Run (fuel-bounded) ────────────────────────────────────── */
int run(State *s, const Instr *prog, int prog_len, int fuel) {
    for (int i = 0; i < fuel; i++) {
        if (s->halted) return 0;
        int err = step(s, prog, prog_len);
        if (err) return err;
    }
    return 0;
}
