Commits

Jasper Schulz committed 7623c78

some work on complex numbers

  • Participants
  • Parent commits 4a0dbf4
  • Branches numpypy-complex2

Comments (0)

Files changed (7)

File pypy/module/micronumpy/__init__.py

         'unicode_': 'interp_boxes.W_UnicodeBox',
         'void': 'interp_boxes.W_VoidBox',
         'complexfloating': 'interp_boxes.W_ComplexFloatingBox',
-        'complex128': 'interp_boxes.W_Complex128Box'
+        'complex_': 'interp_boxes.W_Complex128Box',
+        'complex128': 'interp_boxes.W_Complex128Box',
+        'complex64': 'interp_boxes.W_Complex64Box',
     }
 
     # ufuncs
         ("arccosh", "arccosh"),
         ("arcsinh", "arcsinh"),
         ("arctanh", "arctanh"),
+        ("conj", "conjugate"),
+        ("conjugate", "conjugate"),
         ("copysign", "copysign"),
         ("cos", "cos"),
         ("cosh", "cosh"),

File pypy/module/micronumpy/interp_boxes.py

     def convert_to(self, dtype):
         return dtype.box(self.value)
 
+class ComplexBox(object):
+    _mixin_ = True
+
+    def __init__(self, real, imag):
+        self.real = real
+        self.imag = imag
+
+    def descr_get_real(self, space):
+        return space.wrap(self._COMPONENTS_BOX(self.real))
+
+    def descr_get_imag(self, space):
+        return space.wrap(self._COMPONENTS_BOX(self.imag))
+
+    def convert_to(self, dtype):
+        return dtype.box_complex(self.real, self.imag)
+
 
 class W_GenericBox(Wrappable):
     _attrs_ = ()
 class W_ComplexFloatingBox(W_InexactBox):
     _attrs_ = ()
 
-class W_Complex128Box(W_ComplexFloatingBox):
+class W_Complex64Box(ComplexBox, W_ComplexFloatingBox):
+    descr__new__, _get_dtype = new_dtype_getter("complex64")
+    _COMPONENTS_BOX = W_Float32Box
+
+class W_Complex128Box(ComplexBox, W_ComplexFloatingBox):
     descr__new__, _get_dtype = new_dtype_getter("complex128")
+    _COMPONENTS_BOX = W_Float64Box
 
-    def __init__(self, real, imag):
-        self.real = real
-        self.imag = imag
-
-    def descr_get_real(self, space):
-        return space.wrap(self.real)
-
-    def descr_get_imag(self, space):
-        return space.wrap(self.imag)
-
+    
 
 W_GenericBox.typedef = TypeDef("generic",
     __module__ = "numpypy",
     __module__ = "numpypy",
 )
 
+
 W_Complex128Box.typedef = TypeDef("complex128", (W_ComplexFloatingBox.typedef, complex_typedef),
     __module__ = "numpypy",
     __new__ = interp2app(W_Complex128Box.descr__new__.im_func),
     real = GetSetProperty(W_Complex128Box.descr_get_real),
     imag = GetSetProperty(W_Complex128Box.descr_get_imag),
+)
+
+W_Complex64Box.typedef = TypeDef("complex64", (W_ComplexFloatingBox.typedef),
+    __module__ = "numpypy",
+    __new__ = interp2app(W_Complex64Box.descr__new__.im_func),
+    real = GetSetProperty(W_Complex64Box.descr_get_real),
+    imag = GetSetProperty(W_Complex64Box.descr_get_imag),
 )

File pypy/module/micronumpy/interp_dtype.py

     def box(self, value):
         return self.itemtype.box(value)
 
+    @specialize.argtype(1, 2)
+    def box_complex(self, real, imag):
+        return self.itemtype.box_complex(real, imag)
+
+
     def coerce(self, space, w_item):
         return self.itemtype.coerce(space, self, w_item)
 
             alternate_constructors=[space.w_float],
             aliases=["float"],
         )
