#!/usr/bin/env python3
"""mpmath O3 for mechanical.statics.slip_or_tip."""
from __future__ import annotations
import json, math, shutil
from pathlib import Path
import mpmath as mp

def pick(inputs, *keys):
    for key in keys:
        if key in inputs and inputs[key] not in (None, ""):
            return mp.mpf(str(inputs[key]))
    return None


def compute(inputs):
    W = pick(inputs, "W", "weight")
    mu = pick(inputs, "mu", "mu_s")
    b = pick(inputs, "b", "width")
    d = pick(inputs, "d", "height")
    P_slip = mu * W
    P_tip = (W * b) / (2 * d)
    scale = 1 + abs(P_slip) + abs(P_tip)
    mode = "either" if abs(P_slip - P_tip) <= mp.mpf("1e-8") * scale else ("slip" if P_slip < P_tip else "tip")
    P = P_tip if mode == "tip" else P_slip
    return {"P": P, "P_slip": P_slip, "P_tip": P_tip, "mode": mode}


def row(vid, inputs, kind):
    out = compute(inputs)
    packed = {"id": vid, "kind": kind, "inputs": inputs}
    for k, v in out.items():
        if isinstance(v, str):
            packed[k] = v
        else:
            packed[f"{k}_f64"] = float(v)
            packed[f"{k}_decimal"] = mp.nstr(v, 40, strip_zeros=False)
    return packed

def main():
    mp.mp.dps = 80
    here = Path(__file__).resolve().parent
    vectors = [
        row("o3-slip", {"W": "100", "mu": "0.25", "b": "2", "d": "2"}, "slip"),
        row("o3-tip", {"W": "100", "mu": "0.6", "b": "2", "d": "2"}, "tip"),
        row("o3-either", {"W": "100", "mu": "0.5", "b": "2", "d": "2"}, "either"),
        row("o3-alias", {"weight": "80", "mu_s": "0.2", "width": "3", "height": "1.5"}, "alias"),
        row("o3-awkward", {"W": "137", "mu": "0.37", "b": "2.5", "d": "1.8"}, "awkward"),
        row("o3-small", {"W": "1", "mu": "0.1", "b": "0.4", "d": "0.2"}, "small"),
        row("o3-tall", {"W": "200", "mu": "0.3", "b": "1", "d": "4"}, "tall"),
        row("o3-wide", {"W": "50", "mu": "0.4", "b": "6", "d": "1"}, "wide"),
    ]
    table = {
        "family": "slip_or_tip",
        "generator_id": "slip-or-tip-mpmath-o3",
        "generator_version": "1.0.0",
        "seed": "20260927.slip-or-tip-o3",
        "mpmath_dps": 80,
        "precision_bits": int(80 * math.log2(10)),
        "library": f"mpmath {mp.__version__}",
        "notes": "Level surface: P_slip = μW and P_tip = W b/(2 d); P is the smaller. + O3 mpmath tabulated P.",
        "vectors": vectors,
    }
    dest = here / "slip-or-tip-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 / "slip-or-tip-o3-tables.json")
    shutil.copy2(Path(__file__), public / "generate-slip-or-tip-o3.py")
    print(f"wrote {dest} ({len(vectors)} vectors)")

if __name__ == "__main__":
    main()
