"""
Practice: measuring a LoRA fine-tune. LoRA's size (lora.py), the lab's field-by-field grading
(tickets.score), the made-up order number check (01_prompt_first.py, Exercise 2) and the paired
test that says whether 99% really beats 96% (stats.mcnemar_p).

Fill in the TODOs, then run the tests:
    pytest test_finetune_practice.py        (locally)
or press "Run tests" on the chapter page.
"""
import math

FIELDS = ["intent", "order_id", "sku", "urgency"]
NULLS = {None, "", "null", "none", "n/a", "na"}


def lora_params(shapes: list[tuple[int, int]], r: int, layers: int) -> int:
    """Trainable numbers LoRA adds. Each wrapped layer of shape (d_in, d_out) gets A (d_in × r) and
    B (r × d_out). `shapes` is the list of wrapped layers in one block; the model has `layers` blocks.

    TODO 1
    """
    return 0


def score(pred: dict | None, gold: dict) -> dict:
    """Grade one answer field by field: {"intent": bool, "order_id": bool, "sku": bool, "urgency": bool, "all": bool}.
    Before comparing, normalize each value: strip spaces; a value in NULLS (any case) means None;
    order_id and sku compare uppercased, intent and urgency lowercased. A missing field is wrong.
    A missing answer (None) gets every field wrong. "all" is True only if all four are right.

    TODO 2
    """
    return {**{f: False for f in FIELDS}, "all": False}


def invented_rate(preds: list[dict | None], golds: list[dict]) -> float:
    """Among the emails whose right order_id is null, the share where the model gave an order
    number anyway (any value that isn't null after normalizing). Return 0.0 if there are none.

    TODO 3
    """
    return 0.0


def mcnemar_p(a: list[bool], b: list[bool]) -> float:
    """Exact two-sided McNemar test: is model B different from model A on the same items?
    Count only the items where they disagree: a_only (A right, B wrong) and b_only (B right, A wrong).
    With n = a_only + b_only and k = min(a_only, b_only), p = min(1, 2 * sum(C(n, i) for i in 0..k) / 2**n).
    If they never disagree, p = 1.0.

    TODO 4
    """
    return 1.0
