| """Solver unit tests.""" |
|
|
| from __future__ import annotations |
|
|
| import math |
|
|
| import pytest |
|
|
| from helpers import load_fixture |
|
|
| from statement_to_tikz.ir import SolveMode |
| from statement_to_tikz.pipeline import solve_with_fallback |
| from statement_to_tikz.solve import evaluate_residuals, solve_geometry |
|
|
|
|
| @pytest.mark.parametrize( |
| "fixture", |
| [ |
| "equilateral.json", |
| "isosceles_median.json", |
| "right_altitude.json", |
| "parallelogram.json", |
| "circumcenter.json", |
| "angle_bisector.json", |
| ], |
| ) |
| def test_exact_fixtures(fixture: str) -> None: |
| ir, expected = load_fixture(fixture) |
| assert expected == "exact" |
| scene = solve_geometry(ir) |
| assert scene.mode == SolveMode.exact, scene.message |
| assert scene.max_residual <= 1e-4 |
|
|
|
|
| def test_equilateral_side_lengths() -> None: |
| ir, _ = load_fixture("equilateral.json") |
| scene = solve_geometry(ir) |
| a = scene.coordinates["A"] |
| b = scene.coordinates["B"] |
| c = scene.coordinates["C"] |
|
|
| def dist(p, q): |
| return math.hypot(p[0] - q[0], p[1] - q[1]) |
|
|
| assert abs(dist(a, b) - 1.0) < 1e-3 |
| assert abs(dist(b, c) - 1.0) < 1e-3 |
| assert abs(dist(c, a) - 1.0) < 1e-3 |
|
|
|
|
| def test_midpoint_property() -> None: |
| ir, _ = load_fixture("isosceles_median.json") |
| scene = solve_geometry(ir) |
| b, c, d = (scene.coordinates[k] for k in ("B", "C", "D")) |
| assert abs(d[0] - 0.5 * (b[0] + c[0])) < 1e-3 |
| assert abs(d[1] - 0.5 * (b[1] + c[1])) < 1e-3 |
| a = scene.coordinates["A"] |
| |
| area = abs((b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0])) |
| assert area > 0.5 |
|
|
|
|
| def test_underconstrained_becomes_schematic() -> None: |
| ir, expected = load_fixture("underconstrained_triangle.json") |
| assert expected == "schematic" |
| scene = solve_with_fallback(ir) |
| assert scene.mode == SolveMode.schematic |
| assert set(scene.coordinates) >= {"A", "B", "C"} |
|
|
|
|
| def test_conflicting_falls_back_schematic() -> None: |
| ir, expected = load_fixture("conflicting_lengths.json") |
| exact = solve_geometry(ir) |
| assert exact.mode != SolveMode.exact |
| scene = solve_with_fallback(ir) |
| assert scene.mode == SolveMode.schematic |
| |
| res = evaluate_residuals(ir, scene.coordinates) |
| assert max(res) > 0.1 |
|
|