from __future__ import annotations

import random
random.seed(0)
import pytest

from safegcd import divstep, divstep_ext


# --- 你原本的三個 sanity checks ---

def test_even_g():
    assert divstep(1, 7, 10) == (3, 7, 5)

def test_odd_delta_neg():
    assert divstep(-1, 7, 9) == (1, 7, 8)

def test_odd_delta_nonneg():
    assert divstep(0, 7, 9) == (2, 9, 1)


# --- 更多：邊界值 / 確認分支條件 ---

def test_g_zero_is_even_branch():
    # g=0 => even branch, g>>1 stays 0
    assert divstep(5, 123, 0) == (7, 123, 0)

def test_g_one_odd_delta_neg():
    # g=1 (odd), delta<0 => (g+f)>>1
    assert divstep(-3, 7, 1) == (-1, 7, (1 + 7) >> 1)

def test_g_one_odd_delta_nonneg():
    # g=1 (odd), delta>=0 => (g-f)>>1
    assert divstep(2, 7, 1) == (0, 1, (1 - 7) >> 1)

def test_negative_g_even_branch():
    # Python >> is arithmetic shift
    assert divstep(0, 7, -10) == (2, 7, (-10) >> 1)

def test_negative_g_odd_branch_delta_nonneg():
    assert divstep(0, 7, -9) == (2, -9, (-9 - 7) >> 1)

def test_delta_boundary_zero_goes_to_nonneg_branch():
    # delta==0 should take delta>=0 branch when g is odd
    assert divstep(0, 5, 3) == (2, 3, (3 - 5) >> 1)


# --- 更多：性質測試（不看固定例子，看 invariants） ---

def test_even_branch_properties_random():
    # 如果 g 是偶數：new_f==f, new_delta==delta+2, new_g==g>>1
    for _ in range(1000):
        delta = random.randint(-1000, 1000)
        f = random.randint(-10**9, 10**9)
        g = random.randint(-10**9, 10**9)
        g = g & ~1  # force even

        nd, nf, ng = divstep(delta, f, g)
        assert nf == f
        assert nd == delta + 2
        assert ng == (g >> 1)

def test_odd_delta_neg_branch_properties_random():
    # g odd, delta<0：new_f==f, new_delta==delta+2, new_g==(g+f)>>1
    for _ in range(1000):
        delta = random.randint(-1000, -1)  # strictly negative
        f = random.randint(-10**9, 10**9)
        g = random.randint(-10**9, 10**9) | 1  # force odd

        nd, nf, ng = divstep(delta, f, g)
        assert nf == f
        assert nd == delta + 2
        assert ng == ((g + f) >> 1)

def test_odd_delta_nonneg_branch_properties_random():
    # g odd, delta>=0：new_f==g, new_delta==2-delta, new_g==(g-f)>>1
    for _ in range(1000):
        delta = random.randint(0, 1000)
        f = random.randint(-10**9, 10**9)
        g = random.randint(-10**9, 10**9) | 1  # force odd

        nd, nf, ng = divstep(delta, f, g)
        assert nf == g
        assert nd == 2 - delta
        assert ng == ((g - f) >> 1)


# --- ext：core state 必須一致 + 模域輸出範圍 ---

def test_divstep_ext_matches_divstep_on_core_state_random():
    for _ in range(2000):
        delta = random.randint(-2000, 2000)
        f = random.randint(-10**12, 10**12)
        g = random.randint(-10**12, 10**12)

        u = random.randint(-10**12, 10**12)
        v = random.randint(-10**12, 10**12)
        r = random.randint(-10**12, 10**12)
        s = random.randint(-10**12, 10**12)

        d1, f1, g1 = divstep(delta, f, g)
        d2, f2, g2, *_ = divstep_ext(delta, f, g, u, v, r, s, field_char=0)
        assert (d1, f1, g1) == (d2, f2, g2)

def test_divstep_ext_mod_outputs_in_range():
    p = 101  # odd modulus so inv2 exists
    for _ in range(2000):
        delta = random.randint(-2000, 2000)
        f = random.randint(-10**12, 10**12)
        g = random.randint(-10**12, 10**12)

        u = random.randint(-10**12, 10**12)
        v = random.randint(-10**12, 10**12)
        r = random.randint(-10**12, 10**12)
        s = random.randint(-10**12, 10**12)

        out = divstep_ext(delta, f, g, u, v, r, s, field_char=p)
        new_r = out[5]
        new_s = out[6]
        assert 0 <= new_r < p
        assert 0 <= new_s < p


# --- ext：模域分支的「等價性」測試（用代表元比對） ---

def test_divstep_ext_field_matches_integer_when_even_numerators():
    # 這個測試很關鍵：在「可整除 2」的情況下，
    # (x/2) mod p 應該等於 x*inv2 mod p
    p = 101
    inv2 = pow(2, -1, p)

    def half_int(x: int) -> int:
        # exact half
        return x // 2

    for _ in range(2000):
        # 讓 r,s,u,v 都是偶數，確保 (r±u)/2 是整數
        delta = random.randint(-2000, 2000)
        f = random.randint(-10**6, 10**6)
        g = random.randint(-10**6, 10**6)
        u = random.randint(-10**6, 10**6) * 2
        v = random.randint(-10**6, 10**6) * 2
        r = random.randint(-10**6, 10**6) * 2
        s = random.randint(-10**6, 10**6) * 2

        # 整數分支（用 field_char=0）
        outZ = divstep_ext(delta, f, g, u, v, r, s, field_char=0)
        # 模域分支
        outF = divstep_ext(delta, f, g, u, v, r, s, field_char=p)

        # 比對 new_r/new_s：把整數結果 mod p，應等於模域結果
        assert (outZ[5] % p) == outF[5]
        assert (outZ[6] % p) == outF[6]

        # 同時也可檢查 new_u/new_v/new_f/new_g/new_delta 一致性（它們不是 mod 的）
        assert outZ[0:5] == outF[0:5]


def test_divstep_ext_rejects_field_char_1():
    with pytest.raises(ValueError):
        divstep_ext(0, 1, 2, 0, 0, 0, 0, field_char=1)

def test_divstep_ext_rejects_even_modulus():
    # pow(2, -1, p) 對偶數 modulus 會 ValueError
    with pytest.raises(ValueError):
        divstep_ext(0, 1, 2, 0, 0, 0, 0, field_char=100)
