Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 102 additions & 30 deletions Lib/test/test_complex.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import unittest
import sys
from test import support
from test.support import os_helper
from test.test_grammar import (VALID_UNDERSCORE_LITERALS,
INVALID_UNDERSCORE_LITERALS)

Expand All @@ -12,6 +12,14 @@
NAN = float("nan")
# These tests ensure that complex math does the right thing

ZERO_DIVISION = (
(1+1j, 0+0j),
(1+1j, 0.0),
(1+1j, 0),
(1.0, 0+0j),
(1, 0+0j),
)

class ComplexTest(unittest.TestCase):

def assertAlmostEqual(self, a, b):
Expand Down Expand Up @@ -100,20 +108,34 @@ def test_truediv(self):
self.check_div(complex(random(), random()),
complex(random(), random()))

self.assertRaises(ZeroDivisionError, complex.__truediv__, 1+1j, 0+0j)
self.assertRaises(OverflowError, pow, 1e200+1j, 1e200+1j)

self.assertAlmostEqual(complex.__truediv__(2+0j, 1+1j), 1-1j)
self.assertRaises(ZeroDivisionError, complex.__truediv__, 1+1j, 0+0j)

for denom_real, denom_imag in [(0, NAN), (NAN, 0), (NAN, NAN)]:
z = complex(0, 0) / complex(denom_real, denom_imag)
self.assertTrue(isnan(z.real))
self.assertTrue(isnan(z.imag))

def test_truediv_zero_division(self):
for a, b in ZERO_DIVISION:
with self.assertRaises(ZeroDivisionError):
a / b

def test_floordiv(self):
self.assertRaises(TypeError, complex.__floordiv__, 3+0j, 1.5+0j)
self.assertRaises(TypeError, complex.__floordiv__, 3+0j, 0+0j)
with self.assertRaises(TypeError):
(1+1j) // (1+0j)
with self.assertRaises(TypeError):
(1+1j) // 1.0
with self.assertRaises(TypeError):
(1+1j) // 1
with self.assertRaises(TypeError):
1.0 // (1+0j)
with self.assertRaises(TypeError):
1 // (1+0j)

def test_floordiv_zero_division(self):
for a, b in ZERO_DIVISION:
with self.assertRaises(TypeError):
a // b

def test_richcompare(self):
self.assertIs(complex.__eq__(1+1j, 1<<10000), False)
Expand Down Expand Up @@ -162,13 +184,32 @@ def check(n, deltas, is_equal, imag = 0.0):

def test_mod(self):
# % is no longer supported on complex numbers
self.assertRaises(TypeError, (1+1j).__mod__, 0+0j)
self.assertRaises(TypeError, lambda: (3.33+4.43j) % 0)
self.assertRaises(TypeError, (1+1j).__mod__, 4.3j)
with self.assertRaises(TypeError):
(1+1j) % (1+0j)
with self.assertRaises(TypeError):
(1+1j) % 1.0
with self.assertRaises(TypeError):
(1+1j) % 1
with self.assertRaises(TypeError):
1.0 % (1+0j)
with self.assertRaises(TypeError):
1 % (1+0j)

def test_mod_zero_division(self):
for a, b in ZERO_DIVISION:
with self.assertRaises(TypeError):
a % b

def test_divmod(self):
self.assertRaises(TypeError, divmod, 1+1j, 1+0j)
self.assertRaises(TypeError, divmod, 1+1j, 0+0j)
self.assertRaises(TypeError, divmod, 1+1j, 1.0)
self.assertRaises(TypeError, divmod, 1+1j, 1)
self.assertRaises(TypeError, divmod, 1.0, 1+0j)
self.assertRaises(TypeError, divmod, 1, 1+0j)

def test_divmod_zero_division(self):
for a, b in ZERO_DIVISION:
self.assertRaises(TypeError, divmod, a, b)

def test_pow(self):
self.assertAlmostEqual(pow(1+1j, 0+0j), 1.0)
Expand All @@ -177,6 +218,7 @@ def test_pow(self):
self.assertAlmostEqual(pow(1j, -1), 1/1j)
self.assertAlmostEqual(pow(1j, 200), 1)
self.assertRaises(ValueError, pow, 1+1j, 1+1j, 1+1j)
self.assertRaises(OverflowError, pow, 1e200+1j, 1e200+1j)

a = 3.33+4.43j
self.assertEqual(a ** 0j, 1)
Expand Down Expand Up @@ -209,6 +251,54 @@ def test_pow(self):
b = 5.1+2.3j
self.assertRaises(ValueError, pow, a, b, 0)

