/* engram_verify.h — the VERIFIER layer: GROUNDING + CONSISTENCY over the live
 * geometry (engram_geometry.h) and reasoning (engram_reason.h) operators.
 *
 * The geometry PROPOSES (cheap, creative, sometimes wrong); the verifier DISPOSES.
 * This layer catches the class of failure a grammar check never sees: a fluent,
 * confident, WRONG output — the "plausible lie". The motivating case: a translation
 * that DELETED a negation so "you never fought" became "you argued" — reassurance
 * inverted into accusation, grammatical and invisible, catchable ONLY by the geometry.
 *
 *   GROUNDING   claim → is there ANY real structure that supports it, or is it
 *               floating free of the manifold?  (anti-hallucination gate)
 *   CONSISTENCY claim → does it CONTRADICT the established structure?  Two catches:
 *               (a) POLARITY: the claim lands on the OPPOSITE side of a negation
 *                   axis from the grounded truth  (the reassurance→accusation catch),
 *               (b) GEOMETRIC: the claim sits inside a region it must be far from,
 *                   or violates a max-distance constraint to its context.
 *
 * PURE + READ-ONLY (stdlib + libm only): every function consumes a claim POINT
 * (float* in R^dim) plus GeoDescriptor(s), and NEVER touches the store, index, or
 * activation. All geometry is delegated to engram_reason_point_fit / engram_geo_*;
 * this file only composes and applies thresholds.
 *
 * FRAME CONTRACT (inherited): the claim point and every descriptor passed together
 * MUST share emb `dim` and the same `global_mean` frame — exactly the §5 operator
 * contract. A function returns <0 on a dim/frame mismatch or bad argument.
 */
#ifndef ENGRAM_VERIFY_H
#define ENGRAM_VERIFY_H

#include "engram_geometry.h"
#include "engram_reason.h"

/* ═══════════════════════════════════════════════════════════════════════════
 * GROUNDING — anti-hallucination. Score how well a claimed POINT is supported by
 * the ACTUAL structure: fit the claim against every real evidence neighborhood
 * (engram_reason_point_fit → in-distribution Mahalanobis + off-model orthogonal
 * residual) and take the BEST supporter. A claim that sits inside real structure
 * scores high (grounded); a claim floating far from every neighborhood scores low
 * on all of them → flagged UNGROUNDED (a hallucination).
 *
 * This is an ABSOLUTE-THRESHOLD gate, deliberately distinct from ABDUCTION (which
 * always RANKS and picks a winner among competing hypotheses): grounding asks the
 * prior question — "is there any real support at all?" — and is allowed to answer no.
 * The off-model `ortho_residual` is the sharpest hallucination signal: energy in a
 * direction the manifold does not even span.
 * ═══════════════════════════════════════════════════════════════════════════ */
typedef struct {
    double  grounding;          /* ∈[0,1]: overall support = best fit score           */
    int     grounded;           /* 1 iff grounding >= ground_threshold                */
    int     best;               /* index of best-supporting evidence structure, or −1 */
    double  best_distance;      /* full point-to-manifold distance to the best        */
    double  best_ortho;         /* off-model orthogonal residual of the best fit      */
    double  nearest_centroid_l2;/* raw L2 to the nearest evidence centroid (coarse)   */
    int     n_evidence;
    double* scores;             /* per-evidence fit score, higher = better     (owned)*/
} GeoGrounding;
/* ext_floor>0 guards zero-extent axes (default 1.0). ground_threshold∈(0,1): the
 * minimum best-fit score to call the claim grounded (default 0.5). */
int  engram_verify_grounding(const float* claim, int dim,
                             const GeoDescriptor* const* evidence, int n_evidence,
                             double ext_floor, double ground_threshold,
                             GeoGrounding* out);
void engram_verify_grounding_free(GeoGrounding* out);

/* ═══════════════════════════════════════════════════════════════════════════
 * CONSISTENCY — contradiction detection. Does the claim contradict the established
 * structure?  Two independent sub-checks (either can fire; both flags are reported):
 *
 * (a) POLARITY / negation inversion.  A polarity axis p is defined by two REAL
 *     poles — pole_pos (asserts X) and pole_neg (asserts ¬X):
 *         p = (c_pos − c_neg)/‖·‖ ,  midpoint o = ½(c_pos + c_neg).
 *     The claim's side  = p·(claim − o);  the reference's side = p·(c_context − o).
 *     If the two sides have OPPOSITE sign AND both clear the neutral deadzone, the
 *     claim asserts the polarity opposite to the grounded truth → INVERSION flagged.
 *     This is the "you never fought"→"you argued" catch: the truth ("never fought")
 *     sits on the negate pole, the claim ("argued") on the affirm pole → opposite
 *     sides → flagged, though every word is grammatical.
 *
 * (b) GEOMETRIC contradiction.  The claim sits INSIDE a `forbidden` region it must
 *     be far from (point_fit score to forbidden ≥ forbidden_thresh), OR it violates
 *     a max-distance constraint to its context centroid (L2 > max_distance).
 *
 * pole_pos/pole_neg may both be NULL to skip the polarity check; forbidden may be
 * NULL and max_distance≤0 to skip the geometric check. `context` (the grounded truth
 * region) is required whenever polarity or the distance constraint is used.
 * ═══════════════════════════════════════════════════════════════════════════ */
typedef enum {
    GEO_CONSIST_OK        = 0,  /* consistent with context                            */
    GEO_CONSIST_POLARITY  = 1,  /* polarity/negation inversion (asserts ¬X where X)    */
    GEO_CONSIST_GEOMETRIC = 2   /* geometric contradiction (in forbidden / too far)    */
} GeoConsistencyVerdict;
typedef struct {
    GeoConsistencyVerdict verdict;   /* headline (polarity takes precedence)          */
    double consistency;              /* ∈[0,1]: min over the checks (1 = fully consistent)*/
    /* polarity sub-check */
    int    inverted;                 /* 1 iff a polarity inversion was detected        */
    double polarity_claim;           /* p·(claim − o)     (signed position on the axis)*/
    double polarity_reference;       /* p·(c_context − o) (the grounded truth's side)  */
    double polarity_separation;      /* ½‖c_pos − c_neg‖  (the axis half-length / scale)*/
    /* geometric sub-check */
    int    geo_violation;            /* 1 iff a geometric contradiction was detected   */
    double forbidden_fit;            /* claim's point_fit score to the forbidden region*/
    double context_distance;         /* L2(claim, c_context)                           */
} GeoConsistency;
/* ext_floor>0 (default 1.0). deadzone_frac∈[0,1): a polarity side within
 * deadzone_frac·separation of the midpoint is "neutral" and never triggers inversion
 * (default 0.10). forbidden_thresh∈(0,1): fit-to-forbidden at/above which the claim
 * counts as inside the forbidden region (default 0.5). max_distance>0 enables the
 * distance constraint; ≤0 disables it. */
int engram_verify_consistency(const float* claim, int dim,
                              const GeoDescriptor* context,
                              const GeoDescriptor* pole_pos, const GeoDescriptor* pole_neg,
                              const GeoDescriptor* forbidden,
                              double ext_floor, double deadzone_frac,
                              double forbidden_thresh, double max_distance,
                              GeoConsistency* out);

#endif /* ENGRAM_VERIFY_H */
