[Python-checkins] cpython: Issue #7652: Integrate the decimal floating point libmpdec library to speed

Andrew Svetlov andrew.svetlov at gmail.com
Wed Mar 21 18:49:18 CET 2012


Excellent!
On Wed, Mar 21, 2012 at 7:27 PM, stefan.krah <python-checkins at python.org> wrote:
> http://hg.python.org/cpython/rev/7355550d5357
> changeset:   75850:7355550d5357
> user:        Stefan Krah <skrah at bytereef.org>
> date:        Wed Mar 21 18:25:23 2012 +0100
> summary:
>  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.
>> files:
>  Doc/library/decimal.rst                                   |   178 +-
>  Doc/library/numeric.rst                                   |     6 +-
>  Doc/whatsnew/3.3.rst                                      |    87 +
>  Include/longintrepr.h                                     |     2 +-
>  Lib/decimal.py                                            |   196 +-
>  Lib/test/support.py                                       |     4 +-
>  Lib/test/test_decimal.py                                  |  4550 ++++-
>  Lib/test/test_fractions.py                                |    12 +-
>  Lib/test/test_numeric_tower.py                            |     2 +-
>  Misc/NEWS                                                 |     4 +
>  Misc/valgrind-python.supp                                 |    13 +
>  Modules/_decimal/ISSUES.txt                               |    56 +
>  Modules/_decimal/README.txt                               |    46 +
>  Modules/_decimal/_decimal.c                               |  5512 +++++++
>  Modules/_decimal/docstrings.h                             |   753 +
>  Modules/_decimal/libmpdec/README.txt                      |    90 +
>  Modules/_decimal/libmpdec/basearith.c                     |   635 +
>  Modules/_decimal/libmpdec/basearith.h                     |   213 +
>  Modules/_decimal/libmpdec/bits.h                          |   192 +
>  Modules/_decimal/libmpdec/constants.c                     |   132 +
>  Modules/_decimal/libmpdec/constants.h                     |    83 +
>  Modules/_decimal/libmpdec/context.c                       |   286 +
>  Modules/_decimal/libmpdec/convolute.c                     |   174 +
>  Modules/_decimal/libmpdec/convolute.h                     |    43 +
>  Modules/_decimal/libmpdec/crt.c                           |   179 +
>  Modules/_decimal/libmpdec/crt.h                           |    40 +
>  Modules/_decimal/libmpdec/difradix2.c                     |   173 +
>  Modules/_decimal/libmpdec/difradix2.h                     |    41 +
>  Modules/_decimal/libmpdec/fnt.c                           |    81 +
>  Modules/_decimal/libmpdec/fnt.h                           |    42 +
>  Modules/_decimal/libmpdec/fourstep.c                      |   255 +
>  Modules/_decimal/libmpdec/fourstep.h                      |    41 +
>  Modules/_decimal/libmpdec/io.c                            |  1575 ++
>  Modules/_decimal/libmpdec/io.h                            |    59 +
>  Modules/_decimal/libmpdec/literature/REFERENCES.txt       |    51 +
>  Modules/_decimal/libmpdec/literature/bignum.txt           |    83 +
>  Modules/_decimal/libmpdec/literature/fnt.py               |   208 +
>  Modules/_decimal/libmpdec/literature/matrix-transform.txt |   256 +
>  Modules/_decimal/libmpdec/literature/mulmod-64.txt        |   127 +
>  Modules/_decimal/libmpdec/literature/mulmod-ppro.txt      |   269 +
>  Modules/_decimal/libmpdec/literature/six-step.txt         |    63 +
>  Modules/_decimal/libmpdec/literature/umodarith.lisp       |   692 +
>  Modules/_decimal/libmpdec/memory.c                        |   292 +
>  Modules/_decimal/libmpdec/memory.h                        |    44 +
>  Modules/_decimal/libmpdec/mpdecimal.c                     |  7596 ++++++++++
>  Modules/_decimal/libmpdec/mpdecimal.h                     |   800 +
>  Modules/_decimal/libmpdec/numbertheory.c                  |   132 +
>  Modules/_decimal/libmpdec/numbertheory.h                  |    71 +
>  Modules/_decimal/libmpdec/sixstep.c                       |   212 +
>  Modules/_decimal/libmpdec/sixstep.h                       |    41 +
>  Modules/_decimal/libmpdec/transpose.c                     |   276 +
>  Modules/_decimal/libmpdec/transpose.h                     |    55 +
>  Modules/_decimal/libmpdec/typearith.h                     |   669 +
>  Modules/_decimal/libmpdec/umodarith.h                     |   650 +
>  Modules/_decimal/libmpdec/vccompat.h                      |    62 +
>  Modules/_decimal/libmpdec/vcdiv64.asm                     |    48 +
>  Modules/_decimal/libmpdec/vcstdint.h                      |   232 +
>  Modules/_decimal/tests/README.txt                         |    15 +
>  Modules/_decimal/tests/bench.py                           |   116 +
>  Modules/_decimal/tests/deccheck.py                        |  1074 +
>  Modules/_decimal/tests/formathelper.py                    |   344 +
>  Modules/_decimal/tests/randdec.py                         |   559 +
>  Modules/_decimal/tests/randfloat.py                       |   250 +
>  Modules/_decimal/tests/runall-memorydebugger.sh           |   175 +
>  Modules/_decimal/tests/runall.bat                         |   121 +
>  PCbuild/_decimal.vcproj                                   |   743 +
>  PCbuild/pcbuild.sln                                       |    21 +
>  configure                                                 |   171 +
>  configure.ac                                              |   100 +
>  pyconfig.h.in                                             |    13 +
>  setup.py                                                  |   113 +
>  71 files changed, 31440 insertions(+), 1059 deletions(-)
>>> diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst
> --- a/Doc/library/decimal.rst
> +++ b/Doc/library/decimal.rst
> @@ -9,6 +9,7 @@
>  .. moduleauthor:: Raymond Hettinger <python at rcn.com>
>  .. moduleauthor:: Aahz <aahz at pobox.com>
>  .. moduleauthor:: Tim Peters <tim.one at comcast.net>
> +.. moduleauthor:: Stefan Krah <skrah at bytereef.org>
>  .. sectionauthor:: Raymond D. Hettinger <python at rcn.com>
>>  .. import modules for testing inline doctests with the Sphinx doctest builder
> @@ -20,8 +21,9 @@
>    # make sure each group gets a fresh context
>    setcontext(Context())
>> -The :mod:`decimal` module provides support for decimal floating point
> -arithmetic.  It offers several advantages over the :class:`float` datatype:
> +The :mod:`decimal` module provides support for fast correctly-rounded
> +decimal floating point arithmetic. It offers several advantages over the
> +:class:`float` datatype:
>>  * Decimal "is based on a floating-point model which was designed with people
>   in mind, and necessarily has a paramount guiding principle -- computers must
> @@ -92,7 +94,7 @@
>  considered as informational, or treated as exceptions. The signals in the
>  decimal module are: :const:`Clamped`, :const:`InvalidOperation`,
>  :const:`DivisionByZero`, :const:`Inexact`, :const:`Rounded`, :const:`Subnormal`,
> -:const:`Overflow`, and :const:`Underflow`.
> +:const:`Overflow`, :const:`Underflow` and :const:`FloatOperation`.
>>  For each signal there is a flag and a trap enabler.  When a signal is
>  encountered, its flag is set to one, then, if the trap enabler is
> @@ -122,7 +124,7 @@
>>    >>> from decimal import *
>    >>> getcontext()
> -   Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999,
> +   Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999,
>            capitals=1, clamp=0, flags=[], traps=[Overflow, DivisionByZero,
>            InvalidOperation])
>> @@ -132,7 +134,7 @@
>  Construction from an integer or a float performs an exact conversion of the
>  value of that integer or float.  Decimal numbers include special values such as
>  :const:`NaN` which stands for "Not a number", positive and negative
> -:const:`Infinity`, and :const:`-0`.
> +:const:`Infinity`, and :const:`-0`::
>>    >>> getcontext().prec = 28
>    >>> Decimal(10)
> @@ -152,6 +154,25 @@
>    >>> Decimal('-Infinity')
>    Decimal('-Infinity')
>> +If the :exc:`FloatOperation` signal is trapped, accidental mixing of
> +decimals and floats in constructors or ordering comparisons raises
> +an exception::
> +
> +   >>> c = getcontext()
> +   >>> c.traps[FloatOperation] = True
> +   >>> Decimal(3.14)
> +   Traceback (most recent call last):
> +   File "<stdin>", line 1, in <module>
> +   decimal.FloatOperation: [<class 'decimal.FloatOperation'>]
> +   >>> Decimal('3.5') < 3.7
> +   Traceback (most recent call last):
> +     File "<stdin>", line 1, in <module>
> +   decimal.FloatOperation: [<class 'decimal.FloatOperation'>]
> +   >>> Decimal('3.5') == 3.5
> +   True
> +
> +.. versionadded:: 3.3
> +
>  The significance of a new Decimal is determined solely by the number of digits
>  input.  Context precision and rounding only come into play during arithmetic
>  operations.
> @@ -169,6 +190,16 @@
>    >>> Decimal('3.1415926535') + Decimal('2.7182818285')
>    Decimal('5.85988')
>> +If the internal limits of the C version are exceeded, constructing
> +a decimal raises :class:`InvalidOperation`::
> +
> +   >>> Decimal("1e9999999999999999999")
> +   Traceback (most recent call last):
> +     File "<stdin>", line 1, in <module>
> +   decimal.InvalidOperation: [<class 'decimal.InvalidOperation'>]
> +
> +.. versionchanged:: 3.3
> +
>  Decimals interact well with much of the rest of Python.  Here is a small decimal
>  floating point flying circus:
>> @@ -244,7 +275,7 @@
>    Decimal('0.142857142857142857142857142857142857142857142857142857142857')
>>    >>> ExtendedContext
> -   Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999,
> +   Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999,
>            capitals=1, clamp=0, flags=[], traps=[])
>    >>> setcontext(ExtendedContext)
>    >>> Decimal(1) / Decimal(7)
> @@ -269,7 +300,7 @@
>    >>> Decimal(355) / Decimal(113)
>    Decimal('3.14159292')
>    >>> getcontext()
> -   Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999,
> +   Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999,
>            capitals=1, clamp=0, flags=[Inexact, Rounded], traps=[])
>>  The *flags* entry shows that the rational approximation to :const:`Pi` was
> @@ -358,6 +389,10 @@
>       The argument to the constructor is now permitted to be a :class:`float`
>       instance.
>> +   .. versionchanged:: 3.3
> +      :class:`float` arguments raise an exception if the :exc:`FloatOperation`
> +      trap is set. By default the trap is off.
> +
>    Decimal floating point objects share many properties with the other built-in
>    numeric types such as :class:`float` and :class:`int`.  All of the usual math
>    operations and special methods apply.  Likewise, decimal objects can be
> @@ -880,39 +915,33 @@
>    In single threaded environments, it is preferable to not use this context at
>    all.  Instead, simply create contexts explicitly as described below.
>> -   The default values are precision=28, rounding=ROUND_HALF_EVEN, and enabled traps
> -   for Overflow, InvalidOperation, and DivisionByZero.
> +   The default values are :attr:`prec`\ =\ :const:`28`,
> +   :attr:`rounding`\ =\ :const:`ROUND_HALF_EVEN`,
> +   and enabled traps for :class:`Overflow`, :class:`InvalidOperation`, and
> +   :class:`DivisionByZero`.
>>  In addition to the three supplied contexts, new contexts can be created with the
>  :class:`Context` constructor.
>>> -.. class:: Context(prec=None, rounding=None, traps=None, flags=None, Emin=None, Emax=None, capitals=None, clamp=None)
> +.. class:: Context(prec=None, rounding=None, Emin=None, Emax=None, capitals=None, clamp=None, flags=None, traps=None)
>>    Creates a new context.  If a field is not specified or is :const:`None`, the
>    default values are copied from the :const:`DefaultContext`.  If the *flags*
>    field is not specified or is :const:`None`, all flags are cleared.
>> -   The *prec* field is a positive integer that sets the precision for arithmetic
> -   operations in the context.
> -
> -   The *rounding* option is one of:
> -
> -   * :const:`ROUND_CEILING` (towards :const:`Infinity`),
> -   * :const:`ROUND_DOWN` (towards zero),
> -   * :const:`ROUND_FLOOR` (towards :const:`-Infinity`),
> -   * :const:`ROUND_HALF_DOWN` (to nearest with ties going towards zero),
> -   * :const:`ROUND_HALF_EVEN` (to nearest with ties going to nearest even integer),
> -   * :const:`ROUND_HALF_UP` (to nearest with ties going away from zero), or
> -   * :const:`ROUND_UP` (away from zero).
> -   * :const:`ROUND_05UP` (away from zero if last digit after rounding towards zero
> -     would have been 0 or 5; otherwise towards zero)
> +   *prec* is an integer in the range [:const:`1`, :const:`MAX_PREC`] that sets
> +   the precision for arithmetic operations in the context.
> +
> +   The *rounding* option is one of the constants listed in the section
> +   `Rounding Modes`_.
>>    The *traps* and *flags* fields list any signals to be set. Generally, new
>    contexts should only set traps and leave the flags clear.
>>    The *Emin* and *Emax* fields are integers specifying the outer limits allowable
> -   for exponents.
> +   for exponents. *Emin* must be in the range [:const:`MIN_EMIN`, :const:`0`],
> +   *Emax* in the range [:const:`0`, :const:`MAX_EMAX`].
>>    The *capitals* field is either :const:`0` or :const:`1` (the default). If set to
>    :const:`1`, exponents are printed with a capital :const:`E`; otherwise, a
> @@ -951,6 +980,12 @@
>>       Resets all of the flags to :const:`0`.
>> +   .. method:: clear_traps()
> +
> +      Resets all of the traps to :const:`0`.
> +
> +      .. versionadded:: 3.3
> +
>    .. method:: copy()
>>       Return a duplicate of the context.
> @@ -1250,8 +1285,13 @@
>       With two arguments, compute ``x**y``.  If ``x`` is negative then ``y``
>       must be integral.  The result will be inexact unless ``y`` is integral and
>       the result is finite and can be expressed exactly in 'precision' digits.
> -      The result should always be correctly rounded, using the rounding mode of
> -      the current thread's context.
> +      The rounding mode of the context is used. Results are always correctly-rounded
> +      in the Python version.
> +
> +      .. versionchanged:: 3.3
> +         The C module computes :meth:`power` in terms of the correctly-rounded
> +         :meth:`exp` and :meth:`ln` functions. The result is well-defined but
> +         only "almost always correctly-rounded".
>>       With three arguments, compute ``(x**y) % modulo``.  For the three argument
>       form, the following restrictions on the arguments hold:
> @@ -1339,6 +1379,66 @@
>>  .. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
>> +.. _decimal-rounding-modes:
> +
> +Constants
> +---------
> +
> +The constants in this section are only relevant for the C module. They
> +are also included in the pure Python version for compatibility.
> +
> ++--------------------+---------------------+------------------------------+
> +|                    |       32-bit        |            64-bit            |
> ++====================+=====================+==============================+
> +| .. data:: MAX_PREC | :const:`425000000`  | :const:`999999999999999999`  |
> ++--------------------+---------------------+------------------------------+
> +| .. data:: MAX_EMAX | :const:`425000000`  | :const:`999999999999999999`  |
> ++--------------------+---------------------+------------------------------+
> +| .. data:: MIN_EMIN | :const:`-425000000` | :const:`-999999999999999999` |
> ++--------------------+---------------------+------------------------------+
> +
> +.. data:: HAVE_THREADS
> +
> +   The default value is True. If Python is compiled without threads, the
> +   C version automatically disables the expensive thread local context
> +   machinery. In this case, the value is False.
> +
> +Rounding modes
> +--------------
> +
> +.. data:: ROUND_CEILING
> +
> +   Round towards :const:`Infinity`.
> +
> +.. data:: ROUND_DOWN
> +
> +   Round towards zero.
> +
> +.. data:: ROUND_FLOOR
> +
> +   Round towards :const:`-Infinity`.
> +
> +.. data:: ROUND_HALF_DOWN
> +
> +   Round to nearest with ties going towards zero.
> +
> +.. data:: ROUND_HALF_EVEN
> +
> +   Round to nearest with ties going to nearest even integer.
> +
> +.. data:: ROUND_HALF_UP
> +
> +   Round to nearest with ties going away from zero.
> +
> +.. data:: ROUND_UP
> +
> +   Round away from zero.
> +
> +.. data:: ROUND_05UP
> +
> +   Round away from zero if last digit after rounding towards zero would have
> +   been 0 or 5; otherwise round towards zero.
> +
>>  .. _decimal-signals:
>> @@ -1403,7 +1503,6 @@
>       Infinity / Infinity
>       x % 0
>       Infinity % x
> -      x._rescale( non-integer )
>       sqrt(-x) and x > 0
>       0 ** 0
>       x ** (non-integer)
> @@ -1446,6 +1545,23 @@
>    Occurs when a subnormal result is pushed to zero by rounding. :class:`Inexact`
>    and :class:`Subnormal` are also signaled.
>> +
> +.. class:: FloatOperation
> +
> +    Enable stricter semantics for mixing floats and Decimals.
> +
> +    If the signal is not trapped (default), mixing floats and Decimals is
> +    permitted in the :class:`~decimal.Decimal` constructor,
> +    :meth:`~decimal.Context.create_decimal` and all comparison operators.
> +    Both conversion and comparisons are exact. Any occurrence of a mixed
> +    operation is silently recorded by setting :exc:`FloatOperation` in the
> +    context flags. Explicit conversions with :meth:`~decimal.Decimal.from_float`
> +    or :meth:`~decimal.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 :exc:`FloatOperation`.
> +
> +
>  The following table summarizes the hierarchy of signals::
>>    exceptions.ArithmeticError(exceptions.Exception)
> @@ -1458,10 +1574,12 @@
>            InvalidOperation
>            Rounded
>            Subnormal
> +           FloatOperation
>>  .. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
>>> +
>  .. _decimal-notes:
>>  Floating Point Notes
> @@ -1571,7 +1689,7 @@
>  the following calculation returns a value equal to zero:
>>    >>> 1 / Decimal('Infinity')
> -   Decimal('0E-1000000026')
> +   Decimal('0E-1000026')
>>  .. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
>> @@ -1583,7 +1701,7 @@
>>  The :func:`getcontext` function accesses a different :class:`Context` object for
>  each thread.  Having separate thread contexts means that threads may make
> -changes (such as ``getcontext.prec=10``) without interfering with other threads.
> +changes (such as ``getcontext().prec=10``) without interfering with other threads.
>>  Likewise, the :func:`setcontext` function automatically assigns its target to
>  the current thread.
> diff --git a/Doc/library/numeric.rst b/Doc/library/numeric.rst
> --- a/Doc/library/numeric.rst
> +++ b/Doc/library/numeric.rst
> @@ -8,9 +8,9 @@
>  The modules described in this chapter provide numeric and math-related functions
>  and data types. The :mod:`numbers` module defines an abstract hierarchy of
>  numeric types. The :mod:`math` and :mod:`cmath` modules contain various
> -mathematical functions for floating-point and complex numbers. For users more
> -interested in decimal accuracy than in speed, the :mod:`decimal` module supports
> -exact representations of decimal numbers.
> +mathematical functions for floating-point and complex numbers. The :mod:`decimal`
> +module supports exact representations of decimal numbers, using arbitrary precision
> +arithmetic.
>>  The following modules are documented in this chapter:
>> diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst
> --- a/Doc/whatsnew/3.3.rst
> +++ b/Doc/whatsnew/3.3.rst
> @@ -596,6 +596,93 @@
>>  (Contributed by Iñigo Serna in :issue:`6755`)
>> +decimal
> +-------
> +
> +:issue:`7652` - integrate fast native decimal arithmetic.
> +   C-module and libmpdec written by Stefan Krah.
> +
> +The new C version of the decimal module integrates the high speed libmpdec
> +library for arbitrary precision correctly-rounded decimal arithmetic.
> +libmpdec conforms to IBM's General Decimal Arithmetic Specification.
> +
> +Performance gains range from 12x for database applications to 80x for
> +numerically intensive applications:
> +
> +   +---------+-------------+--------------+-------------+
> +   |         |  decimal.py |   _decimal   |   speedup   |
> +   +=========+=============+==============+=============+
> +   |   pi    |    42.75s   |    0.58s     |     74x     |
> +   +---------+-------------+--------------+-------------+
> +   | telco   |   172.19s   |    5.68s     |     30x     |
> +   +---------+-------------+--------------+-------------+
> +   | psycopg |     3.57s   |    0.29s     |     12x     |
> +   +---------+-------------+--------------+-------------+
> +
> +Features
> +~~~~~~~~
> +
> +* The :exc:`~decimal.FloatOperation` signal optionally enables stricter
> +  semantics for mixing floats and Decimals.
> +
> +* If Python is compiled without threads, the C version automatically
> +  disables the expensive thread local context machinery. In this case,
> +  the variable :data:`~decimal.HAVE_THREADS` is set to False.
> +
> +API changes
> +~~~~~~~~~~~
> +
> +* The C module has the following context limits, depending on the machine
> +  architecture:
> +
> +   +-------------------+---------------------+------------------------------+
> +   |                   |       32-bit        |            64-bit            |
> +   +===================+=====================+==============================+
> +   | :const:`MAX_PREC` | :const:`425000000`  | :const:`999999999999999999`  |
> +   +-------------------+---------------------+------------------------------+
> +   | :const:`MAX_EMAX` | :const:`425000000`  | :const:`999999999999999999`  |
> +   +-------------------+---------------------+------------------------------+
> +   | :const:`MIN_EMIN` | :const:`-425000000` | :const:`-999999999999999999` |
> +   +-------------------+---------------------+------------------------------+
> +
> +* In the context templates (:class:`~decimal.DefaultContext`,
> +  :class:`~decimal.BasicContext` and :class:`~decimal.ExtendedContext`)
> +  the magnitude of :attr:`~decimal.Context.Emax` and
> +  :attr:`~decimal.Context.Emin` has changed to :const:`999999`.
> +
> +* The :class:`~decimal.Decimal` constructor in decimal.py does not observe
> +  the context limits and converts values with arbitrary exponents or precision
> +  exactly. Since the C version has internal limits, the following scheme is
> +  used: If possible, values are converted exactly, otherwise
> +  :exc:`~decimal.InvalidOperation` is raised and the result is NaN. In the
> +  latter case it is always possible to use :meth:`~decimal.Context.create_decimal`
> +  in order to obtain a rounded or inexact value.
> +
> +
> +* The power function in decimal.py is always correctly-rounded. In the
> +  C version, it is defined in terms of the correctly-rounded
> +  :meth:`~decimal.Decimal.exp` and :meth:`~decimal.Decimal.ln` functions,
> +  but the final result is only "almost always correctly rounded".
> +
> +
> +* In the C version, the context dictionary containing the signals is a
> +  :class:`~collections.abc.MutableMapping`.  For speed reasons,
> +  :attr:`~decimal.Context.flags` and :attr:`~decimal.Context.traps` always
> +  refer to the same :class:`~collections.abc.MutableMapping` that the context
> +  was initialized with. If a new signal dictionary is assigned,
> +  :attr:`~decimal.Context.flags` and :attr:`~decimal.Context.traps`
> +  are updated with the new values, but they do not reference the RHS
> +  dictionary.
> +
> +
> +* Pickling a :class:`~decimal.Context` produces a different output in order
> +  to have a common interchange format for the Python and C versions.
> +
> +
> +* The order of arguments in the :class:`~decimal.Context` constructor has been
> +  changed to match the order displayed by :func:`repr`.
> +
> +
>  faulthandler
>  ------------
>> diff --git a/Include/longintrepr.h b/Include/longintrepr.h
> --- a/Include/longintrepr.h
> +++ b/Include/longintrepr.h
> @@ -6,7 +6,7 @@
>  #endif
>>> -/* This is published for the benefit of "friend" marshal.c only. */
> +/* This is published for the benefit of "friends" marshal.c and _decimal.c. */
>>  /* Parameters of the long integer representation.  There are two different
>    sets of parameters: one set for 30-bit digits, stored in an unsigned 32-bit
> diff --git a/Lib/decimal.py b/Lib/decimal.py
> --- 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
> diff --git a/Lib/test/support.py b/Lib/test/support.py
> --- a/Lib/test/support.py
> +++ b/Lib/test/support.py
> @@ -1416,7 +1416,7 @@
>  #=======================================================================
>  # doctest driver.
>> -def run_doctest(module, verbosity=None):
> +def run_doctest(module, verbosity=None, optionflags=0):
>     """Run doctest on the given module.  Return (#failures, #tests).
>>     If optional argument verbosity is not specified (or is None), pass
> @@ -1431,7 +1431,7 @@
>     else:
>         verbosity = None
>> -    f, t = doctest.testmod(module, verbose=verbosity)
> +    f, t = doctest.testmod(module, verbose=verbosity, optionflags=optionflags)
>     if f:
>         raise TestFailed("%d of %d doctests failed" % (f, t))
>     if verbose:
> diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py
> --- a/Lib/test/test_decimal.py
> +++ b/Lib/test/test_decimal.py
> @@ -16,7 +16,7 @@
>>  Cowlishaw's tests can be downloaded from:
>> -   www2.hursley.ibm.com/decimal/dectest.zip
> +   http://speleotrove.com/decimal/dectest.zip
>>  This test module can be called from command line with one parameter (Arithmetic
>  or Behaviour) to test each part, or without parameter to test both parts. If
> @@ -30,37 +30,74 @@
>  import warnings
>  import pickle, copy
>  import unittest
> -from decimal import *
>  import numbers
> +import locale
>  from test.support import (run_unittest, run_doctest, is_resource_enabled,
>                           requires_IEEE_754)
> -from test.support import check_warnings
> +from test.support import check_warnings, import_fresh_module, TestFailed
>  import random
> +import time
> +import warnings
>  try:
>     import threading
>  except ImportError:
>     threading = None
>> +
> +C = import_fresh_module('decimal', fresh=['_decimal'])
> +P = import_fresh_module('decimal', blocked=['_decimal'])
> +orig_sys_decimal = sys.modules['decimal']
> +
> +# fractions module must import the correct decimal module.
> +cfractions = import_fresh_module('fractions', fresh=['fractions'])
> +sys.modules['decimal'] = P
> +pfractions = import_fresh_module('fractions', fresh=['fractions'])
> +sys.modules['decimal'] = C
> +fractions = {C:cfractions, P:pfractions}
> +sys.modules['decimal'] = orig_sys_decimal
> +
> +
>  # Useful Test Constant
> -Signals = tuple(getcontext().flags.keys())
> -
> +Signals = {
> +  C: tuple(C.getcontext().flags.keys()) if C else None,
> +  P: tuple(P.getcontext().flags.keys())
> +}
>  # Signals ordered with respect to precedence: when an operation
>  # produces multiple signals, signals occurring later in the list
>  # should be handled before those occurring earlier in the list.
> -OrderedSignals = (Clamped, Rounded, Inexact, Subnormal,
> -                  Underflow, Overflow, DivisionByZero, InvalidOperation)
> +OrderedSignals = {
> +  C: [C.Clamped, C.Rounded, C.Inexact, C.Subnormal, C.Underflow,
> +      C.Overflow, C.DivisionByZero, C.InvalidOperation,
> +      C.FloatOperation] if C else None,
> +  P: [P.Clamped, P.Rounded, P.Inexact, P.Subnormal, P.Underflow,
> +      P.Overflow, P.DivisionByZero, P.InvalidOperation,
> +      P.FloatOperation]
> +}
> +def assert_signals(cls, context, attr, expected):
> +    d = getattr(context, attr)
> +    cls.assertTrue(all(d[s] if s in expected else not d[s] for s in d))
> +
> +RoundingModes = {
> +  C: (C.ROUND_UP, C.ROUND_DOWN, C.ROUND_CEILING, C.ROUND_FLOOR,
> +      C.ROUND_HALF_UP, C.ROUND_HALF_DOWN, C.ROUND_HALF_EVEN,
> +      C.ROUND_05UP) if C else None,
> +  P: (P.ROUND_UP, P.ROUND_DOWN, P.ROUND_CEILING, P.ROUND_FLOOR,
> +      P.ROUND_HALF_UP, P.ROUND_HALF_DOWN, P.ROUND_HALF_EVEN,
> +      P.ROUND_05UP)
> +}
>>  # Tests are built around these assumed context defaults.
>  # test_main() restores the original context.
> -def init():
> -    global ORIGINAL_CONTEXT
> -    ORIGINAL_CONTEXT = getcontext().copy()
> -    DefaultTestContext = Context(
> -        prec = 9,
> -        rounding = ROUND_HALF_EVEN,
> -        traps = dict.fromkeys(Signals, 0)
> -        )
> -    setcontext(DefaultTestContext)
> +ORIGINAL_CONTEXT = {
> +  C: C.getcontext().copy() if C else None,
> +  P: P.getcontext().copy()
> +}
> +def init(m):
> +    if not m: return
> +    DefaultTestContext = m.Context(
> +       prec=9, rounding=m.ROUND_HALF_EVEN, traps=dict.fromkeys(Signals[m], 0)
> +    )
> +    m.setcontext(DefaultTestContext)
>>  TESTDATADIR = 'decimaltestdata'
>  if __name__ == '__main__':
> @@ -72,149 +109,175 @@
>>  skip_expected = not os.path.isdir(directory)
>> -# list of individual .decTest test ids that correspond to tests that
> -# we're skipping for one reason or another.
> -skipped_test_ids = set([
> -    # Skip implementation-specific scaleb tests.
> -    'scbx164',
> -    'scbx165',
> -
> -    # For some operations (currently exp, ln, log10, power), the decNumber
> -    # reference implementation imposes additional restrictions on the context
> -    # and operands.  These restrictions are not part of the specification;
> -    # however, the effect of these restrictions does show up in some of the
> -    # testcases.  We skip testcases that violate these restrictions, since
> -    # Decimal behaves differently from decNumber for these testcases so these
> -    # testcases would otherwise fail.
> -    'expx901',
> -    'expx902',
> -    'expx903',
> -    'expx905',
> -    'lnx901',
> -    'lnx902',
> -    'lnx903',
> -    'lnx905',
> -    'logx901',
> -    'logx902',
> -    'logx903',
> -    'logx905',
> -    'powx1183',
> -    'powx1184',
> -    'powx4001',
> -    'powx4002',
> -    'powx4003',
> -    'powx4005',
> -    'powx4008',
> -    'powx4010',
> -    'powx4012',
> -    'powx4014',
> -    ])
> -
>  # Make sure it actually raises errors when not expected and caught in flags
>  # Slower, since it runs some things several times.
>  EXTENDEDERRORTEST = False
>> -#Map the test cases' error names to the actual errors
> -ErrorNames = {'clamped' : Clamped,
> -              'conversion_syntax' : InvalidOperation,
> -              'division_by_zero' : DivisionByZero,
> -              'division_impossible' : InvalidOperation,
> -              'division_undefined' : InvalidOperation,
> -              'inexact' : Inexact,
> -              'invalid_context' : InvalidOperation,
> -              'invalid_operation' : InvalidOperation,
> -              'overflow' : Overflow,
> -              'rounded' : Rounded,
> -              'subnormal' : Subnormal,
> -              'underflow' : Underflow}
> -
> -
> -def Nonfunction(*args):
> -    """Doesn't do anything."""
> -    return None
> -
> -RoundingDict = {'ceiling' : ROUND_CEILING, #Maps test-case names to roundings.
> -                'down' : ROUND_DOWN,
> -                'floor' : ROUND_FLOOR,
> -                'half_down' : ROUND_HALF_DOWN,
> -                'half_even' : ROUND_HALF_EVEN,
> -                'half_up' : ROUND_HALF_UP,
> -                'up' : ROUND_UP,
> -                '05up' : ROUND_05UP}
> -
> -# Name adapter to be able to change the Decimal and Context
> -# interface without changing the test files from Cowlishaw
> -nameAdapter = {'and':'logical_and',
> -               'apply':'_apply',
> -               'class':'number_class',
> -               'comparesig':'compare_signal',
> -               'comparetotal':'compare_total',
> -               'comparetotmag':'compare_total_mag',
> -               'copy':'copy_decimal',
> -               'copyabs':'copy_abs',
> -               'copynegate':'copy_negate',
> -               'copysign':'copy_sign',
> -               'divideint':'divide_int',
> -               'invert':'logical_invert',
> -               'iscanonical':'is_canonical',
> -               'isfinite':'is_finite',
> -               'isinfinite':'is_infinite',
> -               'isnan':'is_nan',
> -               'isnormal':'is_normal',
> -               'isqnan':'is_qnan',
> -               'issigned':'is_signed',
> -               'issnan':'is_snan',
> -               'issubnormal':'is_subnormal',
> -               'iszero':'is_zero',
> -               'maxmag':'max_mag',
> -               'minmag':'min_mag',
> -               'nextminus':'next_minus',
> -               'nextplus':'next_plus',
> -               'nexttoward':'next_toward',
> -               'or':'logical_or',
> -               'reduce':'normalize',
> -               'remaindernear':'remainder_near',
> -               'samequantum':'same_quantum',
> -               'squareroot':'sqrt',
> -               'toeng':'to_eng_string',
> -               'tointegral':'to_integral_value',
> -               'tointegralx':'to_integral_exact',
> -               'tosci':'to_sci_string',
> -               'xor':'logical_xor',
> -              }
> -
> -# The following functions return True/False rather than a Decimal instance
> -
> -LOGICAL_FUNCTIONS = (
> -    'is_canonical',
> -    'is_finite',
> -    'is_infinite',
> -    'is_nan',
> -    'is_normal',
> -    'is_qnan',
> -    'is_signed',
> -    'is_snan',
> -    'is_subnormal',
> -    'is_zero',
> -    'same_quantum',
> -    )
> -
> -class DecimalTest(unittest.TestCase):
> -    """Class which tests the Decimal class against the test cases.
> -
> -    Changed for unittest.
> -    """
> +# Test extra functionality in the C version (-DEXTRA_FUNCTIONALITY).
> +EXTRA_FUNCTIONALITY = True if hasattr(C, 'DecClamped') else False
> +requires_extra_functionality = unittest.skipUnless(
> +  EXTRA_FUNCTIONALITY, "test requires build with -DEXTRA_FUNCTIONALITY")
> +skip_if_extra_functionality = unittest.skipIf(
> +  EXTRA_FUNCTIONALITY, "test requires regular build")
> +
> +
> +class IBMTestCases(unittest.TestCase):
> +    """Class which tests the Decimal class against the IBM test cases."""
> +
>     def setUp(self):
> -        self.context = Context()
> +        self.context = self.decimal.Context()
> +        self.readcontext = self.decimal.Context()
>         self.ignore_list = ['#']
> -        # Basically, a # means return NaN InvalidOperation.
> -        # Different from a sNaN in trim
> -
> +
> +        # List of individual .decTest test ids that correspond to tests that
> +        # we're skipping for one reason or another.
> +        self.skipped_test_ids = set([
> +            # Skip implementation-specific scaleb tests.
> +            'scbx164',
> +            'scbx165',
> +
> +            # For some operations (currently exp, ln, log10, power), the decNumber
> +            # reference implementation imposes additional restrictions on the context
> +            # and operands.  These restrictions are not part of the specification;
> +            # however, the effect of these restrictions does show up in some of the
> +            # testcases.  We skip testcases that violate these restrictions, since
> +            # Decimal behaves differently from decNumber for these testcases so these
> +            # testcases would otherwise fail.
> +            'expx901',
> +            'expx902',
> +            'expx903',
> +            'expx905',
> +            'lnx901',
> +            'lnx902',
> +            'lnx903',
> +            'lnx905',
> +            'logx901',
> +            'logx902',
> +            'logx903',
> +            'logx905',
> +            'powx1183',
> +            'powx1184',
> +            'powx4001',
> +            'powx4002',
> +            'powx4003',
> +            'powx4005',
> +            'powx4008',
> +            'powx4010',
> +            'powx4012',
> +            'powx4014',
> +            ])
> +
> +        if self.decimal == C:
> +            # status has additional Subnormal, Underflow
> +            self.skipped_test_ids.add('pwsx803')
> +            self.skipped_test_ids.add('pwsx805')
> +            # Correct rounding (skipped for decNumber, too)
> +            self.skipped_test_ids.add('powx4302')
> +            self.skipped_test_ids.add('powx4303')
> +            self.skipped_test_ids.add('powx4342')
> +            self.skipped_test_ids.add('powx4343')
> +            # http://bugs.python.org/issue7049
> +            self.skipped_test_ids.add('pwmx325')
> +            self.skipped_test_ids.add('pwmx326')
> +
> +        # Map test directives to setter functions.
>         self.ChangeDict = {'precision' : self.change_precision,
> -                      'rounding' : self.change_rounding_method,
> -                      'maxexponent' : self.change_max_exponent,
> -                      'minexponent' : self.change_min_exponent,
> -                      'clamp' : self.change_clamp}
> +                           'rounding' : self.change_rounding_method,
> +                           'maxexponent' : self.change_max_exponent,
> +                           'minexponent' : self.change_min_exponent,
> +                           'clamp' : self.change_clamp}
> +
> +        # Name adapter to be able to change the Decimal and Context
> +        # interface without changing the test files from Cowlishaw.
> +        self.NameAdapter = {'and':'logical_and',
> +                            'apply':'_apply',
> +                            'class':'number_class',
> +                            'comparesig':'compare_signal',
> +                            'comparetotal':'compare_total',
> +                            'comparetotmag':'compare_total_mag',
> +                            'copy':'copy_decimal',
> +                            'copyabs':'copy_abs',
> +                            'copynegate':'copy_negate',
> +                            'copysign':'copy_sign',
> +                            'divideint':'divide_int',
> +                            'invert':'logical_invert',
> +                            'iscanonical':'is_canonical',
> +                            'isfinite':'is_finite',
> +                            'isinfinite':'is_infinite',
> +                            'isnan':'is_nan',
> +                            'isnormal':'is_normal',
> +                            'isqnan':'is_qnan',
> +                            'issigned':'is_signed',
> +                            'issnan':'is_snan',
> +                            'issubnormal':'is_subnormal',
> +                            'iszero':'is_zero',
> +                            'maxmag':'max_mag',
> +                            'minmag':'min_mag',
> +                            'nextminus':'next_minus',
> +                            'nextplus':'next_plus',
> +                            'nexttoward':'next_toward',
> +                            'or':'logical_or',
> +                            'reduce':'normalize',
> +                            'remaindernear':'remainder_near',
> +                            'samequantum':'same_quantum',
> +                            'squareroot':'sqrt',
> +                            'toeng':'to_eng_string',
> +                            'tointegral':'to_integral_value',
> +                            'tointegralx':'to_integral_exact',
> +                            'tosci':'to_sci_string',
> +                            'xor':'logical_xor'}
> +
> +        # Map test-case names to roundings.
> +        self.RoundingDict = {'ceiling' : self.decimal.ROUND_CEILING,
> +                             'down' : self.decimal.ROUND_DOWN,
> +                             'floor' : self.decimal.ROUND_FLOOR,
> +                             'half_down' : self.decimal.ROUND_HALF_DOWN,
> +                             'half_even' : self.decimal.ROUND_HALF_EVEN,
> +                             'half_up' : self.decimal.ROUND_HALF_UP,
> +                             'up' : self.decimal.ROUND_UP,
> +                             '05up' : self.decimal.ROUND_05UP}
> +
> +        # Map the test cases' error names to the actual errors.
> +        self.ErrorNames = {'clamped' : self.decimal.Clamped,
> +                           'conversion_syntax' : self.decimal.InvalidOperation,
> +                           'division_by_zero' : self.decimal.DivisionByZero,
> +                           'division_impossible' : self.decimal.InvalidOperation,
> +                           'division_undefined' : self.decimal.InvalidOperation,
> +                           'inexact' : self.decimal.Inexact,
> +                           'invalid_context' : self.decimal.InvalidOperation,
> +                           'invalid_operation' : self.decimal.InvalidOperation,
> +                           'overflow' : self.decimal.Overflow,
> +                           'rounded' : self.decimal.Rounded,
> +                           'subnormal' : self.decimal.Subnormal,
> +                           'underflow' : self.decimal.Underflow}
> +
> +        # The following functions return True/False rather than a
> +        # Decimal instance.
> +        self.LogicalFunctions = ('is_canonical',
> +                                 'is_finite',
> +                                 'is_infinite',
> +                                 'is_nan',
> +                                 'is_normal',
> +                                 'is_qnan',
> +                                 'is_signed',
> +                                 'is_snan',
> +                                 'is_subnormal',
> +                                 'is_zero',
> +                                 'same_quantum')
> +
> +    def read_unlimited(self, v, context):
> +        """Work around the limitations of the 32-bit _decimal version. The
> +           guaranteed maximum values for prec, Emax etc. are 425000000,
> +           but higher values usually work, except for rare corner cases.
> +           In particular, all of the IBM tests pass with maximum values
> +           of 1070000000."""
> +        if self.decimal == C and self.decimal.MAX_EMAX == 425000000:
> +            self.readcontext._unsafe_setprec(1070000000)
> +            self.readcontext._unsafe_setemax(1070000000)
> +            self.readcontext._unsafe_setemin(-1070000000)
> +            return self.readcontext.create_decimal(v)
> +        else:
> +            return self.decimal.Decimal(v, context)
>>     def eval_file(self, file):
>         global skip_expected
> @@ -227,7 +290,7 @@
>                 #print line
>                 try:
>                     t = self.eval_line(line)
> -                except DecimalException as exception:
> +                except self.decimal.DecimalException as exception:
>                     #Exception raised where there shouldn't have been one.
>                     self.fail('Exception "'+exception.__class__.__name__ + '" raised on line '+line)
>> @@ -254,23 +317,23 @@
>     def eval_directive(self, s):
>         funct, value = (x.strip().lower() for x in s.split(':'))
>         if funct == 'rounding':
> -            value = RoundingDict[value]
> +            value = self.RoundingDict[value]
>         else:
>             try:
>                 value = int(value)
>             except ValueError:
>                 pass
>> -        funct = self.ChangeDict.get(funct, Nonfunction)
> +        funct = self.ChangeDict.get(funct, (lambda *args: None))
>         funct(value)
>>     def eval_equation(self, s):
> -        #global DEFAULT_PRECISION
> -        #print DEFAULT_PRECISION
>>         if not TEST_ALL and random.random() < 0.90:
>             return
>> +        self.context.clear_flags()
> +
>         try:
>             Sides = s.split('->')
>             L = Sides[0].strip().split()
> @@ -283,26 +346,26 @@
>             ans = L[0]
>             exceptions = L[1:]
>         except (TypeError, AttributeError, IndexError):
> -            raise InvalidOperation
> +            raise self.decimal.InvalidOperation
>         def FixQuotes(val):
>             val = val.replace("''", 'SingleQuote').replace('""', 'DoubleQuote')
>             val = val.replace("'", '').replace('"', '')
>             val = val.replace('SingleQuote', "'").replace('DoubleQuote', '"')
>             return val
>> -        if id in skipped_test_ids:
> +        if id in self.skipped_test_ids:
>             return
>> -        fname = nameAdapter.get(funct, funct)
> +        fname = self.NameAdapter.get(funct, funct)
>         if fname == 'rescale':
>             return
>         funct = getattr(self.context, fname)
>         vals = []
>         conglomerate = ''
>         quote = 0
> -        theirexceptions = [ErrorNames[x.lower()] for x in exceptions]
> -
> -        for exception in Signals:
> +        theirexceptions = [self.ErrorNames[x.lower()] for x in exceptions]
> +
> +        for exception in Signals[self.decimal]:
>             self.context.traps[exception] = 1 #Catch these bugs...
>         for exception in theirexceptions:
>             self.context.traps[exception] = 0
> @@ -324,7 +387,7 @@
>                             funct(self.context.create_decimal(v))
>                         except error:
>                             pass
> -                        except Signals as e:
> +                        except Signals[self.decimal] as e:
>                             self.fail("Raised %s in %s when %s disabled" % \
>                                       (e, s, error))
>                         else:
> @@ -332,7 +395,7 @@
>                         self.context.traps[error] = 0
>                 v = self.context.create_decimal(v)
>             else:
> -                v = Decimal(v, self.context)
> +                v = self.read_unlimited(v, self.context)
>             vals.append(v)
>>         ans = FixQuotes(ans)
> @@ -344,7 +407,7 @@
>                     funct(*vals)
>                 except error:
>                     pass
> -                except Signals as e:
> +                except Signals[self.decimal] as e:
>                     self.fail("Raised %s in %s when %s disabled" % \
>                               (e, s, error))
>                 else:
> @@ -352,14 +415,14 @@
>                 self.context.traps[error] = 0
>>             # as above, but add traps cumulatively, to check precedence
> -            ordered_errors = [e for e in OrderedSignals if e in theirexceptions]
> +            ordered_errors = [e for e in OrderedSignals[self.decimal] if e in theirexceptions]
>             for error in ordered_errors:
>                 self.context.traps[error] = 1
>                 try:
>                     funct(*vals)
>                 except error:
>                     pass
> -                except Signals as e:
> +                except Signals[self.decimal] as e:
>                     self.fail("Raised %s in %s; expected %s" %
>                               (type(e), s, error))
>                 else:
> @@ -373,54 +436,69 @@
>             print("--", self.context)
>         try:
>             result = str(funct(*vals))
> -            if fname in LOGICAL_FUNCTIONS:
> +            if fname in self.LogicalFunctions:
>                 result = str(int(eval(result))) # 'True', 'False' -> '1', '0'
> -        except Signals as error:
> +        except Signals[self.decimal] as error:
>             self.fail("Raised %s in %s" % (error, s))
>         except: #Catch any error long enough to state the test case.
>             print("ERROR:", s)
>             raise
>>         myexceptions = self.getexceptions()
> -        self.context.clear_flags()
>>         myexceptions.sort(key=repr)
>         theirexceptions.sort(key=repr)
>>         self.assertEqual(result, ans,
>                          'Incorrect answer for ' + s + ' -- got ' + result)
> +
>         self.assertEqual(myexceptions, theirexceptions,
>               'Incorrect flags set in ' + s + ' -- got ' + str(myexceptions))
>         return
>>     def getexceptions(self):
> -        return [e for e in Signals if self.context.flags[e]]
> +        return [e for e in Signals[self.decimal] if self.context.flags[e]]
>>     def change_precision(self, prec):
> -        self.context.prec = prec
> +        if self.decimal == C and self.decimal.MAX_PREC == 425000000:
> +            self.context._unsafe_setprec(prec)
> +        else:
> +            self.context.prec = prec
>     def change_rounding_method(self, rounding):
>         self.context.rounding = rounding
>     def change_min_exponent(self, exp):
> -        self.context.Emin = exp
> +        if self.decimal == C and self.decimal.MAX_PREC == 425000000:
> +            self.context._unsafe_setemin(exp)
> +        else:
> +            self.context.Emin = exp
>     def change_max_exponent(self, exp):
> -        self.context.Emax = exp
> +        if self.decimal == C and self.decimal.MAX_PREC == 425000000:
> +            self.context._unsafe_setemax(exp)
> +        else:
> +            self.context.Emax = exp
>     def change_clamp(self, clamp):
>         self.context.clamp = clamp
>> -
> +class CIBMTestCases(IBMTestCases):
> +    decimal = C
> +class PyIBMTestCases(IBMTestCases):
> +    decimal = P
>>  # The following classes test the behaviour of Decimal according to PEP 327
>> -class DecimalExplicitConstructionTest(unittest.TestCase):
> +class ExplicitConstructionTest(unittest.TestCase):
>     '''Unit tests for Explicit Construction cases of Decimal.'''
>>     def test_explicit_empty(self):
> +        Decimal = self.decimal.Decimal
>         self.assertEqual(Decimal(), Decimal("0"))
>>     def test_explicit_from_None(self):
> +        Decimal = self.decimal.Decimal
>         self.assertRaises(TypeError, Decimal, None)
>>     def test_explicit_from_int(self):
> +        Decimal = self.decimal.Decimal
>>         #positive
>         d = Decimal(45)
> @@ -438,7 +516,18 @@
>         d = Decimal(0)
>         self.assertEqual(str(d), '0')
>> +        # single word longs
> +        for n in range(0, 32):
> +            for sign in (-1, 1):
> +                for x in range(-5, 5):
> +                    i = sign * (2**n + x)
> +                    d = Decimal(i)
> +                    self.assertEqual(str(d), str(i))
> +
>     def test_explicit_from_string(self):
> +        Decimal = self.decimal.Decimal
> +        InvalidOperation = self.decimal.InvalidOperation
> +        localcontext = self.decimal.localcontext
>>         #empty
>         self.assertEqual(str(Decimal('')), 'NaN')
> @@ -458,8 +547,35 @@
>         #leading and trailing whitespace permitted
>         self.assertEqual(str(Decimal('1.3E4 \n')), '1.3E+4')
>         self.assertEqual(str(Decimal('  -7.89')), '-7.89')
> +        self.assertEqual(str(Decimal("  3.45679  ")), '3.45679')
> +
> +        # unicode whitespace
> +        for lead in ["", ' ', '\u00a0', '\u205f']:
> +            for trail in ["", ' ', '\u00a0', '\u205f']:
> +                self.assertEqual(str(Decimal(lead + '9.311E+28' + trail)),
> +                                 '9.311E+28')
> +
> +        with localcontext() as c:
> +            c.traps[InvalidOperation] = True
> +            # Invalid string
> +            self.assertRaises(InvalidOperation, Decimal, "xyz")
> +            # Two arguments max
> +            self.assertRaises(TypeError, Decimal, "1234", "x", "y")
> +
> +            # space within the numeric part
> +            self.assertRaises(InvalidOperation, Decimal, "1\u00a02\u00a03")
> +            self.assertRaises(InvalidOperation, Decimal, "\u00a01\u00a02\u00a0")
> +
> +            # unicode whitespace
> +            self.assertRaises(InvalidOperation, Decimal, "\u00a0")
> +            self.assertRaises(InvalidOperation, Decimal, "\u00a0\u00a0")
> +
> +            # embedded NUL
> +            self.assertRaises(InvalidOperation, Decimal, "12\u00003")
> +
>>     def test_explicit_from_tuples(self):
> +        Decimal = self.decimal.Decimal
>>         #zero
>         d = Decimal( (0, (0,), 0) )
> @@ -477,6 +593,10 @@
>         d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
>         self.assertEqual(str(d), '-4.34913534E-17')
>> +        #inf
> +        d = Decimal( (0, (), "F") )
> +        self.assertEqual(str(d), 'Infinity')
> +
>         #wrong number of items
>         self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1)) )
>> @@ -491,45 +611,63 @@
>         self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), '1') )
>>         #bad coefficients
> +        self.assertRaises(ValueError, Decimal, (1, "xyz", 2) )
>         self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, None, 1), 2) )
>         self.assertRaises(ValueError, Decimal, (1, (4, -3, 4, 9, 1), 2) )
>         self.assertRaises(ValueError, Decimal, (1, (4, 10, 4, 9, 1), 2) )
>         self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 'a', 1), 2) )
>> +    def test_explicit_from_list(self):
> +        Decimal = self.decimal.Decimal
> +
> +        d = Decimal([0, [0], 0])
> +        self.assertEqual(str(d), '0')
> +
> +        d = Decimal([1, [4, 3, 4, 9, 1, 3, 5, 3, 4], -25])
> +        self.assertEqual(str(d), '-4.34913534E-17')
> +
> +        d = Decimal([1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25])
> +        self.assertEqual(str(d), '-4.34913534E-17')
> +
> +        d = Decimal((1, [4, 3, 4, 9, 1, 3, 5, 3, 4], -25))
> +        self.assertEqual(str(d), '-4.34913534E-17')
> +
>     def test_explicit_from_bool(self):
> +        Decimal = self.decimal.Decimal
> +
>         self.assertIs(bool(Decimal(0)), False)
>         self.assertIs(bool(Decimal(1)), True)
>         self.assertEqual(Decimal(False), Decimal(0))
>         self.assertEqual(Decimal(True), Decimal(1))
>>     def test_explicit_from_Decimal(self):
> +        Decimal = self.decimal.Decimal
>>         #positive
>         d = Decimal(45)
>         e = Decimal(d)
>         self.assertEqual(str(e), '45')
> -        self.assertNotEqual(id(d), id(e))
>>         #very large positive
>         d = Decimal(500000123)
>         e = Decimal(d)
>         self.assertEqual(str(e), '500000123')
> -        self.assertNotEqual(id(d), id(e))
>>         #negative
>         d = Decimal(-45)
>         e = Decimal(d)
>         self.assertEqual(str(e), '-45')
> -        self.assertNotEqual(id(d), id(e))
>>         #zero
>         d = Decimal(0)
>         e = Decimal(d)
>         self.assertEqual(str(e), '0')
> -        self.assertNotEqual(id(d), id(e))
>>     @requires_IEEE_754
>     def test_explicit_from_float(self):
> +
> +        Decimal = self.decimal.Decimal
> +
>         r = Decimal(0.1)
>         self.assertEqual(type(r), Decimal)
>         self.assertEqual(str(r),
> @@ -550,8 +688,11 @@
>             self.assertEqual(x, float(Decimal(x))) # roundtrip
>>     def test_explicit_context_create_decimal(self):
> -
> -        nc = copy.copy(getcontext())
> +        Decimal = self.decimal.Decimal
> +        InvalidOperation = self.decimal.InvalidOperation
> +        Rounded = self.decimal.Rounded
> +
> +        nc = copy.copy(self.decimal.getcontext())
>         nc.prec = 3
>>         # empty
> @@ -592,7 +733,73 @@
>         d = nc.create_decimal(prevdec)
>         self.assertEqual(str(d), '5.00E+8')
>> +        # more integers
> +        nc.prec = 28
> +        nc.traps[InvalidOperation] = True
> +
> +        for v in [-2**63-1, -2**63, -2**31-1, -2**31, 0,
> +                   2**31-1, 2**31, 2**63-1, 2**63]:
> +            d = nc.create_decimal(v)
> +            self.assertTrue(isinstance(d, Decimal))
> +            self.assertEqual(int(d), v)
> +
> +        nc.prec = 3
> +        nc.traps[Rounded] = True
> +        self.assertRaises(Rounded, nc.create_decimal, 1234)
> +
> +        # from string
> +        nc.prec = 28
> +        self.assertEqual(str(nc.create_decimal('0E-017')), '0E-17')
> +        self.assertEqual(str(nc.create_decimal('45')), '45')
> +        self.assertEqual(str(nc.create_decimal('-Inf')), '-Infinity')
> +        self.assertEqual(str(nc.create_decimal('NaN123')), 'NaN123')
> +
> +        # invalid arguments
> +        self.assertRaises(InvalidOperation, nc.create_decimal, "xyz")
> +        self.assertRaises(ValueError, nc.create_decimal, (1, "xyz", -25))
> +        self.assertRaises(TypeError, nc.create_decimal, "1234", "5678")
> +
> +        # too many NaN payload digits
> +        nc.prec = 3
> +        self.assertRaises(InvalidOperation, nc.create_decimal, 'NaN12345')
> +        self.assertRaises(InvalidOperation, nc.create_decimal,
> +                          Decimal('NaN12345'))
> +
> +        nc.traps[InvalidOperation] = False
> +        self.assertEqual(str(nc.create_decimal('NaN12345')), 'NaN')
> +        self.assertTrue(nc.flags[InvalidOperation])
> +
> +        nc.flags[InvalidOperation] = False
> +        self.assertEqual(str(nc.create_decimal(Decimal('NaN12345'))), 'NaN')
> +        self.assertTrue(nc.flags[InvalidOperation])
> +
> +    def test_explicit_context_create_from_float(self):
> +
> +        Decimal = self.decimal.Decimal
> +
> +        nc = self.decimal.Context()
> +        r = nc.create_decimal(0.1)
> +        self.assertEqual(type(r), Decimal)
> +        self.assertEqual(str(r), '0.1000000000000000055511151231')
> +        self.assertTrue(nc.create_decimal(float('nan')).is_qnan())
> +        self.assertTrue(nc.create_decimal(float('inf')).is_infinite())
> +        self.assertTrue(nc.create_decimal(float('-inf')).is_infinite())
> +        self.assertEqual(str(nc.create_decimal(float('nan'))),
> +                         str(nc.create_decimal('NaN')))
> +        self.assertEqual(str(nc.create_decimal(float('inf'))),
> +                         str(nc.create_decimal('Infinity')))
> +        self.assertEqual(str(nc.create_decimal(float('-inf'))),
> +                         str(nc.create_decimal('-Infinity')))
> +        self.assertEqual(str(nc.create_decimal(float('-0.0'))),
> +                         str(nc.create_decimal('-0')))
> +        nc.prec = 100
> +        for i in range(200):
> +            x = random.expovariate(0.01) * (random.random() * 2.0 - 1.0)
> +            self.assertEqual(x, float(nc.create_decimal(x))) # roundtrip
> +
>     def test_unicode_digits(self):
> +        Decimal = self.decimal.Decimal
> +
>         test_values = {
>             '\uff11': '1',
>             '\u0660.\u0660\u0663\u0667\u0662e-\u0663' : '0.0000372',
> @@ -601,29 +808,41 @@
>         for input, expected in test_values.items():
>             self.assertEqual(str(Decimal(input)), expected)
>> -
> -class DecimalImplicitConstructionTest(unittest.TestCase):
> +class CExplicitConstructionTest(ExplicitConstructionTest):
> +    decimal = C
> +class PyExplicitConstructionTest(ExplicitConstructionTest):
> +    decimal = P
> +
> +class ImplicitConstructionTest(unittest.TestCase):
>     '''Unit tests for Implicit Construction cases of Decimal.'''
>>     def test_implicit_from_None(self):
> -        self.assertRaises(TypeError, eval, 'Decimal(5) + None', globals())
> +        Decimal = self.decimal.Decimal
> +        self.assertRaises(TypeError, eval, 'Decimal(5) + None', locals())
>>     def test_implicit_from_int(self):
> +        Decimal = self.decimal.Decimal
> +
>         #normal
>         self.assertEqual(str(Decimal(5) + 45), '50')
>         #exceeding precision
>         self.assertEqual(Decimal(5) + 123456789000, Decimal(123456789000))
>>     def test_implicit_from_string(self):
> -        self.assertRaises(TypeError, eval, 'Decimal(5) + "3"', globals())
> +        Decimal = self.decimal.Decimal
> +        self.assertRaises(TypeError, eval, 'Decimal(5) + "3"', locals())
>>     def test_implicit_from_float(self):
> -        self.assertRaises(TypeError, eval, 'Decimal(5) + 2.2', globals())
> +        Decimal = self.decimal.Decimal
> +        self.assertRaises(TypeError, eval, 'Decimal(5) + 2.2', locals())
>>     def test_implicit_from_Decimal(self):
> +        Decimal = self.decimal.Decimal
>         self.assertEqual(Decimal(5) + Decimal(45), Decimal(50))
>>     def test_rop(self):
> +        Decimal = self.decimal.Decimal
> +
>         # Allow other classes to be trained to interact with Decimals
>         class E:
>             def __divmod__(self, other):
> @@ -671,10 +890,16 @@
>             self.assertEqual(eval('Decimal(10)' + sym + 'E()'),
>                              '10' + rop + 'str')
>> -
> -class DecimalFormatTest(unittest.TestCase):
> +class CImplicitConstructionTest(ImplicitConstructionTest):
> +    decimal = C
> +class PyImplicitConstructionTest(ImplicitConstructionTest):
> +    decimal = P
> +
> +class FormatTest(unittest.TestCase):
>     '''Unit tests for the format function.'''
>     def test_formatting(self):
> +        Decimal = self.decimal.Decimal
> +
>         # triples giving a format, a Decimal, and the expected result
>         test_values = [
>             ('e', '0E-15', '0e-15'),
> @@ -730,6 +955,7 @@
>             ('g', '0E-7', '0e-7'),
>             ('g', '-0E2', '-0e+2'),
>             ('.0g', '3.14159265', '3'),  # 0 sig fig -> 1 sig fig
> +            ('.0n', '3.14159265', '3'),  # same for 'n'
>             ('.1g', '3.14159265', '3'),
>             ('.2g', '3.14159265', '3.1'),
>             ('.5g', '3.14159265', '3.1416'),
> @@ -814,56 +1040,60 @@
>>             # issue 6850
>             ('a=-7.0', '0.12345', 'aaaa0.1'),
> -
> -            # Issue 7094: Alternate formatting (specified by #)
> -            ('.0e', '1.0', '1e+0'),
> -            ('#.0e', '1.0', '1.e+0'),
> -            ('.0f', '1.0', '1'),
> -            ('#.0f', '1.0', '1.'),
> -            ('g', '1.1', '1.1'),
> -            ('#g', '1.1', '1.1'),
> -            ('.0g', '1', '1'),
> -            ('#.0g', '1', '1.'),
> -            ('.0%', '1.0', '100%'),
> -            ('#.0%', '1.0', '100.%'),
>             ]
>         for fmt, d, result in test_values:
>             self.assertEqual(format(Decimal(d), fmt), result)
>> +        # bytes format argument
> +        self.assertRaises(TypeError, Decimal(1).__format__, b'-020')
> +
>     def test_n_format(self):
> +        Decimal = self.decimal.Decimal
> +
>         try:
>             from locale import CHAR_MAX
>         except ImportError:
>             return
>> +        def make_grouping(lst):
> +            return ''.join([chr(x) for x in lst]) if self.decimal == C else lst
> +
> +        def get_fmt(x, override=None, fmt='n'):
> +            if self.decimal == C:
> +                return Decimal(x).__format__(fmt, override)
> +            else:
> +                return Decimal(x).__format__(fmt, _localeconv=override)
> +
>         # Set up some localeconv-like dictionaries
>         en_US = {
>             'decimal_point' : '.',
> -            'grouping' : [3, 3, 0],
> -            'thousands_sep': ','
> +            'grouping' : make_grouping([3, 3, 0]),
> +            'thousands_sep' : ','
>             }
>>         fr_FR = {
>             'decimal_point' : ',',
> -            'grouping' : [CHAR_MAX],
> +            'grouping' : make_grouping([CHAR_MAX]),
>             'thousands_sep' : ''
>             }
>>         ru_RU = {
>             'decimal_point' : ',',
> -            'grouping' : [3, 3, 0],
> +            'grouping': make_grouping([3, 3, 0]),
>             'thousands_sep' : ' '
>             }
>>         crazy = {
>             'decimal_point' : '&',
> -            'grouping' : [1, 4, 2, CHAR_MAX],
> +            'grouping': make_grouping([1, 4, 2, CHAR_MAX]),
>             'thousands_sep' : '-'
>             }
>> -
> -        def get_fmt(x, locale, fmt='n'):
> -            return Decimal.__format__(Decimal(x), fmt, _localeconv=locale)
> +        dotsep_wide = {
> +            'decimal_point' : b'\xc2\xbf'.decode('utf-8'),
> +            'grouping': make_grouping([3, 3, 0]),
> +            'thousands_sep' : b'\xc2\xb4'.decode('utf-8')
> +            }
>>         self.assertEqual(get_fmt(Decimal('12.7'), en_US), '12.7')
>         self.assertEqual(get_fmt(Decimal('12.7'), fr_FR), '12,7')
> @@ -902,11 +1132,33 @@
>         self.assertEqual(get_fmt(123456, crazy, '012n'), '00-01-2345-6')
>         self.assertEqual(get_fmt(123456, crazy, '013n'), '000-01-2345-6')
>> -
> -class DecimalArithmeticOperatorsTest(unittest.TestCase):
> +        # wide char separator and decimal point
> +        self.assertEqual(get_fmt(Decimal('-1.5'), dotsep_wide, '020n'),
> +                         '-0\u00b4000\u00b4000\u00b4000\u00b4001\u00bf5')
> +
> +    def test_wide_char_separator_decimal_point(self):
> +        # locale with wide char separator and decimal point
> +        Decimal = self.decimal.Decimal
> +
> +        try:
> +            locale.setlocale(locale.LC_ALL, 'ps_AF')
> +        except locale.Error:
> +            return
> +
> +        self.assertEqual(format(Decimal('100000000.123'), 'n'),
> +                         '100\u066c000\u066c000\u066b123')
> +        locale.resetlocale()
> +
> +class CFormatTest(FormatTest):
> +    decimal = C
> +class PyFormatTest(FormatTest):
> +    decimal = P
> +
> +class ArithmeticOperatorsTest(unittest.TestCase):
>     '''Unit tests for all arithmetic operators, binary and unary.'''
>>     def test_addition(self):
> +        Decimal = self.decimal.Decimal
>>         d1 = Decimal('-11.1')
>         d2 = Decimal('22.2')
> @@ -934,6 +1186,7 @@
>         self.assertEqual(d1, Decimal('16.1'))
>>     def test_subtraction(self):
> +        Decimal = self.decimal.Decimal
>>         d1 = Decimal('-11.1')
>         d2 = Decimal('22.2')
> @@ -961,6 +1214,7 @@
>         self.assertEqual(d1, Decimal('-38.3'))
>>     def test_multiplication(self):
> +        Decimal = self.decimal.Decimal
>>         d1 = Decimal('-5')
>         d2 = Decimal('3')
> @@ -988,6 +1242,7 @@
>         self.assertEqual(d1, Decimal('-75'))
>>     def test_division(self):
> +        Decimal = self.decimal.Decimal
>>         d1 = Decimal('-5')
>         d2 = Decimal('2')
> @@ -1015,6 +1270,7 @@
>         self.assertEqual(d1, Decimal('-0.625'))
>>     def test_floor_division(self):
> +        Decimal = self.decimal.Decimal
>>         d1 = Decimal('5')
>         d2 = Decimal('2')
> @@ -1042,6 +1298,7 @@
>         self.assertEqual(d1, Decimal('1'))
>>     def test_powering(self):
> +        Decimal = self.decimal.Decimal
>>         d1 = Decimal('5')
>         d2 = Decimal('2')
> @@ -1069,6 +1326,7 @@
>         self.assertEqual(d1, Decimal('390625'))
>>     def test_module(self):
> +        Decimal = self.decimal.Decimal
>>         d1 = Decimal('5')
>         d2 = Decimal('2')
> @@ -1096,6 +1354,7 @@
>         self.assertEqual(d1, Decimal('1'))
>>     def test_floor_div_module(self):
> +        Decimal = self.decimal.Decimal
>>         d1 = Decimal('5')
>         d2 = Decimal('2')
> @@ -1122,6 +1381,8 @@
>         self.assertEqual(type(q), type(d1))
>>     def test_unary_operators(self):
> +        Decimal = self.decimal.Decimal
> +
>         self.assertEqual(+Decimal(45), Decimal(+45))           #  +
>         self.assertEqual(-Decimal(45), Decimal(-45))           #  -
>         self.assertEqual(abs(Decimal(45)), abs(Decimal(-45)))  # abs
> @@ -1134,6 +1395,9 @@
>>         # equality comparisons (==, !=) involving only quiet nans
>         # don't signal, but return False or True respectively.
> +        Decimal = self.decimal.Decimal
> +        InvalidOperation = self.decimal.InvalidOperation
> +        localcontext = self.decimal.localcontext
>>         n = Decimal('NaN')
>         s = Decimal('sNaN')
> @@ -1179,53 +1443,124 @@
>                     self.assertRaises(InvalidOperation, op, x, y)
>>     def test_copy_sign(self):
> +        Decimal = self.decimal.Decimal
> +
>         d = Decimal(1).copy_sign(Decimal(-2))
> -
>         self.assertEqual(Decimal(1).copy_sign(-2), d)
>         self.assertRaises(TypeError, Decimal(1).copy_sign, '-2')
>> +class CArithmeticOperatorsTest(ArithmeticOperatorsTest):
> +    decimal = C
> +class PyArithmeticOperatorsTest(ArithmeticOperatorsTest):
> +    decimal = P
> +
>  # The following are two functions used to test threading in the next class
>>  def thfunc1(cls):
> +    Decimal = cls.decimal.Decimal
> +    InvalidOperation = cls.decimal.InvalidOperation
> +    DivisionByZero = cls.decimal.DivisionByZero
> +    Overflow = cls.decimal.Overflow
> +    Underflow = cls.decimal.Underflow
> +    Inexact = cls.decimal.Inexact
> +    getcontext = cls.decimal.getcontext
> +    localcontext = cls.decimal.localcontext
> +
>     d1 = Decimal(1)
>     d3 = Decimal(3)
>     test1 = d1/d3
> +
> +    cls.finish1.set()
>     cls.synchro.wait()
> +
>     test2 = d1/d3
> -    cls.finish1.set()
> -
> -    cls.assertEqual(test1, Decimal('0.3333333333333333333333333333'))
> -    cls.assertEqual(test2, Decimal('0.3333333333333333333333333333'))
> +    with localcontext() as c2:
> +        cls.assertTrue(c2.flags[Inexact])
> +        cls.assertRaises(DivisionByZero, c2.divide, d1, 0)
> +        cls.assertTrue(c2.flags[DivisionByZero])
> +        with localcontext() as c3:
> +            cls.assertTrue(c3.flags[Inexact])
> +            cls.assertTrue(c3.flags[DivisionByZero])
> +            cls.assertRaises(InvalidOperation, c3.compare, d1, Decimal('sNaN'))
> +            cls.assertTrue(c3.flags[InvalidOperation])
> +            del c3
> +        cls.assertFalse(c2.flags[InvalidOperation])
> +        del c2
> +
> +    cls.assertEqual(test1, Decimal('0.333333333333333333333333'))
> +    cls.assertEqual(test2, Decimal('0.333333333333333333333333'))
> +
> +    c1 = getcontext()
> +    cls.assertTrue(c1.flags[Inexact])
> +    for sig in Overflow, Underflow, DivisionByZero, InvalidOperation:
> +        cls.assertFalse(c1.flags[sig])
>     return
>>  def thfunc2(cls):
> +    Decimal = cls.decimal.Decimal
> +    InvalidOperation = cls.decimal.InvalidOperation
> +    DivisionByZero = cls.decimal.DivisionByZero
> +    Overflow = cls.decimal.Overflow
> +    Underflow = cls.decimal.Underflow
> +    Inexact = cls.decimal.Inexact
> +    getcontext = cls.decimal.getcontext
> +    localcontext = cls.decimal.localcontext
> +
>     d1 = Decimal(1)
>     d3 = Decimal(3)
>     test1 = d1/d3
> +
>     thiscontext = getcontext()
>     thiscontext.prec = 18
>     test2 = d1/d3
> +
> +    with localcontext() as c2:
> +        cls.assertTrue(c2.flags[Inexact])
> +        cls.assertRaises(Overflow, c2.multiply, Decimal('1e425000000'), 999)
> +        cls.assertTrue(c2.flags[Overflow])
> +        with localcontext(thiscontext) as c3:
> +            cls.assertTrue(c3.flags[Inexact])
> +            cls.assertFalse(c3.flags[Overflow])
> +            c3.traps[Underflow] = True
> +            cls.assertRaises(Underflow, c3.divide, Decimal('1e-425000000'), 999)
> +            cls.assertTrue(c3.flags[Underflow])
> +            del c3
> +        cls.assertFalse(c2.flags[Underflow])
> +        cls.assertFalse(c2.traps[Underflow])
> +        del c2
> +
>     cls.synchro.set()
>     cls.finish2.set()
>> -    cls.assertEqual(test1, Decimal('0.3333333333333333333333333333'))
> +    cls.assertEqual(test1, Decimal('0.333333333333333333333333'))
>     cls.assertEqual(test2, Decimal('0.333333333333333333'))
> +
> +    cls.assertFalse(thiscontext.traps[Underflow])
> +    cls.assertTrue(thiscontext.flags[Inexact])
> +    for sig in Overflow, Underflow, DivisionByZero, InvalidOperation:
> +        cls.assertFalse(thiscontext.flags[sig])
>     return
>> -
> -class DecimalUseOfContextTest(unittest.TestCase):
> -    '''Unit tests for Use of Context cases in Decimal.'''
> -
> -    try:
> -        import threading
> -    except ImportError:
> -        threading = None
> +class ThreadingTest(unittest.TestCase):
> +    '''Unit tests for thread local contexts in Decimal.'''
>>     # Take care executing this test from IDLE, there's an issue in threading
>     # that hangs IDLE and I couldn't find it
>>     def test_threading(self):
> -        #Test the "threading isolation" of a Context.
> +        DefaultContext = self.decimal.DefaultContext
> +
> +        if self.decimal == C and not self.decimal.HAVE_THREADS:
> +            self.skipTest("compiled without threading")
> +        # Test the "threading isolation" of a Context. Also test changing
> +        # the DefaultContext, which acts as a template for the thread-local
> +        # contexts.
> +        save_prec = DefaultContext.prec
> +        save_emax = DefaultContext.Emax
> +        save_emin = DefaultContext.Emin
> +        DefaultContext.prec = 24
> +        DefaultContext.Emax = 425000000
> +        DefaultContext.Emin = -425000000
>>         self.synchro = threading.Event()
>         self.finish1 = threading.Event()
> @@ -1239,17 +1574,29 @@
>>         self.finish1.wait()
>         self.finish2.wait()
> +
> +        for sig in Signals[self.decimal]:
> +            self.assertFalse(DefaultContext.flags[sig])
> +
> +        DefaultContext.prec = save_prec
> +        DefaultContext.Emax = save_emax
> +        DefaultContext.Emin = save_emin
>         return
>> -    if threading is None:
> -        del test_threading
> -
> -
> -class DecimalUsabilityTest(unittest.TestCase):
> + at unittest.skipUnless(threading, 'threading required')
> +class CThreadingTest(ThreadingTest):
> +    decimal = C
> + at unittest.skipUnless(threading, 'threading required')
> +class PyThreadingTest(ThreadingTest):
> +    decimal = P
> +
> +class UsabilityTest(unittest.TestCase):
>     '''Unit tests for Usability cases of Decimal.'''
>>     def test_comparison_operators(self):
>> +        Decimal = self.decimal.Decimal
> +
>         da = Decimal('23.42')
>         db = Decimal('23.42')
>         dc = Decimal('45')
> @@ -1283,6 +1630,8 @@
>         self.assertEqual(a, b)
>>     def test_decimal_float_comparison(self):
> +        Decimal = self.decimal.Decimal
> +
>         da = Decimal('0.25')
>         db = Decimal('3.0')
>         self.assertLess(da, 3.0)
> @@ -1299,7 +1648,71 @@
>         self.assertEqual(3.0, db)
>         self.assertNotEqual(0.1, Decimal('0.1'))
>> +    def test_decimal_complex_comparison(self):
> +        Decimal = self.decimal.Decimal
> +
> +        da = Decimal('0.25')
> +        db = Decimal('3.0')
> +        self.assertNotEqual(da, (1.5+0j))
> +        self.assertNotEqual((1.5+0j), da)
> +        self.assertEqual(da, (0.25+0j))
> +        self.assertEqual((0.25+0j), da)
> +        self.assertEqual((3.0+0j), db)
> +        self.assertEqual(db, (3.0+0j))
> +
> +        self.assertNotEqual(db, (3.0+1j))
> +        self.assertNotEqual((3.0+1j), db)
> +
> +        self.assertIs(db.__lt__(3.0+0j), NotImplemented)
> +        self.assertIs(db.__le__(3.0+0j), NotImplemented)
> +        self.assertIs(db.__gt__(3.0+0j), NotImplemented)
> +        self.assertIs(db.__le__(3.0+0j), NotImplemented)
> +
> +    def test_decimal_fraction_comparison(self):
> +        D = self.decimal.Decimal
> +        F = fractions[self.decimal].Fraction
> +        Context = self.decimal.Context
> +        localcontext = self.decimal.localcontext
> +        InvalidOperation = self.decimal.InvalidOperation
> +
> +
> +        emax = C.MAX_EMAX if C else 999999999
> +        emin = C.MIN_EMIN if C else -999999999
> +        etiny = C.MIN_ETINY if C else -1999999997
> +        c = Context(Emax=emax, Emin=emin)
> +
> +        with localcontext(c):
> +            c.prec = emax
> +            self.assertLess(D(0), F(1,9999999999999999999999999999999999999))
> +            self.assertLess(F(-1,9999999999999999999999999999999999999), D(0))
> +            self.assertLess(F(0,1), D("1e" + str(etiny)))
> +            self.assertLess(D("-1e" + str(etiny)), F(0,1))
> +            self.assertLess(F(0,9999999999999999999999999), D("1e" + str(etiny)))
> +            self.assertLess(D("-1e" + str(etiny)), F(0,9999999999999999999999999))
> +
> +            self.assertEqual(D("0.1"), F(1,10))
> +            self.assertEqual(F(1,10), D("0.1"))
> +
> +            c.prec = 300
> +            self.assertNotEqual(D(1)/3, F(1,3))
> +            self.assertNotEqual(F(1,3), D(1)/3)
> +
> +            self.assertLessEqual(F(120984237, 9999999999), D("9e" + str(emax)))
> +            self.assertGreaterEqual(D("9e" + str(emax)), F(120984237, 9999999999))
> +
> +            self.assertGreater(D('inf'), F(99999999999,123))
> +            self.assertGreater(D('inf'), F(-99999999999,123))
> +            self.assertLess(D('-inf'), F(99999999999,123))
> +            self.assertLess(D('-inf'), F(-99999999999,123))
> +
> +            self.assertRaises(InvalidOperation, D('nan').__gt__, F(-9,123))
> +            self.assertIs(NotImplemented, F(-9,123).__lt__(D('nan')))
> +            self.assertNotEqual(D('nan'), F(-9,123))
> +            self.assertNotEqual(F(-9,123), D('nan'))
> +
>     def test_copy_and_deepcopy_methods(self):
> +        Decimal = self.decimal.Decimal
> +
>         d = Decimal('43.24')
>         c = copy.copy(d)
>         self.assertEqual(id(c), id(d))
> @@ -1307,6 +1720,10 @@
>         self.assertEqual(id(dc), id(d))
>>     def test_hash_method(self):
> +
> +        Decimal = self.decimal.Decimal
> +        localcontext = self.decimal.localcontext
> +
>         def hashit(d):
>             a = hash(d)
>             b = d.__hash__()
> @@ -1367,24 +1784,27 @@
>             d = Decimal(s)
>             self.assertEqual(hashit(f), hashit(d))
>> -        # check that the value of the hash doesn't depend on the
> -        # current context (issue #1757)
> -        c = getcontext()
> -        old_precision = c.prec
> -        x = Decimal("123456789.1")
> -
> -        c.prec = 6
> -        h1 = hashit(x)
> -        c.prec = 10
> -        h2 = hashit(x)
> -        c.prec = 16
> -        h3 = hashit(x)
> -
> -        self.assertEqual(h1, h2)
> -        self.assertEqual(h1, h3)
> -        c.prec = old_precision
> +        with localcontext() as c:
> +            # check that the value of the hash doesn't depend on the
> +            # current context (issue #1757)
> +            x = Decimal("123456789.1")
> +
> +            c.prec = 6
> +            h1 = hashit(x)
> +            c.prec = 10
> +            h2 = hashit(x)
> +            c.prec = 16
> +            h3 = hashit(x)
> +
> +            self.assertEqual(h1, h2)
> +            self.assertEqual(h1, h3)
> +
> +            c.prec = 10000
> +            x = 1100 ** 1248
> +            self.assertEqual(hashit(Decimal(x)), hashit(x))
>>     def test_min_and_max_methods(self):
> +        Decimal = self.decimal.Decimal
>>         d1 = Decimal('15.32')
>         d2 = Decimal('28.5')
> @@ -1404,6 +1824,8 @@
>         self.assertIs(max(d2,l1), d2)
>>     def test_as_nonzero(self):
> +        Decimal = self.decimal.Decimal
> +
>         #as false
>         self.assertFalse(Decimal(0))
>         #as true
> @@ -1411,6 +1833,7 @@
>>     def test_tostring_methods(self):
>         #Test str and repr methods.
> +        Decimal = self.decimal.Decimal
>>         d = Decimal('15.32')
>         self.assertEqual(str(d), '15.32')               # str
> @@ -1418,6 +1841,7 @@
>>     def test_tonum_methods(self):
>         #Test float and int methods.
> +        Decimal = self.decimal.Decimal
>>         d1 = Decimal('66')
>         d2 = Decimal('15.32')
> @@ -1440,6 +1864,7 @@
>             ('-11.0', -11),
>             ('0.0', 0),
>             ('-0E3', 0),
> +            ('89891211712379812736.1', 89891211712379812736),
>             ]
>         for d, i in test_pairs:
>             self.assertEqual(math.floor(Decimal(d)), i)
> @@ -1459,6 +1884,7 @@
>             ('-11.0', -11),
>             ('0.0', 0),
>             ('-0E3', 0),
> +            ('89891211712379812736.1', 89891211712379812737),
>             ]
>         for d, i in test_pairs:
>             self.assertEqual(math.ceil(Decimal(d)), i)
> @@ -1516,9 +1942,8 @@
>         for d, n, r in test_triples:
>             self.assertEqual(str(round(Decimal(d), n)), r)
>> -
> -
>     def test_eval_round_trip(self):
> +        Decimal = self.decimal.Decimal
>>         #with zero
>         d = Decimal( (0, (0,), 0) )
> @@ -1537,6 +1962,7 @@
>         self.assertEqual(d, eval(repr(d)))
>>     def test_as_tuple(self):
> +        Decimal = self.decimal.Decimal
>>         #with zero
>         d = Decimal(0)
> @@ -1550,7 +1976,7 @@
>         d = Decimal("-4.34913534E-17")
>         self.assertEqual(d.as_tuple(), (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
>> -        #inf
> +        # XXX non-compliant infinity payload.
>         d = Decimal("Infinity")
>         self.assertEqual(d.as_tuple(), (0, (0,), 'F') )
>> @@ -1570,14 +1996,2158 @@
>         d = Decimal( (1, (), 'n') )
>         self.assertEqual(d.as_tuple(), (1, (), 'n') )
>> -        #coefficient in infinity should be ignored
> -        d = Decimal( (0, (4, 5, 3, 4), 'F') )
> -        self.assertEqual(d.as_tuple(), (0, (0,), 'F'))
> -        d = Decimal( (1, (0, 2, 7, 1), 'F') )
> -        self.assertEqual(d.as_tuple(), (1, (0,), 'F'))
> -
> -    def test_immutability_operations(self):
> +        # XXX coefficient in infinity should raise an error
> +        if self.decimal == P:
> +            d = Decimal( (0, (4, 5, 3, 4), 'F') )
> +            self.assertEqual(d.as_tuple(), (0, (0,), 'F'))
> +            d = Decimal( (1, (0, 2, 7, 1), 'F') )
> +            self.assertEqual(d.as_tuple(), (1, (0,), 'F'))
> +
> +    def test_subclassing(self):
> +        # Different behaviours when subclassing Decimal
> +        Decimal = self.decimal.Decimal
> +
> +        class MyDecimal(Decimal):
> +            pass
> +
> +        d1 = MyDecimal(1)
> +        d2 = MyDecimal(2)
> +        d = d1 + d2
> +        self.assertIs(type(d), Decimal)
> +
> +        d = d1.max(d2)
> +        self.assertIs(type(d), Decimal)
> +
> +        d = copy.copy(d1)
> +        self.assertIs(type(d), MyDecimal)
> +        self.assertEqual(d, d1)
> +
> +        d = copy.deepcopy(d1)
> +        self.assertIs(type(d), MyDecimal)
> +        self.assertEqual(d, d1)
> +
> +    def test_implicit_context(self):
> +        Decimal = self.decimal.Decimal
> +        getcontext = self.decimal.getcontext
> +
> +        # Check results when context given implicitly.  (Issue 2478)
> +        c = getcontext()
> +        self.assertEqual(str(Decimal(0).sqrt()),
> +                         str(c.sqrt(Decimal(0))))
> +
> +    def test_conversions_from_int(self):
> +        # Check that methods taking a second Decimal argument will
> +        # always accept an integer in place of a Decimal.
> +        Decimal = self.decimal.Decimal
> +
> +        self.assertEqual(Decimal(4).compare(3),
> +                         Decimal(4).compare(Decimal(3)))
> +        self.assertEqual(Decimal(4).compare_signal(3),
> +                         Decimal(4).compare_signal(Decimal(3)))
> +        self.assertEqual(Decimal(4).compare_total(3),
> +                         Decimal(4).compare_total(Decimal(3)))
> +        self.assertEqual(Decimal(4).compare_total_mag(3),
> +                         Decimal(4).compare_total_mag(Decimal(3)))
> +        self.assertEqual(Decimal(10101).logical_and(1001),
> +                         Decimal(10101).logical_and(Decimal(1001)))
> +        self.assertEqual(Decimal(10101).logical_or(1001),
> +                         Decimal(10101).logical_or(Decimal(1001)))
> +        self.assertEqual(Decimal(10101).logical_xor(1001),
> +                         Decimal(10101).logical_xor(Decimal(1001)))
> +        self.assertEqual(Decimal(567).max(123),
> +                         Decimal(567).max(Decimal(123)))
> +        self.assertEqual(Decimal(567).max_mag(123),
> +                         Decimal(567).max_mag(Decimal(123)))
> +        self.assertEqual(Decimal(567).min(123),
> +                         Decimal(567).min(Decimal(123)))
> +        self.assertEqual(Decimal(567).min_mag(123),
> +                         Decimal(567).min_mag(Decimal(123)))
> +        self.assertEqual(Decimal(567).next_toward(123),
> +                         Decimal(567).next_toward(Decimal(123)))
> +        self.assertEqual(Decimal(1234).quantize(100),
> +                         Decimal(1234).quantize(Decimal(100)))
> +        self.assertEqual(Decimal(768).remainder_near(1234),
> +                         Decimal(768).remainder_near(Decimal(1234)))
> +        self.assertEqual(Decimal(123).rotate(1),
> +                         Decimal(123).rotate(Decimal(1)))
> +        self.assertEqual(Decimal(1234).same_quantum(1000),
> +                         Decimal(1234).same_quantum(Decimal(1000)))
> +        self.assertEqual(Decimal('9.123').scaleb(-100),
> +                         Decimal('9.123').scaleb(Decimal(-100)))
> +        self.assertEqual(Decimal(456).shift(-1),
> +                         Decimal(456).shift(Decimal(-1)))
> +
> +        self.assertEqual(Decimal(-12).fma(Decimal(45), 67),
> +                         Decimal(-12).fma(Decimal(45), Decimal(67)))
> +        self.assertEqual(Decimal(-12).fma(45, 67),
> +                         Decimal(-12).fma(Decimal(45), Decimal(67)))
> +        self.assertEqual(Decimal(-12).fma(45, Decimal(67)),
> +                         Decimal(-12).fma(Decimal(45), Decimal(67)))
> +
> +class CUsabilityTest(UsabilityTest):
> +    decimal = C
> +class PyUsabilityTest(UsabilityTest):
> +    decimal = P
> +
> +class PythonAPItests(unittest.TestCase):
> +
> +    def test_abc(self):
> +        Decimal = self.decimal.Decimal
> +
> +        self.assertTrue(issubclass(Decimal, numbers.Number))
> +        self.assertFalse(issubclass(Decimal, numbers.Real))
> +        self.assertIsInstance(Decimal(0), numbers.Number)
> +        self.assertNotIsInstance(Decimal(0), numbers.Real)
> +
> +    def test_pickle(self):
> +        Decimal = self.decimal.Decimal
> +
> +        savedecimal = sys.modules['decimal']
> +
> +        # Round trip
> +        sys.modules['decimal'] = self.decimal
> +        d = Decimal('-3.141590000')
> +        p = pickle.dumps(d)
> +        e = pickle.loads(p)
> +        self.assertEqual(d, e)
> +
> +        if C:
> +            # Test interchangeability
> +            x = C.Decimal('-3.123e81723')
> +            y = P.Decimal('-3.123e81723')
> +
> +            sys.modules['decimal'] = C
> +            sx = pickle.dumps(x)
> +            sys.modules['decimal'] = P
> +            r = pickle.loads(sx)
> +            self.assertIsInstance(r, P.Decimal)
> +            self.assertEqual(r, y)
> +
> +            sys.modules['decimal'] = P
> +            sy = pickle.dumps(y)
> +            sys.modules['decimal'] = C
> +            r = pickle.loads(sy)
> +            self.assertIsInstance(r, C.Decimal)
> +            self.assertEqual(r, x)
> +
> +        sys.modules['decimal'] = savedecimal
> +
> +    def test_int(self):
> +        Decimal = self.decimal.Decimal
> +        ROUND_DOWN = self.decimal.ROUND_DOWN
> +
> +        for x in range(-250, 250):
> +            s = '%0.2f' % (x / 100.0)
> +            # should work the same as for floats
> +            self.assertEqual(int(Decimal(s)), int(float(s)))
> +            # should work the same as to_integral in the ROUND_DOWN mode
> +            d = Decimal(s)
> +            r = d.to_integral(ROUND_DOWN)
> +            self.assertEqual(Decimal(int(d)), r)
> +
> +        self.assertRaises(ValueError, int, Decimal('-nan'))
> +        self.assertRaises(ValueError, int, Decimal('snan'))
> +        self.assertRaises(OverflowError, int, Decimal('inf'))
> +        self.assertRaises(OverflowError, int, Decimal('-inf'))
> +
> +    def test_trunc(self):
> +        Decimal = self.decimal.Decimal
> +        ROUND_DOWN = self.decimal.ROUND_DOWN
> +
> +        for x in range(-250, 250):
> +            s = '%0.2f' % (x / 100.0)
> +            # should work the same as for floats
> +            self.assertEqual(int(Decimal(s)), int(float(s)))
> +            # should work the same as to_integral in the ROUND_DOWN mode
> +            d = Decimal(s)
> +            r = d.to_integral(ROUND_DOWN)
> +            self.assertEqual(Decimal(math.trunc(d)), r)
> +
> +    def test_from_float(self):
> +
> +        Decimal = self.decimal.Decimal
> +
> +        class MyDecimal(Decimal):
> +            pass
> +
> +        self.assertTrue(issubclass(MyDecimal, Decimal))
> +
> +        r = MyDecimal.from_float(0.1)
> +        self.assertEqual(type(r), MyDecimal)
> +        self.assertEqual(str(r),
> +                '0.1000000000000000055511151231257827021181583404541015625')
> +        bigint = 12345678901234567890123456789
> +        self.assertEqual(MyDecimal.from_float(bigint), MyDecimal(bigint))
> +        self.assertTrue(MyDecimal.from_float(float('nan')).is_qnan())
> +        self.assertTrue(MyDecimal.from_float(float('inf')).is_infinite())
> +        self.assertTrue(MyDecimal.from_float(float('-inf')).is_infinite())
> +        self.assertEqual(str(MyDecimal.from_float(float('nan'))),
> +                         str(Decimal('NaN')))
> +        self.assertEqual(str(MyDecimal.from_float(float('inf'))),
> +                         str(Decimal('Infinity')))
> +        self.assertEqual(str(MyDecimal.from_float(float('-inf'))),
> +                         str(Decimal('-Infinity')))
> +        self.assertRaises(TypeError, MyDecimal.from_float, 'abc')
> +        for i in range(200):
> +            x = random.expovariate(0.01) * (random.random() * 2.0 - 1.0)
> +            self.assertEqual(x, float(MyDecimal.from_float(x))) # roundtrip
> +
> +    def test_create_decimal_from_float(self):
> +        Decimal = self.decimal.Decimal
> +        Context = self.decimal.Context
> +        ROUND_DOWN = self.decimal.ROUND_DOWN
> +        ROUND_UP = self.decimal.ROUND_UP
> +        Inexact = self.decimal.Inexact
> +
> +        context = Context(prec=5, rounding=ROUND_DOWN)
> +        self.assertEqual(
> +            context.create_decimal_from_float(math.pi),
> +            Decimal('3.1415')
> +        )
> +        context = Context(prec=5, rounding=ROUND_UP)
> +        self.assertEqual(
> +            context.create_decimal_from_float(math.pi),
> +            Decimal('3.1416')
> +        )
> +        context = Context(prec=5, traps=[Inexact])
> +        self.assertRaises(
> +            Inexact,
> +            context.create_decimal_from_float,
> +            math.pi
> +        )
> +        self.assertEqual(repr(context.create_decimal_from_float(-0.0)),
> +                         "Decimal('-0')")
> +        self.assertEqual(repr(context.create_decimal_from_float(1.0)),
> +                         "Decimal('1')")
> +        self.assertEqual(repr(context.create_decimal_from_float(10)),
> +                         "Decimal('10')")
> +
> +    def test_quantize(self):
> +        Decimal = self.decimal.Decimal
> +        Context = self.decimal.Context
> +        InvalidOperation = self.decimal.InvalidOperation
> +        ROUND_DOWN = self.decimal.ROUND_DOWN
> +
> +        c = Context(Emax=99999, Emin=-99999)
> +        self.assertEqual(
> +            Decimal('7.335').quantize(Decimal('.01')),
> +            Decimal('7.34')
> +        )
> +        self.assertEqual(
> +            Decimal('7.335').quantize(Decimal('.01'), rounding=ROUND_DOWN),
> +            Decimal('7.33')
> +        )
> +        self.assertRaises(
> +            InvalidOperation,
> +            Decimal("10e99999").quantize, Decimal('1e100000'), context=c
> +        )
> +
> +        c = Context()
> +        d = Decimal("0.871831e800")
> +        x = d.quantize(context=c, exp=Decimal("1e797"), rounding=ROUND_DOWN)
> +        self.assertEqual(x, Decimal('8.71E+799'))
> +
> +    def test_complex(self):
> +        Decimal = self.decimal.Decimal
> +
> +        x = Decimal("9.8182731e181273")
> +        self.assertEqual(x.real, x)
> +        self.assertEqual(x.imag, 0)
> +        self.assertEqual(x.conjugate(), x)
> +
> +        x = Decimal("1")
> +        self.assertEqual(complex(x), complex(float(1)))
> +
> +        self.assertRaises(AttributeError, setattr, x, 'real', 100)
> +        self.assertRaises(AttributeError, setattr, x, 'imag', 100)
> +        self.assertRaises(AttributeError, setattr, x, 'conjugate', 100)
> +        self.assertRaises(AttributeError, setattr, x, '__complex__', 100)
> +
> +    def test_named_parameters(self):
> +        D = self.decimal.Decimal
> +        Context = self.decimal.Context
> +        localcontext = self.decimal.localcontext
> + ...
>> [Message clipped]
> _______________________________________________
> Python-checkins mailing list
> Python-checkins at python.org
> http://mail.python.org/mailman/listinfo/python-checkins
>
-- 
Thanks,
Andrew Svetlov


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /