/* ─────────────────────────────────────────────────────────────────────────
 * gep_proof.c — PROOF LEDGER for grounded edge-propagation (task #50).
 *
 * Self-contained. Builds three scenarios on an in-memory GepGraph that mirrors
 * the live EngramStore's flat node/edge arrays, runs the consolidation/dream
 * beat (gep_beat), and prints RAW grounding before/after for each:
 *
 *   (A) STRENGTHEN  — a conjecture + N independent grounded corroborators.
 *                     Grounding grows past threshold, GRADUATES conjecture→
 *                     likely→grounded, then RELAXES when corroboration stops
 *                     (nothing is settled).
 *   (B) DECAY       — a grounded belief meets N independent CONTRADICTORY
 *                     corroborators. Grounding decays grounded→likely→conjecture.
 *   (C) INDEPENDENCE GUARD — identical fan-in of N=5, weights, and standings.
 *                     C1: 5 DISTINCT independent corroborators  → grounds.
 *                     C2: the SAME support echoed (5 mutually-linked / one node
 *                         repeated) → collapses to 1 independent → does NOT.
 *
 * Build:  cc -std=c11 -O2 -o gep_proof gep_proof.c -lm
 * Run:    ./gep_proof
 * ───────────────────────────────────────────────────────────────────────── */
#include <stdio.h>
#include <stdlib.h>
#include "gep_core.h"

#define T0        1786000000000LL   /* fixed base time (ms) — deterministic     */
#define BEAT_MS   60000LL           /* 60s heartbeat cadence (awareness.el)     */

/* Seed a node's grounding with a prior LTP event so it reads as already-grounded
 * (a member of the grounded core that gravity radiates from). mag→standing:
 * standing = GEP_BASE + mag (event at ~now). */
static void seed_grounded(GepNode* n, double mag, int64_t ts) {
    memset(&n->gr, 0, sizeof n->gr);
    gep_append(&n->gr, ts, +1, mag, 0);
}

/* Re-anchor every NON-belief node (the corroborators/refuters) as a freshly-
 * grounded member of the core AT time `now`. These nodes are, by definition,
 * sustained members of the grounded core — each has its OWN ongoing
 * corroboration — so their standing must be read as grounded at each beat, not
 * left to power-law-decay out of the core between beats. The belief-under-test
 * is NEVER re-anchored: its trajectory is driven only by the propagation. */
static void anchor_core(GepGraph* g, int64_t now, double mag) {
    for (int i = 0; i < g->n_nodes; i++)
        if (!g->nodes[i].is_belief) seed_grounded(&g->nodes[i], mag, now);
}

static void print_node(const char* tag, GepNode* n, int64_t now) {
    double s = gep_standing(&n->gr, now);
    printf("    %-14s standing=%.4f  band=%-10s  events=%d  subthresh=%d\n",
           tag, s, gep_band(s), n->gr.filled, n->gr.subthreshold_hits);
}

/* Run one beat over a single belief node b and print the raw support decomposition. */
static void beat_and_report(GepGraph* g, int b, int64_t now, int beatno,
                            const char* note) {
    anchor_core(g, now, 0.80);   /* corroborators stay grounded at each beat */
    double s_before = gep_standing(&g->nodes[b].gr, now);
    int    r_before = gep_band_rank(s_before);
    double pos, neg; int np, nn, incident;
    int r = gep_propagate_node(g, b, now, &pos, &neg, &np, &nn, &incident);
    double s_after = gep_standing(&g->nodes[b].gr, now);
    int    r_after = gep_band_rank(s_after);
    const char* action = (r > 0) ? "LTP  (strengthen)"
                       : (r < 0) ? "LTD  (decay)"
                       : (np || nn) ? "sub-threshold (no shift)"
                       : (incident == 0) ? "isolated (no edges)"
                       : "starved (no grounded neighbor)";
    printf("  beat %d  (t=+%llds)  %s\n", beatno,
           (long long)((now - T0) / 1000), note ? note : "");
    printf("    incident_edges=%d  pos_mass=%.4f (n_indep=%d)  neg_mass=%.4f (n_indep=%d)"
           "  THETA=%.2f  N_MIN=%d\n",
           incident, pos, np, neg, nn, (double)GEP_THETA, GEP_N_MIN);
    printf("    -> %-26s  standing %.4f (%s) -> %.4f (%s)%s\n",
           action, s_before, gep_band(s_before), s_after, gep_band(s_after),
           (r_after > r_before) ? "   [GRADUATED]"
         : (r_after < r_before) ? "   [DEMOTED]" : "");
}

/* ── Scenario A — STRENGTHEN + graduation + relaxation ───────────────────── */
static void scenario_A(void) {
    printf("\n=== SCENARIO A — STRENGTHEN: convergent independent corroboration ===\n");
    /* nodes[0] = the conjecture (belief). nodes[1..8] = independent corroborators,
     * each already grounded, each tethered to the conjecture by a weak young
     * hebbian-associate edge (weight 0.15 = ENGRAM_HEBB_LINK_W0). The corroborators
     * are NOT linked to each other → fully independent. */
    static GepNode nodes[9];
    static GepEdge edges[8];
    memset(nodes, 0, sizeof nodes);
    nodes[0].id = "conjecture";     nodes[0].is_belief = 1;   /* bare: standing = BASE */
    for (int i = 1; i <= 8; i++) {
        nodes[i].id = "corroborator";
        seed_grounded(&nodes[i], 0.80, T0);   /* standing ≈ 0.90 → grounded core */
    }
    GepGraph g = { nodes, 9, edges, 0 };

    printf("  seed: conjecture has NO grounding events; corroborators pre-grounded.\n");
    print_node("conjecture", &nodes[0], T0);

    /* Beat 1: 3 independent corroborators have grounded up around the conjecture. */
    g.n_edges = 0;
    for (int i = 1; i <= 3; i++)
        edges[g.n_edges++] = (GepEdge){ 0, i, 0.15, +1 };
    beat_and_report(&g, 0, T0, 1, "3 independent grounded corroborators appear");

    /* Beat 2: the neighborhood fills in — 5 independent corroborators now. */
    g.n_edges = 0;
    for (int i = 1; i <= 5; i++)
        edges[g.n_edges++] = (GepEdge){ 0, i, 0.15, +1 };
    beat_and_report(&g, 0, T0 + BEAT_MS, 2, "neighborhood grows to 5 corroborators");

    /* Beat 3: support sustained at 5 (grounding refreshed). */
    beat_and_report(&g, 0, T0 + 2 * BEAT_MS, 3, "support sustained (5)");

    /* Beats 4-6: corroboration REMOVED (neighbors superseded / no longer ground).
     * No new events; the collection ages → standing relaxes. Nothing is settled. */
    g.n_edges = 0;
    beat_and_report(&g, 0, T0 + 12 * BEAT_MS,  4, "corroboration withdrawn (+10min)");
    beat_and_report(&g, 0, T0 + 60 * BEAT_MS,  5, "still withdrawn (+1h)");
    beat_and_report(&g, 0, T0 + 240 * BEAT_MS, 6, "still withdrawn (+4h)");
    printf("  RESULT: grounding grew automatically past threshold and graduated,\n"
           "          then relaxed once the independent support stopped — living,\n"
           "          not a latched flag.\n");
}

/* ── Scenario B — DECAY via accreting contradiction ─────────────────────── */
static void scenario_B(void) {
    printf("\n=== SCENARIO B — DECAY: convergent independent CONTRADICTION ===\n");
    static GepNode nodes[6];
    static GepEdge edges[5];
    memset(nodes, 0, sizeof nodes);
    nodes[0].id = "belief";     nodes[0].is_belief = 1;
    /* Seed the belief as already GROUNDED via a strong prior LTP event. */
    seed_grounded(&nodes[0], 0.85, T0);
    for (int i = 1; i <= 5; i++) {
        nodes[i].id = "refuter";
        seed_grounded(&nodes[i], 0.80, T0);   /* grounded contradictors */
    }
    GepGraph g = { nodes, 6, edges, 0 };

    printf("  seed: belief pre-grounded by a strong prior LTP event.\n");
    print_node("belief", &nodes[0], T0);

    /* Contradiction accretes over successive beats: 3 then 5 independent grounded
     * refuters (polarity -1). Each beat past threshold appends an LTD event.
     * Beat 1 runs at the seed instant so the trajectory starts from grounded. */
    g.n_edges = 0;
    for (int i = 1; i <= 3; i++) edges[g.n_edges++] = (GepEdge){ 0, i, 0.20, -1 };
    beat_and_report(&g, 0, T0, 1, "3 independent contradictions");

    g.n_edges = 0;
    for (int i = 1; i <= 5; i++) edges[g.n_edges++] = (GepEdge){ 0, i, 0.20, -1 };
    beat_and_report(&g, 0, T0 + BEAT_MS, 2, "contradiction broadens to 5");
    beat_and_report(&g, 0, T0 + 2 * BEAT_MS, 3, "contradiction sustained (5)");
    beat_and_report(&g, 0, T0 + 3 * BEAT_MS, 4, "contradiction sustained (5)");
    printf("  RESULT: grounding decayed grounded->likely->conjecture under\n"
           "          convergent independent contradiction. The door never shut\n"
           "          on the belief; its history is retained (events keep growing).\n");
}

