Author: Amaury Forgeot d'Arc <[email protected]>
Branch: decimal-libmpdec
Changeset: r73596:c48bbeeb5798
Date: 2014-09-17 22:06 +0200
http://bitbucket.org/pypy/pypy/changeset/c48bbeeb5798/

Log:    One more test, many methods to add.

diff --git a/pypy/module/_decimal/interp_decimal.py 
b/pypy/module/_decimal/interp_decimal.py
--- a/pypy/module/_decimal/interp_decimal.py
+++ b/pypy/module/_decimal/interp_decimal.py
@@ -360,6 +360,26 @@
     def descr_rpow(self, space, w_other):
         return W_Decimal.pow_impl(space, w_other, self, None)
 
+    def copy_abs_w(self, space):
+        w_result = W_Decimal.allocate(space)
+        with lltype.scoped_alloc(rffi.CArrayPtr(rffi.UINT).TO, 1,
+                                 zero=True) as status_ptr:
+            rmpdec.mpd_qcopy_abs(w_result.mpd, self.mpd, status_ptr)
+            status = rffi.cast(lltype.Signed, status_ptr[0])
+        if status & rmpdec.MPD_Malloc_error:
+            raise OperationError(space.w_MemoryError, space.w_None)
+        return w_result
+
+    def copy_negate_w(self, space):
+        w_result = W_Decimal.allocate(space)
+        with lltype.scoped_alloc(rffi.CArrayPtr(rffi.UINT).TO, 1,
+                                 zero=True) as status_ptr:
+            rmpdec.mpd_qcopy_negate(w_result.mpd, self.mpd, status_ptr)
+            status = rffi.cast(lltype.Signed, status_ptr[0])
+        if status & rmpdec.MPD_Malloc_error:
+            raise OperationError(space.w_MemoryError, space.w_None)
+        return w_result
+
     def copy_sign_w(self, space, w_other, w_context=None):
         context = convert_context(space, w_context)
         w_other = convert_op_raise(space, context, w_other)
@@ -371,6 +391,11 @@
 
     # Unary arithmetic functions, optional context arg
 
+    def number_class_w(self, space, w_context=None):
+        context = interp_context.ensure_context(space, w_context)
+        cp = rmpdec.mpd_class(self.mpd, context.ctx)
+        return space.wrap(rffi.charp2str(cp))
+
     def to_integral_w(self, space, w_rounding=None, w_context=None):
         context = interp_context.ensure_context(space, w_context)
         w_workctx = context.copy_w(space)
@@ -396,13 +421,42 @@
             rmpdec.mpd_qround_to_intx(w_result.mpd, self.mpd,
                                       w_workctx.ctx, status_ptr)
         return w_result
+
+    # Ternary arithmetic functions, optional context arg
+    def fma_w(self, space, w_other, w_third, w_context=None):
+        context = interp_context.ensure_context(space, w_context)
+        w_a, w_b, w_c = convert_ternop_raise(space, context,
+                                             self, w_other, w_third)
+        w_result = W_Decimal.allocate(space)
+        with context.catch_status(space) as (ctx, status_ptr):
+            rmpdec.mpd_qfma(w_result.mpd, w_a.mpd, w_b.mpd, w_c.mpd,
+                            ctx, status_ptr)
+        return w_result
         
-
     # Boolean functions
+    def is_canonical_w(self, space):
+        return space.wrap(bool(rmpdec.mpd_iscanonical(self.mpd)))
+    def is_nan_w(self, space):
+        return space.wrap(bool(rmpdec.mpd_isnan(self.mpd)))
     def is_qnan_w(self, space):
         return space.wrap(bool(rmpdec.mpd_isqnan(self.mpd)))
+    def is_snan_w(self, space):
+        return space.wrap(bool(rmpdec.mpd_issnan(self.mpd)))
     def is_infinite_w(self, space):
         return space.wrap(bool(rmpdec.mpd_isinfinite(self.mpd)))
+    def is_finite_w(self, space):
+        return space.wrap(bool(rmpdec.mpd_isfinite(self.mpd)))
+    def is_signed_w(self, space):
+        return space.wrap(bool(rmpdec.mpd_issigned(self.mpd)))
+    def is_zero_w(self, space):
+        return space.wrap(bool(rmpdec.mpd_iszero(self.mpd)))
+    # Boolean functions, optional context arg
+    def is_normal_w(self, space, w_context=None):
+        context = interp_context.ensure_context(space, w_context)
+        return space.wrap(bool(rmpdec.mpd_isnormal(self.mpd, context.ctx)))
+    def is_subnormal_w(self, space, w_context=None):
+        context = interp_context.ensure_context(space, w_context)
+        return space.wrap(bool(rmpdec.mpd_issubnormal(self.mpd, context.ctx)))
 
     def as_tuple_w(self, space):
         "Return the DecimalTuple representation of a Decimal"
@@ -492,6 +546,24 @@
                     space.type(w_y))
     return w_a, w_b
 
+def convert_ternop_raise(space, context, w_x, w_y, w_z):
+    w_err, w_a = convert_op(space, context, w_x)
+    if w_err:
+        raise oefmt(space.w_TypeError,
+                    "conversion from %N to Decimal is not supported",
+                    space.type(w_x))
+    w_err, w_b = convert_op(space, context, w_y)
+    if w_err:
+        raise oefmt(space.w_TypeError,
+                    "conversion from %N to Decimal is not supported",
+                    space.type(w_y))
+    w_err, w_c = convert_op(space, context, w_z)
+    if w_err:
+        raise oefmt(space.w_TypeError,
+                    "conversion from %N to Decimal is not supported",
+                    space.type(w_z))
+    return w_a, w_b, w_c
+
 # Convert rationals for comparison
 def _multiply_by_denominator(space, w_v, w_r, context):
     # w_v is not special, w_r is a rational.
@@ -502,7 +574,7 @@
     if not vv:
         raise OperationError(space.w_MemoryError, space.w_None)
     try:
-        result = W_Decimal(space)
+        w_result = W_Decimal.allocate(space)
         with lltype.scoped_alloc(rmpdec.MPD_CONTEXT_PTR.TO) as maxctx:
             rmpdec.mpd_maxcontext(maxctx)
             # Prevent Overflow in the following multiplication. The
@@ -514,18 +586,19 @@
             vv.c_exp = 0
             with lltype.scoped_alloc(rffi.CArrayPtr(rffi.UINT).TO, 1,
                                      zero=True) as status_ptr:
-                rmpdec.mpd_qmul(result.mpd, vv, denom.mpd, maxctx, status_ptr)
+                rmpdec.mpd_qmul(w_result.mpd, vv, denom.mpd, maxctx, 
status_ptr)
                 # If any status has been accumulated during the multiplication,
                 # the result is invalid. This is very unlikely, since even the
                 # 32-bit version supports 425000000 digits.
-                if status_ptr[0]:
-                    raise oefmt(space.w_ValueError,
-                                "exact conversion for comparison failed")
-            result.mpd.c_exp = exp
+                status = rffi.cast(lltype.Signed, status_ptr[0])
+            if status:
+                raise oefmt(space.w_ValueError,
+                            "exact conversion for comparison failed")
+            w_result.mpd.c_exp = exp
     finally:
         rmpdec.mpd_del(vv)
 
-    return space.wrap(result)
+    return w_result
 
 def _numerator_as_decimal(space, w_r, context):
     w_numerator = space.getattr(w_r, space.wrap("numerator"))
@@ -609,6 +682,22 @@
         return binary_number_method(space, mpd_func, w_other, w_self)
     return interp2app(descr_method)
 
+# Unary function with an optional context arg.
+def unary_method(space, mpd_func, w_self, w_context):
+    self = space.interp_w(W_Decimal, w_self)
+    context = convert_context(space, w_context)
+    w_result = W_Decimal.allocate(space)
+    with context.catch_status(space) as (ctx, status_ptr):
+        mpd_func(w_result.mpd, self.mpd, ctx, status_ptr)
+    return w_result
+    
+def make_unary_method(mpd_func_name):
+    mpd_func = getattr(rmpdec, mpd_func_name)
+    @func_renamer('descr_%s' % mpd_func_name)
+    def descr_method(space, w_self, w_context=None):
+        return unary_method(space, mpd_func, w_self, w_context)
+    return interp2app(descr_method)
+
 def convert_context(space, w_context):
     if w_context is None:
         return interp_context.getcontext(space)
@@ -891,14 +980,39 @@
     __rmod__ = make_binary_number_method_right('mpd_qrem'),
     __rdivmod__ = interp2app(W_Decimal.descr_rdivmod),
     __rpow__ = interp2app(W_Decimal.descr_rpow),
+    # Unary functions, optional context arg for conversion errors
+    copy_abs = interp2app(W_Decimal.copy_abs_w),
+    copy_negate = interp2app(W_Decimal.copy_negate_w),
     # Unary arithmetic functions, optional context arg
+    exp = make_unary_method('mpd_qexp'),
+    ln = make_unary_method('mpd_qln'),
+    log10 = make_unary_method('mpd_qlog10'),
+    logb = make_unary_method('mpd_qlogb'),
+    logical_invert = make_unary_method('mpd_qinvert'),
+    next_minus = make_unary_method('mpd_qnext_minus'),
+    next_plus = make_unary_method('mpd_qnext_plus'),
+    normalize = make_unary_method('mpd_qreduce'),
+    number_class = interp2app(W_Decimal.number_class_w),
     to_integral = interp2app(W_Decimal.to_integral_w),
+    to_integral_exact = interp2app(W_Decimal.to_integral_exact_w),
     to_integral_value = interp2app(W_Decimal.to_integral_w),
-    to_integral_exact = interp2app(W_Decimal.to_integral_exact_w),
-    #
+    sqrt = make_unary_method('mpd_qsqrt'),
+    # Ternary arithmetic functions, optional context arg
+    fma = interp2app(W_Decimal.fma_w),
+    # Boolean functions, no context arg
+    is_canonical = interp2app(W_Decimal.is_canonical_w),
+    is_nan = interp2app(W_Decimal.is_nan_w),
+    is_qnan = interp2app(W_Decimal.is_qnan_w),
+    is_snan = interp2app(W_Decimal.is_snan_w),
+    is_infinite = interp2app(W_Decimal.is_infinite_w),
+    is_finite = interp2app(W_Decimal.is_finite_w),
+    is_signed = interp2app(W_Decimal.is_signed_w),
+    is_zero = interp2app(W_Decimal.is_zero_w),
+    # Boolean functions, optional context arg
+    is_normal = interp2app(W_Decimal.is_normal_w),
+    is_subnormal = interp2app(W_Decimal.is_subnormal_w),
+    # Binary functions, optional context arg for conversion errors
     copy_sign = interp2app(W_Decimal.copy_sign_w),
-    is_qnan = interp2app(W_Decimal.is_qnan_w),
-    is_infinite = interp2app(W_Decimal.is_infinite_w),
     #
     as_tuple = interp2app(W_Decimal.as_tuple_w),
     from_float = interp2app(decimal_from_float_w, as_classmethod=True),
diff --git a/pypy/module/_decimal/test/test_context.py 
b/pypy/module/_decimal/test/test_context.py
--- a/pypy/module/_decimal/test/test_context.py
+++ b/pypy/module/_decimal/test/test_context.py
@@ -21,6 +21,9 @@
         def assertEqual(space, w_x, w_y):
             assert space.eq_w(w_x, w_y)
         cls.w_assertEqual = space.wrap(gateway.interp2app(assertEqual))
+        def assertIs(space, w_x, w_y):
+            assert space.is_w(w_x, w_y)
+        cls.w_assertIs = space.wrap(gateway.interp2app(assertIs))
 
         cls.w_assertRaises = space.appexec([], """(): return raises""")
 
@@ -274,3 +277,84 @@
         self.assertEqual(c.is_zero(10), d)
         self.assertRaises(TypeError, c.is_zero, '10')
 
+    def test_implicit_context(self):
+        Decimal = self.decimal.Decimal
+        localcontext = self.decimal.localcontext
+
+        with localcontext() as c:
+            c.prec = 1
+            c.Emax = 1
+            c.Emin = -1
+
+            # abs
+            self.assertEqual(abs(Decimal("-10")), 10)
+            # add
+            self.assertEqual(Decimal("7") + 1, 8)
+            # divide
+            self.assertEqual(Decimal("10") / 5, 2)
+            # divide_int
+            self.assertEqual(Decimal("10") // 7, 1)
+            # fma
+            self.assertEqual(Decimal("1.2").fma(Decimal("0.01"), 1), 1)
+            self.assertIs(Decimal("NaN").fma(7, 1).is_nan(), True)
+            # three arg power
+            self.assertEqual(pow(Decimal(10), 2, 7), 2)
+            # exp
+            self.assertEqual(Decimal("1.01").exp(), 3)
+            # is_normal
+            self.assertIs(Decimal("0.01").is_normal(), False)
+            # is_subnormal
+            self.assertIs(Decimal("0.01").is_subnormal(), True)
+            # ln
+            self.assertEqual(Decimal("20").ln(), 3)
+            # log10
+            self.assertEqual(Decimal("20").log10(), 1)
+            # logb
+            self.assertEqual(Decimal("580").logb(), 2)
+            # logical_invert
+            self.assertEqual(Decimal("10").logical_invert(), 1)
+            # minus
+            self.assertEqual(-Decimal("-10"), 10)
+            # multiply
+            self.assertEqual(Decimal("2") * 4, 8)
+            # next_minus
+            self.assertEqual(Decimal("10").next_minus(), 9)
+            # next_plus
+            self.assertEqual(Decimal("10").next_plus(), Decimal('2E+1'))
+            # normalize
+            self.assertEqual(Decimal("-10").normalize(), Decimal('-1E+1'))
+            # number_class
+            self.assertEqual(Decimal("10").number_class(), '+Normal')
+            # plus
+            self.assertEqual(+Decimal("-1"), -1)
+            # remainder
+            self.assertEqual(Decimal("10") % 7, 3)
+            # subtract
+            self.assertEqual(Decimal("10") - 7, 3)
+            # to_integral_exact
+            self.assertEqual(Decimal("1.12345").to_integral_exact(), 1)
+
+            # Boolean functions
+            self.assertTrue(Decimal("1").is_canonical())
+            self.assertTrue(Decimal("1").is_finite())
+            self.assertTrue(Decimal("1").is_finite())
+            self.assertTrue(Decimal("snan").is_snan())
+            self.assertTrue(Decimal("-1").is_signed())
+            self.assertTrue(Decimal("0").is_zero())
+            self.assertTrue(Decimal("0").is_zero())
+
+        # Copy
+        with localcontext() as c:
+            c.prec = 10000
+            x = 1228 ** 1523
+            y = -Decimal(x)
+
+            z = y.copy_abs()
+            self.assertEqual(z, x)
+
+            z = y.copy_negate()
+            self.assertEqual(z, x)
+
+            z = y.copy_sign(Decimal(1))
+            self.assertEqual(z, x)
+
diff --git a/rpython/rlib/rmpdec.py b/rpython/rlib/rmpdec.py
--- a/rpython/rlib/rmpdec.py
+++ b/rpython/rlib/rmpdec.py
@@ -41,6 +41,7 @@
         "mpd_qcopy", "mpd_qncopy", "mpd_setspecial", "mpd_clear_flags",
         "mpd_qimport_u32", "mpd_qexport_u32", "mpd_qexport_u16",
         "mpd_set_sign", "mpd_set_positive", "mpd_sign", "mpd_qfinalize",
+        "mpd_class",
         "mpd_getprec", "mpd_getemin",  "mpd_getemax", "mpd_getround", 
"mpd_getclamp",
         "mpd_qsetprec", "mpd_qsetemin",  "mpd_qsetemax", "mpd_qsetround", 
"mpd_qsetclamp",
         "mpd_maxcontext",
@@ -48,7 +49,7 @@
         "mpd_to_sci", "mpd_to_sci_size",
         "mpd_iszero", "mpd_isnegative", "mpd_issigned",
         "mpd_isfinite", "mpd_isinfinite",
-        "mpd_isnormal", "mpd_issubnormal", "mpd_isspecial",
+        "mpd_isnormal", "mpd_issubnormal", "mpd_isspecial", "mpd_iscanonical",
         "mpd_isnan", "mpd_issnan", "mpd_isqnan",
         "mpd_qcmp", "mpd_qcompare", "mpd_qcompare_signal",
         "mpd_qmin", "mpd_qmax", "mpd_qmin_mag", "mpd_qmax_mag",
@@ -58,9 +59,10 @@
         "mpd_qadd", "mpd_qsub", "mpd_qmul", "mpd_qdiv", "mpd_qdivint",
         "mpd_qrem", "mpd_qrem_near", "mpd_qdivmod", "mpd_qpow", "mpd_qpowmod", 
         "mpd_qfma",
-        "mpd_qexp", "mpd_qln", "mpd_qlog10", "mpd_qsqrt", "mpd_qinvert",
+        "mpd_qexp", "mpd_qln", "mpd_qlog10", "mpd_qlogb",
+        "mpd_qsqrt", "mpd_qinvert",
         "mpd_qand", "mpd_qor", "mpd_qxor",
-        "mpd_qcopy_sign",
+        "mpd_qcopy_sign", "mpd_qcopy_abs", "mpd_qcopy_negate",
         "mpd_qround_to_int", "mpd_qround_to_intx",
         ],
     compile_extra=compile_extra,
