[pypy-commit] pypy py3.3: Add a cffi implementation of the _decimal module.

amauryfa noreply at buildbot.pypy.org
Tue Mar 10 21:56:47 CET 2015


Author: Amaury Forgeot d'Arc <amauryfa at gmail.com>
Branch: py3.3
Changeset: r76303:99b65d047f8c
Date: 2015-03-10 14:47 +0100
http://bitbucket.org/pypy/pypy/changeset/99b65d047f8c/

Log:	Add a cffi implementation of the _decimal module.

diff too long, truncating to 2000 out of 18186 lines

diff --git a/lib-python/3/test/test_decimal.py b/lib-python/3/test/test_decimal.py
--- a/lib-python/3/test/test_decimal.py
+++ b/lib-python/3/test/test_decimal.py
@@ -4011,8 +4011,8 @@
         self.assertRaises(KeyError, Context, traps=["Q"])
 
         # Type error in conversion
-        self.assertRaises(TypeError, Context, flags=(0,1))
-        self.assertRaises(TypeError, Context, traps=(1,0))
+        self.assertRaises(TypeError, Context, flags=0)
+        self.assertRaises(TypeError, Context, traps=1)
 
 class CContextInputValidation(ContextInputValidation):
     decimal = C
@@ -4150,7 +4150,7 @@
         self.assertEqual(C.__version__, P.__version__)
         self.assertEqual(C.__libmpdec_version__, P.__libmpdec_version__)
 
-        x = dir(C)
+        x = [s for s in dir(C) if '__' in s or not s.startswith('_')]
         y = [s for s in dir(P) if '__' in s or not s.startswith('_')]
         self.assertEqual(set(x) - set(y), set())
 
@@ -4158,6 +4158,7 @@
 
         x = [s for s in dir(C.Context()) if '__' in s or not s.startswith('_')]
         y = [s for s in dir(P.Context()) if '__' in s or not s.startswith('_')]
+        y.append('__slots__')
         self.assertEqual(set(x) - set(y), set())
 
     def test_decimal_attributes(self):
@@ -4793,15 +4794,17 @@
         self.assertRaises(OverflowError, Context, Emax=int_max+1)
         self.assertRaises(OverflowError, Context, Emin=-int_max-2)
         self.assertRaises(OverflowError, Context, clamp=int_max+1)
-        self.assertRaises(OverflowError, Context, capitals=int_max+1)
+        self.assertRaises((OverflowError, ValueError), Context, capitals=int_max+1)
 
         # OverflowError, general ValueError
         for attr in ('prec', 'Emin', 'Emax', 'capitals', 'clamp'):
-            self.assertRaises(OverflowError, setattr, c, attr, int_max+1)
-            self.assertRaises(OverflowError, setattr, c, attr, -int_max-2)
+            self.assertRaises((OverflowError, ValueError), setattr, c, attr,
+                              int_max+1)
+            self.assertRaises((OverflowError, ValueError), setattr, c, attr,
+                              -int_max-2)
             if sys.platform != 'win32':
-                self.assertRaises(ValueError, setattr, c, attr, int_max)
-                self.assertRaises(ValueError, setattr, c, attr, -int_max-1)
+                self.assertRaises((OverflowError, ValueError), setattr, c, attr, int_max)
+                self.assertRaises((OverflowError, ValueError), setattr, c, attr, -int_max-1)
 
         # OverflowError: _unsafe_setprec, _unsafe_setemin, _unsafe_setemax
         if C.MAX_PREC == 425000000:
@@ -4830,8 +4833,8 @@
             self.assertRaises(ValueError, setattr, c, attr, 2)
             self.assertRaises(TypeError, setattr, c, attr, [1,2,3])
             if HAVE_CONFIG_64:
