// Lock down the interp's raw-pointer store width per primitive type.
//
// Each helper allocates a 32-byte buffer through `context.allocator`,
// fills it with a sentinel byte (0xAA), writes ONE typed value at
// offset 8, then sums every byte back. A correctly-sized store touches
// exactly `sizeof(T)` bytes, so the sum equals
//   31 * 0xAA + sum-of-bytes-in-the-written-value.
// A wrong width (e.g. an 8-byte store at a 1-byte slot) clobbers
// neighbors with zeros and the sum drops.
//
// Each test computes its expected sum at COMPTIME (the value is baked
// into a `#run` constant — the interp's `storeAtRawPtr` runs). The
// runtime program prints the same checksum computed by codegen
// (LLVM-emitted typed stores). The two MUST match — that's the
// regression assertion.
//
// To pin the test: every helper returns its checksum; main prints
// "ok" iff every comptime-baked checksum equals the runtime-recomputed
// one. Failure prints which width diverged.

#import "modules/std.sx";

SENTINEL :u8: 0xAA;       // 170 — neighbor pattern
BUF_SIZE :s64: 32;
TARGET   :s64: 8;          // offset where the typed store lands

// ── per-width helpers ───────────────────────────────────────────────

fill :: (buf: [*]u8) {
    i : s64 = 0;
    while i < BUF_SIZE { buf[i] = SENTINEL; i += 1; }
}

sum_bytes :: (buf: [*]u8) -> s64 {
    s : s64 = 0;
    i : s64 = 0;
    while i < BUF_SIZE { s += xx buf[i]; i += 1; }
    s;
}

run_u8 :: () -> s64 {
    buf : [*]u8 = xx malloc(BUF_SIZE);
    fill(buf);
    p : *u8 = xx @buf[TARGET];
    p.* = 0x42;
    s := sum_bytes(buf);
    free(xx buf);
    s;
}

run_u16 :: () -> s64 {
    buf : [*]u8 = xx malloc(BUF_SIZE);
    fill(buf);
    p : *u16 = xx @buf[TARGET];
    p.* = 0x0102;
    s := sum_bytes(buf);
    free(xx buf);
    s;
}

run_u32 :: () -> s64 {
    buf : [*]u8 = xx malloc(BUF_SIZE);
    fill(buf);
    p : *u32 = xx @buf[TARGET];
    p.* = 0x01020304;
    s := sum_bytes(buf);
    free(xx buf);
    s;
}

run_u64 :: () -> s64 {
    buf : [*]u8 = xx malloc(BUF_SIZE);
    fill(buf);
    p : *u64 = xx @buf[TARGET];
    p.* = 0x0102030405060708;
    s := sum_bytes(buf);
    free(xx buf);
    s;
}

run_s8 :: () -> s64 {
    buf : [*]u8 = xx malloc(BUF_SIZE);
    fill(buf);
    p : *s8 = xx @buf[TARGET];
    p.* = 0x42;
    s := sum_bytes(buf);
    free(xx buf);
    s;
}

run_s16 :: () -> s64 {
    buf : [*]u8 = xx malloc(BUF_SIZE);
    fill(buf);
    p : *s16 = xx @buf[TARGET];
    p.* = 0x0102;
    s := sum_bytes(buf);
    free(xx buf);
    s;
}

run_s32 :: () -> s64 {
    buf : [*]u8 = xx malloc(BUF_SIZE);
    fill(buf);
    p : *s32 = xx @buf[TARGET];
    p.* = 0x01020304;
    s := sum_bytes(buf);
    free(xx buf);
    s;
}

run_s64 :: () -> s64 {
    buf : [*]u8 = xx malloc(BUF_SIZE);
    fill(buf);
    p : *s64 = xx @buf[TARGET];
    p.* = 0x0102030405060708;
    s := sum_bytes(buf);
    free(xx buf);
    s;
}

run_bool :: () -> s64 {
    buf : [*]u8 = xx malloc(BUF_SIZE);
    fill(buf);
    p : *bool = xx @buf[TARGET];
    p.* = true;
    s := sum_bytes(buf);
    free(xx buf);
    s;
}

run_f32 :: () -> s64 {
    buf : [*]u8 = xx malloc(BUF_SIZE);
    fill(buf);
    p : *f32 = xx @buf[TARGET];
    p.* = 1.0;
    s := sum_bytes(buf);
    free(xx buf);
    s;
}

run_f64 :: () -> s64 {
    buf : [*]u8 = xx malloc(BUF_SIZE);
    fill(buf);
    p : *f64 = xx @buf[TARGET];
    p.* = 1.0;
    s := sum_bytes(buf);
    free(xx buf);
    s;
}

// ── comptime-baked expected checksums ───────────────────────────────
// `#run` evaluates each helper via the interp, so its
// `storeAtRawPtr(addr, val, val_ty)` honors the declared width.

EXP_U8   :: #run run_u8();
EXP_U16  :: #run run_u16();
EXP_U32  :: #run run_u32();
EXP_U64  :: #run run_u64();
EXP_S8   :: #run run_s8();
EXP_S16  :: #run run_s16();
EXP_S32  :: #run run_s32();
EXP_S64  :: #run run_s64();
EXP_BOOL :: #run run_bool();
EXP_F32  :: #run run_f32();
EXP_F64  :: #run run_f64();

// ── runtime comparison ──────────────────────────────────────────────

check :: (label: string, got: s64, want: s64) -> bool {
    if got == want { return true; }
    print("FAIL {}: comptime={} runtime={}\n", label, want, got);
    false;
}

main :: () -> s32 {
    ok := true;
    if !check("u8",   run_u8(),   EXP_U8)   { ok = false; }
    if !check("u16",  run_u16(),  EXP_U16)  { ok = false; }
    if !check("u32",  run_u32(),  EXP_U32)  { ok = false; }
    if !check("u64",  run_u64(),  EXP_U64)  { ok = false; }
    if !check("s8",   run_s8(),   EXP_S8)   { ok = false; }
    if !check("s16",  run_s16(),  EXP_S16)  { ok = false; }
    if !check("s32",  run_s32(),  EXP_S32)  { ok = false; }
    if !check("s64",  run_s64(),  EXP_S64)  { ok = false; }
    if !check("bool", run_bool(), EXP_BOOL) { ok = false; }
    if !check("f32",  run_f32(),  EXP_F32)  { ok = false; }
    if !check("f64",  run_f64(),  EXP_F64)  { ok = false; }
    if ok { print("ok\n"); return 0; }
    return 1;
}
