Commit bd361100 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge pull request #1023 from Daetalus/test_cmath

Add cmath module and fix some bugs discovered in test_cmath.
parents bf2bae9d 829f6a95
......@@ -36,6 +36,7 @@ file(GLOB_RECURSE STDMODULE_SRCS Modules
bufferedio.c
bytesio.c
cache.c
cmathmodule.c
connection.c
cStringIO.c
cursor.c
......@@ -83,6 +84,7 @@ file(GLOB_RECURSE STDOBJECT_SRCS Objects
bytes_methods.c
capsule.c
cobject.c
complexobject.c
dictproxy.c
exceptions.c
floatobject.c
......
# expected: fail
from test.test_support import run_unittest
from test.test_math import parse_testfile, test_file
from test.test_math import parse_testfile
import unittest
import os, sys
import cmath, math
from cmath import phase, polar, rect, pi
INF = float('inf')
NAN = float('nan')
# Pyston change: pull testcases file from source directory
# locate file with test values
if __name__ == '__main__':
file = sys.argv[0]
else:
file = __file__
test_dir = os.path.dirname(file) or os.curdir
test_file = os.path.join(test_dir, 'cmath_testcases.txt')
complex_zeros = [complex(x, y) for x in [0.0, -0.0] for y in [0.0, -0.0]]
complex_infinities = [complex(x, y) for x, y in [
(INF, 0.0), # 1st quadrant
......
This diff is collapsed.
......@@ -39,6 +39,13 @@ def bz2_ext():
"Modules/bz2module.c",
]), libraries = ['bz2'])
@unique
def cmath_ext():
return Extension("cmath", sources = map(relpath, [
"Modules/cmathmodule.c",
]))
@unique
def ctypes_ext():
ext = Extension("_ctypes", sources = map(relpath, [
......@@ -144,6 +151,7 @@ ext_modules = [future_builtins_ext(),
pyexpat_ext(),
elementtree_ext(),
bz2_ext(),
cmath_ext(),
ctypes_ext(),
ctypes_test_ext(),
grp_ext(),
......
......@@ -95,7 +95,7 @@ extern "C" Box* abs_(Box* x) {
return boxInt(n >= 0 ? n : -n);
} else if (x->cls == float_cls) {
double d = static_cast<BoxedFloat*>(x)->d;
return boxFloat(d >= 0 ? d : -d);
return boxFloat(std::abs(d));
} else if (x->cls == long_cls) {
return longAbs(static_cast<BoxedLong*>(x));
} else {
......
......@@ -21,6 +21,8 @@
#include "runtime/objmodel.h"
#include "runtime/types.h"
extern "C" PyObject* complex_pow(PyObject* v, PyObject* w, PyObject* z) noexcept;
namespace pyston {
static inline void raiseDivZeroExc() {
......@@ -398,147 +400,15 @@ static void _addFunc(const char* name, ConcreteCompilerType* rtn_type, void* com
complex_cls->giveAttr(name, new BoxedFunction(md));
}
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);
PyObject* res = complex_pow(lhs, _rhs, mod);
if (res == NULL)
throwCAPIException();
return res;
}
Box* complexRpow(BoxedComplex* _lhs, Box* _rhs) {
......@@ -613,34 +483,19 @@ Box* complexConjugate(BoxedComplex* self) {
return new BoxedComplex(self->real, -self->imag);
}
Box* complexAbs(BoxedComplex* self) {
if (!PyComplex_Check(self))
Box* complexAbs(BoxedComplex* _self) {
if (!PyComplex_Check(_self))
raiseExcHelper(TypeError, "descriptor '__abs__' requires a 'complex' object but received a '%s'",
getTypeName(self));
getTypeName(_self));
double result;
Py_complex self = PyComplex_AsCComplex(_self);
result = c_abs(self);
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)) {
if (errno == ERANGE) {
raiseExcHelper(OverflowError, "absolute value too large");
}
return boxFloat(result);
return PyFloat_FromDouble(result);
}
Box* complexGetnewargs(BoxedComplex* self) {
......
......@@ -760,6 +760,14 @@ Box* floatPos(BoxedFloat* self) {
return PyFloat_FromDouble(self->d);
}
Box* floatAbs(BoxedFloat* self) {
if (!PyFloat_Check(self))
raiseExcHelper(TypeError, "descriptor '__abs__' requires a 'float' object but received a '%s'",
getTypeName(self));
double res = std::abs(self->d);
return boxFloat(res);
}
bool floatNonzeroUnboxed(BoxedFloat* self) {
assert(self->cls == float_cls);
return self->d != 0.0;
......@@ -1614,6 +1622,7 @@ void setupFloat() {
float_cls->giveAttr("__gt__", new BoxedFunction(FunctionMetadata::create((void*)floatGt, UNKNOWN, 2)));
float_cls->giveAttr("__neg__", new BoxedFunction(FunctionMetadata::create((void*)floatNeg, BOXED_FLOAT, 1)));
float_cls->giveAttr("__pos__", new BoxedFunction(FunctionMetadata::create((void*)floatPos, BOXED_FLOAT, 1)));
float_cls->giveAttr("__abs__", new BoxedFunction(FunctionMetadata::create((void*)floatAbs, BOXED_FLOAT, 1)));
FunctionMetadata* nonzero = FunctionMetadata::create((void*)floatNonzeroUnboxed, BOOL, 1);
nonzero->addVersion((void*)floatNonzero, UNKNOWN);
......
......@@ -894,6 +894,14 @@ extern "C" Box* intInt(BoxedInt* self) {
return boxInt(self->n);
}
Box* intFloat(BoxedInt* self) {
if (!PyInt_Check(self))
raiseExcHelper(TypeError, "descriptor '__float__' requires a 'int' object but received a '%s'",
getTypeName(self));
return boxFloat(self->n);
}
extern "C" Box* intIndex(BoxedInt* v) {
if (PyInt_CheckExact(v))
return v;
......@@ -1180,6 +1188,7 @@ void setupInt() {
int_cls->giveAttr("__trunc__", new BoxedFunction(FunctionMetadata::create((void*)intTrunc, BOXED_INT, 1)));
int_cls->giveAttr("__index__", new BoxedFunction(FunctionMetadata::create((void*)intIndex, BOXED_INT, 1)));
int_cls->giveAttr("__int__", new BoxedFunction(FunctionMetadata::create((void*)intInt, BOXED_INT, 1)));
int_cls->giveAttr("__float__", new BoxedFunction(FunctionMetadata::create((void*)intFloat, BOXED_FLOAT, 1)));
auto int_new = FunctionMetadata::create((void*)intNew<CXX>, UNKNOWN, 3, false, false,
ParamNames({ "", "x", "base" }, "", ""), CXX);
......
......@@ -42,7 +42,6 @@ test_cfgparser [unknown]
test_cgi [unknown]
test_class needs ellipsis
test_cl [unknown]
test_cmath [unknown]
test_cmd_line_script [unknown]
test_cmd_line [unknown]
test_codecencodings_cn [unknown]
......
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