#!/usr/bin/env python3
"""Precompute mpmath O3 vectors for engineering.process.guard_band.

GB = k·u (k defaults to 2). Optional A_USL = USL−GB, A_LSL = LSL+GB.
Not ILAC G8 risk tables. 80 dps, round-to-nearest binary64.

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

import json
import math
import shutil
from pathlib import Path

import mpmath as mp

DPS = 80
GENERATOR_ID = "guard-band-mpmath-o3"
GENERATOR_VERSION = "1.0.0"
SEED = "20260916.guard-band-o3"


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


def gb(u, k=2, usl=None, lsl=None) -> dict:
    u = mp.mpf(u)
    k = mp.mpf(k)
    band = k * u
    out = {
        "GB_f64": f64(band),
        "GB_decimal": mp.nstr(band, 40, strip_zeros=False),
        "k_f64": f64(k),
        "u_f64": f64(u),
    }
    if usl is not None:
        a = mp.mpf(usl) - band
        out["accept_usl_f64"] = f64(a)
        out["accept_usl_decimal"] = mp.nstr(a, 40, strip_zeros=False)
    if lsl is not None:
        a = mp.mpf(lsl) + band
        out["accept_lsl_f64"] = f64(a)
        out["accept_lsl_decimal"] = mp.nstr(a, 40, strip_zeros=False)
    return out


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


def vectors() -> list[dict]:
    return [
        row("o3-default", {"u": 0.5}, gb("0.5"), kind="default-k"),
        row("o3-k2", {"u": 0.5, "k": 2}, gb("0.5", 2), kind="explicit-k"),
        row("o3-spec", {"u": 0.5, "k": 2, "usl": 12, "lsl": 0}, gb("0.5", 2, 12, 0), kind="two-sided"),
        row("o3-usl", {"u": 0.5, "k": 2, "usl": 12}, gb("0.5", 2, 12, None), kind="usl-only"),
        row("o3-lsl", {"u": 0.5, "k": 2, "lsl": 0}, gb("0.5", 2, None, 0), kind="lsl-only"),
        row("o3-k3", {"u": 1, "k": 3}, gb(1, 3), kind="explicit-k"),
        row("o3-zero-u", {"u": 0, "k": 2, "usl": 12, "lsl": 0}, gb(0, 2, 12, 0), kind="two-sided"),
        row("o3-wide", {"u": 0.25, "k": 2, "usl": 10, "lsl": -10}, gb("0.25", 2, 10, -10), kind="two-sided"),
    ]


def main() -> None:
    mp.mp.dps = DPS
    here = Path(__file__).resolve().parent
    table = {
        "family": "guard_band",
        "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": "Simple GB=k·u only. Independent of Node Math and the IUT. ILAC G8 risk is not tabulated.",
        "vectors": vectors(),
    }
    dest = here / "guard-band-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 / "guard-band-o3-tables.json")
    shutil.copy2(Path(__file__), public / "generate-guard-band-o3.py")
    print(f"wrote {dest} ({len(table['vectors'])} vectors)")


if __name__ == "__main__":
    main()
