"""Small arithmetic illustration for the CurveBall article, not a security tool.

Python 3.10+. No network, certificates, signatures, Windows APIs or file writes.
Run: python toy-curve.py
The JSON on stdout is the reproducible article calculation record.
"""
from __future__ import annotations

import json

Point = tuple[int, int] | None
P, A, B = 17, 2, 2
G: Point = (5, 1)


def on_curve(point: Point) -> bool:
    return point is None or (
        0 <= point[0] < P and 0 <= point[1] < P
        and (point[1] ** 2 - point[0] ** 3 - A * point[0] - B) % P == 0
    )


def add(left: Point, right: Point) -> Point:
    if not on_curve(left) or not on_curve(right):
        raise ValueError("Point is not on the fixed demonstration curve")
    if left is None:
        return right
    if right is None:
        return left
    x, y = left
    u, v = right
    if x == u and (y + v) % P == 0:
        return None
    slope = ((3 * x * x + A) * pow(2 * y, -1, P)
             if left == right else (v - y) * pow(u - x, -1, P)) % P
    result_x = (slope * slope - x - u) % P
    result = (result_x, (slope * (x - result_x) - y) % P)
    if not on_curve(result):
        raise ArithmeticError("Result left the demonstration curve")
    return result


def multiply(count: int, point: Point) -> Point:
    if count < 0 or not on_curve(point):
        raise ValueError("Invalid demonstration input")
    result: Point = None
    for _ in range(count):
        result = add(result, point)
    return result


def main() -> None:
    # All numbers, including d=7, are chosen inputs, not recovered secrets.
    d = 7
    q = multiply(d, G)
    other_g, other_d = q, 1
    curve_points = [(x, y) for x in range(P) for y in range(P)
                    if on_curve((x, y))]
    checks = {
        "curve_is_nonsingular": (4 * A ** 3 + 27 * B ** 2) % P != 0,
        "group_has_19_points_including_infinity": len(curve_points) + 1 == 19,
        "base_point_has_order_19": multiply(19, G) is None and G is not None,
        "seven_times_G_is_Q": q == (0, 6),
        "new_base_point_differs": other_g != G,
        "one_times_new_base_point_is_Q": multiply(other_d, other_g) == q,
        "one_times_original_base_point_is_not_Q": multiply(other_d, G) != q,
        "new_base_point_has_order_19": other_g is not None and multiply(19, other_g) is None,
    }
    if not all(checks.values()):
        raise AssertionError(checks)
    record = {
        "purpose": "Toy elliptic-curve arithmetic only; not a Windows vulnerability reproduction",
        "curve": {"p": P, "a": A, "b": B, "order": 19},
        "original": {"G": G, "chosen_d": d, "Q": q},
        "multiples_of_G": [{"multiple": n, "point": multiply(n, G)} for n in range(1, 20)],
        "alternative": {"G_prime": other_g, "chosen_d_prime": other_d,
                        "Q_prime": multiply(other_d, other_g)},
        "checks": checks,
    }
    print(json.dumps(record, ensure_ascii=False, indent=2))


if __name__ == "__main__":
    main()