+        # self.w_float128dtype = W_Dtype(
+        #     types.Float128(),
+        #     num=13,
+        #     kind=FLOATINGLTR,
+        #     name="float128",
+        #     ...
+        # )
+        self.w_complex64dtype = W_Dtype(
+            types.Complex64(),
+            num=14,
+            kind=FLOATINGLTR,
+            name="complex64",
+            char="F",
+            w_box_type = space.gettypefor(interp_boxes.W_Complex64Box),
+        )
         self.w_complex128dtype = W_Dtype(
             types.Complex128(),
             num=15,
             kind=FLOATINGLTR,
             name="complex128",
-            char="c",
+            char="D",
             w_box_type = space.gettypefor(interp_boxes.W_Complex128Box),
             alternate_constructors=[space.w_complex],
             aliases=["complex"],
             self.w_int16dtype, self.w_uint16dtype, self.w_int32dtype,
             self.w_uint32dtype, self.w_longdtype, self.w_ulongdtype,
             self.w_int64dtype, self.w_uint64dtype,
-            self.w_float32dtype, self.w_float64dtype, self.w_complex128dtype,
+            self.w_float32dtype, self.w_float64dtype, self.w_complex64dtype,
+            self.w_complex128dtype,
             self.w_stringdtype, self.w_unicodedtype,
             self.w_voiddtype,
         ]

File pypy/module/micronumpy/interp_ufuncs.py

     bool_dtype = interp_dtype.get_dtype_cache(space).w_booldtype
     long_dtype = interp_dtype.get_dtype_cache(space).w_longdtype
     int64_dtype = interp_dtype.get_dtype_cache(space).w_int64dtype
+    complex_type = interp_dtype.get_dtype_cache(space).w_complex128dtype
 
     if isinstance(w_obj, interp_boxes.W_GenericBox):
         dtype = w_obj.get_dtype(space)
             current_guess is long_dtype or current_guess is int64_dtype):
             return int64_dtype
         return current_guess
+    elif space.isinstance_w(w_obj, space.w_complex):
+        if (current_guess is None or current_guess is bool_dtype or
+            current_guess is long_dtype or current_guess is int64_dtype or
+            current_guess is complex_type):
+            return complex_type
+        return current_guess
+
     return interp_dtype.get_dtype_cache(space).w_float64dtype
 
 
             ("sign", "sign", 1, {"promote_bools": True}),
             ("signbit", "signbit", 1, {"bool_result": True}),
             ("reciprocal", "reciprocal", 1),
+            ("conjugate", "conj", 1),
 
             ("fabs", "fabs", 1, {"promote_to_float": True}),
             ("fmax", "fmax", 2, {"promote_to_float": True}),

File pypy/module/micronumpy/test/test_dtypes.py

         assert numpy.complexfloating.__mro__ == (numpy.complexfloating,
             numpy.inexact, numpy.number, numpy.generic, object)
 
+    def test_complex_format(self):
+        import _numpypy as numpy
+        
+        for complex_ in (numpy.complex128, numpy.complex64,):
+            for real, imag, should in [
+                (1, 2, '(1+2j)'),
+                (0, 1, '1j'),
+                (1, 0, '(1+0j)'),
+                (-1, -2, '(-1-2j)'),
+                (0.5, -0.75, '(0.5-0.75j)'),
+                #xxx
+                #(numpy.inf, numpy.inf, '(inf+inf*j)'),
+                ]:
+            
+                c = complex_(complex(real, imag))
+                assert c == complex(real, imag)
+                assert c.real == real
+                assert c.imag == imag
+                assert repr(c) == should
+            
+        real, imag, should = (1e100, 3e66, '(1e+100+3e+66j)')
+        c128 = numpy.complex128(complex(real, imag))
+        assert type(c128.real) is type(c128.imag) is numpy.float64
+        assert c128.real == real
+        assert c128.imag == imag
+        assert repr(c128) == should
+
+        c64 = numpy.complex64(complex(real, imag))
+        assert repr(c64.real) == 'inf'  
+        assert type(c64.real) is type(c64.imag) is numpy.float32
+        assert repr(c64.imag).startswith('inf')
+        assert repr(c64) in ('(inf+inf*j)', '(inf+infj)')
+
+
+        assert numpy.complex128(1.2) == numpy.complex128(complex(1.2, 0))
+        assert numpy.complex64(1.2) == numpy.complex64(complex(1.2, 0))
+
     def test_complex(self):
         import _numpypy as numpy
 
