#!/usr/bin/env python3
"""Reproduce the constructed score-change examples used in the paper."""

from __future__ import annotations

import argparse
import math


def measures(start: float, end: float) -> dict[str, float | None]:
    point_change = end - start
    ordinary_percent = None if start == 0 else 100 * point_change / start
    log_percent = None
    if start > 0 and end > 0:
        log_percent = 100 * math.log(end / start)
    return {
        "start": start,
        "end": end,
        "point_change": point_change,
        "ordinary_percent": ordinary_percent,
        "log_percent": log_percent,
    }


def display(value: float | None) -> str:
    return "undefined" if value is None else f"{value:.6f}"


def self_test() -> None:
    cases = [
        (40, 50, 10, 25.0, 22.314355),
        (80, 90, 10, 12.5, 11.778304),
        (50, 100, 50, 100.0, 69.314718),
        (100, 50, -50, -50.0, -69.314718),
        (0, 10, 10, None, None),
    ]
    for start, end, points, ordinary, log_value in cases:
        result = measures(start, end)
        assert math.isclose(result["point_change"], points, abs_tol=1e-12)
        if ordinary is None:
            assert result["ordinary_percent"] is None
        else:
            assert math.isclose(result["ordinary_percent"], ordinary, abs_tol=1e-6)
        if log_value is None:
            assert result["log_percent"] is None
        else:
            assert math.isclose(result["log_percent"], log_value, abs_tol=1e-6)
    assert math.isclose(
        measures(50, 100)["log_percent"],
        -measures(100, 50)["log_percent"],
        abs_tol=1e-12,
    )


def main() -> None:
    parser = argparse.ArgumentParser(
        description="Compare point, ordinary-percent and log-percent score changes."
    )
    parser.add_argument("start", nargs="?", type=float)
    parser.add_argument("end", nargs="?", type=float)
    parser.add_argument("--self-test", action="store_true")
    args = parser.parse_args()

    if args.self_test:
        self_test()
        print("self-test passed")
        return
    if args.start is None or args.end is None:
        parser.error("provide START and END, or use --self-test")

    result = measures(args.start, args.end)
    for key in ("start", "end", "point_change", "ordinary_percent", "log_percent"):
        print(f"{key}={display(result[key])}")


if __name__ == "__main__":
    main()
