# Tests for error cases in BigInt-related builtins and operations
# All error messages must match CPython exactly
# Uses 'in str(e)' checks since Ouros's str(e) includes the type name

# === Setup constants ===
MAX_I64 = 9223372036854775807
BIGINT = MAX_I64 + 1  # Force BigInt creation


# === hex() errors ===
try:
    hex('str')
    assert False, 'hex(str) should raise TypeError'
except TypeError as e:
    assert "'str' object cannot be interpreted as an integer" in str(e), f'hex str error: {e}'

try:
    hex(1.5)
    assert False, 'hex(float) should raise TypeError'
except TypeError as e:
    assert "'float' object cannot be interpreted as an integer" in str(e), f'hex float error: {e}'

try:
    hex([])
    assert False, 'hex(list) should raise TypeError'
except TypeError as e:
    assert "'list' object cannot be interpreted as an integer" in str(e), f'hex list error: {e}'


# === bin() errors ===
try:
    bin('str')
    assert False, 'bin(str) should raise TypeError'
except TypeError as e:
    assert "'str' object cannot be interpreted as an integer" in str(e), f'bin str error: {e}'

try:
    bin(1.5)
    assert False, 'bin(float) should raise TypeError'
except TypeError as e:
    assert "'float' object cannot be interpreted as an integer" in str(e), f'bin float error: {e}'

try:
    bin({})
    assert False, 'bin(dict) should raise TypeError'
except TypeError as e:
    assert "'dict' object cannot be interpreted as an integer" in str(e), f'bin dict error: {e}'


# === oct() errors ===
try:
    oct('str')
    assert False, 'oct(str) should raise TypeError'
except TypeError as e:
    assert "'str' object cannot be interpreted as an integer" in str(e), f'oct str error: {e}'

try:
    oct(1.5)
    assert False, 'oct(float) should raise TypeError'
except TypeError as e:
    assert "'float' object cannot be interpreted as an integer" in str(e), f'oct float error: {e}'

try:
    oct((1, 2))
    assert False, 'oct(tuple) should raise TypeError'
except TypeError as e:
    assert "'tuple' object cannot be interpreted as an integer" in str(e), f'oct tuple error: {e}'


# === divmod() division by zero ===
try:
    divmod(10, 0)
    assert False, 'divmod(int, 0) should raise ZeroDivisionError'
except ZeroDivisionError as e:
    assert 'division by zero' in str(e), f'divmod int/0 error: {e}'

try:
    divmod(BIGINT, 0)
    assert False, 'divmod(bigint, 0) should raise ZeroDivisionError'
except ZeroDivisionError as e:
    assert 'division by zero' in str(e), f'divmod bigint/0 error: {e}'

try:
    divmod(10, BIGINT - BIGINT)  # BigInt zero
    assert False, 'divmod(int, bigint_zero) should raise ZeroDivisionError'
except ZeroDivisionError as e:
    assert 'division by zero' in str(e), f'divmod int/bigint_zero error: {e}'

try:
    divmod(BIGINT, BIGINT - BIGINT)  # BigInt / BigInt zero
    assert False, 'divmod(bigint, bigint_zero) should raise ZeroDivisionError'
except ZeroDivisionError as e:
    assert 'division by zero' in str(e), f'divmod bigint/bigint_zero error: {e}'

try:
    divmod(10.0, 0.0)
    assert False, 'divmod(float, 0.0) should raise ZeroDivisionError'
except ZeroDivisionError as e:
    assert 'division by zero' in str(e), f'divmod float/0.0 error: {e}'

try:
    divmod(10, 0.0)
    assert False, 'divmod(int, 0.0) should raise ZeroDivisionError'
except ZeroDivisionError as e:
    assert 'division by zero' in str(e), f'divmod int/0.0 error: {e}'

try:
    divmod(10.0, 0)
    assert False, 'divmod(float, 0) should raise ZeroDivisionError'
except ZeroDivisionError as e:
    assert 'division by zero' in str(e), f'divmod float/0 error: {e}'


# === divmod() type errors ===
try:
    divmod('a', 5)
    assert False, 'divmod(str, int) should raise TypeError'
except TypeError as e:
    assert "unsupported operand type(s) for divmod(): 'str' and 'int'" in str(e), f'divmod str/int error: {e}'

try:
    divmod(5, 'a')
    assert False, 'divmod(int, str) should raise TypeError'
except TypeError as e:
    assert "unsupported operand type(s) for divmod(): 'int' and 'str'" in str(e), f'divmod int/str error: {e}'

try:
    divmod([], 5)
    assert False, 'divmod(list, int) should raise TypeError'
except TypeError as e:
    assert "unsupported operand type(s) for divmod(): 'list' and 'int'" in str(e), f'divmod list/int error: {e}'

try:
    divmod(BIGINT, 'a')
    assert False, 'divmod(bigint, str) should raise TypeError'
