[pypy-commit] pypy default: Merge branch 'fix-result-types'

rlamy noreply at buildbot.pypy.org
Sat May 30 02:02:29 CEST 2015


Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch: 
Changeset: r77699:6cc7f2175bcd
Date: 2015-05-30 00:58 +0100
http://bitbucket.org/pypy/pypy/changeset/6cc7f2175bcd/

Log:	Merge branch 'fix-result-types'

	* Refactor dtype casting and promotion rules for consistency and
	compatibility with CNumPy.
	* Refactor ufunc creation.
	* Implement np.promote_types().

diff too long, truncating to 2000 out of 2900 lines

diff --git a/pypy/module/micronumpy/__init__.py b/pypy/module/micronumpy/__init__.py
--- a/pypy/module/micronumpy/__init__.py
+++ b/pypy/module/micronumpy/__init__.py
@@ -24,6 +24,7 @@
         'result_type': 'casting.result_type',
         'can_cast': 'casting.can_cast',
         'min_scalar_type': 'casting.min_scalar_type',
+        'promote_types': 'casting.w_promote_types',
 
         'set_string_function': 'appbridge.set_string_function',
         'typeinfo': 'descriptor.get_dtype_cache(space).w_typeinfo',
diff --git a/pypy/module/micronumpy/arrayops.py b/pypy/module/micronumpy/arrayops.py
--- a/pypy/module/micronumpy/arrayops.py
+++ b/pypy/module/micronumpy/arrayops.py
@@ -1,11 +1,12 @@
 from pypy.interpreter.error import OperationError, oefmt
 from pypy.interpreter.gateway import unwrap_spec
-from pypy.module.micronumpy import loop, descriptor, ufuncs, support, \
-    constants as NPY
+from pypy.module.micronumpy import loop, descriptor, support
+from pypy.module.micronumpy import constants as NPY
 from pypy.module.micronumpy.base import convert_to_array, W_NDimArray
 from pypy.module.micronumpy.converters import clipmode_converter
 from pypy.module.micronumpy.strides import (
     Chunk, Chunks, shape_agreement, shape_agreement_multiple)
+from .casting import find_binop_result_dtype, find_result_type
 
 
 def where(space, w_arr, w_x=None, w_y=None):
@@ -84,8 +85,7 @@
         if arr.get_dtype().itemtype.bool(arr.get_scalar_value()):
             return x
         return y
-    dtype = ufuncs.find_binop_result_dtype(space, x.get_dtype(),
-                                                  y.get_dtype())
+    dtype = find_result_type(space, [x, y], [])
     shape = shape_agreement(space, arr.get_shape(), x)
     shape = shape_agreement(space, shape, y)
     out = W_NDimArray.from_shape(space, shape, dtype)
@@ -137,19 +137,8 @@
                 raise OperationError(space.w_ValueError, space.wrap(
                     "all the input array dimensions except for the "
                     "concatenation axis must match exactly"))
-        a_dt = arr.get_dtype()
-        if dtype.is_record() and a_dt.is_record():
-            # Record types must match
-            for f in dtype.fields:
-                if f not in a_dt.fields or \
-                             dtype.fields[f] != a_dt.fields[f]:
-                    raise OperationError(space.w_TypeError,
-                               space.wrap("invalid type promotion"))
-        elif dtype.is_record() or a_dt.is_record():
-            raise OperationError(space.w_TypeError,
-                        space.wrap("invalid type promotion"))
-        dtype = ufuncs.find_binop_result_dtype(space, dtype,
-                                                      arr.get_dtype())
+
+    dtype = find_result_type(space, args_w, [])
     # concatenate does not handle ndarray subtypes, it always returns a ndarray
     res = W_NDimArray.from_shape(space, shape, dtype, 'C')
     chunks = [Chunk(0, i, 1, i) for i in shape]
diff --git a/pypy/module/micronumpy/boxes.py b/pypy/module/micronumpy/boxes.py
--- a/pypy/module/micronumpy/boxes.py
+++ b/pypy/module/micronumpy/boxes.py
@@ -35,8 +35,8 @@
 def new_dtype_getter(num):
     @specialize.memo()
     def _get_dtype(space):
-        from pypy.module.micronumpy.descriptor import get_dtype_cache
-        return get_dtype_cache(space).dtypes_by_num[num]
+        from pypy.module.micronumpy.descriptor import num2dtype
+        return num2dtype(space, num)
 
     def descr__new__(space, w_subtype, w_value=None):
         from pypy.module.micronumpy.ctors import array
@@ -144,7 +144,7 @@
         return self
 
     def get_flags(self):
