"""Exact-arithmetic tests for points and vectors. Oracles are hand-derived from the geometry defined in FreeRouting's ``geometry/planar/{IntPoint,IntVector,RationalPoint,RationalVector}.java``. Emphasis is on exactness: collinearity, large coordinates that would overflow a Java ``long``, and rational equality. """ from __future__ import annotations from freeroute.geometry import ( IntPoint, IntVector, RationalPoint, RationalVector, Side, Signum, point, rational_point, ) # --- IntVector determinant / side_of ---------------------------------------- def test_determinant_exact(): # determinant of (1,0),(0,1) = 1 assert IntVector(1, 0).determinant(IntVector(0, 1)) == 1 assert IntVector(0, 1).determinant(IntVector(1, 0)) == -1 assert IntVector(2, 3).determinant(IntVector(4, 6)) == 0 # parallel def test_determinant_exact_beyond_java_long(): # 10**12 * 10**12 = 10**24 overflows a Java long (~9.2e18); Python int is exact. a = IntVector(10**12, 0) b = IntVector(0, 10**12) assert a.determinant(b) == 10**24 def test_side_of_collinear_left_right(): # line from origin toward (1,0): +y is left, -y is right, on axis collinear. right_dir = IntVector(1, 0) assert IntVector(0, 1).side_of(right_dir) == Side.ON_THE_LEFT assert IntVector(0, -1).side_of(right_dir) == Side.ON_THE_RIGHT assert IntVector(5, 0).side_of(right_dir) == Side.COLLINEAR def test_side_of_exact_near_collinear(): # (10**9, 10**9) vs (10**9, 10**9 + 1): a hair off collinear, exact int detects it. base = IntVector(10**9, 10**9) assert IntVector(10**9, 10**9 + 1).side_of(base) != Side.COLLINEAR assert IntVector(2 * 10**9, 2 * 10**9).side_of(base) == Side.COLLINEAR def test_vector_add_and_negate(): assert IntVector(1, 2).add(IntVector(3, 4)) == IntVector(4, 6) assert IntVector(1, 2).negate() == IntVector(-1, -2) def test_turn_90_degree(): v = IntVector(1, 0) assert v.turn_90_degree(1) == IntVector(0, 1) assert v.turn_90_degree(2) == IntVector(-1, 0) assert v.turn_90_degree(3) == IntVector(0, -1) assert v.turn_90_degree(4) == IntVector(1, 0) assert v.turn_90_degree(-1) == IntVector(0, -1) def test_projection_signum(): assert IntVector(1, 0).projection(IntVector(1, 0)) == Signum.POSITIVE assert IntVector(1, 0).projection(IntVector(-1, 0)) == Signum.NEGATIVE assert IntVector(1, 0).projection(IntVector(0, 1)) == Signum.ZERO def test_orthogonal_diagonal(): assert IntVector(3, 0).is_orthogonal() assert IntVector(0, -4).is_orthogonal() assert IntVector(2, 2).is_diagonal() assert IntVector(-5, 5).is_diagonal() assert IntVector(1, 2).is_multiple_of_45_degree() is False # --- IntPoint --------------------------------------------------------------- def test_point_difference_and_translate(): p = IntPoint(3, 4) q = IntPoint(1, 1) d = p.difference_by(q) assert isinstance(d, IntVector) assert d == IntVector(2, 3) assert q.translate_by(d) == p def test_point_side_of_directed_line(): p0 = IntPoint(0, 0) p1 = IntPoint(2, 2) assert IntPoint(1, 1).side_of(p0, p1) == Side.COLLINEAR assert IntPoint(0, 1).side_of(p0, p1) == Side.ON_THE_LEFT assert IntPoint(1, 0).side_of(p0, p1) == Side.ON_THE_RIGHT def test_point_compare(): assert IntPoint(1, 5).compare_x(IntPoint(3, 2)) == -1 assert IntPoint(3, 5).compare_x(IntPoint(3, 2)) == 0 assert IntPoint(3, 5).compare_y(IntPoint(3, 2)) == 1 assert IntPoint(3, 2).compare_x_y(IntPoint(3, 5)) == -1 # x tie, y smaller def test_point_turn_90_around_pole(): pole = IntPoint(1, 1) # (2,1) rotated 90 about (1,1) -> (1,2) assert IntPoint(2, 1).turn_90_degree(1, pole) == IntPoint(1, 2) # --- RationalPoint / factory ------------------------------------------------ def test_rational_point_equality_scale_invariant(): assert RationalPoint(3, 2, 2) == RationalPoint(6, 4, 4) assert RationalPoint(3, 2, 2) != RationalPoint(3, 2, 3) def test_rational_point_negative_denominator_normalized_via_factory(): p = rational_point(3, 2, -2) # -> (−3/−2)=... factory flips sign -> (-3,-2,2) assert isinstance(p, RationalPoint) assert p == RationalPoint(-3, -2, 2) def test_rational_point_reduces_to_int_when_divisible(): p = rational_point(6, 4, 2) assert isinstance(p, IntPoint) assert p == IntPoint(3, 2) def test_rational_point_infinite(): inf = rational_point(1, 1, 0) assert isinstance(inf, RationalPoint) assert inf.is_infinite() def test_rational_point_contained_in_box_exact(): from freeroute.geometry import IntBox box = IntBox(0, 0, 2, 2) assert RationalPoint(3, 2, 2).is_contained_in(box) # (1.5, 1.0) inside assert not RationalPoint(5, 2, 2).is_contained_in(box) # (2.5, 1.0) outside def test_int_and_rational_difference_promotes(): p = IntPoint(2, 2) q = RationalPoint(3, 2, 2) # (1.5, 1.0) d = p.difference_by(q) assert isinstance(d, RationalVector) # (2,2) - (1.5,1.0) = (0.5, 1.0) == (1,2,2) assert d == RationalVector(1, 2, 2) def test_factory_point_is_intpoint_even_for_huge_coords(): p = point(10**15, -(10**15)) assert isinstance(p, IntPoint) assert (p.x, p.y) == (10**15, -(10**15))