+        assert numpy.complex_ is numpy.complex128
+        assert numpy.complex64.__mro__ == (numpy.complex64,
+            numpy.complexfloating, numpy.inexact, numpy.number, numpy.generic,
+            object)
         assert numpy.complex128.__mro__ == (numpy.complex128,
             numpy.complexfloating, numpy.inexact, numpy.number, numpy.generic,
             complex, object)
 
-        for real, imag, should in [
-            (1, 2, '(1+2j)'),
-            (0, 1, '1j'),
-            (1, 0, '(1+0j)'),
-            (-1, -2, '(-1-2j)')
-            ]:
-
-            c = numpy.complex128(complex(real, imag))
-            assert c.real == real
-            assert c.imag == imag
-            assert repr(c) == should
-
-    def test_complex_dtype(self):
-        import _numpypy as numpy
-
-        assert numpy.dtype(complex) is numpy.dtype("complex")
+        assert numpy.dtype(complex).type is numpy.complex128
+        assert numpy.dtype("complex").type is numpy.complex128
 
     def test_subclass_type(self):
         import _numpypy as numpy

File pypy/module/micronumpy/test/test_ufuncs.py

         assert logaddexp2(float('-inf'), float('inf')) == float('inf')
         assert logaddexp2(float('inf'), float('-inf')) == float('inf')
         assert logaddexp2(float('inf'), float('inf')) == float('inf')
+
+    def test_conjugate(self):
+        from _numpypy import conj, conjugate, complex128, complex64
+        import _numpypy as np
+
+        c0 = complex128(complex(2.5, 0))
+        c1 = complex64(complex(1, 2))
+
+        assert conj is conjugate
+        assert conj(c0) == c0
+        assert conj(c1) == complex(1, -2)
+        assert conj(1) == 1
+        assert conj(-3) == -3
+        assert conj(float('-inf')) == float('-inf')
+
+
+        assert np.conjugate(1+2j) == 1-2j
+
+        x = np.eye(2) + 1j * np.eye(2)
+        for a, b in zip(np.conjugate(x), np.array([[ 1.-1.j,  0.-0.j], [ 0.-0.j,  1.-1.j]])):
+            assert a[0] == b[0]
+            assert a[1] == b[1]
+
+
+    def test_complex(self):
+        from _numpypy import (array, complex128, complex64, add, subtract as sub, multiply,
+            divide, negative, conjugate, conj, abs)
+        from _numpypy import equal, not_equal, greater, greater_equal, less, less_equal
+
+        for complex_ in complex128, complex64:
+
+            O = complex(0, 0)
+            c0 = complex_(complex(2.5, 0))
+            c1 = complex_(complex(1, 2))
+            c2 = complex_(complex(3, 4))
+            c3 = complex_(complex(-3, -3))
+
+            assert equal(c0, 2.5)
+            assert equal(c1, complex_(complex(1, 2)))
+            assert equal(c1, complex(1, 2))
+            assert equal(c1, c1)
+            assert not_equal(c1, c2)
+            assert not equal(c1, c2)
+
+            assert less(c1, c2)
+            assert less_equal(c1, c2)
+            assert less_equal(c1, c1)
+            assert not less(c1, c1)
+
+            assert greater(c2, c1)
+            assert greater_equal(c2, c1)
+            assert not greater(c1, c2)
+
+            assert add(c1, c2) == complex_(complex(4, 6))
+            assert add(c1, c2) == complex(4, 6)
+            
+            assert sub(c0, c0) == sub(c1, c1) == 0
+            assert sub(c1, c2) == complex(-2, -2)
+            assert negative(complex(1,1)) == complex(-1, -1)
+            assert negative(complex(0, 0)) == 0
+            
+
+            assert multiply(1, c1) == c1
+            assert multiply(2, c2) == complex(6, 8)
+            assert multiply(c1, c2) == complex(-5, 10)
+
+            assert divide(c0, 1) == c0
+            assert divide(c2, -1) == negative(c2)
+            assert divide(c1, complex(0, 1)) == complex(2, -1)
+            n = divide(c1, O)
+            assert repr(n.real) == 'nan'
+            assert repr(n.imag).startswith('nan') #can be nan*j or nanj
+            assert divide(c0, c0) == 1
+            res = divide(c2, c1)
+            assert abs(res.real-2.2) < 0.001
+            assert abs(res.imag+0.4) < 0.001
+
+            assert abs(c0) == 2.5
+            assert abs(c2) == 5
+
+
+
+    def test_complex_math(self):
+        # from _numpypy import 
+        pass