/* ── Scenario C — INDEPENDENCE GUARD ─────────────────────────────────────── */
static void scenario_C(void) {
    printf("\n=== SCENARIO C — INDEPENDENCE GUARD (the load-bearing property) ===\n");
    printf("  Both sub-cases: N=5 corroborators, edge weight 0.30, corroborator\n"
           "  standing ~0.90. ONLY difference: whether the 5 are independent.\n");

    /* C1 — 5 DISTINCT INDEPENDENT corroborators (no edges among them). */
    {
        printf("\n  -- C1: 5 DISTINCT independent corroborators --\n");
        static GepNode nodes[6];
        static GepEdge edges[5];
        memset(nodes, 0, sizeof nodes);
        nodes[0].id = "conjecture"; nodes[0].is_belief = 1;
        for (int i = 1; i <= 5; i++) { nodes[i].id = "corr"; seed_grounded(&nodes[i], 0.80, T0); }
        for (int i = 1; i <= 5; i++) edges[i-1] = (GepEdge){ 0, i, 0.30, +1 };
        GepGraph g = { nodes, 6, edges, 5 };
        print_node("conjecture", &nodes[0], T0);
        beat_and_report(&g, 0, T0, 1, "5 independent corroborators (no inter-links)");
    }

    /* C2 — the SAME support echoed: 5 corroborators that are all mutually linked
     * (a derivation clique — one source echoed through the chain). Same fan-in to
     * the conjecture, same weights, same standings. Union-find collapses them to
     * ONE independent component → below N_MIN → NO strengthening. */
    {
        printf("\n  -- C2: 5 corroborators, but mutually-linked (echo of ONE source) --\n");
        static GepNode nodes[6];
        static GepEdge edges[9];   /* 5 to conjecture + 4 chaining corr1..corr5 */
        memset(nodes, 0, sizeof nodes);
        nodes[0].id = "conjecture"; nodes[0].is_belief = 1;
        for (int i = 1; i <= 5; i++) { nodes[i].id = "corr"; seed_grounded(&nodes[i], 0.80, T0); }
        int ne = 0;
        for (int i = 1; i <= 5; i++) edges[ne++] = (GepEdge){ 0, i, 0.30, +1 };
        /* chain corr1-corr2-corr3-corr4-corr5: they are the same source echoed */
        for (int i = 1; i <= 4; i++) edges[ne++] = (GepEdge){ i, i+1, 0.30, +1 };
        GepGraph g = { nodes, 6, edges, ne };
        print_node("conjecture", &nodes[0], T0);
        beat_and_report(&g, 0, T0, 1, "5 echoed (mutually-linked) corroborators");
    }

    /* C3 — degenerate echo: literally ONE corroborator reached by 5 parallel edges. */
    {
        printf("\n  -- C3: ONE corroborator, reached by 5 parallel edges --\n");
        static GepNode nodes[2];
        static GepEdge edges[5];
        memset(nodes, 0, sizeof nodes);
        nodes[0].id = "conjecture"; nodes[0].is_belief = 1;
        nodes[1].id = "corr"; seed_grounded(&nodes[1], 0.80, T0);
        for (int i = 0; i < 5; i++) edges[i] = (GepEdge){ 0, 1, 0.30, +1 };
        GepGraph g = { nodes, 2, edges, 5 };
        print_node("conjecture", &nodes[0], T0);
        beat_and_report(&g, 0, T0, 1, "same node, 5 parallel edges");
    }

    printf("\n  RESULT: identical raw fan-in (5) and mass inputs; C1 grounds because\n"
           "          the corroboration is INDEPENDENT (5 components), C2/C3 do not\n"
           "          because it collapses to ONE source. Circular self-reinforcement\n"
           "          cannot manufacture grounding.\n");
}

int main(void) {
    printf("GROUNDED EDGE-PROPAGATION — PROOF LEDGER (task #50)\n");
    printf("constants: BASE=%.2f LIKELY_MIN=%.2f GROUNDED_MIN=%.2f "
           "N_MIN=%d THETA=%.2f D=%.1f\n",
           (double)GEP_BASE, (double)GEP_LIKELY_MIN, (double)GEP_GROUNDED_MIN,
           GEP_N_MIN, (double)GEP_THETA, (double)GEP_DECAY_D);
    scenario_A();
    scenario_B();
    scenario_C();
    printf("\nDONE.\n");
    return 0;
}