-        return (NPY.ARRAY_C_CONTIGUOUS | NPY.ARRAY_F_CONTIGUOUS | 
+        return (NPY.ARRAY_C_CONTIGUOUS | NPY.ARRAY_F_CONTIGUOUS |
                 NPY.ARRAY_WRITEABLE | NPY.ARRAY_OWNDATA)
 
     def item(self, space):
@@ -180,10 +180,11 @@
 
     def descr_getitem(self, space, w_item):
         from pypy.module.micronumpy.base import convert_to_array
-        if space.is_w(w_item, space.w_Ellipsis) or \
-                (space.isinstance_w(w_item, space.w_tuple) and
+        if space.is_w(w_item, space.w_Ellipsis):
+            return convert_to_array(space, self)
+        elif (space.isinstance_w(w_item, space.w_tuple) and
                     space.len_w(w_item) == 0):
-            return convert_to_array(space, self)
+            return self
         raise OperationError(space.w_IndexError, space.wrap(
             "invalid index to scalar variable"))
 
@@ -239,7 +240,7 @@
 
     # TODO: support all kwargs in ufuncs like numpy ufunc_object.c
     sig = None
-    cast = None
+    cast = 'unsafe'
     extobj = None
 
     def _unaryop_impl(ufunc_name):
diff --git a/pypy/module/micronumpy/casting.py b/pypy/module/micronumpy/casting.py
--- a/pypy/module/micronumpy/casting.py
+++ b/pypy/module/micronumpy/casting.py
@@ -1,16 +1,19 @@
 """Functions and helpers for converting between dtypes"""
 
 from rpython.rlib import jit
+from rpython.rlib.signature import signature, types as ann
 from pypy.interpreter.gateway import unwrap_spec
-from pypy.interpreter.error import oefmt
+from pypy.interpreter.error import oefmt, OperationError
 
 from pypy.module.micronumpy.base import W_NDimArray, convert_to_array
 from pypy.module.micronumpy import constants as NPY
-from pypy.module.micronumpy.ufuncs import (
-    find_binop_result_dtype, find_dtype_for_scalar)
 from .types import (
-    Bool, ULong, Long, Float64, Complex64, UnicodeType, VoidType, ObjectType)
-from .descriptor import get_dtype_cache, as_dtype, is_scalar_w
+    BaseType, Bool, ULong, Long, Float64, Complex64,
+    StringType, UnicodeType, VoidType, ObjectType,
+    int_types, float_types, complex_types, number_types, all_types)
+from .descriptor import (
+    W_Dtype, get_dtype_cache, as_dtype, is_scalar_w, variable_dtype,
+    new_string_dtype, new_unicode_dtype, num2dtype)
 
 @jit.unroll_safe
 def result_type(space, __args__):
@@ -21,12 +24,96 @@
     if not args_w:
         raise oefmt(space.w_ValueError,
             "at least one array or dtype is required")
+    arrays_w = []
+    dtypes_w = []
+    for w_arg in args_w:
+        if isinstance(w_arg, W_NDimArray):
+            arrays_w.append(w_arg)
+        elif is_scalar_w(space, w_arg):
+            w_scalar = as_scalar(space, w_arg)
+            w_arr = W_NDimArray.from_scalar(space, w_scalar)
+            arrays_w.append(w_arr)
+        else:
+            dtype = as_dtype(space, w_arg)
+            dtypes_w.append(dtype)
+    return find_result_type(space, arrays_w, dtypes_w)
+
+
+def find_result_type(space, arrays_w, dtypes_w):
+    # equivalent to PyArray_ResultType
+    if len(arrays_w) == 1 and not dtypes_w:
+        return arrays_w[0].get_dtype()
+    elif not arrays_w and len(dtypes_w) == 1:
+        return dtypes_w[0]
     result = None
-    for w_arg in args_w:
-        dtype = as_dtype(space, w_arg)
-        result = find_binop_result_dtype(space, result, dtype)
+    if not _use_min_scalar(arrays_w, dtypes_w):
+        for w_array in arrays_w:
+            if result is None:
+                result = w_array.get_dtype()
+            else:
+                result = promote_types(space, result, w_array.get_dtype())
+        for dtype in dtypes_w:
+            if result is None:
+                result = dtype
+            else:
+                result = promote_types(space, result, dtype)
+    else:
+        small_unsigned = False
+        for w_array in arrays_w:
+            dtype = w_array.get_dtype()
+            small_unsigned_scalar = False
+            if w_array.is_scalar() and dtype.is_number():
+                num, alt_num = w_array.get_scalar_value().min_dtype()
+                small_unsigned_scalar = (num != alt_num)
+                dtype = num2dtype(space, num)
+            if result is None:
+                result = dtype
+                small_unsigned = small_unsigned_scalar
+            else:
+                result, small_unsigned = _promote_types_su(
+                    space, result, dtype,
+                    small_unsigned, small_unsigned_scalar)
+        for dtype in dtypes_w:
+            if result is None:
+                result = dtype
+                small_unsigned = False
+            else:
+                result, small_unsigned = _promote_types_su(
+                    space, result, dtype,
+                    small_unsigned, False)
     return result
 
+simple_kind_ordering = {
+    Bool.kind: 0, ULong.kind: 1, Long.kind: 1,
+    Float64.kind: 2, Complex64.kind: 2,
+    NPY.STRINGLTR: 3, NPY.STRINGLTR2: 3,
+    UnicodeType.kind: 3, VoidType.kind: 3, ObjectType.kind: 3}
+
+def _use_min_scalar(arrays_w, dtypes_w):
+    """Helper for find_result_type()"""
+    if not arrays_w:
+        return False
+    all_scalars = True
+    max_scalar_kind = 0
+    max_array_kind = 0
+    for w_array in arrays_w:
+        if w_array.is_scalar():
+            kind = simple_kind_ordering[w_array.get_dtype().kind]
+            if kind > max_scalar_kind:
+                max_scalar_kind = kind
+        else:
+            all_scalars = False
+            kind = simple_kind_ordering[w_array.get_dtype().kind]
+            if kind > max_array_kind:
+                max_array_kind = kind
+    for dtype in dtypes_w:
+        all_scalars = False
+        kind = simple_kind_ordering[dtype.kind]
+        if kind > max_array_kind:
+            max_array_kind = kind
+    return not all_scalars and max_array_kind >= max_scalar_kind
+
+
 @unwrap_spec(casting=str)
 def can_cast(space, w_from, w_totype, casting='safe'):
     try:
@@ -56,6 +143,11 @@
 
 def can_cast_type(space, origin, target, casting):
     # equivalent to PyArray_CanCastTypeTo
+    if origin == target:
+        return True
+    if origin.is_record() or target.is_record():
+        return can_cast_record(space, origin, target, casting)
+
     if casting == 'no':
         return origin.eq(space, target)
     elif casting == 'equiv':
@@ -63,13 +155,29 @@
     elif casting == 'unsafe':
         return True
     elif casting == 'same_kind':
-        if origin.can_cast_to(target):
+        if can_cast_to(origin, target):
             return True
         if origin.kind in kind_ordering and target.kind in kind_ordering:
             return kind_ordering[origin.kind] <= kind_ordering[target.kind]
         return False
-    else:
-        return origin.can_cast_to(target)
+    else:  # 'safe'
+        return can_cast_to(origin, target)
+
+def can_cast_record(space, origin, target, casting):
+    if origin is target:
+        return True
+    if origin.fields is None or target.fields is None:
+        return False
+    if len(origin.fields) != len(target.fields):
+        return False
+    for name, (offset, orig_field) in origin.fields.iteritems():
+        if name not in target.fields:
+            return False
+        target_field = target.fields[name][1]
+        if not can_cast_type(space, orig_field, target_field, casting):
+            return False
+    return True
+
 
 def can_cast_array(space, w_from, target, casting):
     # equivalent to PyArray_CanCastArrayTo
@@ -91,11 +199,11 @@
     dtypenum, altnum = value.min_dtype()
     if target.is_unsigned():
         dtypenum = altnum
-    dtype = get_dtype_cache(space).dtypes_by_num[dtypenum]
+    dtype = num2dtype(space, dtypenum)
     return can_cast_type(space, dtype, target, casting)
 
 def as_scalar(space, w_obj):
-    dtype = find_dtype_for_scalar(space, w_obj)
+    dtype = scalar2dtype(space, w_obj)
     return dtype.coerce(space, w_obj)
 
 def min_scalar_type(space, w_a):
@@ -103,6 +211,231 @@
     dtype = w_array.get_dtype()
     if w_array.is_scalar() and dtype.is_number():
         num, alt_num = w_array.get_scalar_value().min_dtype()
-        return get_dtype_cache(space).dtypes_by_num[num]
+        return num2dtype(space, num)
     else:
         return dtype
+
+def w_promote_types(space, w_type1, w_type2):
+    dt1 = as_dtype(space, w_type1, allow_None=False)
+    dt2 = as_dtype(space, w_type2, allow_None=False)
+    return promote_types(space, dt1, dt2)
+
+def find_binop_result_dtype(space, dt1, dt2):
+    if dt2 is None:
+        return dt1
+    if dt1 is None:
+        return dt2
+    return promote_types(space, dt1, dt2)
+
+def promote_types(space, dt1, dt2):
+    """Return the smallest dtype to which both input dtypes can be safely cast"""
+    # Equivalent to PyArray_PromoteTypes
+    num = promotion_table[dt1.num][dt2.num]
+    if num != -1:
+        return num2dtype(space, num)
+
+    # dt1.num should be <= dt2.num
+    if dt1.num > dt2.num:
+        dt1, dt2 = dt2, dt1
+
+    if dt2.is_str():
+        if dt1.is_str():
+            if dt1.elsize > dt2.elsize:
+                return dt1
+            else:
+                return dt2
+        else:  # dt1 is numeric
+            dt1_size = dt1.itemtype.strlen
+            if dt1_size > dt2.elsize:
+                return new_string_dtype(space, dt1_size)
+            else:
+                return dt2
+    elif dt2.is_unicode():
+        if dt1.is_unicode():
+            if dt1.elsize > dt2.elsize:
+                return dt1
+            else:
+                return dt2
+        elif dt1.is_str():
+            if dt2.elsize >= 4 * dt1.elsize:
+                return dt2
+            else:
+                return new_unicode_dtype(space, dt1.elsize)
+        else:  # dt1 is numeric
+            dt1_size = dt1.itemtype.strlen
+            if 4 * dt1_size > dt2.elsize:
+                return new_unicode_dtype(space, dt1_size)
+            else:
+                return dt2
+    else:
+        assert dt2.num == NPY.VOID
+        if can_cast_type(space, dt1, dt2, casting='equiv'):
+            return dt1
+    raise oefmt(space.w_TypeError, "invalid type promotion")
+
+def _promote_types_su(space, dt1, dt2, su1, su2):
+    """Like promote_types(), but handles the small_unsigned flag as well"""
+    if su1:
+        if dt2.is_bool() or dt2.is_unsigned():
+            dt1 = dt1.as_unsigned(space)
+        else:
+            dt1 = dt1.as_signed(space)
+    elif su2:
+        if dt1.is_bool() or dt1.is_unsigned():
+            dt2 = dt2.as_unsigned(space)
+        else:
+            dt2 = dt2.as_signed(space)
+    if dt1.elsize < dt2.elsize:
+        su = su2 and (su1 or not dt1.is_signed())
+    elif dt1.elsize == dt2.elsize:
+        su = su1 and su2
+    else:
+        su = su1 and (su2 or not dt2.is_signed())
+    return promote_types(space, dt1, dt2), su
+
+def scalar2dtype(space, w_obj):
+    from .boxes import W_GenericBox
+    bool_dtype = get_dtype_cache(space).w_booldtype
+    long_dtype = get_dtype_cache(space).w_longdtype
+    int64_dtype = get_dtype_cache(space).w_int64dtype
+    uint64_dtype = get_dtype_cache(space).w_uint64dtype
+    complex_dtype = get_dtype_cache(space).w_complex128dtype
+    float_dtype = get_dtype_cache(space).w_float64dtype
+    object_dtype = get_dtype_cache(space).w_objectdtype
+    if isinstance(w_obj, W_GenericBox):
+        return w_obj.get_dtype(space)
+
+    if space.isinstance_w(w_obj, space.w_bool):
+        return bool_dtype
+    elif space.isinstance_w(w_obj, space.w_int):
+        return long_dtype
+    elif space.isinstance_w(w_obj, space.w_long):
+        try:
+            space.int_w(w_obj)
+        except OperationError, e:
+            if e.match(space, space.w_OverflowError):
+                if space.is_true(space.le(w_obj, space.wrap(0))):
+                    return int64_dtype
+                return uint64_dtype
+            raise
+        return int64_dtype
+    elif space.isinstance_w(w_obj, space.w_float):
+        return float_dtype
+    elif space.isinstance_w(w_obj, space.w_complex):
+        return complex_dtype
+    elif space.isinstance_w(w_obj, space.w_str):
+        return variable_dtype(space, 'S%d' % space.len_w(w_obj))
+    return object_dtype
+
+ at signature(ann.instance(W_Dtype), ann.instance(W_Dtype), returns=ann.bool())
+def can_cast_to(dt1, dt2):
+    """Return whether dtype `dt1` can be cast safely to `dt2`"""
+    # equivalent to PyArray_CanCastTo
+    from .casting import can_cast_itemtype
+    result = can_cast_itemtype(dt1.itemtype, dt2.itemtype)
+    if result:
+        if dt1.num == NPY.STRING:
+            if dt2.num == NPY.STRING:
+                return dt1.elsize <= dt2.elsize
+            elif dt2.num == NPY.UNICODE:
+                return dt1.elsize * 4 <= dt2.elsize
+        elif dt1.num == NPY.UNICODE and dt2.num == NPY.UNICODE:
+            return dt1.elsize <= dt2.elsize
+        elif dt2.num in (NPY.STRING, NPY.UNICODE):
+            if dt2.num == NPY.STRING:
+                char_size = 1
+            else:  # NPY.UNICODE
+                char_size = 4
+            if dt2.elsize == 0:
+                return True
+            if dt1.is_int():
+                return dt2.elsize >= dt1.itemtype.strlen * char_size
+    return result
+
+
+ at signature(ann.instance(BaseType), ann.instance(BaseType), returns=ann.bool())
+def can_cast_itemtype(tp1, tp2):
+    # equivalent to PyArray_CanCastSafely
+    return casting_table[tp1.num][tp2.num]
+
+#_________________________
+
+
+casting_table = [[False] * NPY.NTYPES for _ in range(NPY.NTYPES)]
+
+def enable_cast(type1, type2):
+    casting_table[type1.num][type2.num] = True
+
+def _can_cast(type1, type2):
+    """NOT_RPYTHON: operates on BaseType subclasses"""
+    return casting_table[type1.num][type2.num]
+
+for tp in all_types:
+    enable_cast(tp, tp)
+    if tp.num != NPY.DATETIME:
+        enable_cast(Bool, tp)
+    enable_cast(tp, ObjectType)
+    enable_cast(tp, VoidType)
+enable_cast(StringType, UnicodeType)
+#enable_cast(Bool, TimeDelta)
+
+for tp in number_types:
+    enable_cast(tp, StringType)
+    enable_cast(tp, UnicodeType)
+
+for tp1 in int_types:
+    for tp2 in int_types:
+        if tp1.signed:
+            if tp2.signed and tp1.basesize() <= tp2.basesize():
+                enable_cast(tp1, tp2)
+        else:
+            if tp2.signed and tp1.basesize() < tp2.basesize():
+                enable_cast(tp1, tp2)
+            elif not tp2.signed and tp1.basesize() <= tp2.basesize():
+                enable_cast(tp1, tp2)
+for tp1 in int_types:
+    for tp2 in float_types + complex_types:
+        size1 = tp1.basesize()
+        size2 = tp2.basesize()
+        if (size1 < 8 and size2 > size1) or (size1 >= 8 and size2 >= size1):
+            enable_cast(tp1, tp2)
+for tp1 in float_types:
+    for tp2 in float_types + complex_types:
+        if tp1.basesize() <= tp2.basesize():
+            enable_cast(tp1, tp2)
+for tp1 in complex_types:
+    for tp2 in complex_types:
+        if tp1.basesize() <= tp2.basesize():
+            enable_cast(tp1, tp2)
+
+promotion_table = [[-1] * NPY.NTYPES for _ in range(NPY.NTYPES)]
+def promotes(tp1, tp2, tp3):
+    if tp3 is None:
+        num = -1
+    else:
+        num = tp3.num
+    promotion_table[tp1.num][tp2.num] = num
+
+
+for tp in all_types:
+    promotes(tp, ObjectType, ObjectType)
+    promotes(ObjectType, tp, ObjectType)
+
+for tp1 in [Bool] + number_types:
+    for tp2 in [Bool] + number_types:
+        if tp1 is tp2:
+            promotes(tp1, tp1, tp1)
+        elif _can_cast(tp1, tp2):
+            promotes(tp1, tp2, tp2)
+        elif _can_cast(tp2, tp1):
+            promotes(tp1, tp2, tp1)
+        else:
+            # Brute-force search for the least upper bound
+            result = None
+            for tp3 in number_types:
+                if _can_cast(tp1, tp3) and _can_cast(tp2, tp3):
+                    if result is None:
+                        result = tp3
+                    elif _can_cast(tp3, result) and not _can_cast(result, tp3):
+                        result = tp3
+            promotes(tp1, tp2, result)
diff --git a/pypy/module/micronumpy/ctors.py b/pypy/module/micronumpy/ctors.py
--- a/pypy/module/micronumpy/ctors.py
+++ b/pypy/module/micronumpy/ctors.py
@@ -64,8 +64,8 @@
     #print 'create view from shape',shape,'dtype',dtype,'descr',w_descr,'data',data_w[0],'rw',rw
     raise oefmt(space.w_NotImplementedError,
                 "creating array from __array_interface__ not supported yet")
-    return 
-    
+    return
+
 
 @unwrap_spec(ndmin=int, copy=bool, subok=bool)
 def array(space, w_object, w_dtype=None, copy=True, w_order=None, subok=False,
@@ -114,9 +114,9 @@
             elif not copy and (subok or type(w_object) is W_NDimArray):
                 return w_object
         if subok and not type(w_object) is W_NDimArray:
-            raise oefmt(space.w_NotImplementedError, 
+            raise oefmt(space.w_NotImplementedError,
                 "array(..., subok=True) only partially implemented")
-        # we have a ndarray, but need to copy or change dtype 
+        # we have a ndarray, but need to copy or change dtype
         if dtype is None:
             dtype = w_object.get_dtype()
         if dtype != w_object.get_dtype():
@@ -126,7 +126,7 @@
             shape = w_object.get_shape()
             w_arr = W_NDimArray.from_shape(space, shape, dtype, order=order)
             if support.product(shape) == 1:
-                w_arr.set_scalar_value(dtype.coerce(space, 
+                w_arr.set_scalar_value(dtype.coerce(space,
                         w_object.implementation.getitem(0)))
             else:
                 loop.setslice(space, shape, w_arr.implementation, w_object.implementation)
@@ -137,13 +137,13 @@
             with imp as storage:
                 sz = support.product(w_object.get_shape()) * dtype.elsize
                 return W_NDimArray.from_shape_and_storage(space,
-                    w_object.get_shape(), storage, dtype, storage_bytes=sz, 
+                    w_object.get_shape(), storage, dtype, storage_bytes=sz,
                     w_base=w_base, start=imp.start)
     else:
         # not an array
         shape, elems_w = strides.find_shape_and_elems(space, w_object, dtype)
     if dtype is None or (dtype.is_str_or_unicode() and dtype.elsize < 1):
-        dtype = strides.find_dtype_for_seq(space, elems_w, dtype)
+        dtype = find_dtype_for_seq(space, elems_w, dtype)
         if dtype is None:
             dtype = descriptor.get_dtype_cache(space).w_float64dtype
         elif dtype.is_str_or_unicode() and dtype.elsize < 1:
@@ -170,7 +170,7 @@
         return w_array
 
     shape, elems_w = strides.find_shape_and_elems(space, w_object, None)
-    dtype = strides.find_dtype_for_seq(space, elems_w, None)
+    dtype = find_dtype_for_seq(space, elems_w, None)
     if dtype is None:
         dtype = descriptor.get_dtype_cache(space).w_float64dtype
     elif dtype.is_str_or_unicode() and dtype.elsize < 1:
@@ -184,6 +184,21 @@
         loop.assign(space, w_arr, elems_w)
         return w_arr
 
+def _dtype_guess(space, dtype, w_elem):
+    from .casting import scalar2dtype, find_binop_result_dtype
+    if isinstance(w_elem, W_NDimArray) and w_elem.is_scalar():
+        w_elem = w_elem.get_scalar_value()
+    elem_dtype = scalar2dtype(space, w_elem)
+    return find_binop_result_dtype(space, elem_dtype, dtype)
+
+def find_dtype_for_seq(space, elems_w, dtype):
+    if len(elems_w) == 1:
+        w_elem = elems_w[0]
+        return _dtype_guess(space, dtype, w_elem)
+    for w_elem in elems_w:
+        dtype = _dtype_guess(space, dtype, w_elem)
+    return dtype
+
 
 def _zeros_or_empty(space, w_shape, w_dtype, w_order, zero):
     dtype = space.interp_w(descriptor.W_Dtype,
@@ -359,5 +374,5 @@
         return a
     else:
         writable = not buf.readonly
-    return W_NDimArray.from_shape_and_storage(space, [n], storage, storage_bytes=s, 
+    return W_NDimArray.from_shape_and_storage(space, [n], storage, storage_bytes=s,
                                 dtype=dtype, w_base=w_buffer, writable=writable)
diff --git a/pypy/module/micronumpy/descriptor.py b/pypy/module/micronumpy/descriptor.py
--- a/pypy/module/micronumpy/descriptor.py
+++ b/pypy/module/micronumpy/descriptor.py
@@ -8,7 +8,6 @@
 from rpython.rlib import jit
 from rpython.rlib.objectmodel import specialize, compute_hash, we_are_translated
 from rpython.rlib.rarithmetic import r_longlong, r_ulonglong
-from rpython.rlib.signature import finishsigs, signature, types as ann
 from pypy.module.micronumpy import types, boxes, support, constants as NPY
 from .base import W_NDimArray
 from pypy.module.micronumpy.appbridge import get_appbridge_cache
@@ -29,22 +28,18 @@
     """ agree on dtype from a list of arrays. if out is allocated,
     use it's dtype, otherwise allocate a new one with agreed dtype
     """
-    from pypy.module.micronumpy.ufuncs import find_binop_result_dtype
+    from .casting import find_result_type
 
     if not space.is_none(out):
         return out
-    dtype = None
-    for w_arr in w_arr_list:
-        if not space.is_none(w_arr):
-            dtype = find_binop_result_dtype(space, dtype, w_arr.get_dtype())
+    arr_w = [w_arr for w_arr in w_arr_list if not space.is_none(w_arr)]
+    dtype = find_result_type(space, arr_w, [])
     assert dtype is not None
     out = W_NDimArray.from_shape(space, shape, dtype)
     return out
 
 
-_REQ_STRLEN = [0, 3, 5, 10, 10, 20, 20, 20, 20]  # data for can_cast_to()
 
- at finishsigs
 class W_Dtype(W_Root):
     _immutable_fields_ = [
         "itemtype?", "w_box_type", "byteorder?", "names?", "fields?",
@@ -98,41 +93,6 @@
     def box_complex(self, real, imag):
         return self.itemtype.box_complex(real, imag)
 
-    @signature(ann.self(), ann.self(), returns=ann.bool())
-    def can_cast_to(self, other):
-        # equivalent to PyArray_CanCastTo
-        result = self.itemtype.can_cast_to(other.itemtype)
-        if result:
-            if self.num == NPY.STRING:
-                if other.num == NPY.STRING:
-                    return self.elsize <= other.elsize
-                elif other.num == NPY.UNICODE:
-                    return self.elsize * 4 <= other.elsize
-            elif self.num == NPY.UNICODE and other.num == NPY.UNICODE:
-                return self.elsize <= other.elsize
-            elif other.num in (NPY.STRING, NPY.UNICODE):
-                if other.num == NPY.STRING:
-                    char_size = 1
-                else:  # NPY.UNICODE
-                    char_size = 4
-                if other.elsize == 0:
-                    return True
-                if self.is_bool():
-                    return other.elsize >= 5 * char_size
-                elif self.is_unsigned():
-                    if self.elsize > 8 or self.elsize < 0:
-                        return False
-                    else:
-                        return (other.elsize >=
-                                _REQ_STRLEN[self.elsize] * char_size)
-                elif self.is_signed():
-                    if self.elsize > 8 or self.elsize < 0:
-                        return False
-                    else:
-                        return (other.elsize >=
-                                (_REQ_STRLEN[self.elsize] + 1) * char_size)
-        return result
-
     def coerce(self, space, w_item):
         return self.itemtype.coerce(space, self, w_item)
 
@@ -161,6 +121,9 @@
     def is_str(self):
         return self.num == NPY.STRING
 
+    def is_unicode(self):
+        return self.num == NPY.UNICODE
+
     def is_object(self):
         return self.num == NPY.OBJECT
 
@@ -176,6 +139,20 @@
     def is_native(self):
         return self.byteorder in (NPY.NATIVE, NPY.NATBYTE)
 
+    def as_signed(self, space):
+        """Convert from an unsigned integer dtype to its signed partner"""
+        if self.is_unsigned():
+            return num2dtype(space, self.num - 1)
+        else:
+            return self
+
+    def as_unsigned(self, space):
+        """Convert from a signed integer dtype to its unsigned partner"""
+        if self.is_signed():
+            return num2dtype(space, self.num + 1)
+        else:
+            return self
+
     def get_float_dtype(self, space):
         assert self.is_complex()
         dtype = get_dtype_cache(space).component_dtypes[self.num]
@@ -309,20 +286,24 @@
         return space.wrap(not self.eq(space, w_other))
 
     def descr_le(self, space, w_other):
+        from .casting import can_cast_to
         w_other = as_dtype(space, w_other)
-        return space.wrap(self.can_cast_to(w_other))
+        return space.wrap(can_cast_to(self, w_other))
 
     def descr_ge(self, space, w_other):
+        from .casting import can_cast_to
         w_other = as_dtype(space, w_other)
-        return space.wrap(w_other.can_cast_to(self))
+        return space.wrap(can_cast_to(w_other, self))
 
     def descr_lt(self, space, w_other):
+        from .casting import can_cast_to
         w_other = as_dtype(space, w_other)
-        return space.wrap(self.can_cast_to(w_other) and not self.eq(space, w_other))
+        return space.wrap(can_cast_to(self, w_other) and not self.eq(space, w_other))
 
     def descr_gt(self, space, w_other):
+        from .casting import can_cast_to
         w_other = as_dtype(space, w_other)
-        return space.wrap(w_other.can_cast_to(self) and not self.eq(space, w_other))
+        return space.wrap(can_cast_to(w_other, self) and not self.eq(space, w_other))
 
     def _compute_hash(self, space, x):
         from rpython.rlib.rarithmetic import intmask
@@ -861,8 +842,8 @@
             NPY.UBYTE:       ['ubyte'],
             NPY.SHORT:       ['short'],
             NPY.USHORT:      ['ushort'],
-            NPY.LONG:        ['int', 'intp', 'p'],
-            NPY.ULONG:       ['uint', 'uintp', 'P'],
+            NPY.LONG:        ['int'],
+            NPY.ULONG:       ['uint'],
             NPY.LONGLONG:    ['longlong'],
             NPY.ULONGLONG:   ['ulonglong'],
             NPY.FLOAT:       ['single'],
@@ -904,17 +885,20 @@
             NPY.CDOUBLE:     self.w_float64dtype,
             NPY.CLONGDOUBLE: self.w_floatlongdtype,
         }
-        self.builtin_dtypes = [
-            self.w_booldtype,
+        integer_dtypes = [
             self.w_int8dtype, self.w_uint8dtype,
             self.w_int16dtype, self.w_uint16dtype,
+            self.w_int32dtype, self.w_uint32dtype,
             self.w_longdtype, self.w_ulongdtype,
-            self.w_int32dtype, self.w_uint32dtype,
-            self.w_int64dtype, self.w_uint64dtype,
-            ] + float_dtypes + complex_dtypes + [
-            self.w_stringdtype, self.w_unicodedtype, self.w_voiddtype,
-            self.w_objectdtype,
-        ]
+            self.w_int64dtype, self.w_uint64dtype]
+        self.builtin_dtypes = ([self.w_booldtype] + integer_dtypes +
+            float_dtypes + complex_dtypes + [
+                self.w_stringdtype, self.w_unicodedtype, self.w_voiddtype,
+                self.w_objectdtype,
+            ])
+        self.integer_dtypes = integer_dtypes
+        self.float_dtypes = float_dtypes
+        self.complex_dtypes = complex_dtypes
         self.float_dtypes_by_num_bytes = sorted(
             (dtype.elsize, dtype)
             for dtype in float_dtypes
@@ -923,7 +907,9 @@
         self.dtypes_by_name = {}
         # we reverse, so the stuff with lower numbers override stuff with
         # higher numbers
-        for dtype in reversed(self.builtin_dtypes):
+        # However, Long/ULong always take precedence over Intxx
+        for dtype in reversed(
+                [self.w_longdtype, self.w_ulongdtype] + self.builtin_dtypes):
             dtype.fields = None  # mark these as builtin
             self.dtypes_by_num[dtype.num] = dtype
             self.dtypes_by_name[dtype.get_name()] = dtype
@@ -936,6 +922,14 @@
             if dtype.num in aliases:
                 for alias in aliases[dtype.num]:
                     self.dtypes_by_name[alias] = dtype
+        if self.w_longdtype.elsize == self.w_int32dtype.elsize:
+            intp_dtype = self.w_int32dtype
+            uintp_dtype = self.w_uint32dtype
+        else:
+            intp_dtype = self.w_longdtype
+            uintp_dtype = self.w_ulongdtype
+        self.dtypes_by_name['p'] = self.dtypes_by_name['intp'] = intp_dtype
+        self.dtypes_by_name['P'] = self.dtypes_by_name['uintp'] = uintp_dtype
 
         typeinfo_full = {
             'LONGLONG': self.w_int64dtype,
@@ -1012,16 +1006,19 @@
 def get_dtype_cache(space):
     return space.fromcache(DtypeCache)
 
+ at jit.elidable
+def num2dtype(space, num):
+    return get_dtype_cache(space).dtypes_by_num[num]
+
 def as_dtype(space, w_arg, allow_None=True):
-    from pypy.module.micronumpy.ufuncs import find_dtype_for_scalar
+    from pypy.module.micronumpy.casting import scalar2dtype
     # roughly equivalent to CNumPy's PyArray_DescrConverter2
     if not allow_None and space.is_none(w_arg):
         raise TypeError("Cannot create dtype from None here")
     if isinstance(w_arg, W_NDimArray):
         return w_arg.get_dtype()
     elif is_scalar_w(space, w_arg):
-        result = find_dtype_for_scalar(space, w_arg)
-        assert result is not None  # XXX: not guaranteed
+        result = scalar2dtype(space, w_arg)
         return result
     else:
         return space.interp_w(W_Dtype,
diff --git a/pypy/module/micronumpy/loop.py b/pypy/module/micronumpy/loop.py
--- a/pypy/module/micronumpy/loop.py
+++ b/pypy/module/micronumpy/loop.py
@@ -18,35 +18,7 @@
     greens=['shapelen', 'func', 'calc_dtype', 'res_dtype'],
     reds='auto')
 
-def call2(space, shape, func, calc_dtype, res_dtype, w_lhs, w_rhs, out):
-    # handle array_priority
-    # w_lhs and w_rhs could be of different ndarray subtypes. Numpy does:
-    # 1. if __array_priorities__ are equal and one is an ndarray and the
-    #        other is a subtype,  return a subtype
-    # 2. elif rhs.__array_priority__ is higher, return the type of rhs
-
-    w_ndarray = space.gettypefor(W_NDimArray)
-    lhs_type = space.type(w_lhs)
-    rhs_type = space.type(w_rhs)
-    lhs_for_subtype = w_lhs
-    rhs_for_subtype = w_rhs
-    #it may be something like a FlatIter, which is not an ndarray
-    if not space.is_true(space.issubtype(lhs_type, w_ndarray)):
-        lhs_type = space.type(w_lhs.base)
-        lhs_for_subtype = w_lhs.base
-    if not space.is_true(space.issubtype(rhs_type, w_ndarray)):
-        rhs_type = space.type(w_rhs.base)
-        rhs_for_subtype = w_rhs.base
-
-    w_highpriority = w_lhs
-    highpriority_subtype = lhs_for_subtype
-    if space.is_w(lhs_type, w_ndarray) and not space.is_w(rhs_type, w_ndarray):
-        highpriority_subtype = rhs_for_subtype
-        w_highpriority = w_rhs
-    if support.is_rhs_priority_higher(space, w_lhs, w_rhs):
-        highpriority_subtype = rhs_for_subtype
-        w_highpriority = w_rhs
-
+def call2(space, shape, func, calc_dtype, w_lhs, w_rhs, out):
     if w_lhs.get_size() == 1:
         w_left = w_lhs.get_scalar_value().convert_to(space, calc_dtype)
         left_iter = left_state = None
@@ -63,13 +35,9 @@
         right_iter, right_state = w_rhs.create_iter(shape)
         right_iter.track_index = False
 
-    if out is None:
-        w_ret = W_NDimArray.from_shape(space, shape, res_dtype,
-                                     w_instance=highpriority_subtype)
-    else:
-        w_ret = out
-    out_iter, out_state = w_ret.create_iter(shape)
+    out_iter, out_state = out.create_iter(shape)
     shapelen = len(shape)
+    res_dtype = out.get_dtype()
     while not out_iter.done(out_state):
         call2_driver.jit_merge_point(shapelen=shapelen, func=func,
                                      calc_dtype=calc_dtype, res_dtype=res_dtype)
@@ -82,25 +50,19 @@
         out_iter.setitem(out_state, func(calc_dtype, w_left, w_right).convert_to(
             space, res_dtype))
         out_state = out_iter.next(out_state)
-    if out is None:
-        w_ret = space.call_method(w_highpriority, '__array_wrap__', w_ret)
-    return w_ret
+    return out
 
 call1_driver = jit.JitDriver(
     name='numpy_call1',
     greens=['shapelen', 'func', 'calc_dtype', 'res_dtype'],
     reds='auto')
 
-def call1(space, shape, func, calc_dtype, res_dtype, w_obj, out):
+def call1(space, shape, func, calc_dtype, w_obj, w_ret):
     obj_iter, obj_state = w_obj.create_iter(shape)
     obj_iter.track_index = False
-
-    if out is None:
-        w_ret = W_NDimArray.from_shape(space, shape, res_dtype, w_instance=w_obj)
-    else:
-        w_ret = out
     out_iter, out_state = w_ret.create_iter(shape)
     shapelen = len(shape)
+    res_dtype = w_ret.get_dtype()
     while not out_iter.done(out_state):
         call1_driver.jit_merge_point(shapelen=shapelen, func=func,
                                      calc_dtype=calc_dtype, res_dtype=res_dtype)
@@ -108,8 +70,6 @@
         out_iter.setitem(out_state, func(calc_dtype, elem).convert_to(space, res_dtype))
         out_state = out_iter.next(out_state)
         obj_state = obj_iter.next(obj_state)
-    if out is None:
-        w_ret = space.call_method(w_obj, '__array_wrap__', w_ret)
     return w_ret
 
 call_many_to_one_driver = jit.JitDriver(
@@ -181,7 +141,7 @@
             vals[i] = in_iters[i].getitem(in_states[i])
         w_arglist = space.newlist(vals)
         w_outvals = space.call_args(func, Arguments.frompacked(space, w_arglist))
-        # w_outvals should be a tuple, but func can return a single value as well 
+        # w_outvals should be a tuple, but func can return a single value as well
         if space.isinstance_w(w_outvals, space.w_tuple):
             batch = space.listview(w_outvals)
             for i in range(len(batch)):
@@ -254,9 +214,10 @@
         obj_state = obj_iter.next(obj_state)
     return cur_value
 
-reduce_cum_driver = jit.JitDriver(name='numpy_reduce_cum_driver',
-                                  greens = ['shapelen', 'func', 'dtype'],
-                                  reds = 'auto')
+reduce_cum_driver = jit.JitDriver(
+    name='numpy_reduce_cum_driver',
+    greens=['shapelen', 'func', 'dtype', 'out_dtype'],
+    reds='auto')
 
 def compute_reduce_cumulative(space, obj, out, calc_dtype, func, identity):
     obj_iter, obj_state = obj.create_iter()
@@ -270,12 +231,14 @@
     else:
         cur_value = identity.convert_to(space, calc_dtype)
     shapelen = len(obj.get_shape())
+    out_dtype = out.get_dtype()
     while not obj_iter.done(obj_state):
-        reduce_cum_driver.jit_merge_point(shapelen=shapelen, func=func,
-                                          dtype=calc_dtype)
+        reduce_cum_driver.jit_merge_point(
+            shapelen=shapelen, func=func,
+            dtype=calc_dtype, out_dtype=out_dtype)
         rval = obj_iter.getitem(obj_state).convert_to(space, calc_dtype)
         cur_value = func(calc_dtype, cur_value, rval)
-        out_iter.setitem(out_state, cur_value)
+        out_iter.setitem(out_state, out_dtype.coerce(space, cur_value))
         out_state = out_iter.next(out_state)
         obj_state = obj_iter.next(obj_state)
 
diff --git a/pypy/module/micronumpy/ndarray.py b/pypy/module/micronumpy/ndarray.py
--- a/pypy/module/micronumpy/ndarray.py
+++ b/pypy/module/micronumpy/ndarray.py
@@ -896,7 +896,7 @@
     # --------------------- operations ----------------------------
     # TODO: support all kwargs like numpy ufunc_object.c
     sig = None
-    cast = None
+    cast = 'unsafe'
     extobj = None
 
 
@@ -1013,6 +1013,7 @@
         return space.newtuple([w_quotient, w_remainder])
 
     def descr_dot(self, space, w_other, w_out=None):
+        from .casting import find_result_type
         if space.is_none(w_out):
             out = None
         elif not isinstance(w_out, W_NDimArray):
@@ -1027,8 +1028,7 @@
             w_res = self.descr_mul(space, other)
             assert isinstance(w_res, W_NDimArray)
             return w_res.descr_sum(space, space.wrap(-1), out)
-        dtype = ufuncs.find_binop_result_dtype(space, self.get_dtype(),
-                                               other.get_dtype())
+        dtype = find_result_type(space, [self, other], [])
         if self.get_size() < 1 and other.get_size() < 1:
             # numpy compatability
             return W_NDimArray.new_scalar(space, dtype, space.wrap(0))
diff --git a/pypy/module/micronumpy/nditer.py b/pypy/module/micronumpy/nditer.py
--- a/pypy/module/micronumpy/nditer.py
+++ b/pypy/module/micronumpy/nditer.py
@@ -9,6 +9,7 @@
 from pypy.module.micronumpy.iterators import ArrayIter
 from pypy.module.micronumpy.strides import (calculate_broadcast_strides,
                                             shape_agreement, shape_agreement_multiple)
+from pypy.module.micronumpy.casting import find_binop_result_dtype
 
 
 def parse_op_arg(space, name, w_op_flags, n, parse_one_arg):
@@ -173,7 +174,7 @@
     def __init__(self, array, size, shape, strides, backstrides,
                  op_flags, base):
         OperandIter.__init__(self, array, size, shape, strides, backstrides)
-        self.slice_shape =[] 
+        self.slice_shape =[]
         self.slice_stride = []
         self.slice_backstride = []
         if op_flags.rw == 'r':
@@ -302,7 +303,7 @@
     But after coalesce(), getoperand() will return a slice by removing
     the fastest varying dimension(s) from the beginning or end of the shape.
     If flat is true, then the slice will be 1d, otherwise stack up the shape of
-    the fastest varying dimension in the slice, so an iterator of a  'C' array 
+    the fastest varying dimension in the slice, so an iterator of a  'C' array
     of shape (2,4,3) after two calls to coalesce will iterate 2 times over a slice
     of shape (4,3) by setting the offset to the beginning of the data at each iteration
     '''
@@ -367,8 +368,6 @@
     _immutable_fields_ = ['ndim', ]
     def __init__(self, space, w_seq, w_flags, w_op_flags, w_op_dtypes,
                  w_casting, w_op_axes, w_itershape, buffersize=0, order='K'):
-        from pypy.module.micronumpy.ufuncs import find_binop_result_dtype
-        
         self.order = order
         self.external_loop = False
         self.buffered = False
diff --git a/pypy/module/micronumpy/strides.py b/pypy/module/micronumpy/strides.py
--- a/pypy/module/micronumpy/strides.py
+++ b/pypy/module/micronumpy/strides.py
@@ -220,24 +220,6 @@
         batch = new_batch
 
 
-def find_dtype_for_seq(space, elems_w, dtype):
-    from pypy.module.micronumpy.ufuncs import find_dtype_for_scalar
-    if len(elems_w) == 1:
-        w_elem = elems_w[0]
-        if isinstance(w_elem, W_NDimArray) and w_elem.is_scalar():
-            w_elem = w_elem.get_scalar_value()
-        return find_dtype_for_scalar(space, w_elem, dtype)
-    return _find_dtype_for_seq(space, elems_w, dtype)
-
-
-def _find_dtype_for_seq(space, elems_w, dtype):
-    from pypy.module.micronumpy.ufuncs import find_dtype_for_scalar
-    for w_elem in elems_w:
-        if isinstance(w_elem, W_NDimArray) and w_elem.is_scalar():
-            w_elem = w_elem.get_scalar_value()
-        dtype = find_dtype_for_scalar(space, w_elem, dtype)
-    return dtype
-
 
 @jit.unroll_safe
 def shape_agreement(space, shape1, w_arr2, broadcast_down=True):
@@ -247,11 +229,15 @@
     shape2 = w_arr2.get_shape()
     ret = _shape_agreement(shape1, shape2)
     if len(ret) < max(len(shape1), len(shape2)):
+        def format_shape(shape):
+            if len(shape) > 1:
+                return ",".join([str(x) for x in shape])
+            else:
+                return '%d,' % shape[0]
         raise OperationError(space.w_ValueError,
             space.wrap("operands could not be broadcast together with shapes (%s) (%s)" % (
-                ",".join([str(x) for x in shape1]),
-                ",".join([str(x) for x in shape2]),
-            ))
+                format_shape(shape1), format_shape(shape2)),
+            )
         )
     if not broadcast_down and len([x for x in ret if x != 1]) > len([x for x in shape2 if x != 1]):
         raise OperationError(space.w_ValueError,
diff --git a/pypy/module/micronumpy/test/dummy_module.py b/pypy/module/micronumpy/test/dummy_module.py
--- a/pypy/module/micronumpy/test/dummy_module.py
+++ b/pypy/module/micronumpy/test/dummy_module.py
@@ -24,6 +24,7 @@
 for t in types:
     globals()[t + '_'] = dtype(t).type
 del types
+globals()['uint'] = dtype('uint').type
 
 types = ['Generic', 'Number', 'Integer', 'SignedInteger', 'UnsignedInteger',
          'Inexact', 'Floating', 'ComplexFloating', 'Flexible', 'Character']
diff --git a/pypy/module/micronumpy/test/test_casting.py b/pypy/module/micronumpy/test/test_casting.py
--- a/pypy/module/micronumpy/test/test_casting.py
+++ b/pypy/module/micronumpy/test/test_casting.py
@@ -1,4 +1,8 @@
 from pypy.module.micronumpy.test.test_base import BaseNumpyAppTest
+from pypy.module.micronumpy.descriptor import get_dtype_cache, num2dtype
+from pypy.module.micronumpy.casting import (
+    promote_types, can_cast_type, _promote_types_su)
+import pypy.module.micronumpy.constants as NPY
 
 
 class AppTestNumSupport(BaseNumpyAppTest):
@@ -24,6 +28,7 @@
         assert np.can_cast(np.int32, np.int64)
         assert np.can_cast(np.float64, complex)
         assert not np.can_cast(np.complex64, float)
+        assert np.can_cast(np.bool_, np.bool_)
 
         assert np.can_cast('i8', 'f8')
         assert not np.can_cast('i8', 'f4')
@@ -113,9 +118,64 @@
         assert np.can_cast(1., np.complex64)
         assert not np.can_cast(1e50, np.complex64)
 
+    def test_can_cast_record(self):
+        import numpy as np
+        rec1 = np.dtype([('x', int), ('y', float)])
+        rec2 = np.dtype([('x', float), ('y', float)])
+        rec3 = np.dtype([('y', np.float64), ('x', float)])
+        assert not np.can_cast(rec1, rec2, 'equiv')
+        assert np.can_cast(rec2, rec3, 'equiv')
+        assert np.can_cast(rec1, rec2)
+
     def test_min_scalar_type(self):
         import numpy as np
         assert np.min_scalar_type(2**8 - 1) == np.dtype('uint8')
         assert np.min_scalar_type(2**64 - 1) == np.dtype('uint64')
         # XXX: np.asarray(2**64) fails with OverflowError
         # assert np.min_scalar_type(2**64) == np.dtype('O')
+
+    def test_promote_types(self):
+        import numpy as np
+        assert np.promote_types('f4', 'f8') == np.dtype('float64')
+        assert np.promote_types('i8', 'f4') == np.dtype('float64')
+        assert np.promote_types('>i8', '<c8') == np.dtype('complex128')
+        assert np.promote_types('i4', 'S8') == np.dtype('S11')
+        assert np.promote_types('f4', 'S8') == np.dtype('S32')
+        assert np.promote_types('f4', 'U8') == np.dtype('U32')
+        assert np.promote_types('?', '?') is np.dtype('?')
+        assert np.promote_types('?', 'float64') is np.dtype('float64')
+        assert np.promote_types('float64', '?') is np.dtype('float64')
+        assert np.promote_types('i', 'b') is np.dtype('i')
+        assert np.promote_types('i', '?') is np.dtype('i')
+        assert np.promote_types('c8', 'f8') is np.dtype('c16')
+        assert np.promote_types('c8', 'longdouble') == np.dtype('clongdouble')
+        assert np.promote_types('c16', 'longdouble') == np.dtype('clongdouble')
+
+    def test_result_type(self):
+        import numpy as np
+        assert np.result_type(np.uint8, np.int8) == np.int16
+        assert np.result_type(np.uint16(1), np.int8(0)) == np.int32
+        assert np.result_type(np.uint16(1), np.int8(0), np.uint8) == np.uint8
+        assert np.result_type(-1, np.uint8, 1) == np.int16
+
+def test_can_cast_same_type(space):
+    dt_bool = get_dtype_cache(space).w_booldtype
+    assert can_cast_type(space, dt_bool, dt_bool, 'no')
+    assert can_cast_type(space, dt_bool, dt_bool, 'equiv')
+    assert can_cast_type(space, dt_bool, dt_bool, 'safe')
+    assert can_cast_type(space, dt_bool, dt_bool, 'same_kind')
+    assert can_cast_type(space, dt_bool, dt_bool, 'unsafe')
+
+def test_promote_types_su(space):
+    dt_int8 = num2dtype(space, NPY.BYTE)
+    dt_uint8 = num2dtype(space, NPY.UBYTE)
+    dt_int16 = num2dtype(space, NPY.SHORT)
+    dt_uint16 = num2dtype(space, NPY.USHORT)
+    # The results must be signed
+    assert _promote_types_su(space, dt_int8, dt_int16, False, False) == (dt_int16, False)
+    assert _promote_types_su(space, dt_int8, dt_int16, True, False) == (dt_int16, False)
+    assert _promote_types_su(space, dt_int8, dt_int16, False, True) == (dt_int16, False)
+
+    # The results may be unsigned
+    assert _promote_types_su(space, dt_int8, dt_int16, True, True) == (dt_int16, True)
+    assert _promote_types_su(space, dt_uint8, dt_int16, False, True) == (dt_uint16, True)
diff --git a/pypy/module/micronumpy/test/test_dtypes.py b/pypy/module/micronumpy/test/test_dtypes.py
--- a/pypy/module/micronumpy/test/test_dtypes.py
+++ b/pypy/module/micronumpy/test/test_dtypes.py
@@ -146,16 +146,18 @@
             assert dtype('uint32').num == 8
             assert dtype('int64').num == 9
             assert dtype('uint64').num == 10
+            assert dtype('intp').num == 5
+            assert dtype('uintp').num == 6
         else:
             assert dtype('int32').num == 5
             assert dtype('uint32').num == 6
             assert dtype('int64').num == 7
             assert dtype('uint64').num == 8
+            assert dtype('intp').num == 7
+            assert dtype('uintp').num == 8
         assert dtype(int).num == 7
         assert dtype('int').num == 7
         assert dtype('uint').num == 8
-        assert dtype('intp').num == 7
-        assert dtype('uintp').num == 8
         assert dtype(long).num == 9
         assert dtype(float).num == 12
         assert dtype('float').num == 12
@@ -288,7 +290,7 @@
             types += ['g', 'G']
         a = array([True], '?')
         for t in types:
-            assert (a + array([0], t)).dtype is dtype(t)
+            assert (a + array([0], t)).dtype == dtype(t)
 
     def test_binop_types(self):
         from numpy import array, dtype
@@ -312,7 +314,7 @@
         for d1, d2, dout in tests:
             # make a failed test print helpful info
             d3 = (array([1], d1) + array([1], d2)).dtype
-            assert (d1, d2) == (d1, d2) and d3 is dtype(dout)
+            assert (d1, d2) == (d1, d2) and d3 == dtype(dout)
 
     def test_add(self):
         import numpy as np
@@ -850,36 +852,6 @@
             assert issubclass(int64, int)
             assert int_ is int64
 
-    def test_various_types(self):
-        import numpy
-
-        assert numpy.int16 is numpy.short
-        assert numpy.int8 is numpy.byte
-        assert numpy.bool_ is numpy.bool8
-        assert numpy.intp().dtype.num == 7
-        assert numpy.intp().dtype.char == 'l'
-        if self.ptr_size == 4:
-            assert numpy.intp().dtype.name == 'int32'
-            assert numpy.intp is numpy.int32
-            assert numpy.uintp is numpy.uint32
-        elif self.ptr_size == 8:
-            assert numpy.intp().dtype.name == 'int64'
-            assert numpy.intp is numpy.int64
-            assert numpy.uintp is numpy.uint64
-
-        assert issubclass(numpy.float64, numpy.floating)
-        assert issubclass(numpy.longfloat, numpy.floating)
-        assert not issubclass(numpy.float64, numpy.longfloat)
-        assert not issubclass(numpy.longfloat, numpy.float64)
-
-    def test_mro(self):
-        import numpy
-
-        assert numpy.int16.__mro__ == (numpy.int16, numpy.signedinteger,
-                                       numpy.integer, numpy.number,
-                                       numpy.generic, object)
-        assert numpy.bool_.__mro__ == (numpy.bool_, numpy.generic, object)
-
     def test_operators(self):
         from operator import truediv
         from numpy import float64, int_, True_, False_
@@ -959,20 +931,22 @@
 
     def test_intp(self):
         from numpy import dtype
-        for s in ['p', 'int']:
-            assert dtype(s) is dtype('intp')
-        for s in ['P', 'uint']:
-            assert dtype(s) is dtype('uintp')
-        assert dtype('p').num == 7
-        assert dtype('P').num == 8
-        assert dtype('p').char == 'l'
-        assert dtype('P').char == 'L'
+        assert dtype('p') is dtype('intp')
+        assert dtype('P') is dtype('uintp')
         assert dtype('p').kind == 'i'
         assert dtype('P').kind == 'u'
         if self.ptr_size == 4:
+            assert dtype('p').num == 5
+            assert dtype('P').num == 6
+            assert dtype('p').char == 'i'
+            assert dtype('P').char == 'I'
             assert dtype('p').name == 'int32'
             assert dtype('P').name == 'uint32'
         else:
+            assert dtype('p').num == 7
+            assert dtype('P').num == 8
+            assert dtype('p').char == 'l'
+            assert dtype('P').char == 'L'
             assert dtype('p').name == 'int64'
             assert dtype('P').name == 'uint64'
 
diff --git a/pypy/module/micronumpy/test/test_ndarray.py b/pypy/module/micronumpy/test/test_ndarray.py
--- a/pypy/module/micronumpy/test/test_ndarray.py
+++ b/pypy/module/micronumpy/test/test_ndarray.py
@@ -997,7 +997,7 @@
         r = [1, 2] + array([1, 2])
         assert (r == [2, 4]).all()
 
-    def test_inline_op_scalar(self):
+    def test_inplace_op_scalar(self):
         from numpy import array
         for op in [
                 '__iadd__',
@@ -1016,7 +1016,7 @@
             getattr(a, op).__call__(2)
             assert id(a) == id(b)
 
-    def test_inline_op_array(self):
+    def test_inplace_op_array(self):
         from numpy import array
         for op in [
                 '__iadd__',
@@ -1040,6 +1040,14 @@
             for i in range(5):
                 assert a[i] == getattr(c[i], reg_op).__call__(d[i])
 
+    def test_inplace_cast(self):
+        import numpy as np
+        a = np.zeros(5, dtype=np.float64)
+        b = np.zeros(5, dtype=np.complex64)
+        a += b
+        assert a.sum() == 0
+        assert a.dtype is np.dtype(np.float64)
+
     def test_add_list(self):
         from numpy import array, ndarray
         a = array(range(5))
@@ -1084,6 +1092,7 @@
         b = a * a
         for i in range(5):
             assert b[i] == i * i
+        assert a.dtype.num == b.dtype.num
         assert b.dtype is a.dtype
 
         a = numpy.array(range(5), dtype=bool)
@@ -1828,7 +1837,7 @@
         assert y.base is x
         assert y.strides == (-2048, 64, 8)
         y[:] = 1000
-        assert x[-1, 0, 0] == 1000 
+        assert x[-1, 0, 0] == 1000
 
         a = empty([3, 2, 1], dtype='float64')
         b = a.view(dtype('uint32'))
@@ -1953,7 +1962,10 @@
         b = concatenate((a[:3], a[-3:]))
         assert (b == [2, 6, 10, 2, 6, 10]).all()
         a = concatenate((array([1]), array(['abc'])))
-        assert str(a.dtype) == '|S3'
+        if dtype('l').itemsize == 4:  # 32-bit platform
+            assert str(a.dtype) == '|S11'
+        else:
+            assert str(a.dtype) == '|S21'
         a = concatenate((array([]), array(['abc'])))
         assert a[0] == 'abc'
         a = concatenate((['abcdef'], ['abc']))
@@ -3864,7 +3876,7 @@
                    ([4, 5, 6], [5.5, 6.5, 7.5, 8.5, 9.5])], dtype=d)
 
         assert len(list(a[0])) == 2
-        
+
         mdtype = dtype([('a', bool), ('b', bool), ('c', bool)])
         a = array([0, 0, 0, 1, 1])
         # this creates a value of (x, x, x) in b for each x in a
diff --git a/pypy/module/micronumpy/test/test_nditer.py b/pypy/module/micronumpy/test/test_nditer.py
--- a/pypy/module/micronumpy/test/test_nditer.py
+++ b/pypy/module/micronumpy/test/test_nditer.py
@@ -113,12 +113,14 @@
             r.append((value, it.index))
         assert r == [(0, 0), (1, 2), (2, 4), (3, 1), (4, 3), (5, 5)]
 
-    @py.test.mark.xfail(reason="Fortran order not implemented")
     def test_iters_with_different_order(self):
         from numpy import nditer, array
 
         a = array([[1, 2], [3, 4]], order="C")
-        b = array([[1, 2], [3, 4]], order="F")
+        try:
+            b = array([[1, 2], [3, 4]], order="F")
+        except (NotImplementedError, ValueError):
+            skip('Fortran order not implemented')
 
         it = nditer([a, b])
 
@@ -217,7 +219,7 @@
         assert r == [(0, 0), (1, 1), (2, 2), (0, 3), (1, 4), (2, 5)]
         a = arange(2)
         exc = raises(ValueError, nditer, [a, b])
-        assert str(exc.value).find('shapes (2) (2,3)') > 0
+        assert str(exc.value).find('shapes (2,) (2,3)') > 0
 
     def test_outarg(self):
         from numpy import nditer, zeros, arange
@@ -246,7 +248,7 @@
         assert (c == [1., 4., 9.]).all()
         assert (b == c).all()
         exc = raises(ValueError, square2, arange(6).reshape(2, 3), out=b)
-        assert str(exc.value).find('cannot be broadcasted') > 0
+        assert str(exc.value).find("doesn't match the broadcast shape") > 0
 
     def test_outer_product(self):
         from numpy import nditer, arange
@@ -332,25 +334,25 @@
         i = nditer([a, None], [], [['readonly'], ['writeonly','allocate']],
                             op_axes=[[0,1,None], None],
                             itershape=(-1,-1,4))
-        assert_equal(i.operands[1].shape, (2,3,4))
-        assert_equal(i.operands[1].strides, (24,8,2))
+        assert i.operands[1].shape == (2,3,4)
+        assert i.operands[1].strides, (24,8,2)
 
         i = nditer([a.T, None], [], [['readonly'], ['writeonly','allocate']],
                             op_axes=[[0,1,None], None],
                             itershape=(-1,-1,4))
-        assert_equal(i.operands[1].shape, (3,2,4))
-        assert_equal(i.operands[1].strides, (8,24,2))
+        assert i.operands[1].shape, (3,2,4)
+        assert i.operands[1].strides, (8,24,2)
 
         i = nditer([a.T, None], [], [['readonly'], ['writeonly','allocate']],
                             order='F',
                             op_axes=[[0,1,None], None],
                             itershape=(-1,-1,4))
-        assert_equal(i.operands[1].shape, (3,2,4))
-        assert_equal(i.operands[1].strides, (2,6,12))
+        assert i.operands[1].shape, (3,2,4)
+        assert i.operands[1].strides, (2,6,12)
 
         # If we specify 1 in the itershape, it shouldn't allow broadcasting
         # of that dimension to a bigger value
-        assert_raises(ValueError, nditer, [a, None], [],
+        raises(ValueError, nditer, [a, None], [],
                             [['readonly'], ['writeonly','allocate']],
                             op_axes=[[0,1,None], None],
                             itershape=(-1,1,4))
diff --git a/pypy/module/micronumpy/test/test_scalar.py b/pypy/module/micronumpy/test/test_scalar.py
--- a/pypy/module/micronumpy/test/test_scalar.py
+++ b/pypy/module/micronumpy/test/test_scalar.py
@@ -3,6 +3,45 @@
 class AppTestScalar(BaseNumpyAppTest):
     spaceconfig = dict(usemodules=["micronumpy", "binascii", "struct"])
 
+    def test_integer_types(self):
+        import numpy as np
+        _32BIT = np.dtype('int').itemsize == 4
+        if _32BIT:
+            assert np.int32 is np.dtype('l').type
+            assert np.uint32 is np.dtype('L').type
+            assert np.intp is np.dtype('i').type
+            assert np.uintp is np.dtype('I').type
+            assert np.int64 is np.dtype('q').type
+            assert np.uint64 is np.dtype('Q').type
+        else:
+            assert np.int32 is np.dtype('i').type
+            assert np.uint32 is np.dtype('I').type
+            assert np.intp is np.dtype('l').type
+            assert np.uintp is np.dtype('L').type
+            assert np.int64 is np.dtype('l').type
+            assert np.uint64 is np.dtype('L').type
+        assert np.int16 is np.short is np.dtype('h').type
+        assert np.int_ is np.dtype('l').type
+        assert np.uint is np.dtype('L').type
+        assert np.dtype('intp') == np.dtype('int')
+        assert np.dtype('uintp') == np.dtype('uint')
+        assert np.dtype('i') is not np.dtype('l') is not np.dtype('q')
+        assert np.dtype('I') is not np.dtype('L') is not np.dtype('Q')
+
+    def test_hierarchy(self):
+        import numpy
+        assert issubclass(numpy.float64, numpy.floating)
+        assert issubclass(numpy.longfloat, numpy.floating)
+        assert not issubclass(numpy.float64, numpy.longfloat)
+        assert not issubclass(numpy.longfloat, numpy.float64)
+
+    def test_mro(self):
+        import numpy
+        assert numpy.int16.__mro__ == (numpy.int16, numpy.signedinteger,
+                                       numpy.integer, numpy.number,
+                                       numpy.generic, object)
+        assert numpy.bool_.__mro__ == (numpy.bool_, numpy.generic, object)
+
     def test_init(self):
         import numpy as np
         import math
@@ -104,7 +143,7 @@
         assert f.round(decimals=1) == 13.4
         assert f.round(decimals=1, out=None) == 13.4
         assert b.round() == 1.0
-        assert b.round(decimals=5) is b
+        raises(TypeError,  b.round, decimals=5)
 
     def test_astype(self):
         import numpy as np
@@ -183,10 +222,14 @@
     def test_indexing(self):
         import numpy as np
         v = np.int32(2)
-        for b in [v[()], v[...]]:
-            assert isinstance(b, np.ndarray)
-            assert b.shape == ()
-            assert b == v
+        b = v[()]
+        assert isinstance(b, np.int32)
+        assert b.shape == ()
+        assert b == v
+        b = v[...]
+        assert isinstance(b, np.ndarray)
+        assert b.shape == ()
+        assert b == v
         raises(IndexError, "v['blah']")
 
     def test_realimag(self):
diff --git a/pypy/module/micronumpy/test/test_selection.py b/pypy/module/micronumpy/test/test_selection.py
--- a/pypy/module/micronumpy/test/test_selection.py
+++ b/pypy/module/micronumpy/test/test_selection.py
@@ -24,11 +24,13 @@
         assert (a.argsort() == [[1, 0], [0, 1]]).all()
         a = array(range(10) + range(10) + range(10))
         b = a.argsort()
-        assert (b[:3] == [0, 10, 20]).all()
+        assert ((b[:3] == [0, 10, 20]).all() or
+                (b[:3] == [0, 20, 10]).all())
         #trigger timsort 'run' mode which calls arg_getitem_slice
         a = array(range(100) + range(100) + range(100))
         b = a.argsort()
-        assert (b[:3] == [0, 100, 200]).all()
+        assert ((b[:3] == [0, 100, 200]).all() or
+                (b[:3] == [0, 200, 100]).all())
         a = array([[[]]]).reshape(3,4,0)
         b = a.argsort()
         assert b.size == 0
@@ -176,8 +178,10 @@
         assert (d == c).all(), "test sort with default axis"
 
     def test_sort_corner_cases_string_records(self):
-        skip('not implemented yet')
         from numpy import array, dtype
+        import sys
+        if '__pypy__' in sys.builtin_module_names:
+            skip('not implemented yet in PyPy')
         # test string sorts.
         s = 'aaaaaaaa'
         a = array([s + chr(i) for i in range(101)])
@@ -225,8 +229,10 @@
 
     def test_sort_objects(self):
         # test object array sorts.
-        skip('object type not supported yet')
         from numpy import empty
+        import sys
+        if '__pypy__' in sys.builtin_module_names:
+            skip('not implemented yet in PyPy')
         try:
             a = empty((101,), dtype=object)
         except:
@@ -273,9 +279,10 @@
 
     def test_sort_order(self):
         from numpy import array, zeros
-        from sys import byteorder
+        from sys import byteorder, builtin_module_names
+        if '__pypy__' in builtin_module_names:
+            skip('not implemented yet in PyPy')
         # Test sorting an array with fields
-        skip('not implemented yet')
         x1 = array([21, 32, 14])
         x2 = array(['my', 'first', 'name'])
         x3=array([3.1, 4.5, 6.2])
diff --git a/pypy/module/micronumpy/test/test_ufuncs.py b/pypy/module/micronumpy/test/test_ufuncs.py
--- a/pypy/module/micronumpy/test/test_ufuncs.py
+++ b/pypy/module/micronumpy/test/test_ufuncs.py
@@ -1,93 +1,12 @@
 from pypy.module.micronumpy.test.test_base import BaseNumpyAppTest
-from pypy.module.micronumpy.ufuncs import (find_binop_result_dtype,
-        find_unaryop_result_dtype, W_UfuncGeneric)
+from pypy.module.micronumpy.ufuncs import W_UfuncGeneric, unary_ufunc
 from pypy.module.micronumpy.support import _parse_signature
 from pypy.module.micronumpy.descriptor import get_dtype_cache
 from pypy.module.micronumpy.base import W_NDimArray
 from pypy.module.micronumpy.concrete import VoidBoxStorage
-from pypy.interpreter.gateway import interp2app
-from pypy.conftest import option
 from pypy.interpreter.error import OperationError
 
 
-class TestUfuncCoercion(object):
-    def test_binops(self, space):
-        bool_dtype = get_dtype_cache(space).w_booldtype
-        int8_dtype = get_dtype_cache(space).w_int8dtype
-        int32_dtype = get_dtype_cache(space).w_int32dtype
-        float64_dtype = get_dtype_cache(space).w_float64dtype
-        c64_dtype = get_dtype_cache(space).w_complex64dtype
-        c128_dtype = get_dtype_cache(space).w_complex128dtype
-        cld_dtype = get_dtype_cache(space).w_complexlongdtype
-        fld_dtype = get_dtype_cache(space).w_floatlongdtype
-
-        # Basic pairing
-        assert find_binop_result_dtype(space, bool_dtype, bool_dtype) is bool_dtype
-        assert find_binop_result_dtype(space, bool_dtype, float64_dtype) is float64_dtype
-        assert find_binop_result_dtype(space, float64_dtype, bool_dtype) is float64_dtype
-        assert find_binop_result_dtype(space, int32_dtype, int8_dtype) is int32_dtype
-        assert find_binop_result_dtype(space, int32_dtype, bool_dtype) is int32_dtype
-        assert find_binop_result_dtype(space, c64_dtype, float64_dtype) is c128_dtype
-        assert find_binop_result_dtype(space, c64_dtype, fld_dtype) is cld_dtype
-        assert find_binop_result_dtype(space, c128_dtype, fld_dtype) is cld_dtype
-
-        # With promote bool (happens on div), the result is that the op should
-        # promote bools to int8
-        assert find_binop_result_dtype(space, bool_dtype, bool_dtype, promote_bools=True) is int8_dtype
-        assert find_binop_result_dtype(space, bool_dtype, float64_dtype, promote_bools=True) is float64_dtype
-
-        # Coerce to floats
-        assert find_binop_result_dtype(space, bool_dtype, float64_dtype, promote_to_float=True) is float64_dtype
-
-    def test_unaryops(self, space):
-        bool_dtype = get_dtype_cache(space).w_booldtype
-        int8_dtype = get_dtype_cache(space).w_int8dtype
-        uint8_dtype = get_dtype_cache(space).w_uint8dtype
-        int16_dtype = get_dtype_cache(space).w_int16dtype
-        uint16_dtype = get_dtype_cache(space).w_uint16dtype
-        int32_dtype = get_dtype_cache(space).w_int32dtype
-        uint32_dtype = get_dtype_cache(space).w_uint32dtype
-        long_dtype = get_dtype_cache(space).w_longdtype
-        ulong_dtype = get_dtype_cache(space).w_ulongdtype
-        int64_dtype = get_dtype_cache(space).w_int64dtype
-        uint64_dtype = get_dtype_cache(space).w_uint64dtype
-        float16_dtype = get_dtype_cache(space).w_float16dtype
-        float32_dtype = get_dtype_cache(space).w_float32dtype
-        float64_dtype = get_dtype_cache(space).w_float64dtype
-
-        # Normal rules, everything returns itself
-        assert find_unaryop_result_dtype(space, bool_dtype) is bool_dtype
-        assert find_unaryop_result_dtype(space, int8_dtype) is int8_dtype
-        assert find_unaryop_result_dtype(space, uint8_dtype) is uint8_dtype
-        assert find_unaryop_result_dtype(space, int16_dtype) is int16_dtype
-        assert find_unaryop_result_dtype(space, uint16_dtype) is uint16_dtype
-        assert find_unaryop_result_dtype(space, int32_dtype) is int32_dtype
-        assert find_unaryop_result_dtype(space, uint32_dtype) is uint32_dtype
-        assert find_unaryop_result_dtype(space, long_dtype) is long_dtype
-        assert find_unaryop_result_dtype(space, ulong_dtype) is ulong_dtype
-        assert find_unaryop_result_dtype(space, int64_dtype) is int64_dtype
-        assert find_unaryop_result_dtype(space, uint64_dtype) is uint64_dtype
-        assert find_unaryop_result_dtype(space, float32_dtype) is float32_dtype
-        assert find_unaryop_result_dtype(space, float64_dtype) is float64_dtype
-
-        # Coerce to floats, some of these will eventually be float16, or
-        # whatever our smallest float type is.
-        assert find_unaryop_result_dtype(space, bool_dtype, promote_to_float=True) is float16_dtype
-        assert find_unaryop_result_dtype(space, int8_dtype, promote_to_float=True) is float16_dtype
-        assert find_unaryop_result_dtype(space, uint8_dtype, promote_to_float=True) is float16_dtype
-        assert find_unaryop_result_dtype(space, int16_dtype, promote_to_float=True) is float32_dtype
-        assert find_unaryop_result_dtype(space, uint16_dtype, promote_to_float=True) is float32_dtype
-        assert find_unaryop_result_dtype(space, int32_dtype, promote_to_float=True) is float64_dtype
-        assert find_unaryop_result_dtype(space, uint32_dtype, promote_to_float=True) is float64_dtype
-        assert find_unaryop_result_dtype(space, int64_dtype, promote_to_float=True) is float64_dtype
-        assert find_unaryop_result_dtype(space, uint64_dtype, promote_to_float=True) is float64_dtype
-        assert find_unaryop_result_dtype(space, float32_dtype, promote_to_float=True) is float32_dtype
-        assert find_unaryop_result_dtype(space, float64_dtype, promote_to_float=True) is float64_dtype
-
-        # promote bools, happens with sign ufunc
-        assert find_unaryop_result_dtype(space, bool_dtype, promote_bools=True) is int8_dtype
-
-
 class TestGenericUfuncOperation(object):
     def test_signature_parser(self, space):
         class Ufunc(object):
@@ -96,10 +15,10 @@
                 self.nout = nout
                 self.nargs = nin + nout
                 self.core_enabled = True
-                self.core_num_dim_ix = 0 
-                self.core_num_dims = [0] * self.nargs  
+                self.core_num_dim_ix = 0
+                self.core_num_dims = [0] * self.nargs
                 self.core_offsets = [0] * self.nargs
-                self.core_dim_ixs = [] 
+                self.core_dim_ixs = []
 
         u = Ufunc(2, 1)
         _parse_signature(space, u, '(m,n), (n,r)->(m,r)')
@@ -116,8 +35,8 @@
         b_dtype = get_dtype_cache(space).w_booldtype
 
         ufunc = W_UfuncGeneric(space, [None, None, None], 'eigenvals', None, 1, 1,
-                     [f32_dtype, c64_dtype, 
-                      f64_dtype, c128_dtype, 
+                     [f32_dtype, c64_dtype,
+                      f64_dtype, c128_dtype,
                       c128_dtype, c128_dtype],
                      '')
         f32_array = W_NDimArray(VoidBoxStorage(0, f32_dtype))
@@ -135,6 +54,22 @@
         exc = raises(OperationError, ufunc.type_resolver, space, [f32_array], [None],
                                 'i->i', ufunc.dtypes)
 
+    def test_allowed_types(self, space):
+        dt_bool = get_dtype_cache(space).w_booldtype
+        dt_float16 = get_dtype_cache(space).w_float16dtype
+        dt_int32 = get_dtype_cache(space).w_int32dtype
+        ufunc = unary_ufunc(space, None, 'x', int_only=True)
+        assert ufunc._calc_dtype(space, dt_bool, out=None) == (dt_bool, dt_bool)
+        assert ufunc.dtypes  # XXX: shouldn't contain too much stuff
+
+        ufunc = unary_ufunc(space, None, 'x', promote_to_float=True)
+        assert ufunc._calc_dtype(space, dt_bool, out=None) == (dt_float16, dt_float16)
+        assert ufunc._calc_dtype(space, dt_bool, casting='same_kind') == (dt_float16, dt_float16)
+        raises(OperationError, ufunc._calc_dtype, space, dt_bool, casting='no')
+
+        ufunc = unary_ufunc(space, None, 'x')
+        assert ufunc._calc_dtype(space, dt_int32, out=None) == (dt_int32, dt_int32)
+
 class AppTestUfuncs(BaseNumpyAppTest):
     def test_constants(self):
         import numpy as np
@@ -167,7 +102,7 @@
             assert 'object' in str(e)
             # Use pypy specific extension for out_dtype
             adder_ufunc0 = frompyfunc(adder, 2, 1, dtypes=['match'])
-            sumdiff = frompyfunc(sumdiff, 2, 2, dtypes=['match'], 
+            sumdiff = frompyfunc(sumdiff, 2, 2, dtypes=['match'],
                                     signature='(i),(i)->(i),(i)')
             adder_ufunc1 = frompyfunc([adder, adder], 2, 1,
                             dtypes=[int, int, int, float, float, float])
@@ -194,6 +129,10 @@
         assert (res[1] == a).all()
 
     def test_frompyfunc_outerloop(self):
+        import sys
+        from numpy import frompyfunc, dtype, arange
+        if '__pypy__' not in sys.builtin_module_names:
+            skip('PyPy only frompyfunc extension')
         def int_times2(in_array, out_array):
             assert in_array.dtype == int
             in_flat = in_array.flat
@@ -206,7 +145,6 @@
             out_flat = out_array.flat
             for i in range(in_array.size):
                 out_flat[i] = in_flat[i] * 2
-        from numpy import frompyfunc, dtype, arange
         ufunc = frompyfunc([int_times2, double_times2], 1, 1,
                             signature='()->()',
                             dtypes=[dtype(int), dtype(int),
@@ -225,12 +163,15 @@
         ac1 = ufunc(ac)
 
     def test_frompyfunc_2d_sig(self):
+        import sys
+        from numpy import frompyfunc, dtype, arange
+        if '__pypy__' not in sys.builtin_module_names:
+            skip('PyPy only frompyfunc extension')
         def times_2(in_array, out_array):
             assert len(in_array.shape) == 2
             assert in_array.shape == out_array.shape
             out_array[:] = in_array * 2
 
-        from numpy import frompyfunc, dtype, arange
         ufunc = frompyfunc([times_2], 1, 1,
                             signature='(m,n)->(n,m)',
                             dtypes=[dtype(int), dtype(int)],
@@ -259,11 +200,14 @@
         assert (ai2 == aiV * 2).all()
 
     def test_frompyfunc_needs_nditer(self):
+        import sys
+        from numpy import frompyfunc, dtype, arange
+        if '__pypy__' not in sys.builtin_module_names:
+            skip('PyPy only frompyfunc extension')
         def summer(in0):
             print 'in summer, in0=',in0,'in0.shape=',in0.shape
             return in0.sum()
 
-        from numpy import frompyfunc, dtype, arange
         ufunc = frompyfunc([summer], 1, 1,
                             signature='(m,m)->()',
                             dtypes=[dtype(int), dtype(int)],
@@ -274,13 +218,16 @@
         assert ao.size == 3
 
     def test_frompyfunc_sig_broadcast(self):
+        import sys
+        from numpy import frompyfunc, dtype, arange
+        if '__pypy__' not in sys.builtin_module_names:
+            skip('PyPy only frompyfunc extension')
         def sum_along_0(in_array, out_array):
             out_array[...] = in_array.sum(axis=0)
 
         def add_two(in0, in1, out):
             out[...] = in0 + in1
 
-        from numpy import frompyfunc, dtype, arange
         ufunc_add = frompyfunc(add_two, 2, 1,
                             signature='(m,n),(m,n)->(m,n)',
                             dtypes=[dtype(int), dtype(int), dtype(int)],
@@ -298,7 +245,10 @@
         assert aout.shape == (3, 3)
 
     def test_frompyfunc_fortran(self):
+        import sys
         import numpy as np
+        if '__pypy__' not in sys.builtin_module_names:
+            skip('PyPy only frompyfunc extension')
         def tofrom_fortran(in0, out0):
             out0[:] = in0.T
 
@@ -333,6 +283,14 @@
         raises(TypeError, adder_ufunc, *args, extobj=True)
         raises(RuntimeError, adder_ufunc, *args, sig='(d,d)->(d)', dtype=int)
 
+    def test_unary_ufunc_kwargs(self):
+        from numpy import array, sin, float16
+        bool_array = array([True])
+        raises(TypeError, sin, bool_array, casting='no')
+        assert sin(bool_array, casting='same_kind').dtype == float16
+        raises(TypeError, sin, bool_array, out=bool_array, casting='same_kind')
+        assert sin(bool_array).dtype == float16
+
     def test_ufunc_attrs(self):
         from numpy import add, multiply, sin
 
@@ -409,6 +367,8 @@
         # test on the base-class dtypes: int, bool, float, complex, object
         # We need this test since they have no common base class.
         import numpy as np
+        not_implemented = set(['ldexp', 'frexp', 'cbrt', 'spacing',
+            'hypot', 'modf', 'remainder', 'nextafter'])
         def find_uncallable_ufuncs(dtype):
             uncallable = set()
             array = np.array(1, dtype)
@@ -428,16 +388,22 @@
             return uncallable
         assert find_uncallable_ufuncs('int') == set()
         assert find_uncallable_ufuncs('bool') == set(['sign'])
-        assert find_uncallable_ufuncs('float') == set(
+        uncallable = find_uncallable_ufuncs('float')
+        uncallable = uncallable.difference(not_implemented)
+        assert uncallable == set(
                 ['bitwise_and', 'bitwise_not', 'bitwise_or', 'bitwise_xor',
                  'left_shift', 'right_shift', 'invert'])
-        assert find_uncallable_ufuncs('complex') == set(
+        uncallable = find_uncallable_ufuncs('complex')
+        uncallable = uncallable.difference(not_implemented)
+        assert uncallable == set(
                 ['bitwise_and', 'bitwise_not', 'bitwise_or', 'bitwise_xor',
                  'arctan2', 'deg2rad', 'degrees', 'rad2deg', 'radians',
                  'fabs', 'fmod', 'invert', 'mod',
                  'logaddexp', 'logaddexp2', 'left_shift', 'right_shift',
                  'copysign', 'signbit', 'ceil', 'floor', 'trunc'])
-        assert find_uncallable_ufuncs('object') == set(
+        uncallable = find_uncallable_ufuncs('object')
+        uncallable = uncallable.difference(not_implemented)
+        assert uncallable == set(
                 ['isnan', 'logaddexp2', 'copysign', 'isfinite', 'signbit',
                  'isinf', 'logaddexp'])
 
@@ -465,6 +431,12 @@
         b = negative(a + a)
         assert (b == [[-2, -4], [-6, -8]]).all()
 
+        class Obj(object):
+            def __neg__(self):
+                return 'neg'
+        x = Obj()
+        assert type(negative(x)) is str
+
     def test_abs(self):
         from numpy import array, absolute
 
@@ -481,6 +453,11 @@
         c = add(a, b)
         for i in range(3):
             assert c[i] == a[i] + b[i]
+        class Obj(object):
+            def __add__(self, other):
+                return 'add'
+        x = Obj()
+        assert type(add(x, 0)) is str
 
     def test_divide(self):
         from numpy import array, divide
@@ -1058,6 +1035,10 @@
             assert np.equal.reduce([1, 2], dtype=dtype) == True
             assert np.equal.reduce([1, 2, 0], dtype=dtype) == False
 
+    def test_reduce_fmax(self):
+        import numpy as np
+        assert np.fmax.reduce(np.arange(11).astype('b')) == 10
+
     def test_reduceND(self):
         from numpy import add, arange
         a = arange(12).reshape(3, 4)
diff --git a/pypy/module/micronumpy/types.py b/pypy/module/micronumpy/types.py
--- a/pypy/module/micronumpy/types.py
+++ b/pypy/module/micronumpy/types.py
@@ -40,7 +40,7 @@
             assert offset < storage._obj.getlength()
         except AttributeError:
             pass
-        return _raw_storage_setitem_unaligned(storage, offset, value) 
+        return _raw_storage_setitem_unaligned(storage, offset, value)
 
     def raw_storage_getitem_unaligned(T, storage, offset):
         assert offset >=0
@@ -48,7 +48,7 @@
             assert offset < storage._obj.getlength()
         except AttributeError:
             pass
-        return _raw_storage_getitem_unaligned(T, storage, offset) 
+        return _raw_storage_getitem_unaligned(T, storage, offset)
 '''
 def simple_unary_op(func):
     specialize.argtype(1)(func)
@@ -134,6 +134,7 @@
 
 class BaseType(object):
     _immutable_fields_ = ['native', 'space']
+    strlen = 0  # chars needed to print any possible value of the type
 
     def __init__(self, space, native=True):
         assert isinstance(space, ObjSpace)
@@ -153,10 +154,6 @@
     def basesize(cls):
         return rffi.sizeof(cls.T)
 
-    def can_cast_to(self, other):
-        # equivalent to PyArray_CanCastSafely
-        return casting_table[self.num][other.num]
-
 class Primitive(object):
     _mixin_ = True
 
@@ -354,6 +351,7 @@
     char = NPY.BOOLLTR
     BoxType = boxes.W_BoolBox
     format_code = "?"
+    strlen = 5  # "False"
 
     _True = BoxType(True)
     _False = BoxType(False)
@@ -439,7 +437,9 @@
     @specialize.argtype(1)
     def round(self, v, decimals=0):
         if decimals != 0:
-            return v
+            # numpy incompatible message
+            raise oefmt(self.space.w_TypeError,
+                "Cannot use float math on bool dtype")
         return Float64(self.space).box(self.unbox(v))
 
 class Integer(Primitive):
@@ -719,6 +719,7 @@
 
 class Float(Primitive):
     _mixin_ = True
+    strlen = 32
 
     def _coerce(self, space, w_item):
         if w_item is None:
@@ -1045,7 +1046,7 @@
         else:
             return x
 
-class Float16(BaseType, Float):
+class Float16(Float, BaseType):
     _STORAGE_T = rffi.USHORT
     T = rffi.SHORT
     num = NPY.HALF
@@ -1090,7 +1091,7 @@
             hbits = byteswap(hbits)
         raw_storage_setitem_unaligned(storage, i + offset, hbits)
 
-class Float32(BaseType, Float):
+class Float32(Float, BaseType):
     T = rffi.FLOAT
     num = NPY.FLOAT
     kind = NPY.FLOATINGLTR
@@ -1099,7 +1100,7 @@
     format_code = "f"
     max_value = 3.4e38
 
-class Float64(BaseType, Float):
+class Float64(Float, BaseType):
     T = rffi.DOUBLE
     num = NPY.DOUBLE
     kind = NPY.FLOATINGLTR
@@ -1110,6 +1111,7 @@
 
 class ComplexFloating(object):
     _mixin_ = True
+    strlen = 64


More information about the pypy-commit mailing list