Issue #7652: Integrate the decimal floating point libmpdec library to speed
up the decimal module. Performance gains of the new C implementation are
between 12x and 80x, depending on the application.
diff --git a/Lib/decimal.py b/Lib/decimal.py
index e946182..b6f66ab 100644
--- a/Lib/decimal.py
+++ b/Lib/decimal.py
@@ -46,8 +46,8 @@
 Decimal('-0.0123')
 >>> Decimal(123456)
 Decimal('123456')
->>> Decimal('123.45e12345678901234567890')
-Decimal('1.2345E+12345678901234567892')
+>>> Decimal('123.45e12345678')
+Decimal('1.2345E+12345680')
 >>> Decimal('1.33') + Decimal('1.27')
 Decimal('2.60')
 >>> Decimal('12.34') + Decimal('3.87') - Decimal('18.41')
@@ -122,13 +122,20 @@
     # Exceptions
     'DecimalException', 'Clamped', 'InvalidOperation', 'DivisionByZero',
     'Inexact', 'Rounded', 'Subnormal', 'Overflow', 'Underflow',
+    'FloatOperation',
 
     # Constants for use in setting up contexts
     'ROUND_DOWN', 'ROUND_HALF_UP', 'ROUND_HALF_EVEN', 'ROUND_CEILING',
     'ROUND_FLOOR', 'ROUND_UP', 'ROUND_HALF_DOWN', 'ROUND_05UP',
 
     # Functions for manipulating contexts
-    'setcontext', 'getcontext', 'localcontext'
+    'setcontext', 'getcontext', 'localcontext',
+
+    # Limits for the C version for compatibility
+    'MAX_PREC',  'MAX_EMAX', 'MIN_EMIN', 'MIN_ETINY',
+
+    # C version: compile time choice that enables the thread local context
+    'HAVE_THREADS'
 ]
 
 __version__ = '1.70'    # Highest version of the spec this complies with
@@ -137,6 +144,7 @@
 import copy as _copy
 import math as _math
 import numbers as _numbers
+import sys
 
 try:
     from collections import namedtuple as _namedtuple
@@ -154,6 +162,19 @@
 ROUND_HALF_DOWN = 'ROUND_HALF_DOWN'
 ROUND_05UP = 'ROUND_05UP'
 
+# 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):
@@ -370,9 +391,24 @@
     In all cases, Inexact, Rounded, and Subnormal will also be raised.
     """
 
+class FloatOperation(DecimalException):
+    """Enable stricter semantics for mixing floats and Decimals.
+
+    If the signal is not trapped (default), mixing floats and Decimals is
+    permitted in the Decimal() constructor, context.create_decimal() and
+    all comparison operators. Both conversion and comparisons are exact.
+    Any occurrence of a mixed operation is silently recorded by setting
+    FloatOperation in the context flags.  Explicit conversions with
+    Decimal.from_float() or context.create_decimal_from_float() do not
+    set the flag.
+
+    Otherwise (the signal is trapped), only equality comparisons and explicit
+    conversions are silent. All other mixed operations raise FloatOperation.
+    """
+
 # List of public traps and flags
 _signals = [Clamped, DivisionByZero, Inexact, Overflow, Rounded,
-           Underflow, InvalidOperation, Subnormal]
+            Underflow, InvalidOperation, Subnormal, FloatOperation]
 
 # Map conditions (per the spec) to signals
 _condition_map = {ConversionSyntax:InvalidOperation,
@@ -380,6 +416,10 @@
                   DivisionUndefined:InvalidOperation,
                   InvalidContext:InvalidOperation}
 
+# Valid rounding modes
+_rounding_modes = (ROUND_DOWN, ROUND_HALF_UP, ROUND_HALF_EVEN, ROUND_CEILING,
+                   ROUND_FLOOR, ROUND_UP, ROUND_HALF_DOWN, ROUND_05UP)
+
 ##### Context Functions ##################################################
 
 # The getcontext() and setcontext() function manage access to a thread-local
@@ -392,12 +432,11 @@
     import threading
 except ImportError:
     # Python was compiled without threads; create a mock object instead
-    import sys
     class MockThreading(object):
         def local(self, sys=sys):
             return sys.modules[__name__]
     threading = MockThreading()
-    del sys, MockThreading
+    del MockThreading
 
 try:
     threading.local
@@ -650,6 +689,11 @@
             return self
 
         if isinstance(value, float):
+            if context is None:
+                context = getcontext()
+            context._raise_error(FloatOperation,
+                "strict semantics for mixing floats and Decimals are "
+                "enabled")
             value = Decimal.from_float(value)
             self._exp  = value._exp
             self._sign = value._sign
@@ -684,7 +728,9 @@
         """
         if isinstance(f, int):                # handle integer inputs
             return cls(f)
