/* M2 item 2: scalar-kernel capability demo. N threads, each owning
 * its own ailang_ctx_t, drive the headline scalar kernel; must be
 * -fsanitize=thread-clean with every thread's result == the serial
 * oracle. NO negative control here: a non-allocating scalar kernel
 * writes no shared mutable state even under a shared ctx, so a
 * shared-ctx variant cannot race (the de-globalisation negative
 * control lives in the rc_accounting harness — Task 5 Step 3). */
#include <stdint.h>
#include <stdio.h>
#include <pthread.h>

typedef struct ailang_ctx ailang_ctx_t;
extern ailang_ctx_t *ailang_ctx_new(void);
extern void          ailang_ctx_free(ailang_ctx_t *);
extern int64_t backtest_step(ailang_ctx_t *ctx, int64_t state, int64_t sample);

#define NTHREADS 8
#define NITERS   200000

static int64_t serial_oracle(void) {
    /* mirrors examples/embed_backtest_step.ail: state += sample*sample,
     * sample = (i & 7) over NITERS iterations. */
    int64_t s = 0;
    for (int i = 0; i < NITERS; i++) { int64_t x = i & 7; s = s + x * x; }
    return s;
}

static void *worker(void *out) {
    ailang_ctx_t *ctx = ailang_ctx_new();
    int64_t s = 0;
    for (int i = 0; i < NITERS; i++) s = backtest_step(ctx, s, (i & 7));
    ailang_ctx_free(ctx);
    *(int64_t *)out = s;
    return NULL;
}

int main(void) {
    pthread_t t[NTHREADS];
    int64_t res[NTHREADS];
    for (int i = 0; i < NTHREADS; i++) pthread_create(&t[i], NULL, worker, &res[i]);
    for (int i = 0; i < NTHREADS; i++) pthread_join(t[i], NULL);
    int64_t oracle = serial_oracle();
    for (int i = 0; i < NTHREADS; i++) {
        if (res[i] != oracle) { fprintf(stderr, "thread %d: %lld != %lld\n",
            i, (long long)res[i], (long long)oracle); return 1; }
    }
    printf("swarm: ok (%lld)\n", (long long)oracle);
    return 0;
}
