#!/usr/bin/env python3
"""Precompute mpmath O3 vectors for engineering.msa.gauge_rr.

AIAG compose GRR/TV/%GRR/ndc and range EV=R̄/d2. Not a full ANOVA study.
80 dps, round-to-nearest binary64.

Re-run: python3 scripts/lib/cvp/oracles/generate-gauge-rr-o3.py
"""
from __future__ import annotations

import json
import math
import shutil
from pathlib import Path

import mpmath as mp

DPS = 80
GENERATOR_ID = "gauge-rr-mpmath-o3"
GENERATOR_VERSION = "1.0.0"
SEED = "20260916.gauge-rr-o3"
D2_N2 = mp.mpf("1.128")
NDC_FACTOR = mp.mpf("1.41")


def f64(x: mp.mpf) -> float:
    return float(x)


def compose(ev, av, pv) -> dict:
    ev = mp.mpf(ev)
    av = mp.mpf(av)
    pv = mp.mpf(pv)
    grr = mp.sqrt(ev * ev + av * av)
    tv = mp.sqrt(grr * grr + pv * pv)
    pct = (100 * grr) / tv
    ndc = (NDC_FACTOR * pv) / grr
    return {
        "EV_f64": f64(ev),
        "EV_decimal": mp.nstr(ev, 40, strip_zeros=False),
        "GRR_f64": f64(grr),
        "GRR_decimal": mp.nstr(grr, 40, strip_zeros=False),
        "TV_f64": f64(tv),
        "TV_decimal": mp.nstr(tv, 40, strip_zeros=False),
        "pct_grr_f64": f64(pct),
        "pct_grr_decimal": mp.nstr(pct, 40, strip_zeros=False),
        "ndc_f64": f64(ndc),
        "ndc_decimal": mp.nstr(ndc, 40, strip_zeros=False),
    }


def row(vid: str, inputs: dict, extra: dict, *, kind: str) -> dict:
    return {"id": vid, "kind": kind, "inputs": inputs, **extra}


def vectors() -> list[dict]:
    rbar = mp.mpf("1.128")
    return [
        row("o3-3412", {"ev": 3, "av": 4, "pv": 12}, compose(3, 4, 12), kind="stated-ev"),
        row("o3-range", {"rbar": 1.128, "av": 4, "pv": 12}, compose(rbar / D2_N2, 4, 12), kind="range-ev"),
        row(
            "o3-d2",
            {"rbar": 2.256, "d2": 1.128, "av": 4, "pv": 12},
            compose(mp.mpf("2.256") / D2_N2, 4, 12),
            kind="custom-d2",
        ),
        row("o3-av0", {"ev": 3, "av": 0, "pv": 12}, compose(3, 0, 12), kind="stated-ev"),
        row("o3-pv0", {"ev": 5, "av": 12, "pv": 0}, compose(5, 12, 0), kind="pv-zero"),
        row("o3-alias", {"EV": 3, "AV": 4, "PV": 12}, compose(3, 4, 12), kind="alias"),
        row("o3-unit", {"ev": 1, "av": 0, "pv": 1}, compose(1, 0, 1), kind="stated-ev"),
        row("o3-scale", {"ev": 6, "av": 8, "pv": 24}, compose(6, 8, 24), kind="stated-ev"),
    ]


def main() -> None:
    mp.mp.dps = DPS
    here = Path(__file__).resolve().parent
    table = {
        "family": "gauge_rr",
        "generator_id": GENERATOR_ID,
        "generator_version": GENERATOR_VERSION,
        "seed": SEED,
        "mpmath_dps": DPS,
        "precision_bits": int(DPS * math.log2(10)),
        "library": f"mpmath {mp.__version__}",
        "notes": "AIAG compose only. Independent of Node Math and the IUT. Full ANOVA is not tabulated.",
        "vectors": vectors(),
    }
    dest = here / "gauge-rr-o3-tables.json"
    dest.write_text(json.dumps(table, indent=2) + "\n", encoding="utf-8")
    public = here.parents[3] / "public/developers/cvp/reproduce"
    public.mkdir(parents=True, exist_ok=True)
    shutil.copy2(dest, public / "gauge-rr-o3-tables.json")
    shutil.copy2(Path(__file__), public / "generate-gauge-rr-o3.py")
    print(f"wrote {dest} ({len(table['vectors'])} vectors)")


if __name__ == "__main__":
    main()