-        if _math.isinf(f) or _math.isnan(f):  # raises TypeError if not a float
+        if not isinstance(f, float):
+            raise TypeError("argument must be int or float.")
+        if _math.isinf(f) or _math.isnan(f):
             return cls(repr(f))
         if _math.copysign(1.0, f) == 1.0:
             sign = 0
@@ -1906,11 +1952,12 @@
     def _power_modulo(self, other, modulo, context=None):
         """Three argument version of __pow__"""
 
-        # if can't convert other and modulo to Decimal, raise
-        # TypeError; there's no point returning NotImplemented (no
-        # equivalent of __rpow__ for three argument pow)
-        other = _convert_other(other, raiseit=True)
-        modulo = _convert_other(modulo, raiseit=True)
+        other = _convert_other(other)
+        if other is NotImplemented:
+            return other
+        modulo = _convert_other(modulo)
+        if modulo is NotImplemented:
+            return modulo
 
         if context is None:
             context = getcontext()
@@ -3832,11 +3879,9 @@
     clamp -  If 1, change exponents if too high (Default 0)
     """
 
-    def __init__(self, prec=None, rounding=None,
-                 traps=None, flags=None,
-                 Emin=None, Emax=None,
-                 capitals=None, clamp=None,
-                 _ignored_flags=None):
+    def __init__(self, prec=None, rounding=None, Emin=None, Emax=None,
+                       capitals=None, clamp=None, flags=None, traps=None,
+                       _ignored_flags=None):
         # Set defaults; for everything except flags and _ignored_flags,
         # inherit from DefaultContext.
         try:
@@ -3859,17 +3904,78 @@
         if traps is None:
             self.traps = dc.traps.copy()
         elif not isinstance(traps, dict):
-            self.traps = dict((s, int(s in traps)) for s in _signals)
+            self.traps = dict((s, int(s in traps)) for s in _signals + traps)
         else:
             self.traps = traps
 
         if flags is None:
             self.flags = dict.fromkeys(_signals, 0)
         elif not isinstance(flags, dict):
-            self.flags = dict((s, int(s in flags)) for s in _signals)
+            self.flags = dict((s, int(s in flags)) for s in _signals + flags)
         else:
             self.flags = flags
 
+    def _set_integer_check(self, name, value, vmin, vmax):
+        if not isinstance(value, int):
+            raise TypeError("%s must be an integer" % name)
+        if vmin == '-inf':
+            if value > vmax:
+                raise ValueError("%s must be in [%s, %d]. got: %s" % (name, vmin, vmax, value))
+        elif vmax == 'inf':
+            if value < vmin:
+                raise ValueError("%s must be in [%d, %s]. got: %s" % (name, vmin, vmax, value))
+        else:
+            if value < vmin or value > vmax:
+                raise ValueError("%s must be in [%d, %d]. got %s" % (name, vmin, vmax, value))
+        return object.__setattr__(self, name, value)
+
+    def _set_signal_dict(self, name, d):
+        if not isinstance(d, dict):
+            raise TypeError("%s must be a signal dict" % d)
+        for key in d:
+            if not key in _signals:
+                raise KeyError("%s is not a valid signal dict" % d)
+        for key in _signals:
+            if not key in d:
+                raise KeyError("%s is not a valid signal dict" % d)
+        return object.__setattr__(self, name, d)
+
+    def __setattr__(self, name, value):
+        if name == 'prec':
+            return self._set_integer_check(name, value, 1, 'inf')
+        elif name == 'Emin':
+            return self._set_integer_check(name, value, '-inf', 0)
+        elif name == 'Emax':
+            return self._set_integer_check(name, value, 0, 'inf')
+        elif name == 'capitals':
+            return self._set_integer_check(name, value, 0, 1)
+        elif name == 'clamp':
+            return self._set_integer_check(name, value, 0, 1)
+        elif name == 'rounding':
+            if not value in _rounding_modes:
+                # raise TypeError even for strings to have consistency
+                # among various implementations.
+                raise TypeError("%s: invalid rounding mode" % value)
+            return object.__setattr__(self, name, value)
+        elif name == 'flags' or name == 'traps':
+            return self._set_signal_dict(name, value)
+        elif name == '_ignored_flags':
+            return object.__setattr__(self, name, value)
+        else:
+            raise AttributeError(
+                "'decimal.Context' object has no attribute '%s'" % name)
+
+    def __delattr__(self, name):
+        raise AttributeError("%s cannot be deleted" % name)
+
+    # Support for pickling, copy, and deepcopy
+    def __reduce__(self):
+        flags = [sig for sig, v in self.flags.items() if v]
+        traps = [sig for sig, v in self.traps.items() if v]
+        return (self.__class__,
+                (self.prec, self.rounding, self.Emin, self.Emax,
+                 self.capitals, self.clamp, flags, traps))
+
     def __repr__(self):
         """Show the current context."""
         s = []
@@ -3888,18 +3994,24 @@
         for flag in self.flags:
             self.flags[flag] = 0
 
+    def clear_traps(self):
+        """Reset all traps to zero"""
+        for flag in self.traps:
+            self.traps[flag] = 0
+
     def _shallow_copy(self):
         """Returns a shallow copy from self."""
-        nc = Context(self.prec, self.rounding, self.traps,
-                     self.flags, self.Emin, self.Emax,
-                     self.capitals, self.clamp, self._ignored_flags)
+        nc = Context(self.prec, self.rounding, self.Emin, self.Emax,
+                     self.capitals, self.clamp, self.flags, self.traps,
+                     self._ignored_flags)
         return nc
 
     def copy(self):
         """Returns a deep copy from self."""
-        nc = Context(self.prec, self.rounding, self.traps.copy(),
-                     self.flags.copy(), self.Emin, self.Emax,
-                     self.capitals, self.clamp, self._ignored_flags)
+        nc = Context(self.prec, self.rounding, self.Emin, self.Emax,
+                     self.capitals, self.clamp,
+                     self.flags.copy(), self.traps.copy(),
+                     self._ignored_flags)
         return nc
     __copy__ = copy
 
@@ -4062,6 +4174,8 @@
         >>> ExtendedContext.canonical(Decimal('2.50'))
         Decimal('2.50')
         """
+        if not isinstance(a, Decimal):
+            raise TypeError("canonical requires a Decimal as an argument.")
         return a.canonical(context=self)
 
     def compare(self, a, b):
@@ -4372,6 +4486,8 @@
         >>> ExtendedContext.is_canonical(Decimal('2.50'))
         True
         """
+        if not isinstance(a, Decimal):
+            raise TypeError("is_canonical requires a Decimal as an argument.")
         return a.is_canonical()
 
     def is_finite(self, a):
@@ -4964,7 +5080,7 @@
           +Normal
           +Infinity
 
-        >>> c = Context(ExtendedContext)
+        >>> c = ExtendedContext.copy()
         >>> c.Emin = -999
         >>> c.Emax = 999
         >>> c.number_class(Decimal('Infinity'))
@@ -5916,6 +6032,12 @@
     if equality_op and isinstance(other, _numbers.Complex) and other.imag == 0:
         other = other.real
     if isinstance(other, float):
+        context = getcontext()
+        if equality_op:
+            context.flags[FloatOperation] = 1
+        else:
+            context._raise_error(FloatOperation,
+                "strict semantics for mixing floats and Decimals are enabled")
         return self, Decimal.from_float(other)
     return NotImplemented, NotImplemented
 
@@ -5929,8 +6051,8 @@
         prec=28, rounding=ROUND_HALF_EVEN,
         traps=[DivisionByZero, Overflow, InvalidOperation],
         flags=[],
-        Emax=999999999,
-        Emin=-999999999,
+        Emax=999999,
+        Emin=-999999,
         capitals=1,
         clamp=0
 )
@@ -6080,7 +6202,7 @@
     # if format type is 'g' or 'G' then a precision of 0 makes little
     # sense; convert it to 1.  Same if format type is unspecified.
     if format_dict['precision'] == 0:
-        if format_dict['type'] is None or format_dict['type'] in 'gG':
+        if format_dict['type'] is None or format_dict['type'] in 'gGn':
             format_dict['precision'] = 1
 
     # determine thousands separator, grouping, and decimal separator, and
@@ -6254,16 +6376,26 @@
 
 # Constants related to the hash implementation;  hash(x) is based
 # on the reduction of x modulo _PyHASH_MODULUS
-import sys
 _PyHASH_MODULUS = sys.hash_info.modulus
 # hash values to use for positive and negative infinities, and nans
 _PyHASH_INF = sys.hash_info.inf
 _PyHASH_NAN = sys.hash_info.nan
-del sys
 
 # _PyHASH_10INV is the inverse of 10 modulo the prime _PyHASH_MODULUS
 _PyHASH_10INV = pow(10, _PyHASH_MODULUS - 2, _PyHASH_MODULUS)
+del sys
 
+try:
+    import _decimal
+except ImportError:
+    pass
+else:
+    s1 = set(dir())
+    s2 = set(dir(_decimal))
+    for name in s1 - s2:
+        del globals()[name]
+    del s1, s2, name
+    from _decimal import *
 
 if __name__ == '__main__':
     import doctest, decimal