-                self.assertRaises(ValueError, setattr, c, attr, 2**32)
-                self.assertRaises(ValueError, setattr, c, attr, 2**32+1)
+                self.assertRaises((ValueError, OverflowError), setattr, c, attr, 2**32)
+                self.assertRaises((ValueError, OverflowError), setattr, c, attr, 2**32+1)
 
         # Invalid local context
         self.assertRaises(TypeError, exec, 'with localcontext("xyz"): pass',
@@ -4845,6 +4848,8 @@
         self.assertRaises(TypeError, setcontext, "xyz")
         setcontext(saved_context)
 
+    # pypy does not keep interned strings
+    @cpython_only
     def test_rounding_strings_interned(self):
 
         self.assertIs(C.ROUND_UP, P.ROUND_UP)
@@ -5371,6 +5376,7 @@
             x = (1, (0, 1), "N")
             self.assertEqual(str(Decimal(x)), '-sNaN1')
 
+    @cpython_only
     def test_sizeof(self):
         Decimal = C.Decimal
         HAVE_CONFIG_64 = (C.MAX_PREC > 425000000)
diff --git a/lib_pypy/_decimal.py b/lib_pypy/_decimal.py
new file mode 100644
--- /dev/null
+++ b/lib_pypy/_decimal.py
@@ -0,0 +1,1900 @@
+# Implementation of the "decimal" module, based on libmpdec library.
+
+from cffi import FFI as _FFI
+import collections as _collections
+import math as _math
+import numbers as _numbers
+import sys as _sys
+
+# Compatibility with the C version
+HAVE_THREADS = True
+if _sys.maxsize == 2**63-1:
+    MAX_PREC = 999999999999999999
+    MAX_EMAX = 999999999999999999
+    MIN_EMIN = -999999999999999999
+else:
+    MAX_PREC = 425000000
+    MAX_EMAX = 425000000
+    MIN_EMIN = -425000000
+
+MIN_ETINY = MIN_EMIN - (MAX_PREC-1)
+
+# Errors
+
+class DecimalException(ArithmeticError):
+    __module__ = 'decimal'
+    def handle(self, context, *args):
+        pass
+
+class Clamped(DecimalException):
+    __module__ = 'decimal'
+
+class InvalidOperation(DecimalException):
+    __module__ = 'decimal'
+    def handle(self, context, *args):
+        if args:
+            ans = _dec_from_triple(args[0]._sign, args[0]._int, 'n', True)
+            return ans._fix_nan(context)
+        return _NaN
+
+class ConversionSyntax(InvalidOperation):
+    __module__ = 'decimal'
+    def handle(self, context, *args):
+        return _NaN
+
+class DivisionByZero(DecimalException, ZeroDivisionError):
+    __module__ = 'decimal'
+    def handle(self, context, sign, *args):
+        return _SignedInfinity[sign]
+
+class DivisionImpossible(InvalidOperation):
+    __module__ = 'decimal'
+    def handle(self, context, *args):
+        return _NaN
+
+class DivisionUndefined(InvalidOperation, ZeroDivisionError):
+    __module__ = 'decimal'
+    def handle(self, context, *args):
+        return _NaN
+
+class Inexact(DecimalException):
+    __module__ = 'decimal'
+
+class InvalidContext(InvalidOperation):
+    __module__ = 'decimal'
+    def handle(self, context, *args):
+        return _NaN
+
+class Rounded(DecimalException):
+    __module__ = 'decimal'
+
+class Subnormal(DecimalException):
+    __module__ = 'decimal'
+
+class Overflow(Inexact, Rounded):
+    __module__ = 'decimal'
+    def handle(self, context, sign, *args):
+        if context.rounding in (ROUND_HALF_UP, ROUND_HALF_EVEN,
+                                ROUND_HALF_DOWN, ROUND_UP):
+            return _SignedInfinity[sign]
+        if sign == 0:
+            if context.rounding == ROUND_CEILING:
+                return _SignedInfinity[sign]
+            return _dec_from_triple(sign, '9'*context.prec,
+                            context.Emax-context.prec+1)
+        if sign == 1:
+            if context.rounding == ROUND_FLOOR:
+                return _SignedInfinity[sign]
+            return _dec_from_triple(sign, '9'*context.prec,
+                             context.Emax-context.prec+1)
+
+class Underflow(Inexact, Rounded, Subnormal):
+    __module__ = 'decimal'
+
+class FloatOperation(DecimalException, TypeError):
+    __module__ = 'decimal'
+
+# Bindings to the libmpdec library
+
+_ffi = _FFI()
+_ffi.cdef("""
+typedef size_t mpd_size_t; /* unsigned size type */
+typedef ssize_t mpd_ssize_t; /* signed size type */
+typedef size_t mpd_uint_t;
+#define MPD_SIZE_MAX ...
+#define MPD_SSIZE_MIN ...
+#define MPD_SSIZE_MAX ...
+
+const char *mpd_version(void);
+void mpd_free(void *ptr);
+
+typedef struct mpd_context_t {
+    mpd_ssize_t prec;   /* precision */
+    mpd_ssize_t emax;   /* max positive exp */
+    mpd_ssize_t emin;   /* min negative exp */
+    uint32_t traps;     /* status events that should be trapped */
+    uint32_t status;    /* status flags */
+    uint32_t newtrap;   /* set by mpd_addstatus_raise() */
+    int      round;     /* rounding mode */
+    int      clamp;     /* clamp mode */
+    int      allcr;     /* all functions correctly rounded */
+} mpd_context_t;
+
+enum {
+    MPD_ROUND_UP,          /* round away from 0               */
+    MPD_ROUND_DOWN,        /* round toward 0 (truncate)       */
+    MPD_ROUND_CEILING,     /* round toward +infinity          */
+    MPD_ROUND_FLOOR,       /* round toward -infinity          */
+    MPD_ROUND_HALF_UP,     /* 0.5 is rounded up               */
+    MPD_ROUND_HALF_DOWN,   /* 0.5 is rounded down             */
+    MPD_ROUND_HALF_EVEN,   /* 0.5 is rounded to even          */
+    MPD_ROUND_05UP,        /* round zero or five away from 0  */
+    MPD_ROUND_TRUNC,       /* truncate, but set infinity      */
+    MPD_ROUND_GUARD
+};
+
+#define MPD_Clamped             ...
+#define MPD_Conversion_syntax   ...
+#define MPD_Division_by_zero    ...
+#define MPD_Division_impossible ...
+#define MPD_Division_undefined  ...
+#define MPD_Fpu_error           ...
+#define MPD_Inexact             ...
+#define MPD_Invalid_context     ...
+#define MPD_Invalid_operation   ...
+#define MPD_Malloc_error        ...
+#define MPD_Not_implemented     ...
+#define MPD_Overflow            ...
+#define MPD_Rounded             ...
+#define MPD_Subnormal           ...
+#define MPD_Underflow           ...
+#define MPD_Max_status          ...
+/* Conditions that result in an IEEE 754 exception */
+#define MPD_IEEE_Invalid_operation ...
+/* Errors that require the result of an operation to be set to NaN */
+#define MPD_Errors              ...
+
+
+
+void mpd_maxcontext(mpd_context_t *ctx);
+int mpd_qsetprec(mpd_context_t *ctx, mpd_ssize_t prec);
+int mpd_qsetemax(mpd_context_t *ctx, mpd_ssize_t emax);
+int mpd_qsetemin(mpd_context_t *ctx, mpd_ssize_t emin);
+int mpd_qsetround(mpd_context_t *ctx, int newround);
+int mpd_qsettraps(mpd_context_t *ctx, uint32_t flags);
+int mpd_qsetstatus(mpd_context_t *ctx, uint32_t flags);
+int mpd_qsetclamp(mpd_context_t *ctx, int c);
+
+
+
+
+typedef struct mpd_t {
+    uint8_t flags;
+    mpd_ssize_t exp;
+    mpd_ssize_t digits;
+    mpd_ssize_t len;
+    mpd_ssize_t alloc;
+    mpd_uint_t *data;
+} mpd_t;
+
+#define MPD_POS                 ...
+#define MPD_NEG                 ...
+#define MPD_INF                 ...
+#define MPD_NAN                 ...
+#define MPD_SNAN                ...
+#define MPD_SPECIAL             ...
+#define MPD_STATIC              ...
+#define MPD_STATIC_DATA         ...
+#define MPD_SHARED_DATA         ...
+#define MPD_CONST_DATA          ...
+#define MPD_DATAFLAGS           ...
+
+
+mpd_t *mpd_qnew(void);
+void mpd_del(mpd_t *dec);
+
+
+/* Operations */
+void mpd_qabs(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qplus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qminus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsqrt(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qexp(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qln(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qlog10(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qlogb(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qinvert(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+
+void mpd_qmax(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmax_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmin(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmin_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+
+void mpd_qadd(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsub(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmul(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qdiv(mpd_t *q, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qdivint(mpd_t *q, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qfma(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qrem(mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qrem_near(mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qpow(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qpowmod(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_t *mod, const mpd_context_t *ctx, uint32_t *status);
+int mpd_qcopy_sign(mpd_t *result, const mpd_t *a, const mpd_t *b, uint32_t *status);
+int mpd_qcopy_abs(mpd_t *result, const mpd_t *a, uint32_t *status);
+int mpd_qcopy_negate(mpd_t *result, const mpd_t *a, uint32_t *status);
+void mpd_qdivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qand(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qor(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qxor(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+int mpd_same_quantum(const mpd_t *a, const mpd_t *b);
+
+void mpd_qround_to_intx(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qround_to_int(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+int mpd_qcopy(mpd_t *result, const mpd_t *a,  uint32_t *status);
+
+int mpd_qcmp(const mpd_t *a, const mpd_t *b, uint32_t *status);
+int mpd_qcompare(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+int mpd_qcompare_signal(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+int mpd_compare_total(mpd_t *result, const mpd_t *a, const mpd_t *b);
+int mpd_compare_total_mag(mpd_t *result, const mpd_t *a, const mpd_t *b);
+void mpd_qnext_toward(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qnext_minus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qnext_plus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qquantize(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+
+void mpd_qrotate(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qscaleb(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qshift(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qreduce(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+
+/* Get attributes */
+uint8_t mpd_sign(const mpd_t *dec);
+int mpd_isnegative(const mpd_t *dec);
+int mpd_ispositive(const mpd_t *dec);
+int mpd_iszero(const mpd_t *dec);
+int mpd_isfinite(const mpd_t *dec);
+int mpd_isinfinite(const mpd_t *dec);
+int mpd_issigned(const mpd_t *dec);
+int mpd_isnan(const mpd_t *dec);
+int mpd_issnan(const mpd_t *dec);
+int mpd_isspecial(const mpd_t *dec);
+int mpd_isqnan(const mpd_t *dec);
+int mpd_isnormal(const mpd_t *dec, const mpd_context_t *ctx);
+int mpd_issubnormal(const mpd_t *dec, const mpd_context_t *ctx);
+mpd_ssize_t mpd_adjexp(const mpd_t *dec);
+mpd_ssize_t mpd_etiny(const mpd_context_t *ctx);
+mpd_ssize_t mpd_etop(const mpd_context_t *ctx);
+
+mpd_t *mpd_qncopy(const mpd_t *a);
+
+/* Set attributes */
+void mpd_set_sign(mpd_t *result, uint8_t sign);
+void mpd_set_positive(mpd_t *result);
+void mpd_clear_flags(mpd_t *result);
+void mpd_seterror(mpd_t *result, uint32_t flags, uint32_t *status);
+void mpd_setspecial(mpd_t *dec, uint8_t sign, uint8_t type);
+
+/* I/O */
+void mpd_qimport_u16(mpd_t *result, const uint16_t *srcdata, size_t srclen,
+                     uint8_t srcsign, uint32_t srcbase,
+                     const mpd_context_t *ctx, uint32_t *status);
+size_t mpd_qexport_u16(uint16_t **rdata, size_t rlen, uint32_t base,
+                       const mpd_t *src, uint32_t *status);
+void mpd_qset_string(mpd_t *dec, const char *s, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qset_uint(mpd_t *result, mpd_uint_t a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qset_ssize(mpd_t *result, mpd_ssize_t a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsset_ssize(mpd_t *result, mpd_ssize_t a, const mpd_context_t *ctx, uint32_t *status);
+mpd_ssize_t mpd_qget_ssize(const mpd_t *dec, uint32_t *status);
+int mpd_lsnprint_signals(char *dest, int nmemb, uint32_t flags, const char *signal_string[]);
+#define MPD_MAX_SIGNAL_LIST ...
+const char *dec_signal_string[];
+
+void mpd_qfinalize(mpd_t *result, const mpd_context_t *ctx, uint32_t *status);
+const char *mpd_class(const mpd_t *a, const mpd_context_t *ctx);
+
+/* format specification */
+typedef struct mpd_spec_t {
+    mpd_ssize_t min_width; /* minimum field width */
+    mpd_ssize_t prec;      /* fraction digits or significant digits */
+    char type;             /* conversion specifier */
+    char align;            /* alignment */
+    char sign;             /* sign printing/alignment */
+    char fill[5];          /* fill character */
+    const char *dot;       /* decimal point */
+    const char *sep;       /* thousands separator */
+    const char *grouping;  /* grouping of digits */
+} mpd_spec_t;
+
+char *mpd_to_sci(const mpd_t *dec, int fmt);
+char *mpd_to_eng(const mpd_t *dec, int fmt);
+int mpd_parse_fmt_str(mpd_spec_t *spec, const char *fmt, int caps);
+int mpd_validate_lconv(mpd_spec_t *spec);
+char *mpd_qformat_spec(const mpd_t *dec, const mpd_spec_t *spec, const mpd_context_t *ctx, uint32_t *status);
+
+""")
+
+import os
+
+_libdir = os.path.join(os.path.dirname(__file__), '_libmpdec')
+_mpdec = _ffi.verify(
+    """
+#include "mpdecimal.h"
+
+const char *dec_signal_string[MPD_NUM_FLAGS] = {
+    "Clamped",
+    "InvalidOperation",
+    "DivisionByZero",
+    "InvalidOperation",
+    "InvalidOperation",
+    "InvalidOperation",
+    "Inexact",
+    "InvalidOperation",
+    "InvalidOperation",
+    "InvalidOperation",
+    "FloatOperation",
+    "Overflow",
+    "Rounded",
+    "Subnormal",
+    "Underflow",
+};
+""",
+    sources=[os.path.join(_libdir, 'mpdecimal.c'),
+             os.path.join(_libdir, 'basearith.c'),
+             os.path.join(_libdir, 'convolute.c'),
+             os.path.join(_libdir, 'constants.c'),
+             os.path.join(_libdir, 'context.c'),
+             os.path.join(_libdir, 'io.c'),
+             os.path.join(_libdir, 'fourstep.c'),
+             os.path.join(_libdir, 'sixstep.c'),
+             os.path.join(_libdir, 'transpose.c'),
+             os.path.join(_libdir, 'difradix2.c'),
+             os.path.join(_libdir, 'numbertheory.c'),
+             os.path.join(_libdir, 'fnt.c'),
+             os.path.join(_libdir, 'crt.c'),
+             os.path.join(_libdir, 'memory.c'),
+         ],
+    include_dirs=[_libdir],
+    extra_compile_args=[
+        "-DANSI",
+        "-DHAVE_STDINT_H",
+        "-DHAVE_INTTYPES_H",
+        "-DCONFIG_64" if _sys.maxsize > 1 << 32 else "-DCONFIG_32",
+    ],
+)
+
+del os
+
+_mpdec.MPD_Float_operation = _mpdec.MPD_Not_implemented
+
+__version__ = "1.70"
+__libmpdec_version__ = _ffi.string(_mpdec.mpd_version())
+
+# Default context
+
+import threading
+local = threading.local()
+
+def getcontext(*, _local=local):
+    """Returns this thread's context.
+    
+    If this thread does not yet have a context, returns
+    a new context and sets this thread's context.
+    New contexts are copies of DefaultContext.
+    """
+    try:
+        return _local.__decimal_context__
+    except AttributeError:
+        context = Context()
+        _local.__decimal_context__ = context
+        return context
+
+def _getcontext(context=None):
+    if context is None:
+        return getcontext()
+    if not isinstance(context, Context):
+        raise TypeError
+    return context
+
+def setcontext(context, *, _local=local):
+    """Set this thread's context to context."""
+    if context in (DefaultContext, BasicContext, ExtendedContext):
+        context = context.copy()
+        context.clear_flags()
+    if not isinstance(context, Context):
+        raise TypeError
+    _local.__decimal_context__ = context
+
+
+del local, threading
+
+def localcontext(ctx=None):
+    """Return a context manager for a copy of the supplied context.
+    """
+    return _ContextManager(_getcontext(ctx))
+
+
+from collections import namedtuple as _namedtuple
+DecimalTuple = _namedtuple('DecimalTuple', 'sign digits exponent')
+
+
+# A codecs error handler to handle unicode digits
+import codecs as _codecs
+import unicodedata as _unicodedata
+def _handle_decimaldigits(exc):
+    res = ""
+    for c in exc.object[exc.start:exc.end]:
+        if c.isspace():
+            res += ' '
+        else:
+            res += str(_unicodedata.digit(c))
+    return res, exc.end
+_codecs.register_error('_decimal_encode', _handle_decimaldigits)
+
+
+# Decimal class
+
+_DEC_MINALLOC = 4
+
+class Decimal(object):
+    __module__ = 'decimal'
+
+    __slots__ = ('_mpd', '_data')
+
+    def __new__(cls, value="0", context=None):
+        return cls._from_object(value, context, exact=True)
+
+    @classmethod
+    def _new_empty(cls):
+        self = object.__new__(cls)
+        self._mpd = mpd = _ffi.new("struct mpd_t*")
+        self._data = _ffi.new("mpd_uint_t[]", _DEC_MINALLOC)
+        mpd.flags = _mpdec.MPD_STATIC | _mpdec.MPD_STATIC_DATA
+        mpd.alloc = _DEC_MINALLOC
+        mpd.exp = 0
+        mpd.digits = 0
+        mpd.len = 0
+        mpd.data = self._data
+        return self
+
+    def __del__(self):
+        _mpdec.mpd_del(self._mpd)
+
+    @classmethod
+    def _from_object(cls, value, context, exact=True):
+        if isinstance(value, Decimal):
+            return cls._from_decimal(value, context, exact=exact)
+        if isinstance(value, str):
+            return cls._from_str(value, context, exact=exact, strip=exact)
+        if isinstance(value, int):
+            return cls._from_int(value, context, exact=exact)
+        if isinstance(value, (list, tuple)):
+            return cls._from_tuple(value, context, exact=exact)
+        if isinstance(value, float):
+            context = _getcontext(context)
+            context._add_status(_mpdec.MPD_Float_operation)
+            return cls._from_float(value, context, exact=exact)
+        raise TypeError("conversion from %s to Decimal is not supported" %
+                        value.__class__.__name__)
+
+    @classmethod
+    def _from_decimal(cls, value, context, exact=True):
+        if exact:
+            if cls is Decimal and type(value) is Decimal:
+                return value
+            self = cls._new_empty()
+            with _CatchConversions(self._mpd, context, exact) as (
+                    ctx, status_ptr):
+                _mpdec.mpd_qcopy(self._mpd, value._mpd, status_ptr)
+            return self
+        else:
+            if (_mpdec.mpd_isnan(value._mpd) and
+                value._mpd.digits > (context._ctx.prec - context._ctx.clamp)):
+                # Special case: too many NaN payload digits
+                context._add_status(_mpdec.MPD_Conversion_syntax)
+                self = cls._new_empty()
+                _mpdec.mpd_setspecial(self._mpd, _mpdec.MPD_POS, _mpdec.MPD_NAN)
+                return self
+            else:
+                self = cls._new_empty()
+                with _CatchStatus(context) as (ctx, status_ptr):
+                    _mpdec.mpd_qcopy(self._mpd, value._mpd, status_ptr)
+                    _mpdec.mpd_qfinalize(self._mpd, ctx, status_ptr)
+                return self
+
+    @classmethod
+    def _from_str(cls, value, context, exact=True, strip=True):
+        s = value.encode('ascii', '_decimal_encode')
+        if b'\0' in s:
+            s = b''  # empty string triggers ConversionSyntax.
+        if strip:
+            s = s.strip()
+        return cls._from_bytes(s, context, exact=exact)
+
+    @classmethod
+    def _from_bytes(cls, value, context, exact=True):
+        self = cls._new_empty()
+        with _CatchConversions(self._mpd, context, exact) as (ctx, status_ptr):
+            _mpdec.mpd_qset_string(self._mpd, value, ctx, status_ptr)
+        return self
+
+    @classmethod
+    def _from_int(cls, value, context, exact=True):
+        self = cls._new_empty()
+        with _CatchConversions(self._mpd, context, exact) as (ctx, status_ptr):
+            size = (((value|1).bit_length() + 15) // 16) + 5
+            if value < 0:
+                value = -value
+                sign = _mpdec.MPD_NEG
+            else:
+                sign = _mpdec.MPD_POS
+            array = value.to_bytes(2*size, byteorder='little', signed=False)
+            digits = _ffi.new("uint8_t[]", array)
+            _mpdec.mpd_qimport_u16(
+                self._mpd, _ffi.cast("uint16_t*", digits),
+                size, sign, 0x10000, ctx, status_ptr)
+        return self
+
+    @classmethod
+    def _from_tuple(cls, value, context, exact=True):
+        sign, digits, exponent  = value
+
+        # Make a bytes string representation of a DecimalTuple
+        builder = []
+
+        # sign
+        if not isinstance(sign, int) or sign not in (0, 1):
+            raise ValueError("sign must be an integer with the value 0 or 1")
+        builder.append(b'-' if sign else b'+')
+
+        # exponent or encoding for a special number
+        is_infinite = False
+        is_special = False
+        if isinstance(exponent, str):
+            # special
+            is_special = True
+            if exponent == 'F':
+                builder.append(b'Inf')
+                is_infinite = True
+            elif exponent == 'n':
+                builder.append(b'Nan')
+            elif exponent == 'N':
+                builder.append(b'sNan')
+            else:
+                raise ValueError("string argument in the third position "
+                                 "must be 'F', 'n' or 'N'")
+            exponent = 0
+        else:
+            if not isinstance(exponent, int):
+                raise ValueError("exponent must be an integer")
+            if not -_sys.maxsize-1 <= exponent <= _sys.maxsize:
+                # Compatibility with CPython
+                raise OverflowError()
+
+        # coefficients
+        if not digits and not is_special:
+            # empty tuple: zero coefficient, except for special numbers
+            builder.append(b'0')
+        for digit in digits:
+            if not isinstance(digit, int) or not 0 <= digit <= 9:
+                raise ValueError("coefficient must be a tuple of digits")
+            if is_infinite:
+                # accept but ignore any well-formed coefficient for
+                # compatibility with decimal.py
+                continue
+            builder.append(bytes([ord('0') + digit]))
+
+        if not is_special:
+            builder.append(b'E')
+            builder.append(str(exponent).encode())
+
+        return cls._from_bytes(b''.join(builder), context, exact=exact) 
+
+    @classmethod
+    def from_float(cls, value):
+        return cls._from_float(value, getcontext(), exact=True)
+
+    @classmethod
+    def _from_float(cls, value, context, exact=True):
+        if isinstance(value, int):
+            return cls._from_int(value, context, exact=exact)
+        sign = 0 if _math.copysign(1.0, value) == 1.0 else 1
+
+        if _math.isnan(value):
+            self = cls._new_empty()
+            # decimal.py calls repr(float(+-nan)), which always gives a
+            # positive result.
+            _mpdec.mpd_setspecial(self._mpd, _mpdec.MPD_POS, _mpdec.MPD_NAN)
+            return self
+        if _math.isinf(value):
+            self = cls._new_empty()
+            _mpdec.mpd_setspecial(self._mpd, sign, _mpdec.MPD_INF)
+            return self
+
+        # float as integer ratio: numerator/denominator
+        num, den = abs(value).as_integer_ratio()
+        k = den.bit_length() - 1
+
+        self = cls._from_int(num, context, exact=True)
+
+        # Compute num * 5**k
+        d1 = _mpdec.mpd_qnew()
+        if not d1:
+            raise MemoryError()
+        try:
+            d2 = _mpdec.mpd_qnew()
+            if not d2:
+                raise MemoryError()
+            try:
+                with _CatchConversions(self._mpd, context, exact=True) as (
+                        ctx, status_ptr):
+                    _mpdec.mpd_qset_uint(d1, 5, ctx, status_ptr)
+                    _mpdec.mpd_qset_ssize(d2, k, ctx, status_ptr)
+                    _mpdec.mpd_qpow(d1, d1, d2, ctx, status_ptr)
+            finally:
+                _mpdec.mpd_del(d2)
+            with _CatchConversions(self._mpd, context, exact=True) as (
+                    ctx, status_ptr):
+                _mpdec.mpd_qmul(self._mpd, self._mpd, d1, ctx, status_ptr)
+        finally:
+            _mpdec.mpd_del(d1)
+
+        # result = +- n * 5**k * 10**-k
+        _mpdec.mpd_set_sign(self._mpd, sign)
+        self._mpd.exp = - k
+
+        if not exact:
+            with _CatchStatus(context) as (ctx, status_ptr):
+                _mpdec.mpd_qfinalize(self._mpd, ctx, status_ptr)
+        return self
+
+    def __str__(self):
+        return getcontext().to_sci_string(self)
+
+    def __repr__(self):
+        context = getcontext()
+        output = _mpdec.mpd_to_sci(self._mpd, context._capitals)
+        if not output:
+            raise MemoryError
+        try:
+            result = _ffi.string(output)
+        finally:
+            _mpdec.mpd_free(output)
+        return "Decimal('%s')" % result.decode()
+
+    def as_tuple(self):
+        "Return the DecimalTuple representation of a Decimal"
+        mpd = self._mpd
+        sign = _mpdec.mpd_sign(mpd)
+        if _mpdec.mpd_isinfinite(mpd):
+            expt = "F"
+            # decimal.py has non-compliant infinity payloads.
+            coeff = (0,)
+        else:
+            if _mpdec.mpd_isnan(mpd):
+                if _mpdec.mpd_issnan(mpd):
+                    expt = "N"
+                else:
+                    expt = "n"
+            else:
+                expt = mpd.exp
+
+            if mpd.len > 0:
+                # coefficient is defined
+
+                # make an integer
+                # XXX this should be done in C...
+                x = _mpdec.mpd_qncopy(mpd)
+                if not x:
+                    raise MemoryError
+                try:
+                    x.exp = 0
+                    # clear NaN and sign
+                    _mpdec.mpd_clear_flags(x)
+                    intstring = _mpdec.mpd_to_sci(x, 1)
+                finally:
+                    _mpdec.mpd_del(x)
+                if not intstring:
+                    raise MemoryError
+                try:
+                    digits = _ffi.string(intstring)
+                finally:
+                    _mpdec.mpd_free(intstring)
+                coeff = tuple(d - ord('0') for d in digits)
+            else:
+                coeff = ()
+
+        return DecimalTuple(sign, coeff, expt)
+
+    def _convert_for_comparison(self, other, op):
+        if isinstance(other, Decimal):
+            return self, other
+
+        context = getcontext()
+        if isinstance(other, int):
+            other = Decimal._from_int(other, context)
+        elif isinstance(other, float):
+            if op not in ('eq', 'ne'):
+                # Add status, and maybe raise
+                context._add_status(_mpdec.MPD_Float_operation)
+            else:
+                # Add status, but don't raise
+                context._ctx.status |= _mpdec.MPD_Float_operation
+            other = Decimal._from_float(other, context)
+        elif isinstance(other, complex):
+            if op not in ('eq', 'ne'):
+                return NotImplemented, NotImplemented
+            if other.imag != 0.0:
+                return NotImplemented, NotImplemented
+            # Add status, but don't raise
+            context._ctx.status |= _mpdec.MPD_Float_operation
+            other = Decimal._from_float(other.real, context)
+        elif isinstance(other, _numbers.Rational):
+            numerator = Decimal._from_int(other.numerator, context)
+            if not _mpdec.mpd_isspecial(self._mpd):
+                # multiplied = self * other.denominator
+                # 
+                # Prevent Overflow in the following multiplication.
+                # The result of the multiplication is
+                # only used in mpd_qcmp, which can handle values that
+                # are technically out of bounds, like (for 32-bit)
+                # 99999999999999999999...99999999e+425000000.
+                vv = _mpdec.mpd_qncopy(self._mpd)
+                if not vv:
+                    raise MemoryError
+                try:
+                    exp = vv.exp
+                    vv.exp = 0
+                    multiplied = Decimal._new_empty()
+                    denom = Decimal(other.denominator)
+                    with _CatchStatus(context) as (ctx, status_ptr):
+                        _mpdec.mpd_qmul(multiplied._mpd, vv, denom._mpd,
+                                        ctx, status_ptr)
+                    multiplied._mpd.exp = exp
+                finally:
+                    _mpdec.mpd_del(vv)
+
+                return multiplied, numerator
+            else:
+                return self, numerator
+        else:
+            return NotImplemented, NotImplemented
+        return self, other
+
+    # _PyHASH_10INV is the inverse of 10 modulo the prime _PyHASH_MODULUS
+    _PyHASH_MODULUS = _sys.hash_info.modulus
+    _PyHASH_10INV = pow(10, _PyHASH_MODULUS - 2, _PyHASH_MODULUS)
+
+    def __bool__(self):
+        return not _mpdec.mpd_iszero(self._mpd)
+
+    def __hash__(self):
+        # In order to make sure that the hash of a Decimal instance
+        # agrees with the hash of a numerically equal integer, float
+        # or Fraction, we follow the rules for numeric hashes outlined
+        # in the documentation.  (See library docs, 'Built-in Types').
+        mpd = self._mpd
+        if _mpdec.mpd_isspecial(mpd):
+            if _mpdec.mpd_issnan(mpd):
+                raise TypeError("cannot hash a signaling NaN value")
+            elif _mpdec.mpd_isnan(mpd):
+                return _sys.hash_info.nan
+            elif _mpdec.mpd_isnegative(mpd):
+                return -_sys.hash_info.inf
+            else:
+                return _sys.hash_info.inf
+
+        maxctx = _ffi.new("struct mpd_context_t*")
+        _mpdec.mpd_maxcontext(maxctx)
+        status_ptr = _ffi.new("uint32_t*")
+
+        # XXX cache these
+        p = self._new_empty()
+        _mpdec.mpd_qset_ssize(p._mpd, self._PyHASH_MODULUS,
+                              maxctx, status_ptr)
+        ten = self._new_empty()
+        _mpdec.mpd_qset_ssize(ten._mpd, 10, 
+                              maxctx, status_ptr)
+        inv10_p = self._new_empty()
+        _mpdec.mpd_qset_ssize(inv10_p._mpd, self._PyHASH_10INV,
+                              maxctx, status_ptr)
+
+        tmp = self._new_empty()
+        exp_hash = self._new_empty()
+
+        if mpd.exp >= 0:
+            # 10**exp(v) % p
+            _mpdec.mpd_qsset_ssize(tmp._mpd, mpd.exp, maxctx, status_ptr)
+            _mpdec.mpd_qpowmod(exp_hash._mpd, ten._mpd, tmp._mpd, p._mpd,
+                               maxctx, status_ptr)
+        else:
+            # inv10_p**(-exp(v)) % p
+            _mpdec.mpd_qsset_ssize(tmp._mpd, -mpd.exp, maxctx, status_ptr)
+            _mpdec.mpd_qpowmod(exp_hash._mpd, inv10_p._mpd, tmp._mpd, p._mpd,
+                               maxctx, status_ptr)
+
+        # hash = (int(v) * exp_hash) % p
+        if not _mpdec.mpd_qcopy(tmp._mpd, mpd, status_ptr):
+            raise MemoryError
+
+        tmp._mpd.exp = 0
+        _mpdec.mpd_set_positive(tmp._mpd)
+
+        maxctx.prec = MAX_PREC + 21
+        maxctx.emax = MAX_EMAX + 21
+        maxctx.emin = MIN_EMIN - 21
+
+        _mpdec.mpd_qmul(tmp._mpd, tmp._mpd, exp_hash._mpd, maxctx, status_ptr)
+        _mpdec.mpd_qrem(tmp._mpd, tmp._mpd, p._mpd, maxctx, status_ptr)
+
+        result = _mpdec.mpd_qget_ssize(tmp._mpd, status_ptr)
+        result = result if _mpdec.mpd_ispositive(mpd) else -result
+        result = result if result != -1 else -2
+
+        if status_ptr[0]:
+            if status_ptr[0] & _mpdec.MPD_Malloc_error:
+                raise MemoryError
+            else:
+                raise SystemError("Decimal.__hash__")
+
+        return result
+
+    def _cmp(self, other, op):
+        a, b = self._convert_for_comparison(other, op)
+        if a is NotImplemented:
+            return NotImplemented
+        status_ptr = _ffi.new("uint32_t*")
+        r = _mpdec.mpd_qcmp(a._mpd, b._mpd, status_ptr)
+        if r > 1:  # INT_MAX
+            # sNaNs or op={le,ge,lt,gt} always signal
+            if (_mpdec.mpd_issnan(a._mpd) or
+                _mpdec.mpd_issnan(b._mpd) or
+                op not in ('eq', 'ne')):
+                getcontext()._add_status(status_ptr[0])
+            # qNaN comparison with op={eq,ne} or comparison with
+            # InvalidOperation disabled.
+            # Arrange to return False.
+            if op in ('gt', 'ge'):
+                return -1
+            else:
+                return 1
+        return r
+
+    def __eq__(self, other):
+        r = self._cmp(other, 'eq')
+        if r is NotImplemented:
+            return NotImplemented
+        return r == 0
+
+    def __ne__(self, other):
+        r = self._cmp(other, 'ne')
+        if r is NotImplemented:
+            return NotImplemented
+        return r != 0
+
+    def __lt__(self, other):
+        r = self._cmp(other, 'lt')
+        if r is NotImplemented:
+            return NotImplemented
+        return r < 0
+
+    def __le__(self, other):
+        r = self._cmp(other, 'le')
+        if r is NotImplemented:
+            return NotImplemented
+        return r <= 0
+
+    def __gt__(self, other):
+        r = self._cmp(other, 'gt')
+        if r is NotImplemented:
+            return NotImplemented
+        return r > 0
+
+    def __ge__(self, other):
+        r = self._cmp(other, 'ge')
+        if r is NotImplemented:
+            return NotImplemented
+        return r >= 0
+
+    # operations
+    def _make_unary_operation(name, ctxop_name=None):
+        ctxop_name = ctxop_name or name
+        if name.startswith('__'):
+            def method(self):
+                return getattr(getcontext(), ctxop_name)(self)
+        else:
+            # Allow optional context
+            def method(self, context=None):
+                context = _getcontext(context)
+                return getattr(context, ctxop_name)(self)
+        method.__name__ = name
+        return method
+
+    def _make_unary_operation_noctx(name, ctxop_name=None):
+        ctxop_name = ctxop_name or name
+        def method(self):
+            return getattr(getcontext(), ctxop_name)(self)
+        method.__name__ = name
+        return method
+
+    def _make_binary_operation(name, ctxop_name=None):
+        ctxop_name = ctxop_name or name
+        if name.startswith('__'):
+            def method(self, other):
+                return getattr(getcontext(), ctxop_name)(
+                    self, other, strict=False)
+        else:
+            def method(self, other, context=None):
+                context = _getcontext(context)
+                return getattr(context, ctxop_name)(
+                    self, other)
+        method.__name__ = name
+        return method
+
+    def _make_binary_roperation(name, ctxop_name):
+        def method(self, other):
+            return getattr(getcontext(), ctxop_name)(other, self, strict=False)
+        method.__name__ = name
+        return method
+
+    __abs__ = _make_unary_operation('__abs__', 'abs')
+    __pos__ = _make_unary_operation('__pos__', 'plus')
+    __neg__ = _make_unary_operation('__neg__', 'minus')
+
+    __add__ = _make_binary_operation('__add__', 'add')
+    __sub__ = _make_binary_operation('__sub__', 'subtract')
+    __mul__ = _make_binary_operation('__mul__', 'multiply')
+    __floordiv__ = _make_binary_operation('__floordiv__', 'divide_int')
+    __truediv__ = _make_binary_operation('__truediv__', 'divide')
+    __mod__ = _make_binary_operation('__mod__', 'remainder')
+    __divmod__ = _make_binary_operation('__divmod__', 'divmod')
+
+    __radd__ = _make_binary_roperation('__radd__', 'add')
+    __rsub__ = _make_binary_roperation('__rsub__', 'subtract')
+    __rmul__ = _make_binary_roperation('__rmul__', 'multiply')
+    __rfloordiv__ = _make_binary_roperation('__rfloordiv__', 'divide_int')
+    __rtruediv__ = _make_binary_roperation('__rtruediv__', 'divide')
+    __rmod__ = _make_binary_roperation('__rmod__', 'remainder')
+    __rdivmod__ = _make_binary_roperation('__rdivmod__', 'divmod')
+
+    def __pow__(self, other, modulo=None):
+        return getcontext().power(self, other, modulo, strict=False)
+    def __rpow__(self, other):
+        return getcontext().power(other, self, strict=False)
+
+    copy_sign = _make_binary_operation('copy_sign')
+    copy_abs = _make_unary_operation_noctx('copy_abs')
+    copy_negate = _make_unary_operation_noctx('copy_negate')
+
+    sqrt = _make_unary_operation('sqrt')
+    exp = _make_unary_operation('exp')
+    ln = _make_unary_operation('ln')
+    log10 = _make_unary_operation('log10')
+    logb = _make_unary_operation('logb')
+    logical_invert = _make_unary_operation('logical_invert')
+    normalize = _make_unary_operation('normalize')
+
+    compare = _make_binary_operation('compare')
+    compare_signal = _make_binary_operation('compare_signal')
+    compare_total = _make_binary_operation('compare')
+    compare_total_mag = _make_binary_operation('compare')
+    logical_and = _make_binary_operation('logical_and')
+    logical_or = _make_binary_operation('logical_or')
+    logical_xor = _make_binary_operation('logical_xor')
+    max = _make_binary_operation('max')
+    max_mag = _make_binary_operation('max_mag')
+    min = _make_binary_operation('min')
+    min_mag = _make_binary_operation('min_mag')
+    next_minus = _make_unary_operation('next_minus')
+    next_plus = _make_unary_operation('next_plus')
+    next_toward = _make_binary_operation('next_toward')
+    remainder_near = _make_binary_operation('remainder_near')
+    rotate = _make_binary_operation('rotate')
+    same_quantum = _make_binary_operation('same_quantum')
+    scaleb = _make_binary_operation('scaleb')
+    shift = _make_binary_operation('shift')
+
+    is_normal = _make_unary_operation('is_normal')
+    is_subnormal = _make_unary_operation('is_subnormal')
+    is_signed = _make_unary_operation_noctx('is_signed')
+    is_zero = _make_unary_operation_noctx('is_zero')
+    is_nan = _make_unary_operation_noctx('is_nan')
+    is_snan = _make_unary_operation_noctx('is_snan')
+    is_qnan = _make_unary_operation_noctx('is_qnan')
+    is_finite = _make_unary_operation_noctx('is_finite')
+    is_infinite = _make_unary_operation_noctx('is_infinite')
+    number_class = _make_unary_operation('number_class')
+
+    to_eng_string = _make_unary_operation('to_eng_string')
+    
+    def fma(self, other, third, context=None):
+        context = _getcontext(context)
+        return context.fma(self, other, third)
+
+    def _to_int(self, rounding):
+        mpd = self._mpd
+        if _mpdec.mpd_isspecial(mpd):
+            if _mpdec.mpd_isnan(mpd):
+                raise ValueError("cannot convert NaN to integer")
+            else:
+                raise OverflowError("cannot convert Infinity to integer")
+
+        x = Decimal._new_empty()
+        context = getcontext()
+        tempctx = context.copy()
+        tempctx._ctx.round = rounding
+        with _CatchStatus(context) as (ctx, status_ptr):
+            # We round with the temporary context, but set status and
+            # raise errors on the global one.
+            _mpdec.mpd_qround_to_int(x._mpd, mpd, tempctx._ctx, status_ptr)
+
+            # XXX mpd_qexport_u64 would be faster...
+            digits_ptr = _ffi.new("uint16_t**")
+            n = _mpdec.mpd_qexport_u16(digits_ptr, 0, 0x10000,
+                                       x._mpd, status_ptr)
+            if n == _mpdec.MPD_SIZE_MAX:
+                raise MemoryError
+            try:
+                s = _ffi.buffer(digits_ptr[0], n * 2)[:]
+            finally:
+                _mpdec.mpd_free(digits_ptr[0])
+            result = int.from_bytes(s, 'little', signed=False)
+        if _mpdec.mpd_isnegative(x._mpd) and not _mpdec.mpd_iszero(x._mpd):
+            result = -result
+        return result        
+
+    def __int__(self):
+        return self._to_int(_mpdec.MPD_ROUND_DOWN)
+
+    __trunc__ = __int__
+
+    def __floor__(self):
+        return self._to_int(_mpdec.MPD_ROUND_FLOOR)        
+
+    def __ceil__(self):
+        return self._to_int(_mpdec.MPD_ROUND_CEILING)        
+
+    def to_integral(self, rounding=None, context=None):
+        context = _getcontext(context)
+        workctx = context.copy()
+        if rounding is not None:
+            workctx.rounding = rounding
+        result = Decimal._new_empty()
+        with _CatchStatus(context) as (ctx, status_ptr):
+            # We round with the temporary context, but set status and
+            # raise errors on the global one.
+            _mpdec.mpd_qround_to_int(result._mpd, self._mpd,
+                                     workctx._ctx, status_ptr)
+        return result
+
+    to_integral_value = to_integral
+        
+    def to_integral_exact(self, rounding=None, context=None):
+        context = _getcontext(context)
+        workctx = context.copy()
+        if rounding is not None:
+            workctx.rounding = rounding
+        result = Decimal._new_empty()
+        with _CatchStatus(context) as (ctx, status_ptr):
+            # We round with the temporary context, but set status and
+            # raise errors on the global one.
+            _mpdec.mpd_qround_to_intx(result._mpd, self._mpd,
+                                      workctx._ctx, status_ptr)
+        return result
+
+    def quantize(self, exp, rounding=None, context=None):
+        context = _getcontext(context)
+        exp = context._convert_unaryop(exp)
+        workctx = context.copy()
+        if rounding is not None:
+            workctx.rounding = rounding
+        result = Decimal._new_empty()
+        with _CatchStatus(context) as (ctx, status_ptr):
+            # We round with the temporary context, but set status and
+            # raise errors on the global one.
+            _mpdec.mpd_qquantize(result._mpd, self._mpd, exp._mpd,
+                                 workctx._ctx, status_ptr)
+        return result
+
+    def __round__(self, x=None):
+        if x is None:
+            return self._to_int(_mpdec.MPD_ROUND_HALF_EVEN)
+        result = Decimal._new_empty()
+        context = getcontext()
+        q = Decimal._from_int(1, context)
+        if x == _mpdec.MPD_SSIZE_MIN:
+            q._mpd.exp = _mpdec.MPD_SSIZE_MAX
+        elif x == -_mpdec.MPD_SSIZE_MIN:
+            raise OverflowError  # For compatibility with CPython.
+        else:
+            q._mpd.exp = -x
+        with _CatchStatus(context) as (ctx, status_ptr):
+            _mpdec.mpd_qquantize(result._mpd, self._mpd, q._mpd,
+                                 ctx, status_ptr)
+        return result
+
+    def __float__(self):
+        if _mpdec.mpd_isnan(self._mpd):
+            if _mpdec.mpd_issnan(self._mpd):
+                raise ValueError("cannot convert signaling NaN to float")
+            if _mpdec.mpd_isnegative(self._mpd):
+                return float("-nan")
+            else:
+                return float("nan")
+        else:
+            return float(str(self))
+
+    def radix(self):
+        return Decimal(10)
+
+    def canonical(self):
+        return self
+
+    def is_canonical(self):
+        return True
+
+    def adjusted(self):
+        if _mpdec.mpd_isspecial(self._mpd):
+            return 0
+        return _mpdec.mpd_adjexp(self._mpd)
+    
+    @property
+    def real(self):
+        return self
+
+    @property
+    def imag(self):
+        return Decimal(0)
+
+    def conjugate(self):
+        return self
+
+    def __complex__(self):
+        return complex(float(self))
+
+    def __copy__(self):
+        return self
+
+    def __deepcopy__(self, memo=None):
+        return self
+
+    def __reduce__(self):
+        return (type(self), (str(self),))
+
+    def __format__(self, specifier, override=None):
+        if not isinstance(specifier, str):
+            raise TypeError
+        fmt = specifier.encode('utf-8')
+        context = getcontext()
+
+        replace_fillchar = False 
+        if fmt and fmt[0] == 0:
+            # NUL fill character: must be replaced with a valid UTF-8 char
+            # before calling mpd_parse_fmt_str().
+            replace_fillchar = True
+            fmt = b'_' + fmt[1:]
+
+        spec = _ffi.new("mpd_spec_t*")
+        if not _mpdec.mpd_parse_fmt_str(spec, fmt, context._capitals):
+            raise ValueError("invalid format string")
+        if replace_fillchar:
+            # In order to avoid clobbering parts of UTF-8 thousands
+            # separators or decimal points when the substitution is
+            # reversed later, the actual placeholder must be an invalid
+            # UTF-8 byte.
+            spec.fill = b'\xff\x00'
+
+        if override:
+            # Values for decimal_point, thousands_sep and grouping can
+            # be explicitly specified in the override dict. These values
+            # take precedence over the values obtained from localeconv()
+            # in mpd_parse_fmt_str(). The feature is not documented and
+            # is only used in test_decimal.
+            try:
+                dot = _ffi.new("char[]", override['decimal_point'].encode())
+            except KeyError:
+                pass
+            else:
+                spec.dot = dot
+            try:
+                sep = _ffi.new("char[]", override['thousands_sep'].encode())
+            except KeyError:
+                pass
+            else:
+                spec.sep = sep
+            try:
+                grouping = _ffi.new("char[]", override['grouping'].encode())
+            except KeyError:
+                pass
+            else:
+                spec.grouping = grouping
+            if _mpdec.mpd_validate_lconv(spec) < 0:
+                raise ValueError("invalid override dict")
+
+        with _CatchStatus(context) as (ctx, status_ptr):
+            decstring = _mpdec.mpd_qformat_spec(
+                self._mpd, spec, ctx, status_ptr)
+            status = status_ptr[0]
+        if not decstring:
+            if status & _mpdec.MPD_Malloc_error:
+                raise MemoryError
+            else:
+                raise ValueError("format specification exceeds "
+                                 "internal limits of _decimal")
+        result = _ffi.string(decstring)
+        if replace_fillchar:
+            result = result.replace(b'\xff', b'\0')
+        return result.decode('utf-8')
+
+        
+# Register Decimal as a kind of Number (an abstract base class).
+# However, do not register it as Real (because Decimals are not
+# interoperable with floats).
+_numbers.Number.register(Decimal)
+
+# Context class
+
+_DEC_DFLT_EMAX = 999999
+_DEC_DFLT_EMIN = -999999
+
+# Rounding
+_ROUNDINGS = {
+    'ROUND_DOWN': _mpdec.MPD_ROUND_DOWN, 
+    'ROUND_HALF_UP': _mpdec.MPD_ROUND_HALF_UP,
+    'ROUND_HALF_EVEN': _mpdec.MPD_ROUND_HALF_EVEN,
+    'ROUND_CEILING': _mpdec.MPD_ROUND_CEILING,
+    'ROUND_FLOOR': _mpdec.MPD_ROUND_FLOOR,
+    'ROUND_UP': _mpdec.MPD_ROUND_UP,
+    'ROUND_HALF_DOWN': _mpdec.MPD_ROUND_HALF_DOWN,
+    'ROUND_05UP': _mpdec.MPD_ROUND_05UP,
+}
+for _rounding in _ROUNDINGS:
+    globals()[_rounding] = _rounding
+
+_SIGNALS = {
+    InvalidOperation: _mpdec.MPD_IEEE_Invalid_operation,
+    FloatOperation: _mpdec.MPD_Float_operation,
+    DivisionByZero: _mpdec.MPD_Division_by_zero ,
+    Overflow: _mpdec.MPD_Overflow ,
+    Underflow: _mpdec.MPD_Underflow ,
+    Subnormal: _mpdec.MPD_Subnormal ,
+    Inexact: _mpdec.MPD_Inexact ,
+    Rounded: _mpdec.MPD_Rounded,
+    Clamped: _mpdec.MPD_Clamped,
+}
+
+class _ContextManager(object):
+    """Context manager class to support localcontext().
+
+      Sets a copy of the supplied context in __enter__() and restores
+      the previous decimal context in __exit__()
+    """
+    def __init__(self, new_context):
+        self.new_context = new_context.copy()
+    def __enter__(self):
+        self.saved_context = getcontext()
+        setcontext(self.new_context)
+        return self.new_context
+    def __exit__(self, t, v, tb):
+        setcontext(self.saved_context)
+
+
+class Context(object):
+    """Contains the context for a Decimal instance.
+
+    Contains:
+    prec - precision (for use in rounding, division, square roots..)
+    rounding - rounding type (how you round)
+    traps - If traps[exception] = 1, then the exception is
+                    raised when it is caused.  Otherwise, a value is
+                    substituted in.
+    flags  - When an exception is caused, flags[exception] is set.
+             (Whether or not the trap_enabler is set)
+             Should be reset by user of Decimal instance.
+    Emin -   Minimum exponent
+    Emax -   Maximum exponent
+    capitals -      If 1, 1*10^1 is printed as 1E+1.
+                    If 0, printed as 1e1
+    clamp -  If 1, change exponents if too high (Default 0)
+    """
+
+    __module__ = 'decimal'
+
+    __slots__ = ('_ctx', '_capitals')
+
+    def __new__(cls, *args, **kwargs):
+        self = object.__new__(cls)
+        self._ctx = ctx = _ffi.new("struct mpd_context_t*")
+        # Default context
+        ctx.prec = 28
+        ctx.emax = _DEC_DFLT_EMAX
+        ctx.emin = _DEC_DFLT_EMIN
+        ctx.traps = (_mpdec.MPD_IEEE_Invalid_operation|
+                     _mpdec.MPD_Division_by_zero|
+                     _mpdec.MPD_Overflow)
+        ctx.status = 0
+        ctx.newtrap = 0
+        ctx.round = _mpdec.MPD_ROUND_HALF_EVEN
+        ctx.clamp = 0
+        ctx.allcr = 1
+        
+        self._capitals = 1
+        return self
+
+    def __init__(self, prec=None, rounding=None, Emin=None, Emax=None,
+                 capitals=None, clamp=None, flags=None, traps=None):
+        ctx = self._ctx
+
+        try:
+            dc = DefaultContext._ctx
+        except NameError:
+            pass
+        else:
+            ctx[0] = dc[0]
+        if prec is not None:
+            self.prec = prec
+        if rounding is not None:
+            self.rounding = rounding
+        if Emin is not None:
+            self.Emin = Emin
+        if Emax is not None:
+            self.Emax = Emax
+        if clamp is not None:
+            self.clamp = clamp
+        if capitals is not None:
+            self.capitals = capitals
+
+        if traps is None:
+            ctx.traps = dc.traps
+        elif not isinstance(traps, dict):
+            ctx.traps = 0
+            for signal in traps:
+                ctx.traps |= _SIGNALS[signal]
+        else:
+            ctx.traps = 0
+            for signal, value in traps.items():
+                if value:
+                    ctx.traps |= _SIGNALS[signal]
+
+        if flags is None:
+            ctx.status = 0
+        elif not isinstance(flags, dict):
+            ctx.status = 0
+            for signal in flags:
+                ctx.status |= _SIGNALS[signal]
+        else:
+            for signal, value in flags.items():
+                if value:
+                    ctx.status |= _SIGNALS[signal]
+
+    def clear_flags(self):
+        self._ctx.status = 0
+
+    def clear_traps(self):
+        self._ctx.traps = 0
+
+    @property
+    def prec(self):
+        return self._ctx.prec
+    @prec.setter
+    def prec(self, value):
+        if not _mpdec.mpd_qsetprec(self._ctx, value):
+            raise ValueError("valid range for prec is [1, MAX_PREC]")
+
+    @property
+    def clamp(self):
+        return self._ctx.clamp
+    @clamp.setter
+    def clamp(self, value):
+        if not _mpdec.mpd_qsetclamp(self._ctx, value):
+            raise ValueError("valid values for clamp are 0 or 1")
+
+    @property
+    def rounding(self):
+        return next(name
+                    for (name, value) in _ROUNDINGS.items()
+                    if value==self._ctx.round)
+    @rounding.setter
+    def rounding(self, value):
+        if value not in _ROUNDINGS:
+            raise TypeError(
+                "valid values for rounding are:\n"
+                "[ROUND_CEILING, ROUND_FLOOR, ROUND_UP, ROUND_DOWN,\n"
+                "ROUND_HALF_UP, ROUND_HALF_DOWN, ROUND_HALF_EVEN,\n"
+                "ROUND_05UP]")
+        if not _mpdec.mpd_qsetround(self._ctx, _ROUNDINGS[value]):
+            raise RuntimeError("internal error while setting rounding")
+
+    @property
+    def Emin(self):
+        return self._ctx.emin
+    @Emin.setter
+    def Emin(self, value):
+        if not _mpdec.mpd_qsetemin(self._ctx, value):
+            raise ValueError("valid range for Emin is [MIN_EMIN, 0]")
+
+    @property
+    def Emax(self):
+        return self._ctx.emax
+    @Emax.setter
+    def Emax(self, value):
+        if not _mpdec.mpd_qsetemax(self._ctx, value):
+            raise ValueError("valid range for Emax is [0, MAX_EMAX]")
+
+    @property
+    def flags(self):
+        return _SignalDict(self._ctx, 'status')
+    @flags.setter
+    def flags(self, value):
+        if not isinstance(value, _collections.abc.Mapping):
+            raise TypeError
+        if len(value) != len(_SIGNALS):
+            raise KeyError("Invalid signal dict")
+        for signal, value in value.items():
+            if value:
+                self._ctx.status |= _SIGNALS[signal]
+
+    @property
+    def traps(self):
+        return _SignalDict(self._ctx, 'traps')
+    @traps.setter
+    def traps(self, value):
+        if not isinstance(value, _collections.abc.Mapping):
+            raise TypeError
+        if len(value) != len(_SIGNALS):
+            raise KeyError("Invalid signal dict")
+        for signal, value in value.items():
+            if value:
+                self._ctx.traps |= _SIGNALS[signal]
+
+    @property
+    def capitals(self):
+        return self._capitals
+    @capitals.setter
+    def capitals(self, value):
+        if not isinstance(value, int):
+            raise TypeError
+        if value not in (0, 1):
+            raise ValueError("valid values for capitals are 0 or 1")
+        self._capitals = value
+
+    def __repr__(self):
+        ctx = self._ctx
+        return ("Context(prec=%s, rounding=%s, Emin=%s, Emax=%s, "
+                "capitals=%s, clamp=%s, flags=%s, traps=%s)" % (
+                    ctx.prec, self.rounding,
+                    ctx.emin, ctx.emax,
+                    self._capitals, ctx.clamp,
+                    self.flags, self.traps))
+
+    def radix(self):
+        return Decimal(10)
+
+    def Etiny(self):
+        return _mpdec.mpd_etiny(self._ctx)
+
+    def Etop(self):
+        return _mpdec.mpd_etop(self._ctx)
+
+    def is_canonical(self, a):
+        if not isinstance(a, Decimal):
+            raise TypeError("is_canonical requires a Decimal as an argument.")
+        return a.is_canonical()
+
+    def canonical(self, a):
+        if not isinstance(a, Decimal):
+            raise TypeError("argument must be a Decimal")
+        return a
+
+    def copy(self):
+        other = Context()
+        other._ctx[0] = self._ctx[0]
+        other._capitals = self._capitals
+        return other
+
+    def __copy__(self):
+        return self.copy()
+
+    def __reduce__(self):
+        return (type(self), (
+            self.prec, self.rounding, self.Emin, self.Emax,
+            self._capitals, self.clamp,
+            self.flags._as_list(),
+            self.traps._as_list()))
+
+    def _add_status(self, status):
+        self._ctx.status |= status
+        if self._ctx.status & _mpdec.MPD_Malloc_error:
+            raise MemoryError()
+        trapped = self._ctx.traps & status
+        if trapped:
+            for exception, flag in _SIGNALS.items():
+                if trapped & flag:
+                    raise exception
+            raise RuntimeError("Invalid error flag", trapped)
+
+    def create_decimal(self, num="0"):
+        return Decimal._from_object(num, self, exact=False)
+
+    def create_decimal_from_float(self, value):
+        return Decimal._from_float(value, self, exact=False)
+
+    # operations
+    def _convert_unaryop(self, a, strict=True):
+        if isinstance(a, Decimal):
+            return a
+        elif isinstance(a, int):
+            return Decimal._from_int(a, self)
+        if strict:
+            raise TypeError("Unable to convert %s to Decimal" % (a,))
+        else:
+            return NotImplemented
+
+    def _convert_binop(self, a, b, strict=True):
+        a = self._convert_unaryop(a, strict=strict)
+        b = self._convert_unaryop(b, strict=strict)
+        if b is NotImplemented:
+            return b, b
+        return a, b
+        
+    def _make_unary_method(name, mpd_func_name):
+        mpd_func = getattr(_mpdec, mpd_func_name)
+
+        def method(self, a, *, strict=True):
+            a = self._convert_unaryop(a, strict=strict)
+            if a is NotImplemented:
+                return NotImplemented
+            res = Decimal._new_empty()
+            with _CatchStatus(self) as (ctx, status_ptr):
+                mpd_func(res._mpd, a._mpd, ctx, status_ptr)
+            return res
+        method.__name__ = name
+        return method
+
+    def _make_unary_method_noctx(name, mpd_func_name):
+        mpd_func = getattr(_mpdec, mpd_func_name)
+
+        def method(self, a, *, strict=True):
+            a = self._convert_unaryop(a, strict=strict)
+            if a is NotImplemented:
+                return NotImplemented
+            res = Decimal._new_empty()
+            with _CatchStatus(self) as (ctx, status_ptr):
+                mpd_func(res._mpd, a._mpd, status_ptr)
+            return res
+        method.__name__ = name
+        return method
+
+    def _make_bool_method(name, mpd_func_name):
+        mpd_func = getattr(_mpdec, mpd_func_name)
+
+        def method(self, a):
+            a = self._convert_unaryop(a)
+            return bool(mpd_func(a._mpd, self._ctx))
+        method.__name__ = name
+        return method
+
+    def _make_bool_method_noctx(name, mpd_func_name):
+        mpd_func = getattr(_mpdec, mpd_func_name)
+
+        def method(self, a):
+            a = self._convert_unaryop(a)
+            return bool(mpd_func(a._mpd))
+        method.__name__ = name
+        return method
+
+    def _make_binary_method(name, mpd_func_name):
+        mpd_func = getattr(_mpdec, mpd_func_name)
+
+        def method(self, a, b, *, strict=True):
+            a, b = self._convert_binop(a, b, strict=strict)
+            if a is NotImplemented:
+                return NotImplemented
+            res = Decimal._new_empty()
+            with _CatchStatus(self) as (ctx, status_ptr):
+                mpd_func(res._mpd, a._mpd, b._mpd, ctx, status_ptr)
+            return res
+        method.__name__ = name
+        return method
+
+    def _make_binary_bool_method(name, mpd_func_name):
+        mpd_func = getattr(_mpdec, mpd_func_name)
+
+        def method(self, a, b):
+            a, b = self._convert_binop(a, b)
+            return bool(mpd_func(a._mpd, b._mpd))
+        method.__name__ = name
+        return method
+
+    def _make_binary_method_noctx(name, mpd_func_name):
+        mpd_func = getattr(_mpdec, mpd_func_name)
+
+        def method(self, a, b, *, strict=True):
+            a, b = self._convert_binop(a, b, strict=strict)
+            if a is NotImplemented:
+                return NotImplemented
+            res = Decimal._new_empty()
+            with _CatchStatus(self) as (ctx, status_ptr):
+                mpd_func(res._mpd, a._mpd, b._mpd, status_ptr)
+            return res
+        method.__name__ = name
+        return method
+
+    def _make_binary_method_nostatus(name, mpd_func_name):
+        mpd_func = getattr(_mpdec, mpd_func_name)
+
+        def method(self, a, b, *, strict=True):
+            a, b = self._convert_binop(a, b, strict=strict)
+            if a is NotImplemented:
+                return NotImplemented
+            res = Decimal._new_empty()
+            mpd_func(res._mpd, a._mpd, b._mpd)
+            return res
+        method.__name__ = name
+        return method
+
+    abs = _make_unary_method('abs', 'mpd_qabs')
+    plus = _make_unary_method('plus', 'mpd_qplus')
+    minus = _make_unary_method('minus', 'mpd_qminus')
+    sqrt = _make_unary_method('sqrt', 'mpd_qsqrt')
+    exp = _make_unary_method('exp', 'mpd_qexp')
+    ln = _make_unary_method('ln', 'mpd_qln')
+    log10 = _make_unary_method('log10', 'mpd_qlog10')
+    logb = _make_unary_method('logb', 'mpd_qlogb')
+    logical_invert = _make_unary_method('logical_invert', 'mpd_qinvert')
+    normalize = _make_unary_method('normalize', 'mpd_qreduce')
+
+    add = _make_binary_method('add', 'mpd_qadd')
+    subtract = _make_binary_method('add', 'mpd_qsub')
+    multiply = _make_binary_method('multiply', 'mpd_qmul')
+    divide = _make_binary_method('divide', 'mpd_qdiv')
+    divide_int = _make_binary_method('divide_int', 'mpd_qdivint')
+    remainder = _make_binary_method('remainder', 'mpd_qrem')
+    remainder_near = _make_binary_method('remainder_near', 'mpd_qrem_near')
+    copy_sign = _make_binary_method_noctx('copy_sign', 'mpd_qcopy_sign')
+    copy_abs = _make_unary_method_noctx('copy_abs', 'mpd_qcopy_abs')
+    copy_negate = _make_unary_method_noctx('copy_negate', 'mpd_qcopy_negate')
+
+    compare = _make_binary_method('compare', 'mpd_qcompare')
+    compare_signal = _make_binary_method('compare_signal',
+                                         'mpd_qcompare_signal')
+    compare_total = _make_binary_method_nostatus('compare_total',
+                                                 'mpd_compare_total')
+    compare_total_mag = _make_binary_method_nostatus('compare_total_mag',
+                                                     'mpd_compare_total_mag')
+    logical_and = _make_binary_method('logical_and', 'mpd_qand')
+    logical_or = _make_binary_method('logical_or', 'mpd_qor')
+    logical_xor = _make_binary_method('logical_xor', 'mpd_qxor')
+    max = _make_binary_method('max', 'mpd_qmax')
+    max_mag = _make_binary_method('max_mag', 'mpd_qmax_mag')
+    min = _make_binary_method('min', 'mpd_qmin')
+    min_mag = _make_binary_method('min_mag', 'mpd_qmin_mag')
+    next_minus = _make_unary_method('next_minus', 'mpd_qnext_minus')
+    next_plus = _make_unary_method('next_plus', 'mpd_qnext_plus')
+    next_toward = _make_binary_method('next_toward', 'mpd_qnext_toward')
+    rotate = _make_binary_method('rotate', 'mpd_qrotate')
+    same_quantum = _make_binary_bool_method('same_quantum', 'mpd_same_quantum')
+    scaleb = _make_binary_method('scaleb', 'mpd_qscaleb')
+    shift = _make_binary_method('shift', 'mpd_qshift')
+    quantize = _make_binary_method('quantize', 'mpd_qquantize')
+
+    is_normal = _make_bool_method('is_normal', 'mpd_isnormal')
+    is_signed = _make_bool_method_noctx('is_signed', 'mpd_issigned')
+    is_zero = _make_bool_method_noctx('is_signed', 'mpd_iszero')
+    is_subnormal = _make_bool_method('is_subnormal', 'mpd_issubnormal')
+    is_nan = _make_bool_method_noctx('is_qnan', 'mpd_isnan')
+    is_snan = _make_bool_method_noctx('is_qnan', 'mpd_issnan')
+    is_qnan = _make_bool_method_noctx('is_qnan', 'mpd_isqnan')
+    is_finite = _make_bool_method_noctx('is_finite', 'mpd_isfinite')
+    is_infinite = _make_bool_method_noctx('is_infinite', 'mpd_isinfinite')
+
+    def _apply(self, a):
+        # Apply the context to the input operand.
+        a = self._convert_unaryop(a)
+        result = Decimal._new_empty()
+        with _CatchStatus(self) as (ctx, status_ptr):
+            _mpdec.mpd_qcopy(result._mpd, a._mpd, status_ptr)
+            _mpdec.mpd_qfinalize(result._mpd, ctx, status_ptr)
+        return result
+
+    def divmod(self, a, b, strict=True):
+        a, b = self._convert_binop(a, b, strict=strict)
+        if a is NotImplemented:
+            return NotImplemented
+        q = Decimal._new_empty()
+        r = Decimal._new_empty()
+        with _CatchStatus(self) as (ctx, status_ptr):
+            _mpdec.mpd_qdivmod(q._mpd, r._mpd, a._mpd, b._mpd,
+                               ctx, status_ptr)
+        return q, r
+
+    def power(self, a, b, modulo=None, strict=True):
+        a, b = self._convert_binop(a, b, strict=strict)
+        if a is NotImplemented:
+            return NotImplemented
+        if modulo is not None:
+            modulo = self._convert_unaryop(modulo, strict=strict)
+            if modulo is NotImplemented:
+                return NotImplemented
+        res = Decimal._new_empty()
+        with _CatchStatus(self) as (ctx, status_ptr):
+            if modulo is not None:
+                _mpdec.mpd_qpowmod(res._mpd, a._mpd, b._mpd, modulo._mpd,
+                                   ctx, status_ptr)
+            else:
+                _mpdec.mpd_qpow(res._mpd, a._mpd, b._mpd,
+                                ctx, status_ptr)
+        return res
+
+    to_integral = _make_unary_method('to_integral', 'mpd_qround_to_int')
+    to_integral_value = to_integral
+    to_integral_exact = _make_unary_method('to_integral_exact',
+                                           'mpd_qround_to_intx')
+
+    def fma(self, a, b, c):
+        a = self._convert_unaryop(a)
+        b = self._convert_unaryop(b)
+        c = self._convert_unaryop(c)
+        res = Decimal._new_empty()
+        with _CatchStatus(self) as (ctx, status_ptr):
+            _mpdec.mpd_qfma(res._mpd, a._mpd, b._mpd, c._mpd,
+                            ctx, status_ptr)
+        return res
+
+    def copy_decimal(self, a):
+        return self._convert_unaryop(a)
+
+    def number_class(self, a):
+        a = self._convert_unaryop(a)
+        cp = _mpdec.mpd_class(a._mpd, self._ctx)
+        return _ffi.string(cp).decode()
+
+    def to_eng_string(self, a):
+        a = self._convert_unaryop(a)
+        output = _mpdec.mpd_to_eng(a._mpd, self._capitals)
+        if not output:
+            raise MemoryError
+        try:
+            result = _ffi.string(output)
+        finally:
+            _mpdec.mpd_free(output)
+        return result.decode()
+
+    def to_sci_string(self, a):
+        a = self._convert_unaryop(a)
+        output = _mpdec.mpd_to_sci(a._mpd, self._capitals)
+        if not output:
+            raise MemoryError
+        try:
+            result = _ffi.string(output)
+        finally:
+            _mpdec.mpd_free(output)
+        return result.decode()
+
+
+class _SignalDict(_collections.abc.MutableMapping):
+
+    def __init__(self, ctx, attrname):
+        self.ctx = ctx
+        self.attrname = attrname
+
+    def __repr__(self):
+        value = getattr(self.ctx, self.attrname)
+        buf = _ffi.new("char[]", _mpdec.MPD_MAX_SIGNAL_LIST)
+        n = _mpdec.mpd_lsnprint_signals(buf, len(buf), value,
+                                        _mpdec.dec_signal_string)
+        if not 0 <= n < len(buf):
+            raise SystemError("flags repr")
+        return _ffi.buffer(buf, n)[:].decode()
+
+    def _as_list(self):
+        value = getattr(self.ctx, self.attrname)
+        names = []
+        for name, flag in _SIGNALS.items():
+            if value & flag:
+                names.append(name)
+        return names
+
+    def _as_dict(self):
+        value = getattr(self.ctx, self.attrname)
+        return {name: bool(value & flag)
+                for (name, flag) in _SIGNALS.items()}
+
+    def copy(self):
+        return self._as_dict()
+        
+    def __len__(self):
+        return len(_SIGNALS)
+
+    def __iter__(self):
+        return iter(_SIGNALS)
+
+    def __getitem__(self, key):
+        return bool(getattr(self.ctx, self.attrname) & _SIGNALS[key])
+
+    def __setitem__(self, key, value):
+        if value:
+            setattr(self.ctx, self.attrname,
+                    getattr(self.ctx, self.attrname) | _SIGNALS[key])
+        else:
+            setattr(self.ctx, self.attrname,
+                    getattr(self.ctx, self.attrname) & ~_SIGNALS[key])
+
+    def __delitem__(self, key):
+        raise ValueError("signal keys cannot be deleted")
+
+
+class _CatchConversions:
+    def __init__(self, mpd, context, exact):
+        self.mpd = mpd
+        self.context = _getcontext(context)
+        self.exact = exact
+
+    def __enter__(self):
+        if self.exact:
+            self.ctx = _ffi.new("struct mpd_context_t*")
+            _mpdec.mpd_maxcontext(self.ctx)
+        else:
+            self.ctx = self.context._ctx
+        self.status_ptr = _ffi.new("uint32_t*")
+        return self.ctx, self.status_ptr
+
+    def __exit__(self, *args):
+        if self.exact:
+            # we want exact results
+            status = self.status_ptr[0]
+            if status & (_mpdec.MPD_Inexact |
+                         _mpdec.MPD_Rounded |
+                         _mpdec.MPD_Clamped):
+                _mpdec.mpd_seterror(
+                    self.mpd, _mpdec.MPD_Invalid_operation, self.status_ptr)
+        status = self.status_ptr[0]
+        if self.exact:
+            status &= _mpdec.MPD_Errors
+        # May raise a DecimalException
+        self.context._add_status(status)
+
+class _CatchStatus:
+    def __init__(self, context):
+        self.context = context
+
+    def __enter__(self):
+        self.status_ptr = _ffi.new("uint32_t*")
+        return self.context._ctx, self.status_ptr
+        
+    def __exit__(self, *args):
+        status = self.status_ptr[0]
+        # May raise a DecimalException
+        self.context._add_status(status)
+
+##### Setup Specific Contexts ############################################
+
+# The default context prototype used by Context()
+# Is mutable, so that new contexts can have different default values
+
+DefaultContext = Context(
+    prec=28, rounding=ROUND_HALF_EVEN,
+    traps=[DivisionByZero, Overflow, InvalidOperation],
+    flags=[],
+    Emax=999999,
+    Emin=-999999,
+    capitals=1,
+    clamp=0
+)
+
+# Pre-made alternate contexts offered by the specification
+# Don't change these; the user should be able to select these
+# contexts and be able to reproduce results from other implementations
+# of the spec.
+
+BasicContext = Context(
+    prec=9, rounding=ROUND_HALF_UP,
+    traps=[DivisionByZero, Overflow, InvalidOperation, Clamped, Underflow],
+    flags=[],
+)
+
+ExtendedContext = Context(
+    prec=9, rounding=ROUND_HALF_EVEN,
+    traps=[],
+    flags=[],
+)
diff --git a/lib_pypy/_libmpdec/basearith.c b/lib_pypy/_libmpdec/basearith.c
new file mode 100644
--- /dev/null
+++ b/lib_pypy/_libmpdec/basearith.c
@@ -0,0 +1,658 @@
+/*
+ * Copyright (c) 2008-2016 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright


More information about the pypy-commit mailing list