File pypy/module/micronumpy/types.py

 from pypy.module.micronumpy import interp_boxes
 from pypy.objspace.std.floatobject import float2string
 from pypy.objspace.std.complexobject import W_ComplexObject, str_format
-from pypy.rlib import rfloat, libffi, clibffi
+from pypy.rlib import rfloat, libffi, clibffi, rcomplex
 from pypy.rlib.objectmodel import specialize, we_are_translated
 from pypy.rlib.rarithmetic import widen, byteswap
 from pypy.rpython.lltypesystem import lltype, rffi
 from pypy.rlib.rstruct.runpack import runpack
 from pypy.tool.sourcetools import func_with_new_name
 from pypy.rlib import jit
+from pypy.module import cmath
 
 
 VOID_STORAGE = lltype.Array(lltype.Char, hints={'nolength': True,
         )
     return dispatcher
 
+def complex_unary_op(func):
+    specialize.argtype(1)(func)
+    @functools.wraps(func)
+    def dispatcher(self, v):
+        return self.box_complex(
+            *func(
+                self,
+                self.for_computation(self.unbox(v))
+            )
+        )
+    return dispatcher
+
 def raw_unary_op(func):
     specialize.argtype(1)(func)
     @functools.wraps(func)
         )
     return dispatcher
 
+def complex_binary_op(func):
+    specialize.argtype(1, 2)(func)
+    @functools.wraps(func)
+    def dispatcher(self, v1, v2):
+        return self.box_complex(
+            *func(
+                self,
+                self.for_computation(self.unbox(v1)),
+                self.for_computation(self.unbox(v2)),
+            )
+        )
+    return dispatcher
+
 def raw_binary_op(func):
     specialize.argtype(1, 2)(func)
     @functools.wraps(func)
         return -v
 
     @simple_unary_op
+    def conj(self, v):
+        return v
+
+    @simple_unary_op
     def abs(self, v):
         return abs(v)
 
     BoxType = interp_boxes.W_Float64Box
     format_code = "d"
 
-class Complex128(BaseType):
+class ComplexFloating(object):
+    _mixin_ = True
+    _attrs_ = ()
+
+    def _coerce(self, space, w_item):
+        w_item = space.call_function(space.w_complex, w_item)
+        real, imag = space.unpackcomplex(w_item)
+        return self.box_complex(real, imag)
+
+    def coerce(self, space, dtype, w_item):
+        if isinstance(w_item, self.BoxType):
+            return w_item
+        return self.coerce_subtype(space, space.gettypefor(self.BoxType), w_item)
+
+    def coerce_subtype(self, space, w_subtype, w_item):
+        w_tmpobj = self._coerce(space, w_item)
+        w_obj = space.allocate_instance(self.BoxType, w_subtype)
+        assert isinstance(w_obj, self.BoxType)
+        w_obj.__init__(w_tmpobj.real, w_tmpobj.imag)
+        return w_obj
+
+    def str_format(self, box):
+        real, imag = self.for_computation(self.unbox(box))
+        imag_str = str_format(imag) + 'j'
+        
+        # (0+2j) => 2j
+        if real == 0:
+            return imag_str        
+
+        real_str = str_format(real)
+        op = '+' if imag >= 0 else ''
+        return ''.join(['(', real_str, op, imag_str, ')'])
+
+    def for_computation(self, v):   
+        return float(v[0]), float(v[1])
+
+    def get_element_size(self):
+        return 2 * rffi.sizeof(self._COMPONENTS_T)
+
+    @specialize.argtype(1)
+    def box(self, value):
+        return self.BoxType(
+            rffi.cast(self._COMPONENTS_T, value),
+            rffi.cast(self._COMPONENTS_T, 0.0))
+
+    @specialize.argtype(1, 2)
+    def box_complex(self, real, imag):
+        return self.BoxType(
+            rffi.cast(self._COMPONENTS_T, real),
+            rffi.cast(self._COMPONENTS_T, imag))
+
+    def unbox(self, box):
+        assert isinstance(box, self.BoxType)
+        return box.real, box.imag
+
+    @complex_binary_op
+    def add(self, v1, v2):
+        return rcomplex.c_add(v1, v2)
+
+    @complex_binary_op
+    def sub(self, v1, v2):
+        return rcomplex.c_sub(v1, v2)
+
+    @complex_binary_op
+    def mul(self, v1, v2):
+        return rcomplex.c_mul(v1, v2)
+    
+    @complex_binary_op
+    def div(self, v1, v2):
+        try:
+            return rcomplex.c_div(v1, v2)
+        except ZeroDivisionError:
+            return rfloat.NAN, rfloat.NAN
+
+
+
+    @complex_unary_op
+    def pos(self, v):
+        return v
+
+    @complex_unary_op
+    def neg(self, v):
+        return -v[0], -v[1]
+
+    @complex_unary_op
+    def conj(self, v):
+        return v[0], -v[1]
+
+    @raw_unary_op
+    def abs(self, v):
+        return rcomplex.c_abs(v[0], v[1])
+
+    @raw_unary_op
+    def isnan(self, v):
+        '''a complex number is nan if one of the parts is nan'''
+        return rfloat.isnan(v[0]) or rfloat.isnan(v[1])
+
+    @raw_unary_op
+    def isinf(self, v):
+        '''a complex number is inf if one of the parts is inf'''
+        return rfloat.isinf(v[0]) or rfloat.isinf(v[1])
+
+    def _eq(self, v1, v2):
+        return v1[0] == v2[0] and v1[1] == v2[1]
+
+    @raw_binary_op
+    def eq(self, v1, v2):
+        #compare the parts, so nan == nan is False
+        return self._eq(v1, v2)
+
+    @raw_binary_op
+    def ne(self, v1, v2):
+        return not self._eq(v1, v2)
+
+    def _lt(self, v1, v2):
+        (r1, i1), (r2, i2) = v1, v2
+        if r1 < r2:
+            return True
+        elif not r1 <= r2:
+            return False
+        return i1 < i2
+
+    @raw_binary_op
+    def lt(self, v1, v2):
+        return self._lt(v1, v2)
+
+    @raw_binary_op
+    def le(self, v1, v2):
+        return self._lt(v1, v2) or self._eq(v1, v2) 
+
+    @raw_binary_op
+    def gt(self, v1, v2):
+        return self._lt(v2, v1)
+
+    @raw_binary_op
+    def ge(self, v1, v2):
+        return self._lt(v2, v1) or self._eq(v2, v1) 
+
+    @raw_binary_op
+    def logical_and(self, v1, v2):
+        return bool(v1) and bool(v2)
+
+    @raw_binary_op
+    def logical_or(self, v1, v2):
+        return bool(v1) or bool(v2)
+
+    @raw_unary_op
+    def logical_not(self, v):
+        return not bool(v)
+
+    @raw_binary_op
+    def logical_xor(self, v1, v2):
+        return bool(v1) ^ bool(v2)
+
+    def bool(self, v):
+        return bool(self.for_computation(self.unbox(v)))
+
+    @simple_binary_op
+    def max(self, v1, v2):
+        return max(v1, v2)
+
+    @simple_binary_op
+    def min(self, v1, v2):
+        return min(v1, v2)
+
+
+    @simple_binary_op
+    def floordiv(self, v1, v2):
+        try:
+            r, i = rcomplex.c_div(v1, v2)
+            return math.floor(r), 0
+        except ZeroDivisionError:
+            return rfloat.NAN, 0
+
+    @simple_binary_op
+    def mod(self, v1, v2):
+        return math.fmod(v1, v2)
+
+    @simple_binary_op
+    def pow(self, v1, v2):
+        try:
+            return math.pow(v1, v2)
+        except ValueError:
+            return rfloat.NAN
+        except OverflowError:
+            if math.modf(v2)[0] == 0 and math.modf(v2 / 2)[0] != 0:
+                # Odd integer powers result in the same sign as the base
+                return rfloat.copysign(rfloat.INFINITY, v1)
+            return rfloat.INFINITY
+
+    @simple_binary_op
+    def copysign(self, v1, v2):
+        return math.copysign(v1, v2)
+
+    @simple_unary_op
+    def sign(self, v):
+        if v == 0.0:
+            return 0.0
+        return rfloat.copysign(1.0, v)
+
+    @raw_unary_op
+    def signbit(self, v):
+        return rfloat.copysign(1.0, v) < 0.0
+
+    @simple_unary_op
+    def fabs(self, v):
+        return math.fabs(v)
+
+    @simple_binary_op
+    def fmax(self, v1, v2):
+        if math.isnan(v1):
+            return v1
+        elif math.isnan(v2):
+            return v2
+        return max(v1, v2)
+
+    @simple_binary_op
+    def fmin(self, v1, v2):
+        if math.isnan(v1):
+            return v1
+        elif math.isnan(v2):
+            return v2
+        return min(v1, v2)
+
+    @simple_binary_op
+    def fmod(self, v1, v2):
+        try:
+            return math.fmod(v1, v2)
+        except ValueError:
+            return rfloat.NAN
+
+    @simple_unary_op
+    def reciprocal(self, v):
+        if v == 0.0:
+            return rfloat.copysign(rfloat.INFINITY, v)
+        return 1.0 / v
+
+    @simple_unary_op
+    def floor(self, v):
+        return math.floor(v)
+
+    @simple_unary_op
+    def ceil(self, v):
+        return math.ceil(v)
+
+    @simple_unary_op
+    def trunc(self, v):
+        if v < 0:
+            return math.ceil(v)
+        else:
+            return math.floor(v)
+
+    @simple_unary_op
+    def exp(self, v):
+        try:
+            return math.exp(v)
+        except OverflowError:
+            return rfloat.INFINITY
+
+    @simple_unary_op
+    def exp2(self, v):
+        try:
+            return math.pow(2, v)
+        except OverflowError:
+            return rfloat.INFINITY
+
+    @simple_unary_op
+    def expm1(self, v):
+        try:
+            return rfloat.expm1(v)
+        except OverflowError:
+            return rfloat.INFINITY
+
+    @simple_unary_op
+    def sin(self, v):
+        return math.sin(v)
+
+    @simple_unary_op
+    def cos(self, v):
+        return math.cos(v)
+
+    @simple_unary_op
+    def tan(self, v):
+        return math.tan(v)
+
+    @simple_unary_op
+    def arcsin(self, v):
+        if not -1.0 <= v <= 1.0:
+            return rfloat.NAN
+        return math.asin(v)
+
+    @simple_unary_op
+    def arccos(self, v):
+        if not -1.0 <= v <= 1.0:
+            return rfloat.NAN
+        return math.acos(v)
+
+    @simple_unary_op
+    def arctan(self, v):
+        return math.atan(v)
+
+    @simple_binary_op
+    def arctan2(self, v1, v2):
+        return math.atan2(v1, v2)
+
+    @simple_unary_op
+    def sinh(self, v):
+        return math.sinh(v)
+
+    @simple_unary_op
+    def cosh(self, v):
+        return math.cosh(v)
+
+    @simple_unary_op
+    def tanh(self, v):
+        return math.tanh(v)
+
+    @simple_unary_op
+    def arcsinh(self, v):
+        return math.asinh(v)
+
+    @simple_unary_op
+    def arccosh(self, v):
+        if v < 1.0:
+            return rfloat.NAN
+        return math.acosh(v)
+
+    @simple_unary_op
+    def arctanh(self, v):
+        if v == 1.0 or v == -1.0:
+            return math.copysign(rfloat.INFINITY, v)
+        if not -1.0 < v < 1.0:
+            return rfloat.NAN
+        return math.atanh(v)
+
+    @simple_unary_op
+    def sqrt(self, v):
+        try:
+            return math.sqrt(v)
+        except ValueError:
+            return rfloat.NAN
+
+    @simple_unary_op
+    def square(self, v):
+        return v*v
+
+    @raw_unary_op
+    def isnan(self, v):
+        return rfloat.isnan(v)
+
+    @raw_unary_op
+    def isinf(self, v):
+        return rfloat.isinf(v)
+
+    @raw_unary_op
+    def isneginf(self, v):
+        return rfloat.isinf(v) and v < 0
+
+    @raw_unary_op
+    def isposinf(self, v):
+        return rfloat.isinf(v) and v > 0
+
+    @raw_unary_op
+    def isfinite(self, v):
+        return not (rfloat.isinf(v) or rfloat.isnan(v))
+
+    @simple_unary_op
+    def radians(self, v):
+        return v * degToRad
+    deg2rad = radians
+
+    @simple_unary_op
+    def degrees(self, v):
+        return v / degToRad
+
+    @simple_unary_op
+    def log(self, v):
+        try:
+            return math.log(v)
+        except ValueError:
+            if v == 0.0:
+                # CPython raises ValueError here, so we have to check
+                # the value to find the correct numpy return value
+                return -rfloat.INFINITY
+            return rfloat.NAN
+
+    @simple_unary_op
+    def log2(self, v):
+        try:
+            return math.log(v) / log2
+        except ValueError:
+            if v == 0.0:
+                # CPython raises ValueError here, so we have to check
+                # the value to find the correct numpy return value
+                return -rfloat.INFINITY
+            return rfloat.NAN
+
+    @simple_unary_op
+    def log10(self, v):
+        try:
+            return math.log10(v)
+        except ValueError:
+            if v == 0.0:
+                # CPython raises ValueError here, so we have to check
+                # the value to find the correct numpy return value
+                return -rfloat.INFINITY
+            return rfloat.NAN
+
+    @simple_unary_op
+    def log1p(self, v):
+        try:
+            return rfloat.log1p(v)
+        except OverflowError:
+            return -rfloat.INFINITY
+        except ValueError:
+            return rfloat.NAN
+
+    @simple_binary_op
+    def logaddexp(self, v1, v2):
+        tmp = v1 - v2
+        if tmp > 0:
+            return v1 + rfloat.log1p(math.exp(-tmp))
+        elif tmp <= 0:
+            return v2 + rfloat.log1p(math.exp(tmp))
+        else:
+            return v1 + v2
+
+    def npy_log2_1p(self, v):
+        return log2e * rfloat.log1p(v)
+
+    @simple_binary_op
+    def logaddexp2(self, v1, v2):
+        tmp = v1 - v2
+        if tmp > 0:
+            return v1 + self.npy_log2_1p(math.pow(2, -tmp))
+        if tmp <= 0:
+            return v2 + self.npy_log2_1p(math.pow(2, tmp))
+        else:
+            return v1 + v2
+
+class Complex64(ComplexFloating, BaseType):
     _attrs_ = ()
 
     T = rffi.CHAR
+    _COMPONENTS_T = rffi.FLOAT
+    BoxType = interp_boxes.W_Complex64Box
+
+
+
+NonNativeComplex64 = Complex64
+
+class Complex128(ComplexFloating, BaseType):
+    _attrs_ = ()
+
+    T = rffi.CHAR
+    _COMPONENTS_T = rffi.DOUBLE
     BoxType = interp_boxes.W_Complex128Box
 
-    def get_element_size(self):
-        return 2 * rffi.sizeof(rffi.DOUBLE)
-
-    def coerce_subtype(self, space, w_subtype, w_item):
-        real, imag = space.unpackcomplex(w_item)
-        w_obj = space.allocate_instance(self.BoxType, w_subtype)
-        assert isinstance(w_obj, self.BoxType)
-        w_obj.__init__(real, imag)
-        return w_obj
-
-    def str_format(self, box):
-        imag_str = str_format(box.imag) + 'j'
-        
-        # (0+2j) => 2j
-        if box.real == 0:
-            return imag_str        
-
-        real_str = str_format(box.real)
-        op = '+' if box.imag >= 0 else ''
-        return ''.join(['(', real_str, op, imag_str, ')'])
-
 
 NonNativeComplex128 = Complex128