# Check some boundary conditions; some of these used to invoke
# undefined behaviour (https://bugs.python.org/issue44698). We're
# not actually checking the results of these operations, just making
# sure they don't crash (for example when using clang's
# UndefinedBehaviourSanitizer).
values = (sys.maxsize, sys.maxsize+1, sys.maxsize-1,
-sys.maxsize, -sys.maxsize+1, -sys.maxsize+1)
for real in values:
for imag in values:
with self.subTest(real=real, imag=imag):
c = complex(real, imag)
try:
c ** real
except OverflowError:
pass
try:
c ** c
except OverflowError:
pass

def test_pow_with_small_integer_exponents(self):
# Check that small integer exponents are handled identically
# regardless of their type.
values = [
complex(5.0, 12.0),
complex(5.0e100, 12.0e100),
complex(-4.0, INF),
complex(INF, 0.0),
]
exponents = [-19, -5, -3, -2, -1, 0, 1, 2, 3, 5, 19]
for value in values:
for exponent in exponents:
with self.subTest(value=value, exponent=exponent):
try:
int_pow = value**exponent
except OverflowError:
int_pow = "overflow"
try:
float_pow = value**float(exponent)
except OverflowError:
float_pow = "overflow"
try:
complex_pow = value**complex(exponent)
except OverflowError:
complex_pow = "overflow"
self.assertEqual(str(float_pow), str(int_pow))
self.assertEqual(str(complex_pow), str(int_pow))

def test_boolcontext(self):
for i in range(100):
self.assertTrue(complex(random() + 1e-6, random() + 1e-6))
Expand Down Expand Up @@ -509,22 +599,6 @@ def test(v, expected, test_fn=self.assertEqual):
def test_neg(self):
self.assertEqual(-(1+6j), -1-6j)

def test_file(self):
a = 3.33+4.43j
b = 5.1+2.3j

fo = None
try:
fo = open(os_helper.TESTFN, "w")
print(a, b, file=fo)
fo.close()
fo = open(os_helper.TESTFN, "r")
self.assertEqual(fo.read(), ("%s %s\n" % (a, b)))
finally:
if (fo is not None) and (not fo.closed):
fo.close()
os_helper.unlink(os_helper.TESTFN)

def test_getnewargs(self):
self.assertEqual((1+2j).__getnewargs__(), (1.0, 2.0))
self.assertEqual((1-2j).__getnewargs__(), (1.0, -2.0))
Expand Down Expand Up @@ -715,8 +789,6 @@ def test_format(self):
self.assertEqual(format(complex(INF, 1), 'F'), 'INF+1.000000j')
self.assertEqual(format(complex(INF, -1), 'F'), 'INF-1.000000j')

def test_main():
support.run_unittest(ComplexTest)

if __name__ == "__main__":
test_main()
unittest.main()
29 changes: 0 additions & 29 deletions vm/src/builtins/complex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use crate::{
use num_complex::Complex64;
use num_traits::Zero;
use rustpython_common::{float_ops, hash};
use std::convert::Infallible as Never;

/// Create a complex number from a real part and an optional imaginary part.
///
Expand Down Expand Up @@ -272,16 +271,6 @@ impl PyComplex {
self.value.conj()
}

#[pymethod(magic)]
fn float(&self, vm: &VirtualMachine) -> PyResult<Never> {
Err(vm.new_type_error(String::from("Can't convert complex to float")))
}

#[pymethod(magic)]
fn int(&self, vm: &VirtualMachine) -> PyResult<Never> {
Err(vm.new_type_error(String::from("Can't convert complex to int")))
}

#[pymethod(name = "__rmul__")]
#[pymethod(magic)]
fn mul(
Expand Down Expand Up @@ -310,24 +299,6 @@ impl PyComplex {
self.op(other, |a, b| inner_div(b, a, vm), vm)
}

#[pymethod(name = "__mod__")]
#[pymethod(name = "__rmod__")]
fn mod_(&self, _other: PyObjectRef, vm: &VirtualMachine) -> PyResult<Never> {
Err(vm.new_type_error("can't mod complex numbers.".to_owned()))
}

#[pymethod(name = "__rfloordiv__")]
#[pymethod(magic)]
fn floordiv(&self, _other: PyObjectRef, vm: &VirtualMachine) -> PyResult<Never> {
Err(vm.new_type_error("can't take floor of complex number.".to_owned()))
}

#[pymethod(name = "__rdivmod__")]
#[pymethod(magic)]
fn divmod(&self, _other: PyObjectRef, vm: &VirtualMachine) -> PyResult<Never> {
Err(vm.new_type_error("can't take floor or mod of complex number.".to_owned()))
}

#[pymethod(magic)]
fn pos(&self) -> Complex64 {
self.value
Expand Down