/*
 * hybrid_zeckendorf_nucleus.c
 *
 * CAB-certified extraction from:
 *   HeytingLean.Bridge.Veselov.HybridZeckendorf.NucleusBridge
 *
 * Bridge lemmas connecting HybridZeckendorf normalization/carry to
 * closure-style (Nucleus) semantics. Proves normalization is an
 * idempotent closure operator.
 *
 * Provenance: Lean 4 kernel-verified -> LambdaIR -> MiniC -> C
 * Certificate: semantic preservation verified at each stage
 */

#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>

#define HZ_MAX_LEVELS 8
#define HZ_MAX_PAYLOAD 64

typedef struct {
    uint32_t indices[HZ_MAX_PAYLOAD];
    size_t   count;
} hz_payload_t;

typedef struct {
    hz_payload_t levels[HZ_MAX_LEVELS];
    size_t       num_levels;
} hz_number_t;

/* Forward declarations */
extern uint64_t hz_weight(uint32_t i);
extern uint64_t hz_lazy_eval_fib(const uint32_t *indices, size_t count);
extern uint64_t hz_eval(const hz_number_t *X);
extern void hz_carry_at(uint32_t i, hz_number_t *X);
extern void hz_normalize(const hz_number_t *lazy_in, hz_number_t *out);
extern bool hz_repr_equal(const hz_number_t *X, const hz_number_t *Y);

/* --- Carry idempotence -------------------------------------------------- */

/*
 * Carry at a fixed level is representationally idempotent on canonical
 * states: carryAt(i, carryAt(i, X)) = carryAt(i, X).
 *
 * (Lean: carryAt_idempotent)
 *
 * Proof: both sides are canonical (carryAt_preserves_canonical applied
 * twice). Their weighted evaluations agree at every level (carryAt is
 * eval-preserving + Euclidean division is idempotent on remainders).
 * canonical_eval_injective gives representational equality.
 */
bool hz_carry_at_idempotent_check(uint32_t i, const hz_number_t *X) {
    hz_number_t once = *X;
    hz_carry_at(i, &once);

    hz_number_t twice = once;
    hz_carry_at(i, &twice);

    return hz_repr_equal(&once, &twice);
}

/* --- Eval-level monotonicity -------------------------------------------- */

/*
 * Semantic monotonicity of carry under pointwise per-level order:
 *   forall j, levelEval(X_j) <= levelEval(Y_j)
 *   => eval(carryAt(i, X)) <= eval(carryAt(i, Y))
 *
 * (Lean: carryAt_eval_monotone)
 *
 * This is the strongest monotonicity available. Pointwise representational
 * monotonicity FAILS: the Euclidean division discontinuity at carry
 * thresholds can decrease one coordinate while increasing the next.
 */
bool hz_carry_eval_monotone_check(uint32_t i,
                                  const hz_number_t *X,
                                  const hz_number_t *Y) {
    hz_number_t cx = *X, cy = *Y;
    hz_carry_at(i, &cx);
    hz_carry_at(i, &cy);
    return hz_eval(&cx) <= hz_eval(&cy);
}

/* --- Normalization closure ---------------------------------------------- */

/*
 * Normalization is an idempotent closure operator:
 *   normalize(toLazy(normalize(X))) = normalize(X)
 *
 * (Lean: normalize_is_closure_operator)
 *
 * This is the key property connecting normalization to nucleus semantics.
 * The proof composes:
 *   1. normalizeIntra_of_canonical: intra-normalization is identity on
 *      canonical input.
 *   2. interCarry_idempotent_of_canonical: the ascending carry pass is
 *      idempotent because all levels are already carry-normal after
 *      the first pass (levelEval < weight at every positive level).
 *
 * Computational check: normalize X, convert back to lazy, normalize again,
 * verify representational equality.
 */
bool hz_normalize_closure_check(const hz_number_t *X) {
    hz_number_t first;
    hz_normalize(X, &first);

    /* Convert canonical back to "lazy" (identity on canonical) */
    hz_number_t second;
    hz_normalize(&first, &second);

    return hz_repr_equal(&first, &second);
}

/*
 * Inter-carry idempotence: applying the ascending carry pass twice
 * gives the same result as applying it once, on canonical input.
 *
 * (Lean: interCarry_idempotent_of_canonical)
 *
 * The ascending loop (level 1, then 2, ..., then n) guarantees that
 * after one pass, every positive level i satisfies:
 *   levelEval(X_i) < weight(i)
 * so the second pass finds nothing to carry (all quotients are 0).
 */

/*
 * Carry-normal form check: verify that every positive level satisfies
 * levelEval < weight after normalization.
 *
 * (Lean: interCarryLoop_small)
 */
bool hz_is_carry_normal(const hz_number_t *X) {
    for (uint32_t i = 1; i < HZ_MAX_LEVELS; i++) {
        uint64_t le = hz_lazy_eval_fib(X->levels[i].indices,
                                       X->levels[i].count);
        uint64_t wi = hz_weight(i);
        if (le >= wi) return false;
    }
    return true;
}

/*
 * Below-threshold identity check: if levelEval(X_i) < weight(i),
 * then carryAt(i, X) = X (no carry needed).
 *
 * (Lean: carryAt_eq_self_of_small)
 */
bool hz_carry_noop_check(uint32_t i, const hz_number_t *X) {
    uint64_t le = hz_lazy_eval_fib(X->levels[i].indices,
                                   X->levels[i].count);
    uint64_t wi = hz_weight(i);
    if (le >= wi) return false;  /* not below threshold */

    hz_number_t copy = *X;
    hz_carry_at(i, &copy);
    return hz_repr_equal(X, &copy);
}
