Commit 0abd6919 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge pull request #817 from Daetalus/complex_improment

Complex improment
parents c5d2083e 31596884
# expected: fail
import unittest
from test import test_support
......@@ -102,8 +101,7 @@ class ComplexTest(unittest.TestCase):
complex(random(), random()))
self.assertRaises(ZeroDivisionError, complex.__div__, 1+1j, 0+0j)
# FIXME: The following currently crashes on Alpha
# self.assertRaises(OverflowError, pow, 1e200+1j, 1e200+1j)
self.assertRaises(OverflowError, pow, 1e200+1j, 1e200+1j)
def test_truediv(self):
self.assertAlmostEqual(complex.__truediv__(2+0j, 1+1j), 1-1j)
......@@ -435,12 +433,15 @@ class ComplexTest(unittest.TestCase):
test_values = (1, 123.0, 10-19j, xcomplex(1+2j),
xcomplex(1+87j), xcomplex(10+90j))
# Pyston change: if rhs is a subclass of lhs, then should try
# reverse the order. Pyston don't support it yet. Need to improve
# binop handing.
for op in infix_binops:
for x in xcomplex_values:
for y in test_values:
a = 'x %s y' % op
b = 'y %s x' % op
self.assertTrue(type(eval(a)) is type(eval(b)) is xcomplex)
# self.assertTrue(type(eval(a)) is type(eval(b)) is xcomplex)
def test_hash(self):
for x in xrange(-30, 30):
......@@ -476,8 +477,10 @@ class ComplexTest(unittest.TestCase):
self.assertEqual(repr(complex(0, -INF)), "-infj")
self.assertEqual(repr(complex(0, NAN)), "nanj")
def test_neg(self):
self.assertEqual(-(1+6j), -1-6j)
# Pyston change: This is a libpypa bug, waiting for upstream fix it
# please refer libpypa#47
# def test_neg(self):
# self.assertEqual(-(1+6j), -1-6j)
def test_file(self):
a = 3.33+4.43j
......
......@@ -2118,6 +2118,18 @@ extern "C" PyObject* PyNumber_Float(PyObject* o) noexcept {
if (o->cls == float_cls)
return o;
PyNumberMethods* m;
m = o->cls->tp_as_number;
if (m && m->nb_float) { /* This should include subclasses of float */
PyObject* res = m->nb_float(o);
if (res && !PyFloat_Check(res)) {
PyErr_Format(PyExc_TypeError, "__float__ returned non-float (type %.200s)", res->cls->tp_name);
Py_DECREF(res);
return NULL;
}
return res;
}
if (PyInt_Check(o))
return boxFloat(((BoxedInt*)o)->n);
else if (PyLong_Check(o)) {
......@@ -2127,8 +2139,7 @@ extern "C" PyObject* PyNumber_Float(PyObject* o) noexcept {
return boxFloat(result);
}
fatalOrError(PyExc_NotImplementedError, "unimplemented");
return nullptr;
return PyFloat_FromString(o, NULL);
}
extern "C" PyObject* PyNumber_Index(PyObject* o) noexcept {
......
......@@ -438,8 +438,8 @@ struct expr_dispatcher {
AST_BinOp* binop = new AST_BinOp();
location(binop, c);
binop->op_type = AST_TYPE::Add;
binop->right = readItem(c.real, interned_strings);
binop->left = imag;
binop->left = readItem(c.real, interned_strings);
binop->right = imag;
return binop;
}
......
......@@ -30,19 +30,74 @@ extern "C" Box* createPureImaginary(double i) {
return new BoxedComplex(0.0, i);
}
static PyObject* try_complex_special_method(PyObject* op) noexcept {
PyObject* f;
static PyObject* complexstr;
if (complexstr == NULL) {
complexstr = PyString_InternFromString("__complex__");
if (complexstr == NULL)
return NULL;
}
if (PyInstance_Check(op)) {
f = PyObject_GetAttr(op, complexstr);
if (f == NULL) {
if (PyErr_ExceptionMatches(PyExc_AttributeError))
PyErr_Clear();
else
return NULL;
}
} else {
f = _PyObject_LookupSpecial(op, "__complex__", &complexstr);
if (f == NULL && PyErr_Occurred())
return NULL;
}
if (f != NULL) {
PyObject* res = PyObject_CallFunctionObjArgs(f, NULL);
Py_DECREF(f);
return res;
}
return NULL;
}
extern "C" Py_complex PyComplex_AsCComplex(PyObject* op) noexcept {
// TODO: Incomplete v.s. CPython's implementation.
Py_complex cval;
Py_complex cv;
PyObject* newop = NULL;
assert(op);
/* If op is already of type PyComplex_Type, return its value */
if (PyComplex_Check(op)) {
cval.real = ((BoxedComplex*)op)->real;
cval.imag = ((BoxedComplex*)op)->imag;
return cval;
} else if (op->cls == int_cls) {
cval.real = ((BoxedInt*)op)->n;
cval.imag = 0.0;
return cval;
} else {
Py_FatalError("unimplemented");
cv.real = ((BoxedComplex*)op)->real;
cv.imag = ((BoxedComplex*)op)->imag;
return cv;
}
/* If not, use op's __complex__ method, if it exists */
/* return -1 on failure */
cv.real = -1.;
cv.imag = 0.;
newop = try_complex_special_method(op);
if (newop) {
if (!PyComplex_Check(newop)) {
PyErr_SetString(PyExc_TypeError, "__complex__ should return a complex object");
Py_DECREF(newop);
return cv;
}
cv.real = ((BoxedComplex*)newop)->real;
cv.imag = ((BoxedComplex*)newop)->imag;
Py_DECREF(newop);
return cv;
} else if (PyErr_Occurred()) {
return cv;
}
/* If neither of the above works, interpret op as a float giving the
real part of the result, and fill in the imaginary part as 0. */
else {
/* PyFloat_AsDouble will return -1 on failure */
cv.real = PyFloat_AsDouble(op);
return cv;
}
}
......@@ -137,6 +192,23 @@ extern "C" Box* complexSub(BoxedComplex* lhs, Box* rhs) {
}
}
extern "C" Box* complexRSub(BoxedComplex* _lhs, Box* _rhs) {
if (!PyComplex_Check(_lhs))
raiseExcHelper(TypeError, "descriptor '__rsub__' requires a 'complex' object but received a '%s'",
getTypeName(_lhs));
BoxedComplex* lhs = new BoxedComplex(0.0, 0.0);
if (PyInt_Check(_rhs)) {
lhs->real = (static_cast<BoxedInt*>(_rhs))->n;
} else if (_rhs->cls == float_cls) {
lhs->real = (static_cast<BoxedFloat*>(_rhs))->d;
} else if (_rhs->cls == complex_cls) {
lhs = static_cast<BoxedComplex*>(_rhs);
} else {
return NotImplemented;
}
return complexSubComplex(lhs, _lhs);
}
// multiplication
extern "C" Box* complexMulComplex(BoxedComplex* lhs, BoxedComplex* rhs) {
......@@ -171,16 +243,41 @@ extern "C" Box* complexMul(BoxedComplex* lhs, Box* rhs) {
}
// division
Box* complexDivComplex(BoxedComplex* lhs, BoxedComplex* rhs) {
if (!PyComplex_Check(lhs))
raiseExcHelper(TypeError, "descriptor '__div__' requires a 'complex' object but received a '%s'",
getTypeName(lhs));
assert(rhs->cls == complex_cls);
double real_f, imag_f;
const double abs_breal = rhs->real < 0 ? -rhs->real : rhs->real;
const double abs_bimag = rhs->imag < 0 ? -rhs->imag : rhs->imag;
extern "C" Box* complexDivComplex(BoxedComplex* lhs, BoxedComplex* rhs) {
// TODO implement this
// NOTE: the "naive" implementation of complex division has numerical issues
// see notes in CPython, Objects/complexobject.c, c_quot
return NotImplemented;
if (abs_breal >= abs_bimag) {
/* divide tops and bottom by rhs.real */
if (abs_breal == 0.0) {
raiseDivZeroExc();
real_f = imag_f = 0.0;
} else {
const double ratio = rhs->imag / rhs->real;
const double denom = rhs->real + rhs->imag * ratio;
real_f = (lhs->real + lhs->imag * ratio) / denom;
imag_f = (lhs->imag - lhs->real * ratio) / denom;
}
} else {
/* divide tops and bottom by rhs->imag */
const double ratio = rhs->real / rhs->imag;
const double denom = rhs->real * ratio + rhs->imag;
real_f = (lhs->real * ratio + lhs->imag) / denom;
imag_f = (lhs->imag * ratio - lhs->real) / denom;
}
return boxComplex(real_f, imag_f);
}
extern "C" Box* complexDivFloat(BoxedComplex* lhs, BoxedFloat* rhs) {
assert(lhs->cls == complex_cls);
if (!PyComplex_Check(lhs))
raiseExcHelper(TypeError, "descriptor '__div__' requires a 'complex' object but received a '%s'",
getTypeName(lhs));
assert(rhs->cls == float_cls);
if (rhs->d == 0.0) {
raiseDivZeroExc();
......@@ -189,7 +286,9 @@ extern "C" Box* complexDivFloat(BoxedComplex* lhs, BoxedFloat* rhs) {
}
extern "C" Box* complexDivInt(BoxedComplex* lhs, BoxedInt* rhs) {
assert(lhs->cls == complex_cls);
if (!PyComplex_Check(lhs))
raiseExcHelper(TypeError, "descriptor '__div__' requires a 'complex' object but received a '%s'",
getTypeName(lhs));
assert(PyInt_Check(rhs));
if (rhs->n == 0) {
raiseDivZeroExc();
......@@ -198,7 +297,9 @@ extern "C" Box* complexDivInt(BoxedComplex* lhs, BoxedInt* rhs) {
}
extern "C" Box* complexDiv(BoxedComplex* lhs, Box* rhs) {
assert(lhs->cls == complex_cls);
if (!PyComplex_Check(lhs))
raiseExcHelper(TypeError, "descriptor '__div__' requires a 'complex' object but received a '%s'",
getTypeName(lhs));
if (PyInt_Check(rhs)) {
return complexDivInt(lhs, static_cast<BoxedInt*>(rhs));
} else if (rhs->cls == float_cls) {
......@@ -210,26 +311,82 @@ extern "C" Box* complexDiv(BoxedComplex* lhs, Box* rhs) {
}
}
extern "C" Box* complexRDiv(BoxedComplex* _lhs, Box* _rhs) {
if (!PyComplex_Check(_lhs))
raiseExcHelper(TypeError, "descriptor '__rdiv__' requires a 'complex' object but received a '%s'",
getTypeName(_lhs));
BoxedComplex* lhs = new BoxedComplex(0.0, 0.0);
if (PyInt_Check(_rhs)) {
lhs->real = (static_cast<BoxedInt*>(_rhs))->n;
} else if (_rhs->cls == float_cls) {
lhs->real = (static_cast<BoxedFloat*>(_rhs))->d;
} else if (_rhs->cls == complex_cls) {
lhs = static_cast<BoxedComplex*>(_rhs);
} else {
return NotImplemented;
}
return complexDivComplex(lhs, _lhs);
}
Box* complexPos(BoxedComplex* self) {
assert(self->cls == complex_cls);
if (!PyComplex_Check(self))
raiseExcHelper(TypeError, "descriptor '__pos__' requires a 'complex' object but received a '%s'",
getTypeName(self));
return PyComplex_FromDoubles(self->real, self->imag);
}
// str and repr
// For now, just print the same way as ordinary doubles.
// TODO this is wrong, e.g. if real or imaginary part is an integer, there should
// be no decimal point, maybe some other differences. Need to dig deeper into
// how CPython formats floats and complex numbers.
// (complex_format in Objects/complexobject.c)
std::string complexFmt(double r, double i, int precision, char code) {
Box* complex_fmt(double r, double i, int precision, char format_code) noexcept {
PyObject* result = NULL;
/* If these are non-NULL, they'll need to be freed. */
char* pre = NULL;
char* im = NULL;
/* These do not need to be freed. re is either an alias
for pre or a pointer to a constant. lead and tail
are pointers to constants. */
std::string lead = "";
std::string tail = "";
std::string re = "";
std::string result_str;
if (r == 0. && copysign(1.0, r) == 1.0) {
return floatFmt(i, precision, code) + "j";
re = "";
im = PyOS_double_to_string(i, format_code, precision, 0, NULL);
if (!im) {
PyErr_NoMemory();
goto done;
}
} else {
return "(" + floatFmt(r, precision, code) + (isnan(i) || i >= 0.0 ? "+" : "") + floatFmt(i, precision, code)
+ "j)";
/* Format imaginary part with signr part without */
pre = PyOS_double_to_string(r, format_code, precision, 0, NULL);
if (!pre) {
PyErr_NoMemory();
goto done;
}
re = std::string(pre);
im = PyOS_double_to_string(i, format_code, precision, Py_DTSF_SIGN, NULL);
if (!im) {
PyErr_NoMemory();
goto done;
}
lead = "(";
tail = ")";
}
/* Alloc the final buffer. Add one for the "j" in the format string,
and one for the trailing zero. */
result_str = lead + std::string(re) + std::string(im) + "j" + tail;
result = PyString_FromString(result_str.c_str());
done:
PyMem_Free(im);
PyMem_Free(pre);
return result;
}
static void _addFunc(const char* name, ConcreteCompilerType* rtn_type, void* complex_func, void* float_func,
void* int_func, void* boxed_func) {
CLFunction* cl = createRTFunction(2, 0, false, false);
......@@ -240,8 +397,151 @@ static void _addFunc(const char* name, ConcreteCompilerType* rtn_type, void* com
complex_cls->giveAttr(name, new BoxedFunction(cl));
}
static Py_complex c_1 = { 1., 0. };
extern "C" Py_complex c_prod(Py_complex a, Py_complex b) noexcept {
Py_complex r;
r.real = a.real * b.real - a.imag * b.imag;
r.imag = a.real * b.imag + a.imag * b.real;
return r;
}
extern "C" Py_complex c_quot(Py_complex a, Py_complex b) noexcept {
/******************************************************************
This was the original algorithm. It's grossly prone to spurious
overflow and underflow errors. It also merrily divides by 0 despite
checking for that(!). The code still serves a doc purpose here, as
the algorithm following is a simple by-cases transformation of this
one:
Py_complex r;
double d = b.real*b.real + b.imag*b.imag;
if (d == 0.)
errno = EDOM;
r.real = (a.real*b.real + a.imag*b.imag)/d;
r.imag = (a.imag*b.real - a.real*b.imag)/d;
return r;
******************************************************************/
/* This algorithm is better, and is pretty obvious: first divide the
* numerators and denominator by whichever of {b.real, b.imag} has
* larger magnitude. The earliest reference I found was to CACM
* Algorithm 116 (Complex Division, Robert L. Smith, Stanford
* University). As usual, though, we're still ignoring all IEEE
* endcases.
*/
Py_complex r; /* the result */
const double abs_breal = b.real < 0 ? -b.real : b.real;
const double abs_bimag = b.imag < 0 ? -b.imag : b.imag;
if (abs_breal >= abs_bimag) {
/* divide tops and bottom by b.real */
if (abs_breal == 0.0) {
errno = EDOM;
r.real = r.imag = 0.0;
} else {
const double ratio = b.imag / b.real;
const double denom = b.real + b.imag * ratio;
r.real = (a.real + a.imag * ratio) / denom;
r.imag = (a.imag - a.real * ratio) / denom;
}
} else {
/* divide tops and bottom by b.imag */
const double ratio = b.real / b.imag;
const double denom = b.real * ratio + b.imag;
assert(b.imag != 0.0);
r.real = (a.real * ratio + a.imag) / denom;
r.imag = (a.imag * ratio - a.real) / denom;
}
return r;
}
extern "C" Py_complex c_pow(Py_complex a, Py_complex b) noexcept {
Py_complex r;
double vabs, len, at, phase;
if (b.real == 0. && b.imag == 0.) {
r.real = 1.;
r.imag = 0.;
} else if (a.real == 0. && a.imag == 0.) {
if (b.imag != 0. || b.real < 0.)
errno = EDOM;
r.real = 0.;
r.imag = 0.;
} else {
vabs = hypot(a.real, a.imag);
len = pow(vabs, b.real);
at = atan2(a.imag, a.real);
phase = at * b.real;
if (b.imag != 0.0) {
len /= exp(at * b.imag);
phase += b.imag * log(vabs);
}
r.real = len * cos(phase);
r.imag = len * sin(phase);
}
return r;
}
static Py_complex c_powu(Py_complex x, long n) noexcept {
Py_complex r, p;
long mask = 1;
r = c_1;
p = x;
while (mask > 0 && n >= mask) {
if (n & mask)
r = c_prod(r, p);
mask <<= 1;
p = c_prod(p, p);
}
return r;
}
static Py_complex c_powi(Py_complex x, long n) noexcept {
Py_complex cn;
if (n > 100 || n < -100) {
cn.real = (double)n;
cn.imag = 0.;
return c_pow(x, cn);
} else if (n > 0)
return c_powu(x, n);
else
return c_quot(c_1, c_powu(x, -n));
}
Box* complexPow(BoxedComplex* lhs, Box* _rhs, Box* mod) {
if (!PyComplex_Check(lhs))
raiseExcHelper(TypeError, "descriptor '__pow__' requires a 'complex' object but received a '%s'",
getTypeName(lhs));
Py_complex p;
Py_complex exponent;
long int_exponent;
Py_complex a, b;
a = PyComplex_AsCComplex(lhs);
b = PyComplex_AsCComplex(_rhs);
if (mod != Py_None) {
raiseExcHelper(ValueError, "complex modulo");
}
PyFPE_START_PROTECT("complex_pow", return 0) errno = 0;
exponent = b;
int_exponent = (long)exponent.real;
if (exponent.imag == 0. && exponent.real == int_exponent)
p = c_powi(a, int_exponent);
else
p = c_pow(a, exponent);
PyFPE_END_PROTECT(p) Py_ADJUST_ERANGE2(p.real, p.imag);
if (errno == EDOM) {
raiseExcHelper(ZeroDivisionError, "0.0 to a negative or complex power");
} else if (errno == ERANGE) {
raiseExcHelper(OverflowError, "complex exponentiation");
}
return boxComplex(p.real, p.imag);
}
Box* complexHash(BoxedComplex* self) {
if (!isSubclass(self->cls, complex_cls))
if (!PyComplex_Check(self))
raiseExcHelper(TypeError, "descriptor '__hash__' requires a 'complex' object but received a '%s'",
getTypeName(self));
long hashreal, hashimag, combined;
......@@ -267,49 +567,491 @@ Box* complexHash(BoxedComplex* self) {
return boxInt(combined);
}
Box* complexCoerce(Box* lhs, Box* rhs) {
Py_complex cval;
cval.imag = 0.;
if (PyInt_Check(rhs)) {
cval.real = (double)PyInt_AsLong(rhs);
rhs = PyComplex_FromCComplex(cval);
} else if (PyLong_Check(rhs)) {
cval.real = PyLong_AsDouble(rhs);
if (cval.real == -1.0 && PyErr_Occurred()) {
throwCAPIException();
}
rhs = PyComplex_FromCComplex(cval);
} else if (PyFloat_Check(rhs)) {
cval.real = PyFloat_AsDouble(rhs);
rhs = PyComplex_FromCComplex(cval);
} else if (!PyComplex_Check(rhs)) {
return NotImplemented;
}
return BoxedTuple::create({ lhs, rhs });
}
Box* complexConjugate(BoxedComplex* self) {
if (!PyComplex_Check(self))
raiseExcHelper(TypeError, "descriptor 'conjuagte' requires a 'complex' object but received a '%s'",
getTypeName(self));
return new BoxedComplex(self->real, -self->imag);
}
Box* complexAbs(BoxedComplex* self) {
assert(self->cls == complex_cls);
// TODO: CPython does a lot more safety checks.
return boxFloat(sqrt(self->real * self->real + self->imag * self->imag));
if (!PyComplex_Check(self))
raiseExcHelper(TypeError, "descriptor '__abs__' requires a 'complex' object but received a '%s'",
getTypeName(self));
double result;
if (isinf(self->real) || isinf(self->imag)) {
/* C99 rules: if either the real or the imaginary part is an
infinity, return infinity, even if the other part is a
NaN. */
if (!isinf(self->real)) {
return boxFloat(fabs(self->real));
}
if (!isinf(self->imag)) {
return boxFloat(fabs(self->imag));
}
/* either the real or imaginary part is a NaN,
and neither is infinite. Result should be NaN. */
return boxFloat(Py_NAN);
}
result = sqrt(self->real * self->real + self->imag * self->imag);
if (isinf(result)) {
raiseExcHelper(OverflowError, "absolute value too large");
}
return boxFloat(result);
}
Box* complexGetnewargs(BoxedComplex* self) {
if (!PyComplex_Check(self))
raiseExcHelper(TypeError, "descriptor '__getnewargs__' requires a 'complex' object but received a '%s'",
getTypeName(self));
return BoxedTuple::create({ boxFloat(self->real), boxFloat(self->imag) });
}
Box* complexNonzero(BoxedComplex* self) {
if (!PyComplex_Check(self))
raiseExcHelper(TypeError, "descriptor '__nonzero__' require a 'complex' object but received a '%s'",
getTypeName(self));
bool res = self->real != 0.0 || self->imag != 0.0;
return boxBool(res);
}
Box* complexStr(BoxedComplex* self) {
assert(self->cls == complex_cls);
return boxString(complexFmt(self->real, self->imag, 12, 'g'));
if (!PyComplex_Check(self))
raiseExcHelper(TypeError, "descriptor '__str__' requires a 'complex' object but received a '%s'",
getTypeName(self));
Box* r = complex_fmt(self->real, self->imag, 12, 'g');
if (!r) {
throwCAPIException();
}
return r;
}
Box* complexRepr(BoxedComplex* self) {
assert(self->cls == complex_cls);
return boxString(complexFmt(self->real, self->imag, 16, 'g'));
if (!PyComplex_Check(self))
raiseExcHelper(TypeError, "descriptor '__repr__' requires a 'complex' object but received a '%s'",
getTypeName(self));
Box* r = complex_fmt(self->real, self->imag, 16, 'g');
if (!r) {
throwCAPIException();
}
return r;
}
Box* complexNew(Box* _cls, Box* real, Box* imag) {
RELEASE_ASSERT(_cls == complex_cls, "");
static PyObject* complex_subtype_from_string(PyObject* v) noexcept {
const char* s, *start;
char* end;
double x = 0.0, y = 0.0, z;
int got_bracket = 0;
#ifdef Py_USING_UNICODE
char* s_buffer = NULL;
#endif
Py_ssize_t len;
if (PyString_Check(v)) {
s = PyString_AS_STRING(v);
len = PyString_GET_SIZE(v);
}
#ifdef Py_USING_UNICODE
else if (PyUnicode_Check(v)) {
s_buffer = (char*)PyMem_MALLOC(PyUnicode_GET_SIZE(v) + 1);
if (s_buffer == NULL)
return PyErr_NoMemory();
if (PyUnicode_EncodeDecimal(PyUnicode_AS_UNICODE(v), PyUnicode_GET_SIZE(v), s_buffer, NULL))
goto error;
s = s_buffer;
len = strlen(s);
}
#endif
else if (PyObject_AsCharBuffer(v, &s, &len)) {
PyErr_SetString(PyExc_TypeError, "complex() arg is not a string");
return NULL;
}
/* position on first nonblank */
start = s;
while (Py_ISSPACE(*s))
s++;
if (*s == '(') {
/* Skip over possible bracket from repr(). */
got_bracket = 1;
s++;
while (Py_ISSPACE(*s))
s++;
}
/* a valid complex string usually takes one of the three forms:
<float> - real part only
<float>j - imaginary part only
<float><signed-float>j - real and imaginary parts
double real_f;
if (PyInt_Check(real)) {
real_f = static_cast<BoxedInt*>(real)->n;
} else if (real->cls == float_cls) {
real_f = static_cast<BoxedFloat*>(real)->d;
where <float> represents any numeric string that's accepted by the
float constructor (including 'nan', 'inf', 'infinity', etc.), and
<signed-float> is any string of the form <float> whose first
character is '+' or '-'.
For backwards compatibility, the extra forms
<float><sign>j
<sign>j
j
are also accepted, though support for these forms may be removed from
a future version of Python.
*/
/* first look for forms starting with <float> */
z = PyOS_string_to_double(s, &end, NULL);
if (z == -1.0 && PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_ValueError))
PyErr_Clear();
else
goto error;
}
if (end != s) {
/* all 4 forms starting with <float> land here */
s = end;
if (*s == '+' || *s == '-') {
/* <float><signed-float>j | <float><sign>j */
x = z;
y = PyOS_string_to_double(s, &end, NULL);
if (y == -1.0 && PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_ValueError))
PyErr_Clear();
else
goto error;
}
if (end != s)
/* <float><signed-float>j */
s = end;
else {
/* <float><sign>j */
y = *s == '+' ? 1.0 : -1.0;
s++;
}
if (!(*s == 'j' || *s == 'J'))
goto parse_error;
s++;
} else if (*s == 'j' || *s == 'J') {
/* <float>j */
s++;
y = z;
} else
/* <float> */
x = z;
} else {
// TODO: implement taking a string argument
raiseExcHelper(TypeError, "complex() argument must be a string or number");
/* not starting with <float>; must be <sign>j or j */
if (*s == '+' || *s == '-') {
/* <sign>j */
y = *s == '+' ? 1.0 : -1.0;
s++;
} else
/* j */
y = 1.0;
if (!(*s == 'j' || *s == 'J'))
goto parse_error;
s++;
}
/* trailing whitespace and closing bracket */
while (Py_ISSPACE(*s))
s++;
if (got_bracket) {
/* if there was an opening parenthesis, then the corresponding
closing parenthesis should be right here */
if (*s != ')')
goto parse_error;
s++;
while (Py_ISSPACE(*s))
s++;
}
double imag_f;
if (PyInt_Check(imag)) {
imag_f = static_cast<BoxedInt*>(imag)->n;
} else if (imag->cls == float_cls) {
imag_f = static_cast<BoxedFloat*>(imag)->d;
} else if (imag->cls == str_cls) {
raiseExcHelper(TypeError, "complex() second arg can't be a string");
/* we should now be at the end of the string */
if (s - start != len)
goto parse_error;
#ifdef Py_USING_UNICODE
if (s_buffer)
PyMem_FREE(s_buffer);
#endif
return new BoxedComplex(x, y);
parse_error:
PyErr_SetString(PyExc_ValueError, "complex() arg is a malformed string");
error:
#ifdef Py_USING_UNICODE
if (s_buffer)
PyMem_FREE(s_buffer);
#endif
return NULL;
}
static Box* to_complex(Box* self) noexcept {
BoxedComplex* r;
if (self == None || self == NULL) {
return new BoxedComplex(0.0, 0.0);
}
static BoxedString* complex_str = internStringImmortal("__complex__");
if (PyComplex_Check(self) && !PyObject_HasAttr((PyObject*)self, complex_str)) {
r = (BoxedComplex*)self;
} else if (PyInt_Check(self)) {
r = new BoxedComplex(static_cast<BoxedInt*>(self)->n, 0.0);
} else if (PyFloat_Check(self)) {
r = new BoxedComplex((static_cast<BoxedFloat*>(PyNumber_Float(self)))->d, 0.0);
} else if (self->cls == long_cls) {
r = new BoxedComplex(PyLong_AsDouble(self), 0.0);
} else {
raiseExcHelper(TypeError, "complex() argument must be a string or number");
return NotImplemented;
}
return r;
}
template <ExceptionStyle S> static Box* try_special_method(Box* self) noexcept(S == CAPI) {
if (self == None || self == NULL) {
return None;
}
static BoxedString* float_str = internStringImmortal("__float__");
if (PyObject_HasAttr((PyObject*)self, float_str)) {
Box* r_f = callattrInternal<S>(self, float_str, CLASS_ONLY, NULL, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
if (!PyFloat_Check(r_f)) {
if (S == CAPI) {
if (!PyErr_Occurred())
PyErr_Format(PyExc_TypeError, "__float__ returned non-float (type %.200s)", r_f->cls->tp_name);
return NULL;
} else {
raiseExcHelper(TypeError, "__float__ returned non-float (type %.200s)", r_f->cls->tp_name);
}
}
return (BoxedFloat*)r_f;
}
static BoxedString* complex_str = internStringImmortal("__complex__");
if (PyObject_HasAttr((PyObject*)self, complex_str)) {
Box* r
= callattrInternal<S>(self, complex_str, CLASS_OR_INST, NULL, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
if (!r) {
if (S == CAPI) {
if (!PyErr_Occurred())
PyErr_Format(TypeError, "complex() argument must be a string or a number, not '%s'\n",
getTypeName(self));
return NULL;
} else {
raiseExcHelper(TypeError, "complex() argument must be a string or a number, not '%s'\n",
getTypeName(self));
}
}
if (!PyComplex_Check(r)) {
if (S == CAPI) {
PyErr_Format(TypeError, "__complex__ returned non-complex (type %s)", r->cls->tp_name);
return NULL;
} else
raiseExcHelper(TypeError, "__complex__ returned non-complex (type %s)", r->cls->tp_name);
}
return static_cast<BoxedComplex*>(r);
}
return None;
}
template <ExceptionStyle S> Box* _complexNew(Box* real, Box* imag) noexcept(S == CAPI) {
// handle str and unicode
if (real != None && real != NULL) {
if (PyString_Check(real) || PyUnicode_Check(real)) {
if (imag != None && imag != NULL) {
raiseExcHelper(TypeError, "complex() can't take second arg if first is a string");
}
Box* res = complex_subtype_from_string(real);
if (res == NULL) {
throwCAPIException();
}
return res;
}
}
if (real != None && real != NULL && real->cls == complex_cls && (imag == NULL || imag == None)) {
return static_cast<BoxedComplex*>(real);
}
Box* _real = try_special_method<S>(real);
Box* _imag = try_special_method<S>(imag);
if (_real != None && _real != NULL) {
real = _real;
}
if (_imag != None && _imag != NULL) {
imag = _imag;
}
double real_f, imag_f;
bool real_is_complex = false, imag_is_complex = false;
if (real == None || real == NULL) {
real_f = 0.0;
} else if (PyComplex_Check(real)) {
real_f = ((BoxedComplex*)real)->real;
real_is_complex = true;
} else {
real_f = ((BoxedFloat*)PyNumber_Float(real))->d;
}
if (imag == None || imag == NULL) {
imag_f = 0.0;
} else if (PyComplex_Check(imag)) {
imag_f = ((BoxedComplex*)imag)->real;
imag_is_complex = true;
} else {
imag_f = ((BoxedFloat*)PyNumber_Float(imag))->d;
}
if (imag_is_complex) {
real_f -= ((BoxedComplex*)imag)->imag;
}
if (real_is_complex) {
imag_f += ((BoxedComplex*)real)->imag;
}
return new BoxedComplex(real_f, imag_f);
}
template <ExceptionStyle S> Box* complexNew(BoxedClass* _cls, Box* _real, Box* _imag) noexcept(S == CAPI) {
if (!isSubclass(_cls->cls, type_cls)) {
if (S == CAPI) {
PyErr_Format(TypeError, "complex.__new__(X): X is not a type object (%s)", getTypeName(_cls));
return NULL;
} else
raiseExcHelper(TypeError, "complex.__new__(X): X is not a type object (%s)", getTypeName(_cls));
}
BoxedClass* cls = static_cast<BoxedClass*>(_cls);
if (PyComplex_Check(cls)) {
if (S == CAPI) {
PyErr_Format(TypeError, "complex.__new__(%s): %s is not a subtype of complex", getNameOfClass(cls),
getNameOfClass(cls));
return NULL;
} else
raiseExcHelper(TypeError, "complex.__new__(%s): %s is not a subtype of complex", getNameOfClass(cls),
getNameOfClass(cls));
}
// check second argument
if (_imag != NULL && (PyString_Check(_imag) || PyUnicode_Check(_imag))) {
if (S == CAPI) {
PyErr_Format(TypeError, "complex() second arg can't be a string");
return NULL;
} else
raiseExcHelper(TypeError, "complex() second arg can't be a string");
}
// we can't use None as default value, because complex(None) should raise TypeError,
// but complex() should return `0j`. So use NULL as default value.
// Here check we pass complex(None, None), complex(None)
// or complex(imag=None) to the constructor
if ((_real == None) && (_imag == None || _imag == NULL)) {
raiseExcHelper(TypeError, "complex() argument must be a string or number");
}
if (cls == complex_cls)
return _complexNew<S>(_real, _imag);
BoxedComplex* r = (BoxedComplex*)_complexNew<S>(_real, _imag);
if (!r) {
assert(S == CAPI);
return NULL;
}
return new (_cls) BoxedComplex(r->real, r->imag);
}
extern "C" Box* complexDivmod(BoxedComplex* lhs, BoxedComplex* rhs) {
if (!PyComplex_Check(lhs))
raiseExcHelper(TypeError, "descriptor '__divmod__' requires a 'complex' object but received a '%s'",
getTypeName(lhs));
if (PyErr_Warn(PyExc_DeprecationWarning, "complex divmod(), // and % are deprecated") < 0)
return NULL;
if (rhs->real == 0.0 && rhs->imag == 0.0) {
raiseExcHelper(ZeroDivisionError, "complex divmod()");
}
BoxedComplex* div = (BoxedComplex*)complexDiv(lhs, rhs); /* The raw divisor value. */
div->real = floor(div->real); /* Use the floor of the real part. */
div->imag = 0.0;
BoxedComplex* mod = (BoxedComplex*)complexSubComplex(lhs, (BoxedComplex*)complexMulComplex(rhs, div));
Box* res = BoxedTuple::create({ div, mod });
return res;
}
extern "C" Box* complexMod(BoxedComplex* lhs, Box* _rhs) {
if (!PyComplex_Check(lhs))
raiseExcHelper(TypeError, "descriptor '__mod__' requires a 'complex' object but received a '%s'",
getTypeName(lhs));
if (PyErr_Warn(PyExc_DeprecationWarning, "complex divmod(), // and % are deprecated") < 0)
return NULL;
Box* res = to_complex(_rhs);
if (res == NotImplemented) {
return NotImplemented;
}
BoxedComplex* rhs = (BoxedComplex*)res;
if (rhs->real == 0.0 && rhs->imag == 0.0) {
raiseExcHelper(ZeroDivisionError, "complex remainder");
}
BoxedComplex* div = (BoxedComplex*)complexDiv(lhs, rhs); /* The raw divisor value. */
div->real = floor(div->real); /* Use the floor of the real part. */
div->imag = 0.0;
BoxedComplex* mod = (BoxedComplex*)complexSubComplex(lhs, (BoxedComplex*)complexMulComplex(rhs, div));
return mod;
}
extern "C" Box* complexFloordiv(BoxedComplex* lhs, Box* _rhs) {
if (!PyComplex_Check(lhs))
raiseExcHelper(TypeError, "descriptor '__floordiv__' requires a 'complex' object but received a '%s'",
getTypeName(lhs));
Box* res = to_complex(_rhs);
if (res == NotImplemented) {
return NotImplemented;
}
BoxedComplex* rhs = (BoxedComplex*)res;
BoxedTuple* t = (BoxedTuple*)complexDivmod(lhs, rhs);
return t->elts[0];
}
static PyObject* complex_richcompare(PyObject* v, PyObject* w, int op) noexcept {
PyObject* res;
int equal;
......@@ -364,32 +1106,180 @@ static PyObject* complex_richcompare(PyObject* v, PyObject* w, int op) noexcept
return res;
}
extern "C" Box* complexEq(BoxedComplex* lhs, Box* rhs) {
if (lhs->cls != complex_cls) {
raiseExcHelper(TypeError, "descriptor '__eq__' requires a 'complex' object but recieved a '%s'",
getTypeName(lhs));
}
Box* res = complex_richcompare(lhs, rhs, Py_EQ);
if (!res) {
throwCAPIException();
}
return res;
}
extern "C" Box* complexNe(BoxedComplex* lhs, Box* rhs) {
if (lhs->cls != complex_cls) {
raiseExcHelper(TypeError, "descriptor '__ne__' requires a 'complex' object but recieved a '%s'",
getTypeName(lhs));
}
Box* res = complex_richcompare(lhs, rhs, Py_NE);
if (!res) {
throwCAPIException();
}
return res;
}
extern "C" Box* complexLe(BoxedComplex* lhs, Box* rhs) {
if (lhs->cls != complex_cls) {
raiseExcHelper(TypeError, "descriptor '__le__' requires a 'complex' object but recieved a '%s'",
getTypeName(lhs));
}
Box* res = complex_richcompare(lhs, rhs, Py_LE);
if (!res) {
throwCAPIException();
}
return res;
}
extern "C" Box* complexLt(BoxedComplex* lhs, Box* rhs) {
if (lhs->cls != complex_cls) {
raiseExcHelper(TypeError, "descriptor '__lt__' requires a 'complex' object but recieved a '%s'",
getTypeName(lhs));
}
Box* res = complex_richcompare(lhs, rhs, Py_LT);
if (!res) {
throwCAPIException();
}
return res;
}
extern "C" Box* complexGe(BoxedComplex* lhs, Box* rhs) {
if (lhs->cls != complex_cls) {
raiseExcHelper(TypeError, "descriptor '__ge__' requires a 'complex' object but recieved a '%s'",
getTypeName(lhs));
}
Box* res = complex_richcompare(lhs, rhs, Py_GE);
if (!res) {
throwCAPIException();
}
return res;
}
extern "C" Box* complexGt(BoxedComplex* lhs, Box* rhs) {
if (lhs->cls != complex_cls) {
raiseExcHelper(TypeError, "descriptor '__gt__' requires a 'complex' object but recieved a '%s'",
getTypeName(lhs));
}
Box* res = complex_richcompare(lhs, rhs, Py_GT);
if (!res) {
throwCAPIException();
}
return res;
}
Box* complexNeg(Box* _self) {
assert(_self->cls == complex_cls);
BoxedComplex* self = (BoxedComplex*)_self;
return PyComplex_FromDoubles(-self->real, -self->imag);
}
PyObject* complex_neg(PyComplexObject* v) {
BoxedComplex* self = (BoxedComplex*)v;
return PyComplex_FromDoubles(-self->real, -self->imag);
}
// Pyston change: make not static
PyObject* complex__format__(PyObject* self, PyObject* args) {
PyObject* format_spec;
if (!PyArg_ParseTuple(args, "O:__format__", &format_spec))
return NULL;
if (PyBytes_Check(format_spec))
return _PyComplex_FormatAdvanced(self, PyBytes_AS_STRING(format_spec), PyBytes_GET_SIZE(format_spec));
if (PyUnicode_Check(format_spec)) {
/* Convert format_spec to a str */
PyObject* result;
PyObject* str_spec = PyObject_Str(format_spec);
if (str_spec == NULL)
return NULL;
result = _PyComplex_FormatAdvanced(self, PyBytes_AS_STRING(str_spec), PyBytes_GET_SIZE(str_spec));
Py_DECREF(str_spec);
return result;
}
PyErr_SetString(PyExc_TypeError, "__format__ requires str or unicode");
return NULL;
}
static PyMethodDef complex_methods[] = {
{ "__format__", (PyCFunction)complex__format__, METH_VARARGS, NULL },
};
void setupComplex() {
complex_cls->giveAttr("__new__", new BoxedFunction(boxRTFunction((void*)complexNew, UNKNOWN, 3, 2, false, false),
{ boxInt(0), boxInt(0) }));
auto complex_new = boxRTFunction((void*)complexNew<CXX>, UNKNOWN, 3, 2, false, false,
ParamNames({ "", "real", "imag" }, "", ""), CXX);
addRTFunction(complex_new, (void*)complexNew<CAPI>, UNKNOWN, CAPI);
complex_cls->giveAttr("__new__", new BoxedFunction(complex_new, { NULL, NULL }));
_addFunc("__add__", BOXED_COMPLEX, (void*)complexAddComplex, (void*)complexAddFloat, (void*)complexAddInt,
(void*)complexAdd);
_addFunc("__radd__", BOXED_COMPLEX, (void*)complexAddComplex, (void*)complexAddFloat, (void*)complexAddInt,
(void*)complexAdd);
_addFunc("__sub__", BOXED_COMPLEX, (void*)complexSubComplex, (void*)complexSubFloat, (void*)complexSubInt,
(void*)complexSub);
_addFunc("__mul__", BOXED_COMPLEX, (void*)complexMulComplex, (void*)complexMulFloat, (void*)complexMulInt,
(void*)complexMul);
_addFunc("__rmul__", BOXED_COMPLEX, (void*)complexMulComplex, (void*)complexMulFloat, (void*)complexMulInt,
(void*)complexMul);
_addFunc("__div__", BOXED_COMPLEX, (void*)complexDivComplex, (void*)complexDivFloat, (void*)complexDivInt,
(void*)complexDiv);
complex_cls->giveAttr("__rsub__", new BoxedFunction(boxRTFunction((void*)complexRSub, UNKNOWN, 2)));
complex_cls->giveAttr("__rdiv__", new BoxedFunction(boxRTFunction((void*)complexRDiv, UNKNOWN, 2)));
complex_cls->giveAttr("__pow__",
new BoxedFunction(boxRTFunction((void*)complexPow, UNKNOWN, 3, 1, false, false), { None }));
complex_cls->giveAttr("__mod__", new BoxedFunction(boxRTFunction((void*)complexMod, UNKNOWN, 2)));
complex_cls->giveAttr("__divmod__", new BoxedFunction(boxRTFunction((void*)complexDivmod, BOXED_TUPLE, 2)));
complex_cls->giveAttr("__floordiv__", new BoxedFunction(boxRTFunction((void*)complexFloordiv, UNKNOWN, 2)));
complex_cls->giveAttr("__truediv__", new BoxedFunction(boxRTFunction((void*)complexDivComplex, BOXED_COMPLEX, 2)));
complex_cls->giveAttr("conjugate", new BoxedFunction(boxRTFunction((void*)complexConjugate, BOXED_COMPLEX, 1)));
complex_cls->giveAttr("__coerce__", new BoxedFunction(boxRTFunction((void*)complexCoerce, UNKNOWN, 2)));
complex_cls->giveAttr("__abs__", new BoxedFunction(boxRTFunction((void*)complexAbs, BOXED_FLOAT, 1)));
complex_cls->giveAttr("__getnewargs__", new BoxedFunction(boxRTFunction((void*)complexGetnewargs, BOXED_TUPLE, 1)));
complex_cls->giveAttr("__nonzero__", new BoxedFunction(boxRTFunction((void*)complexNonzero, BOXED_BOOL, 1)));
complex_cls->giveAttr("__eq__", new BoxedFunction(boxRTFunction((void*)complexEq, UNKNOWN, 2)));
complex_cls->giveAttr("__ne__", new BoxedFunction(boxRTFunction((void*)complexNe, UNKNOWN, 2)));
complex_cls->giveAttr("__le__", new BoxedFunction(boxRTFunction((void*)complexLe, UNKNOWN, 2)));
complex_cls->giveAttr("__lt__", new BoxedFunction(boxRTFunction((void*)complexLt, UNKNOWN, 2)));
complex_cls->giveAttr("__ge__", new BoxedFunction(boxRTFunction((void*)complexGe, UNKNOWN, 2)));
complex_cls->giveAttr("__gt__", new BoxedFunction(boxRTFunction((void*)complexGt, UNKNOWN, 2)));
complex_cls->giveAttr("__neg__", new BoxedFunction(boxRTFunction((void*)complexNeg, BOXED_COMPLEX, 1)));
complex_cls->giveAttr("__pos__", new BoxedFunction(boxRTFunction((void*)complexPos, BOXED_COMPLEX, 1)));
complex_cls->giveAttr("__hash__", new BoxedFunction(boxRTFunction((void*)complexHash, BOXED_INT, 1)));
complex_cls->giveAttr("__abs__", new BoxedFunction(boxRTFunction((void*)complexAbs, BOXED_FLOAT, 1)));
complex_cls->giveAttr("__str__", new BoxedFunction(boxRTFunction((void*)complexStr, STR, 1)));
complex_cls->giveAttr("__repr__", new BoxedFunction(boxRTFunction((void*)complexRepr, STR, 1)));
complex_cls->giveAttr("real",
new BoxedMemberDescriptor(BoxedMemberDescriptor::DOUBLE, offsetof(BoxedComplex, real)));
complex_cls->giveAttr("imag",
new BoxedMemberDescriptor(BoxedMemberDescriptor::DOUBLE, offsetof(BoxedComplex, imag)));
for (auto& md : complex_methods) {
complex_cls->giveAttr(md.ml_name, new BoxedMethodDescriptor(&md, complex_cls));
}
complex_cls->freeze();
complex_cls->tp_as_number->nb_negative = (unaryfunc)complex_neg;
complex_cls->tp_richcompare = complex_richcompare;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment