aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmaury Forgeot d'Arc <amauryfa@gmail.com>2015-03-10 14:47:51 +0100
committerAmaury Forgeot d'Arc <amauryfa@gmail.com>2015-03-10 14:47:51 +0100
commitc7f9d4d1d25734a6bdb6087bedf0b011ed1b9361 (patch)
tree77a87096882376b1ee185d77a6833ea4a687160b /lib_pypy/_decimal.py
parentAdd a failing test that shows that interned strings are not always preserved. (diff)
downloadpypy-c7f9d4d1d25734a6bdb6087bedf0b011ed1b9361.tar.gz
pypy-c7f9d4d1d25734a6bdb6087bedf0b011ed1b9361.tar.bz2
pypy-c7f9d4d1d25734a6bdb6087bedf0b011ed1b9361.zip
Add a cffi implementation of the _decimal module.
Diffstat (limited to 'lib_pypy/_decimal.py')
-rw-r--r--lib_pypy/_decimal.py1900
1 files changed, 1900 insertions, 0 deletions
diff --git a/lib_pypy/_decimal.py b/lib_pypy/_decimal.py
new file mode 100644
index 0000000000..b5d389ecb5
--- /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=[],
+)