except TypeError as e:
    assert "unsupported operand type(s) for divmod(): 'int' and 'str'" in str(e), f'divmod bigint/str error: {e}'


# === pow() zero to negative power ===
try:
    pow(0.0, -1)
    assert False, 'pow(0.0, -1) should raise ZeroDivisionError'
except ZeroDivisionError as e:
    assert 'zero to a negative power' in str(e), f'pow 0.0/-1 error: {e}'

try:
    pow(0, -1.0)
    assert False, 'pow(0, -1.0) should raise ZeroDivisionError'
except ZeroDivisionError as e:
    assert 'zero to a negative power' in str(e), f'pow 0/-1.0 error: {e}'

try:
    pow(0.0, -2.0)
    assert False, 'pow(0.0, -2.0) should raise ZeroDivisionError'
except ZeroDivisionError as e:
    assert 'zero to a negative power' in str(e), f'pow 0.0/-2.0 error: {e}'


# === pow() with modulo errors ===
try:
    pow(2, 10, 0)
    assert False, 'pow(2, 10, 0) should raise ValueError'
except ValueError as e:
    assert 'pow() 3rd argument cannot be 0' in str(e), f'pow mod=0 error: {e}'

# Note: pow(2, -1, 5) computes modular inverse in Python 3.8+, not an error
# But pow(2, -1, 4) raises an error because 2 is not invertible mod 4
try:
    pow(2, -1, 4)  # gcd(2, 4) != 1, no inverse exists
    assert False, 'pow(2, -1, 4) should raise ValueError'
except ValueError as e:
    # CPython: "base is not invertible for the given modulus"
    # Ouros: "pow() 2nd argument cannot be negative when 3rd argument specified"
    # Accept either message since Ouros doesn't support modular inverse yet
    assert 'not invertible' in str(e) or 'cannot be negative' in str(e), f'pow non-invertible error: {e}'

try:
    pow(2.0, 2, 5)
    assert False, 'pow(float, int, int) should raise TypeError'
except TypeError as e:
    assert 'pow() 3rd argument not allowed unless all arguments are integers' in str(e), f'pow float mod error: {e}'

try:
    pow(2, 2.0, 5)
    assert False, 'pow(int, float, int) should raise TypeError'
except TypeError as e:
    assert 'pow() 3rd argument not allowed unless all arguments are integers' in str(e), f'pow float exp mod error: {e}'

try:
    pow(2, 2, 5.0)
    assert False, 'pow(int, int, float) should raise TypeError'
except TypeError as e:
    assert 'pow() 3rd argument not allowed unless all arguments are integers' in str(e), f'pow float mod2 error: {e}'


# === abs() type errors ===
try:
    abs('str')
    assert False, 'abs(str) should raise TypeError'
except TypeError as e:
    assert "bad operand type for abs(): 'str'" in str(e), f'abs str error: {e}'

try:
    abs([])
    assert False, 'abs(list) should raise TypeError'
except TypeError as e:
    assert "bad operand type for abs(): 'list'" in str(e), f'abs list error: {e}'

try:
    abs({})
    assert False, 'abs(dict) should raise TypeError'
except TypeError as e:
    assert "bad operand type for abs(): 'dict'" in str(e), f'abs dict error: {e}'


# === pow() type errors (** operator) ===
try:
    5 ** 'x'
    assert False, '5 ** str should raise TypeError'
except TypeError as e:
    assert "unsupported operand type(s) for ** or pow(): 'int' and 'str'" in str(e), f'int ** str error: {e}'

try:
    'x' ** 5
    assert False, 'str ** int should raise TypeError'
except TypeError as e:
    assert "unsupported operand type(s) for ** or pow(): 'str' and 'int'" in str(e), f'str ** int error: {e}'

try:
    BIGINT ** 'x'
    assert False, 'bigint ** str should raise TypeError'
except TypeError as e:
    assert "unsupported operand type(s) for ** or pow(): 'int' and 'str'" in str(e), f'bigint ** str error: {e}'


# === Division by zero with BigInt ===
try:
    BIGINT // 0
    assert False, 'bigint // 0 should raise ZeroDivisionError'
except ZeroDivisionError as e:
    assert 'division by zero' in str(e), f'bigint floordiv error: {e}'

try:
    BIGINT % 0
    assert False, 'bigint % 0 should raise ZeroDivisionError'
except ZeroDivisionError as e:
    assert 'division by zero' in str(e), f'bigint mod error: {e}'

try:
    10 // (BIGINT - BIGINT)  # int // BigInt zero
    assert False, 'int // bigint_zero should raise ZeroDivisionError'
except ZeroDivisionError as e:
    assert 'division by zero' in str(e), f'int floordiv bigint_zero error: {e}'

try:
    10 % (BIGINT - BIGINT)  # int % BigInt zero
    assert False, 'int % bigint_zero should raise ZeroDivisionError'
except ZeroDivisionError as e:
    assert 'division by zero' in str(e), f'int mod bigint_zero error: {e}'