@@ -187,6 +189,8 @@
     'mpd_sign', [MPD_PTR], rffi.UCHAR)
 mpd_qfinalize = external(
     'mpd_qfinalize', [MPD_PTR, MPD_CONTEXT_PTR, rffi.UINTP], lltype.Void)
+mpd_class = external(
+    'mpd_class', [MPD_PTR, MPD_CONTEXT_PTR], rffi.CCHARP)
 
 # Context operations
 mpd_getprec = external(
@@ -247,6 +251,8 @@
     'mpd_issubnormal', [MPD_PTR, MPD_CONTEXT_PTR], rffi.INT)
 mpd_isspecial = external(
     'mpd_isspecial', [MPD_PTR], rffi.INT)
+mpd_iscanonical = external(
+    'mpd_iscanonical', [MPD_PTR], rffi.INT)
 mpd_isnan = external(
     'mpd_isnan', [MPD_PTR], rffi.INT)
 mpd_issnan = external(
@@ -358,6 +364,9 @@
 mpd_qlog10 = external(
     'mpd_qlog10',
     [MPD_PTR, MPD_PTR, MPD_CONTEXT_PTR, rffi.UINTP], lltype.Void)
+mpd_qlogb = external(
+    'mpd_qlogb',
+    [MPD_PTR, MPD_PTR, MPD_CONTEXT_PTR, rffi.UINTP], lltype.Void)
 mpd_qsqrt = external(
     'mpd_qsqrt',
     [MPD_PTR, MPD_PTR, MPD_CONTEXT_PTR, rffi.UINTP], lltype.Void)
@@ -369,6 +378,14 @@
     'mpd_qcopy_sign',
     [MPD_PTR, MPD_PTR, MPD_PTR, rffi.UINTP],
     lltype.Void)
+mpd_qcopy_abs = external(
+    'mpd_qcopy_abs',
+    [MPD_PTR, MPD_PTR, rffi.UINTP],
+    lltype.Void)
+mpd_qcopy_negate = external(
+    'mpd_qcopy_negate',
+    [MPD_PTR, MPD_PTR, rffi.UINTP],
+    lltype.Void)
 
 mpd_qround_to_int = external(
     'mpd_qround_to_int', [MPD_PTR, MPD_PTR, MPD_CONTEXT_PTR, rffi.UINTP],
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to