/*
 * bench.c — Benchmarks: interpreted vs compiled vs JIT
 */
#include "lumbda.h"
#include "jit.h"

static double bench_time(void) {
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return ts.tv_sec + ts.tv_nsec / 1e9;
}

typedef struct {
    const char *name;
    const char *setup;
    const char *expr;
    int iterations;
} Benchmark;

static Benchmark benchmarks[] = {
    {
        "fib-iter(30)",
        "(define (fib-iter n)\n"
        "  (let loop ((a 0) (b 1) (i 0))\n"
        "    (if (= i n) a (loop b (+ a b) (+ i 1)))))\n",
        "(fib-iter 30)",
        10000
    },
    {
        "fib-rec(25)",
        "(define (fib-rec n)\n"
        "  (if (<= n 1) n (+ (fib-rec (- n 1)) (fib-rec (- n 2)))))\n",
        "(fib-rec 25)",
        10
    },
    {
        "list-sum(1000)",
        "(define (list-sum lst)\n"
        "  (let loop ((l lst) (acc 0))\n"
        "    (if (null? l) acc (loop (cdr l) (+ acc (car l))))))\n"
        "(define big-list (iota 1000))\n",
        "(list-sum big-list)",
        1000
    },
    {
        "map-square(1000)",
        "(define (my-map f lst)\n"
        "  (if (null? lst) '()\n"
        "      (cons (f (car lst)) (my-map f (cdr lst)))))\n"
        "(define big-list (iota 1000))\n",
        "(my-map (lambda (x) (* x x)) big-list)",
        100
    },
    {
        "tak(18,12,6)",
        "(define (tak x y z)\n"
        "  (if (not (< y x)) z\n"
        "      (tak (tak (- x 1) y z)\n"
        "           (tak (- y 1) z x)\n"
        "           (tak (- z 1) x y))))\n",
        "(tak 18 12 6)",
        10
    },
    {
        "ack(3,4)",
        "(define (ack m n)\n"
        "  (if (= m 0) (+ n 1)\n"
        "      (if (= n 0) (ack (- m 1) 1)\n"
        "          (ack (- m 1) (ack m (- n 1))))))\n",
        "(ack 3 4)",
        100
    },
    {
        "ack(3,7)",
        "(define (ack m n)\n"
        "  (if (= m 0) (+ n 1)\n"
        "      (if (= n 0) (ack (- m 1) 1)\n"
        "          (ack (- m 1) (ack m (- n 1))))))\n",
        "(ack 3 7)",
        10
    },
    {
        "eml-compose(1M)",
        /* EML insight (arXiv:2603.21852v2): eml(x,y) = exp(x) - ln(y) generates\n"
         * ALL elementary functions from one operator. This benchmark exercises\n"
         * integer-only EML-style composition: repeated add/sub (exp/ln analog\n"
         * in the integer domain). Tests whether JIT compiles well the pattern\n"
         * of composing minimal primitives into higher operations. */
        "(define (eml-pow base exp)\n"
        "  (let loop ((e exp) (acc 1))\n"
        "    (if (= e 0) acc (loop (- e 1) (* acc base)))))\n"
        "(define (eml-log-approx n base)\n"
        "  (let loop ((n n) (count 0))\n"
        "    (if (< n base) count (loop (- n base) (+ count 1)))))\n"
        "(define (eml-compose x y)\n"
        "  (let ((p (eml-pow x y)))\n"
        "    (eml-log-approx p x)))\n",
        "(eml-compose 3 7)",
        100000
    },
    {
        "named-let-sum(100k)",
        /* Named-let compiles to a native loop with jmp — zero overhead. */
        "(define (sum-to n)\n"
        "  (let loop ((i n) (acc 0))\n"
        "    (if (= i 0) acc (loop (- i 1) (+ acc i)))))\n",
        "(sum-to 100000)",
        100
    },
    {
        "list-walk(1000)",
        /* Tests JIT car/cdr/null?/cons pipeline */
        "(define (list-reverse lst)\n"
        "  (let loop ((l lst) (acc '()))\n"
        "    (if (null? l) acc (loop (cdr l) (cons (car l) acc)))))\n"
        "(define big-list (iota 1000))\n",
        "(list-reverse big-list)",
        1000
    },
    {NULL, NULL, NULL, 0}
};

static void run_benchmark(Benchmark *b, const char *mode) {
    Env *g = make_global_env();
    int count;
    Value *prelude_exprs = read_all(PRELUDE, &count, false);
    for (int i = 0; i < count; i++) leval(prelude_exprs[i], g);
    ul_free(prelude_exprs);

    g_auto_compile = false;
    g_jit_enabled = false;

    if (strcmp(mode, "compiled") == 0) {
        g_auto_compile = true;
    } else if (strcmp(mode, "jit") == 0) {
        g_jit_enabled = true;
    }

    /* Setup */
    Value *setup_exprs = read_all(b->setup, &count, false);
    for (int i = 0; i < count; i++) leval(setup_exprs[i], g);
    ul_free(setup_exprs);

    /* Parse the benchmark expression once */
    Value *bench_exprs = read_all(b->expr, &count, false);

    /* Warmup */
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < count; j++) leval(bench_exprs[j], g);
    }

    /* Timed run */
    double start = bench_time();
    Value last = VAL_VOID;
    for (int i = 0; i < b->iterations; i++) {
        for (int j = 0; j < count; j++) last = leval(bench_exprs[j], g);
    }
    double elapsed = bench_time() - start;

    char *result_str = show(last, false);
    printf("  %-25s %-10s %8d iters  %.3f s  (%.1f us/iter)  => %s\n",
           b->name, mode,
           b->iterations, elapsed,
           (elapsed / b->iterations) * 1e6,
           result_str);
    ul_free(result_str);

    ul_free(bench_exprs);
    g_auto_compile = false;
    g_jit_enabled = false;
}

int main(void) {
    int stack_anchor;
#ifdef USE_BOEHM_GC
    GC_INIT();
    lumbda_gc_init();
    lumbda_gc_set_stack_base(&stack_anchor);
#else
    (void)stack_anchor;
#endif
    init_symbols();

    /* Set up root error context for prelude loading */
    ErrorContext root_ctx;
    root_ctx.call_stack_depth = 0;
    root_ctx.error_obj = VAL_NIL;
    root_ctx.source_line = 0;
    g_error_ctx = &root_ctx;
    if (setjmp(root_ctx.jmp) != 0) {
        fprintf(stderr, "fatal error: %s\n", root_ctx.message);
        return 1;
    }

    printf("lumbda C benchmarks — interpreted vs compiled vs JIT\n");
    printf("═════════════════════════════════════════════════════════════════\n");

    const char *modes[] = { "interp", "compiled", "jit", NULL };

    for (int i = 0; benchmarks[i].name; i++) {
        for (int m = 0; modes[m]; m++) {
            TRY(ctx) {
                run_benchmark(&benchmarks[i], modes[m]);
            } CATCH {
                printf("  %-25s %-10s ERROR: %s\n", benchmarks[i].name, modes[m], ctx.message);
            } ENDTRY;
        }
        printf("  ─────────────────────────────────────────────────────────────\n");
    }

    printf("═════════════════════════════════════════════════════════════════\n");
    return 0;
}
