"""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"] # non-degenerate: A not on BC 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 # residuals remain large for conflicting lengths res = evaluate_residuals(ir, scene.coordinates) assert max(res) > 0.1