.. 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
# 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
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
>>> 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])
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)
>>> 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.
>>> 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:
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)
>>> 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
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
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:
+ *prec* is an integer in the range [:const:`1`, :const:`MAX_PREC`] that sets
+ the precision for arithmetic operations in the context.
- * :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)
+ 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
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.
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:
.. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+.. _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:
Infinity / Infinity
x % 0
Infinity % x
- x._rescale( non-integer )
sqrt(-x) and x > 0
0 ** 0
x ** (non-integer)
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)
InvalidOperation
Rounded
Subnormal
+ FloatOperation
.. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
.. _decimal-notes:
Floating Point Notes
the following calculation returns a value equal to zero:
>>> 1 / Decimal('Infinity')
- Decimal('0E-1000000026')
+ Decimal('0E-1000026')
.. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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.
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:
(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
------------
#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
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')
# 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
import copy as _copy
import math as _math
import numbers as _numbers
+import sys
try:
from collections import namedtuple as _namedtuple
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):
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,
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
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
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
"""
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
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()
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:
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 = []
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
>>> 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):
>>> 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):
+Normal
+Infinity
- >>> c = Context(ExtendedContext)
+ >>> c = ExtendedContext.copy()
>>> c.Emin = -999
>>> c.Emax = 999
>>> c.number_class(Decimal('Infinity'))
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
prec=28, rounding=ROUND_HALF_EVEN,
traps=[DivisionByZero, Overflow, InvalidOperation],
flags=[],
- Emax=999999999,
- Emin=-999999999,
+ Emax=999999,
+ Emin=-999999,
capitals=1,
clamp=0
)
# 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
# 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
#=======================================================================
# 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
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:
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
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
-# Useful Test Constant
-Signals = tuple(getcontext().flags.keys())
+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 = {
+ 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__':
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',
- )
+# 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 DecimalTest(unittest.TestCase):
- """Class which tests the Decimal class against the test cases.
- Changed for unittest.
- """
+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
#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)
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()
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]
+ theirexceptions = [self.ErrorNames[x.lower()] for x in exceptions]
- for exception in Signals:
+ for exception in Signals[self.decimal]:
self.context.traps[exception] = 1 #Catch these bugs...
for exception in theirexceptions:
self.context.traps[exception] = 0
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:
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)
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:
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:
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)
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')
#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) )
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)) )
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),
self.assertEqual(x, float(Decimal(x))) # roundtrip
def test_explicit_context_create_decimal(self):
+ Decimal = self.decimal.Decimal
+ InvalidOperation = self.decimal.InvalidOperation
+ Rounded = self.decimal.Rounded
- nc = copy.copy(getcontext())
+ nc = copy.copy(self.decimal.getcontext())
nc.prec = 3
# empty
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',
for input, expected in test_values.items():
self.assertEqual(str(Decimal(input)), expected)
+class CExplicitConstructionTest(ExplicitConstructionTest):
+ decimal = C
+class PyExplicitConstructionTest(ExplicitConstructionTest):
+ decimal = P
-class DecimalImplicitConstructionTest(unittest.TestCase):
+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):
self.assertEqual(eval('Decimal(10)' + sym + 'E()'),
'10' + rop + 'str')
+class CImplicitConstructionTest(ImplicitConstructionTest):
+ decimal = C
+class PyImplicitConstructionTest(ImplicitConstructionTest):
+ decimal = P
-class DecimalFormatTest(unittest.TestCase):
+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'),
('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'),
# 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')
self.assertEqual(get_fmt(123456, crazy, '012n'), '00-01-2345-6')
self.assertEqual(get_fmt(123456, crazy, '013n'), '000-01-2345-6')
+ # 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 DecimalArithmeticOperatorsTest(unittest.TestCase):
+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')
self.assertEqual(d1, Decimal('16.1'))
def test_subtraction(self):
+ Decimal = self.decimal.Decimal
d1 = Decimal('-11.1')
d2 = Decimal('22.2')
self.assertEqual(d1, Decimal('-38.3'))
def test_multiplication(self):
+ Decimal = self.decimal.Decimal
d1 = Decimal('-5')
d2 = Decimal('3')
self.assertEqual(d1, Decimal('-75'))
def test_division(self):
+ Decimal = self.decimal.Decimal
d1 = Decimal('-5')
d2 = Decimal('2')
self.assertEqual(d1, Decimal('-0.625'))
def test_floor_division(self):
+ Decimal = self.decimal.Decimal
d1 = Decimal('5')
d2 = Decimal('2')
self.assertEqual(d1, Decimal('1'))
def test_powering(self):
+ Decimal = self.decimal.Decimal
d1 = Decimal('5')
d2 = Decimal('2')
self.assertEqual(d1, Decimal('390625'))
def test_module(self):
+ Decimal = self.decimal.Decimal
d1 = Decimal('5')
d2 = Decimal('2')
self.assertEqual(d1, Decimal('1'))
def test_floor_div_module(self):
+ Decimal = self.decimal.Decimal
d1 = Decimal('5')
d2 = Decimal('2')
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
# 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')
self.assertRaises(InvalidOperation, op, x, y)
def test_copy_sign(self):
- d = Decimal(1).copy_sign(Decimal(-2))
+ 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.synchro.wait()
- test2 = d1/d3
+
cls.finish1.set()
+ cls.synchro.wait()
- cls.assertEqual(test1, Decimal('0.3333333333333333333333333333'))
- cls.assertEqual(test2, Decimal('0.3333333333333333333333333333'))
+ test2 = d1/d3
+ 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'))
- return
-
-class DecimalUseOfContextTest(unittest.TestCase):
- '''Unit tests for Use of Context cases in Decimal.'''
+ cls.assertFalse(thiscontext.traps[Underflow])
+ cls.assertTrue(thiscontext.flags[Inexact])
+ for sig in Overflow, Underflow, DivisionByZero, InvalidOperation:
+ cls.assertFalse(thiscontext.flags[sig])
+ return
- 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()
self.finish1.wait()
self.finish2.wait()
- return
- if threading is None:
- del test_threading
+ for sig in Signals[self.decimal]:
+ self.assertFalse(DefaultContext.flags[sig])
+
+ DefaultContext.prec = save_prec
+ DefaultContext.Emax = save_emax
+ DefaultContext.Emin = save_emin
+ return
+@unittest.skipUnless(threading, 'threading required')
+class CThreadingTest(ThreadingTest):
+ decimal = C
+@unittest.skipUnless(threading, 'threading required')
+class PyThreadingTest(ThreadingTest):
+ decimal = P
-class DecimalUsabilityTest(unittest.TestCase):
+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')
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)
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))
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__()
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")
+ 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)
- 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)
- self.assertEqual(h1, h2)
- self.assertEqual(h1, h3)
- c.prec = old_precision
+ 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')
self.assertIs(max(d2,l1), d2)
def test_as_nonzero(self):
+ Decimal = self.decimal.Decimal
+
#as false
self.assertFalse(Decimal(0))
#as true
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
def test_tonum_methods(self):
#Test float and int methods.
+ Decimal = self.decimal.Decimal
d1 = Decimal('66')
d2 = Decimal('15.32')
('-11.0', -11),
('0.0', 0),
('-0E3', 0),
+ ('89891211712379812736.1', 89891211712379812736),
]
for d, i in test_pairs:
self.assertEqual(math.floor(Decimal(d)), i)
('-11.0', -11),
('0.0', 0),
('-0E3', 0),
+ ('89891211712379812736.1', 89891211712379812737),
]
for d, i in test_pairs:
self.assertEqual(math.ceil(Decimal(d)), i)
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) )
self.assertEqual(d, eval(repr(d)))
def test_as_tuple(self):
+ Decimal = self.decimal.Decimal
#with zero
d = Decimal(0)
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') )
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):
- # Do operations and check that it didn't change change internal objects.
-
- d1 = Decimal('-25e55')
- b1 = Decimal('-25e55')
- d2 = Decimal('33e+33')
- b2 = Decimal('33e+33')
-
- def checkSameDec(operation, useOther=False):
- if useOther:
- eval("d1." + operation + "(d2)")
- self.assertEqual(d1._sign, b1._sign)
- self.assertEqual(d1._int, b1._int)
- self.assertEqual(d1._exp, b1._exp)
- self.assertEqual(d2._sign, b2._sign)
- self.assertEqual(d2._int, b2._int)
- self.assertEqual(d2._exp, b2._exp)
- else:
- eval("d1." + operation + "()")
- self.assertEqual(d1._sign, b1._sign)
- self.assertEqual(d1._int, b1._int)
- self.assertEqual(d1._exp, b1._exp)
- return
-
- Decimal(d1)
- self.assertEqual(d1._sign, b1._sign)
- self.assertEqual(d1._int, b1._int)
- self.assertEqual(d1._exp, b1._exp)
-
- checkSameDec("__abs__")
- checkSameDec("__add__", True)
- checkSameDec("__divmod__", True)
- checkSameDec("__eq__", True)
- checkSameDec("__ne__", True)
- checkSameDec("__le__", True)
- checkSameDec("__lt__", True)
- checkSameDec("__ge__", True)
- checkSameDec("__gt__", True)
- checkSameDec("__float__")
- checkSameDec("__floordiv__", True)
- checkSameDec("__hash__")
- checkSameDec("__int__")
- checkSameDec("__trunc__")
- checkSameDec("__mod__", True)
- checkSameDec("__mul__", True)
- checkSameDec("__neg__")
- checkSameDec("__bool__")
- checkSameDec("__pos__")
- checkSameDec("__pow__", True)
- checkSameDec("__radd__", True)
- checkSameDec("__rdivmod__", True)
- checkSameDec("__repr__")
- checkSameDec("__rfloordiv__", True)
- checkSameDec("__rmod__", True)
- checkSameDec("__rmul__", True)
- checkSameDec("__rpow__", True)
- checkSameDec("__rsub__", True)
- checkSameDec("__str__")
- checkSameDec("__sub__", True)
- checkSameDec("__truediv__", True)
- checkSameDec("adjusted")
- checkSameDec("as_tuple")
- checkSameDec("compare", True)
- checkSameDec("max", True)
- checkSameDec("min", True)
- checkSameDec("normalize")
- checkSameDec("quantize", True)
- checkSameDec("remainder_near", True)
- checkSameDec("same_quantum", True)
- checkSameDec("sqrt")
- checkSameDec("to_eng_string")
- checkSameDec("to_integral")
+ # 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
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()),
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),
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 DecimalPythonAPItests(unittest.TestCase):
+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.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
def test_from_float(self):
- class MyDecimal(Decimal):
+ 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),
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),
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
+ InvalidOperation = self.decimal.InvalidOperation
+ Overflow = self.decimal.Overflow
+
+ xc = Context()
+ xc.prec = 1
+ xc.Emax = 1
+ xc.Emin = -1
+
+ with localcontext() as c:
+ c.clear_flags()
+
+ self.assertEqual(D(9, xc), 9)
+ self.assertEqual(D(9, context=xc), 9)
+ self.assertEqual(D(context=xc, value=9), 9)
+ self.assertEqual(D(context=xc), 0)
+ xc.clear_flags()
+ self.assertRaises(InvalidOperation, D, "xyz", context=xc)
+ self.assertTrue(xc.flags[InvalidOperation])
+ self.assertFalse(c.flags[InvalidOperation])
+
+ xc.clear_flags()
+ self.assertEqual(D(2).exp(context=xc), 7)
+ self.assertRaises(Overflow, D(8).exp, context=xc)
+ self.assertTrue(xc.flags[Overflow])
+ self.assertFalse(c.flags[Overflow])
+
+ xc.clear_flags()
+ self.assertEqual(D(2).ln(context=xc), D('0.7'))
+ self.assertRaises(InvalidOperation, D(-1).ln, context=xc)
+ self.assertTrue(xc.flags[InvalidOperation])
+ self.assertFalse(c.flags[InvalidOperation])
+
+ self.assertEqual(D(0).log10(context=xc), D('-inf'))
+ self.assertEqual(D(-1).next_minus(context=xc), -2)
+ self.assertEqual(D(-1).next_plus(context=xc), D('-0.9'))
+ self.assertEqual(D("9.73").normalize(context=xc), D('1E+1'))
+ self.assertEqual(D("9999").to_integral(context=xc), 9999)
+ self.assertEqual(D("-2000").to_integral_exact(context=xc), -2000)
+ self.assertEqual(D("123").to_integral_value(context=xc), 123)
+ self.assertEqual(D("0.0625").sqrt(context=xc), D('0.2'))
+
+ self.assertEqual(D("0.0625").compare(context=xc, other=3), -1)
+ xc.clear_flags()
+ self.assertRaises(InvalidOperation,
+ D("0").compare_signal, D('nan'), context=xc)
+ self.assertTrue(xc.flags[InvalidOperation])
+ self.assertFalse(c.flags[InvalidOperation])
+ self.assertEqual(D("0.01").max(D('0.0101'), context=xc), D('0.0'))
+ self.assertEqual(D("0.01").max(D('0.0101'), context=xc), D('0.0'))
+ self.assertEqual(D("0.2").max_mag(D('-0.3'), context=xc),
+ D('-0.3'))
+ self.assertEqual(D("0.02").min(D('-0.03'), context=xc), D('-0.0'))
+ self.assertEqual(D("0.02").min_mag(D('-0.03'), context=xc),
+ D('0.0'))
+ self.assertEqual(D("0.2").next_toward(D('-1'), context=xc), D('0.1'))
+ xc.clear_flags()
+ self.assertRaises(InvalidOperation,
+ D("0.2").quantize, D('1e10'), context=xc)
+ self.assertTrue(xc.flags[InvalidOperation])
+ self.assertFalse(c.flags[InvalidOperation])
+ self.assertEqual(D("9.99").remainder_near(D('1.5'), context=xc),
+ D('-0.5'))
+
+ self.assertEqual(D("9.9").fma(third=D('0.9'), context=xc, other=7),
+ D('7E+1'))
+
+ self.assertRaises(TypeError, D(1).is_canonical, context=xc)
+ self.assertRaises(TypeError, D(1).is_finite, context=xc)
+ self.assertRaises(TypeError, D(1).is_infinite, context=xc)
+ self.assertRaises(TypeError, D(1).is_nan, context=xc)
+ self.assertRaises(TypeError, D(1).is_qnan, context=xc)
+ self.assertRaises(TypeError, D(1).is_snan, context=xc)
+ self.assertRaises(TypeError, D(1).is_signed, context=xc)
+ self.assertRaises(TypeError, D(1).is_zero, context=xc)
+
+ self.assertFalse(D("0.01").is_normal(context=xc))
+ self.assertTrue(D("0.01").is_subnormal(context=xc))
+
+ self.assertRaises(TypeError, D(1).adjusted, context=xc)
+ self.assertRaises(TypeError, D(1).conjugate, context=xc)
+ self.assertRaises(TypeError, D(1).radix, context=xc)
+
+ self.assertEqual(D(-111).logb(context=xc), 2)
+ self.assertEqual(D(0).logical_invert(context=xc), 1)
+ self.assertEqual(D('0.01').number_class(context=xc), '+Subnormal')
+ self.assertEqual(D('0.21').to_eng_string(context=xc), '0.21')
+
+ self.assertEqual(D('11').logical_and(D('10'), context=xc), 0)
+ self.assertEqual(D('11').logical_or(D('10'), context=xc), 1)
+ self.assertEqual(D('01').logical_xor(D('10'), context=xc), 1)
+ self.assertEqual(D('23').rotate(1, context=xc), 3)
+ self.assertEqual(D('23').rotate(1, context=xc), 3)
+ xc.clear_flags()
+ self.assertRaises(Overflow,
+ D('23').scaleb, 1, context=xc)
+ self.assertTrue(xc.flags[Overflow])
+ self.assertFalse(c.flags[Overflow])
+ self.assertEqual(D('23').shift(-1, context=xc), 0)
+
+ self.assertRaises(TypeError, D.from_float, 1.1, context=xc)
+ self.assertRaises(TypeError, D(0).as_tuple, context=xc)
+
+ if (self.decimal == C):
+ self.assertRaises(TypeError, D(1).canonical, context=xc)
+ self.assertEqual(D("-1").copy_abs(context=xc), 1)
+ self.assertEqual(D("1").copy_negate(context=xc), -1)
+ else:
+ self.assertEqual(D(1).canonical(context=xc), 1)
+ self.assertRaises(TypeError, D("-1").copy_abs, context=xc)
+ self.assertRaises(TypeError, D("-1").copy_negate, context=xc)
+
+class CPythonAPItests(PythonAPItests):
+ decimal = C
+class PyPythonAPItests(PythonAPItests):
+ decimal = P
+
class ContextAPItests(unittest.TestCase):
def test_pickle(self):
+
+ Context = self.decimal.Context
+
+ savedecimal = sys.modules['decimal']
+
+ # Round trip
+ sys.modules['decimal'] = self.decimal
c = Context()
e = pickle.loads(pickle.dumps(c))
- for k in vars(c):
- v1 = vars(c)[k]
- v2 = vars(e)[k]
- self.assertEqual(v1, v2)
+
+ self.assertEqual(c.prec, e.prec)
+ self.assertEqual(c.Emin, e.Emin)
+ self.assertEqual(c.Emax, e.Emax)
+ self.assertEqual(c.rounding, e.rounding)
+ self.assertEqual(c.capitals, e.capitals)
+ self.assertEqual(c.clamp, e.clamp)
+ self.assertEqual(c.flags, e.flags)
+ self.assertEqual(c.traps, e.traps)
+
+ # Test interchangeability
+ combinations = [(C, P), (P, C)] if C else [(P, P)]
+ for dumper, loader in combinations:
+ for ri, _ in enumerate(RoundingModes[dumper]):
+ for fi, _ in enumerate(OrderedSignals[dumper]):
+ for ti, _ in enumerate(OrderedSignals[dumper]):
+
+ prec = random.randrange(1, 100)
+ emin = random.randrange(-100, 0)
+ emax = random.randrange(1, 100)
+ caps = random.randrange(2)
+ clamp = random.randrange(2)
+
+ # One module dumps
+ sys.modules['decimal'] = dumper
+ c = dumper.Context(
+ prec=prec, Emin=emin, Emax=emax,
+ rounding=RoundingModes[dumper][ri],
+ capitals=caps, clamp=clamp,
+ flags=OrderedSignals[dumper][:fi],
+ traps=OrderedSignals[dumper][:ti]
+ )
+ s = pickle.dumps(c)
+
+ # The other module loads
+ sys.modules['decimal'] = loader
+ d = pickle.loads(s)
+ self.assertIsInstance(d, loader.Context)
+
+ self.assertEqual(d.prec, prec)
+ self.assertEqual(d.Emin, emin)
+ self.assertEqual(d.Emax, emax)
+ self.assertEqual(d.rounding, RoundingModes[loader][ri])
+ self.assertEqual(d.capitals, caps)
+ self.assertEqual(d.clamp, clamp)
+ assert_signals(self, d, 'flags', OrderedSignals[loader][:fi])
+ assert_signals(self, d, 'traps', OrderedSignals[loader][:ti])
+
+ sys.modules['decimal'] = savedecimal
def test_equality_with_other_types(self):
+ Decimal = self.decimal.Decimal
+
self.assertIn(Decimal(10), ['a', 1.0, Decimal(10), (1,2), {}])
self.assertNotIn(Decimal(10), ['a', 1.0, (1,2), {}])
def test_copy(self):
# All copies should be deep
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.copy()
self.assertNotEqual(id(c), id(d))
self.assertNotEqual(id(c.flags), id(d.flags))
self.assertNotEqual(id(c.traps), id(d.traps))
+ k1 = set(c.flags.keys())
+ k2 = set(d.flags.keys())
+ self.assertEqual(k1, k2)
+ self.assertEqual(c.flags, d.flags)
def test__clamp(self):
# In Python 3.2, the private attribute `_clamp` was made
# only, the attribute should be gettable/settable via both
# `clamp` and `_clamp`; in Python 3.3, `_clamp` should be
# removed.
+ Context = self.decimal.Context
c = Context()
- with self.assertRaises(AttributeError):
- clamp_value = c._clamp
+ self.assertRaises(AttributeError, getattr, c, '_clamp')
def test_abs(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.abs(Decimal(-1))
self.assertEqual(c.abs(-1), d)
self.assertRaises(TypeError, c.abs, '-1')
def test_add(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.add(Decimal(1), Decimal(1))
self.assertEqual(c.add(1, 1), d)
self.assertRaises(TypeError, c.add, 1, '1')
def test_compare(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.compare(Decimal(1), Decimal(1))
self.assertEqual(c.compare(1, 1), d)
self.assertRaises(TypeError, c.compare, 1, '1')
def test_compare_signal(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.compare_signal(Decimal(1), Decimal(1))
self.assertEqual(c.compare_signal(1, 1), d)
self.assertRaises(TypeError, c.compare_signal, 1, '1')
def test_compare_total(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.compare_total(Decimal(1), Decimal(1))
self.assertEqual(c.compare_total(1, 1), d)
self.assertRaises(TypeError, c.compare_total, 1, '1')
def test_compare_total_mag(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.compare_total_mag(Decimal(1), Decimal(1))
self.assertEqual(c.compare_total_mag(1, 1), d)
self.assertRaises(TypeError, c.compare_total_mag, 1, '1')
def test_copy_abs(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.copy_abs(Decimal(-1))
self.assertEqual(c.copy_abs(-1), d)
self.assertRaises(TypeError, c.copy_abs, '-1')
def test_copy_decimal(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.copy_decimal(Decimal(-1))
self.assertEqual(c.copy_decimal(-1), d)
self.assertRaises(TypeError, c.copy_decimal, '-1')
def test_copy_negate(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.copy_negate(Decimal(-1))
self.assertEqual(c.copy_negate(-1), d)
self.assertRaises(TypeError, c.copy_negate, '-1')
def test_copy_sign(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.copy_sign(Decimal(1), Decimal(-2))
self.assertEqual(c.copy_sign(1, -2), d)
self.assertRaises(TypeError, c.copy_sign, 1, '-2')
def test_divide(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.divide(Decimal(1), Decimal(2))
self.assertEqual(c.divide(1, 2), d)
self.assertRaises(TypeError, c.divide, 1, '2')
def test_divide_int(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.divide_int(Decimal(1), Decimal(2))
self.assertEqual(c.divide_int(1, 2), d)
self.assertRaises(TypeError, c.divide_int, 1, '2')
def test_divmod(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.divmod(Decimal(1), Decimal(2))
self.assertEqual(c.divmod(1, 2), d)
self.assertRaises(TypeError, c.divmod, 1, '2')
def test_exp(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.exp(Decimal(10))
self.assertEqual(c.exp(10), d)
self.assertRaises(TypeError, c.exp, '10')
def test_fma(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.fma(Decimal(2), Decimal(3), Decimal(4))
self.assertEqual(c.fma(2, 3, 4), d)
Decimal('snan'), 1.222)
def test_is_finite(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.is_finite(Decimal(10))
self.assertEqual(c.is_finite(10), d)
self.assertRaises(TypeError, c.is_finite, '10')
def test_is_infinite(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.is_infinite(Decimal(10))
self.assertEqual(c.is_infinite(10), d)
self.assertRaises(TypeError, c.is_infinite, '10')
def test_is_nan(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.is_nan(Decimal(10))
self.assertEqual(c.is_nan(10), d)
self.assertRaises(TypeError, c.is_nan, '10')
def test_is_normal(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.is_normal(Decimal(10))
self.assertEqual(c.is_normal(10), d)
self.assertRaises(TypeError, c.is_normal, '10')
def test_is_qnan(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.is_qnan(Decimal(10))
self.assertEqual(c.is_qnan(10), d)
self.assertRaises(TypeError, c.is_qnan, '10')
def test_is_signed(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.is_signed(Decimal(10))
self.assertEqual(c.is_signed(10), d)
self.assertRaises(TypeError, c.is_signed, '10')
def test_is_snan(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.is_snan(Decimal(10))
self.assertEqual(c.is_snan(10), d)
self.assertRaises(TypeError, c.is_snan, '10')
def test_is_subnormal(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.is_subnormal(Decimal(10))
self.assertEqual(c.is_subnormal(10), d)
self.assertRaises(TypeError, c.is_subnormal, '10')
def test_is_zero(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.is_zero(Decimal(10))
self.assertEqual(c.is_zero(10), d)
self.assertRaises(TypeError, c.is_zero, '10')
def test_ln(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.ln(Decimal(10))
self.assertEqual(c.ln(10), d)
self.assertRaises(TypeError, c.ln, '10')
def test_log10(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.log10(Decimal(10))
self.assertEqual(c.log10(10), d)
self.assertRaises(TypeError, c.log10, '10')
def test_logb(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.logb(Decimal(10))
self.assertEqual(c.logb(10), d)
self.assertRaises(TypeError, c.logb, '10')
def test_logical_and(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.logical_and(Decimal(1), Decimal(1))
self.assertEqual(c.logical_and(1, 1), d)
self.assertRaises(TypeError, c.logical_and, 1, '1')
def test_logical_invert(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.logical_invert(Decimal(1000))
self.assertEqual(c.logical_invert(1000), d)
self.assertRaises(TypeError, c.logical_invert, '1000')
def test_logical_or(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.logical_or(Decimal(1), Decimal(1))
self.assertEqual(c.logical_or(1, 1), d)
self.assertRaises(TypeError, c.logical_or, 1, '1')
def test_logical_xor(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.logical_xor(Decimal(1), Decimal(1))
self.assertEqual(c.logical_xor(1, 1), d)
self.assertRaises(TypeError, c.logical_xor, 1, '1')
def test_max(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.max(Decimal(1), Decimal(2))
self.assertEqual(c.max(1, 2), d)
self.assertRaises(TypeError, c.max, 1, '2')
def test_max_mag(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.max_mag(Decimal(1), Decimal(2))
self.assertEqual(c.max_mag(1, 2), d)
self.assertRaises(TypeError, c.max_mag, 1, '2')
def test_min(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.min(Decimal(1), Decimal(2))
self.assertEqual(c.min(1, 2), d)
self.assertRaises(TypeError, c.min, 1, '2')
def test_min_mag(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.min_mag(Decimal(1), Decimal(2))
self.assertEqual(c.min_mag(1, 2), d)
self.assertRaises(TypeError, c.min_mag, 1, '2')
def test_minus(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.minus(Decimal(10))
self.assertEqual(c.minus(10), d)
self.assertRaises(TypeError, c.minus, '10')
def test_multiply(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.multiply(Decimal(1), Decimal(2))
self.assertEqual(c.multiply(1, 2), d)
self.assertRaises(TypeError, c.multiply, 1, '2')
def test_next_minus(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.next_minus(Decimal(10))
self.assertEqual(c.next_minus(10), d)
self.assertRaises(TypeError, c.next_minus, '10')
def test_next_plus(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.next_plus(Decimal(10))
self.assertEqual(c.next_plus(10), d)
self.assertRaises(TypeError, c.next_plus, '10')
def test_next_toward(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.next_toward(Decimal(1), Decimal(2))
self.assertEqual(c.next_toward(1, 2), d)
self.assertRaises(TypeError, c.next_toward, 1, '2')
def test_normalize(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.normalize(Decimal(10))
self.assertEqual(c.normalize(10), d)
self.assertRaises(TypeError, c.normalize, '10')
def test_number_class(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
self.assertEqual(c.number_class(123), c.number_class(Decimal(123)))
self.assertEqual(c.number_class(0), c.number_class(Decimal(0)))
self.assertEqual(c.number_class(-45), c.number_class(Decimal(-45)))
- def test_power(self):
- c = Context()
- d = c.power(Decimal(1), Decimal(4), Decimal(2))
- self.assertEqual(c.power(1, 4, 2), d)
- self.assertEqual(c.power(Decimal(1), 4, 2), d)
- self.assertEqual(c.power(1, Decimal(4), 2), d)
- self.assertEqual(c.power(1, 4, Decimal(2)), d)
- self.assertEqual(c.power(Decimal(1), Decimal(4), 2), d)
- self.assertRaises(TypeError, c.power, '1', 4, 2)
- self.assertRaises(TypeError, c.power, 1, '4', 2)
- self.assertRaises(TypeError, c.power, 1, 4, '2')
-
def test_plus(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.plus(Decimal(10))
self.assertEqual(c.plus(10), d)
self.assertRaises(TypeError, c.plus, '10')
+ def test_power(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
+ c = Context()
+ d = c.power(Decimal(1), Decimal(4))
+ self.assertEqual(c.power(1, 4), d)
+ self.assertEqual(c.power(Decimal(1), 4), d)
+ self.assertEqual(c.power(1, Decimal(4)), d)
+ self.assertEqual(c.power(Decimal(1), Decimal(4)), d)
+ self.assertRaises(TypeError, c.power, '1', 4)
+ self.assertRaises(TypeError, c.power, 1, '4')
+ self.assertEqual(c.power(modulo=5, b=8, a=2), 1)
+
def test_quantize(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.quantize(Decimal(1), Decimal(2))
self.assertEqual(c.quantize(1, 2), d)
self.assertRaises(TypeError, c.quantize, 1, '2')
def test_remainder(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.remainder(Decimal(1), Decimal(2))
self.assertEqual(c.remainder(1, 2), d)
self.assertRaises(TypeError, c.remainder, 1, '2')
def test_remainder_near(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.remainder_near(Decimal(1), Decimal(2))
self.assertEqual(c.remainder_near(1, 2), d)
self.assertRaises(TypeError, c.remainder_near, 1, '2')
def test_rotate(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.rotate(Decimal(1), Decimal(2))
self.assertEqual(c.rotate(1, 2), d)
self.assertRaises(TypeError, c.rotate, 1, '2')
def test_sqrt(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.sqrt(Decimal(10))
self.assertEqual(c.sqrt(10), d)
self.assertRaises(TypeError, c.sqrt, '10')
def test_same_quantum(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.same_quantum(Decimal(1), Decimal(2))
self.assertEqual(c.same_quantum(1, 2), d)
self.assertRaises(TypeError, c.same_quantum, 1, '2')
def test_scaleb(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.scaleb(Decimal(1), Decimal(2))
self.assertEqual(c.scaleb(1, 2), d)
self.assertRaises(TypeError, c.scaleb, 1, '2')
def test_shift(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.shift(Decimal(1), Decimal(2))
self.assertEqual(c.shift(1, 2), d)
self.assertRaises(TypeError, c.shift, 1, '2')
def test_subtract(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.subtract(Decimal(1), Decimal(2))
self.assertEqual(c.subtract(1, 2), d)
self.assertRaises(TypeError, c.subtract, 1, '2')
def test_to_eng_string(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.to_eng_string(Decimal(10))
self.assertEqual(c.to_eng_string(10), d)
self.assertRaises(TypeError, c.to_eng_string, '10')
def test_to_sci_string(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.to_sci_string(Decimal(10))
self.assertEqual(c.to_sci_string(10), d)
self.assertRaises(TypeError, c.to_sci_string, '10')
def test_to_integral_exact(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.to_integral_exact(Decimal(10))
self.assertEqual(c.to_integral_exact(10), d)
self.assertRaises(TypeError, c.to_integral_exact, '10')
def test_to_integral_value(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+
c = Context()
d = c.to_integral_value(Decimal(10))
self.assertEqual(c.to_integral_value(10), d)
self.assertRaises(TypeError, c.to_integral_value, '10')
+ self.assertRaises(TypeError, c.to_integral_value, 10, 'x')
+
+class CContextAPItests(ContextAPItests):
+ decimal = C
+class PyContextAPItests(ContextAPItests):
+ decimal = P
-class WithStatementTest(unittest.TestCase):
+class ContextWithStatement(unittest.TestCase):
# Can't do these as docstrings until Python 2.6
# as doctest can't handle __future__ statements
def test_localcontext(self):
# Use a copy of the current context in the block
+ getcontext = self.decimal.getcontext
+ localcontext = self.decimal.localcontext
+
orig_ctx = getcontext()
with localcontext() as enter_ctx:
set_ctx = getcontext()
def test_localcontextarg(self):
# Use a copy of the supplied context in the block
+ Context = self.decimal.Context
+ getcontext = self.decimal.getcontext
+ localcontext = self.decimal.localcontext
+
+ localcontext = self.decimal.localcontext
orig_ctx = getcontext()
new_ctx = Context(prec=42)
with localcontext(new_ctx) as enter_ctx:
self.assertIsNot(new_ctx, set_ctx, 'did not copy the context')
self.assertIs(set_ctx, enter_ctx, '__enter__ returned wrong context')
+ def test_nested_with_statements(self):
+ # Use a copy of the supplied context in the block
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+ getcontext = self.decimal.getcontext
+ localcontext = self.decimal.localcontext
+ Clamped = self.decimal.Clamped
+ Overflow = self.decimal.Overflow
+
+ orig_ctx = getcontext()
+ orig_ctx.clear_flags()
+ new_ctx = Context(Emax=384)
+ with localcontext() as c1:
+ self.assertEqual(c1.flags, orig_ctx.flags)
+ self.assertEqual(c1.traps, orig_ctx.traps)
+ c1.traps[Clamped] = True
+ c1.Emin = -383
+ self.assertNotEqual(orig_ctx.Emin, -383)
+ self.assertRaises(Clamped, c1.create_decimal, '0e-999')
+ self.assertTrue(c1.flags[Clamped])
+ with localcontext(new_ctx) as c2:
+ self.assertEqual(c2.flags, new_ctx.flags)
+ self.assertEqual(c2.traps, new_ctx.traps)
+ self.assertRaises(Overflow, c2.power, Decimal('3.4e200'), 2)
+ self.assertFalse(c2.flags[Clamped])
+ self.assertTrue(c2.flags[Overflow])
+ del c2
+ self.assertFalse(c1.flags[Overflow])
+ del c1
+ self.assertNotEqual(orig_ctx.Emin, -383)
+ self.assertFalse(orig_ctx.flags[Clamped])
+ self.assertFalse(orig_ctx.flags[Overflow])
+ self.assertFalse(new_ctx.flags[Clamped])
+ self.assertFalse(new_ctx.flags[Overflow])
+
+ def test_with_statements_gc1(self):
+ localcontext = self.decimal.localcontext
+
+ with localcontext() as c1:
+ del c1
+ with localcontext() as c2:
+ del c2
+ with localcontext() as c3:
+ del c3
+ with localcontext() as c4:
+ del c4
+
+ def test_with_statements_gc2(self):
+ localcontext = self.decimal.localcontext
+
+ with localcontext() as c1:
+ with localcontext(c1) as c2:
+ del c1
+ with localcontext(c2) as c3:
+ del c2
+ with localcontext(c3) as c4:
+ del c3
+ del c4
+
+ def test_with_statements_gc3(self):
+ Context = self.decimal.Context
+ localcontext = self.decimal.localcontext
+ getcontext = self.decimal.getcontext
+ setcontext = self.decimal.setcontext
+
+ with localcontext() as c1:
+ del c1
+ n1 = Context(prec=1)
+ setcontext(n1)
+ with localcontext(n1) as c2:
+ del n1
+ self.assertEqual(c2.prec, 1)
+ del c2
+ n2 = Context(prec=2)
+ setcontext(n2)
+ del n2
+ self.assertEqual(getcontext().prec, 2)
+ n3 = Context(prec=3)
+ setcontext(n3)
+ self.assertEqual(getcontext().prec, 3)
+ with localcontext(n3) as c3:
+ del n3
+ self.assertEqual(c3.prec, 3)
+ del c3
+ n4 = Context(prec=4)
+ setcontext(n4)
+ del n4
+ self.assertEqual(getcontext().prec, 4)
+ with localcontext() as c4:
+ self.assertEqual(c4.prec, 4)
+ del c4
+
+class CContextWithStatement(ContextWithStatement):
+ decimal = C
+class PyContextWithStatement(ContextWithStatement):
+ decimal = P
+
class ContextFlags(unittest.TestCase):
+
def test_flags_irrelevant(self):
# check that the result (numeric result + flags raised) of an
# arithmetic operation doesn't depend on the current flags
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+ Inexact = self.decimal.Inexact
+ Rounded = self.decimal.Rounded
+ Underflow = self.decimal.Underflow
+ Clamped = self.decimal.Clamped
+ Subnormal = self.decimal.Subnormal
+ ROUND_HALF_EVEN = self.decimal.ROUND_HALF_EVEN
+
+ def raise_error(context, flag):
+ if self.decimal == C:
+ context.flags[flag] = True
+ if context.traps[flag]:
+ raise flag
+ else:
+ context._raise_error(flag)
- context = Context(prec=9, Emin = -999999999, Emax = 999999999,
- rounding=ROUND_HALF_EVEN, traps=[], flags=[])
+ context = Context(prec=9, Emin = -425000000, Emax = 425000000,
+ rounding=ROUND_HALF_EVEN, traps=[], flags=[])
# operations that raise various flags, in the form (function, arglist)
operations = [
- (context._apply, [Decimal("100E-1000000009")]),
+ (context._apply, [Decimal("100E-425000010")]),
(context.sqrt, [Decimal(2)]),
(context.add, [Decimal("1.23456789"), Decimal("9.87654321")]),
(context.multiply, [Decimal("1.23456789"), Decimal("9.87654321")]),
# set flags, before calling operation
context.clear_flags()
for flag in extra_flags:
- context._raise_error(flag)
+ raise_error(context, flag)
new_ans = fn(*args)
# flags that we expect to be set after the operation
"operation raises different flags depending on flags set: " +
"expected %s, got %s" % (expected_flags, new_flags))
+ def test_flag_comparisons(self):
+ Context = self.decimal.Context
+ Inexact = self.decimal.Inexact
+ Rounded = self.decimal.Rounded
+
+ c = Context()
+
+ # Valid SignalDict
+ self.assertNotEqual(c.flags, c.traps)
+ self.assertNotEqual(c.traps, c.flags)
+
+ c.flags = c.traps
+ self.assertEqual(c.flags, c.traps)
+ self.assertEqual(c.traps, c.flags)
+
+ c.flags[Rounded] = True
+ c.traps = c.flags
+ self.assertEqual(c.flags, c.traps)
+ self.assertEqual(c.traps, c.flags)
+
+ d = {}
+ d.update(c.flags)
+ self.assertEqual(d, c.flags)
+ self.assertEqual(c.flags, d)
+
+ d[Inexact] = True
+ self.assertNotEqual(d, c.flags)
+ self.assertNotEqual(c.flags, d)
+
+ # Invalid SignalDict
+ d = {Inexact:False}
+ self.assertNotEqual(d, c.flags)
+ self.assertNotEqual(c.flags, d)
+
+ d = ["xyz"]
+ self.assertNotEqual(d, c.flags)
+ self.assertNotEqual(c.flags, d)
+
+ @requires_IEEE_754
+ def test_float_operation(self):
+ Decimal = self.decimal.Decimal
+ FloatOperation = self.decimal.FloatOperation
+ localcontext = self.decimal.localcontext
+
+ with localcontext() as c:
+ ##### trap is off by default
+ self.assertFalse(c.traps[FloatOperation])
+
+ # implicit conversion sets the flag
+ c.clear_flags()
+ self.assertEqual(Decimal(7.5), 7.5)
+ self.assertTrue(c.flags[FloatOperation])
+
+ c.clear_flags()
+ self.assertEqual(c.create_decimal(7.5), 7.5)
+ self.assertTrue(c.flags[FloatOperation])
+
+ # explicit conversion does not set the flag
+ c.clear_flags()
+ x = Decimal.from_float(7.5)
+ self.assertFalse(c.flags[FloatOperation])
+ # comparison sets the flag
+ self.assertEqual(x, 7.5)
+ self.assertTrue(c.flags[FloatOperation])
+
+ c.clear_flags()
+ x = c.create_decimal_from_float(7.5)
+ self.assertFalse(c.flags[FloatOperation])
+ self.assertEqual(x, 7.5)
+ self.assertTrue(c.flags[FloatOperation])
+
+ ##### set the trap
+ c.traps[FloatOperation] = True
+
+ # implicit conversion raises
+ c.clear_flags()
+ self.assertRaises(FloatOperation, Decimal, 7.5)
+ self.assertTrue(c.flags[FloatOperation])
+
+ c.clear_flags()
+ self.assertRaises(FloatOperation, c.create_decimal, 7.5)
+ self.assertTrue(c.flags[FloatOperation])
+
+ # explicit conversion is silent
+ c.clear_flags()
+ x = Decimal.from_float(7.5)
+ self.assertFalse(c.flags[FloatOperation])
+
+ c.clear_flags()
+ x = c.create_decimal_from_float(7.5)
+ self.assertFalse(c.flags[FloatOperation])
+
+ def test_float_comparison(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+ FloatOperation = self.decimal.FloatOperation
+ localcontext = self.decimal.localcontext
+
+ def assert_attr(a, b, attr, context, signal=None):
+ context.clear_flags()
+ f = getattr(a, attr)
+ if signal == FloatOperation:
+ self.assertRaises(signal, f, b)
+ else:
+ self.assertIs(f(b), True)
+ self.assertTrue(context.flags[FloatOperation])
+
+ small_d = Decimal('0.25')
+ big_d = Decimal('3.0')
+ small_f = 0.25
+ big_f = 3.0
+
+ zero_d = Decimal('0.0')
+ neg_zero_d = Decimal('-0.0')
+ zero_f = 0.0
+ neg_zero_f = -0.0
+
+ inf_d = Decimal('Infinity')
+ neg_inf_d = Decimal('-Infinity')
+ inf_f = float('inf')
+ neg_inf_f = float('-inf')
+
+ def doit(c, signal=None):
+ # Order
+ for attr in '__lt__', '__le__':
+ assert_attr(small_d, big_f, attr, c, signal)
+
+ for attr in '__gt__', '__ge__':
+ assert_attr(big_d, small_f, attr, c, signal)
+
+ # Equality
+ assert_attr(small_d, small_f, '__eq__', c, None)
+
+ assert_attr(neg_zero_d, neg_zero_f, '__eq__', c, None)
+ assert_attr(neg_zero_d, zero_f, '__eq__', c, None)
+
+ assert_attr(zero_d, neg_zero_f, '__eq__', c, None)
+ assert_attr(zero_d, zero_f, '__eq__', c, None)
+
+ assert_attr(neg_inf_d, neg_inf_f, '__eq__', c, None)
+ assert_attr(inf_d, inf_f, '__eq__', c, None)
+
+ # Inequality
+ assert_attr(small_d, big_f, '__ne__', c, None)
+
+ assert_attr(Decimal('0.1'), 0.1, '__ne__', c, None)
+
+ assert_attr(neg_inf_d, inf_f, '__ne__', c, None)
+ assert_attr(inf_d, neg_inf_f, '__ne__', c, None)
+
+ assert_attr(Decimal('NaN'), float('nan'), '__ne__', c, None)
+
+ def test_containers(c, signal=None):
+ c.clear_flags()
+ s = set([100.0, Decimal('100.0')])
+ self.assertEqual(len(s), 1)
+ self.assertTrue(c.flags[FloatOperation])
+
+ c.clear_flags()
+ if signal:
+ self.assertRaises(signal, sorted, [1.0, Decimal('10.0')])
+ else:
+ s = sorted([10.0, Decimal('10.0')])
+ self.assertTrue(c.flags[FloatOperation])
+
+ c.clear_flags()
+ b = 10.0 in [Decimal('10.0'), 1.0]
+ self.assertTrue(c.flags[FloatOperation])
+
+ c.clear_flags()
+ b = 10.0 in {Decimal('10.0'):'a', 1.0:'b'}
+ self.assertTrue(c.flags[FloatOperation])
+
+ nc = Context()
+ with localcontext(nc) as c:
+ self.assertFalse(c.traps[FloatOperation])
+ doit(c, signal=None)
+ test_containers(c, signal=None)
+
+ c.traps[FloatOperation] = True
+ doit(c, signal=FloatOperation)
+ test_containers(c, signal=FloatOperation)
+
+ def test_float_operation_default(self):
+ Decimal = self.decimal.Decimal
+ Context = self.decimal.Context
+ Inexact = self.decimal.Inexact
+ FloatOperation= self.decimal.FloatOperation
+
+ context = Context()
+ self.assertFalse(context.flags[FloatOperation])
+ self.assertFalse(context.traps[FloatOperation])
+
+ context.clear_traps()
+ context.traps[Inexact] = True
+ context.traps[FloatOperation] = True
+ self.assertTrue(context.traps[FloatOperation])
+ self.assertTrue(context.traps[Inexact])
+
+class CContextFlags(ContextFlags):
+ decimal = C
+class PyContextFlags(ContextFlags):
+ decimal = P
+
+class SpecialContexts(unittest.TestCase):
+ """Test the context templates."""
+
+ def test_context_templates(self):
+ BasicContext = self.decimal.BasicContext
+ ExtendedContext = self.decimal.ExtendedContext
+ getcontext = self.decimal.getcontext
+ setcontext = self.decimal.setcontext
+ InvalidOperation = self.decimal.InvalidOperation
+ DivisionByZero = self.decimal.DivisionByZero
+ Overflow = self.decimal.Overflow
+ Underflow = self.decimal.Underflow
+ Clamped = self.decimal.Clamped
+
+ assert_signals(self, BasicContext, 'traps',
+ [InvalidOperation, DivisionByZero, Overflow, Underflow, Clamped]
+ )
+
+ savecontext = getcontext().copy()
+ basic_context_prec = BasicContext.prec
+ extended_context_prec = ExtendedContext.prec
+
+ ex = None
+ try:
+ BasicContext.prec = ExtendedContext.prec = 441
+ for template in BasicContext, ExtendedContext:
+ setcontext(template)
+ c = getcontext()
+ self.assertIsNot(c, template)
+ self.assertEqual(c.prec, 441)
+ except Exception as e:
+ ex = e.__class__
+ finally:
+ BasicContext.prec = basic_context_prec
+ ExtendedContext.prec = extended_context_prec
+ setcontext(savecontext)
+ if ex:
+ raise ex
+
+ def test_default_context(self):
+ DefaultContext = self.decimal.DefaultContext
+ BasicContext = self.decimal.BasicContext
+ ExtendedContext = self.decimal.ExtendedContext
+ getcontext = self.decimal.getcontext
+ setcontext = self.decimal.setcontext
+ InvalidOperation = self.decimal.InvalidOperation
+ DivisionByZero = self.decimal.DivisionByZero
+ Overflow = self.decimal.Overflow
+
+ self.assertEqual(BasicContext.prec, 9)
+ self.assertEqual(ExtendedContext.prec, 9)
+
+ assert_signals(self, DefaultContext, 'traps',
+ [InvalidOperation, DivisionByZero, Overflow]
+ )
+
+ savecontext = getcontext().copy()
+ default_context_prec = DefaultContext.prec
+
+ ex = None
+ try:
+ c = getcontext()
+ saveprec = c.prec
+
+ DefaultContext.prec = 961
+ c = getcontext()
+ self.assertEqual(c.prec, saveprec)
+
+ setcontext(DefaultContext)
+ c = getcontext()
+ self.assertIsNot(c, DefaultContext)
+ self.assertEqual(c.prec, 961)
+ except Exception as e:
+ ex = e.__class__
+ finally:
+ DefaultContext.prec = default_context_prec
+ setcontext(savecontext)
+ if ex:
+ raise ex
+
+class CSpecialContexts(SpecialContexts):
+ decimal = C
+class PySpecialContexts(SpecialContexts):
+ decimal = P
+
+class ContextInputValidation(unittest.TestCase):
+
+ def test_invalid_context(self):
+ Context = self.decimal.Context
+ DefaultContext = self.decimal.DefaultContext
+
+ c = DefaultContext.copy()
+
+ # prec, Emax
+ for attr in ['prec', 'Emax']:
+ setattr(c, attr, 999999)
+ self.assertEqual(getattr(c, attr), 999999)
+ self.assertRaises(ValueError, setattr, c, attr, -1)
+ self.assertRaises(TypeError, setattr, c, attr, 'xyz')
+
+ # Emin
+ setattr(c, 'Emin', -999999)
+ self.assertEqual(getattr(c, 'Emin'), -999999)
+ self.assertRaises(ValueError, setattr, c, 'Emin', 1)
+ self.assertRaises(TypeError, setattr, c, 'Emin', (1,2,3))
+
+ # rounding: always raise TypeError in order to get consistent
+ # exceptions across implementations. In decimal, rounding
+ # modes are strings, in _decimal they are integers. The idea
+ # is to view rounding as an abstract type and not mind the
+ # implementation details.
+ # Hence, a user should view the rounding modes as if they
+ # had been defined in a language that supports abstract
+ # data types, e.g. ocaml:
+ #
+ # type rounding = ROUND_DOWN | ROUND_HALF_UP | ... ;;
+ #
+ self.assertRaises(TypeError, setattr, c, 'rounding', -1)
+ self.assertRaises(TypeError, setattr, c, 'rounding', 9)
+ self.assertRaises(TypeError, setattr, c, 'rounding', 1.0)
+ self.assertRaises(TypeError, setattr, c, 'rounding', 'xyz')
+
+ # capitals, clamp
+ for attr in ['capitals', 'clamp']:
+ self.assertRaises(ValueError, setattr, c, attr, -1)
+ self.assertRaises(ValueError, setattr, c, attr, 2)
+ self.assertRaises(TypeError, setattr, c, attr, [1,2,3])
+
+ # Invalid attribute
+ self.assertRaises(AttributeError, setattr, c, 'emax', 100)
+
+ # Invalid signal dict
+ self.assertRaises(TypeError, setattr, c, 'flags', [])
+ self.assertRaises(KeyError, setattr, c, 'flags', {})
+ self.assertRaises(KeyError, setattr, c, 'traps',
+ {'InvalidOperation':0})
+
+ # Attributes cannot be deleted
+ for attr in ['prec', 'Emax', 'Emin', 'rounding', 'capitals', 'clamp',
+ 'flags', 'traps']:
+ self.assertRaises(AttributeError, c.__delattr__, attr)
+
+ # Invalid attributes
+ self.assertRaises(TypeError, getattr, c, 9)
+ self.assertRaises(TypeError, setattr, c, 9)
+
+ # Invalid values in constructor
+ self.assertRaises(TypeError, Context, rounding=999999)
+ self.assertRaises(TypeError, Context, rounding='xyz')
+ self.assertRaises(ValueError, Context, clamp=2)
+ self.assertRaises(ValueError, Context, capitals=-1)
+ self.assertRaises(KeyError, Context, flags=["P"])
+ self.assertRaises(KeyError, Context, traps=["Q"])
+
+ # Type error in conversion
+ self.assertRaises(TypeError, Context, flags=(0,1))
+ self.assertRaises(TypeError, Context, traps=(1,0))
+
+class CContextInputValidation(ContextInputValidation):
+ decimal = C
+class PyContextInputValidation(ContextInputValidation):
+ decimal = P
+
+class ContextSubclassing(unittest.TestCase):
+
+ def test_context_subclassing(self):
+ decimal = self.decimal
+ Decimal = decimal.Decimal
+ Context = decimal.Context
+ ROUND_HALF_EVEN = decimal.ROUND_HALF_EVEN
+ ROUND_DOWN = decimal.ROUND_DOWN
+ Clamped = decimal.Clamped
+ DivisionByZero = decimal.DivisionByZero
+ Inexact = decimal.Inexact
+ Overflow = decimal.Overflow
+ Rounded = decimal.Rounded
+ Subnormal = decimal.Subnormal
+ Underflow = decimal.Underflow
+ InvalidOperation = decimal.InvalidOperation
+
+ class MyContext(Context):
+ def __init__(self, prec=None, rounding=None, Emin=None, Emax=None,
+ capitals=None, clamp=None, flags=None,
+ traps=None):
+ Context.__init__(self)
+ if prec is not None:
+ self.prec = prec
+ if rounding is not None:
+ self.rounding = rounding
+ if Emin is not None:
+ self.Emin = Emin
+ if Emax is not None:
+ self.Emax = Emax
+ if capitals is not None:
+ self.capitals = capitals
+ if clamp is not None:
+ self.clamp = clamp
+ if flags is not None:
+ if isinstance(flags, list):
+ flags = {v:(v in flags) for v in OrderedSignals[decimal] + flags}
+ self.flags = flags
+ if traps is not None:
+ if isinstance(traps, list):
+ traps = {v:(v in traps) for v in OrderedSignals[decimal] + traps}
+ self.traps = traps
+
+ c = Context()
+ d = MyContext()
+ for attr in ('prec', 'rounding', 'Emin', 'Emax', 'capitals', 'clamp',
+ 'flags', 'traps'):
+ self.assertEqual(getattr(c, attr), getattr(d, attr))
+
+ # prec
+ self.assertRaises(ValueError, MyContext, **{'prec':-1})
+ c = MyContext(prec=1)
+ self.assertEqual(c.prec, 1)
+ self.assertRaises(InvalidOperation, c.quantize, Decimal('9e2'), 0)
+
+ # rounding
+ self.assertRaises(TypeError, MyContext, **{'rounding':'XYZ'})
+ c = MyContext(rounding=ROUND_DOWN, prec=1)
+ self.assertEqual(c.rounding, ROUND_DOWN)
+ self.assertEqual(c.plus(Decimal('9.9')), 9)
+
+ # Emin
+ self.assertRaises(ValueError, MyContext, **{'Emin':5})
+ c = MyContext(Emin=-1, prec=1)
+ self.assertEqual(c.Emin, -1)
+ x = c.add(Decimal('1e-99'), Decimal('2.234e-2000'))
+ self.assertEqual(x, Decimal('0.0'))
+ for signal in (Inexact, Underflow, Subnormal, Rounded, Clamped):
+ self.assertTrue(c.flags[signal])
+
+ # Emax
+ self.assertRaises(ValueError, MyContext, **{'Emax':-1})
+ c = MyContext(Emax=1, prec=1)
+ self.assertEqual(c.Emax, 1)
+ self.assertRaises(Overflow, c.add, Decimal('1e99'), Decimal('2.234e2000'))
+ if self.decimal == C:
+ for signal in (Inexact, Overflow, Rounded):
+ self.assertTrue(c.flags[signal])
+
+ # capitals
+ self.assertRaises(ValueError, MyContext, **{'capitals':-1})
+ c = MyContext(capitals=0)
+ self.assertEqual(c.capitals, 0)
+ x = c.create_decimal('1E222')
+ self.assertEqual(c.to_sci_string(x), '1e+222')
+
+ # clamp
+ self.assertRaises(ValueError, MyContext, **{'clamp':2})
+ c = MyContext(clamp=1, Emax=99)
+ self.assertEqual(c.clamp, 1)
+ x = c.plus(Decimal('1e99'))
+ self.assertEqual(str(x), '1.000000000000000000000000000E+99')
+
+ # flags
+ self.assertRaises(TypeError, MyContext, **{'flags':'XYZ'})
+ c = MyContext(flags=[Rounded, DivisionByZero])
+ for signal in (Rounded, DivisionByZero):
+ self.assertTrue(c.flags[signal])
+ c.clear_flags()
+ for signal in OrderedSignals[decimal]:
+ self.assertFalse(c.flags[signal])
+
+ # traps
+ self.assertRaises(TypeError, MyContext, **{'traps':'XYZ'})
+ c = MyContext(traps=[Rounded, DivisionByZero])
+ for signal in (Rounded, DivisionByZero):
+ self.assertTrue(c.traps[signal])
+ c.clear_traps()
+ for signal in OrderedSignals[decimal]:
+ self.assertFalse(c.traps[signal])
+
+class CContextSubclassing(ContextSubclassing):
+ decimal = C
+class PyContextSubclassing(ContextSubclassing):
+ decimal = P
+
+@skip_if_extra_functionality
+class CheckAttributes(unittest.TestCase):
+
+ def test_module_attributes(self):
+
+ # Architecture dependent context limits
+ self.assertEqual(C.MAX_PREC, P.MAX_PREC)
+ self.assertEqual(C.MAX_EMAX, P.MAX_EMAX)
+ self.assertEqual(C.MIN_EMIN, P.MIN_EMIN)
+ self.assertEqual(C.MIN_ETINY, P.MIN_ETINY)
+
+ self.assertTrue(C.HAVE_THREADS is True or C.HAVE_THREADS is False)
+ self.assertTrue(P.HAVE_THREADS is True or P.HAVE_THREADS is False)
+
+ self.assertEqual(C.__version__, P.__version__)
+
+ x = dir(C)
+ y = [s for s in dir(P) if '__' in s or not s.startswith('_')]
+ self.assertEqual(set(x) - set(y), {'MallocError'})
+
+ def test_context_attributes(self):
+
+ x = [s for s in dir(C.Context()) if '__' in s or not s.startswith('_')]
+ y = [s for s in dir(P.Context()) if '__' in s or not s.startswith('_')]
+ self.assertEqual(set(x) - set(y), set())
+
+ def test_decimal_attributes(self):
+
+ x = [s for s in dir(C.Decimal(9)) if '__' in s or not s.startswith('_')]
+ y = [s for s in dir(C.Decimal(9)) if '__' in s or not s.startswith('_')]
+ self.assertEqual(set(x) - set(y), set())
+
+class Coverage(unittest.TestCase):
+
+ def test_adjusted(self):
+ Decimal = self.decimal.Decimal
+
+ self.assertEqual(Decimal('1234e9999').adjusted(), 10002)
+ # XXX raise?
+ self.assertEqual(Decimal('nan').adjusted(), 0)
+ self.assertEqual(Decimal('inf').adjusted(), 0)
+
+ def test_canonical(self):
+ Decimal = self.decimal.Decimal
+ getcontext = self.decimal.getcontext
+
+ x = Decimal(9).canonical()
+ self.assertEqual(x, 9)
+
+ c = getcontext()
+ x = c.canonical(Decimal(9))
+ self.assertEqual(x, 9)
+
+ def test_context_repr(self):
+ c = self.decimal.DefaultContext.copy()
+
+ c.prec = 425000000
+ c.Emax = 425000000
+ c.Emin = -425000000
+ c.rounding = self.decimal.ROUND_HALF_DOWN
+ c.capitals = 0
+ c.clamp = 1
+ for sig in OrderedSignals[self.decimal]:
+ c.flags[sig] = False
+ c.traps[sig] = False
+
+ s = c.__repr__()
+ t = "Context(prec=425000000, rounding=ROUND_HALF_DOWN, " \
+ "Emin=-425000000, Emax=425000000, capitals=0, clamp=1, " \
+ "flags=[], traps=[])"
+ self.assertEqual(s, t)
+
+ def test_implicit_context(self):
+ Decimal = self.decimal.Decimal
+ localcontext = self.decimal.localcontext
+
+ with localcontext() as c:
+ c.prec = 1
+ c.Emax = 1
+ c.Emin = -1
+
+ # abs
+ self.assertEqual(abs(Decimal("-10")), 10)
+ # add
+ self.assertEqual(Decimal("7") + 1, 8)
+ # divide
+ self.assertEqual(Decimal("10") / 5, 2)
+ # divide_int
+ self.assertEqual(Decimal("10") // 7, 1)
+ # fma
+ self.assertEqual(Decimal("1.2").fma(Decimal("0.01"), 1), 1)
+ self.assertIs(Decimal("NaN").fma(7, 1).is_nan(), True)
+ # three arg power
+ self.assertEqual(pow(Decimal(10), 2, 7), 2)
+ # exp
+ self.assertEqual(Decimal("1.01").exp(), 3)
+ # is_normal
+ self.assertIs(Decimal("0.01").is_normal(), False)
+ # is_subnormal
+ self.assertIs(Decimal("0.01").is_subnormal(), True)
+ # ln
+ self.assertEqual(Decimal("20").ln(), 3)
+ # log10
+ self.assertEqual(Decimal("20").log10(), 1)
+ # logb
+ self.assertEqual(Decimal("580").logb(), 2)
+ # logical_invert
+ self.assertEqual(Decimal("10").logical_invert(), 1)
+ # minus
+ self.assertEqual(-Decimal("-10"), 10)
+ # multiply
+ self.assertEqual(Decimal("2") * 4, 8)
+ # next_minus
+ self.assertEqual(Decimal("10").next_minus(), 9)
+ # next_plus
+ self.assertEqual(Decimal("10").next_plus(), Decimal('2E+1'))
+ # normalize
+ self.assertEqual(Decimal("-10").normalize(), Decimal('-1E+1'))
+ # number_class
+ self.assertEqual(Decimal("10").number_class(), '+Normal')
+ # plus
+ self.assertEqual(+Decimal("-1"), -1)
+ # remainder
+ self.assertEqual(Decimal("10") % 7, 3)
+ # subtract
+ self.assertEqual(Decimal("10") - 7, 3)
+ # to_integral_exact
+ self.assertEqual(Decimal("1.12345").to_integral_exact(), 1)
+
+ # Boolean functions
+ self.assertTrue(Decimal("1").is_canonical())
+ self.assertTrue(Decimal("1").is_finite())
+ self.assertTrue(Decimal("1").is_finite())
+ self.assertTrue(Decimal("snan").is_snan())
+ self.assertTrue(Decimal("-1").is_signed())
+ self.assertTrue(Decimal("0").is_zero())
+ self.assertTrue(Decimal("0").is_zero())
+
+ # Copy
+ with localcontext() as c:
+ c.prec = 10000
+ x = 1228 ** 1523
+ y = -Decimal(x)
+
+ z = y.copy_abs()
+ self.assertEqual(z, x)
+
+ z = y.copy_negate()
+ self.assertEqual(z, x)
+
+ z = y.copy_sign(Decimal(1))
+ self.assertEqual(z, x)
+
+ def test_divmod(self):
+ Decimal = self.decimal.Decimal
+ localcontext = self.decimal.localcontext
+ InvalidOperation = self.decimal.InvalidOperation
+ DivisionByZero = self.decimal.DivisionByZero
+
+ with localcontext() as c:
+ q, r = divmod(Decimal("10912837129"), 1001)
+ self.assertEqual(q, Decimal('10901935'))
+ self.assertEqual(r, Decimal('194'))
+
+ q, r = divmod(Decimal("NaN"), 7)
+ self.assertTrue(q.is_nan() and r.is_nan())
+
+ c.traps[InvalidOperation] = False
+ q, r = divmod(Decimal("NaN"), 7)
+ self.assertTrue(q.is_nan() and r.is_nan())
+
+ c.traps[InvalidOperation] = False
+ c.clear_flags()
+ q, r = divmod(Decimal("inf"), Decimal("inf"))
+ self.assertTrue(q.is_nan() and r.is_nan())
+ self.assertTrue(c.flags[InvalidOperation])
+
+ c.clear_flags()
+ q, r = divmod(Decimal("inf"), 101)
+ self.assertTrue(q.is_infinite() and r.is_nan())
+ self.assertTrue(c.flags[InvalidOperation])
+
+ c.clear_flags()
+ q, r = divmod(Decimal(0), 0)
+ self.assertTrue(q.is_nan() and r.is_nan())
+ self.assertTrue(c.flags[InvalidOperation])
+
+ c.traps[DivisionByZero] = False
+ c.clear_flags()
+ q, r = divmod(Decimal(11), 0)
+ self.assertTrue(q.is_infinite() and r.is_nan())
+ self.assertTrue(c.flags[InvalidOperation] and
+ c.flags[DivisionByZero])
+
+ def test_power(self):
+ Decimal = self.decimal.Decimal
+ localcontext = self.decimal.localcontext
+ Overflow = self.decimal.Overflow
+ Rounded = self.decimal.Rounded
+
+ with localcontext() as c:
+ c.prec = 3
+ c.clear_flags()
+ self.assertEqual(Decimal("1.0") ** 100, Decimal('1.00'))
+ self.assertTrue(c.flags[Rounded])
+
+ c.prec = 1
+ c.Emax = 1
+ c.Emin = -1
+ c.clear_flags()
+ c.traps[Overflow] = False
+ self.assertEqual(Decimal(10000) ** Decimal("0.5"), Decimal('inf'))
+ self.assertTrue(c.flags[Overflow])
+
+ def test_quantize(self):
+ Decimal = self.decimal.Decimal
+ localcontext = self.decimal.localcontext
+ InvalidOperation = self.decimal.InvalidOperation
+
+ with localcontext() as c:
+ c.prec = 1
+ c.Emax = 1
+ c.Emin = -1
+ c.traps[InvalidOperation] = False
+ x = Decimal(99).quantize(Decimal("1e1"))
+ self.assertTrue(x.is_nan())
+
+ def test_radix(self):
+ Decimal = self.decimal.Decimal
+ getcontext = self.decimal.getcontext
+
+ c = getcontext()
+ self.assertEqual(Decimal("1").radix(), 10)
+ self.assertEqual(c.radix(), 10)
+
+ def test_rop(self):
+ Decimal = self.decimal.Decimal
+
+ for attr in ('__radd__', '__rsub__', '__rmul__', '__rtruediv__',
+ '__rdivmod__', '__rmod__', '__rfloordiv__', '__rpow__'):
+ self.assertIs(getattr(Decimal("1"), attr)("xyz"), NotImplemented)
+
+ def test_round(self):
+ # Python3 behavior: round() returns Decimal
+ Decimal = self.decimal.Decimal
+ getcontext = self.decimal.getcontext
+
+ c = getcontext()
+ c.prec = 28
+
+ self.assertEqual(str(Decimal("9.99").__round__()), "10")
+ self.assertEqual(str(Decimal("9.99e-5").__round__()), "0")
+ self.assertEqual(str(Decimal("1.23456789").__round__(5)), "1.23457")
+ self.assertEqual(str(Decimal("1.2345").__round__(10)), "1.2345000000")
+ self.assertEqual(str(Decimal("1.2345").__round__(-10)), "0E+10")
+
+ self.assertRaises(TypeError, Decimal("1.23").__round__, "5")
+ self.assertRaises(TypeError, Decimal("1.23").__round__, 5, 8)
+
+ def test_create_decimal(self):
+ c = self.decimal.Context()
+ self.assertRaises(ValueError, c.create_decimal, ["%"])
+
+ def test_int(self):
+ Decimal = self.decimal.Decimal
+ localcontext = self.decimal.localcontext
+
+ with localcontext() as c:
+ c.prec = 9999
+ x = Decimal(1221**1271) / 10**3923
+ self.assertEqual(int(x), 1)
+ self.assertEqual(x.to_integral(), 2)
+
+ def test_copy(self):
+ Context = self.decimal.Context
+
+ c = Context()
+ c.prec = 10000
+ x = -(1172 ** 1712)
+
+ y = c.copy_abs(x)
+ self.assertEqual(y, -x)
+
+ y = c.copy_negate(x)
+ self.assertEqual(y, -x)
+
+ y = c.copy_sign(x, 1)
+ self.assertEqual(y, -x)
+
+class CCoverage(Coverage):
+ decimal = C
+class PyCoverage(Coverage):
+ decimal = P
+
+class PyFunctionality(unittest.TestCase):
+ """Extra functionality in decimal.py"""
+
+ def test_py_quantize_watchexp(self):
+ # watchexp functionality
+ Decimal = P.Decimal
+ localcontext = P.localcontext
+
+ with localcontext() as c:
+ c.prec = 1
+ c.Emax = 1
+ c.Emin = -1
+ x = Decimal(99999).quantize(Decimal("1e3"), watchexp=False)
+ self.assertEqual(x, Decimal('1.00E+5'))
+
+ def test_py_alternate_formatting(self):
+ # triples giving a format, a Decimal, and the expected result
+ Decimal = P.Decimal
+ localcontext = P.localcontext
+
+ test_values = [
+ # 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)
+
+class PyWhitebox(unittest.TestCase):
+ """White box testing for decimal.py"""
+
+ def test_py_exact_power(self):
+ # Rarely exercised lines in _power_exact.
+ Decimal = P.Decimal
+ localcontext = P.localcontext
+
+ with localcontext() as c:
+ c.prec = 8
+ x = Decimal(2**16) ** Decimal("-0.5")
+ self.assertEqual(x, Decimal('0.00390625'))
+
+ x = Decimal(2**16) ** Decimal("-0.6")
+ self.assertEqual(x, Decimal('0.0012885819'))
+
+ x = Decimal("256e7") ** Decimal("-0.5")
+
+ x = Decimal(152587890625) ** Decimal('-0.0625')
+ self.assertEqual(x, Decimal("0.2"))
+
+ x = Decimal("152587890625e7") ** Decimal('-0.0625')
+
+ x = Decimal(5**2659) ** Decimal('-0.0625')
+
+ c.prec = 1
+ x = Decimal("152587890625") ** Decimal('-0.5')
+ c.prec = 201
+ x = Decimal(2**578) ** Decimal("-0.5")
+
+ def test_py_immutability_operations(self):
+ # Do operations and check that it didn't change change internal objects.
+ Decimal = P.Decimal
+ DefaultContext = P.DefaultContext
+ setcontext = P.setcontext
+
+ c = DefaultContext.copy()
+ c.traps = dict((s, 0) for s in OrderedSignals[P])
+ setcontext(c)
+
+ d1 = Decimal('-25e55')
+ b1 = Decimal('-25e55')
+ d2 = Decimal('33e+33')
+ b2 = Decimal('33e+33')
+
+ def checkSameDec(operation, useOther=False):
+ if useOther:
+ eval("d1." + operation + "(d2)")
+ self.assertEqual(d1._sign, b1._sign)
+ self.assertEqual(d1._int, b1._int)
+ self.assertEqual(d1._exp, b1._exp)
+ self.assertEqual(d2._sign, b2._sign)
+ self.assertEqual(d2._int, b2._int)
+ self.assertEqual(d2._exp, b2._exp)
+ else:
+ eval("d1." + operation + "()")
+ self.assertEqual(d1._sign, b1._sign)
+ self.assertEqual(d1._int, b1._int)
+ self.assertEqual(d1._exp, b1._exp)
+ return
+
+ Decimal(d1)
+ self.assertEqual(d1._sign, b1._sign)
+ self.assertEqual(d1._int, b1._int)
+ self.assertEqual(d1._exp, b1._exp)
+
+ checkSameDec("__abs__")
+ checkSameDec("__add__", True)
+ checkSameDec("__divmod__", True)
+ checkSameDec("__eq__", True)
+ checkSameDec("__ne__", True)
+ checkSameDec("__le__", True)
+ checkSameDec("__lt__", True)
+ checkSameDec("__ge__", True)
+ checkSameDec("__gt__", True)
+ checkSameDec("__float__")
+ checkSameDec("__floordiv__", True)
+ checkSameDec("__hash__")
+ checkSameDec("__int__")
+ checkSameDec("__trunc__")
+ checkSameDec("__mod__", True)
+ checkSameDec("__mul__", True)
+ checkSameDec("__neg__")
+ checkSameDec("__bool__")
+ checkSameDec("__pos__")
+ checkSameDec("__pow__", True)
+ checkSameDec("__radd__", True)
+ checkSameDec("__rdivmod__", True)
+ checkSameDec("__repr__")
+ checkSameDec("__rfloordiv__", True)
+ checkSameDec("__rmod__", True)
+ checkSameDec("__rmul__", True)
+ checkSameDec("__rpow__", True)
+ checkSameDec("__rsub__", True)
+ checkSameDec("__str__")
+ checkSameDec("__sub__", True)
+ checkSameDec("__truediv__", True)
+ checkSameDec("adjusted")
+ checkSameDec("as_tuple")
+ checkSameDec("compare", True)
+ checkSameDec("max", True)
+ checkSameDec("min", True)
+ checkSameDec("normalize")
+ checkSameDec("quantize", True)
+ checkSameDec("remainder_near", True)
+ checkSameDec("same_quantum", True)
+ checkSameDec("sqrt")
+ checkSameDec("to_eng_string")
+ checkSameDec("to_integral")
+
+ def test_py_decimal_id(self):
+ Decimal = P.Decimal
+
+ d = Decimal(45)
+ e = Decimal(d)
+ self.assertEqual(str(e), '45')
+ self.assertNotEqual(id(d), id(e))
+
+ def test_py_rescale(self):
+ # Coverage
+ Decimal = P.Decimal
+ ROUND_UP = P.ROUND_UP
+ localcontext = P.localcontext
+
+ with localcontext() as c:
+ x = Decimal("NaN")._rescale(3, ROUND_UP)
+ self.assertTrue(x.is_nan())
+
+ def test_py__round(self):
+ # Coverage
+ Decimal = P.Decimal
+ ROUND_UP = P.ROUND_UP
+
+ self.assertRaises(ValueError, Decimal("3.1234")._round, 0, ROUND_UP)
+
+class CFunctionality(unittest.TestCase):
+ """Extra functionality in _decimal"""
+
+ @requires_extra_functionality
+ def test_c_ieee_context(self):
+ # issue 8786: Add support for IEEE 754 contexts to decimal module.
+ IEEEContext = C.IEEEContext
+ DECIMAL32 = C.DECIMAL32
+ DECIMAL64 = C.DECIMAL64
+ DECIMAL128 = C.DECIMAL128
+
+ def assert_rest(self, context):
+ self.assertEqual(context.clamp, 1)
+ assert_signals(self, context, 'traps', [])
+ assert_signals(self, context, 'flags', [])
+
+ c = IEEEContext(DECIMAL32)
+ self.assertEqual(c.prec, 7)
+ self.assertEqual(c.Emax, 96)
+ self.assertEqual(c.Emin, -95)
+ assert_rest(self, c)
+
+ c = IEEEContext(DECIMAL64)
+ self.assertEqual(c.prec, 16)
+ self.assertEqual(c.Emax, 384)
+ self.assertEqual(c.Emin, -383)
+ assert_rest(self, c)
+
+ c = IEEEContext(DECIMAL128)
+ self.assertEqual(c.prec, 34)
+ self.assertEqual(c.Emax, 6144)
+ self.assertEqual(c.Emin, -6143)
+ assert_rest(self, c)
+
+ # Invalid values
+ self.assertRaises(OverflowError, IEEEContext, 2**63)
+ self.assertRaises(ValueError, IEEEContext, -1)
+ self.assertRaises(ValueError, IEEEContext, 1024)
+
+ @requires_extra_functionality
+ def test_c_context(self):
+ Context = C.Context
+
+ c = Context(flags=C.DecClamped, traps=C.DecRounded)
+ self.assertEqual(c._flags, C.DecClamped)
+ self.assertEqual(c._traps, C.DecRounded)
+
+ @requires_extra_functionality
+ def test_constants(self):
+ # Condition flags
+ cond = (
+ C.DecClamped, C.DecConversionSyntax, C.DecDivisionByZero,
+ C.DecDivisionImpossible, C.DecDivisionUndefined,
+ C.DecFpuError, C.DecInexact, C.DecInvalidContext,
+ C.DecInvalidOperation, C.DecMallocError,
+ C.DecFloatOperation, C.DecOverflow, C.DecRounded,
+ C.DecSubnormal, C.DecUnderflow
+ )
+
+ # IEEEContext
+ self.assertEqual(C.DECIMAL32, 32)
+ self.assertEqual(C.DECIMAL64, 64)
+ self.assertEqual(C.DECIMAL128, 128)
+ self.assertEqual(C.IEEE_CONTEXT_MAX_BITS, 512)
+
+ # Rounding modes
+ for i, v in enumerate(RoundingModes[C]):
+ self.assertEqual(v, i)
+ self.assertEqual(C.ROUND_TRUNC, 8)
+
+ # Conditions
+ for i, v in enumerate(cond):
+ self.assertEqual(v, 1<<i)
+
+ self.assertEqual(C.DecIEEEInvalidOperation,
+ C.DecConversionSyntax|
+ C.DecDivisionImpossible|
+ C.DecDivisionUndefined|
+ C.DecFpuError|
+ C.DecInvalidContext|
+ C.DecInvalidOperation|
+ C.DecMallocError)
+
+ self.assertEqual(C.DecErrors,
+ C.DecIEEEInvalidOperation|
+ C.DecDivisionByZero)
+
+ self.assertEqual(C.DecTraps,
+ C.DecErrors|C.DecOverflow|C.DecUnderflow)
+
+class CWhitebox(unittest.TestCase):
+ """Whitebox testing for _decimal"""
+
+ def test_bignum(self):
+ # Not exactly whitebox, but too slow with pydecimal.
+
+ Decimal = C.Decimal
+ localcontext = C.localcontext
+
+ b1 = 10**35
+ b2 = 10**36
+ with localcontext() as c:
+ c.prec = 1000000
+ for i in range(5):
+ a = random.randrange(b1, b2)
+ b = random.randrange(1000, 1200)
+ x = a ** b
+ y = Decimal(a) ** Decimal(b)
+ self.assertEqual(x, y)
+
+ def test_invalid_construction(self):
+ self.assertRaises(TypeError, C.Decimal, 9, "xyz")
+
+ def test_c_input_restriction(self):
+ # Too large for _decimal to be converted exactly
+ Decimal = C.Decimal
+ InvalidOperation = C.InvalidOperation
+ Context = C.Context
+ localcontext = C.localcontext
+
+ with localcontext(Context()):
+ self.assertRaises(InvalidOperation, Decimal,
+ "1e9999999999999999999")
+
+ def test_c_context_repr(self):
+ # This test is _decimal-only because flags are not printed
+ # in the same order.
+ DefaultContext = C.DefaultContext
+ FloatOperation = C.FloatOperation
+ ROUND_HALF_DOWN = C.ROUND_HALF_DOWN
+
+ c = DefaultContext.copy()
+
+ c.prec = 425000000
+ c.Emax = 425000000
+ c.Emin = -425000000
+ c.rounding = ROUND_HALF_DOWN
+ c.capitals = 0
+ c.clamp = 1
+ for sig in OrderedSignals[C]:
+ c.flags[sig] = True
+ c.traps[sig] = True
+ c.flags[FloatOperation] = True
+ c.traps[FloatOperation] = True
+
+ s = c.__repr__()
+ t = "Context(prec=425000000, rounding=ROUND_HALF_DOWN, " \
+ "Emin=-425000000, Emax=425000000, capitals=0, clamp=1, " \
+ "flags=[Clamped, InvalidOperation, DivisionByZero, Inexact, " \
+ "FloatOperation, Overflow, Rounded, Subnormal, Underflow], " \
+ "traps=[Clamped, InvalidOperation, DivisionByZero, Inexact, " \
+ "FloatOperation, Overflow, Rounded, Subnormal, Underflow])"
+ self.assertEqual(s, t)
+
+ def test_c_context_errors(self):
+ Context = C.Context
+ InvalidOperation = C.InvalidOperation
+ Overflow = C.Overflow
+ FloatOperation = C.FloatOperation
+ localcontext = C.localcontext
+ getcontext = C.getcontext
+ setcontext = C.setcontext
+ HAVE_CONFIG_64 = (C.MAX_PREC > 425000000)
+
+ c = Context()
+
+ # SignalDict: input validation
+ self.assertRaises(KeyError, c.flags.__setitem__, 801, 0)
+ self.assertRaises(KeyError, c.traps.__setitem__, 801, 0)
+ self.assertRaises(ValueError, c.flags.__delitem__, Overflow)
+ self.assertRaises(ValueError, c.traps.__delitem__, InvalidOperation)
+ self.assertRaises(TypeError, setattr, c, 'flags', ['x'])
+ self.assertRaises(TypeError, setattr, c,'traps', ['y'])
+ self.assertRaises(KeyError, setattr, c, 'flags', {0:1})
+ self.assertRaises(KeyError, setattr, c, 'traps', {0:1})
+
+ # Test assignment from a signal dict with the correct length but
+ # one invalid key.
+ d = c.flags.copy()
+ del d[FloatOperation]
+ d["XYZ"] = 91283719
+ self.assertRaises(KeyError, setattr, c, 'flags', d)
+ self.assertRaises(KeyError, setattr, c, 'traps', d)
+
+ # Input corner cases
+ int_max = 2**63-1 if HAVE_CONFIG_64 else 2**31-1
+ gt_max_emax = 10**18 if HAVE_CONFIG_64 else 10**9
+
+ # prec, Emax, Emin
+ for attr in ['prec', 'Emax']:
+ self.assertRaises(ValueError, setattr, c, attr, gt_max_emax)
+ self.assertRaises(ValueError, setattr, c, 'Emin', -gt_max_emax)
+
+ # prec, Emax, Emin in context constructor
+ self.assertRaises(ValueError, Context, prec=gt_max_emax)
+ self.assertRaises(ValueError, Context, Emax=gt_max_emax)
+ self.assertRaises(ValueError, Context, Emin=-gt_max_emax)
+
+ # Overflow in conversion
+ self.assertRaises(OverflowError, Context, prec=int_max+1)
+ self.assertRaises(OverflowError, Context, Emax=int_max+1)
+ self.assertRaises(OverflowError, Context, Emin=-int_max-2)
+ self.assertRaises(OverflowError, Context, rounding=int_max+1)
+ self.assertRaises(OverflowError, Context, clamp=int_max+1)
+ self.assertRaises(OverflowError, Context, capitals=int_max+1)
+
+ # OverflowError, general ValueError
+ for attr in ('prec', 'Emin', 'Emax', 'capitals', 'clamp'):
+ self.assertRaises(OverflowError, setattr, c, attr, int_max+1)
+ self.assertRaises(OverflowError, setattr, c, attr, -int_max-2)
+ if sys.platform != 'win32':
+ self.assertRaises(ValueError, setattr, c, attr, int_max)
+ self.assertRaises(ValueError, setattr, c, attr, -int_max-1)
+
+ # OverflowError, general TypeError
+ for attr in ('rounding',):
+ self.assertRaises(OverflowError, setattr, c, attr, int_max+1)
+ self.assertRaises(OverflowError, setattr, c, attr, -int_max-2)
+ if sys.platform != 'win32':
+ self.assertRaises(TypeError, setattr, c, attr, int_max)
+ self.assertRaises(TypeError, setattr, c, attr, -int_max-1)
+
+ # OverflowError: _unsafe_setprec, _unsafe_setemin, _unsafe_setemax
+ if C.MAX_PREC == 425000000:
+ self.assertRaises(OverflowError, getattr(c, '_unsafe_setprec'),
+ int_max+1)
+ self.assertRaises(OverflowError, getattr(c, '_unsafe_setemax'),
+ int_max+1)
+ self.assertRaises(OverflowError, getattr(c, '_unsafe_setemin'),
+ -int_max-2)
+
+ # ValueError: _unsafe_setprec, _unsafe_setemin, _unsafe_setemax
+ if C.MAX_PREC == 425000000:
+ self.assertRaises(ValueError, getattr(c, '_unsafe_setprec'), 0)
+ self.assertRaises(ValueError, getattr(c, '_unsafe_setprec'),
+ 1070000001)
+ self.assertRaises(ValueError, getattr(c, '_unsafe_setemax'), -1)
+ self.assertRaises(ValueError, getattr(c, '_unsafe_setemax'),
+ 1070000001)
+ self.assertRaises(ValueError, getattr(c, '_unsafe_setemin'),
+ -1070000001)
+ self.assertRaises(ValueError, getattr(c, '_unsafe_setemin'), 1)
+
+ # capitals, clamp
+ for attr in ['capitals', 'clamp']:
+ self.assertRaises(ValueError, setattr, c, attr, -1)
+ self.assertRaises(ValueError, setattr, c, attr, 2)
+ self.assertRaises(TypeError, setattr, c, attr, [1,2,3])
+ if HAVE_CONFIG_64:
+ self.assertRaises(ValueError, setattr, c, attr, 2**32)
+ self.assertRaises(ValueError, setattr, c, attr, 2**32+1)
+
+ # Invalid local context
+ self.assertRaises(TypeError, exec, 'with localcontext("xyz"): pass',
+ locals())
+
+ # setcontext
+ saved_context = getcontext()
+ self.assertRaises(TypeError, setcontext, "xyz")
+ setcontext(saved_context)
+
+ @requires_extra_functionality
+ def test_c_context_errors_extra(self):
+ Context = C.Context
+ InvalidOperation = C.InvalidOperation
+ Overflow = C.Overflow
+ localcontext = C.localcontext
+ getcontext = C.getcontext
+ setcontext = C.setcontext
+ HAVE_CONFIG_64 = (C.MAX_PREC > 425000000)
+
+ c = Context()
+
+ # Input corner cases
+ int_max = 2**63-1 if HAVE_CONFIG_64 else 2**31-1
+
+ # OverflowError, general ValueError
+ self.assertRaises(OverflowError, setattr, c, '_allcr', int_max+1)
+ self.assertRaises(OverflowError, setattr, c, '_allcr', -int_max-2)
+ if sys.platform != 'win32':
+ self.assertRaises(ValueError, setattr, c, '_allcr', int_max)
+ self.assertRaises(ValueError, setattr, c, '_allcr', -int_max-1)
+
+ # OverflowError, general TypeError
+ for attr in ('_flags', '_traps'):
+ self.assertRaises(OverflowError, setattr, c, attr, int_max+1)
+ self.assertRaises(OverflowError, setattr, c, attr, -int_max-2)
+ if sys.platform != 'win32':
+ self.assertRaises(TypeError, setattr, c, attr, int_max)
+ self.assertRaises(TypeError, setattr, c, attr, -int_max-1)
+
+ # _allcr
+ self.assertRaises(ValueError, setattr, c, '_allcr', -1)
+ self.assertRaises(ValueError, setattr, c, '_allcr', 2)
+ self.assertRaises(TypeError, setattr, c, '_allcr', [1,2,3])
+ if HAVE_CONFIG_64:
+ self.assertRaises(ValueError, setattr, c, '_allcr', 2**32)
+ self.assertRaises(ValueError, setattr, c, '_allcr', 2**32+1)
+
+ # _flags, _traps
+ for attr in ['_flags', '_traps']:
+ self.assertRaises(TypeError, setattr, c, attr, 999999)
+ self.assertRaises(TypeError, setattr, c, attr, 'x')
+
+ def test_c_valid_context(self):
+ # These tests are for code coverage in _decimal.
+ DefaultContext = C.DefaultContext
+ ROUND_HALF_UP = C.ROUND_HALF_UP
+ Clamped = C.Clamped
+ Underflow = C.Underflow
+ Inexact = C.Inexact
+ Rounded = C.Rounded
+ Subnormal = C.Subnormal
+
+ c = DefaultContext.copy()
+
+ # Exercise all getters and setters
+ c.prec = 34
+ c.rounding = ROUND_HALF_UP
+ c.Emax = 3000
+ c.Emin = -3000
+ c.capitals = 1
+ c.clamp = 0
+
+ self.assertEqual(c.prec, 34)
+ self.assertEqual(c.rounding, ROUND_HALF_UP)
+ self.assertEqual(c.Emin, -3000)
+ self.assertEqual(c.Emax, 3000)
+ self.assertEqual(c.capitals, 1)
+ self.assertEqual(c.clamp, 0)
+
+ self.assertEqual(c.Etiny(), -3033)
+ self.assertEqual(c.Etop(), 2967)
+
+ # Exercise all unsafe setters
+ if C.MAX_PREC == 425000000:
+ c._unsafe_setprec(999999999)
+ c._unsafe_setemax(999999999)
+ c._unsafe_setemin(-999999999)
+ self.assertEqual(c.prec, 999999999)
+ self.assertEqual(c.Emax, 999999999)
+ self.assertEqual(c.Emin, -999999999)
+
+ @requires_extra_functionality
+ def test_c_valid_context_extra(self):
+ DefaultContext = C.DefaultContext
+
+ c = DefaultContext.copy()
+ self.assertEqual(c._allcr, 1)
+ c._allcr = 0
+ self.assertEqual(c._allcr, 0)
+
+ def test_c_round(self):
+ # Restricted input.
+ Decimal = C.Decimal
+ InvalidOperation = C.InvalidOperation
+ localcontext = C.localcontext
+ MAX_EMAX = C.MAX_EMAX
+ MIN_ETINY = C.MIN_ETINY
+ int_max = 2**63-1 if C.MAX_PREC > 425000000 else 2**31-1
+
+ with localcontext() as c:
+ c.traps[InvalidOperation] = True
+ self.assertRaises(InvalidOperation, Decimal("1.23").__round__,
+ -int_max-1)
+ self.assertRaises(InvalidOperation, Decimal("1.23").__round__,
+ int_max)
+ self.assertRaises(InvalidOperation, Decimal("1").__round__,
+ int(MAX_EMAX+1))
+ self.assertRaises(C.InvalidOperation, Decimal("1").__round__,
+ -int(MIN_ETINY-1))
+ self.assertRaises(OverflowError, Decimal("1.23").__round__,
+ -int_max-2)
+ self.assertRaises(OverflowError, Decimal("1.23").__round__,
+ int_max+1)
+
+ def test_c_format(self):
+ # Restricted input
+ Decimal = C.Decimal
+ InvalidOperation = C.InvalidOperation
+ Rounded = C.Rounded
+ localcontext = C.localcontext
+ HAVE_CONFIG_64 = (C.MAX_PREC > 425000000)
+
+ self.assertRaises(TypeError, Decimal(1).__format__, "=10.10", [], 9)
+ self.assertRaises(TypeError, Decimal(1).__format__, "=10.10", 9)
+ self.assertRaises(TypeError, Decimal(1).__format__, [])
+
+ with localcontext() as c:
+ c.traps[InvalidOperation] = True
+ c.traps[Rounded] = True
+ self.assertRaises(ValueError, Decimal(1).__format__, "<>=10.10")
+ maxsize = 2**63-1 if HAVE_CONFIG_64 else 2**31-1
+ self.assertRaises(InvalidOperation, Decimal("1.23456789").__format__,
+ "=%d.1" % maxsize)
+
+ def test_c_integral(self):
+ Decimal = C.Decimal
+ Inexact = C.Inexact
+ ROUND_UP = C.ROUND_UP
+ localcontext = C.localcontext
+
+ x = Decimal(10)
+ self.assertEqual(x.to_integral(), 10)
+ self.assertRaises(TypeError, x.to_integral, '10')
+ self.assertRaises(TypeError, x.to_integral, 10, 'x')
+ self.assertRaises(TypeError, x.to_integral, 10)
+
+ self.assertEqual(x.to_integral_value(), 10)
+ self.assertRaises(TypeError, x.to_integral_value, '10')
+ self.assertRaises(TypeError, x.to_integral_value, 10, 'x')
+ self.assertRaises(TypeError, x.to_integral_value, 10)
+
+ self.assertEqual(x.to_integral_exact(), 10)
+ self.assertRaises(TypeError, x.to_integral_exact, '10')
+ self.assertRaises(TypeError, x.to_integral_exact, 10, 'x')
+ self.assertRaises(TypeError, x.to_integral_exact, 10)
+
+ with localcontext() as c:
+ x = Decimal("99999999999999999999999999.9").to_integral_value(ROUND_UP)
+ self.assertEqual(x, Decimal('100000000000000000000000000'))
+
+ x = Decimal("99999999999999999999999999.9").to_integral_exact(ROUND_UP)
+ self.assertEqual(x, Decimal('100000000000000000000000000'))
+
+ c.traps[Inexact] = True
+ self.assertRaises(Inexact, Decimal("999.9").to_integral_exact, ROUND_UP)
+
+ def test_c_funcs(self):
+ # Invalid arguments
+ Decimal = C.Decimal
+ InvalidOperation = C.InvalidOperation
+ DivisionByZero = C.DivisionByZero
+ ROUND_UP = C.ROUND_UP
+ getcontext = C.getcontext
+ localcontext = C.localcontext
+
+ self.assertEqual(Decimal('9.99e10').to_eng_string(), '99.9E+9')
+
+ self.assertRaises(TypeError, pow, Decimal(1), 2, "3")
+ self.assertRaises(TypeError, Decimal(9).number_class, "x", "y")
+ self.assertRaises(TypeError, Decimal(9).same_quantum, 3, "x", "y")
+
+ self.assertRaises(
+ TypeError,
+ Decimal("1.23456789").quantize, Decimal('1e-100000'), []
+ )
+ self.assertRaises(
+ TypeError,
+ Decimal("1.23456789").quantize, Decimal('1e-100000'), getcontext()
+ )
+ self.assertRaises(
+ TypeError,
+ Decimal("1.23456789").quantize, Decimal('1e-100000'), 10
+ )
+ self.assertRaises(
+ TypeError,
+ Decimal("1.23456789").quantize, Decimal('1e-100000'), ROUND_UP, 1000
+ )
+
+ with localcontext() as c:
+ c.clear_traps()
+
+ # Invalid arguments
+ self.assertRaises(TypeError, c.copy_sign, Decimal(1), "x", "y")
+ self.assertRaises(TypeError, c.canonical, 200)
+ self.assertRaises(TypeError, c.is_canonical, 200)
+ self.assertRaises(TypeError, c.divmod, 9, 8, "x", "y")
+ self.assertRaises(TypeError, c.same_quantum, 9, 3, "x", "y")
+
+ self.assertEqual(str(c.canonical(Decimal(200))), '200')
+ self.assertEqual(c.radix(), 10)
+
+ c.traps[DivisionByZero] = True
+ self.assertRaises(DivisionByZero, Decimal(9).__divmod__, 0)
+ self.assertRaises(DivisionByZero, c.divmod, 9, 0)
+ self.assertTrue(c.flags[InvalidOperation])
+
+ c.clear_flags()
+ c.traps[InvalidOperation] = True
+ self.assertRaises(InvalidOperation, Decimal(9).__divmod__, 0)
+ self.assertRaises(InvalidOperation, c.divmod, 9, 0)
+ self.assertTrue(c.flags[DivisionByZero])
+
+ c.traps[InvalidOperation] = True
+ c.prec = 2
+ self.assertRaises(InvalidOperation, pow, Decimal(1000), 1, 501)
+
+ @requires_extra_functionality
+ def test_c_context_templates(self):
+ self.assertEqual(
+ C.BasicContext._traps,
+ C.DecIEEEInvalidOperation|C.DecDivisionByZero|C.DecOverflow|
+ C.DecUnderflow|C.DecClamped
+ )
+ self.assertEqual(
+ C.DefaultContext._traps,
+ C.DecIEEEInvalidOperation|C.DecDivisionByZero|C.DecOverflow
+ )
+
+ @requires_extra_functionality
+ def test_c_signal_dict(self):
+
+ # SignalDict coverage
+ Context = C.Context
+ DefaultContext = C.DefaultContext
+
+ InvalidOperation = C.InvalidOperation
+ DivisionByZero = C.DivisionByZero
+ Overflow = C.Overflow
+ Subnormal = C.Subnormal
+ Underflow = C.Underflow
+ Rounded = C.Rounded
+ Inexact = C.Inexact
+ Clamped = C.Clamped
+
+ DecClamped = C.DecClamped
+ DecInvalidOperation = C.DecInvalidOperation
+ DecIEEEInvalidOperation = C.DecIEEEInvalidOperation
+
+ def assertIsExclusivelySet(signal, signal_dict):
+ for sig in signal_dict:
+ if sig == signal:
+ self.assertTrue(signal_dict[sig])
+ else:
+ self.assertFalse(signal_dict[sig])
+
+ c = DefaultContext.copy()
+
+ # Signal dict methods
+ self.assertTrue(Overflow in c.traps)
+ c.clear_traps()
+ for k in c.traps.keys():
+ c.traps[k] = True
+ for v in c.traps.values():
+ self.assertTrue(v)
+ c.clear_traps()
+ for k, v in c.traps.items():
+ self.assertFalse(v)
+
+ self.assertFalse(c.flags.get(Overflow))
+ self.assertIs(c.flags.get("x"), None)
+ self.assertEqual(c.flags.get("x", "y"), "y")
+ self.assertRaises(TypeError, c.flags.get, "x", "y", "z")
+
+ self.assertEqual(len(c.flags), len(c.traps))
+ s = sys.getsizeof(c.flags)
+ s = sys.getsizeof(c.traps)
+ s = c.flags.__repr__()
+
+ # Set flags/traps.
+ c.clear_flags()
+ c._flags = DecClamped
+ self.assertTrue(c.flags[Clamped])
+
+ c.clear_traps()
+ c._traps = DecInvalidOperation
+ self.assertTrue(c.traps[InvalidOperation])
+
+ # Set flags/traps from dictionary.
+ c.clear_flags()
+ d = c.flags.copy()
+ d[DivisionByZero] = True
+ c.flags = d
+ assertIsExclusivelySet(DivisionByZero, c.flags)
+
+ c.clear_traps()
+ d = c.traps.copy()
+ d[Underflow] = True
+ c.traps = d
+ assertIsExclusivelySet(Underflow, c.traps)
+
+ # Random constructors
+ IntSignals = {
+ Clamped: C.DecClamped,
+ Rounded: C.DecRounded,
+ Inexact: C.DecInexact,
+ Subnormal: C.DecSubnormal,
+ Underflow: C.DecUnderflow,
+ Overflow: C.DecOverflow,
+ DivisionByZero: C.DecDivisionByZero,
+ InvalidOperation: C.DecIEEEInvalidOperation
+ }
+ IntCond = [
+ C.DecDivisionImpossible, C.DecDivisionUndefined, C.DecFpuError,
+ C.DecInvalidContext, C.DecInvalidOperation, C.DecMallocError,
+ C.DecConversionSyntax,
+ ]
+
+ lim = len(OrderedSignals[C])
+ for r in range(lim):
+ for t in range(lim):
+ for round in RoundingModes[C]:
+ flags = random.sample(OrderedSignals[C], r)
+ traps = random.sample(OrderedSignals[C], t)
+ prec = random.randrange(1, 10000)
+ emin = random.randrange(-10000, 0)
+ emax = random.randrange(0, 10000)
+ clamp = random.randrange(0, 2)
+ caps = random.randrange(0, 2)
+ cr = random.randrange(0, 2)
+ c = Context(prec=prec, rounding=round, Emin=emin, Emax=emax,
+ capitals=caps, clamp=clamp, flags=list(flags),
+ traps=list(traps))
+
+ self.assertEqual(c.prec, prec)
+ self.assertEqual(c.rounding, round)
+ self.assertEqual(c.Emin, emin)
+ self.assertEqual(c.Emax, emax)
+ self.assertEqual(c.capitals, caps)
+ self.assertEqual(c.clamp, clamp)
+
+ f = 0
+ for x in flags:
+ f |= IntSignals[x]
+ self.assertEqual(c._flags, f)
+
+ f = 0
+ for x in traps:
+ f |= IntSignals[x]
+ self.assertEqual(c._traps, f)
+
+ for cond in IntCond:
+ c._flags = cond
+ self.assertTrue(c._flags&DecIEEEInvalidOperation)
+ assertIsExclusivelySet(InvalidOperation, c.flags)
+
+ for cond in IntCond:
+ c._traps = cond
+ self.assertTrue(c._traps&DecIEEEInvalidOperation)
+ assertIsExclusivelySet(InvalidOperation, c.traps)
+
+ def test_invalid_override(self):
+ Decimal = C.Decimal
+
+ try:
+ from locale import CHAR_MAX
+ except ImportError:
+ return
+
+ def make_grouping(lst):
+ return ''.join([chr(x) for x in lst])
+
+ def get_fmt(x, override=None, fmt='n'):
+ return Decimal(x).__format__(fmt, override)
+
+ invalid_grouping = {
+ 'decimal_point' : ',',
+ 'grouping' : make_grouping([255, 255, 0]),
+ 'thousands_sep' : ','
+ }
+ invalid_dot = {
+ 'decimal_point' : 'xxxxx',
+ 'grouping' : make_grouping([3, 3, 0]),
+ 'thousands_sep' : ','
+ }
+ invalid_sep = {
+ 'decimal_point' : '.',
+ 'grouping' : make_grouping([3, 3, 0]),
+ 'thousands_sep' : 'yyyyy'
+ }
+
+ if CHAR_MAX == 127: # negative grouping in override
+ self.assertRaises(ValueError, get_fmt, 12345,
+ invalid_grouping, 'g')
+
+ self.assertRaises(ValueError, get_fmt, 12345, invalid_dot, 'g')
+ self.assertRaises(ValueError, get_fmt, 12345, invalid_sep, 'g')
+
+
+all_tests = [
+ CExplicitConstructionTest, PyExplicitConstructionTest,
+ CImplicitConstructionTest, PyImplicitConstructionTest,
+ CFormatTest, PyFormatTest,
+ CArithmeticOperatorsTest, PyArithmeticOperatorsTest,
+ CThreadingTest, PyThreadingTest,
+ CUsabilityTest, PyUsabilityTest,
+ CPythonAPItests, PyPythonAPItests,
+ CContextAPItests, PyContextAPItests,
+ CContextWithStatement, PyContextWithStatement,
+ CContextFlags, PyContextFlags,
+ CSpecialContexts, PySpecialContexts,
+ CContextInputValidation, PyContextInputValidation,
+ CContextSubclassing, PyContextSubclassing,
+ CCoverage, PyCoverage,
+ CFunctionality, PyFunctionality,
+ CWhitebox, PyWhitebox,
+ CIBMTestCases, PyIBMTestCases,
+]
+
+# Delete C tests if _decimal.so is not present.
+if not C:
+ all_tests = all_tests[1::2]
+else:
+ all_tests.insert(0, CheckAttributes)
+
+
def test_main(arith=False, verbose=None, todo_tests=None, debug=None):
""" Execute the tests.
is enabled in regrtest.py
"""
- init()
+ init(C)
+ init(P)
global TEST_ALL, DEBUG
TEST_ALL = arith or is_resource_enabled('decimal')
DEBUG = debug
if todo_tests is None:
- test_classes = [
- DecimalExplicitConstructionTest,
- DecimalImplicitConstructionTest,
- DecimalArithmeticOperatorsTest,
- DecimalFormatTest,
- DecimalUseOfContextTest,
- DecimalUsabilityTest,
- DecimalPythonAPItests,
- ContextAPItests,
- DecimalTest,
- WithStatementTest,
- ContextFlags
- ]
+ test_classes = all_tests
else:
- test_classes = [DecimalTest]
+ test_classes = [CIBMTestCases, PyIBMTestCases]
# Dynamically build custom test definition for each file in the test
# directory and add the definitions to the DecimalTest class. This
if todo_tests is not None and head not in todo_tests:
continue
tester = lambda self, f=filename: self.eval_file(directory + f)
- setattr(DecimalTest, 'test_' + head, tester)
+ setattr(CIBMTestCases, 'test_' + head, tester)
+ setattr(PyIBMTestCases, 'test_' + head, tester)
del filename, head, tail, tester
try:
run_unittest(*test_classes)
if todo_tests is None:
- import decimal as DecimalModule
- run_doctest(DecimalModule, verbose)
+ from doctest import IGNORE_EXCEPTION_DETAIL
+ savedecimal = sys.modules['decimal']
+ if C:
+ sys.modules['decimal'] = C
+ run_doctest(C, verbose, optionflags=IGNORE_EXCEPTION_DETAIL)
+ sys.modules['decimal'] = P
+ run_doctest(P, verbose)
+ sys.modules['decimal'] = savedecimal
finally:
- setcontext(ORIGINAL_CONTEXT)
+ if C: C.setcontext(ORIGINAL_CONTEXT[C])
+ P.setcontext(ORIGINAL_CONTEXT[P])
+ if not C:
+ warnings.warn('C tests skipped: no module named _decimal.',
+ UserWarning)
+ if not orig_sys_decimal is sys.modules['decimal']:
+ raise TestFailed("Internal error: unbalanced number of changes to "
+ "sys.modules['decimal'].")
+
if __name__ == '__main__':
import optparse
def testMixingWithDecimal(self):
# Decimal refuses mixed arithmetic (but not mixed comparisons)
- self.assertRaisesMessage(
- TypeError,
- "unsupported operand type(s) for +: 'Fraction' and 'Decimal'",
- operator.add, F(3,11), Decimal('3.1415926'))
- self.assertRaisesMessage(
- TypeError,
- "unsupported operand type(s) for +: 'Decimal' and 'Fraction'",
- operator.add, Decimal('3.1415926'), F(3,11))
+ self.assertRaises(TypeError, operator.add,
+ F(3,11), Decimal('3.1415926'))
+ self.assertRaises(TypeError, operator.add,
+ Decimal('3.1415926'), F(3,11))
def testComparisons(self):
self.assertTrue(F(1, 2) < F(2, 3))
# int, float, Fraction, Decimal
test_values = [
float('-inf'),
- D('-1e999999999'),
+ D('-1e425000000'),
-1e308,
F(-22, 7),
-3.14,
Library
-------
+- 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.
+
- Issue #3573: IDLE hangs when passing invalid command line args
(directory(ies) instead of file(s)) (Patch by Guilherme Polo)
fun:PyUnicode_FSConverter
}
+# Additional suppressions for the unified decimal tests:
+{
+ test_decimal
+ Memcheck:Addr4
+ fun:PyUnicodeUCS2_FSConverter
+}
+
+{
+ test_decimal2
+ Memcheck:Addr4
+ fun:PyUnicode_FSConverter
+}
+
--- /dev/null
+
+
+Normal priority:
+----------------
+
+1) Add C-API importable as capsule.
+
+2) Add --with-system-libmpdec to ./configure.
+
+3) Use same default emin/emax on 32-bit (MAX_EMAX=425000000) and 64-bit
+ (MAX_EMAX=10**18-1).
+
+4) Order of arguments in Context().
+
+5) Documentation.
+
+6) quantize()
+ - exp argument is misleading:
+ Decimal('0.321000e+2').quantize(exp=9) -> user might expect
+ that the result will have exp=9.
+ - watchexp
+
+7) Match the exception hierarchy of decimal.py:
+
+ exceptions.ArithmeticError(exceptions.Exception)
+ DecimalException
+ Clamped
+ DivisionByZero(DecimalException, exceptions.ZeroDivisionError)
+ Inexact
+ Overflow(Inexact, Rounded)
+ Underflow(Inexact, Rounded, Subnormal)
+ InvalidOperation
+ Rounded
+ Subnormal
+ FloatOperation
+
+
+Low priority:
+-------------
+
+1) Convert tabs (wait until commit).
+
+2) Pre-ANSI compilers require '#' in the first column (should be done
+ for the whole Python source tree if we support such compilers). (?)
+
+3) FETCH_CURRENT_CONTEXT vs. CURRENT_CONTEXT?
+
+4) Justify remaining uses of exit on overflow in bignum arith. Short
+ answer: with correct context values the coefficients never get big
+ enough for that to happen.
+
+5) Justify remaining uses of abort() in mpdecimal: These uses are
+ for debug purposes and can't be reached when the library is used
+ correctly.
+
+
--- /dev/null
+
+
+About
+=====
+
+_decimal.c is a wrapper for the libmpdec library. libmpdec is a fast C
+library for correctly-rounded arbitrary precision decimal floating point
+arithmetic. It is a complete implementation of Mike Cowlishaw/IBM's
+General Decimal Arithmetic Specification.
+
+
+Build process for the module
+============================
+
+As usual, the build process for _decimal.so is driven by setup.py in the top
+level directory. setup.py autodetects the following build configurations:
+
+ 1) x64 - 64-bit Python, x86_64 processor (AMD, Intel)
+
+ 2) uint128 - 64-bit Python, compiler provides __uint128_t (gcc)
+
+ 3) ansi64 - 64-bit Python, ANSI C
+
+ 4) ppro - 32-bit Python, x86 CPU, PentiumPro or later
+
+ 5) ansi32 - 32-bit Python, ANSI C
+
+ 6) ansi-legacy - 32-bit Python, compiler without uint64_t
+
+ 7) universal - Mac OS only (multi-arch)
+
+
+It is possible to override autodetection by exporting:
+
+ PYTHON_DECIMAL_WITH_MACHINE=value, where value is one of the above options.
+
+
+NOTE
+====
+
+decimal.so is not built from a static libmpdec.a since doing so led to
+failures on AIX (user report) and Windows (mixing static and dynamic CRTs
+causes locale problems and more).
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <Python.h>
+#include "longintrepr.h"
+#include "pythread.h"
+#include "structmember.h"
+#include "complexobject.h"
+#include "mpdecimal.h"
+
+#include <stdlib.h>
+
+#include "docstrings.h"
+#include "memory.h"
+
+
+/*
+ * Type sizes with assertions in mpdecimal.h and pyport.h:
+ * sizeof(size_t) == sizeof(Py_ssize_t)
+ * sizeof(size_t) == sizeof(mpd_uint_t) == sizeof(mpd_ssize_t)
+ */
+
+#ifdef TEST_COVERAGE
+ #undef Py_LOCAL_INLINE
+ #define Py_LOCAL_INLINE Py_LOCAL
+#endif
+
+#define MPD_Float_operation MPD_Not_implemented
+
+#define BOUNDS_CHECK(x, MIN, MAX) x = (x < MIN || MAX < x) ? MAX : x
+
+
+typedef struct {
+ PyObject_HEAD
+ mpd_t *dec;
+} PyDecObject;
+
+typedef struct {
+ PyObject_HEAD
+ uint32_t *flags;
+} PyDecSignalDictObject;
+
+typedef struct {
+ PyObject_HEAD
+ mpd_context_t ctx;
+ PyObject *traps;
+ PyObject *flags;
+ int capitals;
+} PyDecContextObject;
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *local;
+ PyObject *global;
+} PyDecContextManagerObject;
+
+
+#undef MPD
+#undef CTX
+static PyTypeObject PyDec_Type;
+static PyTypeObject *PyDecSignalDict_Type;
+static PyTypeObject PyDecContext_Type;
+static PyTypeObject PyDecContextManager_Type;
+#define PyDec_CheckExact(v) (Py_TYPE(v) == &PyDec_Type)
+#define PyDec_Check(v) PyObject_TypeCheck(v, &PyDec_Type)
+#define PyDecSignalDict_Check(v) (Py_TYPE(v) == PyDecSignalDict_Type)
+#define PyDecContext_Check(v) PyObject_TypeCheck(v, &PyDecContext_Type)
+#define MPD(v) (((PyDecObject *)v)->dec)
+#define SdFlagAddr(v) (((PyDecSignalDictObject *)v)->flags)
+#define SdFlags(v) (*((PyDecSignalDictObject *)v)->flags)
+#define CTX(v) (&((PyDecContextObject *)v)->ctx)
+#define CtxCaps(v) (((PyDecContextObject *)v)->capitals)
+
+
+Py_LOCAL_INLINE(PyObject *)
+incr_true(void)
+{
+ Py_INCREF(Py_True);
+ return Py_True;
+}
+
+Py_LOCAL_INLINE(PyObject *)
+incr_false(void)
+{
+ Py_INCREF(Py_False);
+ return Py_False;
+}
+
+
+#ifdef WITHOUT_THREADS
+/* Default module context */
+static PyObject *module_context = NULL;
+#else
+/* Key for thread state dictionary */
+static PyObject *tls_context_key = NULL;
+#endif
+
+/* Template for creating new thread contexts, calling Context() without
+ * arguments and initializing the module_context on first access. */
+static PyObject *default_context_template = NULL;
+/* Basic and extended context templates */
+static PyObject *basic_context_template = NULL;
+static PyObject *extended_context_template = NULL;
+
+
+/* Error codes for functions that return signals or conditions */
+#define DEC_INVALID_SIGNALS (MPD_Max_status+1U)
+#define DEC_ERR_OCCURRED (DEC_INVALID_SIGNALS<<1)
+#define DEC_ERRORS (DEC_INVALID_SIGNALS|DEC_ERR_OCCURRED)
+
+typedef struct {
+ const char *name; /* condition or signal name */
+ const char *fqname; /* fully qualified name */
+ uint32_t flag; /* libmpdec flag */
+ PyObject *ex; /* corresponding exception */
+} DecCondMap;
+
+/* Top level Exception; inherits from ArithmeticError */
+static PyObject *DecimalException = NULL;
+
+/* Exceptions that correspond to IEEE signals; inherit from DecimalException */
+#define SIGNAL_MAP_LEN 9
+static DecCondMap signal_map[] = {
+ {"InvalidOperation", "decimal.InvalidOperation", MPD_IEEE_Invalid_operation, NULL},
+ {"FloatOperation", "decimal.FloatOperation", MPD_Float_operation, NULL},
+ {"DivisionByZero", "decimal.DivisionByZero", MPD_Division_by_zero, NULL},
+ {"Overflow", "decimal.Overflow", MPD_Overflow, NULL},
+ {"Underflow", "decimal.Underflow", MPD_Underflow, NULL},
+ {"Subnormal", "decimal.Subnormal", MPD_Subnormal, NULL},
+ {"Inexact", "decimal.Inexact", MPD_Inexact, NULL},
+ {"Rounded", "decimal.Rounded", MPD_Rounded, NULL},
+ {"Clamped", "decimal.Clamped", MPD_Clamped, NULL},
+ {NULL}
+};
+
+/* Exceptions that inherit from InvalidOperation */
+static DecCondMap cond_map[] = {
+ {"InvalidOperation", "decimal.InvalidOperation", MPD_Invalid_operation, NULL},
+ {"ConversionSyntax", "decimal.ConversionSyntax", MPD_Conversion_syntax, NULL},
+ {"DivisionImpossible", "decimal.DivisionImpossible", MPD_Division_impossible, NULL},
+ {"DivisionUndefined", "decimal.DivisionUndefined", MPD_Division_undefined, NULL},
+ {"InvalidContext", "decimal.InvalidContext", MPD_Invalid_context, NULL},
+ {"MallocError", "decimal.MallocError", MPD_Malloc_error, NULL},
+ {NULL}
+};
+
+static const char *dec_signal_string[MPD_NUM_FLAGS] = {
+ "Clamped",
+ "InvalidOperation",
+ "DivisionByZero",
+ "InvalidOperation",
+ "InvalidOperation",
+ "InvalidOperation",
+ "Inexact",
+ "InvalidOperation",
+ "InvalidOperation",
+ "InvalidOperation",
+ "FloatOperation",
+ "Overflow",
+ "Rounded",
+ "Subnormal",
+ "Underflow",
+};
+
+static const char *invalid_rounding_err =
+"valid values for rounding are:\n\
+ [ROUND_CEILING, ROUND_FLOOR, ROUND_UP, ROUND_DOWN,\n\
+ ROUND_HALF_UP, ROUND_HALF_DOWN, ROUND_HALF_EVEN,\n\
+ ROUND_05UP]";
+
+static const char *invalid_signals_err =
+"valid values for signals are:\n\
+ [InvalidOperation, FloatOperation, DivisionByZero,\n\
+ Overflow, Underflow, Subnormal, Inexact, Rounded,\n\
+ Clamped]";
+
+#ifdef EXTRA_FUNCTIONALITY
+static const char *invalid_flags_err =
+"valid values for _flags or _traps are:\n\
+ signals:\n\
+ [DecIEEEInvalidOperation, DecFloatOperation, DecDivisionByZero,\n\
+ DecOverflow, DecUnderflow, DecSubnormal, DecInexact, DecRounded,\n\
+ DecClamped]\n\
+ conditions which trigger DecIEEEInvalidOperation:\n\
+ [DecInvalidOperation, DecConversionSyntax, DecDivisionImpossible,\n\
+ DecDivisionUndefined, DecFpuError, DecInvalidContext, DecMallocError]";
+#endif
+
+static int
+value_error_int(const char *mesg)
+{
+ PyErr_SetString(PyExc_ValueError, mesg);
+ return -1;
+}
+
+#ifdef CONFIG_32
+static PyObject *
+value_error_ptr(const char *mesg)
+{
+ PyErr_SetString(PyExc_ValueError, mesg);
+ return NULL;
+}
+#endif
+
+static int
+type_error_int(const char *mesg)
+{
+ PyErr_SetString(PyExc_TypeError, mesg);
+ return -1;
+}
+
+static PyObject *
+type_error_ptr(const char *mesg)
+{
+ PyErr_SetString(PyExc_TypeError, mesg);
+ return NULL;
+}
+
+static int
+runtime_error_int(const char *mesg)
+{
+ PyErr_SetString(PyExc_RuntimeError, mesg);
+ return -1;
+}
+#define INTERNAL_ERROR_INT(funcname) \
+ return runtime_error_int("internal error in " funcname)
+
+static PyObject *
+runtime_error_ptr(const char *mesg)
+{
+ PyErr_SetString(PyExc_RuntimeError, mesg);
+ return NULL;
+}
+#define INTERNAL_ERROR_PTR(funcname) \
+ return runtime_error_ptr("internal error in " funcname)
+
+static void
+dec_traphandler(mpd_context_t *ctx UNUSED) /* GCOV_NOT_REACHED */
+{ /* GCOV_NOT_REACHED */
+ return; /* GCOV_NOT_REACHED */
+}
+
+static PyObject *
+flags_as_exception(uint32_t flags)
+{
+ DecCondMap *cm;
+
+ for (cm = signal_map; cm->name != NULL; cm++) {
+ if (flags&cm->flag) {
+ return cm->ex;
+ }
+ }
+
+ INTERNAL_ERROR_PTR("flags_as_exception"); /* GCOV_NOT_REACHED */
+}
+
+Py_LOCAL_INLINE(uint32_t)
+exception_as_flag(PyObject *ex)
+{
+ DecCondMap *cm;
+
+ for (cm = signal_map; cm->name != NULL; cm++) {
+ if (cm->ex == ex) {
+ return cm->flag;
+ }
+ }
+
+ PyErr_SetString(PyExc_KeyError, invalid_signals_err);
+ return DEC_INVALID_SIGNALS;
+}
+
+static PyObject *
+flags_as_list(uint32_t flags)
+{
+ PyObject *list;
+ DecCondMap *cm;
+
+ list = PyList_New(0);
+ if (list == NULL) {
+ return NULL;
+ }
+
+ for (cm = cond_map; cm->name != NULL; cm++) {
+ if (flags&cm->flag) {
+ if (PyList_Append(list, cm->ex) < 0) {
+ goto error;
+ }
+ }
+ }
+ for (cm = signal_map+1; cm->name != NULL; cm++) {
+ if (flags&cm->flag) {
+ if (PyList_Append(list, cm->ex) < 0) {
+ goto error;
+ }
+ }
+ }
+
+ return list;
+
+error:
+ Py_DECREF(list);
+ return NULL;
+}
+
+static PyObject *
+signals_as_list(uint32_t flags)
+{
+ PyObject *list;
+ DecCondMap *cm;
+
+ list = PyList_New(0);
+ if (list == NULL) {
+ return NULL;
+ }
+
+ for (cm = signal_map; cm->name != NULL; cm++) {
+ if (flags&cm->flag) {
+ if (PyList_Append(list, cm->ex) < 0) {
+ Py_DECREF(list);
+ return NULL;
+ }
+ }
+ }
+
+ return list;
+}
+
+static uint32_t
+list_as_flags(PyObject *list)
+{
+ PyObject *item;
+ uint32_t flags, x;
+ Py_ssize_t n, j;
+
+ assert(PyList_Check(list));
+
+ n = PyList_Size(list);
+ flags = 0;
+ for (j = 0; j < n; j++) {
+ item = PyList_GetItem(list, j);
+ x = exception_as_flag(item);
+ if (x & DEC_ERRORS) {
+ return x;
+ }
+ flags |= x;
+ }
+
+ return flags;
+}
+
+static PyObject *
+flags_as_dict(uint32_t flags)
+{
+ DecCondMap *cm;
+ PyObject *dict;
+
+ dict = PyDict_New();
+ if (dict == NULL) {
+ return NULL;
+ }
+
+ for (cm = signal_map; cm->name != NULL; cm++) {
+ PyObject *b = flags&cm->flag ? Py_True : Py_False;
+ if (PyDict_SetItem(dict, cm->ex, b) < 0) {
+ Py_DECREF(dict);
+ return NULL;
+ }
+ }
+
+ return dict;
+}
+
+static uint32_t
+dict_as_flags(PyObject *val)
+{
+ PyObject *b;
+ DecCondMap *cm;
+ uint32_t flags = 0;
+ int x;
+
+ if (!PyDict_Check(val)) {
+ PyErr_SetString(PyExc_TypeError,
+ "argument must be a signal dict");
+ return DEC_INVALID_SIGNALS;
+ }
+
+ if (PyDict_Size(val) != SIGNAL_MAP_LEN) {
+ PyErr_SetString(PyExc_KeyError,
+ "invalid signal dict");
+ return DEC_INVALID_SIGNALS;
+ }
+
+ for (cm = signal_map; cm->name != NULL; cm++) {
+ b = PyDict_GetItemWithError(val, cm->ex);
+ if (b == NULL) {
+ if (PyErr_Occurred()) {
+ return DEC_ERR_OCCURRED;
+ }
+ PyErr_SetString(PyExc_KeyError,
+ "invalid signal dict");
+ return DEC_INVALID_SIGNALS;
+ }
+
+ x = PyObject_IsTrue(b);
+ if (x < 0) {
+ return DEC_ERR_OCCURRED;
+ }
+ if (x == 1) {
+ flags |= cm->flag;
+ }
+ }
+
+ return flags;
+}
+
+#ifdef EXTRA_FUNCTIONALITY
+static uint32_t
+long_as_flags(PyObject *v)
+{
+ long x;
+
+ x = PyLong_AsLong(v);
+ if (x == -1 && PyErr_Occurred()) {
+ return DEC_ERR_OCCURRED;
+ }
+ if (x < 0 || x > (long)MPD_Max_status) {
+ PyErr_SetString(PyExc_TypeError, invalid_flags_err);
+ return DEC_INVALID_SIGNALS;
+ }
+
+ return x;
+}
+#endif
+
+static int
+dec_addstatus(PyObject *context, uint32_t status)
+{
+ mpd_context_t *ctx = CTX(context);
+
+ ctx->status |= status;
+ if (ctx->traps&status) {
+ PyObject *ex, *siglist;
+
+ ex = flags_as_exception(ctx->traps&status);
+ if (ex == NULL) {
+ return 1; /* GCOV_NOT_REACHED */
+ }
+ siglist = flags_as_list(ctx->traps&status);
+ if (siglist == NULL) {
+ return 1;
+ }
+
+ PyErr_SetObject(ex, siglist);
+ Py_DECREF(siglist);
+ return 1;
+ }
+ return 0;
+}
+
+
+/******************************************************************************/
+/* SignalDict Object */
+/******************************************************************************/
+
+/* The SignalDict is a MutableMapping that provides access to the
+ mpd_context_t flags, which reside in the context object. When a
+ new context is created, context.traps and context.flags are
+ initialized to new SignalDicts. Once a SignalDict is tied to
+ a context, it cannot be deleted. */
+
+static int
+signaldict_init(PyObject *self, PyObject *args UNUSED, PyObject *kwds UNUSED)
+{
+ SdFlagAddr(self) = NULL;
+ return 0;
+}
+
+static Py_ssize_t
+signaldict_len(PyObject *self UNUSED)
+{
+ return SIGNAL_MAP_LEN;
+}
+
+static PyObject *SignalTuple;
+static PyObject *
+signaldict_iter(PyObject *self UNUSED)
+{
+ return PyTuple_Type.tp_iter(SignalTuple);
+}
+
+static PyObject *
+signaldict_getitem(PyObject *self, PyObject *key)
+{
+ uint32_t flag;
+
+ flag = exception_as_flag(key);
+ if (flag & DEC_ERRORS) {
+ return NULL;
+ }
+
+ return SdFlags(self)&flag ? incr_true() : incr_false();
+}
+
+static int
+signaldict_setitem(PyObject *self, PyObject *key, PyObject *value)
+{
+ uint32_t flag;
+ int x;
+
+ if (value == NULL) {
+ return value_error_int("signal keys cannot be deleted");
+ }
+
+ flag = exception_as_flag(key);
+ if (flag & DEC_ERRORS) {
+ return -1;
+ }
+
+ x = PyObject_IsTrue(value);
+ if (x < 0) {
+ return -1;
+ }
+
+ if (x == 1) {
+ SdFlags(self) |= flag;
+ }
+ else {
+ SdFlags(self) &= ~flag;
+ }
+
+ return 0;
+}
+
+static PyObject *
+signaldict_repr(PyObject *self)
+{
+ DecCondMap *cm;
+ const char *n[SIGNAL_MAP_LEN]; /* name */
+ const char *b[SIGNAL_MAP_LEN]; /* bool */
+ int i;
+
+ assert(SIGNAL_MAP_LEN == 9);
+
+ for (cm=signal_map, i=0; cm->name != NULL; cm++, i++) {
+ n[i] = cm->fqname;
+ b[i] = SdFlags(self)&cm->flag ? "True" : "False";
+ }
+ return PyUnicode_FromFormat(
+ "{<class '%s'>:%s, <class '%s'>:%s, <class '%s'>:%s, "
+ "<class '%s'>:%s, <class '%s'>:%s, <class '%s'>:%s, "
+ "<class '%s'>:%s, <class '%s'>:%s, <class '%s'>:%s}",
+ n[0], b[0], n[1], b[1], n[2], b[2],
+ n[3], b[3], n[4], b[4], n[5], b[5],
+ n[6], b[6], n[7], b[7], n[8], b[8]);
+}
+
+static PyObject *
+signaldict_richcompare(PyObject *v, PyObject *w, int op)
+{
+ PyObject *res = Py_NotImplemented;
+
+ assert(PyDecSignalDict_Check(v));
+
+ if (op == Py_EQ || op == Py_NE) {
+ if (PyDecSignalDict_Check(w)) {
+ res = (SdFlags(v)==SdFlags(w)) ^ (op==Py_NE) ? Py_True : Py_False;
+ }
+ else if (PyDict_Check(w)) {
+ uint32_t flags = dict_as_flags(w);
+ if (flags & DEC_ERRORS) {
+ if (flags & DEC_INVALID_SIGNALS) {
+ /* non-comparable: Py_NotImplemented */
+ PyErr_Clear();
+ }
+ else {
+ return NULL;
+ }
+ }
+ else {
+ res = (SdFlags(v)==flags) ^ (op==Py_NE) ? Py_True : Py_False;
+ }
+ }
+ }
+
+ Py_INCREF(res);
+ return res;
+}
+
+static PyObject *
+signaldict_copy(PyObject *self)
+{
+ return flags_as_dict(SdFlags(self));
+}
+
+
+static PyMappingMethods signaldict_as_mapping = {
+ (lenfunc)signaldict_len, /* mp_length */
+ (binaryfunc)signaldict_getitem, /* mp_subscript */
+ (objobjargproc)signaldict_setitem /* mp_ass_subscript */
+};
+
+static PyMethodDef signaldict_methods[] = {
+ { "copy", (PyCFunction)signaldict_copy, METH_NOARGS, NULL},
+ {NULL, NULL}
+};
+
+
+static PyTypeObject PyDecSignalDictMixin_Type =
+{
+ PyVarObject_HEAD_INIT(0, 0)
+ "decimal.SignalDictMixin", /* tp_name */
+ sizeof(PyDecSignalDictObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ 0, /* tp_dealloc */
+ 0, /* tp_print */
+ (getattrfunc) 0, /* tp_getattr */
+ (setattrfunc) 0, /* tp_setattr */
+ 0, /* tp_reserved */
+ (reprfunc) signaldict_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ &signaldict_as_mapping, /* tp_as_mapping */
+ PyObject_HashNotImplemented, /* tp_hash */
+ 0, /* tp_call */
+ (reprfunc) 0, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ (setattrofunc) 0, /* tp_setattro */
+ (PyBufferProcs *) 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|
+ Py_TPFLAGS_HAVE_GC, /* tp_flags */
+ 0, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ signaldict_richcompare, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)signaldict_iter, /* tp_iter */
+ 0, /* tp_iternext */
+ signaldict_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)signaldict_init, /* tp_init */
+ 0, /* tp_alloc */
+ PyType_GenericNew, /* tp_new */
+};
+
+
+/******************************************************************************/
+/* Context Object, Part 1 */
+/******************************************************************************/
+
+#define Dec_CONTEXT_GET_SSIZE(mem) \
+static PyObject * \
+context_get##mem(PyObject *self, void *closure UNUSED) \
+{ \
+ return PyLong_FromSsize_t(mpd_get##mem(CTX(self))); \
+}
+
+#define Dec_CONTEXT_GET_ULONG(mem) \
+static PyObject * \
+context_get##mem(PyObject *self, void *closure UNUSED) \
+{ \
+ return PyLong_FromUnsignedLong(mpd_get##mem(CTX(self))); \
+}
+
+Dec_CONTEXT_GET_SSIZE(prec)
+Dec_CONTEXT_GET_SSIZE(emax)
+Dec_CONTEXT_GET_SSIZE(emin)
+Dec_CONTEXT_GET_SSIZE(round)
+Dec_CONTEXT_GET_SSIZE(clamp)
+
+#ifdef EXTRA_FUNCTIONALITY
+Dec_CONTEXT_GET_ULONG(traps)
+Dec_CONTEXT_GET_ULONG(status)
+#endif
+
+static PyObject *
+context_getcapitals(PyObject *self, void *closure UNUSED)
+{
+ return PyLong_FromLong(CtxCaps(self));
+}
+
+#ifdef EXTRA_FUNCTIONALITY
+static PyObject *
+context_getallcr(PyObject *self, void *closure UNUSED)
+{
+ return PyLong_FromLong(mpd_getcr(CTX(self)));
+}
+#endif
+
+static PyObject *
+context_getetiny(PyObject *self, PyObject *dummy UNUSED)
+{
+ return PyLong_FromSsize_t(mpd_etiny(CTX(self)));
+}
+
+static PyObject *
+context_getetop(PyObject *self, PyObject *dummy UNUSED)
+{
+ return PyLong_FromSsize_t(mpd_etop(CTX(self)));
+}
+
+static int
+context_setprec(PyObject *self, PyObject *value, void *closure UNUSED)
+{
+ mpd_context_t *ctx;
+ mpd_ssize_t x;
+
+ x = PyLong_AsSsize_t(value);
+ if (x == -1 && PyErr_Occurred()) {
+ return -1;
+ }
+
+ ctx = CTX(self);
+ if (!mpd_qsetprec(ctx, x)) {
+ return value_error_int(
+ "valid range for prec is [1, MAX_PREC]");
+ }
+
+ return 0;
+}
+
+static int
+context_setemin(PyObject *self, PyObject *value, void *closure UNUSED)
+{
+ mpd_context_t *ctx;
+ mpd_ssize_t x;
+
+ x = PyLong_AsSsize_t(value);
+ if (x == -1 && PyErr_Occurred()) {
+ return -1;
+ }
+
+ ctx = CTX(self);
+ if (!mpd_qsetemin(ctx, x)) {
+ return value_error_int(
+ "valid range for Emin is [MIN_EMIN, 0]");
+ }
+
+ return 0;
+}
+
+static int
+context_setemax(PyObject *self, PyObject *value, void *closure UNUSED)
+{
+ mpd_context_t *ctx;
+ mpd_ssize_t x;
+
+ x = PyLong_AsSsize_t(value);
+ if (x == -1 && PyErr_Occurred()) {
+ return -1;
+ }
+
+ ctx = CTX(self);
+ if (!mpd_qsetemax(ctx, x)) {
+ return value_error_int(
+ "valid range for Emax is [0, MAX_EMAX]");
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_32
+static PyObject *
+context_unsafe_setprec(PyObject *self, PyObject *value)
+{
+ mpd_context_t *ctx = CTX(self);
+ mpd_ssize_t x;
+
+ x = PyLong_AsSsize_t(value);
+ if (x == -1 && PyErr_Occurred()) {
+ return NULL;
+ }
+
+ if (x < 1 || x > 1070000000L) {
+ return value_error_ptr(
+ "valid range for unsafe prec is [1, 1070000000]");
+ }
+
+ ctx->prec = x;
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+context_unsafe_setemin(PyObject *self, PyObject *value)
+{
+ mpd_context_t *ctx = CTX(self);
+ mpd_ssize_t x;
+
+ x = PyLong_AsSsize_t(value);
+ if (x == -1 && PyErr_Occurred()) {
+ return NULL;
+ }
+
+ if (x < -1070000000L || x > 0) {
+ return value_error_ptr(
+ "valid range for unsafe emin is [-1070000000, 0]");
+ }
+
+ ctx->emin = x;
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+context_unsafe_setemax(PyObject *self, PyObject *value)
+{
+ mpd_context_t *ctx = CTX(self);
+ mpd_ssize_t x;
+
+ x = PyLong_AsSsize_t(value);
+ if (x == -1 && PyErr_Occurred()) {
+ return NULL;
+ }
+
+ if (x < 0 || x > 1070000000L) {
+ return value_error_ptr(
+ "valid range for unsafe emax is [0, 1070000000]");
+ }
+
+ ctx->emax = x;
+ Py_RETURN_NONE;
+}
+#endif
+
+static int
+context_setround(PyObject *self, PyObject *value, void *closure UNUSED)
+{
+ mpd_context_t *ctx;
+ mpd_ssize_t x;
+
+ x = PyLong_AsSsize_t(value);
+ if (x == -1 && PyErr_Occurred()) {
+ return -1;
+ }
+ BOUNDS_CHECK(x, INT_MIN, INT_MAX);
+
+ ctx = CTX(self);
+ if (!mpd_qsetround(ctx, (int)x)) {
+ return type_error_int(invalid_rounding_err);
+ }
+
+ return 0;
+}
+
+static int
+context_setcapitals(PyObject *self, PyObject *value, void *closure UNUSED)
+{
+ mpd_ssize_t x;
+
+ x = PyLong_AsSsize_t(value);
+ if (x == -1 && PyErr_Occurred()) {
+ return -1;
+ }
+
+ if (x != 0 && x != 1) {
+ return value_error_int(
+ "valid values for capitals are 0 or 1");
+ }
+ CtxCaps(self) = (int)x;
+
+ return 0;
+}
+
+#ifdef EXTRA_FUNCTIONALITY
+static int
+context_settraps(PyObject *self, PyObject *value, void *closure UNUSED)
+{
+ mpd_context_t *ctx;
+ uint32_t flags;
+
+ flags = long_as_flags(value);
+ if (flags & DEC_ERRORS) {
+ return -1;
+ }
+
+ ctx = CTX(self);
+ if (!mpd_qsettraps(ctx, flags)) {
+ INTERNAL_ERROR_INT("context_settraps");
+ }
+
+ return 0;
+}
+#endif
+
+static int
+context_settraps_list(PyObject *self, PyObject *value)
+{
+ mpd_context_t *ctx;
+ uint32_t flags;
+
+ flags = list_as_flags(value);
+ if (flags & DEC_ERRORS) {
+ return -1;
+ }
+
+ ctx = CTX(self);
+ if (!mpd_qsettraps(ctx, flags)) {
+ INTERNAL_ERROR_INT("context_settraps_list");
+ }
+
+ return 0;
+}
+
+static int
+context_settraps_dict(PyObject *self, PyObject *value)
+{
+ mpd_context_t *ctx;
+ uint32_t flags;
+
+ if (PyDecSignalDict_Check(value)) {
+ flags = SdFlags(value);
+ }
+ else {
+ flags = dict_as_flags(value);
+ if (flags & DEC_ERRORS) {
+ return -1;
+ }
+ }
+
+ ctx = CTX(self);
+ if (!mpd_qsettraps(ctx, flags)) {
+ INTERNAL_ERROR_INT("context_settraps_dict");
+ }
+
+ return 0;
+}
+
+#ifdef EXTRA_FUNCTIONALITY
+static int
+context_setstatus(PyObject *self, PyObject *value, void *closure UNUSED)
+{
+ mpd_context_t *ctx;
+ uint32_t flags;
+
+ flags = long_as_flags(value);
+ if (flags & DEC_ERRORS) {
+ return -1;
+ }
+
+ ctx = CTX(self);
+ if (!mpd_qsetstatus(ctx, flags)) {
+ INTERNAL_ERROR_INT("context_setstatus");
+ }
+
+ return 0;
+}
+#endif
+
+static int
+context_setstatus_list(PyObject *self, PyObject *value)
+{
+ mpd_context_t *ctx;
+ uint32_t flags;
+
+ flags = list_as_flags(value);
+ if (flags & DEC_ERRORS) {
+ return -1;
+ }
+
+ ctx = CTX(self);
+ if (!mpd_qsetstatus(ctx, flags)) {
+ INTERNAL_ERROR_INT("context_setstatus_list");
+ }
+
+ return 0;
+}
+
+static int
+context_setstatus_dict(PyObject *self, PyObject *value)
+{
+ mpd_context_t *ctx;
+ uint32_t flags;
+
+ if (PyDecSignalDict_Check(value)) {
+ flags = SdFlags(value);
+ }
+ else {
+ flags = dict_as_flags(value);
+ if (flags & DEC_ERRORS) {
+ return -1;
+ }
+ }
+
+ ctx = CTX(self);
+ if (!mpd_qsetstatus(ctx, flags)) {
+ INTERNAL_ERROR_INT("context_setstatus_dict");
+ }
+
+ return 0;
+}
+
+static int
+context_setclamp(PyObject *self, PyObject *value, void *closure UNUSED)
+{
+ mpd_context_t *ctx;
+ mpd_ssize_t x;
+
+ x = PyLong_AsSsize_t(value);
+ if (x == -1 && PyErr_Occurred()) {
+ return -1;
+ }
+ BOUNDS_CHECK(x, INT_MIN, INT_MAX);
+
+ ctx = CTX(self);
+ if (!mpd_qsetclamp(ctx, (int)x)) {
+ return value_error_int("valid values for clamp are 0 or 1");
+ }
+
+ return 0;
+}
+
+#ifdef EXTRA_FUNCTIONALITY
+static int
+context_setallcr(PyObject *self, PyObject *value, void *closure UNUSED)
+{
+ mpd_context_t *ctx;
+ mpd_ssize_t x;
+
+ x = PyLong_AsSsize_t(value);
+ if (x == -1 && PyErr_Occurred()) {
+ return -1;
+ }
+ BOUNDS_CHECK(x, INT_MIN, INT_MAX);
+
+ ctx = CTX(self);
+ if (!mpd_qsetcr(ctx, (int)x)) {
+ return value_error_int("valid values for _allcr are 0 or 1");
+ }
+
+ return 0;
+}
+#endif
+
+static PyObject *
+context_getattr(PyObject *self, PyObject *name)
+{
+ PyObject *retval;
+
+ if (PyUnicode_Check(name)) {
+ if (PyUnicode_CompareWithASCIIString(name, "traps") == 0) {
+ retval = ((PyDecContextObject *)self)->traps;
+ Py_INCREF(retval);
+ return retval;
+ }
+ if (PyUnicode_CompareWithASCIIString(name, "flags") == 0) {
+ retval = ((PyDecContextObject *)self)->flags;
+ Py_INCREF(retval);
+ return retval;
+ }
+ }
+
+ return PyObject_GenericGetAttr(self, name);
+}
+
+static int
+context_setattr(PyObject *self, PyObject *name, PyObject *value)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_AttributeError,
+ "context attributes cannot be deleted");
+ return -1;
+ }
+
+ if (PyUnicode_Check(name)) {
+ if (PyUnicode_CompareWithASCIIString(name, "traps") == 0) {
+ return context_settraps_dict(self, value);
+ }
+ if (PyUnicode_CompareWithASCIIString(name, "flags") == 0) {
+ return context_setstatus_dict(self, value);
+ }
+ }
+
+ return PyObject_GenericSetAttr(self, name, value);
+}
+
+static PyObject *
+context_clear_traps(PyObject *self, PyObject *dummy UNUSED)
+{
+ CTX(self)->traps = 0;
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+context_clear_flags(PyObject *self, PyObject *dummy UNUSED)
+{
+ CTX(self)->status = 0;
+ Py_RETURN_NONE;
+}
+
+#define DEC_DFLT_EMAX 999999
+#define DEC_DFLT_EMIN -999999
+
+static mpd_context_t dflt_ctx = {
+ 28, DEC_DFLT_EMAX, DEC_DFLT_EMIN,
+ MPD_IEEE_Invalid_operation|MPD_Division_by_zero|MPD_Overflow,
+ 0, 0, MPD_ROUND_HALF_EVEN, 0, 1
+};
+
+static PyObject *
+context_new(PyTypeObject *type, PyObject *args UNUSED, PyObject *kwds UNUSED)
+{
+ PyDecContextObject *self = NULL;
+ mpd_context_t *ctx;
+
+ if (type == &PyDecContext_Type) {
+ self = PyObject_New(PyDecContextObject, &PyDecContext_Type);
+ }
+ else {
+ self = (PyDecContextObject *)type->tp_alloc(type, 0);
+ }
+
+ if (self == NULL) {
+ return NULL;
+ }
+
+ self->traps = PyObject_CallObject((PyObject *)PyDecSignalDict_Type, NULL);
+ if (self->traps == NULL) {
+ self->flags = NULL;
+ Py_DECREF(self);
+ return NULL;
+ }
+ self->flags = PyObject_CallObject((PyObject *)PyDecSignalDict_Type, NULL);
+ if (self->flags == NULL) {
+ Py_DECREF(self);
+ return NULL;
+ }
+
+ ctx = CTX(self);
+
+ if (default_context_template) {
+ *ctx = *CTX(default_context_template);
+ }
+ else {
+ *ctx = dflt_ctx;
+ }
+
+ SdFlagAddr(self->traps) = &ctx->traps;
+ SdFlagAddr(self->flags) = &ctx->status;
+
+ CtxCaps(self) = 1;
+
+ return (PyObject *)self;
+}
+
+static void
+context_dealloc(PyDecContextObject *self)
+{
+ Py_XDECREF(self->traps);
+ Py_XDECREF(self->flags);
+ Py_TYPE(self)->tp_free(self);
+}
+
+static int
+getround(PyObject *v)
+{
+ const char *s;
+ long x;
+ int i;
+
+ if (PyLong_Check(v)) {
+ x = PyLong_AsLong(v);
+ if (x == -1 && PyErr_Occurred()) {
+ return -1;
+ }
+ BOUNDS_CHECK(x, 0, INT_MAX);
+ return (int)x;
+ }
+ else if (PyUnicode_Check(v)) {
+ for (i = 0; i < MPD_ROUND_GUARD; i++) {
+ s = mpd_round_string[i];
+ if (PyUnicode_CompareWithASCIIString(v, s) == 0) {
+ return i;
+ }
+ }
+ }
+
+ return type_error_int("invalid rounding mode");
+}
+
+static int
+context_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {
+ "prec", "rounding", "Emin", "Emax", "capitals", "clamp",
+ "flags", "traps", NULL
+ };
+ PyObject *rounding = NULL;
+ PyObject *traps = NULL;
+ PyObject *status = NULL;
+ mpd_context_t *ctx, t;
+ int capitals = 1;
+ int ret;
+
+ assert(PyTuple_Check(args));
+ ctx = CTX(self);
+
+ t = *ctx;
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kwds,
+ "|nOnniiOO", kwlist,
+ &t.prec, &rounding, &t.emin, &t.emax, &capitals, &t.clamp,
+ &status, &traps
+ )) {
+ return -1;
+ }
+ if (rounding != NULL) {
+ t.round = getround(rounding);
+ if (t.round < 0) {
+ return -1;
+ }
+ }
+
+ if (!mpd_qsetprec(ctx, t.prec) ||
+ !mpd_qsetemin(ctx, t.emin) ||
+ !mpd_qsetemax(ctx, t.emax) ||
+ !mpd_qsetclamp(ctx, t.clamp)) {
+ return value_error_int("invalid context");
+ }
+ if (!mpd_qsetround(ctx, t.round) ||
+ !mpd_qsettraps(ctx, t.traps) ||
+ !mpd_qsetstatus(ctx, t.status)) {
+ return type_error_int("invalid context");
+ }
+
+ if (capitals != 0 && capitals != 1) {
+ return value_error_int("invalid context");
+ }
+ CtxCaps(self) = capitals;
+
+ if (traps != NULL) {
+ if (PyList_Check(traps)) {
+ ret = context_settraps_list(self, traps);
+ }
+#ifdef EXTRA_FUNCTIONALITY
+ else if (PyLong_Check(traps)) {
+ ret = context_settraps(self, traps, NULL);
+ }
+#endif
+ else {
+ ret = context_settraps_dict(self, traps);
+ }
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ if (status != NULL) {
+ if (PyList_Check(status)) {
+ ret = context_setstatus_list(self, status);
+ }
+#ifdef EXTRA_FUNCTIONALITY
+ else if (PyLong_Check(status)) {
+ ret = context_setstatus(self, status, NULL);
+ }
+#endif
+ else {
+ ret = context_setstatus_dict(self, status);
+ }
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static PyObject *
+context_repr(PyDecContextObject *self)
+{
+ mpd_context_t *ctx;
+ char flags[MPD_MAX_SIGNAL_LIST];
+ char traps[MPD_MAX_SIGNAL_LIST];
+ int n, mem;
+
+ assert(PyDecContext_Check(self));
+ ctx = CTX(self);
+
+ mem = MPD_MAX_SIGNAL_LIST;
+ n = mpd_lsnprint_signals(flags, mem, ctx->status, dec_signal_string);
+ if (n < 0 || n >= mem) {
+ INTERNAL_ERROR_PTR("context_repr");
+ }
+
+ n = mpd_lsnprint_signals(traps, mem, ctx->traps, dec_signal_string);
+ if (n < 0 || n >= mem) {
+ INTERNAL_ERROR_PTR("context_repr");
+ }
+
+ return PyUnicode_FromFormat(
+ "Context(prec=%zd, rounding=%s, Emin=%zd, Emax=%zd, "
+ "capitals=%d, clamp=%d, flags=%s, traps=%s)",
+ ctx->prec, mpd_round_string[ctx->round], ctx->emin, ctx->emax,
+ self->capitals, ctx->clamp, flags, traps);
+}
+
+static void
+init_basic_context(PyObject *v)
+{
+ mpd_context_t ctx = dflt_ctx;
+
+ ctx.prec = 9;
+ ctx.traps |= (MPD_Underflow|MPD_Clamped);
+ ctx.round = MPD_ROUND_HALF_UP;
+
+ *CTX(v) = ctx;
+ CtxCaps(v) = 1;
+}
+
+static void
+init_extended_context(PyObject *v)
+{
+ mpd_context_t ctx = dflt_ctx;
+
+ ctx.prec = 9;
+ ctx.traps = 0;
+
+ *CTX(v) = ctx;
+ CtxCaps(v) = 1;
+}
+
+#ifdef EXTRA_FUNCTIONALITY
+/* Factory function for creating IEEE interchange format contexts */
+static PyObject *
+ieee_context(PyObject *dummy UNUSED, PyObject *v)
+{
+ PyObject *context;
+ mpd_ssize_t bits;
+ mpd_context_t ctx;
+
+ bits = PyLong_AsSsize_t(v);
+ if (bits == -1 && PyErr_Occurred()) {
+ return NULL;
+ }
+ if (bits <= 0 || bits > INT_MAX) {
+ goto error;
+ }
+ if (mpd_ieee_context(&ctx, (int)bits) < 0) {
+ goto error;
+ }
+
+ context = PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL);
+ if (context == NULL) {
+ return NULL;
+ }
+ *CTX(context) = ctx;
+
+ return context;
+
+error:
+ PyErr_Format(PyExc_ValueError,
+ "argument must be a multiple of 32, with a maximum of %d",
+ MPD_IEEE_CONTEXT_MAX_BITS);
+
+ return NULL;
+}
+#endif
+
+static PyObject *
+context_copy(PyObject *self)
+{
+ PyObject *copy;
+
+ copy = PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL);
+ if (copy == NULL) {
+ return NULL;
+ }
+
+ *CTX(copy) = *CTX(self);
+ CTX(copy)->newtrap = 0;
+ CtxCaps(copy) = CtxCaps(self);
+
+ return copy;
+}
+
+static PyObject *
+context_reduce(PyObject *self, PyObject *args UNUSED)
+{
+ PyObject *flags;
+ PyObject *traps;
+ PyObject *ret;
+ mpd_context_t *ctx;
+
+ ctx = CTX(self);
+
+ flags = signals_as_list(ctx->status);
+ if (flags == NULL) {
+ return NULL;
+ }
+ traps = signals_as_list(ctx->traps);
+ if (traps == NULL) {
+ Py_DECREF(flags);
+ return NULL;
+ }
+
+ ret = Py_BuildValue(
+ "O(nsnniiOO)",
+ Py_TYPE(self),
+ ctx->prec, mpd_round_string[ctx->round], ctx->emin, ctx->emax,
+ CtxCaps(self), ctx->clamp, flags, traps
+ );
+
+ Py_DECREF(flags);
+ Py_DECREF(traps);
+ return ret;
+}
+
+
+static PyGetSetDef context_getsets [] =
+{
+ { "prec", (getter)context_getprec, (setter)context_setprec, NULL, NULL},
+ { "Emax", (getter)context_getemax, (setter)context_setemax, NULL, NULL},
+ { "Emin", (getter)context_getemin, (setter)context_setemin, NULL, NULL},
+ { "rounding", (getter)context_getround, (setter)context_setround, NULL, NULL},
+ { "capitals", (getter)context_getcapitals, (setter)context_setcapitals, NULL, NULL},
+ { "clamp", (getter)context_getclamp, (setter)context_setclamp, NULL, NULL},
+#ifdef EXTRA_FUNCTIONALITY
+ { "_allcr", (getter)context_getallcr, (setter)context_setallcr, NULL, NULL},
+ { "_traps", (getter)context_gettraps, (setter)context_settraps, NULL, NULL},
+ { "_flags", (getter)context_getstatus, (setter)context_setstatus, NULL, NULL},
+#endif
+ {NULL}
+};
+
+
+#define CONTEXT_CHECK(obj) \
+ if (!PyDecContext_Check(obj)) { \
+ PyErr_SetString(PyExc_TypeError, \
+ "argument must be a context"); \
+ return NULL; \
+ }
+
+#define CONTEXT_CHECK_VA(obj) \
+ if (!PyDecContext_Check(obj)) { \
+ PyErr_SetString(PyExc_TypeError, \
+ "optional argument must be a context"); \
+ return NULL; \
+ }
+
+
+/******************************************************************************/
+/* Global, thread local and temporary contexts */
+/******************************************************************************/
+
+#ifdef WITHOUT_THREADS
+/* Return borrowed reference to the current context. When compiled
+ * without threads, this is always the module context. */
+static int module_context_set = 0;
+static PyObject *
+current_context(void)
+{
+ /* In decimal.py, the module context is automatically initialized
+ * from the DefaultContext when it is first accessed. This
+ * complicates the code and has a speed penalty of 1-2%. */
+ if (module_context_set) {
+ return module_context;
+ }
+
+ *CTX(module_context) = *CTX(default_context_template);
+ module_context_set = 1;
+ return module_context;
+}
+
+/* ctxobj := borrowed reference to the current context */
+#define CURRENT_CONTEXT(ctxobj) \
+ ctxobj = current_context()
+
+/* ctx := pointer to the mpd_context_t struct of the current context */
+#define CURRENT_CONTEXT_ADDR(ctx) \
+ ctx = CTX(current_context())
+
+/* Return current context, increment reference */
+static PyObject *
+PyDec_GetCurrentContext(void)
+{
+ PyObject *context;
+
+ CURRENT_CONTEXT(context);
+
+ Py_INCREF(context);
+ return context;
+}
+
+/* Set the module context to a new context, decrement old reference */
+static PyObject *
+PyDec_SetCurrentContext(PyObject *self UNUSED, PyObject *v)
+{
+ CONTEXT_CHECK(v);
+
+ /* If the new context is one of the templates, make a copy.
+ * This is the current behavior of decimal.py. */
+ if (v == default_context_template ||
+ v == basic_context_template ||
+ v == extended_context_template) {
+ v = context_copy(v);
+ if (v == NULL) {
+ return NULL;
+ }
+ }
+ else {
+ Py_INCREF(v);
+ }
+
+ Py_XDECREF(module_context);
+ module_context = v;
+ module_context_set = 1;
+ Py_RETURN_NONE;
+}
+#else
+/*
+ * Thread local storage currently has a speed penalty of about 16%.
+ * All functions that map Python's arithmetic operators to mpdecimal
+ * functions have to look up the current context for each and every
+ * operation.
+ */
+
+/* Return borrowed reference to thread local context. */
+static PyObject *
+current_context(void)
+{
+ PyObject *dict = NULL;
+ PyObject *tl_context = NULL;
+
+ dict = PyThreadState_GetDict();
+ if (dict == NULL) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "cannot get thread state");
+ return NULL;
+ }
+
+ tl_context = PyDict_GetItemWithError(dict, tls_context_key);
+ if (tl_context != NULL) {
+ /* We already have a thread local context and
+ * return a borrowed reference. */
+ CONTEXT_CHECK(tl_context);
+ return tl_context;
+ }
+ if (PyErr_Occurred()) {
+ return NULL;
+ }
+
+ /* Otherwise, set up a new thread local context. */
+ tl_context = context_copy(default_context_template);
+ if (tl_context == NULL) {
+ return NULL;
+ }
+ if (PyDict_SetItem(dict, tls_context_key, tl_context) < 0) {
+ Py_DECREF(tl_context);
+ return NULL;
+ }
+ Py_DECREF(tl_context);
+
+ /* refcount is 1 */
+ return tl_context;
+}
+
+/* ctxobj := borrowed reference to the current context */
+#define CURRENT_CONTEXT(ctxobj) \
+ ctxobj = current_context(); \
+ if (ctxobj == NULL) { \
+ return NULL; \
+ }
+
+/* ctx := pointer to the mpd_context_t struct of the current context */
+#define CURRENT_CONTEXT_ADDR(ctx) { \
+ PyObject *_c_t_x_o_b_j = current_context(); \
+ if (_c_t_x_o_b_j == NULL) { \
+ return NULL; \
+ } \
+ ctx = CTX(_c_t_x_o_b_j); \
+}
+
+/* Return current context, increment reference */
+static PyObject *
+PyDec_GetCurrentContext(void)
+{
+ PyObject *context;
+
+ context = current_context();
+ if (context == NULL) {
+ return NULL;
+ }
+
+ Py_INCREF(context);
+ return context;
+}
+
+/* Set the thread local context to a new context, decrement old reference */
+static PyObject *
+PyDec_SetCurrentContext(PyObject *self UNUSED, PyObject *v)
+{
+ PyObject *dict;
+
+ CONTEXT_CHECK(v);
+
+ dict = PyThreadState_GetDict();
+ if (dict == NULL) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "cannot get thread state");
+ return NULL;
+ }
+
+ /* If the new context is one of the templates, make a copy.
+ * This is the current behavior of decimal.py. */
+ if (v == default_context_template ||
+ v == basic_context_template ||
+ v == extended_context_template) {
+ v = context_copy(v);
+ if (v == NULL) {
+ return NULL;
+ }
+ }
+ else {
+ Py_INCREF(v);
+ }
+
+ if (PyDict_SetItem(dict, tls_context_key, v) < 0) {
+ Py_DECREF(v);
+ return NULL;
+ }
+
+ Py_DECREF(v);
+ Py_RETURN_NONE;
+}
+#endif
+
+/* Context manager object for the 'with' statement. The manager
+ * owns one reference to the global (outer) context and one
+ * to the local (inner) context. */
+static PyObject *
+ctxmanager_new(PyTypeObject *type UNUSED, PyObject *args)
+{
+ PyDecContextManagerObject *self;
+ PyObject *local;
+ PyObject *global;
+
+ CURRENT_CONTEXT(global);
+ local = global;
+ if (!PyArg_ParseTuple(args, "|O", &local)) {
+ return NULL;
+ }
+ CONTEXT_CHECK_VA(local);
+
+ self = PyObject_New(PyDecContextManagerObject,
+ &PyDecContextManager_Type);
+ if (self == NULL) {
+ return NULL;
+ }
+
+ self->local = context_copy(local);
+ if (self->local == NULL) {
+ self->global = NULL;
+ Py_DECREF(self);
+ return NULL;
+ }
+ self->global = global;
+ Py_INCREF(self->global);
+
+ return (PyObject *)self;
+}
+
+static void
+ctxmanager_dealloc(PyDecContextManagerObject *self)
+{
+ Py_XDECREF(self->local);
+ Py_XDECREF(self->global);
+ PyObject_Del(self);
+}
+
+static PyObject *
+ctxmanager_set_local(PyDecContextManagerObject *self, PyObject *args UNUSED)
+{
+ PyObject *ret;
+
+ ret = PyDec_SetCurrentContext(NULL, self->local);
+ if (ret == NULL) {
+ return NULL;
+ }
+ Py_DECREF(ret);
+
+ Py_INCREF(self->local);
+ return self->local;
+}
+
+static PyObject *
+ctxmanager_restore_global(PyDecContextManagerObject *self,
+ PyObject *args UNUSED)
+{
+ PyObject *ret;
+
+ ret = PyDec_SetCurrentContext(NULL, self->global);
+ if (ret == NULL) {
+ return NULL;
+ }
+ Py_DECREF(ret);
+
+ Py_RETURN_NONE;
+}
+
+
+static PyMethodDef ctxmanager_methods[] = {
+ {"__enter__", (PyCFunction)ctxmanager_set_local, METH_NOARGS, NULL},
+ {"__exit__", (PyCFunction)ctxmanager_restore_global, METH_VARARGS, NULL},
+ {NULL, NULL}
+};
+
+static PyTypeObject PyDecContextManager_Type =
+{
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "decimal.ContextManager", /* tp_name */
+ sizeof(PyDecContextManagerObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor) ctxmanager_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ (getattrfunc) 0, /* tp_getattr */
+ (setattrfunc) 0, /* tp_setattr */
+ 0, /* tp_compare */
+ (reprfunc) 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ (getattrofunc) PyObject_GenericGetAttr, /* tp_getattro */
+ (setattrofunc) 0, /* tp_setattro */
+ (PyBufferProcs *) 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ 0, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ ctxmanager_methods, /* tp_methods */
+};
+
+
+/******************************************************************************/
+/* New Decimal Object */
+/******************************************************************************/
+
+static PyObject *
+PyDecType_New(PyTypeObject *type)
+{
+ PyObject *dec;
+
+ if (type == &PyDec_Type) {
+ dec = (PyObject *)PyObject_New(PyDecObject, &PyDec_Type);
+ }
+ else {
+ dec = type->tp_alloc(type, 0);
+ }
+ if (dec == NULL) {
+ return NULL;
+ }
+
+ MPD(dec) = mpd_qnew();
+ if (MPD(dec) == NULL) {
+ Py_DECREF(dec);
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ return dec;
+}
+#define dec_alloc() PyDecType_New(&PyDec_Type)
+
+static void
+dec_dealloc(PyObject *dec)
+{
+ if (MPD(dec)) {
+ mpd_del(MPD(dec));
+ }
+ Py_TYPE(dec)->tp_free(dec);
+}
+
+
+/******************************************************************************/
+/* Conversions to Decimal */
+/******************************************************************************/
+
+Py_LOCAL_INLINE(int)
+is_space(enum PyUnicode_Kind kind, void *data, Py_ssize_t pos)
+{
+ Py_UCS4 ch = PyUnicode_READ(kind, data, pos);
+ return Py_UNICODE_ISSPACE(ch);
+}
+
+/* Return the ASCII representation of a numeric Unicode string. The numeric
+ string may contain ascii characters in the range [1, 127], any Unicode
+ space and any unicode digit. If strip_ws is true, leading and trailing
+ whitespace is stripped.
+
+ Return NULL if malloc fails and an empty string if invalid characters
+ are found. */
+static char *
+numeric_as_ascii(const PyObject *u, int strip_ws)
+{
+ enum PyUnicode_Kind kind;
+ void *data;
+ Py_UCS4 ch;
+ char *res, *cp;
+ Py_ssize_t j, len;
+ int d;
+
+ assert(PyUnicode_IS_READY(u));
+
+ kind = PyUnicode_KIND(u);
+ data = PyUnicode_DATA(u);
+ len = PyUnicode_GET_LENGTH(u);
+
+ cp = res = PyMem_Malloc(len+1);
+ if (res == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ j = 0;
+ if (strip_ws) {
+ while (len > 0 && is_space(kind, data, len-1)) {
+ len--;
+ }
+ while (j < len && is_space(kind, data, j)) {
+ j++;
+ }
+ }
+
+ for (; j < len; j++) {
+ ch = PyUnicode_READ(kind, data, j);
+ if (0 < ch && ch <= 127) {
+ *cp++ = ch;
+ continue;
+ }
+ if (Py_UNICODE_ISSPACE(ch)) {
+ *cp++ = ' ';
+ continue;
+ }
+ d = Py_UNICODE_TODECIMAL(ch);
+ if (d < 0) {
+ /* empty string triggers ConversionSyntax */
+ *res = '\0';
+ return res;
+ }
+ *cp++ = '0' + d;
+ }
+ *cp = '\0';
+ return res;
+}
+
+/* Return a new PyDecObject or a subtype from a C string. Use the context
+ during conversion. */
+static PyObject *
+PyDecType_FromCString(PyTypeObject *type, const char *s,
+ PyObject *context)
+{
+ PyObject *dec;
+ uint32_t status = 0;
+
+ dec = PyDecType_New(type);
+ if (dec == NULL) {
+ return NULL;
+ }
+
+ mpd_qset_string(MPD(dec), s, CTX(context), &status);
+ if (dec_addstatus(context, status)) {
+ Py_DECREF(dec);
+ return NULL;
+ }
+ return dec;
+}
+
+/* Return a new PyDecObject or a subtype from a C string. Attempt exact
+ conversion. If the operand cannot be converted exactly, set
+ InvalidOperation. */
+static PyObject *
+PyDecType_FromCStringExact(PyTypeObject *type, const char *s,
+ PyObject *context)
+{
+ PyObject *dec;
+ uint32_t status = 0;
+ mpd_context_t maxctx;
+
+ dec = PyDecType_New(type);
+ if (dec == NULL) {
+ return NULL;
+ }
+
+ mpd_maxcontext(&maxctx);
+
+ mpd_qset_string(MPD(dec), s, &maxctx, &status);
+ if (status & (MPD_Inexact|MPD_Rounded)) {
+ /* we want exact results */
+ mpd_seterror(MPD(dec), MPD_Invalid_operation, &status);
+ }
+ status &= MPD_Errors;
+ if (dec_addstatus(context, status)) {
+ Py_DECREF(dec);
+ return NULL;
+ }
+
+ return dec;
+}
+
+/* Return a new PyDecObject or a subtype from a PyUnicodeObject. */
+static PyObject *
+PyDecType_FromUnicode(PyTypeObject *type, const PyObject *u,
+ PyObject *context)
+{
+ PyObject *dec;
+ char *s;
+
+ s = numeric_as_ascii(u, 0);
+ if (s == NULL) {
+ return NULL;
+ }
+
+ dec = PyDecType_FromCString(type, s, context);
+ PyMem_Free(s);
+ return dec;
+}
+
+/* Return a new PyDecObject or a subtype from a PyUnicodeObject. Attempt exact
+ * conversion. If the conversion is not exact, fail with InvalidOperation.
+ * Allow leading and trailing whitespace in the input operand. */
+static PyObject *
+PyDecType_FromUnicodeExactWS(PyTypeObject *type, const PyObject *u,
+ PyObject *context)
+{
+ PyObject *dec;
+ char *s;
+
+ s = numeric_as_ascii(u, 1);
+ if (s == NULL) {
+ return NULL;
+ }
+
+ dec = PyDecType_FromCStringExact(type, s, context);
+ PyMem_Free(s);
+ return dec;
+}
+
+/* Set PyDecObject from triple without any error checking. */
+Py_LOCAL_INLINE(void)
+_dec_settriple(PyObject *dec, uint8_t sign, uint32_t v, mpd_ssize_t exp)
+{
+
+#ifdef CONFIG_64
+ MPD(dec)->data[0] = v;
+ MPD(dec)->len = 1;
+#else
+ uint32_t q, r;
+ q = v / MPD_RADIX;
+ r = v - q * MPD_RADIX;
+ MPD(dec)->data[1] = q;
+ MPD(dec)->data[0] = r;
+ MPD(dec)->len = q ? 2 : 1;
+#endif
+ mpd_set_flags(MPD(dec), sign);
+ MPD(dec)->exp = exp;
+ mpd_setdigits(MPD(dec));
+}
+
+/* Return a new PyDecObject from an mpd_ssize_t. */
+static PyObject *
+PyDecType_FromSsize(PyTypeObject *type, mpd_ssize_t v, PyObject *context)
+{
+ PyObject *dec;
+ uint32_t status = 0;
+
+ dec = PyDecType_New(type);
+ if (dec == NULL) {
+ return NULL;
+ }
+
+ mpd_qset_ssize(MPD(dec), v, CTX(context), &status);
+ if (dec_addstatus(context, status)) {
+ Py_DECREF(dec);
+ return NULL;
+ }
+ return dec;
+}
+
+/* Return a new PyDecObject from an mpd_ssize_t. Conversion is exact. */
+static PyObject *
+PyDecType_FromSsizeExact(PyTypeObject *type, mpd_ssize_t v, PyObject *context)
+{
+ PyObject *dec;
+ uint32_t status = 0;
+ mpd_context_t maxctx;
+
+ dec = PyDecType_New(type);
+ if (dec == NULL) {
+ return NULL;
+ }
+
+ mpd_maxcontext(&maxctx);
+
+ mpd_qset_ssize(MPD(dec), v, &maxctx, &status);
+ if (dec_addstatus(context, status)) {
+ Py_DECREF(dec);
+ return NULL;
+ }
+ return dec;
+}
+
+/* Convert from a PyLongObject. The context is not modified; flags set
+ during conversion are accumulated in the status parameter. */
+static PyObject *
+dec_from_long(PyTypeObject *type, const PyObject *v,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ PyObject *dec;
+ PyLongObject *l = (PyLongObject *)v;
+ Py_ssize_t ob_size;
+ size_t len;
+ uint8_t sign;
+
+ dec = PyDecType_New(type);
+ if (dec == NULL) {
+ return NULL;
+ }
+
+ ob_size = Py_SIZE(l);
+ if (ob_size == 0) {
+ _dec_settriple(dec, MPD_POS, 0, 0);
+ return dec;
+ }
+
+ if (ob_size < 0) {
+ len = -ob_size;
+ sign = MPD_NEG;
+ }
+ else {
+ len = ob_size;
+ sign = MPD_POS;
+ }
+
+ if (len == 1) {
+ _dec_settriple(dec, sign, *l->ob_digit, 0);
+ mpd_qfinalize(MPD(dec), ctx, status);
+ return dec;
+ }
+
+#if PYLONG_BITS_IN_DIGIT == 30
+ mpd_qimport_u32(MPD(dec), l->ob_digit, len, sign, PyLong_BASE,
+ ctx, status);
+#elif PYLONG_BITS_IN_DIGIT == 15
+ mpd_qimport_u16(MPD(dec), l->ob_digit, len, sign, PyLong_BASE,
+ ctx, status);
+#else
+ #error "PYLONG_BITS_IN_DIGIT should be 15 or 30"
+#endif
+
+ return dec;
+}
+
+/* Return a new PyDecObject from a PyLongObject. Use the context for
+ conversion. */
+static PyObject *
+PyDecType_FromLong(PyTypeObject *type, const PyObject *pylong,
+ PyObject *context)
+{
+ PyObject *dec;
+ uint32_t status = 0;
+
+ dec = dec_from_long(type, pylong, CTX(context), &status);
+ if (dec == NULL) {
+ return NULL;
+ }
+
+ if (dec_addstatus(context, status)) {
+ Py_DECREF(dec);
+ return NULL;
+ }
+
+ return dec;
+}
+
+/* Return a new PyDecObject from a PyLongObject. Use a maximum context
+ for conversion. If the conversion is not exact, set InvalidOperation. */
+static PyObject *
+PyDecType_FromLongExact(PyTypeObject *type, const PyObject *pylong,
+ PyObject *context)
+{
+ PyObject *dec;
+ uint32_t status = 0;
+ mpd_context_t maxctx;
+
+ mpd_maxcontext(&maxctx);
+ dec = dec_from_long(type, pylong, &maxctx, &status);
+ if (dec == NULL) {
+ return NULL;
+ }
+
+ if (status & (MPD_Inexact|MPD_Rounded)) {
+ /* we want exact results */
+ mpd_seterror(MPD(dec), MPD_Invalid_operation, &status);
+ }
+ status &= MPD_Errors;
+ if (dec_addstatus(context, status)) {
+ Py_DECREF(dec);
+ return NULL;
+ }
+
+ return dec;
+}
+
+/* Return a PyDecObject or a subtype from a PyFloatObject.
+ Conversion is exact. */
+static PyObject *
+PyDecType_FromFloatExact(PyTypeObject *type, PyObject *v,
+ PyObject *context)
+{
+ PyObject *dec, *tmp;
+ PyObject *n, *d, *n_d;
+ mpd_ssize_t k;
+ double x;
+ int sign;
+ mpd_t *d1, *d2;
+ uint32_t status = 0;
+ mpd_context_t maxctx;
+
+
+ assert(PyType_IsSubtype(type, &PyDec_Type));
+
+ if (PyLong_Check(v)) {
+ return PyDecType_FromLongExact(type, v, context);
+ }
+ if (!PyFloat_Check(v)) {
+ PyErr_SetString(PyExc_TypeError,
+ "argument must be int of float");
+ return NULL;
+ }
+
+ x = PyFloat_AsDouble(v);
+ if (x == -1.0 && PyErr_Occurred()) {
+ return NULL;
+ }
+ sign = (copysign(1.0, x) == 1.0) ? 0 : 1;
+
+ if (Py_IS_NAN(x) || Py_IS_INFINITY(x)) {
+ dec = PyDecType_New(type);
+ if (dec == NULL) {
+ return NULL;
+ }
+ if (Py_IS_NAN(x)) {
+ /* decimal.py calls repr(float(+-nan)),
+ * which always gives a positive result. */
+ mpd_setspecial(MPD(dec), MPD_POS, MPD_NAN);
+ }
+ else {
+ mpd_setspecial(MPD(dec), sign, MPD_INF);
+ }
+ return dec;
+ }
+
+ /* absolute value of the float */
+ tmp = PyObject_CallMethod(v, "__abs__", NULL);
+ if (tmp == NULL) {
+ return NULL;
+ }
+
+ /* float as integer ratio: numerator/denominator */
+ n_d = PyObject_CallMethod(tmp, "as_integer_ratio", NULL);
+ Py_DECREF(tmp);
+ if (n_d == NULL) {
+ return NULL;
+ }
+ n = PyTuple_GET_ITEM(n_d, 0);
+ d = PyTuple_GET_ITEM(n_d, 1);
+
+ tmp = PyObject_CallMethod(d, "bit_length", NULL);
+ if (tmp == NULL) {
+ Py_DECREF(n_d);
+ return NULL;
+ }
+ k = PyLong_AsSsize_t(tmp);
+ Py_DECREF(tmp);
+ if (k == -1 && PyErr_Occurred()) {
+ Py_DECREF(n_d);
+ return NULL;
+ }
+ k--;
+
+ dec = PyDecType_FromLongExact(type, n, context);
+ Py_DECREF(n_d);
+ if (dec == NULL) {
+ return NULL;
+ }
+
+ d1 = mpd_qnew();
+ if (d1 == NULL) {
+ Py_DECREF(dec);
+ PyErr_NoMemory();
+ return NULL;
+ }
+ d2 = mpd_qnew();
+ if (d2 == NULL) {
+ mpd_del(d1);
+ Py_DECREF(dec);
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ mpd_maxcontext(&maxctx);
+ mpd_qset_uint(d1, 5, &maxctx, &status);
+ mpd_qset_ssize(d2, k, &maxctx, &status);
+ mpd_qpow(d1, d1, d2, &maxctx, &status);
+ if (dec_addstatus(context, status)) {
+ mpd_del(d1);
+ mpd_del(d2);
+ Py_DECREF(dec);
+ return NULL;
+ }
+
+ /* result = n * 5**k */
+ mpd_qmul(MPD(dec), MPD(dec), d1, &maxctx, &status);
+ mpd_del(d1);
+ mpd_del(d2);
+ if (dec_addstatus(context, status)) {
+ Py_DECREF(dec);
+ return NULL;
+ }
+ /* result = +- n * 5**k * 10**-k */
+ mpd_set_sign(MPD(dec), sign);
+ MPD(dec)->exp = -k;
+
+ return dec;
+}
+
+static PyObject *
+PyDecType_FromFloat(PyTypeObject *type, PyObject *v,
+ PyObject *context)
+{
+ PyObject *dec;
+ uint32_t status = 0;
+
+ dec = PyDecType_FromFloatExact(type, v, context);
+ if (dec == NULL) {
+ return NULL;
+ }
+
+ mpd_qfinalize(MPD(dec), CTX(context), &status);
+ if (dec_addstatus(context, status)) {
+ Py_DECREF(dec);
+ return NULL;
+ }
+
+ return dec;
+}
+
+static PyObject *
+sequence_as_tuple(PyObject *v, PyObject *ex, const char *mesg)
+{
+ if (PyTuple_Check(v)) {
+ Py_INCREF(v);
+ return v;
+ }
+ if (PyList_Check(v)) {
+ return PyList_AsTuple(v);
+ }
+
+ PyErr_SetString(ex, mesg);
+ return NULL;
+}
+
+/* Return a new C string representation of a DecimalTuple. */
+static char *
+dectuple_as_str(PyObject *dectuple)
+{
+ PyObject *digits = NULL, *tmp;
+ char *decstring = NULL;
+ char sign_special[6];
+ char *cp;
+ long sign, l;
+ mpd_ssize_t exp = 0;
+ Py_ssize_t i, mem, tsize;
+ int n;
+
+ assert(PyTuple_Check(dectuple));
+
+ if (PyTuple_Size(dectuple) != 3) {
+ PyErr_SetString(PyExc_ValueError,
+ "argument must be a sequence of length 3");
+ goto error;
+ }
+
+ /* sign */
+ tmp = PyTuple_GET_ITEM(dectuple, 0);
+ if (!PyLong_Check(tmp)) {
+ PyErr_SetString(PyExc_ValueError,
+ "sign must be an integer with the value 0 or 1");
+ goto error;
+ }
+ sign = PyLong_AsLong(tmp);
+ if (sign == -1 && PyErr_Occurred()) {
+ goto error;
+ }
+ if (sign != 0 && sign != 1) {
+ PyErr_SetString(PyExc_ValueError,
+ "sign must be an integer with the value 0 or 1");
+ goto error;
+ }
+ sign_special[0] = sign ? '-' : '+';
+ sign_special[1] = '\0';
+
+ /* exponent or encoding for a special number */
+ tmp = PyTuple_GET_ITEM(dectuple, 2);
+ if (PyUnicode_Check(tmp)) {
+ /* special */
+ if (PyUnicode_CompareWithASCIIString(tmp, "F") == 0) {
+ strcat(sign_special, "Inf");
+ }
+ else if (PyUnicode_CompareWithASCIIString(tmp, "n") == 0) {
+ strcat(sign_special, "NaN");
+ }
+ else if (PyUnicode_CompareWithASCIIString(tmp, "N") == 0) {
+ strcat(sign_special, "sNaN");
+ }
+ else {
+ PyErr_SetString(PyExc_ValueError,
+ "string argument in the third position "
+ "must be 'F', 'n' or 'N'");
+ goto error;
+ }
+ }
+ else {
+ /* exponent */
+ if (!PyLong_Check(tmp)) {
+ PyErr_SetString(PyExc_ValueError,
+ "exponent must be an integer");
+ goto error;
+ }
+ exp = PyLong_AsSsize_t(tmp);
+ if (exp == -1 && PyErr_Occurred()) {
+ goto error;
+ }
+ }
+
+ /* coefficient */
+ digits = sequence_as_tuple(PyTuple_GET_ITEM(dectuple, 1),
+ PyExc_ValueError, "coefficient must be a tuple of digits");
+ if (digits == NULL) {
+ goto error;
+ }
+
+ tsize = PyTuple_Size(digits);
+ /* [sign][coeffdigits+1][E][-][expdigits+1]['\0'] */
+ mem = 1 + tsize + 3 + MPD_EXPDIGITS + 2;
+ cp = decstring = PyMem_Malloc(mem);
+ if (decstring == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ n = snprintf(cp, mem, "%s", sign_special);
+ if (n < 0 || n >= mem) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "internal error in dec_sequence_as_str");
+ goto error;
+ }
+ cp += n;
+
+ if (tsize == 0 && sign_special[1] == '\0') {
+ /* empty tuple: zero coefficient, except for special numbers */
+ *cp++ = '0';
+ }
+ for (i = 0; i < tsize; i++) {
+ tmp = PyTuple_GET_ITEM(digits, i);
+ if (!PyLong_Check(tmp)) {
+ PyErr_SetString(PyExc_ValueError,
+ "coefficient must be a tuple of digits");
+ goto error;
+ }
+ l = PyLong_AsLong(tmp);
+ if (l == -1 && PyErr_Occurred()) {
+ goto error;
+ }
+ if (l < 0 || l > 9) {
+ PyErr_SetString(PyExc_ValueError,
+ "coefficient must be a tuple of digits");
+ goto error;
+ }
+ *cp++ = (char)l + '0';
+ }
+ *cp = '\0';
+
+ if (sign_special[1] == '\0') {
+ /* not a special number */
+ *cp++ = 'E';
+ n = snprintf(cp, MPD_EXPDIGITS+1, "%" PRI_mpd_ssize_t, exp);
+ if (n < 0 || n >= MPD_EXPDIGITS+1) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "internal error in dec_sequence_as_str");
+ goto error;
+ }
+ }
+
+ Py_XDECREF(digits);
+ return decstring;
+
+
+error:
+ Py_XDECREF(digits);
+ if (decstring) PyMem_Free(decstring);
+ return NULL;
+}
+
+/* Currently accepts tuples and lists. */
+static PyObject *
+PyDecType_FromSequence(PyTypeObject *type, PyObject *v,
+ PyObject *context)
+{
+ PyObject *dectuple;
+ PyObject *dec;
+ char *s;
+
+ dectuple = sequence_as_tuple(v, PyExc_TypeError,
+ "argument must be a tuple or list");
+ if (dectuple == NULL) {
+ return NULL;
+ }
+
+ s = dectuple_as_str(dectuple);
+ Py_DECREF(dectuple);
+ if (s == NULL) {
+ return NULL;
+ }
+
+ dec = PyDecType_FromCString(type, s, context);
+
+ PyMem_Free(s);
+ return dec;
+}
+
+/* Currently accepts tuples and lists. */
+static PyObject *
+PyDecType_FromSequenceExact(PyTypeObject *type, PyObject *v,
+ PyObject *context)
+{
+ PyObject *dectuple;
+ PyObject *dec;
+ char *s;
+
+ dectuple = sequence_as_tuple(v, PyExc_TypeError,
+ "argument must be a tuple or list");
+ if (dectuple == NULL) {
+ return NULL;
+ }
+
+ s = dectuple_as_str(dectuple);
+ Py_DECREF(dectuple);
+ if (s == NULL) {
+ return NULL;
+ }
+
+ dec = PyDecType_FromCStringExact(type, s, context);
+
+ PyMem_Free(s);
+ return dec;
+}
+
+#define PyDec_FromCString(str, context) \
+ PyDecType_FromCString(&PyDec_Type, str, context)
+#define PyDec_FromCStringExact(str, context) \
+ PyDecType_FromCStringExact(&PyDec_Type, str, context)
+
+#define PyDec_FromUnicode(unicode, context) \
+ PyDecType_FromUnicode(&PyDec_Type, unicode, context)
+#define PyDec_FromUnicodeExact(unicode, context) \
+ PyDecType_FromUnicodeExact(&PyDec_Type, unicode, context)
+#define PyDec_FromUnicodeExactWS(unicode, context) \
+ PyDecType_FromUnicodeExactWS(&PyDec_Type, unicode, context)
+
+#define PyDec_FromSsize(v, context) \
+ PyDecType_FromSsize(&PyDec_Type, v, context)
+#define PyDec_FromSsizeExact(v, context) \
+ PyDecType_FromSsizeExact(&PyDec_Type, v, context)
+
+#define PyDec_FromLong(pylong, context) \
+ PyDecType_FromLong(&PyDec_Type, pylong, context)
+#define PyDec_FromLongExact(pylong, context) \
+ PyDecType_FromLongExact(&PyDec_Type, pylong, context)
+
+#define PyDec_FromFloat(pyfloat, context) \
+ PyDecType_FromFloat(&PyDec_Type, pyfloat, context)
+#define PyDec_FromFloatExact(pyfloat, context) \
+ PyDecType_FromFloatExact(&PyDec_Type, pyfloat, context)
+
+#define PyDec_FromSequence(sequence, context) \
+ PyDecType_FromSequence(&PyDec_Type, sequence, context)
+#define PyDec_FromSequenceExact(sequence, context) \
+ PyDecType_FromSequenceExact(&PyDec_Type, sequence, context)
+
+/* class method */
+static PyObject *
+dec_from_float(PyObject *dec, PyObject *pyfloat)
+{
+ PyObject *context;
+
+ CURRENT_CONTEXT(context);
+ return PyDecType_FromFloatExact((PyTypeObject *)dec, pyfloat, context);
+}
+
+/* create_decimal_from_float */
+static PyObject *
+ctx_from_float(PyObject *context, PyObject *v)
+{
+ return PyDec_FromFloat(v, context);
+}
+
+/* Apply the context to the input operand. Return a new PyDecObject. */
+static PyObject *
+dec_apply(PyObject *v, PyObject *context)
+{
+ PyObject *result;
+ uint32_t status = 0;
+
+ result = dec_alloc();
+ if (result == NULL) {
+ return NULL;
+ }
+
+ mpd_qcopy(MPD(result), MPD(v), &status);
+ if (dec_addstatus(context, status)) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ mpd_qfinalize(MPD(result), CTX(context), &status);
+ if (dec_addstatus(context, status)) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ return result;
+}
+
+/* 'v' can have any type accepted by the Decimal constructor. Attempt
+ an exact conversion. If the result does not meet the restrictions
+ for an mpd_t, fail with InvalidOperation. */
+static PyObject *
+PyDecType_FromObjectExact(PyTypeObject *type, PyObject *v, PyObject *context)
+{
+ if (v == NULL) {
+ return PyDecType_FromSsizeExact(type, 0, context);
+ }
+ else if (PyDec_Check(v)) {
+ Py_INCREF(v);
+ return v;
+ }
+ else if (PyUnicode_Check(v)) {
+ return PyDecType_FromUnicodeExactWS(type, v, context);
+ }
+ else if (PyLong_Check(v)) {
+ return PyDecType_FromLongExact(type, v, context);
+ }
+ else if (PyTuple_Check(v) || PyList_Check(v)) {
+ return PyDecType_FromSequenceExact(type, v, context);
+ }
+ else if (PyFloat_Check(v)) {
+ if (dec_addstatus(context, MPD_Float_operation)) {
+ return NULL;
+ }
+ return PyDecType_FromFloatExact(type, v, context);
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "conversion from %s to Decimal is not supported",
+ v->ob_type->tp_name);
+ return NULL;
+ }
+}
+
+/* The context is used during conversion. This function is the
+ equivalent of context.create_decimal(). */
+static PyObject *
+PyDec_FromObject(PyObject *v, PyObject *context)
+{
+ if (v == NULL) {
+ return PyDec_FromSsize(0, context);
+ }
+ else if (PyDec_Check(v)) {
+ mpd_context_t *ctx = CTX(context);
+ if (mpd_isnan(MPD(v)) &&
+ MPD(v)->digits > ctx->prec - ctx->clamp) {
+ /* Special case: too many NaN payload digits */
+ PyObject *result;
+ if (dec_addstatus(context, MPD_Conversion_syntax)) {
+ return NULL;
+ }
+ result = dec_alloc();
+ if (result == NULL) {
+ return NULL;
+ }
+ mpd_setspecial(MPD(result), MPD_POS, MPD_NAN);
+ return result;
+ }
+ return dec_apply(v, context);
+ }
+ else if (PyUnicode_Check(v)) {
+ return PyDec_FromUnicode(v, context);
+ }
+ else if (PyLong_Check(v)) {
+ return PyDec_FromLong(v, context);
+ }
+ else if (PyTuple_Check(v) || PyList_Check(v)) {
+ return PyDec_FromSequence(v, context);
+ }
+ else if (PyFloat_Check(v)) {
+ if (dec_addstatus(context, MPD_Float_operation)) {
+ return NULL;
+ }
+ return PyDec_FromFloat(v, context);
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "conversion from %s to Decimal is not supported",
+ v->ob_type->tp_name);
+ return NULL;
+ }
+}
+
+static PyObject *
+dec_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"value", "context", NULL};
+ PyObject *v = NULL;
+ PyObject *context;
+
+ CURRENT_CONTEXT(context);
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist,
+ &v, &context)) {
+ return NULL;
+ }
+ CONTEXT_CHECK_VA(context);
+
+ return PyDecType_FromObjectExact(type, v, context);
+}
+
+static PyObject *
+ctx_create_decimal(PyObject *context, PyObject *args)
+{
+ PyObject *v = NULL;
+
+ if (!PyArg_ParseTuple(args, "|O", &v)) {
+ return NULL;
+ }
+
+ return PyDec_FromObject(v, context);
+}
+
+
+/******************************************************************************/
+/* Implicit conversions to Decimal */
+/******************************************************************************/
+
+/* Try to convert PyObject v to a new PyDecObject conv. If the conversion
+ fails, set conv to NULL (exception is set). If the conversion is not
+ implemented, set conv to Py_NotImplemented. */
+#define NOT_IMPL 0
+#define TYPE_ERR 1
+Py_LOCAL_INLINE(int)
+convert_op(int type_err, PyObject **conv, PyObject *v, PyObject *context)
+{
+
+ if (PyDec_Check(v)) {
+ *conv = v;
+ Py_INCREF(v);
+ return 1;
+ }
+ if (PyLong_Check(v)) {
+ *conv = PyDec_FromLongExact(v, context);
+ if (*conv == NULL) {
+ return 0;
+ }
+ return 1;
+ }
+
+ if (type_err) {
+ PyErr_Format(PyExc_TypeError,
+ "conversion from %s to Decimal is not supported",
+ v->ob_type->tp_name);
+ }
+ else {
+ Py_INCREF(Py_NotImplemented);
+ *conv = Py_NotImplemented;
+ }
+ return 0;
+}
+
+/* Return NotImplemented for unsupported types. */
+#define CONVERT_OP(a, v, context) \
+ if (!convert_op(NOT_IMPL, a, v, context)) { \
+ return *(a); \
+ }
+
+#define CONVERT_BINOP(a, b, v, w, context) \
+ if (!convert_op(NOT_IMPL, a, v, context)) { \
+ return *(a); \
+ } \
+ if (!convert_op(NOT_IMPL, b, w, context)) { \
+ Py_DECREF(*(a)); \
+ return *(b); \
+ }
+
+#define CONVERT_TERNOP(a, b, c, v, w, x, context) \
+ if (!convert_op(NOT_IMPL, a, v, context)) { \
+ return *(a); \
+ } \
+ if (!convert_op(NOT_IMPL, b, w, context)) { \
+ Py_DECREF(*(a)); \
+ return *(b); \
+ } \
+ if (!convert_op(NOT_IMPL, c, x, context)) { \
+ Py_DECREF(*(a)); \
+ Py_DECREF(*(b)); \
+ return *(c); \
+ }
+
+/* Raise TypeError for unsupported types. */
+#define CONVERT_OP_RAISE(a, v, context) \
+ if (!convert_op(TYPE_ERR, a, v, context)) { \
+ return NULL; \
+ }
+
+#define CONVERT_BINOP_RAISE(a, b, v, w, context) \
+ if (!convert_op(TYPE_ERR, a, v, context)) { \
+ return NULL; \
+ } \
+ if (!convert_op(TYPE_ERR, b, w, context)) { \
+ Py_DECREF(*(a)); \
+ return NULL; \
+ }
+
+#define CONVERT_TERNOP_RAISE(a, b, c, v, w, x, context) \
+ if (!convert_op(TYPE_ERR, a, v, context)) { \
+ return NULL; \
+ } \
+ if (!convert_op(TYPE_ERR, b, w, context)) { \
+ Py_DECREF(*(a)); \
+ return NULL; \
+ } \
+ if (!convert_op(TYPE_ERR, c, x, context)) { \
+ Py_DECREF(*(a)); \
+ Py_DECREF(*(b)); \
+ return NULL; \
+ }
+
+
+/******************************************************************************/
+/* Implicit conversions to Decimal for comparison */
+/******************************************************************************/
+
+/* Convert rationals for comparison */
+static PyObject *Rational = NULL;
+static PyObject *
+multiply_by_denominator(PyObject *v, PyObject *r, PyObject *context)
+{
+ PyObject *result;
+ PyObject *tmp = NULL;
+ PyObject *denom = NULL;
+ uint32_t status = 0;
+ mpd_context_t maxctx;
+ mpd_ssize_t exp;
+ mpd_t *vv;
+
+ /* v is not special, r is a rational */
+ tmp = PyObject_GetAttrString(r, "denominator");
+ if (tmp == NULL) {
+ return NULL;
+ }
+ denom = PyDec_FromLongExact(tmp, context);
+ Py_DECREF(tmp);
+ if (denom == NULL) {
+ return NULL;
+ }
+
+ vv = mpd_qncopy(MPD(v));
+ if (vv == NULL) {
+ Py_DECREF(denom);
+ PyErr_NoMemory();
+ return NULL;
+ }
+ result = dec_alloc();
+ if (result == NULL) {
+ Py_DECREF(denom);
+ mpd_del(vv);
+ return NULL;
+ }
+
+ mpd_maxcontext(&maxctx);
+ /* Prevent Overflow in the following multiplication. The result of
+ the multiplication is only used in mpd_qcmp, which can handle
+ values that are technically out of bounds, like (for 32-bit)
+ 99999999999999999999...99999999e+425000000. */
+ exp = vv->exp;
+ vv->exp = 0;
+ mpd_qmul(MPD(result), vv, MPD(denom), &maxctx, &status);
+ MPD(result)->exp = exp;
+
+ Py_DECREF(denom);
+ mpd_del(vv);
+ /* If any status has been accumulated during the multiplication,
+ the result is invalid. This is very unlikely, since even the
+ 32-bit version supports 425000000 digits. */
+ if (status) {
+ PyErr_SetString(PyExc_ValueError,
+ "exact conversion for comparison failed");
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ return result;
+}
+
+static PyObject *
+numerator_as_decimal(PyObject *r, PyObject *context)
+{
+ PyObject *tmp, *num;
+
+ tmp = PyObject_GetAttrString(r, "numerator");
+ if (tmp == NULL) {
+ return NULL;
+ }
+
+ num = PyDec_FromLongExact(tmp, context);
+ Py_DECREF(tmp);
+ return num;
+}
+
+/* Convert v and w for comparison. v is a Decimal. If w is a Rational, both
+ v and w have to be transformed. Return 1 for success, with new references
+ to the converted objects in vcmp and wcmp. Return 0 for failure. In that
+ case wcmp is either NULL or Py_NotImplemented (new reference) and vcmp
+ is undefined. */
+static int
+convert_op_cmp(PyObject **vcmp, PyObject **wcmp, PyObject *v, PyObject *w,
+ int op, PyObject *context)
+{
+ mpd_context_t *ctx = CTX(context);
+
+ *vcmp = v;
+
+ if (PyDec_Check(w)) {
+ Py_INCREF(w);
+ *wcmp = w;
+ }
+ else if (PyLong_Check(w)) {
+ *wcmp = PyDec_FromLongExact(w, context);
+ }
+ else if (PyFloat_Check(w)) {
+ if (op != Py_EQ && op != Py_NE &&
+ dec_addstatus(context, MPD_Float_operation)) {
+ *wcmp = NULL;
+ }
+ else {
+ ctx->status |= MPD_Float_operation;
+ *wcmp = PyDec_FromFloatExact(w, context);
+ }
+ }
+ else if (PyComplex_Check(w) && (op == Py_EQ || op == Py_NE)) {
+ Py_complex c = PyComplex_AsCComplex(w);
+ if (c.real == -1.0 && PyErr_Occurred()) {
+ *wcmp = NULL;
+ }
+ else if (c.imag == 0.0) {
+ PyObject *tmp = PyFloat_FromDouble(c.real);
+ if (tmp == NULL) {
+ *wcmp = NULL;
+ }
+ else {
+ ctx->status |= MPD_Float_operation;
+ *wcmp = PyDec_FromFloatExact(tmp, context);
+ Py_DECREF(tmp);
+ }
+ }
+ else {
+ Py_INCREF(Py_NotImplemented);
+ *wcmp = Py_NotImplemented;
+ }
+ }
+ else if (PyObject_IsInstance(w, Rational)) {
+ *wcmp = numerator_as_decimal(w, context);
+ if (*wcmp && !mpd_isspecial(MPD(v))) {
+ *vcmp = multiply_by_denominator(v, w, context);
+ if (*vcmp == NULL) {
+ Py_CLEAR(*wcmp);
+ }
+ }
+ }
+ else {
+ Py_INCREF(Py_NotImplemented);
+ *wcmp = Py_NotImplemented;
+ }
+
+ if (*wcmp == NULL || *wcmp == Py_NotImplemented) {
+ return 0;
+ }
+ if (*vcmp == v) {
+ Py_INCREF(v);
+ }
+ return 1;
+}
+
+#define CONVERT_BINOP_CMP(vcmp, wcmp, v, w, op, ctx) \
+ if (!convert_op_cmp(vcmp, wcmp, v, w, op, ctx)) { \
+ return *(wcmp); \
+ } \
+
+
+/******************************************************************************/
+/* Conversions from decimal */
+/******************************************************************************/
+
+static PyObject *
+unicode_fromascii(const char *s, Py_ssize_t size)
+{
+ PyObject *res;
+
+ res = PyUnicode_New(size, 127);
+ if (res == NULL) {
+ return NULL;
+ }
+
+ memcpy(PyUnicode_1BYTE_DATA(res), s, size);
+ return res;
+}
+
+/* PyDecObject as a string. The default module context is only used for
+ the value of 'capitals'. */
+static PyObject *
+dec_str(PyObject *dec)
+{
+ PyObject *res, *context;
+ mpd_ssize_t size;
+ char *cp;
+
+ CURRENT_CONTEXT(context);
+ size = mpd_to_sci_size(&cp, MPD(dec), CtxCaps(context));
+ if (size < 0) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ res = unicode_fromascii(cp, size);
+ mpd_free(cp);
+ return res;
+}
+
+/* Representation of a PyDecObject. */
+static PyObject *
+dec_repr(PyObject *dec)
+{
+ PyObject *res, *context;
+ char *cp;
+
+ CURRENT_CONTEXT(context);
+ cp = mpd_to_sci(MPD(dec), CtxCaps(context));
+ if (cp == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ res = PyUnicode_FromFormat("Decimal('%s')", cp);
+ mpd_free(cp);
+ return res;
+}
+
+/* Convert decimal_point or thousands_sep, which may be multibyte or in
+ the range [128, 255], to a UTF8 string. */
+static PyObject *
+dotsep_as_utf8(const char *s)
+{
+ PyObject *utf8;
+ PyObject *tmp;
+ wchar_t buf[2];
+ size_t n;
+
+ n = mbstowcs(buf, s, 2);
+ if (n != 1) { /* Issue #7442 */
+ PyErr_SetString(PyExc_ValueError,
+ "invalid decimal point or unsupported "
+ "combination of LC_CTYPE and LC_NUMERIC");
+ return NULL;
+ }
+ tmp = PyUnicode_FromWideChar(buf, n);
+ if (tmp == NULL) {
+ return NULL;
+ }
+ utf8 = PyUnicode_AsUTF8String(tmp);
+ Py_DECREF(tmp);
+ return utf8;
+}
+
+/* Formatted representation of a PyDecObject. */
+static PyObject *
+dec_format(PyObject *dec, PyObject *args)
+{
+ PyObject *result = NULL;
+ PyObject *override = NULL;
+ PyObject *dot = NULL;
+ PyObject *sep = NULL;
+ PyObject *grouping = NULL;
+ PyObject *fmt = NULL;
+ PyObject *fmtarg;
+ PyObject *context;
+ mpd_spec_t spec;
+ char *decstring= NULL;
+ uint32_t status = 0;
+ size_t n;
+
+
+ CURRENT_CONTEXT(context);
+ if (!PyArg_ParseTuple(args, "O|O", &fmtarg, &override)) {
+ return NULL;
+ }
+
+ if (PyUnicode_Check(fmtarg)) {
+ fmt = PyUnicode_AsUTF8String(fmtarg);
+ if (fmt == NULL) {
+ return NULL;
+ }
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "format arg must be str");
+ return NULL;
+ }
+
+ if (!mpd_parse_fmt_str(&spec, PyBytes_AS_STRING(fmt),
+ CtxCaps(context))) {
+ PyErr_SetString(PyExc_ValueError,
+ "invalid format string");
+ goto finish;
+ }
+ if (override) {
+ /* Values for decimal_point, thousands_sep and grouping can
+ be explicitly specified in the override dict. These values
+ take precedence over the values obtained from localeconv()
+ in mpd_parse_fmt_str(). The feature is not documented and
+ is only used in test_decimal. */
+ if (!PyDict_Check(override)) {
+ PyErr_SetString(PyExc_TypeError,
+ "optional argument must be a dict");
+ goto finish;
+ }
+ if ((dot = PyDict_GetItemString(override, "decimal_point"))) {
+ if ((dot = PyUnicode_AsUTF8String(dot)) == NULL) {
+ goto finish;
+ }
+ spec.dot = PyBytes_AS_STRING(dot);
+ }
+ if ((sep = PyDict_GetItemString(override, "thousands_sep"))) {
+ if ((sep = PyUnicode_AsUTF8String(sep)) == NULL) {
+ goto finish;
+ }
+ spec.sep = PyBytes_AS_STRING(sep);
+ }
+ if ((grouping = PyDict_GetItemString(override, "grouping"))) {
+ if ((grouping = PyUnicode_AsUTF8String(grouping)) == NULL) {
+ goto finish;
+ }
+ spec.grouping = PyBytes_AS_STRING(grouping);
+ }
+ if (mpd_validate_lconv(&spec) < 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "invalid override dict");
+ goto finish;
+ }
+ }
+ else {
+ n = strlen(spec.dot);
+ if (n > 1 || (n == 1 && !isascii((uchar)spec.dot[0]))) {
+ /* fix locale dependent non-ascii characters */
+ dot = dotsep_as_utf8(spec.dot);
+ if (dot == NULL) {
+ goto finish;
+ }
+ spec.dot = PyBytes_AS_STRING(dot);
+ }
+ n = strlen(spec.sep);
+ if (n > 1 || (n == 1 && !isascii((uchar)spec.sep[0]))) {
+ /* fix locale dependent non-ascii characters */
+ sep = dotsep_as_utf8(spec.sep);
+ if (sep == NULL) {
+ goto finish;
+ }
+ spec.sep = PyBytes_AS_STRING(sep);
+ }
+ }
+
+
+ decstring = mpd_qformat_spec(MPD(dec), &spec, CTX(context), &status);
+ if (decstring == NULL) {
+ dec_addstatus(context, status);
+ goto finish;
+ }
+ result = PyUnicode_DecodeUTF8(decstring, strlen(decstring), NULL);
+
+
+finish:
+ Py_XDECREF(grouping);
+ Py_XDECREF(sep);
+ Py_XDECREF(dot);
+ Py_XDECREF(fmt);
+ if (decstring) mpd_free(decstring);
+ return result;
+}
+
+/* Return a PyLongObject from a PyDecObject, using the specified rounding
+ * mode. The context precision is not observed. */
+static PyObject *
+dec_as_long(PyObject *dec, PyObject *context, int round)
+{
+ PyLongObject *pylong;
+ size_t maxsize, n;
+ Py_ssize_t i;
+ mpd_t *x;
+ mpd_context_t workctx;
+ uint32_t status = 0;
+
+ if (mpd_isspecial(MPD(dec))) {
+ if (mpd_isnan(MPD(dec))) {
+ PyErr_SetString(PyExc_ValueError,
+ "cannot convert NaN to integer");
+ }
+ else {
+ PyErr_SetString(PyExc_OverflowError,
+ "cannot convert Infinity to integer");
+ }
+ return NULL;
+ }
+
+ x = mpd_qnew();
+ if (x == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ workctx = *CTX(context);
+ workctx.round = round;
+ mpd_qround_to_int(x, MPD(dec), &workctx, &status);
+ if (dec_addstatus(context, status)) {
+ mpd_del(x);
+ return NULL;
+ }
+
+ maxsize = mpd_sizeinbase(x, PyLong_BASE);
+ if (maxsize > PY_SSIZE_T_MAX) {
+ mpd_del(x);
+ PyErr_NoMemory();
+ return NULL;
+ }
+ pylong = _PyLong_New(maxsize);
+ if (pylong == NULL) {
+ mpd_del(x);
+ return NULL;
+ }
+
+ status = 0;
+#if PYLONG_BITS_IN_DIGIT == 30
+ n = mpd_qexport_u32(pylong->ob_digit, maxsize, PyLong_BASE, x, &status);
+#elif PYLONG_BITS_IN_DIGIT == 15
+ n = mpd_qexport_u16(pylong->ob_digit, maxsize, PyLong_BASE, x, &status);
+#else
+ #error "PYLONG_BITS_IN_DIGIT should be 15 or 30"
+#endif
+ if (dec_addstatus(context, status)) {
+ Py_DECREF((PyObject *) pylong);
+ mpd_del(x);
+ return NULL;
+ }
+
+ i = n;
+ while ((i > 0) && (pylong->ob_digit[i-1] == 0)) {
+ i--;
+ }
+
+ Py_SIZE(pylong) = i;
+ if (mpd_isnegative(x) && !mpd_iszero(x)) {
+ Py_SIZE(pylong) = -i;
+ }
+
+ mpd_del(x);
+ return (PyObject *) pylong;
+}
+
+static PyObject *
+PyDec_ToIntegralValue(PyObject *dec, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"rounding", "context", NULL};
+ PyObject *result;
+ PyObject *context;
+ uint32_t status = 0;
+ mpd_context_t workctx;
+ int round = -1;
+
+ CURRENT_CONTEXT(context);
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO", kwlist,
+ &round, &context)) {
+ return NULL;
+ }
+ CONTEXT_CHECK_VA(context);
+
+ workctx = *CTX(context);
+ if (round >= 0) {
+ if (!mpd_qsetround(&workctx, round)) {
+ return type_error_ptr(invalid_rounding_err);
+ }
+ }
+
+ result = dec_alloc();
+ if (result == NULL) {
+ return NULL;
+ }
+
+ mpd_qround_to_int(MPD(result), MPD(dec), &workctx, &status);
+ if (dec_addstatus(context, status)) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ return result;
+}
+
+static PyObject *
+PyDec_ToIntegralExact(PyObject *dec, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"rounding", "context", NULL};
+ PyObject *result;
+ PyObject *context;
+ uint32_t status = 0;
+ mpd_context_t workctx;
+ int round = -1;
+
+ CURRENT_CONTEXT(context);
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO", kwlist,
+ &round, &context)) {
+ return NULL;
+ }
+ CONTEXT_CHECK_VA(context);
+
+ workctx = *CTX(context);
+ if (round >= 0) {
+ if (!mpd_qsetround(&workctx, round)) {
+ return type_error_ptr(invalid_rounding_err);
+ }
+ }
+
+ result = dec_alloc();
+ if (result == NULL) {
+ return NULL;
+ }
+
+ mpd_qround_to_intx(MPD(result), MPD(dec), &workctx, &status);
+ if (dec_addstatus(context, status)) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ return result;
+}
+
+static PyObject *
+PyDec_AsFloat(PyObject *dec)
+{
+ PyObject *f, *s;
+
+ s = dec_str(dec);
+ if (s == NULL) {
+ return NULL;
+ }
+
+ f = PyFloat_FromString(s);
+ Py_DECREF(s);
+
+ return f;
+}
+
+static PyObject *
+PyDec_Round(PyObject *dec, PyObject *args)
+{
+ PyObject *result;
+ PyObject *x = NULL;
+ uint32_t status = 0;
+ PyObject *context;
+
+
+ CURRENT_CONTEXT(context);
+ if (!PyArg_ParseTuple(args, "|O", &x)) {
+ return NULL;
+ }
+
+ if (x) {
+ mpd_uint_t dq[1] = {1};
+ mpd_t q = {MPD_STATIC|MPD_CONST_DATA,0,1,1,1,dq};
+ mpd_ssize_t y;
+
+ if (!PyLong_Check(x)) {
+ PyErr_SetString(PyExc_TypeError,
+ "optional arg must be an integer");
+ return NULL;
+ }
+
+ y = PyLong_AsSsize_t(x);
+ if (y == -1 && PyErr_Occurred()) {
+ return NULL;
+ }
+ result = dec_alloc();
+ if (result == NULL) {
+ return NULL;
+ }
+
+ q.exp = (y == MPD_SSIZE_MIN) ? MPD_SSIZE_MAX : -y;
+ mpd_qquantize(MPD(result), MPD(dec), &q, CTX(context), &status);
+ if (dec_addstatus(context, status)) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ return result;
+ }
+ else {
+ return dec_as_long(dec, context, MPD_ROUND_HALF_EVEN);
+ }
+}
+
+PyObject *DecimalTuple = NULL;
+/* Return the DecimalTuple representation of a PyDecObject. */
+static PyObject *
+PyDec_AsTuple(PyObject *dec, PyObject *dummy UNUSED)
+{
+ PyObject *result = NULL;
+ PyObject *sign = NULL;
+ PyObject *coeff = NULL;
+ PyObject *expt = NULL;
+ PyObject *tmp = NULL;
+ mpd_t *x = NULL;
+ char *intstring = NULL;
+ Py_ssize_t intlen, i;
+
+
+ x = mpd_qncopy(MPD(dec));
+ if (x == NULL) {
+ PyErr_NoMemory();
+ goto out;
+ }
+
+ sign = PyLong_FromUnsignedLong(mpd_sign(MPD(dec)));
+ if (sign == NULL) {
+ goto out;
+ }
+
+ if (mpd_isinfinite(x)) {
+ expt = PyUnicode_FromString("F");
+ if (expt == NULL) {
+ goto out;
+ }
+ /* decimal.py has non-compliant infinity payloads. */
+ coeff = Py_BuildValue("(i)", 0);
+ if (coeff == NULL) {
+ goto out;
+ }
+ }
+ else {
+ if (mpd_isnan(x)) {
+ expt = PyUnicode_FromString(mpd_isqnan(x)?"n":"N");
+ }
+ else {
+ expt = PyLong_FromSsize_t(MPD(dec)->exp);
+ }
+ if (expt == NULL) {
+ goto out;
+ }
+
+ /* coefficient is defined */
+ if (x->len > 0) {
+
+ /* make an integer */
+ x->exp = 0;
+ /* clear NaN and sign */
+ mpd_clear_flags(x);
+ intstring = mpd_to_sci(x, 1);
+ if (intstring == NULL) {
+ PyErr_NoMemory();
+ goto out;
+ }
+
+ intlen = strlen(intstring);
+ coeff = PyTuple_New(intlen);
+ if (coeff == NULL) {
+ goto out;
+ }
+
+ for (i = 0; i < intlen; i++) {
+ tmp = PyLong_FromLong(intstring[i]-'0');
+ if (tmp == NULL) {
+ goto out;
+ }
+ PyTuple_SET_ITEM(coeff, i, tmp);
+ }
+ }
+ else {
+ coeff = PyTuple_New(0);
+ if (coeff == NULL) {
+ goto out;
+ }
+ }
+ }
+
+ result = PyObject_CallFunctionObjArgs(DecimalTuple,
+ sign, coeff, expt, NULL);
+
+out:
+ if (x) mpd_del(x);
+ if (intstring) mpd_free(intstring);
+ Py_XDECREF(sign);
+ Py_XDECREF(coeff);
+ Py_XDECREF(expt);
+ return result;
+}
+
+
+/******************************************************************************/
+/* Macros for converting mpdecimal functions to Decimal methods */
+/******************************************************************************/
+
+/* Unary number method that uses the default module context. */
+#define Dec_UnaryNumberMethod(MPDFUNC) \
+static PyObject * \
+nm_##MPDFUNC(PyObject *self) \
+{ \
+ PyObject *result; \
+ PyObject *context; \
+ uint32_t status = 0; \
+ \
+ CURRENT_CONTEXT(context); \
+ if ((result = dec_alloc()) == NULL) { \
+ return NULL; \
+ } \
+ \
+ MPDFUNC(MPD(result), MPD(self), CTX(context), &status); \
+ if (dec_addstatus(context, status)) { \
+ Py_DECREF(result); \
+ return NULL; \
+ } \
+ \
+ return result; \
+}
+
+/* Binary number method that uses default module context. */
+#define Dec_BinaryNumberMethod(MPDFUNC) \
+static PyObject * \
+nm_##MPDFUNC(PyObject *self, PyObject *other) \
+{ \
+ PyObject *a, *b; \
+ PyObject *result; \
+ PyObject *context; \
+ uint32_t status = 0; \
+ \
+ CURRENT_CONTEXT(context) ; \
+ CONVERT_BINOP(&a, &b, self, other, context); \
+ \
+ if ((result = dec_alloc()) == NULL) { \
+ Py_DECREF(a); \
+ Py_DECREF(b); \
+ return NULL; \
+ } \
+ \
+ MPDFUNC(MPD(result), MPD(a), MPD(b), CTX(context), &status); \
+ Py_DECREF(a); \
+ Py_DECREF(b); \
+ if (dec_addstatus(context, status)) { \
+ Py_DECREF(result); \
+ return NULL; \
+ } \
+ \
+ return result; \
+}
+
+/* Boolean function without a context arg. */
+#define Dec_BoolFunc(MPDFUNC) \
+static PyObject * \
+dec_##MPDFUNC(PyObject *self, PyObject *dummy UNUSED) \
+{ \
+ return MPDFUNC(MPD(self)) ? incr_true() : incr_false(); \
+}
+
+/* Boolean function with an optional context arg. */
+#define Dec_BoolFuncVA(MPDFUNC) \
+static PyObject * \
+dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \
+{ \
+ static char *kwlist[] = {"context", NULL}; \
+ PyObject *context; \
+ \
+ CURRENT_CONTEXT(context); \
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, \
+ &context)) { \
+ return NULL; \
+ } \
+ CONTEXT_CHECK_VA(context); \
+ \
+ return MPDFUNC(MPD(self), CTX(context)) ? incr_true() : incr_false(); \
+}
+
+/* Unary function with an optional context arg. */
+#define Dec_UnaryFuncVA(MPDFUNC) \
+static PyObject * \
+dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \
+{ \
+ static char *kwlist[] = {"context", NULL}; \
+ PyObject *result; \
+ PyObject *context; \
+ uint32_t status = 0; \
+ \
+ CURRENT_CONTEXT(context); \
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, \
+ &context)) { \
+ return NULL; \
+ } \
+ CONTEXT_CHECK_VA(context); \
+ \
+ if ((result = dec_alloc()) == NULL) { \
+ return NULL; \
+ } \
+ \
+ MPDFUNC(MPD(result), MPD(self), CTX(context), &status); \
+ if (dec_addstatus(context, status)) { \
+ Py_DECREF(result); \
+ return NULL; \
+ } \
+ \
+ return result; \
+}
+
+/* Unary function with an optional context arg. The actual MPDFUNC
+ only takes a status parameter. */
+#define Dec_UnaryFuncVA_NO_CTX(MPDFUNC) \
+static PyObject * \
+dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \
+{ \
+ static char *kwlist[] = {"context", NULL}; \
+ PyObject *result; \
+ PyObject *context; \
+ uint32_t status = 0; \
+ \
+ CURRENT_CONTEXT(context); \
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, \
+ &context)) { \
+ return NULL; \
+ } \
+ CONTEXT_CHECK_VA(context); \
+ \
+ if ((result = dec_alloc()) == NULL) { \
+ return NULL; \
+ } \
+ \
+ MPDFUNC(MPD(result), MPD(self), &status); \
+ if (dec_addstatus(context, status)) { \
+ Py_DECREF(result); \
+ return NULL; \
+ } \
+ \
+ return result; \
+}
+
+/* Binary function with an optional context arg. */
+#define Dec_BinaryFuncVA(MPDFUNC) \
+static PyObject * \
+dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \
+{ \
+ static char *kwlist[] = {"other", "context", NULL}; \
+ PyObject *other, *context; \
+ PyObject *a, *b; \
+ PyObject *result; \
+ uint32_t status = 0; \
+ \
+ CURRENT_CONTEXT(context); \
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, \
+ &other, &context)) { \
+ return NULL; \
+ } \
+ CONTEXT_CHECK_VA(context); \
+ CONVERT_BINOP_RAISE(&a, &b, self, other, context); \
+ \
+ if ((result = dec_alloc()) == NULL) { \
+ Py_DECREF(a); \
+ Py_DECREF(b); \
+ return NULL; \
+ } \
+ \
+ MPDFUNC(MPD(result), MPD(a), MPD(b), CTX(context), &status); \
+ Py_DECREF(a); \
+ Py_DECREF(b); \
+ if (dec_addstatus(context, status)) { \
+ Py_DECREF(result); \
+ return NULL; \
+ } \
+ \
+ return result; \
+}
+
+/* Binary function with an optional context arg. Actual MPDFUNC does
+ NOT take a context. The context is used to record InvalidOperation
+ if the second operand cannot be converted exactly. */
+#define Dec_BinaryFuncVA_NO_CTX(MPDFUNC) \
+static PyObject * \
+dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \
+{ \
+ static char *kwlist[] = {"other", "context", NULL}; \
+ PyObject *other, *context; \
+ PyObject *a, *b; \
+ PyObject *result; \
+ \
+ CURRENT_CONTEXT(context); \
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, \
+ &other, &context)) { \
+ return NULL; \
+ } \
+ CONTEXT_CHECK_VA(context); \
+ CONVERT_BINOP_RAISE(&a, &b, self, other, context); \
+ \
+ if ((result = dec_alloc()) == NULL) { \
+ Py_DECREF(a); \
+ Py_DECREF(b); \
+ return NULL; \
+ } \
+ \
+ MPDFUNC(MPD(result), MPD(a), MPD(b)); \
+ Py_DECREF(a); \
+ Py_DECREF(b); \
+ \
+ return result; \
+}
+
+/* Ternary function with an optional context arg. */
+#define Dec_TernaryFuncVA(MPDFUNC) \
+static PyObject * \
+dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \
+{ \
+ static char *kwlist[] = {"other", "third", "context", NULL}; \
+ PyObject *other, *third, *context; \
+ PyObject *a, *b, *c; \
+ PyObject *result; \
+ uint32_t status = 0; \
+ \
+ CURRENT_CONTEXT(context); \
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|O", kwlist, \
+ &other, &third, &context)) { \
+ return NULL; \
+ } \
+ CONTEXT_CHECK_VA(context); \
+ CONVERT_TERNOP_RAISE(&a, &b, &c, self, other, third, context); \
+ \
+ if ((result = dec_alloc()) == NULL) { \
+ Py_DECREF(a); \
+ Py_DECREF(b); \
+ Py_DECREF(c); \
+ return NULL; \
+ } \
+ \
+ MPDFUNC(MPD(result), MPD(a), MPD(b), MPD(c), CTX(context), &status); \
+ Py_DECREF(a); \
+ Py_DECREF(b); \
+ Py_DECREF(c); \
+ if (dec_addstatus(context, status)) { \
+ Py_DECREF(result); \
+ return NULL; \
+ } \
+ \
+ return result; \
+}
+
+
+/**********************************************/
+/* Number methods */
+/**********************************************/
+
+Dec_UnaryNumberMethod(mpd_qminus)
+Dec_UnaryNumberMethod(mpd_qplus)
+Dec_UnaryNumberMethod(mpd_qabs)
+
+Dec_BinaryNumberMethod(mpd_qadd)
+Dec_BinaryNumberMethod(mpd_qsub)
+Dec_BinaryNumberMethod(mpd_qmul)
+Dec_BinaryNumberMethod(mpd_qdiv)
+Dec_BinaryNumberMethod(mpd_qrem)
+Dec_BinaryNumberMethod(mpd_qdivint)
+
+static PyObject *
+nm_dec_as_long(PyObject *dec)
+{
+ PyObject *context;
+
+ CURRENT_CONTEXT(context);
+ return dec_as_long(dec, context, MPD_ROUND_DOWN);
+}
+
+static int
+nm_nonzero(PyDecObject *v)
+{
+ return !mpd_iszero(v->dec);
+}
+
+static PyObject *
+nm_mpd_qdivmod(PyObject *v, PyObject *w)
+{
+ PyObject *a, *b;
+ PyObject *q, *r;
+ PyObject *context;
+ uint32_t status = 0;
+ PyObject *ret;
+
+ CURRENT_CONTEXT(context);
+ CONVERT_BINOP(&a, &b, v, w, context);
+
+ q = dec_alloc();
+ if (q == NULL) {
+ Py_DECREF(a);
+ Py_DECREF(b);
+ return NULL;
+ }
+ r = dec_alloc();
+ if (r == NULL) {
+ Py_DECREF(a);
+ Py_DECREF(b);
+ Py_DECREF(q);
+ return NULL;
+ }
+
+ mpd_qdivmod(MPD(q), MPD(r), MPD(a), MPD(b), CTX(context), &status);
+ Py_DECREF(a);
+ Py_DECREF(b);
+ if (dec_addstatus(context, status)) {
+ Py_DECREF(r);
+ Py_DECREF(q);
+ return NULL;
+ }
+
+ ret = Py_BuildValue("(OO)", q, r);
+ Py_DECREF(r);
+ Py_DECREF(q);
+ return ret;
+}
+
+static mpd_uint_t data_zero[1] = {0};
+static const mpd_t zero = {MPD_STATIC|MPD_CONST_DATA, 0, 1, 1, 1, data_zero};
+
+static PyObject *
+nm_mpd_qpow(PyObject *base, PyObject *exp, PyObject *mod)
+{
+ PyObject *a, *b, *c = NULL;
+ PyObject *result;
+ PyObject *context;
+ uint32_t status = 0;
+
+ CURRENT_CONTEXT(context);
+ CONVERT_BINOP(&a, &b, base, exp, context);
+
+ if (mod != Py_None) {
+ if (!convert_op(NOT_IMPL, &c, mod, context)) {
+ Py_DECREF(a);
+ Py_DECREF(b);
+ return c;
+ }
+ }
+
+ result = dec_alloc();
+ if (result == NULL) {
+ Py_DECREF(a);
+ Py_DECREF(b);
+ Py_XDECREF(c);
+ return NULL;
+ }
+
+ if (c == NULL) {
+ mpd_qpow(MPD(result), MPD(a), MPD(b),
+ CTX(context), &status);
+ }
+ else {
+ mpd_qpowmod(MPD(result), MPD(a), MPD(b), MPD(c),
+ CTX(context), &status);
+ status = (status == MPD_Clamped) ? 0 : status;
+ /* remove ideal exponent for compatibility with decimal.py */
+ mpd_qquantize(MPD(result), MPD(result), &zero,
+ CTX(context), &status);
+ Py_DECREF(c);
+ }
+ Py_DECREF(a);
+ Py_DECREF(b);
+ if (dec_addstatus(context, status)) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ return result;
+}
+
+
+/******************************************************************************/
+/* Decimal Methods */
+/******************************************************************************/
+
+/* Unary arithmetic functions, optional context arg */
+Dec_UnaryFuncVA(mpd_qexp)
+Dec_UnaryFuncVA(mpd_qln)
+Dec_UnaryFuncVA(mpd_qlog10)
+Dec_UnaryFuncVA(mpd_qnext_minus)
+Dec_UnaryFuncVA(mpd_qnext_plus)
+Dec_UnaryFuncVA(mpd_qreduce)
+Dec_UnaryFuncVA(mpd_qsqrt)
+
+/* Binary arithmetic functions, optional context arg */
+Dec_BinaryFuncVA(mpd_qcompare)
+Dec_BinaryFuncVA(mpd_qcompare_signal)
+Dec_BinaryFuncVA(mpd_qmax)
+Dec_BinaryFuncVA(mpd_qmax_mag)
+Dec_BinaryFuncVA(mpd_qmin)
+Dec_BinaryFuncVA(mpd_qmin_mag)
+Dec_BinaryFuncVA(mpd_qnext_toward)
+Dec_BinaryFuncVA(mpd_qrem_near)
+
+/* Ternary arithmetic functions, optional context arg */
+Dec_TernaryFuncVA(mpd_qfma)
+
+/* Boolean functions, no context arg */
+Dec_BoolFunc(mpd_iscanonical)
+Dec_BoolFunc(mpd_isfinite)
+Dec_BoolFunc(mpd_isinfinite)
+Dec_BoolFunc(mpd_isnan)
+Dec_BoolFunc(mpd_isqnan)
+Dec_BoolFunc(mpd_issnan)
+Dec_BoolFunc(mpd_issigned)
+Dec_BoolFunc(mpd_iszero)
+
+/* Boolean functions, optional context arg */
+Dec_BoolFuncVA(mpd_isnormal)
+Dec_BoolFuncVA(mpd_issubnormal)
+
+/* Unary functions, no context arg */
+static PyObject *
+dec_mpd_adjexp(PyObject *self, PyObject *dummy UNUSED)
+{
+ mpd_ssize_t retval;
+
+ if (mpd_isspecial(MPD(self))) {
+ retval = 0;
+ }
+ else {
+ retval = mpd_adjexp(MPD(self));
+ }
+
+ return PyLong_FromSsize_t(retval);
+}
+
+static PyObject *
+dec_canonical(PyObject *self, PyObject *dummy UNUSED)
+{
+ Py_INCREF(self);
+ return self;
+}
+
+static PyObject *
+dec_conjugate(PyObject *self, PyObject *dummy UNUSED)
+{
+ Py_INCREF(self);
+ return self;
+}
+
+static PyObject *
+dec_mpd_radix(PyObject *self UNUSED, PyObject *dummy UNUSED)
+{
+ PyObject *result;
+
+ result = dec_alloc();
+ if (result == NULL) {
+ return NULL;
+ }
+
+ _dec_settriple(result, MPD_POS, 10, 0);
+ return result;
+}
+
+/* Unary functions, optional context arg for conversion errors */
+Dec_UnaryFuncVA_NO_CTX(mpd_qcopy_abs)
+Dec_UnaryFuncVA_NO_CTX(mpd_qcopy_negate)
+
+/* Unary functions, optional context arg */
+Dec_UnaryFuncVA(mpd_qinvert)
+Dec_UnaryFuncVA(mpd_qlogb)
+
+static PyObject *
+dec_mpd_class(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"context", NULL};
+ PyObject *context;
+ const char *cp;
+
+ CURRENT_CONTEXT(context);
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist,
+ &context)) {
+ return NULL;
+ }
+ CONTEXT_CHECK_VA(context);
+
+ cp = mpd_class(MPD(self), CTX(context));
+ return PyUnicode_FromString(cp);
+}
+
+static PyObject *
+dec_mpd_to_eng(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"context", NULL};
+ PyObject *result;
+ PyObject *context;
+ mpd_ssize_t size;
+ char *s;
+
+ CURRENT_CONTEXT(context);
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist,
+ &context)) {
+ return NULL;
+ }
+ CONTEXT_CHECK_VA(context);
+
+ size = mpd_to_eng_size(&s, MPD(self), CtxCaps(context));
+ if (size < 0) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ result = unicode_fromascii(s, size);
+ mpd_free(s);
+
+ return result;
+}
+
+/* Binary functions, optional context arg for conversion errors */
+Dec_BinaryFuncVA_NO_CTX(mpd_compare_total)
+Dec_BinaryFuncVA_NO_CTX(mpd_compare_total_mag)
+
+static PyObject *
+dec_mpd_qcopy_sign(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"other", "context", NULL};
+ PyObject *other, *context;
+ PyObject *a, *b;
+ PyObject *result;
+ uint32_t status = 0;
+
+ CURRENT_CONTEXT(context);
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist,
+ &other, &context)) {
+ return NULL;
+ }
+ CONTEXT_CHECK_VA(context);
+ CONVERT_BINOP_RAISE(&a, &b, self, other, context);
+
+ result = dec_alloc();
+ if (result == NULL) {
+ Py_DECREF(a);
+ Py_DECREF(b);
+ return NULL;
+ }
+
+ mpd_qcopy_sign(MPD(result), MPD(a), MPD(b), &status);
+ Py_DECREF(a);
+ Py_DECREF(b);
+ if (dec_addstatus(context, status)) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ return result;
+}
+
+static PyObject *
+dec_mpd_same_quantum(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"other", "context", NULL};
+ PyObject *other, *context;
+ PyObject *a, *b;
+ PyObject *result;
+
+ CURRENT_CONTEXT(context);
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist,
+ &other, &context)) {
+ return NULL;
+ }
+ CONTEXT_CHECK_VA(context);
+ CONVERT_BINOP_RAISE(&a, &b, self, other, context);
+
+ result = mpd_same_quantum(MPD(a), MPD(b)) ? incr_true() : incr_false();
+ Py_DECREF(a);
+ Py_DECREF(b);
+
+ return result;
+}
+
+/* Binary functions, optional context arg */
+Dec_BinaryFuncVA(mpd_qand)
+Dec_BinaryFuncVA(mpd_qor)
+Dec_BinaryFuncVA(mpd_qxor)
+
+Dec_BinaryFuncVA(mpd_qrotate)
+Dec_BinaryFuncVA(mpd_qscaleb)
+Dec_BinaryFuncVA(mpd_qshift)
+
+static PyObject *
+dec_mpd_qquantize(PyObject *v, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"exp", "rounding", "context", NULL};
+ PyObject *w, *context;
+ PyObject *a, *b;
+ PyObject *result;
+ uint32_t status = 0;
+ mpd_context_t workctx;
+ int round = -1;
+
+ CURRENT_CONTEXT(context);
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iO", kwlist,
+ &w, &round, &context)) {
+ return NULL;
+ }
+ CONTEXT_CHECK_VA(context);
+
+ workctx = *CTX(context);
+ if (round >= 0) {
+ if (!mpd_qsetround(&workctx, round)) {
+ return type_error_ptr(invalid_rounding_err);
+ }
+ }
+
+ CONVERT_BINOP_RAISE(&a, &b, v, w, context);
+
+ result = dec_alloc();
+ if (result == NULL) {
+ Py_DECREF(a);
+ Py_DECREF(b);
+ return NULL;
+ }
+
+ mpd_qquantize(MPD(result), MPD(a), MPD(b), &workctx, &status);
+ Py_DECREF(a);
+ Py_DECREF(b);
+ if (dec_addstatus(context, status)) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ return result;
+}
+
+/* Special methods */
+static PyObject *
+dec_richcompare(PyObject *v, PyObject *w, int op)
+{
+ PyObject *a;
+ PyObject *b;
+ PyObject *context;
+ uint32_t status = 0;
+ int a_issnan, b_issnan;
+ int r;
+
+ assert(PyDec_Check(v));
+
+ CURRENT_CONTEXT(context);
+ CONVERT_BINOP_CMP(&a, &b, v, w, op, context);
+
+ a_issnan = mpd_issnan(MPD(a));
+ b_issnan = mpd_issnan(MPD(b));
+
+ r = mpd_qcmp(MPD(a), MPD(b), &status);
+ Py_DECREF(a);
+ Py_DECREF(b);
+ if (r == INT_MAX) {
+ /* sNaNs or op={le,ge,lt,gt} always signal. */
+ if (a_issnan || b_issnan || (op != Py_EQ && op != Py_NE)) {
+ if (dec_addstatus(context, status)) {
+ return NULL;
+ }
+ }
+ /* qNaN comparison with op={eq,ne} or comparison
+ * with InvalidOperation disabled. */
+ return (op == Py_NE) ? incr_true() : incr_false();
+ }
+
+ switch (op) {
+ case Py_EQ:
+ r = (r == 0);
+ break;
+ case Py_NE:
+ r = (r != 0);
+ break;
+ case Py_LE:
+ r = (r <= 0);
+ break;
+ case Py_GE:
+ r = (r >= 0);
+ break;
+ case Py_LT:
+ r = (r == -1);
+ break;
+ case Py_GT:
+ r = (r == 1);
+ break;
+ }
+
+ return PyBool_FromLong(r);
+}
+
+/* __ceil__ */
+static PyObject *
+dec_ceil(PyObject *self, PyObject *dummy UNUSED)
+{
+ PyObject *context;
+
+ CURRENT_CONTEXT(context);
+ return dec_as_long(self, context, MPD_ROUND_CEILING);
+}
+
+/* __complex__ */
+static PyObject *
+dec_complex(PyObject *self, PyObject *dummy UNUSED)
+{
+ PyObject *f;
+ double x;
+
+ f = PyDec_AsFloat(self);
+ if (f == NULL) {
+ return NULL;
+ }
+
+ x = PyFloat_AsDouble(f);
+ Py_DECREF(f);
+ if (x == -1.0 && PyErr_Occurred()) {
+ return NULL;
+ }
+
+ return PyComplex_FromDoubles(x, 0);
+}
+
+/* __copy__ and __deepcopy__ */
+static PyObject *
+dec_copy(PyObject *self, PyObject *dummy UNUSED)
+{
+ Py_INCREF(self);
+ return self;
+}
+
+/* __floor__ */
+static PyObject *
+dec_floor(PyObject *self, PyObject *dummy UNUSED)
+{
+ PyObject *context;
+
+ CURRENT_CONTEXT(context);
+ return dec_as_long(self, context, MPD_ROUND_FLOOR);
+}
+
+/* Always uses the module context */
+static Py_hash_t
+dec_hash(PyObject *v)
+{
+#if defined(CONFIG_64) && _PyHASH_BITS == 61
+ /* 2**61 - 1 */
+ mpd_uint_t p_data[1] = {2305843009213693951ULL};
+ mpd_t p = {MPD_POS|MPD_STATIC|MPD_CONST_DATA, 0, 19, 1, 1, p_data};
+ /* Inverse of 10 modulo p */
+ mpd_uint_t inv10_p_data[2] = {2075258708292324556ULL};
+ mpd_t inv10_p = {MPD_POS|MPD_STATIC|MPD_CONST_DATA,
+ 0, 19, 1, 1, inv10_p_data};
+#elif defined(CONFIG_32) && _PyHASH_BITS == 31
+ /* 2**31 - 1 */
+ mpd_uint_t p_data[2] = {147483647UL, 2};
+ mpd_t p = {MPD_POS|MPD_STATIC|MPD_CONST_DATA, 0, 10, 2, 2, p_data};
+ /* Inverse of 10 modulo p */
+ mpd_uint_t inv10_p_data[2] = {503238553UL, 1};
+ mpd_t inv10_p = {MPD_POS|MPD_STATIC|MPD_CONST_DATA,
+ 0, 10, 2, 2, inv10_p_data};
+#else
+ #error "No valid combination of CONFIG_64, CONFIG_32 and _PyHASH_BITS"
+#endif
+ const Py_hash_t py_hash_inf = 314159;
+ const Py_hash_t py_hash_nan = 0;
+ mpd_uint_t ten_data[1] = {10};
+ mpd_t ten = {MPD_POS|MPD_STATIC|MPD_CONST_DATA,
+ 0, 2, 1, 1, ten_data};
+ Py_hash_t result;
+ mpd_t *exp_hash = NULL;
+ mpd_t *tmp = NULL;
+ mpd_ssize_t exp;
+ uint32_t status = 0;
+ mpd_context_t maxctx;
+ PyObject *context;
+
+
+ context = current_context();
+ if (context == NULL) {
+ return -1;
+ }
+
+ if (mpd_isspecial(MPD(v))) {
+ if (mpd_issnan(MPD(v))) {
+ PyErr_SetString(PyExc_TypeError,
+ "Cannot hash a signaling NaN value");
+ return -1;
+ }
+ else if (mpd_isnan(MPD(v))) {
+ return py_hash_nan;
+ }
+ else {
+ return py_hash_inf * mpd_arith_sign(MPD(v));
+ }
+ }
+
+ mpd_maxcontext(&maxctx);
+ exp_hash = mpd_qnew();
+ if (exp_hash == NULL) {
+ goto malloc_error;
+ }
+ tmp = mpd_qnew();
+ if (tmp == NULL) {
+ goto malloc_error;
+ }
+
+ /*
+ * exp(v): exponent of v
+ * int(v): coefficient of v
+ */
+ exp = MPD(v)->exp;
+ if (exp >= 0) {
+ /* 10**exp(v) % p */
+ mpd_qsset_ssize(tmp, exp, &maxctx, &status);
+ mpd_qpowmod(exp_hash, &ten, tmp, &p, &maxctx, &status);
+ }
+ else {
+ /* inv10_p**(-exp(v)) % p */
+ mpd_qsset_ssize(tmp, -exp, &maxctx, &status);
+ mpd_qpowmod(exp_hash, &inv10_p, tmp, &p, &maxctx, &status);
+ }
+
+ /* hash = (int(v) * exp_hash) % p */
+ if (!mpd_qcopy(tmp, MPD(v), &status)) {
+ goto malloc_error;
+ }
+ tmp->exp = 0;
+ mpd_set_positive(tmp);
+ mpd_qmul(tmp, tmp, exp_hash, &maxctx, &status);
+ mpd_qrem(tmp, tmp, &p, &maxctx, &status);
+
+ result = mpd_qget_ssize(tmp, &status);
+ result = mpd_ispositive(MPD(v)) ? result : -result;
+ result = (result == -1) ? -2 : result;
+
+ if (status != 0) {
+ status |= MPD_Invalid_operation;
+ if (dec_addstatus(context, status)) {
+ result = -1;
+ goto finish;
+ }
+ }
+
+
+finish:
+ if (exp_hash) mpd_del(exp_hash);
+ if (tmp) mpd_del(tmp);
+ return result;
+
+malloc_error:
+ PyErr_NoMemory();
+ result = -1;
+ goto finish;
+}
+
+/* __reduce__ */
+static PyObject *
+dec_reduce(PyObject *self, PyObject *dummy UNUSED)
+{
+ PyObject *result, *str;
+
+ str = dec_str(self);
+ if (str == NULL) {
+ return NULL;
+ }
+
+ result = Py_BuildValue("O(O)", Py_TYPE(self), str);
+ Py_DECREF(str);
+
+ return result;
+}
+
+/* __trunc__ */
+static PyObject *
+dec_trunc(PyObject *self, PyObject *dummy UNUSED)
+{
+ PyObject *context;
+
+ CURRENT_CONTEXT(context);
+ return dec_as_long(self, context, MPD_ROUND_DOWN);
+}
+
+/* real and imag */
+static PyObject *
+dec_real(PyObject *self, void *closure UNUSED)
+{
+ Py_INCREF(self);
+ return self;
+}
+
+static PyObject *
+dec_imag(PyObject *self UNUSED, void *closure UNUSED)
+{
+ PyObject *result;
+
+ result = dec_alloc();
+ if (result == NULL) {
+ return NULL;
+ }
+
+ _dec_settriple(result, MPD_POS, 0, 0);
+ return result;
+}
+
+
+static PyGetSetDef dec_getsets [] =
+{
+ { "real", (getter)dec_real, NULL, NULL, NULL},
+ { "imag", (getter)dec_imag, NULL, NULL, NULL},
+ {NULL}
+};
+
+static PyNumberMethods dec_number_methods =
+{
+ (binaryfunc) nm_mpd_qadd,
+ (binaryfunc) nm_mpd_qsub,
+ (binaryfunc) nm_mpd_qmul,
+ (binaryfunc) nm_mpd_qrem,
+ (binaryfunc) nm_mpd_qdivmod,
+ (ternaryfunc) nm_mpd_qpow,
+ (unaryfunc) nm_mpd_qminus,
+ (unaryfunc) nm_mpd_qplus,
+ (unaryfunc) nm_mpd_qabs,
+ (inquiry) nm_nonzero,
+ (unaryfunc) 0, /* no bit-complement */
+ (binaryfunc) 0, /* no shiftl */
+ (binaryfunc) 0, /* no shiftr */
+ (binaryfunc) 0, /* no bit-and */
+ (binaryfunc) 0, /* no bit-xor */
+ (binaryfunc) 0, /* no bit-ior */
+ (unaryfunc) nm_dec_as_long,
+ 0, /* nb_reserved */
+ (unaryfunc) PyDec_AsFloat,
+ 0, /* binaryfunc nb_inplace_add; */
+ 0, /* binaryfunc nb_inplace_subtract; */
+ 0, /* binaryfunc nb_inplace_multiply; */
+ 0, /* binaryfunc nb_inplace_remainder; */
+ 0, /* ternaryfunc nb_inplace_power; */
+ 0, /* binaryfunc nb_inplace_lshift; */
+ 0, /* binaryfunc nb_inplace_rshift; */
+ 0, /* binaryfunc nb_inplace_and; */
+ 0, /* binaryfunc nb_inplace_xor; */
+ 0, /* binaryfunc nb_inplace_or; */
+ (binaryfunc) nm_mpd_qdivint, /* binaryfunc nb_floor_divide; */
+ (binaryfunc) nm_mpd_qdiv, /* binaryfunc nb_true_divide; */
+ 0, /* binaryfunc nb_inplace_floor_divide; */
+ 0, /* binaryfunc nb_inplace_true_divide; */
+};
+
+static PyMethodDef dec_methods [] =
+{
+ /* Unary arithmetic functions, optional context arg */
+ { "exp", (PyCFunction)dec_mpd_qexp, METH_VARARGS|METH_KEYWORDS, doc_exp },
+ { "ln", (PyCFunction)dec_mpd_qln, METH_VARARGS|METH_KEYWORDS, doc_ln },
+ { "log10", (PyCFunction)dec_mpd_qlog10, METH_VARARGS|METH_KEYWORDS, doc_log10 },
+ { "next_minus", (PyCFunction)dec_mpd_qnext_minus, METH_VARARGS|METH_KEYWORDS, doc_next_minus },
+ { "next_plus", (PyCFunction)dec_mpd_qnext_plus, METH_VARARGS|METH_KEYWORDS, doc_next_plus },
+ { "normalize", (PyCFunction)dec_mpd_qreduce, METH_VARARGS|METH_KEYWORDS, doc_normalize },
+ { "to_integral", (PyCFunction)PyDec_ToIntegralValue, METH_VARARGS|METH_KEYWORDS, doc_to_integral },
+ { "to_integral_exact", (PyCFunction)PyDec_ToIntegralExact, METH_VARARGS|METH_KEYWORDS, doc_to_integral_exact },
+ { "to_integral_value", (PyCFunction)PyDec_ToIntegralValue, METH_VARARGS|METH_KEYWORDS, doc_to_integral_value },
+ { "sqrt", (PyCFunction)dec_mpd_qsqrt, METH_VARARGS|METH_KEYWORDS, doc_sqrt },
+
+ /* Binary arithmetic functions, optional context arg */
+ { "compare", (PyCFunction)dec_mpd_qcompare, METH_VARARGS|METH_KEYWORDS, doc_compare },
+ { "compare_signal", (PyCFunction)dec_mpd_qcompare_signal, METH_VARARGS|METH_KEYWORDS, doc_compare_signal },
+ { "max", (PyCFunction)dec_mpd_qmax, METH_VARARGS|METH_KEYWORDS, doc_max },
+ { "max_mag", (PyCFunction)dec_mpd_qmax_mag, METH_VARARGS|METH_KEYWORDS, doc_max_mag },
+ { "min", (PyCFunction)dec_mpd_qmin, METH_VARARGS|METH_KEYWORDS, doc_min },
+ { "min_mag", (PyCFunction)dec_mpd_qmin_mag, METH_VARARGS|METH_KEYWORDS, doc_min_mag },
+ { "next_toward", (PyCFunction)dec_mpd_qnext_toward, METH_VARARGS|METH_KEYWORDS, doc_next_toward },
+ { "quantize", (PyCFunction)dec_mpd_qquantize, METH_VARARGS|METH_KEYWORDS, doc_quantize },
+ { "remainder_near", (PyCFunction)dec_mpd_qrem_near, METH_VARARGS|METH_KEYWORDS, doc_remainder_near },
+
+ /* Ternary arithmetic functions, optional context arg */
+ { "fma", (PyCFunction)dec_mpd_qfma, METH_VARARGS|METH_KEYWORDS, doc_fma },
+
+ /* Boolean functions, no context arg */
+ { "is_canonical", dec_mpd_iscanonical, METH_NOARGS, doc_is_canonical },
+ { "is_finite", dec_mpd_isfinite, METH_NOARGS, doc_is_finite },
+ { "is_infinite", dec_mpd_isinfinite, METH_NOARGS, doc_is_infinite },
+ { "is_nan", dec_mpd_isnan, METH_NOARGS, doc_is_nan },
+ { "is_qnan", dec_mpd_isqnan, METH_NOARGS, doc_is_qnan },
+ { "is_snan", dec_mpd_issnan, METH_NOARGS, doc_is_snan },
+ { "is_signed", dec_mpd_issigned, METH_NOARGS, doc_is_signed },
+ { "is_zero", dec_mpd_iszero, METH_NOARGS, doc_is_zero },
+
+ /* Boolean functions, optional context arg */
+ { "is_normal", (PyCFunction)dec_mpd_isnormal, METH_VARARGS|METH_KEYWORDS, doc_is_normal },
+ { "is_subnormal", (PyCFunction)dec_mpd_issubnormal, METH_VARARGS|METH_KEYWORDS, doc_is_subnormal },
+
+ /* Unary functions, no context arg */
+ { "adjusted", dec_mpd_adjexp, METH_NOARGS, doc_adjusted },
+ { "canonical", dec_canonical, METH_NOARGS, doc_canonical },
+ { "conjugate", dec_conjugate, METH_NOARGS, doc_conjugate },
+ { "radix", dec_mpd_radix, METH_NOARGS, doc_radix },
+
+ /* Unary functions, optional context arg for conversion errors */
+ { "copy_abs", (PyCFunction)dec_mpd_qcopy_abs, METH_VARARGS|METH_KEYWORDS, doc_copy_abs },
+ { "copy_negate", (PyCFunction)dec_mpd_qcopy_negate, METH_VARARGS|METH_KEYWORDS, doc_copy_negate },
+
+ /* Unary functions, optional context arg */
+ { "logb", (PyCFunction)dec_mpd_qlogb, METH_VARARGS|METH_KEYWORDS, doc_logb },
+ { "logical_invert", (PyCFunction)dec_mpd_qinvert, METH_VARARGS|METH_KEYWORDS, doc_logical_invert },
+ { "number_class", (PyCFunction)dec_mpd_class, METH_VARARGS|METH_KEYWORDS, doc_number_class },
+ { "to_eng_string", (PyCFunction)dec_mpd_to_eng, METH_VARARGS|METH_KEYWORDS, doc_to_eng_string },
+
+ /* Binary functions, optional context arg for conversion errors */
+ { "compare_total", (PyCFunction)dec_mpd_compare_total, METH_VARARGS|METH_KEYWORDS, doc_compare_total },
+ { "compare_total_mag", (PyCFunction)dec_mpd_compare_total_mag, METH_VARARGS|METH_KEYWORDS, doc_compare_total_mag },
+ { "copy_sign", (PyCFunction)dec_mpd_qcopy_sign, METH_VARARGS|METH_KEYWORDS, doc_copy_sign },
+ { "same_quantum", (PyCFunction)dec_mpd_same_quantum, METH_VARARGS|METH_KEYWORDS, doc_same_quantum },
+
+ /* Binary functions, optional context arg */
+ { "logical_and", (PyCFunction)dec_mpd_qand, METH_VARARGS|METH_KEYWORDS, doc_logical_and },
+ { "logical_or", (PyCFunction)dec_mpd_qor, METH_VARARGS|METH_KEYWORDS, doc_logical_or },
+ { "logical_xor", (PyCFunction)dec_mpd_qxor, METH_VARARGS|METH_KEYWORDS, doc_logical_xor },
+ { "rotate", (PyCFunction)dec_mpd_qrotate, METH_VARARGS|METH_KEYWORDS, doc_rotate },
+ { "scaleb", (PyCFunction)dec_mpd_qscaleb, METH_VARARGS|METH_KEYWORDS, doc_scaleb },
+ { "shift", (PyCFunction)dec_mpd_qshift, METH_VARARGS|METH_KEYWORDS, doc_shift },
+
+ /* Miscellaneous */
+ { "from_float", dec_from_float, METH_O|METH_CLASS, doc_from_float },
+ { "as_tuple", PyDec_AsTuple, METH_NOARGS, doc_as_tuple },
+
+ /* Special methods */
+ { "__copy__", dec_copy, METH_NOARGS, NULL },
+ { "__deepcopy__", dec_copy, METH_O, NULL },
+ { "__format__", dec_format, METH_VARARGS, NULL },
+ { "__reduce__", dec_reduce, METH_NOARGS, NULL },
+ { "__round__", PyDec_Round, METH_VARARGS, NULL },
+ { "__ceil__", dec_ceil, METH_NOARGS, NULL },
+ { "__floor__", dec_floor, METH_NOARGS, NULL },
+ { "__trunc__", dec_trunc, METH_NOARGS, NULL },
+ { "__complex__", dec_complex, METH_NOARGS, NULL },
+
+ { NULL, NULL, 1 }
+};
+
+static PyTypeObject PyDec_Type =
+{
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "decimal.Decimal", /* tp_name */
+ sizeof(PyDecObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor) dec_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ (getattrfunc) 0, /* tp_getattr */
+ (setattrfunc) 0, /* tp_setattr */
+ 0, /* tp_reserved */
+ (reprfunc) dec_repr, /* tp_repr */
+ &dec_number_methods, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc) dec_hash, /* tp_hash */
+ 0, /* tp_call */
+ (reprfunc) dec_str, /* tp_str */
+ (getattrofunc) PyObject_GenericGetAttr, /* tp_getattro */
+ (setattrofunc) 0, /* tp_setattro */
+ (PyBufferProcs *) 0, /* tp_as_buffer */
+ (Py_TPFLAGS_DEFAULT|
+ Py_TPFLAGS_BASETYPE), /* tp_flags */
+ doc_decimal, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ dec_richcompare, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ dec_methods, /* tp_methods */
+ 0, /* tp_members */
+ dec_getsets, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ dec_new, /* tp_new */
+ PyObject_Del, /* tp_free */
+};
+
+
+/******************************************************************************/
+/* Context Object, Part 2 */
+/******************************************************************************/
+
+
+/************************************************************************/
+/* Macros for converting mpdecimal functions to Context methods */
+/************************************************************************/
+
+/* Boolean context method. */
+#define DecCtx_BoolFunc(MPDFUNC) \
+static PyObject * \
+ctx_##MPDFUNC(PyObject *context, PyObject *v) \
+{ \
+ PyObject *ret; \
+ PyObject *a; \
+ \
+ CONVERT_OP_RAISE(&a, v, context); \
+ \
+ ret = MPDFUNC(MPD(a), CTX(context)) ? incr_true() : incr_false(); \
+ Py_DECREF(a); \
+ return ret; \
+}
+
+/* Boolean context method. MPDFUNC does NOT use a context. */
+#define DecCtx_BoolFunc_NO_CTX(MPDFUNC) \
+static PyObject * \
+ctx_##MPDFUNC(PyObject *context, PyObject *v) \
+{ \
+ PyObject *ret; \
+ PyObject *a; \
+ \
+ CONVERT_OP_RAISE(&a, v, context); \
+ \
+ ret = MPDFUNC(MPD(a)) ? incr_true() : incr_false(); \
+ Py_DECREF(a); \
+ return ret; \
+}
+
+/* Unary context method. */
+#define DecCtx_UnaryFunc(MPDFUNC) \
+static PyObject * \
+ctx_##MPDFUNC(PyObject *context, PyObject *v) \
+{ \
+ PyObject *result, *a; \
+ uint32_t status = 0; \
+ \
+ CONVERT_OP_RAISE(&a, v, context); \
+ \
+ if ((result = dec_alloc()) == NULL) { \
+ Py_DECREF(a); \
+ return NULL; \
+ } \
+ \
+ MPDFUNC(MPD(result), MPD(a), CTX(context), &status); \
+ Py_DECREF(a); \
+ if (dec_addstatus(context, status)) { \
+ Py_DECREF(result); \
+ return NULL; \
+ } \
+ \
+ return result; \
+}
+
+/* Binary context method. */
+#define DecCtx_BinaryFunc(MPDFUNC) \
+static PyObject * \
+ctx_##MPDFUNC(PyObject *context, PyObject *args) \
+{ \
+ PyObject *v, *w; \
+ PyObject *a, *b; \
+ PyObject *result; \
+ uint32_t status = 0; \
+ \
+ if (!PyArg_ParseTuple(args, "OO", &v, &w)) { \
+ return NULL; \
+ } \
+ \
+ CONVERT_BINOP_RAISE(&a, &b, v, w, context); \
+ \
+ if ((result = dec_alloc()) == NULL) { \
+ Py_DECREF(a); \
+ Py_DECREF(b); \
+ return NULL; \
+ } \
+ \
+ MPDFUNC(MPD(result), MPD(a), MPD(b), CTX(context), &status); \
+ Py_DECREF(a); \
+ Py_DECREF(b); \
+ if (dec_addstatus(context, status)) { \
+ Py_DECREF(result); \
+ return NULL; \
+ } \
+ \
+ return result; \
+}
+
+/*
+ * Binary context method. The context is only used for conversion.
+ * The actual MPDFUNC does NOT take a context arg.
+ */
+#define DecCtx_BinaryFunc_NO_CTX(MPDFUNC) \
+static PyObject * \
+ctx_##MPDFUNC(PyObject *context, PyObject *args) \
+{ \
+ PyObject *v, *w; \
+ PyObject *a, *b; \
+ PyObject *result; \
+ \
+ if (!PyArg_ParseTuple(args, "OO", &v, &w)) { \
+ return NULL; \
+ } \
+ \
+ CONVERT_BINOP_RAISE(&a, &b, v, w, context); \
+ \
+ if ((result = dec_alloc()) == NULL) { \
+ Py_DECREF(a); \
+ Py_DECREF(b); \
+ return NULL; \
+ } \
+ \
+ MPDFUNC(MPD(result), MPD(a), MPD(b)); \
+ Py_DECREF(a); \
+ Py_DECREF(b); \
+ \
+ return result; \
+}
+
+/* Ternary context method. */
+#define DecCtx_TernaryFunc(MPDFUNC) \
+static PyObject * \
+ctx_##MPDFUNC(PyObject *context, PyObject *args) \
+{ \
+ PyObject *v, *w, *x; \
+ PyObject *a, *b, *c; \
+ PyObject *result; \
+ uint32_t status = 0; \
+ \
+ if (!PyArg_ParseTuple(args, "OOO", &v, &w, &x)) { \
+ return NULL; \
+ } \
+ \
+ CONVERT_TERNOP_RAISE(&a, &b, &c, v, w, x, context); \
+ \
+ if ((result = dec_alloc()) == NULL) { \
+ Py_DECREF(a); \
+ Py_DECREF(b); \
+ Py_DECREF(c); \
+ return NULL; \
+ } \
+ \
+ MPDFUNC(MPD(result), MPD(a), MPD(b), MPD(c), CTX(context), &status); \
+ Py_DECREF(a); \
+ Py_DECREF(b); \
+ Py_DECREF(c); \
+ if (dec_addstatus(context, status)) { \
+ Py_DECREF(result); \
+ return NULL; \
+ } \
+ \
+ return result; \
+}
+
+
+/* Unary arithmetic functions */
+DecCtx_UnaryFunc(mpd_qabs)
+DecCtx_UnaryFunc(mpd_qexp)
+DecCtx_UnaryFunc(mpd_qln)
+DecCtx_UnaryFunc(mpd_qlog10)
+DecCtx_UnaryFunc(mpd_qminus)
+DecCtx_UnaryFunc(mpd_qnext_minus)
+DecCtx_UnaryFunc(mpd_qnext_plus)
+DecCtx_UnaryFunc(mpd_qplus)
+DecCtx_UnaryFunc(mpd_qreduce)
+DecCtx_UnaryFunc(mpd_qround_to_int)
+DecCtx_UnaryFunc(mpd_qround_to_intx)
+DecCtx_UnaryFunc(mpd_qsqrt)
+
+/* Binary arithmetic functions */
+DecCtx_BinaryFunc(mpd_qadd)
+DecCtx_BinaryFunc(mpd_qcompare)
+DecCtx_BinaryFunc(mpd_qcompare_signal)
+DecCtx_BinaryFunc(mpd_qdiv)
+DecCtx_BinaryFunc(mpd_qdivint)
+DecCtx_BinaryFunc(mpd_qmax)
+DecCtx_BinaryFunc(mpd_qmax_mag)
+DecCtx_BinaryFunc(mpd_qmin)
+DecCtx_BinaryFunc(mpd_qmin_mag)
+DecCtx_BinaryFunc(mpd_qmul)
+DecCtx_BinaryFunc(mpd_qnext_toward)
+DecCtx_BinaryFunc(mpd_qquantize)
+DecCtx_BinaryFunc(mpd_qrem)
+DecCtx_BinaryFunc(mpd_qrem_near)
+DecCtx_BinaryFunc(mpd_qsub)
+
+static PyObject *
+ctx_mpd_qdivmod(PyObject *context, PyObject *args)
+{
+ PyObject *v, *w;
+ PyObject *a, *b;
+ PyObject *q, *r;
+ uint32_t status = 0;
+ PyObject *ret;
+
+ if (!PyArg_ParseTuple(args, "OO", &v, &w)) {
+ return NULL;
+ }
+
+ CONVERT_BINOP_RAISE(&a, &b, v, w, context);
+
+ q = dec_alloc();
+ if (q == NULL) {
+ Py_DECREF(a);
+ Py_DECREF(b);
+ return NULL;
+ }
+ r = dec_alloc();
+ if (r == NULL) {
+ Py_DECREF(a);
+ Py_DECREF(b);
+ Py_DECREF(q);
+ return NULL;
+ }
+
+ mpd_qdivmod(MPD(q), MPD(r), MPD(a), MPD(b), CTX(context), &status);
+ Py_DECREF(a);
+ Py_DECREF(b);
+ if (dec_addstatus(context, status)) {
+ Py_DECREF(r);
+ Py_DECREF(q);
+ return NULL;
+ }
+
+ ret = Py_BuildValue("(OO)", q, r);
+ Py_DECREF(r);
+ Py_DECREF(q);
+ return ret;
+}
+
+/* Binary or ternary arithmetic functions */
+static PyObject *
+ctx_mpd_qpow(PyObject *context, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"a", "b", "modulo", NULL};
+ PyObject *base, *exp, *mod = NULL;
+ PyObject *a, *b, *c = NULL;
+ PyObject *result;
+ uint32_t status = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|O", kwlist,
+ &base, &exp, &mod)) {
+ return NULL;
+ }
+
+ CONVERT_BINOP_RAISE(&a, &b, base, exp, context);
+
+ if (mod != NULL) {
+ if (!convert_op(TYPE_ERR, &c, mod, context)) {
+ Py_DECREF(a);
+ Py_DECREF(b);
+ return c;
+ }
+ }
+
+ result = dec_alloc();
+ if (result == NULL) {
+ Py_DECREF(a);
+ Py_DECREF(b);
+ Py_XDECREF(c);
+ return NULL;
+ }
+
+ if (c == NULL) {
+ mpd_qpow(MPD(result), MPD(a), MPD(b),
+ CTX(context), &status);
+ }
+ else {
+ mpd_qpowmod(MPD(result), MPD(a), MPD(b), MPD(c),
+ CTX(context), &status);
+ status = (status == MPD_Clamped) ? 0 : status;
+ /* remove ideal exponent for compatibility with decimal.py */
+ mpd_qquantize(MPD(result), MPD(result), &zero,
+ CTX(context), &status);
+ Py_DECREF(c);
+ }
+ Py_DECREF(a);
+ Py_DECREF(b);
+ if (dec_addstatus(context, status)) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ return result;
+}
+
+/* Ternary arithmetic functions */
+DecCtx_TernaryFunc(mpd_qfma)
+
+/* No argument */
+static PyObject *
+ctx_mpd_radix(PyObject *context, PyObject *dummy)
+{
+ return dec_mpd_radix(context, dummy);
+}
+
+/* Boolean functions: single decimal argument */
+DecCtx_BoolFunc(mpd_isnormal)
+DecCtx_BoolFunc(mpd_issubnormal)
+DecCtx_BoolFunc_NO_CTX(mpd_isfinite)
+DecCtx_BoolFunc_NO_CTX(mpd_isinfinite)
+DecCtx_BoolFunc_NO_CTX(mpd_isnan)
+DecCtx_BoolFunc_NO_CTX(mpd_isqnan)
+DecCtx_BoolFunc_NO_CTX(mpd_issigned)
+DecCtx_BoolFunc_NO_CTX(mpd_issnan)
+DecCtx_BoolFunc_NO_CTX(mpd_iszero)
+
+static PyObject *
+ctx_iscanonical(PyObject *context UNUSED, PyObject *v)
+{
+ if (!PyDec_Check(v)) {
+ PyErr_SetString(PyExc_TypeError,
+ "argument must be a Decimal");
+ return NULL;
+ }
+
+ return mpd_iscanonical(MPD(v)) ? incr_true() : incr_false();
+}
+
+/* Functions with a single decimal argument */
+static PyObject *
+PyDecContext_Apply(PyObject *context, PyObject *v)
+{
+ PyObject *result, *a;
+
+ CONVERT_OP_RAISE(&a, v, context);
+
+ result = dec_apply(a, context);
+ Py_DECREF(a);
+ return result;
+}
+
+static PyObject *
+ctx_canonical(PyObject *context UNUSED, PyObject *v)
+{
+ if (!PyDec_Check(v)) {
+ PyErr_SetString(PyExc_TypeError,
+ "argument must be a Decimal");
+ return NULL;
+ }
+
+ Py_INCREF(v);
+ return v;
+}
+
+static PyObject *
+ctx_mpd_qcopy_abs(PyObject *context, PyObject *v)
+{
+ PyObject *result, *a;
+ uint32_t status = 0;
+
+ CONVERT_OP_RAISE(&a, v, context);
+
+ result = dec_alloc();
+ if (result == NULL) {
+ Py_DECREF(a);
+ return NULL;
+ }
+
+ mpd_qcopy_abs(MPD(result), MPD(a), &status);
+ Py_DECREF(a);
+ if (dec_addstatus(context, status)) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ return result;
+}
+
+static PyObject *
+ctx_copy_decimal(PyObject *context, PyObject *v)
+{
+ PyObject *result;
+
+ CONVERT_OP_RAISE(&result, v, context);
+ return result;
+}
+
+static PyObject *
+ctx_mpd_qcopy_negate(PyObject *context, PyObject *v)
+{
+ PyObject *result, *a;
+ uint32_t status = 0;
+
+ CONVERT_OP_RAISE(&a, v, context);
+
+ result = dec_alloc();
+ if (result == NULL) {
+ Py_DECREF(a);
+ return NULL;
+ }
+
+ mpd_qcopy_negate(MPD(result), MPD(a), &status);
+ Py_DECREF(a);
+ if (dec_addstatus(context, status)) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ return result;
+}
+
+DecCtx_UnaryFunc(mpd_qlogb)
+DecCtx_UnaryFunc(mpd_qinvert)
+
+static PyObject *
+ctx_mpd_class(PyObject *context, PyObject *v)
+{
+ PyObject *a;
+ const char *cp;
+
+ CONVERT_OP_RAISE(&a, v, context);
+
+ cp = mpd_class(MPD(a), CTX(context));
+ Py_DECREF(a);
+
+ return PyUnicode_FromString(cp);
+}
+
+static PyObject *
+ctx_mpd_to_sci(PyObject *context, PyObject *v)
+{
+ PyObject *result;
+ PyObject *a;
+ mpd_ssize_t size;
+ char *s;
+
+ CONVERT_OP_RAISE(&a, v, context);
+
+ size = mpd_to_sci_size(&s, MPD(a), CtxCaps(context));
+ Py_DECREF(a);
+ if (size < 0) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ result = unicode_fromascii(s, size);
+ mpd_free(s);
+
+ return result;
+}
+
+static PyObject *
+ctx_mpd_to_eng(PyObject *context, PyObject *v)
+{
+ PyObject *result;
+ PyObject *a;
+ mpd_ssize_t size;
+ char *s;
+
+ CONVERT_OP_RAISE(&a, v, context);
+
+ size = mpd_to_eng_size(&s, MPD(a), CtxCaps(context));
+ Py_DECREF(a);
+ if (size < 0) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ result = unicode_fromascii(s, size);
+ mpd_free(s);
+
+ return result;
+}
+
+/* Functions with two decimal arguments */
+DecCtx_BinaryFunc_NO_CTX(mpd_compare_total)
+DecCtx_BinaryFunc_NO_CTX(mpd_compare_total_mag)
+
+static PyObject *
+ctx_mpd_qcopy_sign(PyObject *context, PyObject *args)
+{
+ PyObject *v, *w;
+ PyObject *a, *b;
+ PyObject *result;
+ uint32_t status = 0;
+
+ if (!PyArg_ParseTuple(args, "OO", &v, &w)) {
+ return NULL;
+ }
+
+ CONVERT_BINOP_RAISE(&a, &b, v, w, context);
+
+ result = dec_alloc();
+ if (result == NULL) {
+ Py_DECREF(a);
+ Py_DECREF(b);
+ return NULL;
+ }
+
+ mpd_qcopy_sign(MPD(result), MPD(a), MPD(b), &status);
+ Py_DECREF(a);
+ Py_DECREF(b);
+ if (dec_addstatus(context, status)) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ return result;
+}
+
+DecCtx_BinaryFunc(mpd_qand)
+DecCtx_BinaryFunc(mpd_qor)
+DecCtx_BinaryFunc(mpd_qxor)
+
+DecCtx_BinaryFunc(mpd_qrotate)
+DecCtx_BinaryFunc(mpd_qscaleb)
+DecCtx_BinaryFunc(mpd_qshift)
+
+static PyObject *
+ctx_mpd_same_quantum(PyObject *context, PyObject *args)
+{
+ PyObject *v, *w;
+ PyObject *a, *b;
+ PyObject *result;
+
+ if (!PyArg_ParseTuple(args, "OO", &v, &w)) {
+ return NULL;
+ }
+
+ CONVERT_BINOP_RAISE(&a, &b, v, w, context);
+
+ result = mpd_same_quantum(MPD(a), MPD(b)) ? incr_true() : incr_false();
+ Py_DECREF(a);
+ Py_DECREF(b);
+
+ return result;
+}
+
+
+static PyMethodDef context_methods [] =
+{
+ /* Unary arithmetic functions */
+ { "abs", ctx_mpd_qabs, METH_O, doc_ctx_abs },
+ { "exp", ctx_mpd_qexp, METH_O, doc_ctx_exp },
+ { "ln", ctx_mpd_qln, METH_O, doc_ctx_ln },
+ { "log10", ctx_mpd_qlog10, METH_O, doc_ctx_log10 },
+ { "minus", ctx_mpd_qminus, METH_O, doc_ctx_minus },
+ { "next_minus", ctx_mpd_qnext_minus, METH_O, doc_ctx_next_minus },
+ { "next_plus", ctx_mpd_qnext_plus, METH_O, doc_ctx_next_plus },
+ { "normalize", ctx_mpd_qreduce, METH_O, doc_ctx_normalize },
+ { "plus", ctx_mpd_qplus, METH_O, doc_ctx_plus },
+ { "to_integral", ctx_mpd_qround_to_int, METH_O, doc_ctx_to_integral },
+ { "to_integral_exact", ctx_mpd_qround_to_intx, METH_O, doc_ctx_to_integral_exact },
+ { "to_integral_value", ctx_mpd_qround_to_int, METH_O, doc_ctx_to_integral_value },
+ { "sqrt", ctx_mpd_qsqrt, METH_O, doc_ctx_sqrt },
+
+ /* Binary arithmetic functions */
+ { "add", ctx_mpd_qadd, METH_VARARGS, doc_ctx_add },
+ { "compare", ctx_mpd_qcompare, METH_VARARGS, doc_ctx_compare },
+ { "compare_signal", ctx_mpd_qcompare_signal, METH_VARARGS, doc_ctx_compare_signal },
+ { "divide", ctx_mpd_qdiv, METH_VARARGS, doc_ctx_divide },
+ { "divide_int", ctx_mpd_qdivint, METH_VARARGS, doc_ctx_divide_int },
+ { "divmod", ctx_mpd_qdivmod, METH_VARARGS, doc_ctx_divmod },
+ { "max", ctx_mpd_qmax, METH_VARARGS, doc_ctx_max },
+ { "max_mag", ctx_mpd_qmax_mag, METH_VARARGS, doc_ctx_max_mag },
+ { "min", ctx_mpd_qmin, METH_VARARGS, doc_ctx_min },
+ { "min_mag", ctx_mpd_qmin_mag, METH_VARARGS, doc_ctx_min_mag },
+ { "multiply", ctx_mpd_qmul, METH_VARARGS, doc_ctx_multiply },
+ { "next_toward", ctx_mpd_qnext_toward, METH_VARARGS, doc_ctx_next_toward },
+ { "quantize", ctx_mpd_qquantize, METH_VARARGS, doc_ctx_quantize },
+ { "remainder", ctx_mpd_qrem, METH_VARARGS, doc_ctx_remainder },
+ { "remainder_near", ctx_mpd_qrem_near, METH_VARARGS, doc_ctx_remainder_near },
+ { "subtract", ctx_mpd_qsub, METH_VARARGS, doc_ctx_subtract },
+
+ /* Binary or ternary arithmetic functions */
+ { "power", (PyCFunction)ctx_mpd_qpow, METH_VARARGS|METH_KEYWORDS, doc_ctx_power },
+
+ /* Ternary arithmetic functions */
+ { "fma", ctx_mpd_qfma, METH_VARARGS, doc_ctx_fma },
+
+ /* No argument */
+ { "Etiny", context_getetiny, METH_NOARGS, doc_ctx_Etiny },
+ { "Etop", context_getetop, METH_NOARGS, doc_ctx_Etop },
+ { "radix", ctx_mpd_radix, METH_NOARGS, doc_ctx_radix },
+
+ /* Boolean functions */
+ { "is_canonical", ctx_iscanonical, METH_O, doc_ctx_is_canonical },
+ { "is_finite", ctx_mpd_isfinite, METH_O, doc_ctx_is_finite },
+ { "is_infinite", ctx_mpd_isinfinite, METH_O, doc_ctx_is_infinite },
+ { "is_nan", ctx_mpd_isnan, METH_O, doc_ctx_is_nan },
+ { "is_normal", ctx_mpd_isnormal, METH_O, doc_ctx_is_normal },
+ { "is_qnan", ctx_mpd_isqnan, METH_O, doc_ctx_is_qnan },
+ { "is_signed", ctx_mpd_issigned, METH_O, doc_ctx_is_signed },
+ { "is_snan", ctx_mpd_issnan, METH_O, doc_ctx_is_snan },
+ { "is_subnormal", ctx_mpd_issubnormal, METH_O, doc_ctx_is_subnormal },
+ { "is_zero", ctx_mpd_iszero, METH_O, doc_ctx_is_zero },
+
+ /* Functions with a single decimal argument */
+ { "_apply", PyDecContext_Apply, METH_O, NULL }, /* alias for apply */
+#ifdef EXTRA_FUNCTIONALITY
+ { "apply", PyDecContext_Apply, METH_O, doc_ctx_apply },
+#endif
+ { "canonical", ctx_canonical, METH_O, doc_ctx_canonical },
+ { "copy_abs", ctx_mpd_qcopy_abs, METH_O, doc_ctx_copy_abs },
+ { "copy_decimal", ctx_copy_decimal, METH_O, doc_ctx_copy_decimal },
+ { "copy_negate", ctx_mpd_qcopy_negate, METH_O, doc_ctx_copy_negate },
+ { "logb", ctx_mpd_qlogb, METH_O, doc_ctx_logb },
+ { "logical_invert", ctx_mpd_qinvert, METH_O, doc_ctx_logical_invert },
+ { "number_class", ctx_mpd_class, METH_O, doc_ctx_number_class },
+ { "to_sci_string", ctx_mpd_to_sci, METH_O, doc_ctx_to_sci_string },
+ { "to_eng_string", ctx_mpd_to_eng, METH_O, doc_ctx_to_eng_string },
+
+ /* Functions with two decimal arguments */
+ { "compare_total", ctx_mpd_compare_total, METH_VARARGS, doc_ctx_compare_total },
+ { "compare_total_mag", ctx_mpd_compare_total_mag, METH_VARARGS, doc_ctx_compare_total_mag },
+ { "copy_sign", ctx_mpd_qcopy_sign, METH_VARARGS, doc_ctx_copy_sign },
+ { "logical_and", ctx_mpd_qand, METH_VARARGS, doc_ctx_logical_and },
+ { "logical_or", ctx_mpd_qor, METH_VARARGS, doc_ctx_logical_or },
+ { "logical_xor", ctx_mpd_qxor, METH_VARARGS, doc_ctx_logical_xor },
+ { "rotate", ctx_mpd_qrotate, METH_VARARGS, doc_ctx_rotate },
+ { "same_quantum", ctx_mpd_same_quantum, METH_VARARGS, doc_ctx_same_quantum },
+ { "scaleb", ctx_mpd_qscaleb, METH_VARARGS, doc_ctx_scaleb },
+ { "shift", ctx_mpd_qshift, METH_VARARGS, doc_ctx_shift },
+
+ /* Set context values */
+ { "clear_flags", context_clear_flags, METH_NOARGS, doc_ctx_clear_flags },
+ { "clear_traps", context_clear_traps, METH_NOARGS, doc_ctx_clear_traps },
+
+#ifdef CONFIG_32
+ /* Unsafe set functions with relaxed range checks */
+ { "_unsafe_setprec", context_unsafe_setprec, METH_O, NULL },
+ { "_unsafe_setemin", context_unsafe_setemin, METH_O, NULL },
+ { "_unsafe_setemax", context_unsafe_setemax, METH_O, NULL },
+#endif
+
+ /* Miscellaneous */
+ { "__copy__", (PyCFunction)context_copy, METH_NOARGS, NULL },
+ { "__reduce__", context_reduce, METH_NOARGS, NULL },
+ { "copy", (PyCFunction)context_copy, METH_NOARGS, doc_ctx_copy },
+ { "create_decimal", ctx_create_decimal, METH_VARARGS, doc_ctx_create_decimal },
+ { "create_decimal_from_float", ctx_from_float, METH_O, doc_ctx_create_decimal_from_float },
+
+ { NULL, NULL, 1 }
+};
+
+static PyTypeObject PyDecContext_Type =
+{
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "decimal.Context", /* tp_name */
+ sizeof(PyDecContextObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor) context_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ (getattrfunc) 0, /* tp_getattr */
+ (setattrfunc) 0, /* tp_setattr */
+ 0, /* tp_reserved */
+ (reprfunc) context_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc) 0, /* tp_hash */
+ 0, /* tp_call */
+ (reprfunc) context_repr, /* tp_str */
+ (getattrofunc) context_getattr, /* tp_getattro */
+ (setattrofunc) context_setattr, /* tp_setattro */
+ (PyBufferProcs *) 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */
+ doc_context, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ context_methods, /* tp_methods */
+ 0, /* tp_members */
+ context_getsets, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ context_init, /* tp_init */
+ 0, /* tp_alloc */
+ context_new, /* tp_new */
+ PyObject_Del, /* tp_free */
+};
+
+
+static PyMethodDef _decimal_methods [] =
+{
+ { "getcontext", (PyCFunction)PyDec_GetCurrentContext, METH_NOARGS, doc_getcontext},
+ { "setcontext", (PyCFunction)PyDec_SetCurrentContext, METH_O, doc_setcontext},
+ { "localcontext", (PyCFunction)ctxmanager_new, METH_VARARGS, doc_localcontext},
+#ifdef EXTRA_FUNCTIONALITY
+ { "IEEEContext", (PyCFunction)ieee_context, METH_O, doc_ieee_context},
+#endif
+ { NULL, NULL, 1, NULL }
+};
+
+static struct PyModuleDef _decimal_module = {
+ PyModuleDef_HEAD_INIT,
+ "decimal",
+ doc__decimal,
+ -1,
+ _decimal_methods,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+struct ssize_constmap { const char *name; mpd_ssize_t val; };
+static struct ssize_constmap ssize_constants [] = {
+ {"MAX_PREC", MPD_MAX_PREC},
+ {"MAX_EMAX", MPD_MAX_EMAX},
+ {"MIN_EMIN", MPD_MIN_EMIN},
+ {"MIN_ETINY", MPD_MIN_ETINY},
+ {NULL}
+};
+
+struct int_constmap { const char *name; int val; };
+static struct int_constmap int_constants [] = {
+ /* int constants */
+#ifdef EXTRA_FUNCTIONALITY
+ {"DECIMAL32", MPD_DECIMAL32},
+ {"DECIMAL64", MPD_DECIMAL64},
+ {"DECIMAL128", MPD_DECIMAL128},
+ {"IEEE_CONTEXT_MAX_BITS", MPD_IEEE_CONTEXT_MAX_BITS},
+#endif
+ {"ROUND_CEILING", MPD_ROUND_CEILING},
+ {"ROUND_FLOOR", MPD_ROUND_FLOOR},
+ {"ROUND_UP", MPD_ROUND_UP},
+ {"ROUND_DOWN", MPD_ROUND_DOWN},
+ {"ROUND_HALF_UP", MPD_ROUND_HALF_UP},
+ {"ROUND_HALF_DOWN", MPD_ROUND_HALF_DOWN},
+ {"ROUND_HALF_EVEN", MPD_ROUND_HALF_EVEN},
+ {"ROUND_05UP", MPD_ROUND_05UP},
+#ifdef EXTRA_FUNCTIONALITY
+ {"ROUND_TRUNC", MPD_ROUND_TRUNC},
+ /* int condition flags */
+ {"DecClamped", MPD_Clamped},
+ {"DecConversionSyntax", MPD_Conversion_syntax},
+ {"DecDivisionByZero", MPD_Division_by_zero},
+ {"DecDivisionImpossible", MPD_Division_impossible},
+ {"DecDivisionUndefined", MPD_Division_undefined},
+ {"DecFpuError", MPD_Fpu_error},
+ {"DecInexact", MPD_Inexact},
+ {"DecInvalidContext", MPD_Invalid_context},
+ {"DecInvalidOperation", MPD_Invalid_operation},
+ {"DecIEEEInvalidOperation", MPD_IEEE_Invalid_operation},
+ {"DecMallocError", MPD_Malloc_error},
+ {"DecFloatOperation", MPD_Float_operation},
+ {"DecOverflow", MPD_Overflow},
+ {"DecRounded", MPD_Rounded},
+ {"DecSubnormal", MPD_Subnormal},
+ {"DecUnderflow", MPD_Underflow},
+ {"DecErrors", MPD_Errors},
+ {"DecTraps", MPD_Traps},
+#endif
+ {NULL}
+};
+
+
+#define CHECK_INT(expr) \
+ do { if ((expr) < 0) goto error; } while (0)
+#define ASSIGN_PTR(result, expr) \
+ do { result = (expr); if (result == NULL) goto error; } while (0)
+#define CHECK_PTR(expr) \
+ do { if ((expr) == NULL) goto error; } while (0)
+
+PyMODINIT_FUNC
+PyInit__decimal(void)
+{
+ PyObject *m = NULL;
+ PyObject *numbers = NULL;
+ PyObject *Number = NULL;
+ PyObject *collections = NULL;
+ PyObject *MutableMapping = NULL;
+ PyObject *obj = NULL;
+ DecCondMap *cm;
+ struct ssize_constmap *ssize_cm;
+ struct int_constmap *int_cm;
+ int i;
+
+
+ /* Init libmpdec */
+ mpd_traphandler = dec_traphandler;
+ mpd_mallocfunc = PyMem_Malloc;
+ mpd_reallocfunc = PyMem_Realloc;
+ mpd_callocfunc = mpd_callocfunc_em;
+ mpd_free = PyMem_Free;
+ mpd_setminalloc(4);
+
+
+ /* Init types */
+ PyDec_Type.tp_base = &PyBaseObject_Type;
+ PyDecContext_Type.tp_base = &PyBaseObject_Type;
+ PyDecContextManager_Type.tp_base = &PyBaseObject_Type;
+ PyDecSignalDictMixin_Type.tp_base = &PyBaseObject_Type;
+
+ CHECK_INT(PyType_Ready(&PyDec_Type));
+ CHECK_INT(PyType_Ready(&PyDecContext_Type));
+ CHECK_INT(PyType_Ready(&PyDecSignalDictMixin_Type));
+ CHECK_INT(PyType_Ready(&PyDecContextManager_Type));
+
+ ASSIGN_PTR(obj, PyUnicode_FromString("decimal"));
+ CHECK_INT(PyDict_SetItemString(PyDec_Type.tp_dict, "__module__", obj));
+ CHECK_INT(PyDict_SetItemString(PyDecContext_Type.tp_dict,
+ "__module__", obj));
+ Py_CLEAR(obj);
+
+
+ /* Numeric abstract base classes */
+ ASSIGN_PTR(numbers, PyImport_ImportModule("numbers"));
+ ASSIGN_PTR(Number, PyObject_GetAttrString(numbers, "Number"));
+ /* Register Decimal with the Number abstract base class */
+ ASSIGN_PTR(obj, PyObject_CallMethod(Number, "register", "(O)",
+ (PyObject *)&PyDec_Type));
+ Py_CLEAR(obj);
+ /* Rational is a global variable used for fraction comparisons. */
+ ASSIGN_PTR(Rational, PyObject_GetAttrString(numbers, "Rational"));
+ /* Done with numbers, Number */
+ Py_CLEAR(numbers);
+ Py_CLEAR(Number);
+
+ /* DecimalTuple */
+ ASSIGN_PTR(collections, PyImport_ImportModule("collections"));
+ ASSIGN_PTR(DecimalTuple, PyObject_CallMethod(collections,
+ "namedtuple", "(ss)", "DecimalTuple",
+ "sign digits exponent"));
+ /* MutableMapping */
+ ASSIGN_PTR(MutableMapping, PyObject_GetAttrString(collections,
+ "MutableMapping"));
+ /* Create SignalDict type */
+ ASSIGN_PTR(PyDecSignalDict_Type,
+ (PyTypeObject *)PyObject_CallFunction(
+ (PyObject *)&PyType_Type, "s(OO){}",
+ "SignalDict", &PyDecSignalDictMixin_Type,
+ MutableMapping));
+
+ /* Done with collections, MutableMapping */
+ Py_CLEAR(collections);
+ Py_CLEAR(MutableMapping);
+
+
+ /* Create the module */
+ ASSIGN_PTR(m, PyModule_Create(&_decimal_module));
+
+
+ /* Add types to the module */
+ Py_INCREF(&PyDec_Type);
+ CHECK_INT(PyModule_AddObject(m, "Decimal", (PyObject *)&PyDec_Type));
+ Py_INCREF(&PyDecContext_Type);
+ CHECK_INT(PyModule_AddObject(m, "Context",
+ (PyObject *)&PyDecContext_Type));
+ Py_INCREF(DecimalTuple);
+ CHECK_INT(PyModule_AddObject(m, "DecimalTuple", DecimalTuple));
+
+
+ /* Create top level exception */
+ ASSIGN_PTR(DecimalException, PyErr_NewException(
+ "decimal.DecimalException",
+ PyExc_ArithmeticError, NULL));
+ Py_INCREF(DecimalException);
+ CHECK_INT(PyModule_AddObject(m, "DecimalException", DecimalException));
+
+ /* Create signal tuple */
+ ASSIGN_PTR(SignalTuple, PyTuple_New(SIGNAL_MAP_LEN));
+
+ /* Add exceptions that correspond to IEEE signals */
+ for (cm=signal_map, i=0; cm->name != NULL; cm++, i++) {
+ ASSIGN_PTR(cm->ex, PyErr_NewException((char *)cm->fqname,
+ DecimalException, NULL));
+
+ /* add to module */
+ Py_INCREF(cm->ex);
+ CHECK_INT(PyModule_AddObject(m, cm->name, cm->ex));
+
+ /* add to signal tuple */
+ Py_INCREF(cm->ex);
+ PyTuple_SET_ITEM(SignalTuple, i, cm->ex);
+ }
+
+ /*
+ * Unfortunately, InvalidOperation is a signal that comprises
+ * several conditions, including InvalidOperation! Naming the
+ * signal IEEEInvalidOperation would prevent the confusion.
+ */
+ cond_map[0].ex = signal_map[0].ex;
+
+ /* Add remaining exceptions, inherit from InvalidOperation */
+ for (cm = cond_map+1; cm->name != NULL; cm++) {
+ ASSIGN_PTR(cm->ex, PyErr_NewException((char *)cm->fqname,
+ signal_map[0].ex, NULL));
+ Py_INCREF(cm->ex);
+ CHECK_INT(PyModule_AddObject(m, cm->name, cm->ex));
+ }
+
+
+ /* Init default context template first */
+ ASSIGN_PTR(default_context_template,
+ PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL));
+ Py_INCREF(default_context_template);
+ CHECK_INT(PyModule_AddObject(m, "DefaultContext",
+ default_context_template));
+
+#ifdef WITHOUT_THREADS
+ /* Init module context */
+ ASSIGN_PTR(module_context,
+ PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL));
+ Py_INCREF(Py_False);
+ CHECK_INT(PyModule_AddObject(m, "HAVE_THREADS", Py_False));
+#else
+ ASSIGN_PTR(tls_context_key, PyUnicode_FromString("___DECIMAL_CTX__"));
+ Py_INCREF(Py_True);
+ CHECK_INT(PyModule_AddObject(m, "HAVE_THREADS", Py_True));
+#endif
+
+ /* Init basic context template */
+ ASSIGN_PTR(basic_context_template,
+ PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL));
+ init_basic_context(basic_context_template);
+ Py_INCREF(basic_context_template);
+ CHECK_INT(PyModule_AddObject(m, "BasicContext",
+ basic_context_template));
+
+ /* Init extended context template */
+ ASSIGN_PTR(extended_context_template,
+ PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL));
+ init_extended_context(extended_context_template);
+ Py_INCREF(extended_context_template);
+ CHECK_INT(PyModule_AddObject(m, "ExtendedContext",
+ extended_context_template));
+
+
+ /* Init mpd_ssize_t constants */
+ for (ssize_cm = ssize_constants; ssize_cm->name != NULL; ssize_cm++) {
+ ASSIGN_PTR(obj, PyLong_FromSsize_t(ssize_cm->val));
+ CHECK_INT(PyModule_AddObject(m, ssize_cm->name, obj));
+ }
+
+ /* Init int constants */
+ for (int_cm = int_constants; int_cm->name != NULL; int_cm++) {
+ CHECK_INT(PyModule_AddIntConstant(m, int_cm->name,
+ int_cm->val));
+ }
+
+ /* Add specification version number */
+ CHECK_INT(PyModule_AddStringConstant(m, "__version__", " 1.70"));
+
+
+ return m;
+
+
+error:
+ Py_XDECREF(obj); /* GCOV_NOT_REACHED */
+ Py_XDECREF(numbers); /* GCOV_NOT_REACHED */
+ Py_XDECREF(Number); /* GCOV_NOT_REACHED */
+ Py_XDECREF(Rational); /* GCOV_NOT_REACHED */
+ Py_XDECREF(collections); /* GCOV_NOT_REACHED */
+ Py_XDECREF(MutableMapping); /* GCOV_NOT_REACHED */
+ Py_XDECREF(SignalTuple); /* GCOV_NOT_REACHED */
+ Py_XDECREF(DecimalTuple); /* GCOV_NOT_REACHED */
+#ifdef WITHOUT_THREADS
+ Py_XDECREF(module_context); /* GCOV_NOT_REACHED */
+#else
+ Py_XDECREF(default_context_template); /* GCOV_NOT_REACHED */
+ Py_XDECREF(tls_context_key); /* GCOV_NOT_REACHED */
+#endif
+ Py_XDECREF(basic_context_template); /* GCOV_NOT_REACHED */
+ Py_XDECREF(extended_context_template); /* GCOV_NOT_REACHED */
+ Py_XDECREF(m); /* GCOV_NOT_REACHED */
+
+ return NULL; /* GCOV_NOT_REACHED */
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2001-2012 Python Software Foundation. All Rights Reserved.
+ * Modified and extended by Stefan Krah.
+ */
+
+
+#ifndef DOCSTRINGS_H
+#define DOCSTRINGS_H
+
+
+#include "pymacro.h"
+
+
+/******************************************************************************/
+/* Module */
+/******************************************************************************/
+
+
+PyDoc_STRVAR(doc__decimal,
+"C decimal arithmetic module");
+
+PyDoc_STRVAR(doc_getcontext,"\n\
+getcontext() - Get the current default context.\n\
+\n");
+
+PyDoc_STRVAR(doc_setcontext,"\n\
+setcontext(c) - Set a new default context.\n\
+\n");
+
+PyDoc_STRVAR(doc_localcontext,"\n\
+localcontext(c) - Return a context manager that will set the default context\n\
+to a copy of c on entry to the with-statement and restore the previous default\n\
+context when exiting the with-statement. If no context is specified, a copy of\n\
+the current default context is used.\n\
+\n");
+
+#ifdef EXTRA_FUNCTIONALITY
+PyDoc_STRVAR(doc_ieee_context,"\n\
+IEEEContext(bits) - Return a context object initialized to the proper values for\n\
+one of the IEEE interchange formats. The argument must be a multiple of 32 and\n\
+less than IEEE_CONTEXT_MAX_BITS. For the most common values, the constants\n\
+DECIMAL32, DECIMAL64 and DECIMAL128 are provided.\n\
+\n");
+#endif
+
+
+/******************************************************************************/
+/* Decimal Object and Methods */
+/******************************************************************************/
+
+PyDoc_STRVAR(doc_decimal,"\n\
+Decimal([value[, context]]): Construct a new Decimal object from value.\n\
+\n\
+value can be an integer, string, tuple, or another Decimal object.\n\
+If no value is given, return Decimal('0'). The context does not affect\n\
+the conversion and is only passed to determine if the InvalidOperation\n\
+trap is active.\n\
+\n");
+
+PyDoc_STRVAR(doc_adjusted,"\n\
+adjusted() - Return the adjusted exponent of the number.\n\
+\n\
+Defined as exp + digits - 1.\n\
+\n");
+
+PyDoc_STRVAR(doc_as_tuple,"\n\
+as_tuple() - Return a tuple representation of the number.\n\
+\n");
+
+PyDoc_STRVAR(doc_canonical,"\n\
+canonical() - Return the canonical encoding of the argument. Currently,\n\
+the encoding of a Decimal instance is always canonical, so this operation\n\
+returns its argument unchanged.\n\
+\n");
+
+PyDoc_STRVAR(doc_compare,"\n\
+compare(other[, context]) - Compare self to other. Return a decimal value:\n\
+\n\
+ a or b is a NaN ==> Decimal('NaN')\n\
+ a < b ==> Decimal('-1')\n\
+ a == b ==> Decimal('0')\n\
+ a > b ==> Decimal('1')\n\
+\n");
+
+PyDoc_STRVAR(doc_compare_signal,"\n\
+compare_signal(other[, context]) - Identical to compare, except that\n\
+all NaNs signal.\n\
+\n");
+
+PyDoc_STRVAR(doc_compare_total,"\n\
+compare_total(other) - Compare two operands using their abstract representation\n\
+rather than their numerical value. Similar to the compare() method, but the\n\
+result gives a total ordering on Decimal instances. Two Decimal instances with\n\
+the same numeric value but different representations compare unequal in this\n\
+ordering:\n\
+\n\
+ >>> Decimal('12.0').compare_total(Decimal('12'))\n\
+ Decimal('-1')\n\
+\n\
+Quiet and signaling NaNs are also included in the total ordering. The result\n\
+of this function is Decimal('0') if both operands have the same representation,\n\
+Decimal('-1') if the first operand is lower in the total order than the second,\n\
+and Decimal('1') if the first operand is higher in the total order than the\n\
+second operand. See the specification for details of the total order.\n\
+\n");
+
+PyDoc_STRVAR(doc_compare_total_mag,"\n\
+compare_total_mag(other) - Compare two operands using their abstract\n\
+representation rather than their value as in compare_total(), but\n\
+ignoring the sign of each operand. x.compare_total_mag(y) is\n\
+equivalent to x.copy_abs().compare_total(y.copy_abs()).\n\
+\n");
+
+PyDoc_STRVAR(doc_conjugate,"\n\
+conjugate() - Return self.\n\
+\n");
+
+PyDoc_STRVAR(doc_copy_abs,"\n\
+copy_abs() - Return the absolute value of the argument. This operation\n\
+is unaffected by the context and is quiet: no flags are changed and no\n\
+rounding is performed.\n\
+\n");
+
+PyDoc_STRVAR(doc_copy_negate,"\n\
+copy_negate() - Return the negation of the argument. This operation is\n\
+unaffected by the context and is quiet: no flags are changed and no\n\
+rounding is performed.\n\
+\n");
+
+PyDoc_STRVAR(doc_copy_sign,"\n\
+copy_sign(other) - Return a copy of the first operand with the sign set\n\
+to be the same as the sign of the second operand. For example:\n\
+\n\
+ >>> Decimal('2.3').copy_sign(Decimal('-1.5'))\n\
+ Decimal('-2.3')\n\
+\n\
+This operation is unaffected by the context and is quiet: no flags are\n\
+changed and no rounding is performed.\n\
+\n");
+
+PyDoc_STRVAR(doc_exp,"\n\
+exp([context]) - Return the value of the (natural) exponential function e**x\n\
+at the given number. The function always uses the ROUND_HALF_EVEN mode and\n\
+the result is correctly rounded.\n\
+\n");
+
+PyDoc_STRVAR(doc_from_float,"\n\
+from_float(f) - Class method that converts a float to a decimal number, exactly.\n\
+Since 0.1 is not exactly representable in binary floating point,\n\
+Decimal.from_float(0.1) is not the same as Decimal('0.1').\n\
+\n\
+ >>> Decimal.from_float(0.1)\n\
+ Decimal('0.1000000000000000055511151231257827021181583404541015625')\n\
+ >>> Decimal.from_float(float('nan'))\n\
+ Decimal('NaN')\n\
+ >>> Decimal.from_float(float('inf'))\n\
+ Decimal('Infinity')\n\
+ >>> Decimal.from_float(float('-inf'))\n\
+ Decimal('-Infinity')\n\
+\n\
+\n");
+
+PyDoc_STRVAR(doc_fma,"\n\
+fma(other, third[, context]) - Fused multiply-add. Return self*other+third\n\
+with no rounding of the intermediate product self*other.\n\
+\n\
+ >>> Decimal(2).fma(3, 5)\n\
+ Decimal('11')\n\
+\n\
+\n");
+
+PyDoc_STRVAR(doc_is_canonical,"\n\
+is_canonical() - Return True if the argument is canonical and False otherwise.\n\
+Currently, a Decimal instance is always canonical, so this operation always\n\
+returns True.\n\
+\n");
+
+PyDoc_STRVAR(doc_is_finite,"\n\
+is_finite() - Return True if the argument is a finite number, and False if the\n\
+argument is infinite or a NaN.\n\
+\n");
+
+PyDoc_STRVAR(doc_is_infinite,"\n\
+is_infinite() - Return True if the argument is either positive or negative\n\
+infinity and False otherwise.\n\
+\n");
+
+PyDoc_STRVAR(doc_is_nan,"\n\
+is_nan() - Return True if the argument is a (quiet or signaling) NaN and\n\
+False otherwise.\n\
+\n");
+
+PyDoc_STRVAR(doc_is_normal,"\n\
+is_normal([context]) - Return True if the argument is a normal finite non-zero\n\
+number with an adjusted exponent greater than or equal to Emin. Return False\n\
+if the argument is zero, subnormal, infinite or a NaN.\n\
+\n");
+
+PyDoc_STRVAR(doc_is_qnan,"\n\
+is_qnan() - Return True if the argument is a quiet NaN, and False otherwise.\n\
+\n");
+
+PyDoc_STRVAR(doc_is_signed,"\n\
+is_signed() - Return True if the argument has a negative sign and\n\
+False otherwise. Note that both zeros and NaNs can carry signs.\n\
+\n");
+
+PyDoc_STRVAR(doc_is_snan,"\n\
+is_snan() - Return True if the argument is a signaling NaN and False otherwise.\n\
+\n");
+
+PyDoc_STRVAR(doc_is_subnormal,"\n\
+is_subnormal([context]) - Return True if the argument is subnormal, and False\n\
+otherwise. A number is subnormal if it is non-zero, finite, and has an\n\
+adjusted exponent less than Emin.\n\
+\n");
+
+PyDoc_STRVAR(doc_is_zero,"\n\
+is_zero() - Return True if the argument is a (positive or negative) zero and\n\
+False otherwise.\n\
+\n");
+
+PyDoc_STRVAR(doc_ln,"\n\
+ln([context]) - Return the natural (base e) logarithm of the operand.\n\
+The function always uses the ROUND_HALF_EVEN mode and the result is\n\
+correctly rounded.\n\
+\n");
+
+PyDoc_STRVAR(doc_log10,"\n\
+log10([context]) - Return the base ten logarithm of the operand.\n\
+The function always uses the ROUND_HALF_EVEN mode and the result is\n\
+correctly rounded.\n\
+\n");
+
+PyDoc_STRVAR(doc_logb,"\n\
+logb([context]) - For a non-zero number, return the adjusted exponent\n\
+of the operand as a Decimal instance. If the operand is a zero, then\n\
+Decimal('-Infinity') is returned and the DivisionByZero condition is\n\
+raised. If the operand is an infinity then Decimal('Infinity') is returned.\n\
+\n");
+
+PyDoc_STRVAR(doc_logical_and,"\n\
+logical_and(other[, context]) - Return the digit-wise and of the two\n\
+(logical) operands.\n\
+\n");
+
+PyDoc_STRVAR(doc_logical_invert,"\n\
+logical_invert([context]) - Return the digit-wise inversion of the\n\
+(logical) operand.\n\
+\n");
+
+PyDoc_STRVAR(doc_logical_or,"\n\
+logical_or(other[, context]) - Return the digit-wise or of the two\n\
+(logical) operands.\n\
+\n");
+
+PyDoc_STRVAR(doc_logical_xor,"\n\
+logical_xor(other[, context]) - Return the digit-wise exclusive or of the\n\
+two (logical) operands.\n\
+\n");
+
+PyDoc_STRVAR(doc_max,"\n\
+max(other[, context]) - Maximum of self and other. If one operand is a quiet\n\
+NaN and the other is numeric, the numeric operand is returned.\n\
+\n");
+
+PyDoc_STRVAR(doc_max_mag,"\n\
+max_mag(other[, context]) - Similar to the max() method, but the comparison is\n\
+done using the absolute values of the operands.\n\
+\n");
+
+PyDoc_STRVAR(doc_min,"\n\
+min(other[, context]) - Minimum of self and other. If one operand is a quiet\n\
+NaN and the other is numeric, the numeric operand is returned.\n\
+\n");
+
+PyDoc_STRVAR(doc_min_mag,"\n\
+min_mag(other[, context]) - Similar to the min() method, but the comparison is\n\
+done using the absolute values of the operands.\n\
+\n");
+
+PyDoc_STRVAR(doc_next_minus,"\n\
+next_minus([context]) - Return the largest number representable in the given\n\
+context (or in the current default context if no context is given) that is\n\
+smaller than the given operand.\n\
+\n");
+
+PyDoc_STRVAR(doc_next_plus,"\n\
+next_plus([context]) - Return the smallest number representable in the given\n\
+context (or in the current default context if no context is given) that is\n\
+larger than the given operand.\n\
+\n");
+
+PyDoc_STRVAR(doc_next_toward,"\n\
+next_toward(other[, context]) - If the two operands are unequal, return the\n\
+number closest to the first operand in the direction of the second operand.\n\
+If both operands are numerically equal, return a copy of the first operand\n\
+with the sign set to be the same as the sign of the second operand.\n\
+\n");
+
+PyDoc_STRVAR(doc_normalize,"\n\
+normalize([context]) - Normalize the number by stripping the rightmost trailing\n\
+zeros and converting any result equal to Decimal('0') to Decimal('0e0'). Used\n\
+for producing canonical values for members of an equivalence class. For example,\n\
+Decimal('32.100') and Decimal('0.321000e+2') both normalize to the equivalent\n\
+value Decimal('32.1').\n\
+\n");
+
+PyDoc_STRVAR(doc_number_class,"\n\
+number_class([context]) - Return a string describing the class of the operand.\n\
+The returned value is one of the following ten strings:\n\
+\n\
+ * '-Infinity', indicating that the operand is negative infinity.\n\
+ * '-Normal', indicating that the operand is a negative normal number.\n\
+ * '-Subnormal', indicating that the operand is negative and subnormal.\n\
+ * '-Zero', indicating that the operand is a negative zero.\n\
+ * '+Zero', indicating that the operand is a positive zero.\n\
+ * '+Subnormal', indicating that the operand is positive and subnormal.\n\
+ * '+Normal', indicating that the operand is a positive normal number.\n\
+ * '+Infinity', indicating that the operand is positive infinity.\n\
+ * 'NaN', indicating that the operand is a quiet NaN (Not a Number).\n\
+ * 'sNaN', indicating that the operand is a signaling NaN.\n\
+\n\
+\n");
+
+PyDoc_STRVAR(doc_quantize,"\n\
+quantize(exp[, rounding[, context]]) - Return a value equal to the first\n\
+operand after rounding and having the exponent of the second operand.\n\
+\n\
+ >>> Decimal('1.41421356').quantize(Decimal('1.000'))\n\
+ Decimal('1.414')\n\
+\n\
+Unlike other operations, if the length of the coefficient after the quantize\n\
+operation would be greater than precision, then an InvalidOperation is signaled.\n\
+This guarantees that, unless there is an error condition, the quantized exponent\n\
+is always equal to that of the right-hand operand.\n\
+\n\
+Also unlike other operations, quantize never signals Underflow, even if the\n\
+result is subnormal and inexact.\n\
+\n\
+If the exponent of the second operand is larger than that of the first, then\n\
+rounding may be necessary. In this case, the rounding mode is determined by the\n\
+rounding argument if given, else by the given context argument; if neither\n\
+argument is given, the rounding mode of the current thread's context is used.\n\
+\n");
+
+PyDoc_STRVAR(doc_radix,"\n\
+radix() - Return Decimal(10), the radix (base) in which the Decimal class does\n\
+all its arithmetic. Included for compatibility with the specification.\n\
+\n");
+
+PyDoc_STRVAR(doc_remainder_near,"\n\
+remainder_near(other[, context]) - Compute the modulo as either a positive\n\
+or negative value depending on which is closest to zero. For instance,\n\
+Decimal(10).remainder_near(6) returns Decimal('-2'), which is closer to zero\n\
+than Decimal('4').\n\
+\n\
+If both are equally close, the one chosen will have the same sign as self.\n\
+\n");
+
+PyDoc_STRVAR(doc_rotate,"\n\
+rotate(other[, context]) - Return the result of rotating the digits of the\n\
+first operand by an amount specified by the second operand. The second operand\n\
+must be an integer in the range -precision through precision. The absolute\n\
+value of the second operand gives the number of places to rotate. If the second\n\
+operand is positive then rotation is to the left; otherwise rotation is to the\n\
+right. The coefficient of the first operand is padded on the left with zeros to\n\
+length precision if necessary. The sign and exponent of the first operand are\n\
+unchanged.\n\
+\n");
+
+PyDoc_STRVAR(doc_same_quantum,"\n\
+same_quantum(other[, context]) - Test whether self and other have the\n\
+same exponent or whether both are NaN.\n\
+\n");
+
+PyDoc_STRVAR(doc_scaleb,"\n\
+scaleb(other[, context]) - Return the first operand with the exponent adjusted\n\
+the second. Equivalently, return the first operand multiplied by 10**other.\n\
+The second operand must be an integer.\n\
+\n");
+
+PyDoc_STRVAR(doc_shift,"\n\
+shift(other[, context]) - Return the result of shifting the digits of\n\
+the first operand by an amount specified by the second operand. The second\n\
+operand must be an integer in the range -precision through precision. The\n\
+absolute value of the second operand gives the number of places to shift.\n\
+If the second operand is positive, then the shift is to the left; otherwise\n\
+the shift is to the right. Digits shifted into the coefficient are zeros.\n\
+The sign and exponent of the first operand are unchanged.\n\
+\n");
+
+PyDoc_STRVAR(doc_sqrt,"\n\
+sqrt([context]) - Return the square root of the argument to full precision.\n\
+The result is correctly rounded using the ROUND_HALF_EVEN rounding mode.\n\
+\n");
+
+PyDoc_STRVAR(doc_to_eng_string,"\n\
+to_eng_string([context]) - Convert to an engineering-type string.\n\
+Engineering notation has an exponent which is a multiple of 3, so\n\
+there are up to 3 digits left of the decimal place. For example,\n\
+Decimal('123E+1') is converted to Decimal('1.23E+3')\n\
+\n");
+
+PyDoc_STRVAR(doc_to_integral,"\n\
+to_integral([rounding[, context]]) - Identical to the to_integral_value()\n\
+method. The to_integral name has been kept for compatibility with older\n\
+versions.\n\
+\n");
+
+PyDoc_STRVAR(doc_to_integral_exact,"\n\
+to_integral_exact([rounding[, context]]) - Round to the nearest integer,\n\
+signaling Inexact or Rounded as appropriate if rounding occurs. The rounding\n\
+mode is determined by the rounding parameter if given, else by the given\n\
+context. If neither parameter is given, then the rounding mode of the current\n\
+default context is used.\n\
+\n");
+
+PyDoc_STRVAR(doc_to_integral_value,"\n\
+to_integral_value([rounding[, context]]) - Round to the nearest integer without\n\
+signaling Inexact or Rounded. The rounding mode is determined by the rounding\n\
+parameter if given, else by the given context. If neither parameter is given,\n\
+then the rounding mode of the current default context is used.\n\
+\n");
+
+
+/******************************************************************************/
+/* Context Object and Methods */
+/******************************************************************************/
+
+PyDoc_STRVAR(doc_context,"\n\
+The context affects almost all operations and controls rounding,\n\
+Over/Underflow, raising of exceptions and much more. A new context\n\
+can be constructed as follows:\n\
+\n\
+ >>> c = Context(prec=28, Emin=-425000000, Emax=425000000,\n\
+ ... rounding=ROUND_HALF_EVEN, capitals=1, clamp=1,\n\
+ ... traps=[InvalidOperation, DivisionByZero, Overflow],\n\
+ ... flags=[])\n\
+ >>>\n\
+\n\
+\n");
+
+#ifdef EXTRA_FUNCTIONALITY
+PyDoc_STRVAR(doc_ctx_apply,"\n\
+apply(x) - Apply self to Decimal x.\n\
+\n");
+#endif
+
+PyDoc_STRVAR(doc_ctx_clear_flags,"\n\
+clear_flags() - Reset all flags to False.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_clear_traps,"\n\
+clear_traps() - Set all traps to False.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_copy,"\n\
+copy() - Return a duplicate of the context with all flags cleared.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_copy_decimal,"\n\
+copy_decimal(x) - Return a copy of Decimal x.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_create_decimal,"\n\
+create_decimal(x) - Create a new Decimal instance from x, using self as the\n\
+context. Unlike the Decimal constructor, this function observes the context\n\
+limits.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_create_decimal_from_float,"\n\
+create_decimal_from_float(f) - Create a new Decimal instance from float f.\n\
+Unlike the Decimal.from_float() class method, this function observes the\n\
+context limits.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_Etiny,"\n\
+Etiny() - Return a value equal to Emin - prec + 1, which is the minimum\n\
+exponent value for subnormal results. When underflow occurs, the exponent\n\
+is set to Etiny.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_Etop,"\n\
+Etop() - Return a value equal to Emax - prec + 1. This is the maximum exponent\n\
+if the _clamp field of the context is set to 1 (IEEE clamp mode). Etop() must\n\
+not be negative.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_abs,"\n\
+abs(x) - Return the absolute value of x.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_add,"\n\
+add(x, y) - Return the sum of x and y.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_canonical,"\n\
+canonical(x) - Return a new instance of x.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_compare,"\n\
+compare(x, y) - Compare x and y numerically.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_compare_signal,"\n\
+compare_signal(x, y) - Compare x and y numerically. All NaNs signal.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_compare_total,"\n\
+compare_total(x, y) - Compare x and y using their abstract representation.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_compare_total_mag,"\n\
+compare_total_mag(x, y) - Compare x and y using their abstract representation,\n\
+ignoring sign.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_copy_abs,"\n\
+copy_abs(x) - Return a copy of x with the sign set to 0.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_copy_negate,"\n\
+copy_negate(x) - Return a copy of x with the sign inverted.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_copy_sign,"\n\
+copy_sign(x, y) - Copy the sign from y to x.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_divide,"\n\
+divide(x, y) - Return x divided by y.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_divide_int,"\n\
+divide_int(x, y) - Return x divided by y, truncated to an integer.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_divmod,"\n\
+divmod(x, y) - Return quotient and remainder of the division x / y.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_exp,"\n\
+exp(x) - Return e ** x.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_fma,"\n\
+fma(x, y, z) - Return x multiplied by y, plus z.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_is_canonical,"\n\
+is_canonical(x) - Return True if x is canonical, False otherwise.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_is_finite,"\n\
+is_finite(x) - Return True if x is finite, False otherwise.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_is_infinite,"\n\
+is_infinite(x) - Return True if x is infinite, False otherwise.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_is_nan,"\n\
+is_nan(x) - Return True if x is a qNaN or sNaN, False otherwise.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_is_normal,"\n\
+is_normal(x) - Return True if x is a normal number, False otherwise.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_is_qnan,"\n\
+is_qnan(x) - Return True if x is a quiet NaN, False otherwise.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_is_signed,"\n\
+is_signed(x) - Return True if x is negative, False otherwise.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_is_snan,"\n\
+is_snan() - Return True if x is a signaling NaN, False otherwise.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_is_subnormal,"\n\
+is_subnormal(x) - Return True if x is subnormal, False otherwise.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_is_zero,"\n\
+is_zero(x) - Return True if x is a zero, False otherwise.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_ln,"\n\
+ln(x) - Return the natural (base e) logarithm of x.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_log10,"\n\
+log10(x) - Return the base 10 logarithm of x.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_logb,"\n\
+logb(x) - Return the exponent of the magnitude of the operand's MSD.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_logical_and,"\n\
+logical_and(x, y) - Digit-wise and of x and y.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_logical_invert,"\n\
+logical_invert(x) - Invert all digits of x.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_logical_or,"\n\
+logical_or(x, y) - Digit-wise or of x and y.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_logical_xor,"\n\
+logical_xor(x, y) - Digit-wise xor of x and y.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_max,"\n\
+max(x, y) - Compare the values numerically and return the maximum.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_max_mag,"\n\
+max_mag(x, y) - Compare the values numerically with their sign ignored.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_min,"\n\
+min(x, y) - Compare the values numerically and return the minimum.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_min_mag,"\n\
+min_mag(x, y) - Compare the values numerically with their sign ignored.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_minus,"\n\
+minus(x) - Minus corresponds to the unary prefix minus operator in Python,\n\
+but applies the context to the result.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_multiply,"\n\
+multiply(x, y) - Return the product of x and y.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_next_minus,"\n\
+next_minus(x) - Return the largest representable number smaller than x.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_next_plus,"\n\
+next_plus(x) - Return the smallest representable number larger than x.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_next_toward,"\n\
+next_toward(x) - Return the number closest to x, in the direction towards y.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_normalize,"\n\
+normalize(x) - Reduce x to its simplest form. Alias for reduce(x).\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_number_class,"\n\
+number_class(x) - Return an indication of the class of x.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_plus,"\n\
+plus(x) - Plus corresponds to the unary prefix plus operator in Python,\n\
+but applies the context to the result.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_power,"\n\
+power(x, y) - Compute x**y. If x is negative, then y must be integral.\n\
+The result will be inexact unless y is integral and the result is finite\n\
+and can be expressed exactly in 'precision' digits. In the Python version\n\
+the result is always correctly rounded, in the C version the result is\n\
+almost always correctly rounded.\n\
+\n\
+power(x, y, m) - Compute (x**y) % m. The following restrictions hold:\n\
+\n\
+ * all three arguments must be integral\n\
+ * y must be nonnegative\n\
+ * at least one of x or y must be nonzero\n\
+ * m must be nonzero and less than 10**prec in absolute value\n\
+\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_quantize,"\n\
+quantize(x, y) - Return a value equal to x (rounded), having the exponent of y.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_radix,"\n\
+radix() - Return 10.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_remainder,"\n\
+remainder(x, y) - Return the remainder from integer division. The sign of\n\
+the result, if non-zero, is the same as that of the original dividend.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_remainder_near,"\n\
+remainder_near(x, y) - Return x - y * n, where n is the integer nearest the\n\
+exact value of x / y (if the result is 0 then its sign will be the sign of x).\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_rotate,"\n\
+rotate(x, y) - Return a copy of x, rotated by y places.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_same_quantum,"\n\
+same_quantum(x, y) - Return True if the two operands have the same exponent.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_scaleb,"\n\
+scaleb(x, y) - Return the first operand after adding the second value\n\
+to its exp.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_shift,"\n\
+shift(x, y) - Return a copy of x, shifted by y places.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_sqrt,"\n\
+sqrt(x) - Square root of a non-negative number to context precision.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_subtract,"\n\
+subtract(x, y) - Return the difference between x and y.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_to_eng_string,"\n\
+to_eng_string(x) - Convert a number to a string, using engineering notation.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_to_integral,"\n\
+to_integral(x) - Identical to to_integral_value(x).\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_to_integral_exact,"\n\
+to_integral_exact(x) - Round to an integer. Signal if the result is\n\
+rounded or inexact.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_to_integral_value,"\n\
+to_integral_value(x) - Round to an integer.\n\
+\n");
+
+PyDoc_STRVAR(doc_ctx_to_sci_string,"\n\
+to_sci_string(x) - Convert a number to a string using scientific notation.\n\
+\n");
+
+
+#endif /* DOCSTRINGS_H */
+
+
+
--- /dev/null
+
+
+libmpdec
+========
+
+libmpdec is a fast C/C++ library for correctly-rounded arbitrary precision
+decimal floating point arithmetic. It is a complete implementation of
+Mike Cowlishaw/IBM's General Decimal Arithmetic Specification.
+
+
+Files required for the Python _decimal module
+=============================================
+
+ Core files for small and medium precision arithmetic
+ ----------------------------------------------------
+
+ basearith.{c,h} -> Core arithmetic in base 10**9 or 10**19.
+ bits.h -> Portable detection of least/most significant one-bit.
+ constants.{c,h} -> Constants that are used in multiple files.
+ context.c -> Context functions.
+ io.{c,h} -> Conversions between mpd_t and ASCII strings,
+ mpd_t formatting (allows UTF-8 fill character).
+ memory.{c,h} -> Allocation handlers with overflow detection
+ and functions for switching between static
+ and dynamic mpd_t.
+ mpdecimal.{c,h} -> All (quiet) functions of the specification.
+ typearith.h -> Fast primitives for double word multiplication,
+ division etc.
+
+ Visual Studio only:
+ ~~~~~~~~~~~~~~~~~~~
+ vccompat.h -> snprintf <==> sprintf_s and similar things.
+ vcstdint.h -> stdint.h (included in VS 2010 but not in VS 2008).
+ vcdiv64.asm -> Double word division used in typearith.h. VS 2008 does
+ not allow inline asm for x64. Also, it does not provide
+ an intrinsic for double word division.
+
+ Files for bignum arithmetic:
+ ----------------------------
+
+ The following files implement the Fast Number Theoretic Transform
+ used for multiplying coefficients with more than 1024 words (see
+ mpdecimal.c: _mpd_fntmul()).
+
+ umodarith.h -> Fast low level routines for unsigned modular arithmetic.
+ numbertheory.{c,h} -> Routines for setting up the Number Theoretic Transform.
+ difradix2.{c,h} -> Decimation in frequency transform, used as the
+ "base case" by the following three files:
+
+ fnt.{c,h} -> Transform arrays up to 4096 words.
+ sixstep.{c,h} -> Transform larger arrays of length 2**n.
+ fourstep.{c,h} -> Transform larger arrays of length 3 * 2**n.
+
+ convolute.{c,h} -> Fast convolution using one of the three transform
+ functions.
+ transpose.{c,h} -> Transpositions needed for the sixstep algorithm.
+ crt.{c,h} -> Chinese Remainder Theorem: use information from three
+ transforms modulo three different primes to get the
+ final result.
+
+
+Pointers to literature, proofs and more
+=======================================
+
+ literature/
+ -----------
+
+ REFERENCES.txt -> List of relevant papers.
+ bignum.txt -> Explanation of the Fast Number Theoretic Transform (FNT).
+ fnt.py -> Verify constants used in the FNT; Python demo for the
+ O(N**2) discrete transform.
+
+ matrix-transform.txt -> Proof for the Matrix Fourier Transform used in
+ fourstep.c.
+ six-step.txt -> Show that the algorithm used in sixstep.c is
+ a variant of the Matrix Fourier Transform.
+ mulmod-64.txt -> Proof for the mulmod64 algorithm from
+ umodarith.h.
+ mulmod-ppro.txt -> Proof for the x87 FPU modular multiplication
+ from umodarith.h.
+ umodarith.lisp -> ACL2 proofs for many functions from umodarith.h.
+
+
+Library Author
+==============
+
+ Stefan Krah <skrah@bytereef.org>
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include "mpdecimal.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "constants.h"
+#include "memory.h"
+#include "typearith.h"
+#include "basearith.h"
+
+
+/*********************************************************************/
+/* Calculations in base MPD_RADIX */
+/*********************************************************************/
+
+
+/*
+ * Knuth, TAOCP, Volume 2, 4.3.1:
+ * w := sum of u (len m) and v (len n)
+ * n > 0 and m >= n
+ * The calling function has to handle a possible final carry.
+ */
+mpd_uint_t
+_mpd_baseadd(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v,
+ mpd_size_t m, mpd_size_t n)
+{
+ mpd_uint_t s;
+ mpd_uint_t carry = 0;
+ mpd_size_t i;
+
+ assert(n > 0 && m >= n);
+
+ /* add n members of u and v */
+ for (i = 0; i < n; i++) {
+ s = u[i] + (v[i] + carry);
+ carry = (s < u[i]) | (s >= MPD_RADIX);
+ w[i] = carry ? s-MPD_RADIX : s;
+ }
+ /* if there is a carry, propagate it */
+ for (; carry && i < m; i++) {
+ s = u[i] + carry;
+ carry = (s == MPD_RADIX);
+ w[i] = carry ? 0 : s;
+ }
+ /* copy the rest of u */
+ for (; i < m; i++) {
+ w[i] = u[i];
+ }
+
+ return carry;
+}
+
+/*
+ * Add the contents of u to w. Carries are propagated further. The caller
+ * has to make sure that w is big enough.
+ */
+void
+_mpd_baseaddto(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n)
+{
+ mpd_uint_t s;
+ mpd_uint_t carry = 0;
+ mpd_size_t i;
+
+ if (n == 0) return;
+
+ /* add n members of u to w */
+ for (i = 0; i < n; i++) {
+ s = w[i] + (u[i] + carry);
+ carry = (s < w[i]) | (s >= MPD_RADIX);
+ w[i] = carry ? s-MPD_RADIX : s;
+ }
+ /* if there is a carry, propagate it */
+ for (; carry; i++) {
+ s = w[i] + carry;
+ carry = (s == MPD_RADIX);
+ w[i] = carry ? 0 : s;
+ }
+}
+
+/*
+ * Add v to w (len m). The calling function has to handle a possible
+ * final carry. Assumption: m > 0.
+ */
+mpd_uint_t
+_mpd_shortadd(mpd_uint_t *w, mpd_size_t m, mpd_uint_t v)
+{
+ mpd_uint_t s;
+ mpd_uint_t carry;
+ mpd_size_t i;
+
+ assert(m > 0);
+
+ /* add v to w */
+ s = w[0] + v;
+ carry = (s < v) | (s >= MPD_RADIX);
+ w[0] = carry ? s-MPD_RADIX : s;
+
+ /* if there is a carry, propagate it */
+ for (i = 1; carry && i < m; i++) {
+ s = w[i] + carry;
+ carry = (s == MPD_RADIX);
+ w[i] = carry ? 0 : s;
+ }
+
+ return carry;
+}
+
+/* Increment u. The calling function has to handle a possible carry. */
+mpd_uint_t
+_mpd_baseincr(mpd_uint_t *u, mpd_size_t n)
+{
+ mpd_uint_t s;
+ mpd_uint_t carry = 1;
+ mpd_size_t i;
+
+ assert(n > 0);
+
+ /* if there is a carry, propagate it */
+ for (i = 0; carry && i < n; i++) {
+ s = u[i] + carry;
+ carry = (s == MPD_RADIX);
+ u[i] = carry ? 0 : s;
+ }
+
+ return carry;
+}
+
+/*
+ * Knuth, TAOCP, Volume 2, 4.3.1:
+ * w := difference of u (len m) and v (len n).
+ * number in u >= number in v;
+ */
+void
+_mpd_basesub(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v,
+ mpd_size_t m, mpd_size_t n)
+{
+ mpd_uint_t d;
+ mpd_uint_t borrow = 0;
+ mpd_size_t i;
+
+ assert(m > 0 && n > 0);
+
+ /* subtract n members of v from u */
+ for (i = 0; i < n; i++) {
+ d = u[i] - (v[i] + borrow);
+ borrow = (u[i] < d);
+ w[i] = borrow ? d + MPD_RADIX : d;
+ }
+ /* if there is a borrow, propagate it */
+ for (; borrow && i < m; i++) {
+ d = u[i] - borrow;
+ borrow = (u[i] == 0);
+ w[i] = borrow ? MPD_RADIX-1 : d;
+ }
+ /* copy the rest of u */
+ for (; i < m; i++) {
+ w[i] = u[i];
+ }
+}
+
+/*
+ * Subtract the contents of u from w. w is larger than u. Borrows are
+ * propagated further, but eventually w can absorb the final borrow.
+ */
+void
+_mpd_basesubfrom(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n)
+{
+ mpd_uint_t d;
+ mpd_uint_t borrow = 0;
+ mpd_size_t i;
+
+ if (n == 0) return;
+
+ /* subtract n members of u from w */
+ for (i = 0; i < n; i++) {
+ d = w[i] - (u[i] + borrow);
+ borrow = (w[i] < d);
+ w[i] = borrow ? d + MPD_RADIX : d;
+ }
+ /* if there is a borrow, propagate it */
+ for (; borrow; i++) {
+ d = w[i] - borrow;
+ borrow = (w[i] == 0);
+ w[i] = borrow ? MPD_RADIX-1 : d;
+ }
+}
+
+/* w := product of u (len n) and v (single word) */
+void
+_mpd_shortmul(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, mpd_uint_t v)
+{
+ mpd_uint_t hi, lo;
+ mpd_uint_t carry = 0;
+ mpd_size_t i;
+
+ assert(n > 0);
+
+ for (i=0; i < n; i++) {
+
+ _mpd_mul_words(&hi, &lo, u[i], v);
+ lo = carry + lo;
+ if (lo < carry) hi++;
+
+ _mpd_div_words_r(&carry, &w[i], hi, lo);
+ }
+ w[i] = carry;
+}
+
+/*
+ * Knuth, TAOCP, Volume 2, 4.3.1:
+ * w := product of u (len m) and v (len n)
+ * w must be initialized to zero
+ */
+void
+_mpd_basemul(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v,
+ mpd_size_t m, mpd_size_t n)
+{
+ mpd_uint_t hi, lo;
+ mpd_uint_t carry;
+ mpd_size_t i, j;
+
+ assert(m > 0 && n > 0);
+
+ for (j=0; j < n; j++) {
+ carry = 0;
+ for (i=0; i < m; i++) {
+
+ _mpd_mul_words(&hi, &lo, u[i], v[j]);
+ lo = w[i+j] + lo;
+ if (lo < w[i+j]) hi++;
+ lo = carry + lo;
+ if (lo < carry) hi++;
+
+ _mpd_div_words_r(&carry, &w[i+j], hi, lo);
+ }
+ w[j+m] = carry;
+ }
+}
+
+/*
+ * Knuth, TAOCP Volume 2, 4.3.1, exercise 16:
+ * w := quotient of u (len n) divided by a single word v
+ */
+mpd_uint_t
+_mpd_shortdiv(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, mpd_uint_t v)
+{
+ mpd_uint_t hi, lo;
+ mpd_uint_t rem = 0;
+ mpd_size_t i;
+
+ assert(n > 0);
+
+ for (i=n-1; i != MPD_SIZE_MAX; i--) {
+
+ _mpd_mul_words(&hi, &lo, rem, MPD_RADIX);
+ lo = u[i] + lo;
+ if (lo < u[i]) hi++;
+
+ _mpd_div_words(&w[i], &rem, hi, lo, v);
+ }
+
+ return rem;
+}
+
+/*
+ * Knuth, TAOCP Volume 2, 4.3.1:
+ * q, r := quotient and remainder of uconst (len nplusm)
+ * divided by vconst (len n)
+ * nplusm >= n
+ *
+ * If r is not NULL, r will contain the remainder. If r is NULL, the
+ * return value indicates if there is a remainder: 1 for true, 0 for
+ * false. A return value of -1 indicates an error.
+ */
+int
+_mpd_basedivmod(mpd_uint_t *q, mpd_uint_t *r,
+ const mpd_uint_t *uconst, const mpd_uint_t *vconst,
+ mpd_size_t nplusm, mpd_size_t n)
+{
+ mpd_uint_t ustatic[MPD_MINALLOC_MAX];
+ mpd_uint_t vstatic[MPD_MINALLOC_MAX];
+ mpd_uint_t *u = ustatic;
+ mpd_uint_t *v = vstatic;
+ mpd_uint_t d, qhat, rhat, w2[2];
+ mpd_uint_t hi, lo, x;
+ mpd_uint_t carry;
+ mpd_size_t i, j, m;
+ int retval = 0;
+
+ assert(n > 1 && nplusm >= n);
+ m = sub_size_t(nplusm, n);
+
+ /* D1: normalize */
+ d = MPD_RADIX / (vconst[n-1] + 1);
+
+ if (nplusm >= MPD_MINALLOC_MAX) {
+ if ((u = mpd_alloc(nplusm+1, sizeof *u)) == NULL) {
+ return -1;
+ }
+ }
+ if (n >= MPD_MINALLOC_MAX) {
+ if ((v = mpd_alloc(n+1, sizeof *v)) == NULL) {
+ mpd_free(u);
+ return -1;
+ }
+ }
+
+ _mpd_shortmul(u, uconst, nplusm, d);
+ _mpd_shortmul(v, vconst, n, d);
+
+ /* D2: loop */
+ for (j=m; j != MPD_SIZE_MAX; j--) {
+
+ /* D3: calculate qhat and rhat */
+ rhat = _mpd_shortdiv(w2, u+j+n-1, 2, v[n-1]);
+ qhat = w2[1] * MPD_RADIX + w2[0];
+
+ while (1) {
+ if (qhat < MPD_RADIX) {
+ _mpd_singlemul(w2, qhat, v[n-2]);
+ if (w2[1] <= rhat) {
+ if (w2[1] != rhat || w2[0] <= u[j+n-2]) {
+ break;
+ }
+ }
+ }
+ qhat -= 1;
+ rhat += v[n-1];
+ if (rhat < v[n-1] || rhat >= MPD_RADIX) {
+ break;
+ }
+ }
+ /* D4: multiply and subtract */
+ carry = 0;
+ for (i=0; i <= n; i++) {
+
+ _mpd_mul_words(&hi, &lo, qhat, v[i]);
+
+ lo = carry + lo;
+ if (lo < carry) hi++;
+
+ _mpd_div_words_r(&hi, &lo, hi, lo);
+
+ x = u[i+j] - lo;
+ carry = (u[i+j] < x);
+ u[i+j] = carry ? x+MPD_RADIX : x;
+ carry += hi;
+ }
+ q[j] = qhat;
+ /* D5: test remainder */
+ if (carry) {
+ q[j] -= 1;
+ /* D6: add back */
+ (void)_mpd_baseadd(u+j, u+j, v, n+1, n);
+ }
+ }
+
+ /* D8: unnormalize */
+ if (r != NULL) {
+ _mpd_shortdiv(r, u, n, d);
+ /* we are not interested in the return value here */
+ retval = 0;
+ }
+ else {
+ retval = !_mpd_isallzero(u, n);
+ }
+
+
+if (u != ustatic) mpd_free(u);
+if (v != vstatic) mpd_free(v);
+return retval;
+}
+
+/*
+ * Left shift of src by 'shift' digits; src may equal dest.
+ *
+ * dest := area of n mpd_uint_t with space for srcdigits+shift digits.
+ * src := coefficient with length m.
+ *
+ * The case splits in the function are non-obvious. The following
+ * equations might help:
+ *
+ * Let msdigits denote the number of digits in the most significant
+ * word of src. Then 1 <= msdigits <= rdigits.
+ *
+ * 1) shift = q * rdigits + r
+ * 2) srcdigits = qsrc * rdigits + msdigits
+ * 3) destdigits = shift + srcdigits
+ * = q * rdigits + r + qsrc * rdigits + msdigits
+ * = q * rdigits + (qsrc * rdigits + (r + msdigits))
+ *
+ * The result has q zero words, followed by the coefficient that
+ * is left-shifted by r. The case r == 0 is trivial. For r > 0, it
+ * is important to keep in mind that we always read m source words,
+ * but write m+1 destination words if r + msdigits > rdigits, m words
+ * otherwise.
+ */
+void
+_mpd_baseshiftl(mpd_uint_t *dest, mpd_uint_t *src, mpd_size_t n, mpd_size_t m,
+ mpd_size_t shift)
+{
+#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__)
+ /* spurious uninitialized warnings */
+ mpd_uint_t l=l, lprev=lprev, h=h;
+#else
+ mpd_uint_t l, lprev, h;
+#endif
+ mpd_uint_t q, r;
+ mpd_uint_t ph;
+
+ assert(m > 0 && n >= m);
+
+ _mpd_div_word(&q, &r, (mpd_uint_t)shift, MPD_RDIGITS);
+
+ if (r != 0) {
+
+ ph = mpd_pow10[r];
+
+ --m; --n;
+ _mpd_divmod_pow10(&h, &lprev, src[m--], MPD_RDIGITS-r);
+ if (h != 0) { /* r + msdigits > rdigits <==> h != 0 */
+ dest[n--] = h;
+ }
+ /* write m-1 shifted words */
+ for (; m != MPD_SIZE_MAX; m--,n--) {
+ _mpd_divmod_pow10(&h, &l, src[m], MPD_RDIGITS-r);
+ dest[n] = ph * lprev + h;
+ lprev = l;
+ }
+ /* write least significant word */
+ dest[q] = ph * lprev;
+ }
+ else {
+ while (--m != MPD_SIZE_MAX) {
+ dest[m+q] = src[m];
+ }
+ }
+
+ mpd_uint_zero(dest, q);
+}
+
+/*
+ * Right shift of src by 'shift' digits; src may equal dest.
+ * Assumption: srcdigits-shift > 0.
+ *
+ * dest := area with space for srcdigits-shift digits.
+ * src := coefficient with length 'slen'.
+ *
+ * The case splits in the function rely on the following equations:
+ *
+ * Let msdigits denote the number of digits in the most significant
+ * word of src. Then 1 <= msdigits <= rdigits.
+ *
+ * 1) shift = q * rdigits + r
+ * 2) srcdigits = qsrc * rdigits + msdigits
+ * 3) destdigits = srcdigits - shift
+ * = qsrc * rdigits + msdigits - (q * rdigits + r)
+ * = (qsrc - q) * rdigits + msdigits - r
+ *
+ * Since destdigits > 0 and 1 <= msdigits <= rdigits:
+ *
+ * 4) qsrc >= q
+ * 5) qsrc == q ==> msdigits > r
+ *
+ * The result has slen-q words if msdigits > r, slen-q-1 words otherwise.
+ */
+mpd_uint_t
+_mpd_baseshiftr(mpd_uint_t *dest, mpd_uint_t *src, mpd_size_t slen,
+ mpd_size_t shift)
+{
+#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__)
+ /* spurious uninitialized warnings */
+ mpd_uint_t l=l, h=h, hprev=hprev; /* low, high, previous high */
+#else
+ mpd_uint_t l, h, hprev; /* low, high, previous high */
+#endif
+ mpd_uint_t rnd, rest; /* rounding digit, rest */
+ mpd_uint_t q, r;
+ mpd_size_t i, j;
+ mpd_uint_t ph;
+
+ assert(slen > 0);
+
+ _mpd_div_word(&q, &r, (mpd_uint_t)shift, MPD_RDIGITS);
+
+ rnd = rest = 0;
+ if (r != 0) {
+
+ ph = mpd_pow10[MPD_RDIGITS-r];
+
+ _mpd_divmod_pow10(&hprev, &rest, src[q], r);
+ _mpd_divmod_pow10(&rnd, &rest, rest, r-1);
+
+ if (rest == 0 && q > 0) {
+ rest = !_mpd_isallzero(src, q);
+ }
+ /* write slen-q-1 words */
+ for (j=0,i=q+1; i<slen; i++,j++) {
+ _mpd_divmod_pow10(&h, &l, src[i], r);
+ dest[j] = ph * l + hprev;
+ hprev = h;
+ }
+ /* write most significant word */
+ if (hprev != 0) { /* always the case if slen==q-1 */
+ dest[j] = hprev;
+ }
+ }
+ else {
+ if (q > 0) {
+ _mpd_divmod_pow10(&rnd, &rest, src[q-1], MPD_RDIGITS-1);
+ /* is there any non-zero digit below rnd? */
+ if (rest == 0) rest = !_mpd_isallzero(src, q-1);
+ }
+ for (j = 0; j < slen-q; j++) {
+ dest[j] = src[q+j];
+ }
+ }
+
+ /* 0-4 ==> rnd+rest < 0.5 */
+ /* 5 ==> rnd+rest == 0.5 */
+ /* 6-9 ==> rnd+rest > 0.5 */
+ return (rnd == 0 || rnd == 5) ? rnd + !!rest : rnd;
+}
+
+
+/*********************************************************************/
+/* Calculations in base b */
+/*********************************************************************/
+
+/*
+ * Add v to w (len m). The calling function has to handle a possible
+ * final carry. Assumption: m > 0.
+ */
+mpd_uint_t
+_mpd_shortadd_b(mpd_uint_t *w, mpd_size_t m, mpd_uint_t v, mpd_uint_t b)
+{
+ mpd_uint_t s;
+ mpd_uint_t carry;
+ mpd_size_t i;
+
+ assert(m > 0);
+
+ /* add v to w */
+ s = w[0] + v;
+ carry = (s < v) | (s >= b);
+ w[0] = carry ? s-b : s;
+
+ /* if there is a carry, propagate it */
+ for (i = 1; carry && i < m; i++) {
+ s = w[i] + carry;
+ carry = (s == b);
+ w[i] = carry ? 0 : s;
+ }
+
+ return carry;
+}
+
+/* w := product of u (len n) and v (single word) */
+void
+_mpd_shortmul_b(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n,
+ mpd_uint_t v, mpd_uint_t b)
+{
+ mpd_uint_t hi, lo;
+ mpd_uint_t carry = 0;
+ mpd_size_t i;
+
+ assert(n > 0);
+
+ for (i=0; i < n; i++) {
+
+ _mpd_mul_words(&hi, &lo, u[i], v);
+ lo = carry + lo;
+ if (lo < carry) hi++;
+
+ _mpd_div_words(&carry, &w[i], hi, lo, b);
+ }
+ w[i] = carry;
+}
+
+/*
+ * Knuth, TAOCP Volume 2, 4.3.1, exercise 16:
+ * w := quotient of u (len n) divided by a single word v
+ */
+mpd_uint_t
+_mpd_shortdiv_b(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n,
+ mpd_uint_t v, mpd_uint_t b)
+{
+ mpd_uint_t hi, lo;
+ mpd_uint_t rem = 0;
+ mpd_size_t i;
+
+ assert(n > 0);
+
+ for (i=n-1; i != MPD_SIZE_MAX; i--) {
+
+ _mpd_mul_words(&hi, &lo, rem, b);
+ lo = u[i] + lo;
+ if (lo < u[i]) hi++;
+
+ _mpd_div_words(&w[i], &rem, hi, lo, v);
+ }
+
+ return rem;
+}
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef BASEARITH_H
+#define BASEARITH_H
+
+
+#include "mpdecimal.h"
+#include <stdio.h>
+#include "typearith.h"
+
+
+mpd_uint_t _mpd_baseadd(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v,
+ mpd_size_t m, mpd_size_t n);
+void _mpd_baseaddto(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n);
+mpd_uint_t _mpd_shortadd(mpd_uint_t *w, mpd_size_t m, mpd_uint_t v);
+mpd_uint_t _mpd_shortadd_b(mpd_uint_t *w, mpd_size_t m, mpd_uint_t v,
+ mpd_uint_t b);
+mpd_uint_t _mpd_baseincr(mpd_uint_t *u, mpd_size_t n);
+void _mpd_basesub(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v,
+ mpd_size_t m, mpd_size_t n);
+void _mpd_basesubfrom(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n);
+void _mpd_basemul(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v,
+ mpd_size_t m, mpd_size_t n);
+void _mpd_shortmul(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n,
+ mpd_uint_t v);
+void _mpd_shortmul_b(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n,
+ mpd_uint_t v, mpd_uint_t b);
+mpd_uint_t _mpd_shortdiv(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n,
+ mpd_uint_t v);
+mpd_uint_t _mpd_shortdiv_b(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n,
+ mpd_uint_t v, mpd_uint_t b);
+int _mpd_basedivmod(mpd_uint_t *q, mpd_uint_t *r, const mpd_uint_t *uconst,
+ const mpd_uint_t *vconst, mpd_size_t nplusm, mpd_size_t n);
+void _mpd_baseshiftl(mpd_uint_t *dest, mpd_uint_t *src, mpd_size_t n,
+ mpd_size_t m, mpd_size_t shift);
+mpd_uint_t _mpd_baseshiftr(mpd_uint_t *dest, mpd_uint_t *src, mpd_size_t slen,
+ mpd_size_t shift);
+
+
+
+#ifdef CONFIG_64
+extern const mpd_uint_t mprime_rdx;
+
+/*
+ * Algorithm from: Division by Invariant Integers using Multiplication,
+ * T. Granlund and P. L. Montgomery, Proceedings of the SIGPLAN '94
+ * Conference on Programming Language Design and Implementation.
+ *
+ * http://gmplib.org/~tege/divcnst-pldi94.pdf
+ *
+ * Variables from the paper and their translations (See section 8):
+ *
+ * N := 64
+ * d := MPD_RADIX
+ * l := 64
+ * m' := floor((2**(64+64) - 1)/MPD_RADIX) - 2**64
+ *
+ * Since N-l == 0:
+ *
+ * dnorm := d
+ * n2 := hi
+ * n10 := lo
+ *
+ * ACL2 proof: mpd-div-words-r-correct
+ */
+static inline void
+_mpd_div_words_r(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo)
+{
+ mpd_uint_t n_adj, h, l, t;
+ mpd_uint_t n1_neg;
+
+ /* n1_neg = if lo >= 2**63 then MPD_UINT_MAX else 0 */
+ n1_neg = (lo & (1ULL<<63)) ? MPD_UINT_MAX : 0;
+ /* n_adj = if lo >= 2**63 then lo+MPD_RADIX else lo */
+ n_adj = lo + (n1_neg & MPD_RADIX);
+
+ /* (h, l) = if lo >= 2**63 then m'*(hi+1) else m'*hi */
+ _mpd_mul_words(&h, &l, mprime_rdx, hi-n1_neg);
+ l = l + n_adj;
+ if (l < n_adj) h++;
+ t = h + hi;
+ /* At this point t == qest, with q == qest or q == qest+1:
+ * 1) 0 <= 2**64*hi + lo - qest*MPD_RADIX < 2*MPD_RADIX
+ */
+
+ /* t = 2**64-1 - qest = 2**64 - (qest+1) */
+ t = MPD_UINT_MAX - t;
+
+ /* (h, l) = 2**64*MPD_RADIX - (qest+1)*MPD_RADIX */
+ _mpd_mul_words(&h, &l, t, MPD_RADIX);
+ l = l + lo;
+ if (l < lo) h++;
+ h += hi;
+ h -= MPD_RADIX;
+ /* (h, l) = 2**64*hi + lo - (qest+1)*MPD_RADIX (mod 2**128)
+ * Case q == qest+1:
+ * a) h == 0, l == r
+ * b) q := h - t == qest+1
+ * c) r := l
+ * Case q == qest:
+ * a) h == MPD_UINT_MAX, l == 2**64-(MPD_RADIX-r)
+ * b) q := h - t == qest
+ * c) r := l + MPD_RADIX = r
+ */
+
+ *q = (h - t);
+ *r = l + (MPD_RADIX & h);
+}
+#else
+static inline void
+_mpd_div_words_r(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo)
+{
+ _mpd_div_words(q, r, hi, lo, MPD_RADIX);
+}
+#endif
+
+
+/* Multiply two single base MPD_RADIX words, store result in array w[2]. */
+static inline void
+_mpd_singlemul(mpd_uint_t w[2], mpd_uint_t u, mpd_uint_t v)
+{
+ mpd_uint_t hi, lo;
+
+ _mpd_mul_words(&hi, &lo, u, v);
+ _mpd_div_words_r(&w[1], &w[0], hi, lo);
+}
+
+/* Multiply u (len 2) and v (len m, 1 <= m <= 2). */
+static inline void
+_mpd_mul_2_le2(mpd_uint_t w[4], mpd_uint_t u[2], mpd_uint_t v[2], mpd_ssize_t m)
+{
+ mpd_uint_t hi, lo;
+
+ _mpd_mul_words(&hi, &lo, u[0], v[0]);
+ _mpd_div_words_r(&w[1], &w[0], hi, lo);
+
+ _mpd_mul_words(&hi, &lo, u[1], v[0]);
+ lo = w[1] + lo;
+ if (lo < w[1]) hi++;
+ _mpd_div_words_r(&w[2], &w[1], hi, lo);
+ if (m == 1) return;
+
+ _mpd_mul_words(&hi, &lo, u[0], v[1]);
+ lo = w[1] + lo;
+ if (lo < w[1]) hi++;
+ _mpd_div_words_r(&w[3], &w[1], hi, lo);
+
+ _mpd_mul_words(&hi, &lo, u[1], v[1]);
+ lo = w[2] + lo;
+ if (lo < w[2]) hi++;
+ lo = w[3] + lo;
+ if (lo < w[3]) hi++;
+ _mpd_div_words_r(&w[3], &w[2], hi, lo);
+}
+
+
+/*
+ * Test if all words from data[len-1] to data[0] are zero. If len is 0, nothing
+ * is tested and the coefficient is regarded as "all zero".
+ */
+static inline int
+_mpd_isallzero(const mpd_uint_t *data, mpd_ssize_t len)
+{
+ while (--len >= 0) {
+ if (data[len] != 0) return 0;
+ }
+ return 1;
+}
+
+/*
+ * Test if all full words from data[len-1] to data[0] are MPD_RADIX-1
+ * (all nines). Return true if len == 0.
+ */
+static inline int
+_mpd_isallnine(const mpd_uint_t *data, mpd_ssize_t len)
+{
+ while (--len >= 0) {
+ if (data[len] != MPD_RADIX-1) return 0;
+ }
+ return 1;
+}
+
+
+#endif /* BASEARITH_H */
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef BITS_H
+#define BITS_H
+
+
+#include "mpdecimal.h"
+#include <stdio.h>
+
+
+/* Check if n is a power of 2. */
+static inline int
+ispower2(mpd_size_t n)
+{
+ return n != 0 && (n & (n-1)) == 0;
+}
+
+#if defined(ANSI)
+/*
+ * Return the most significant bit position of n from 0 to 31 (63).
+ * Assumptions: n != 0.
+ */
+static inline int
+mpd_bsr(mpd_size_t n)
+{
+ int pos = 0;
+ mpd_size_t tmp;
+
+#ifdef CONFIG_64
+ tmp = n >> 32;
+ if (tmp != 0) { n = tmp; pos += 32; }
+#endif
+ tmp = n >> 16;
+ if (tmp != 0) { n = tmp; pos += 16; }
+ tmp = n >> 8;
+ if (tmp != 0) { n = tmp; pos += 8; }
+ tmp = n >> 4;
+ if (tmp != 0) { n = tmp; pos += 4; }
+ tmp = n >> 2;
+ if (tmp != 0) { n = tmp; pos += 2; }
+ tmp = n >> 1;
+ if (tmp != 0) { n = tmp; pos += 1; }
+
+ return pos + (int)n - 1;
+}
+
+/*
+ * Return the least significant bit position of n from 0 to 31 (63).
+ * Assumptions: n != 0.
+ */
+static inline int
+mpd_bsf(mpd_size_t n)
+{
+ int pos;
+
+#ifdef CONFIG_64
+ pos = 63;
+ if (n & 0x00000000FFFFFFFFULL) { pos -= 32; } else { n >>= 32; }
+ if (n & 0x000000000000FFFFULL) { pos -= 16; } else { n >>= 16; }
+ if (n & 0x00000000000000FFULL) { pos -= 8; } else { n >>= 8; }
+ if (n & 0x000000000000000FULL) { pos -= 4; } else { n >>= 4; }
+ if (n & 0x0000000000000003ULL) { pos -= 2; } else { n >>= 2; }
+ if (n & 0x0000000000000001ULL) { pos -= 1; }
+#else
+ pos = 31;
+ if (n & 0x000000000000FFFFUL) { pos -= 16; } else { n >>= 16; }
+ if (n & 0x00000000000000FFUL) { pos -= 8; } else { n >>= 8; }
+ if (n & 0x000000000000000FUL) { pos -= 4; } else { n >>= 4; }
+ if (n & 0x0000000000000003UL) { pos -= 2; } else { n >>= 2; }
+ if (n & 0x0000000000000001UL) { pos -= 1; }
+#endif
+ return pos;
+}
+/* END ANSI */
+
+#elif defined(ASM)
+/*
+ * Bit scan reverse. Assumptions: a != 0.
+ */
+static inline int
+mpd_bsr(mpd_size_t a)
+{
+ mpd_size_t retval;
+
+ __asm__ (
+#ifdef CONFIG_64
+ "bsrq %1, %0\n\t"
+#else
+ "bsr %1, %0\n\t"
+#endif
+ :"=r" (retval)
+ :"r" (a)
+ :"cc"
+ );
+
+ return (int)retval;
+}
+
+/*
+ * Bit scan forward. Assumptions: a != 0.
+ */
+static inline int
+mpd_bsf(mpd_size_t a)
+{
+ mpd_size_t retval;
+
+ __asm__ (
+#ifdef CONFIG_64
+ "bsfq %1, %0\n\t"
+#else
+ "bsf %1, %0\n\t"
+#endif
+ :"=r" (retval)
+ :"r" (a)
+ :"cc"
+ );
+
+ return (int)retval;
+}
+/* END ASM */
+
+#elif defined(MASM)
+#include <intrin.h>
+/*
+ * Bit scan reverse. Assumptions: a != 0.
+ */
+static inline int __cdecl
+mpd_bsr(mpd_size_t a)
+{
+ unsigned long retval;
+
+#ifdef CONFIG_64
+ _BitScanReverse64(&retval, a);
+#else
+ _BitScanReverse(&retval, a);
+#endif
+
+ return (int)retval;
+}
+
+/*
+ * Bit scan forward. Assumptions: a != 0.
+ */
+static inline int __cdecl
+mpd_bsf(mpd_size_t a)
+{
+ unsigned long retval;
+
+#ifdef CONFIG_64
+ _BitScanForward64(&retval, a);
+#else
+ _BitScanForward(&retval, a);
+#endif
+
+ return (int)retval;
+}
+/* END MASM (_MSC_VER) */
+#else
+ #error "missing preprocessor definitions"
+#endif /* BSR/BSF */
+
+
+#endif /* BITS_H */
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include "mpdecimal.h"
+#include <stdio.h>
+#include "constants.h"
+
+
+#if defined(CONFIG_64)
+
+ /* number-theory.c */
+ const mpd_uint_t mpd_moduli[3] = {
+ 18446744069414584321ULL, 18446744056529682433ULL, 18446742974197923841ULL
+ };
+ const mpd_uint_t mpd_roots[3] = {7ULL, 10ULL, 19ULL};
+
+ /* crt.c */
+ const mpd_uint_t INV_P1_MOD_P2 = 18446744055098026669ULL;
+ const mpd_uint_t INV_P1P2_MOD_P3 = 287064143708160ULL;
+ const mpd_uint_t LH_P1P2 = 18446744052234715137ULL; /* (P1*P2) % 2^64 */
+ const mpd_uint_t UH_P1P2 = 18446744052234715141ULL; /* (P1*P2) / 2^64 */
+
+ /* transpose.c */
+ const mpd_size_t mpd_bits[64] = {
+ 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384,
+ 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608,
+ 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824,
+ 2147483648ULL, 4294967296ULL, 8589934592ULL, 17179869184ULL, 34359738368ULL,
+ 68719476736ULL, 137438953472ULL, 274877906944ULL, 549755813888ULL,
+ 1099511627776ULL, 2199023255552ULL, 4398046511104, 8796093022208ULL,
+ 17592186044416ULL, 35184372088832ULL, 70368744177664ULL, 140737488355328ULL,
+ 281474976710656ULL, 562949953421312ULL, 1125899906842624ULL,
+ 2251799813685248ULL, 4503599627370496ULL, 9007199254740992ULL,
+ 18014398509481984ULL, 36028797018963968ULL, 72057594037927936ULL,
+ 144115188075855872ULL, 288230376151711744ULL, 576460752303423488ULL,
+ 1152921504606846976ULL, 2305843009213693952ULL, 4611686018427387904ULL,
+ 9223372036854775808ULL
+ };
+
+ /* mpdecimal.c */
+ const mpd_uint_t mpd_pow10[MPD_RDIGITS+1] = {
+ 1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000,
+ 10000000000ULL,100000000000ULL,1000000000000ULL,10000000000000ULL,
+ 100000000000000ULL,1000000000000000ULL,10000000000000000ULL,
+ 100000000000000000ULL,1000000000000000000ULL,10000000000000000000ULL
+ };
+
+ /* magic number for constant division by MPD_RADIX */
+ const mpd_uint_t mprime_rdx = 15581492618384294730ULL;
+
+#elif defined(CONFIG_32)
+
+ /* number-theory.c */
+ const mpd_uint_t mpd_moduli[3] = {2113929217UL, 2013265921UL, 1811939329UL};
+ const mpd_uint_t mpd_roots[3] = {5UL, 31UL, 13UL};
+
+ /* PentiumPro modular multiplication: These constants have to be loaded as
+ * 80 bit long doubles, which are not supported by certain compilers. */
+ const uint32_t mpd_invmoduli[3][3] = {
+ {4293885170U, 2181570688U, 16352U}, /* ((long double) 1 / 2113929217UL) */
+ {1698898177U, 2290649223U, 16352U}, /* ((long double) 1 / 2013265921UL) */
+ {2716021846U, 2545165803U, 16352U} /* ((long double) 1 / 1811939329UL) */
+ };
+
+ const float MPD_TWO63 = 9223372036854775808.0; /* 2^63 */
+
+ /* crt.c */
+ const mpd_uint_t INV_P1_MOD_P2 = 2013265901UL;
+ const mpd_uint_t INV_P1P2_MOD_P3 = 54UL;
+ const mpd_uint_t LH_P1P2 = 4127195137UL; /* (P1*P2) % 2^32 */
+ const mpd_uint_t UH_P1P2 = 990904320UL; /* (P1*P2) / 2^32 */
+
+ /* transpose.c */
+ const mpd_size_t mpd_bits[32] = {
+ 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384,
+ 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608,
+ 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824,
+ 2147483648UL
+ };
+
+ /* mpdecimal.c */
+ const mpd_uint_t mpd_pow10[MPD_RDIGITS+1] = {
+ 1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000
+ };
+
+#else
+ #error "CONFIG_64 or CONFIG_32 must be defined."
+#endif
+
+const char *mpd_round_string[MPD_ROUND_GUARD] = {
+ "ROUND_UP", /* round away from 0 */
+ "ROUND_DOWN", /* round toward 0 (truncate) */
+ "ROUND_CEILING", /* round toward +infinity */
+ "ROUND_FLOOR", /* round toward -infinity */
+ "ROUND_HALF_UP", /* 0.5 is rounded up */
+ "ROUND_HALF_DOWN", /* 0.5 is rounded down */
+ "ROUND_HALF_EVEN", /* 0.5 is rounded to even */
+ "ROUND_05UP", /* round zero or five away from 0 */
+ "ROUND_TRUNC", /* truncate, but set infinity */
+};
+
+const char *mpd_clamp_string[MPD_CLAMP_GUARD] = {
+ "CLAMP_DEFAULT",
+ "CLAMP_IEEE_754"
+};
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef CONSTANTS_H
+#define CONSTANTS_H
+
+
+#include "mpdecimal.h"
+
+
+/* choice of optimized functions */
+#if defined(CONFIG_64)
+/* x64 */
+ #define MULMOD(a, b) x64_mulmod(a, b, umod)
+ #define MULMOD2C(a0, a1, w) x64_mulmod2c(a0, a1, w, umod)
+ #define MULMOD2(a0, b0, a1, b1) x64_mulmod2(a0, b0, a1, b1, umod)
+ #define POWMOD(base, exp) x64_powmod(base, exp, umod)
+ #define SETMODULUS(modnum) std_setmodulus(modnum, &umod)
+ #define SIZE3_NTT(x0, x1, x2, w3table) std_size3_ntt(x0, x1, x2, w3table, umod)
+#elif defined(PPRO)
+/* PentiumPro (or later) gcc inline asm */
+ #define MULMOD(a, b) ppro_mulmod(a, b, &dmod, dinvmod)
+ #define MULMOD2C(a0, a1, w) ppro_mulmod2c(a0, a1, w, &dmod, dinvmod)
+ #define MULMOD2(a0, b0, a1, b1) ppro_mulmod2(a0, b0, a1, b1, &dmod, dinvmod)
+ #define POWMOD(base, exp) ppro_powmod(base, exp, &dmod, dinvmod)
+ #define SETMODULUS(modnum) ppro_setmodulus(modnum, &umod, &dmod, dinvmod)
+ #define SIZE3_NTT(x0, x1, x2, w3table) ppro_size3_ntt(x0, x1, x2, w3table, umod, &dmod, dinvmod)
+#else
+ /* ANSI C99 */
+ #define MULMOD(a, b) std_mulmod(a, b, umod)
+ #define MULMOD2C(a0, a1, w) std_mulmod2c(a0, a1, w, umod)
+ #define MULMOD2(a0, b0, a1, b1) std_mulmod2(a0, b0, a1, b1, umod)
+ #define POWMOD(base, exp) std_powmod(base, exp, umod)
+ #define SETMODULUS(modnum) std_setmodulus(modnum, &umod)
+ #define SIZE3_NTT(x0, x1, x2, w3table) std_size3_ntt(x0, x1, x2, w3table, umod)
+#endif
+
+/* PentiumPro (or later) gcc inline asm */
+extern const float MPD_TWO63;
+extern const uint32_t mpd_invmoduli[3][3];
+
+enum {P1, P2, P3};
+
+extern const mpd_uint_t mpd_moduli[];
+extern const mpd_uint_t mpd_roots[];
+extern const mpd_size_t mpd_bits[];
+extern const mpd_uint_t mpd_pow10[];
+
+extern const mpd_uint_t INV_P1_MOD_P2;
+extern const mpd_uint_t INV_P1P2_MOD_P3;
+extern const mpd_uint_t LH_P1P2;
+extern const mpd_uint_t UH_P1P2;
+
+
+#endif /* CONSTANTS_H */
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include "mpdecimal.h"
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+
+
+void
+mpd_dflt_traphandler(mpd_context_t *ctx UNUSED)
+{
+ raise(SIGFPE);
+}
+
+void (* mpd_traphandler)(mpd_context_t *) = mpd_dflt_traphandler;
+
+
+/* Set guaranteed minimum number of coefficient words. The function may
+ be used once at program start. Setting MPD_MINALLOC to out-of-bounds
+ values is a catastrophic error, so in that case the function exits rather
+ than relying on the user to check a return value. */
+void
+mpd_setminalloc(mpd_ssize_t n)
+{
+ static int minalloc_is_set = 0;
+
+ if (minalloc_is_set) {
+ mpd_err_warn("mpd_setminalloc: ignoring request to set "
+ "MPD_MINALLOC a second time\n");
+ return;
+ }
+ if (n < MPD_MINALLOC_MIN || n > MPD_MINALLOC_MAX) {
+ mpd_err_fatal("illegal value for MPD_MINALLOC"); /* GCOV_NOT_REACHED */
+ }
+ MPD_MINALLOC = n;
+ minalloc_is_set = 1;
+}
+
+void
+mpd_init(mpd_context_t *ctx, mpd_ssize_t prec)
+{
+ mpd_ssize_t ideal_minalloc;
+
+ mpd_defaultcontext(ctx);
+
+ if (!mpd_qsetprec(ctx, prec)) {
+ mpd_addstatus_raise(ctx, MPD_Invalid_context);
+ return;
+ }
+
+ ideal_minalloc = 2 * ((prec+MPD_RDIGITS-1) / MPD_RDIGITS);
+ if (ideal_minalloc < MPD_MINALLOC_MIN) ideal_minalloc = MPD_MINALLOC_MIN;
+ if (ideal_minalloc > MPD_MINALLOC_MAX) ideal_minalloc = MPD_MINALLOC_MAX;
+
+ mpd_setminalloc(ideal_minalloc);
+}
+
+void
+mpd_maxcontext(mpd_context_t *ctx)
+{
+ ctx->prec=MPD_MAX_PREC;
+ ctx->emax=MPD_MAX_EMAX;
+ ctx->emin=MPD_MIN_EMIN;
+ ctx->round=MPD_ROUND_HALF_EVEN;
+ ctx->traps=MPD_Traps;
+ ctx->status=0;
+ ctx->newtrap=0;
+ ctx->clamp=0;
+ ctx->allcr=1;
+}
+
+void
+mpd_defaultcontext(mpd_context_t *ctx)
+{
+ ctx->prec=2*MPD_RDIGITS;
+ ctx->emax=MPD_MAX_EMAX;
+ ctx->emin=MPD_MIN_EMIN;
+ ctx->round=MPD_ROUND_HALF_UP;
+ ctx->traps=MPD_Traps;
+ ctx->status=0;
+ ctx->newtrap=0;
+ ctx->clamp=0;
+ ctx->allcr=1;
+}
+
+void
+mpd_basiccontext(mpd_context_t *ctx)
+{
+ ctx->prec=9;
+ ctx->emax=MPD_MAX_EMAX;
+ ctx->emin=MPD_MIN_EMIN;
+ ctx->round=MPD_ROUND_HALF_UP;
+ ctx->traps=MPD_Traps|MPD_Clamped;
+ ctx->status=0;
+ ctx->newtrap=0;
+ ctx->clamp=0;
+ ctx->allcr=1;
+}
+
+int
+mpd_ieee_context(mpd_context_t *ctx, int bits)
+{
+ if (bits <= 0 || bits > MPD_IEEE_CONTEXT_MAX_BITS || bits % 32) {
+ return -1;
+ }
+
+ ctx->prec = 9 * (bits/32) - 2;
+ ctx->emax = 3 * ((mpd_ssize_t)1<<(bits/16+3));
+ ctx->emin = 1 - ctx->emax;
+ ctx->round=MPD_ROUND_HALF_EVEN;
+ ctx->traps=0;
+ ctx->status=0;
+ ctx->newtrap=0;
+ ctx->clamp=1;
+ ctx->allcr=1;
+
+ return 0;
+}
+
+mpd_ssize_t
+mpd_getprec(const mpd_context_t *ctx)
+{
+ return ctx->prec;
+}
+
+mpd_ssize_t
+mpd_getemax(const mpd_context_t *ctx)
+{
+ return ctx->emax;
+}
+
+mpd_ssize_t
+mpd_getemin(const mpd_context_t *ctx)
+{
+ return ctx->emin;
+}
+
+int
+mpd_getround(const mpd_context_t *ctx)
+{
+ return ctx->round;
+}
+
+uint32_t
+mpd_gettraps(const mpd_context_t *ctx)
+{
+ return ctx->traps;
+}
+
+uint32_t
+mpd_getstatus(const mpd_context_t *ctx)
+{
+ return ctx->status;
+}
+
+int
+mpd_getclamp(const mpd_context_t *ctx)
+{
+ return ctx->clamp;
+}
+
+int
+mpd_getcr(const mpd_context_t *ctx)
+{
+ return ctx->allcr;
+}
+
+
+int
+mpd_qsetprec(mpd_context_t *ctx, mpd_ssize_t prec)
+{
+ if (prec <= 0 || prec > MPD_MAX_PREC) {
+ return 0;
+ }
+ ctx->prec = prec;
+ return 1;
+}
+
+int
+mpd_qsetemax(mpd_context_t *ctx, mpd_ssize_t emax)
+{
+ if (emax < 0 || emax > MPD_MAX_EMAX) {
+ return 0;
+ }
+ ctx->emax = emax;
+ return 1;
+}
+
+int
+mpd_qsetemin(mpd_context_t *ctx, mpd_ssize_t emin)
+{
+ if (emin > 0 || emin < MPD_MIN_EMIN) {
+ return 0;
+ }
+ ctx->emin = emin;
+ return 1;
+}
+
+int
+mpd_qsetround(mpd_context_t *ctx, int round)
+{
+ if (!(0 <= round && round < MPD_ROUND_GUARD)) {
+ return 0;
+ }
+ ctx->round = round;
+ return 1;
+}
+
+int
+mpd_qsettraps(mpd_context_t *ctx, uint32_t traps)
+{
+ if (traps > MPD_Max_status) {
+ return 0;
+ }
+ ctx->traps = traps;
+ return 1;
+}
+
+int
+mpd_qsetstatus(mpd_context_t *ctx, uint32_t flags)
+{
+ if (flags > MPD_Max_status) {
+ return 0;
+ }
+ ctx->status = flags;
+ return 1;
+}
+
+int
+mpd_qsetclamp(mpd_context_t *ctx, int c)
+{
+ if (c != 0 && c != 1) {
+ return 0;
+ }
+ ctx->clamp = c;
+ return 1;
+}
+
+int
+mpd_qsetcr(mpd_context_t *ctx, int c)
+{
+ if (c != 0 && c != 1) {
+ return 0;
+ }
+ ctx->allcr = c;
+ return 1;
+}
+
+
+void
+mpd_addstatus_raise(mpd_context_t *ctx, uint32_t flags)
+{
+ ctx->status |= flags;
+ if (flags&ctx->traps) {
+ ctx->newtrap = (flags&ctx->traps);
+ mpd_traphandler(ctx);
+ }
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include "mpdecimal.h"
+#include <stdio.h>
+#include "bits.h"
+#include "constants.h"
+#include "fnt.h"
+#include "fourstep.h"
+#include "numbertheory.h"
+#include "sixstep.h"
+#include "umodarith.h"
+#include "convolute.h"
+
+
+/* Bignum: Fast convolution using the Number Theoretic Transform. Used for
+ the multiplication of very large coefficients. */
+
+
+/* Convolute the data in c1 and c2. Result is in c1. */
+int
+fnt_convolute(mpd_uint_t *c1, mpd_uint_t *c2, mpd_size_t n, int modnum)
+{
+ int (*fnt)(mpd_uint_t *, mpd_size_t, int);
+ int (*inv_fnt)(mpd_uint_t *, mpd_size_t, int);
+#ifdef PPRO
+ double dmod;
+ uint32_t dinvmod[3];
+#endif
+ mpd_uint_t n_inv, umod;
+ mpd_size_t i;
+
+
+ SETMODULUS(modnum);
+ n_inv = POWMOD(n, (umod-2));
+
+ if (ispower2(n)) {
+ if (n > SIX_STEP_THRESHOLD) {
+ fnt = six_step_fnt;
+ inv_fnt = inv_six_step_fnt;
+ }
+ else {
+ fnt = std_fnt;
+ inv_fnt = std_inv_fnt;
+ }
+ }
+ else {
+ fnt = four_step_fnt;
+ inv_fnt = inv_four_step_fnt;
+ }
+
+ if (!fnt(c1, n, modnum)) {
+ return 0;
+ }
+ if (!fnt(c2, n, modnum)) {
+ return 0;
+ }
+ for (i = 0; i < n-1; i += 2) {
+ mpd_uint_t x0 = c1[i];
+ mpd_uint_t y0 = c2[i];
+ mpd_uint_t x1 = c1[i+1];
+ mpd_uint_t y1 = c2[i+1];
+ MULMOD2(&x0, y0, &x1, y1);
+ c1[i] = x0;
+ c1[i+1] = x1;
+ }
+
+ if (!inv_fnt(c1, n, modnum)) {
+ return 0;
+ }
+ for (i = 0; i < n-3; i += 4) {
+ mpd_uint_t x0 = c1[i];
+ mpd_uint_t x1 = c1[i+1];
+ mpd_uint_t x2 = c1[i+2];
+ mpd_uint_t x3 = c1[i+3];
+ MULMOD2C(&x0, &x1, n_inv);
+ MULMOD2C(&x2, &x3, n_inv);
+ c1[i] = x0;
+ c1[i+1] = x1;
+ c1[i+2] = x2;
+ c1[i+3] = x3;
+ }
+
+ return 1;
+}
+
+/* Autoconvolute the data in c1. Result is in c1. */
+int
+fnt_autoconvolute(mpd_uint_t *c1, mpd_size_t n, int modnum)
+{
+ int (*fnt)(mpd_uint_t *, mpd_size_t, int);
+ int (*inv_fnt)(mpd_uint_t *, mpd_size_t, int);
+#ifdef PPRO
+ double dmod;
+ uint32_t dinvmod[3];
+#endif
+ mpd_uint_t n_inv, umod;
+ mpd_size_t i;
+
+
+ SETMODULUS(modnum);
+ n_inv = POWMOD(n, (umod-2));
+
+ if (ispower2(n)) {
+ if (n > SIX_STEP_THRESHOLD) {
+ fnt = six_step_fnt;
+ inv_fnt = inv_six_step_fnt;
+ }
+ else {
+ fnt = std_fnt;
+ inv_fnt = std_inv_fnt;
+ }
+ }
+ else {
+ fnt = four_step_fnt;
+ inv_fnt = inv_four_step_fnt;
+ }
+
+ if (!fnt(c1, n, modnum)) {
+ return 0;
+ }
+ for (i = 0; i < n-1; i += 2) {
+ mpd_uint_t x0 = c1[i];
+ mpd_uint_t x1 = c1[i+1];
+ MULMOD2(&x0, x0, &x1, x1);
+ c1[i] = x0;
+ c1[i+1] = x1;
+ }
+
+ if (!inv_fnt(c1, n, modnum)) {
+ return 0;
+ }
+ for (i = 0; i < n-3; i += 4) {
+ mpd_uint_t x0 = c1[i];
+ mpd_uint_t x1 = c1[i+1];
+ mpd_uint_t x2 = c1[i+2];
+ mpd_uint_t x3 = c1[i+3];
+ MULMOD2C(&x0, &x1, n_inv);
+ MULMOD2C(&x2, &x3, n_inv);
+ c1[i] = x0;
+ c1[i+1] = x1;
+ c1[i+2] = x2;
+ c1[i+3] = x3;
+ }
+
+ return 1;
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef CONVOLUTE_H
+#define CONVOLUTE_H
+
+
+#include "mpdecimal.h"
+#include <stdio.h>
+
+#define SIX_STEP_THRESHOLD 4096
+
+
+int fnt_convolute(mpd_uint_t *c1, mpd_uint_t *c2, mpd_size_t n, int modnum);
+int fnt_autoconvolute(mpd_uint_t *c1, mpd_size_t n, int modnum);
+
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include "mpdecimal.h"
+#include <stdio.h>
+#include <assert.h>
+#include "numbertheory.h"
+#include "umodarith.h"
+#include "crt.h"
+
+
+/* Bignum: Chinese Remainder Theorem, extends the maximum transform length. */
+
+
+/* Multiply P1P2 by v, store result in w. */
+static inline void
+_crt_mulP1P2_3(mpd_uint_t w[3], mpd_uint_t v)
+{
+ mpd_uint_t hi1, hi2, lo;
+
+ _mpd_mul_words(&hi1, &lo, LH_P1P2, v);
+ w[0] = lo;
+
+ _mpd_mul_words(&hi2, &lo, UH_P1P2, v);
+ lo = hi1 + lo;
+ if (lo < hi1) hi2++;
+
+ w[1] = lo;
+ w[2] = hi2;
+}
+
+/* Add 3 words from v to w. The result is known to fit in w. */
+static inline void
+_crt_add3(mpd_uint_t w[3], mpd_uint_t v[3])
+{
+ mpd_uint_t carry;
+ mpd_uint_t s;
+
+ s = w[0] + v[0];
+ carry = (s < w[0]);
+ w[0] = s;
+
+ s = w[1] + (v[1] + carry);
+ carry = (s < w[1]);
+ w[1] = s;
+
+ w[2] = w[2] + (v[2] + carry);
+}
+
+/* Divide 3 words in u by v, store result in w, return remainder. */
+static inline mpd_uint_t
+_crt_div3(mpd_uint_t *w, const mpd_uint_t *u, mpd_uint_t v)
+{
+ mpd_uint_t r1 = u[2];
+ mpd_uint_t r2;
+
+ if (r1 < v) {
+ w[2] = 0;
+ }
+ else {
+ _mpd_div_word(&w[2], &r1, u[2], v); /* GCOV_NOT_REACHED */
+ }
+
+ _mpd_div_words(&w[1], &r2, r1, u[1], v);
+ _mpd_div_words(&w[0], &r1, r2, u[0], v);
+
+ return r1;
+}
+
+
+/*
+ * Chinese Remainder Theorem:
+ * Algorithm from Joerg Arndt, "Matters Computational",
+ * Chapter 37.4.1 [http://www.jjj.de/fxt/]
+ *
+ * See also Knuth, TAOCP, Volume 2, 4.3.2, exercise 7.
+ */
+
+/*
+ * CRT with carry: x1, x2, x3 contain numbers modulo p1, p2, p3. For each
+ * triple of members of the arrays, find the unique z modulo p1*p2*p3, with
+ * zmax = p1*p2*p3 - 1.
+ *
+ * In each iteration of the loop, split z into result[i] = z % MPD_RADIX
+ * and carry = z / MPD_RADIX. Let N be the size of carry[] and cmax the
+ * maximum carry.
+ *
+ * Limits for the 32-bit build:
+ *
+ * N = 2**96
+ * cmax = 7711435591312380274
+ *
+ * Limits for the 64 bit build:
+ *
+ * N = 2**192
+ * cmax = 627710135393475385904124401220046371710
+ *
+ * The following statements hold for both versions:
+ *
+ * 1) cmax + zmax < N, so the addition does not overflow.
+ *
+ * 2) (cmax + zmax) / MPD_RADIX == cmax.
+ *
+ * 3) If c <= cmax, then c_next = (c + zmax) / MPD_RADIX <= cmax.
+ */
+void
+crt3(mpd_uint_t *x1, mpd_uint_t *x2, mpd_uint_t *x3, mpd_size_t rsize)
+{
+ mpd_uint_t p1 = mpd_moduli[P1];
+ mpd_uint_t umod;
+#ifdef PPRO
+ double dmod;
+ uint32_t dinvmod[3];
+#endif
+ mpd_uint_t a1, a2, a3;
+ mpd_uint_t s;
+ mpd_uint_t z[3], t[3];
+ mpd_uint_t carry[3] = {0,0,0};
+ mpd_uint_t hi, lo;
+ mpd_size_t i;
+
+ for (i = 0; i < rsize; i++) {
+
+ a1 = x1[i];
+ a2 = x2[i];
+ a3 = x3[i];
+
+ SETMODULUS(P2);
+ s = ext_submod(a2, a1, umod);
+ s = MULMOD(s, INV_P1_MOD_P2);
+
+ _mpd_mul_words(&hi, &lo, s, p1);
+ lo = lo + a1;
+ if (lo < a1) hi++;
+
+ SETMODULUS(P3);
+ s = dw_submod(a3, hi, lo, umod);
+ s = MULMOD(s, INV_P1P2_MOD_P3);
+
+ z[0] = lo;
+ z[1] = hi;
+ z[2] = 0;
+
+ _crt_mulP1P2_3(t, s);
+ _crt_add3(z, t);
+ _crt_add3(carry, z);
+
+ x1[i] = _crt_div3(carry, carry, MPD_RADIX);
+ }
+
+ assert(carry[0] == 0 && carry[1] == 0 && carry[2] == 0);
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef CRT_H
+#define CRT_H
+
+
+#include "mpdecimal.h"
+#include <stdio.h>
+
+
+void crt3(mpd_uint_t *x1, mpd_uint_t *x2, mpd_uint_t *x3, mpd_size_t nmemb);
+
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include "mpdecimal.h"
+#include <stdio.h>
+#include <assert.h>
+#include "bits.h"
+#include "numbertheory.h"
+#include "umodarith.h"
+#include "difradix2.h"
+
+
+/* Bignum: The actual transform routine (decimation in frequency). */
+
+
+/*
+ * Generate index pairs (x, bitreverse(x)) and carry out the permutation.
+ * n must be a power of two.
+ * Algorithm due to Brent/Lehmann, see Joerg Arndt, "Matters Computational",
+ * Chapter 1.14.4. [http://www.jjj.de/fxt/]
+ */
+static inline void
+bitreverse_permute(mpd_uint_t a[], mpd_size_t n)
+{
+ mpd_size_t x = 0;
+ mpd_size_t r = 0;
+ mpd_uint_t t;
+
+ do { /* Invariant: r = bitreverse(x) */
+ if (r > x) {
+ t = a[x];
+ a[x] = a[r];
+ a[r] = t;
+ }
+ /* Flip trailing consecutive 1 bits and the first zero bit
+ * that absorbs a possible carry. */
+ x += 1;
+ /* Mirror the operation on r: Flip n_trailing_zeros(x)+1
+ high bits of r. */
+ r ^= (n - (n >> (mpd_bsf(x)+1)));
+ /* The loop invariant is preserved. */
+ } while (x < n);
+}
+
+
+/* Fast Number Theoretic Transform, decimation in frequency. */
+void
+fnt_dif2(mpd_uint_t a[], mpd_size_t n, struct fnt_params *tparams)
+{
+ mpd_uint_t *wtable = tparams->wtable;
+ mpd_uint_t umod;
+#ifdef PPRO
+ double dmod;
+ uint32_t dinvmod[3];
+#endif
+ mpd_uint_t u0, u1, v0, v1;
+ mpd_uint_t w, w0, w1, wstep;
+ mpd_size_t m, mhalf;
+ mpd_size_t j, r;
+
+
+ assert(ispower2(n));
+ assert(n >= 4);
+
+ SETMODULUS(tparams->modnum);
+
+ /* m == n */
+ mhalf = n / 2;
+ for (j = 0; j < mhalf; j += 2) {
+
+ w0 = wtable[j];
+ w1 = wtable[j+1];
+
+ u0 = a[j];
+ v0 = a[j+mhalf];
+
+ u1 = a[j+1];
+ v1 = a[j+1+mhalf];
+
+ a[j] = addmod(u0, v0, umod);
+ v0 = submod(u0, v0, umod);
+
+ a[j+1] = addmod(u1, v1, umod);
+ v1 = submod(u1, v1, umod);
+
+ MULMOD2(&v0, w0, &v1, w1);
+
+ a[j+mhalf] = v0;
+ a[j+1+mhalf] = v1;
+
+ }
+
+ wstep = 2;
+ for (m = n/2; m >= 2; m>>=1, wstep<<=1) {
+
+ mhalf = m / 2;
+
+ /* j == 0 */
+ for (r = 0; r < n; r += 2*m) {
+
+ u0 = a[r];
+ v0 = a[r+mhalf];
+
+ u1 = a[m+r];
+ v1 = a[m+r+mhalf];
+
+ a[r] = addmod(u0, v0, umod);
+ v0 = submod(u0, v0, umod);
+
+ a[m+r] = addmod(u1, v1, umod);
+ v1 = submod(u1, v1, umod);
+
+ a[r+mhalf] = v0;
+ a[m+r+mhalf] = v1;
+ }
+
+ for (j = 1; j < mhalf; j++) {
+
+ w = wtable[j*wstep];
+
+ for (r = 0; r < n; r += 2*m) {
+
+ u0 = a[r+j];
+ v0 = a[r+j+mhalf];
+
+ u1 = a[m+r+j];
+ v1 = a[m+r+j+mhalf];
+
+ a[r+j] = addmod(u0, v0, umod);
+ v0 = submod(u0, v0, umod);
+
+ a[m+r+j] = addmod(u1, v1, umod);
+ v1 = submod(u1, v1, umod);
+
+ MULMOD2C(&v0, &v1, w);
+
+ a[r+j+mhalf] = v0;
+ a[m+r+j+mhalf] = v1;
+ }
+
+ }
+
+ }
+
+ bitreverse_permute(a, n);
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef DIF_RADIX2_H
+#define DIF_RADIX2_H
+
+
+#include "mpdecimal.h"
+#include <stdio.h>
+#include "numbertheory.h"
+
+
+void fnt_dif2(mpd_uint_t a[], mpd_size_t n, struct fnt_params *tparams);
+
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include "mpdecimal.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "bits.h"
+#include "difradix2.h"
+#include "numbertheory.h"
+#include "fnt.h"
+
+
+/* Bignum: Fast transform for medium-sized coefficients. */
+
+
+/* forward transform, sign = -1 */
+int
+std_fnt(mpd_uint_t *a, mpd_size_t n, int modnum)
+{
+ struct fnt_params *tparams;
+
+ assert(ispower2(n));
+ assert(n >= 4);
+ assert(n <= 3*MPD_MAXTRANSFORM_2N);
+
+ if ((tparams = _mpd_init_fnt_params(n, -1, modnum)) == NULL) {
+ return 0;
+ }
+ fnt_dif2(a, n, tparams);
+
+ mpd_free(tparams);
+ return 1;
+}
+
+/* reverse transform, sign = 1 */
+int
+std_inv_fnt(mpd_uint_t *a, mpd_size_t n, int modnum)
+{
+ struct fnt_params *tparams;
+
+ assert(ispower2(n));
+ assert(n >= 4);
+ assert(n <= 3*MPD_MAXTRANSFORM_2N);
+
+ if ((tparams = _mpd_init_fnt_params(n, 1, modnum)) == NULL) {
+ return 0;
+ }
+ fnt_dif2(a, n, tparams);
+
+ mpd_free(tparams);
+ return 1;
+}
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef FNT_H
+#define FNT_H
+
+
+#include "mpdecimal.h"
+#include <stdio.h>
+
+
+int std_fnt(mpd_uint_t a[], mpd_size_t n, int modnum);
+int std_inv_fnt(mpd_uint_t a[], mpd_size_t n, int modnum);
+
+
+#endif
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include "mpdecimal.h"
+#include <assert.h>
+#include "numbertheory.h"
+#include "sixstep.h"
+#include "transpose.h"
+#include "umodarith.h"
+#include "fourstep.h"
+
+
+/* Bignum: Cache efficient Matrix Fourier Transform for arrays of the
+ form 3 * 2**n (See literature/matrix-transform.txt). */
+
+
+#ifndef PPRO
+static inline void
+std_size3_ntt(mpd_uint_t *x1, mpd_uint_t *x2, mpd_uint_t *x3,
+ mpd_uint_t w3table[3], mpd_uint_t umod)
+{
+ mpd_uint_t r1, r2;
+ mpd_uint_t w;
+ mpd_uint_t s, tmp;
+
+
+ /* k = 0 -> w = 1 */
+ s = *x1;
+ s = addmod(s, *x2, umod);
+ s = addmod(s, *x3, umod);
+
+ r1 = s;
+
+ /* k = 1 */
+ s = *x1;
+
+ w = w3table[1];
+ tmp = MULMOD(*x2, w);
+ s = addmod(s, tmp, umod);
+
+ w = w3table[2];
+ tmp = MULMOD(*x3, w);
+ s = addmod(s, tmp, umod);
+
+ r2 = s;
+
+ /* k = 2 */
+ s = *x1;
+
+ w = w3table[2];
+ tmp = MULMOD(*x2, w);
+ s = addmod(s, tmp, umod);
+
+ w = w3table[1];
+ tmp = MULMOD(*x3, w);
+ s = addmod(s, tmp, umod);
+
+ *x3 = s;
+ *x2 = r2;
+ *x1 = r1;
+}
+#else /* PPRO */
+static inline void
+ppro_size3_ntt(mpd_uint_t *x1, mpd_uint_t *x2, mpd_uint_t *x3, mpd_uint_t w3table[3],
+ mpd_uint_t umod, double *dmod, uint32_t dinvmod[3])
+{
+ mpd_uint_t r1, r2;
+ mpd_uint_t w;
+ mpd_uint_t s, tmp;
+
+
+ /* k = 0 -> w = 1 */
+ s = *x1;
+ s = addmod(s, *x2, umod);
+ s = addmod(s, *x3, umod);
+
+ r1 = s;
+
+ /* k = 1 */
+ s = *x1;
+
+ w = w3table[1];
+ tmp = ppro_mulmod(*x2, w, dmod, dinvmod);
+ s = addmod(s, tmp, umod);
+
+ w = w3table[2];
+ tmp = ppro_mulmod(*x3, w, dmod, dinvmod);
+ s = addmod(s, tmp, umod);
+
+ r2 = s;
+
+ /* k = 2 */
+ s = *x1;
+
+ w = w3table[2];
+ tmp = ppro_mulmod(*x2, w, dmod, dinvmod);
+ s = addmod(s, tmp, umod);
+
+ w = w3table[1];
+ tmp = ppro_mulmod(*x3, w, dmod, dinvmod);
+ s = addmod(s, tmp, umod);
+
+ *x3 = s;
+ *x2 = r2;
+ *x1 = r1;
+}
+#endif
+
+
+/* forward transform, sign = -1; transform length = 3 * 2**n */
+int
+four_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum)
+{
+ mpd_size_t R = 3; /* number of rows */
+ mpd_size_t C = n / 3; /* number of columns */
+ mpd_uint_t w3table[3];
+ mpd_uint_t kernel, w0, w1, wstep;
+ mpd_uint_t *s, *p0, *p1, *p2;
+ mpd_uint_t umod;
+#ifdef PPRO
+ double dmod;
+ uint32_t dinvmod[3];
+#endif
+ mpd_size_t i, k;
+
+
+ assert(n >= 48);
+ assert(n <= 3*MPD_MAXTRANSFORM_2N);
+
+
+ /* Length R transform on the columns. */
+ SETMODULUS(modnum);
+ _mpd_init_w3table(w3table, -1, modnum);
+ for (p0=a, p1=p0+C, p2=p0+2*C; p0<a+C; p0++,p1++,p2++) {
+
+ SIZE3_NTT(p0, p1, p2, w3table);
+ }
+
+ /* Multiply each matrix element (addressed by i*C+k) by r**(i*k). */
+ kernel = _mpd_getkernel(n, -1, modnum);
+ for (i = 1; i < R; i++) {
+ w0 = 1; /* r**(i*0): initial value for k=0 */
+ w1 = POWMOD(kernel, i); /* r**(i*1): initial value for k=1 */
+ wstep = MULMOD(w1, w1); /* r**(2*i) */
+ for (k = 0; k < C-1; k += 2) {
+ mpd_uint_t x0 = a[i*C+k];
+ mpd_uint_t x1 = a[i*C+k+1];
+ MULMOD2(&x0, w0, &x1, w1);
+ MULMOD2C(&w0, &w1, wstep); /* r**(i*(k+2)) = r**(i*k) * r**(2*i) */
+ a[i*C+k] = x0;
+ a[i*C+k+1] = x1;
+ }
+ }
+
+ /* Length C transform on the rows. */
+ for (s = a; s < a+n; s += C) {
+ if (!six_step_fnt(s, C, modnum)) {
+ return 0;
+ }
+ }
+
+#if 0 /* An unordered transform is sufficient for convolution. */
+ /* Transpose the matrix. */
+ transpose_3xpow2(a, R, C);
+#endif
+
+ return 1;
+}
+
+/* backward transform, sign = 1; transform length = 3 * 2**n */
+int
+inv_four_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum)
+{
+ mpd_size_t R = 3; /* number of rows */
+ mpd_size_t C = n / 3; /* number of columns */
+ mpd_uint_t w3table[3];
+ mpd_uint_t kernel, w0, w1, wstep;
+ mpd_uint_t *s, *p0, *p1, *p2;
+ mpd_uint_t umod;
+#ifdef PPRO
+ double dmod;
+ uint32_t dinvmod[3];
+#endif
+ mpd_size_t i, k;
+
+
+ assert(n >= 48);
+ assert(n <= 3*MPD_MAXTRANSFORM_2N);
+
+
+#if 0 /* An unordered transform is sufficient for convolution. */
+ /* Transpose the matrix, producing an R*C matrix. */
+ transpose_3xpow2(a, C, R);
+#endif
+
+ /* Length C transform on the rows. */
+ for (s = a; s < a+n; s += C) {
+ if (!inv_six_step_fnt(s, C, modnum)) {
+ return 0;
+ }
+ }
+
+ /* Multiply each matrix element (addressed by i*C+k) by r**(i*k). */
+ SETMODULUS(modnum);
+ kernel = _mpd_getkernel(n, 1, modnum);
+ for (i = 1; i < R; i++) {
+ w0 = 1;
+ w1 = POWMOD(kernel, i);
+ wstep = MULMOD(w1, w1);
+ for (k = 0; k < C; k += 2) {
+ mpd_uint_t x0 = a[i*C+k];
+ mpd_uint_t x1 = a[i*C+k+1];
+ MULMOD2(&x0, w0, &x1, w1);
+ MULMOD2C(&w0, &w1, wstep);
+ a[i*C+k] = x0;
+ a[i*C+k+1] = x1;
+ }
+ }
+
+ /* Length R transform on the columns. */
+ _mpd_init_w3table(w3table, 1, modnum);
+ for (p0=a, p1=p0+C, p2=p0+2*C; p0<a+C; p0++,p1++,p2++) {
+
+ SIZE3_NTT(p0, p1, p2, w3table);
+ }
+
+ return 1;
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef FOUR_STEP_H
+#define FOUR_STEP_H
+
+
+#include "mpdecimal.h"
+#include <stdio.h>
+
+
+int four_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum);
+int inv_four_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum);
+
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include "mpdecimal.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <assert.h>
+#include <errno.h>
+#include <locale.h>
+#include "bits.h"
+#include "constants.h"
+#include "memory.h"
+#include "typearith.h"
+#include "io.h"
+
+
+/* This file contains functions for decimal <-> string conversions, including
+ PEP-3101 formatting for numeric types. */
+
+
+/*
+ * Work around the behavior of tolower() and strcasecmp() in certain
+ * locales. For example, in tr_TR.utf8:
+ *
+ * tolower((unsigned char)'I') == 'I'
+ *
+ * u is the exact uppercase version of l; n is strlen(l) or strlen(l)+1
+ */
+static inline int
+_mpd_strneq(const char *s, const char *l, const char *u, size_t n)
+{
+ while (--n != SIZE_MAX) {
+ if (*s != *l && *s != *u) {
+ return 0;
+ }
+ s++; u++; l++;
+ }
+
+ return 1;
+}
+
+static mpd_ssize_t
+strtoexp(const char *s)
+{
+ char *end;
+ mpd_ssize_t retval;
+
+ errno = 0;
+ retval = mpd_strtossize(s, &end, 10);
+ if (errno == 0 && !(*s != '\0' && *end == '\0'))
+ errno = EINVAL;
+
+ return retval;
+}
+
+/*
+ * Scan 'len' words. The most significant word contains 'r' digits,
+ * the remaining words are full words. Skip dpoint. The string 's' must
+ * consist of digits and an optional single decimal point at 'dpoint'.
+ */
+static void
+string_to_coeff(mpd_uint_t *data, const char *s, const char *dpoint, int r,
+ size_t len)
+{
+ int j;
+
+ if (r > 0) {
+ data[--len] = 0;
+ for (j = 0; j < r; j++, s++) {
+ if (s == dpoint) s++;
+ data[len] = 10 * data[len] + (*s - '0');
+ }
+ }
+
+ while (--len != SIZE_MAX) {
+ data[len] = 0;
+ for (j = 0; j < MPD_RDIGITS; j++, s++) {
+ if (s == dpoint) s++;
+ data[len] = 10 * data[len] + (*s - '0');
+ }
+ }
+}
+
+/*
+ * Partially verify a numeric string of the form:
+ *
+ * [cdigits][.][cdigits][eE][+-][edigits]
+ *
+ * If successful, return a pointer to the location of the first
+ * relevant coefficient digit. This digit is either non-zero or
+ * part of one of the following patterns:
+ *
+ * ["0\x00", "0.\x00", "0.E", "0.e", "0E", "0e"]
+ *
+ * The locations of a single optional dot or indicator are stored
+ * in 'dpoint' and 'exp'.
+ *
+ * The end of the string is stored in 'end'. If an indicator [eE]
+ * occurs without trailing [edigits], the condition is caught
+ * later by strtoexp().
+ */
+static const char *
+scan_dpoint_exp(const char *s, const char **dpoint, const char **exp,
+ const char **end)
+{
+ const char *coeff = NULL;
+
+ *dpoint = NULL;
+ *exp = NULL;
+ for (; *s != '\0'; s++) {
+ switch (*s) {
+ case '.':
+ if (*dpoint != NULL || *exp != NULL)
+ return NULL;
+ *dpoint = s;
+ break;
+ case 'E': case 'e':
+ if (*exp != NULL)
+ return NULL;
+ *exp = s;
+ if (*(s+1) == '+' || *(s+1) == '-')
+ s++;
+ break;
+ default:
+ if (!isdigit((uchar)*s))
+ return NULL;
+ if (coeff == NULL && *exp == NULL) {
+ if (*s == '0') {
+ if (!isdigit((uchar)*(s+1)))
+ if (!(*(s+1) == '.' &&
+ isdigit((uchar)*(s+2))))
+ coeff = s;
+ }
+ else {
+ coeff = s;
+ }
+ }
+ break;
+
+ }
+ }
+
+ *end = s;
+ return coeff;
+}
+
+/* scan the payload of a NaN */
+static const char *
+scan_payload(const char *s, const char **end)
+{
+ const char *coeff;
+
+ while (*s == '0')
+ s++;
+ coeff = s;
+
+ while (isdigit((uchar)*s))
+ s++;
+ *end = s;
+
+ return (*s == '\0') ? coeff : NULL;
+}
+
+/* convert a character string to a decimal */
+void
+mpd_qset_string(mpd_t *dec, const char *s, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ mpd_ssize_t q, r, len;
+ const char *coeff, *end;
+ const char *dpoint = NULL, *exp = NULL;
+ size_t digits;
+ uint8_t sign = MPD_POS;
+
+ mpd_set_flags(dec, 0);
+ dec->len = 0;
+ dec->exp = 0;
+
+ /* sign */
+ if (*s == '+') {
+ s++;
+ }
+ else if (*s == '-') {
+ mpd_set_negative(dec);
+ sign = MPD_NEG;
+ s++;
+ }
+
+ if (_mpd_strneq(s, "nan", "NAN", 3)) { /* NaN */
+ s += 3;
+ mpd_setspecial(dec, sign, MPD_NAN);
+ if (*s == '\0')
+ return;
+ /* validate payload: digits only */
+ if ((coeff = scan_payload(s, &end)) == NULL)
+ goto conversion_error;
+ /* payload consists entirely of zeros */
+ if (*coeff == '\0')
+ return;
+ digits = end - coeff;
+ /* prec >= 1, clamp is 0 or 1 */
+ if (digits > (size_t)(ctx->prec-ctx->clamp))
+ goto conversion_error;
+ } /* sNaN */
+ else if (_mpd_strneq(s, "snan", "SNAN", 4)) {
+ s += 4;
+ mpd_setspecial(dec, sign, MPD_SNAN);
+ if (*s == '\0')
+ return;
+ /* validate payload: digits only */
+ if ((coeff = scan_payload(s, &end)) == NULL)
+ goto conversion_error;
+ /* payload consists entirely of zeros */
+ if (*coeff == '\0')
+ return;
+ digits = end - coeff;
+ if (digits > (size_t)(ctx->prec-ctx->clamp))
+ goto conversion_error;
+ }
+ else if (_mpd_strneq(s, "inf", "INF", 3)) {
+ s += 3;
+ if (*s == '\0' || _mpd_strneq(s, "inity", "INITY", 6)) {
+ /* numeric-value: infinity */
+ mpd_setspecial(dec, sign, MPD_INF);
+ return;
+ }
+ goto conversion_error;
+ }
+ else {
+ /* scan for start of coefficient, decimal point, indicator, end */
+ if ((coeff = scan_dpoint_exp(s, &dpoint, &exp, &end)) == NULL)
+ goto conversion_error;
+
+ /* numeric-value: [exponent-part] */
+ if (exp) {
+ /* exponent-part */
+ end = exp; exp++;
+ dec->exp = strtoexp(exp);
+ if (errno) {
+ if (!(errno == ERANGE &&
+ (dec->exp == MPD_SSIZE_MAX ||
+ dec->exp == MPD_SSIZE_MIN)))
+ goto conversion_error;
+ }
+ }
+
+ digits = end - coeff;
+ if (dpoint) {
+ size_t fracdigits = end-dpoint-1;
+ if (dpoint > coeff) digits--;
+
+ if (fracdigits > MPD_MAX_PREC) {
+ goto conversion_error;
+ }
+ if (dec->exp < MPD_SSIZE_MIN+(mpd_ssize_t)fracdigits) {
+ dec->exp = MPD_SSIZE_MIN;
+ }
+ else {
+ dec->exp -= (mpd_ssize_t)fracdigits;
+ }
+ }
+ if (digits > MPD_MAX_PREC) {
+ goto conversion_error;
+ }
+ if (dec->exp > MPD_EXP_INF) {
+ dec->exp = MPD_EXP_INF;
+ }
+ if (dec->exp == MPD_SSIZE_MIN) {
+ dec->exp = MPD_SSIZE_MIN+1;
+ }
+ }
+
+ _mpd_idiv_word(&q, &r, (mpd_ssize_t)digits, MPD_RDIGITS);
+
+ len = (r == 0) ? q : q+1;
+ if (len == 0) {
+ goto conversion_error; /* GCOV_NOT_REACHED */
+ }
+ if (!mpd_qresize(dec, len, status)) {
+ mpd_seterror(dec, MPD_Malloc_error, status);
+ return;
+ }
+ dec->len = len;
+
+ string_to_coeff(dec->data, coeff, dpoint, (int)r, len);
+
+ mpd_setdigits(dec);
+ mpd_qfinalize(dec, ctx, status);
+ return;
+
+conversion_error:
+ /* standard wants a positive NaN */
+ mpd_seterror(dec, MPD_Conversion_syntax, status);
+}
+
+/* Print word x with n decimal digits to string s. dot is either NULL
+ or the location of a decimal point. */
+#define EXTRACT_DIGIT(s, x, d, dot) \
+ if (s == dot) *s++ = '.'; *s++ = '0' + (char)(x / d); x %= d
+static inline char *
+word_to_string(char *s, mpd_uint_t x, int n, char *dot)
+{
+ switch(n) {
+#ifdef CONFIG_64
+ case 20: EXTRACT_DIGIT(s, x, 10000000000000000000ULL, dot); /* GCOV_NOT_REACHED */
+ case 19: EXTRACT_DIGIT(s, x, 1000000000000000000ULL, dot);
+ case 18: EXTRACT_DIGIT(s, x, 100000000000000000ULL, dot);
+ case 17: EXTRACT_DIGIT(s, x, 10000000000000000ULL, dot);
+ case 16: EXTRACT_DIGIT(s, x, 1000000000000000ULL, dot);
+ case 15: EXTRACT_DIGIT(s, x, 100000000000000ULL, dot);
+ case 14: EXTRACT_DIGIT(s, x, 10000000000000ULL, dot);
+ case 13: EXTRACT_DIGIT(s, x, 1000000000000ULL, dot);
+ case 12: EXTRACT_DIGIT(s, x, 100000000000ULL, dot);
+ case 11: EXTRACT_DIGIT(s, x, 10000000000ULL, dot);
+#endif
+ case 10: EXTRACT_DIGIT(s, x, 1000000000UL, dot);
+ case 9: EXTRACT_DIGIT(s, x, 100000000UL, dot);
+ case 8: EXTRACT_DIGIT(s, x, 10000000UL, dot);
+ case 7: EXTRACT_DIGIT(s, x, 1000000UL, dot);
+ case 6: EXTRACT_DIGIT(s, x, 100000UL, dot);
+ case 5: EXTRACT_DIGIT(s, x, 10000UL, dot);
+ case 4: EXTRACT_DIGIT(s, x, 1000UL, dot);
+ case 3: EXTRACT_DIGIT(s, x, 100UL, dot);
+ case 2: EXTRACT_DIGIT(s, x, 10UL, dot);
+ default: if (s == dot) *s++ = '.'; *s++ = '0' + (char)x;
+ }
+
+ *s = '\0';
+ return s;
+}
+
+/* Print exponent x to string s. Undefined for MPD_SSIZE_MIN. */
+static inline char *
+exp_to_string(char *s, mpd_ssize_t x)
+{
+ char sign = '+';
+
+ if (x < 0) {
+ sign = '-';
+ x = -x;
+ }
+ *s++ = sign;
+
+ return word_to_string(s, x, mpd_word_digits(x), NULL);
+}
+
+/* Print the coefficient of dec to string s. len(dec) > 0. */
+static inline char *
+coeff_to_string(char *s, const mpd_t *dec)
+{
+ mpd_uint_t x;
+ mpd_ssize_t i;
+
+ /* most significant word */
+ x = mpd_msword(dec);
+ s = word_to_string(s, x, mpd_word_digits(x), NULL);
+
+ /* remaining full words */
+ for (i=dec->len-2; i >= 0; --i) {
+ x = dec->data[i];
+ s = word_to_string(s, x, MPD_RDIGITS, NULL);
+ }
+
+ return s;
+}
+
+/* Print the coefficient of dec to string s. len(dec) > 0. dot is either
+ NULL or a pointer to the location of a decimal point. */
+static inline char *
+coeff_to_string_dot(char *s, char *dot, const mpd_t *dec)
+{
+ mpd_uint_t x;
+ mpd_ssize_t i;
+
+ /* most significant word */
+ x = mpd_msword(dec);
+ s = word_to_string(s, x, mpd_word_digits(x), dot);
+
+ /* remaining full words */
+ for (i=dec->len-2; i >= 0; --i) {
+ x = dec->data[i];
+ s = word_to_string(s, x, MPD_RDIGITS, dot);
+ }
+
+ return s;
+}
+
+/* Format type */
+#define MPD_FMT_LOWER 0x00000000
+#define MPD_FMT_UPPER 0x00000001
+#define MPD_FMT_TOSCI 0x00000002
+#define MPD_FMT_TOENG 0x00000004
+#define MPD_FMT_EXP 0x00000008
+#define MPD_FMT_FIXED 0x00000010
+#define MPD_FMT_PERCENT 0x00000020
+#define MPD_FMT_SIGN_SPACE 0x00000040
+#define MPD_FMT_SIGN_PLUS 0x00000080
+
+/* Default place of the decimal point for MPD_FMT_TOSCI, MPD_FMT_EXP */
+#define MPD_DEFAULT_DOTPLACE 1
+
+/*
+ * Set *result to the string representation of a decimal. Return the length
+ * of *result, not including the terminating '\0' character.
+ *
+ * Formatting is done according to 'flags'. A return value of -1 with *result
+ * set to NULL indicates MPD_Malloc_error.
+ *
+ * 'dplace' is the default place of the decimal point. It is always set to
+ * MPD_DEFAULT_DOTPLACE except for zeros in combination with MPD_FMT_EXP.
+ */
+static mpd_ssize_t
+_mpd_to_string(char **result, const mpd_t *dec, int flags, mpd_ssize_t dplace)
+{
+ char *decstring = NULL, *cp = NULL;
+ mpd_ssize_t ldigits;
+ mpd_ssize_t mem = 0, k;
+
+ if (mpd_isspecial(dec)) {
+
+ mem = sizeof "-Infinity";
+ if (mpd_isnan(dec) && dec->len > 0) {
+ /* diagnostic code */
+ mem += dec->digits;
+ }
+ cp = decstring = mpd_alloc(mem, sizeof *decstring);
+ if (cp == NULL) {
+ *result = NULL;
+ return -1;
+ }
+
+ if (mpd_isnegative(dec)) {
+ *cp++ = '-';
+ }
+ else if (flags&MPD_FMT_SIGN_SPACE) {
+ *cp++ = ' ';
+ }
+ else if (flags&MPD_FMT_SIGN_PLUS) {
+ *cp++ = '+';
+ }
+
+ if (mpd_isnan(dec)) {
+ if (mpd_isqnan(dec)) {
+ strcpy(cp, "NaN");
+ cp += 3;
+ }
+ else {
+ strcpy(cp, "sNaN");
+ cp += 4;
+ }
+ if (dec->len > 0) { /* diagnostic code */
+ cp = coeff_to_string(cp, dec);
+ }
+ }
+ else if (mpd_isinfinite(dec)) {
+ strcpy(cp, "Infinity");
+ cp += 8;
+ }
+ else { /* debug */
+ abort(); /* GCOV_NOT_REACHED */
+ }
+ }
+ else {
+ assert(dec->len > 0);
+
+ /*
+ * For easier manipulation of the decimal point's location
+ * and the exponent that is finally printed, the number is
+ * rescaled to a virtual representation with exp = 0. Here
+ * ldigits denotes the number of decimal digits to the left
+ * of the decimal point and remains constant once initialized.
+ *
+ * dplace is the location of the decimal point relative to
+ * the start of the coefficient. Note that 3) always holds
+ * when dplace is shifted.
+ *
+ * 1) ldigits := dec->digits - dec->exp
+ * 2) dplace := ldigits (initially)
+ * 3) exp := ldigits - dplace (initially exp = 0)
+ *
+ * 0.00000_.____._____000000.
+ * ^ ^ ^ ^
+ * | | | |
+ * | | | `- dplace >= digits
+ * | | `- dplace in the middle of the coefficient
+ * | ` dplace = 1 (after the first coefficient digit)
+ * `- dplace <= 0
+ */
+
+ ldigits = dec->digits + dec->exp;
+
+ if (flags&MPD_FMT_EXP) {
+ ;
+ }
+ else if (flags&MPD_FMT_FIXED || (dec->exp <= 0 && ldigits > -6)) {
+ /* MPD_FMT_FIXED: always use fixed point notation.
+ * MPD_FMT_TOSCI, MPD_FMT_TOENG: for a certain range,
+ * override exponent notation. */
+ dplace = ldigits;
+ }
+ else if (flags&MPD_FMT_TOENG) {
+ if (mpd_iszero(dec)) {
+ /* If the exponent is divisible by three,
+ * dplace = 1. Otherwise, move dplace one
+ * or two places to the left. */
+ dplace = -1 + mod_mpd_ssize_t(dec->exp+2, 3);
+ }
+ else { /* ldigits-1 is the adjusted exponent, which
+ * should be divisible by three. If not, move
+ * dplace one or two places to the right. */
+ dplace += mod_mpd_ssize_t(ldigits-1, 3);
+ }
+ }
+
+ /*
+ * Basic space requirements:
+ *
+ * [-][.][coeffdigits][E][-][expdigits+1][%]['\0']
+ *
+ * If the decimal point lies outside of the coefficient digits,
+ * space is adjusted accordingly.
+ */
+ if (dplace <= 0) {
+ mem = -dplace + dec->digits + 2;
+ }
+ else if (dplace >= dec->digits) {
+ mem = dplace;
+ }
+ else {
+ mem = dec->digits;
+ }
+ mem += (MPD_EXPDIGITS+1+6);
+
+ cp = decstring = mpd_alloc(mem, sizeof *decstring);
+ if (cp == NULL) {
+ *result = NULL;
+ return -1;
+ }
+
+
+ if (mpd_isnegative(dec)) {
+ *cp++ = '-';
+ }
+ else if (flags&MPD_FMT_SIGN_SPACE) {
+ *cp++ = ' ';
+ }
+ else if (flags&MPD_FMT_SIGN_PLUS) {
+ *cp++ = '+';
+ }
+
+ if (dplace <= 0) {
+ /* space: -dplace+dec->digits+2 */
+ *cp++ = '0';
+ *cp++ = '.';
+ for (k = 0; k < -dplace; k++) {
+ *cp++ = '0';
+ }
+ cp = coeff_to_string(cp, dec);
+ }
+ else if (dplace >= dec->digits) {
+ /* space: dplace */
+ cp = coeff_to_string(cp, dec);
+ for (k = 0; k < dplace-dec->digits; k++) {
+ *cp++ = '0';
+ }
+ }
+ else {
+ /* space: dec->digits+1 */
+ cp = coeff_to_string_dot(cp, cp+dplace, dec);
+ }
+
+ /*
+ * Conditions for printing an exponent:
+ *
+ * MPD_FMT_TOSCI, MPD_FMT_TOENG: only if ldigits != dplace
+ * MPD_FMT_FIXED: never (ldigits == dplace)
+ * MPD_FMT_EXP: always
+ */
+ if (ldigits != dplace || flags&MPD_FMT_EXP) {
+ /* space: expdigits+2 */
+ *cp++ = (flags&MPD_FMT_UPPER) ? 'E' : 'e';
+ cp = exp_to_string(cp, ldigits-dplace);
+ }
+
+ if (flags&MPD_FMT_PERCENT) {
+ *cp++ = '%';
+ }
+ }
+
+ assert(cp < decstring+mem);
+ assert(cp-decstring < MPD_SSIZE_MAX);
+
+ *cp = '\0';
+ *result = decstring;
+ return (mpd_ssize_t)(cp-decstring);
+}
+
+char *
+mpd_to_sci(const mpd_t *dec, int fmt)
+{
+ char *res;
+ int flags = MPD_FMT_TOSCI;
+
+ flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER;
+ (void)_mpd_to_string(&res, dec, flags, MPD_DEFAULT_DOTPLACE);
+ return res;
+}
+
+char *
+mpd_to_eng(const mpd_t *dec, int fmt)
+{
+ char *res;
+ int flags = MPD_FMT_TOENG;
+
+ flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER;
+ (void)_mpd_to_string(&res, dec, flags, MPD_DEFAULT_DOTPLACE);
+ return res;
+}
+
+mpd_ssize_t
+mpd_to_sci_size(char **res, const mpd_t *dec, int fmt)
+{
+ int flags = MPD_FMT_TOSCI;
+
+ flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER;
+ return _mpd_to_string(res, dec, flags, MPD_DEFAULT_DOTPLACE);
+}
+
+mpd_ssize_t
+mpd_to_eng_size(char **res, const mpd_t *dec, int fmt)
+{
+ int flags = MPD_FMT_TOENG;
+
+ flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER;
+ return _mpd_to_string(res, dec, flags, MPD_DEFAULT_DOTPLACE);
+}
+
+/* Copy a single UTF-8 char to dest. See: The Unicode Standard, version 5.2,
+ chapter 3.9: Well-formed UTF-8 byte sequences. */
+static int
+_mpd_copy_utf8(char dest[5], const char *s)
+{
+ const uchar *cp = (const uchar *)s;
+ uchar lb, ub;
+ int count, i;
+
+
+ if (*cp == 0) {
+ /* empty string */
+ dest[0] = '\0';
+ return 0;
+ }
+ else if (*cp <= 0x7f) {
+ /* ascii */
+ dest[0] = *cp;
+ dest[1] = '\0';
+ return 1;
+ }
+ else if (0xc2 <= *cp && *cp <= 0xdf) {
+ lb = 0x80; ub = 0xbf;
+ count = 2;
+ }
+ else if (*cp == 0xe0) {
+ lb = 0xa0; ub = 0xbf;
+ count = 3;
+ }
+ else if (*cp <= 0xec) {
+ lb = 0x80; ub = 0xbf;
+ count = 3;
+ }
+ else if (*cp == 0xed) {
+ lb = 0x80; ub = 0x9f;
+ count = 3;
+ }
+ else if (*cp <= 0xef) {
+ lb = 0x80; ub = 0xbf;
+ count = 3;
+ }
+ else if (*cp == 0xf0) {
+ lb = 0x90; ub = 0xbf;
+ count = 4;
+ }
+ else if (*cp <= 0xf3) {
+ lb = 0x80; ub = 0xbf;
+ count = 4;
+ }
+ else if (*cp == 0xf4) {
+ lb = 0x80; ub = 0x8f;
+ count = 4;
+ }
+ else {
+ /* invalid */
+ goto error;
+ }
+
+ dest[0] = *cp++;
+ if (*cp < lb || ub < *cp) {
+ goto error;
+ }
+ dest[1] = *cp++;
+ for (i = 2; i < count; i++) {
+ if (*cp < 0x80 || 0xbf < *cp) {
+ goto error;
+ }
+ dest[i] = *cp++;
+ }
+ dest[i] = '\0';
+
+ return count;
+
+error:
+ dest[0] = '\0';
+ return -1;
+}
+
+int
+mpd_validate_lconv(mpd_spec_t *spec)
+{
+ size_t n;
+#if CHAR_MAX == SCHAR_MAX
+ const char *cp = spec->grouping;
+ while (*cp != '\0') {
+ if (*cp++ < 0) {
+ return -1;
+ }
+ }
+#endif
+ n = strlen(spec->dot);
+ if (n == 0 || n > 4) {
+ return -1;
+ }
+ if (strlen(spec->sep) > 4) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+mpd_parse_fmt_str(mpd_spec_t *spec, const char *fmt, int caps)
+{
+ char *cp = (char *)fmt;
+ int have_align = 0, n;
+
+ /* defaults */
+ spec->min_width = 0;
+ spec->prec = -1;
+ spec->type = caps ? 'G' : 'g';
+ spec->align = '>';
+ spec->sign = '-';
+ spec->dot = "";
+ spec->sep = "";
+ spec->grouping = "";
+
+
+ /* presume that the first character is a UTF-8 fill character */
+ if ((n = _mpd_copy_utf8(spec->fill, cp)) < 0) {
+ return 0;
+ }
+
+ /* alignment directive, prefixed by a fill character */
+ if (*cp && (*(cp+n) == '<' || *(cp+n) == '>' ||
+ *(cp+n) == '=' || *(cp+n) == '^')) {
+ cp += n;
+ spec->align = *cp++;
+ have_align = 1;
+ } /* alignment directive */
+ else {
+ /* default fill character */
+ spec->fill[0] = ' ';
+ spec->fill[1] = '\0';
+ if (*cp == '<' || *cp == '>' ||
+ *cp == '=' || *cp == '^') {
+ spec->align = *cp++;
+ have_align = 1;
+ }
+ }
+
+ /* sign formatting */
+ if (*cp == '+' || *cp == '-' || *cp == ' ') {
+ spec->sign = *cp++;
+ }
+
+ /* zero padding */
+ if (*cp == '0') {
+ /* zero padding implies alignment, which should not be
+ * specified twice. */
+ if (have_align) {
+ return 0;
+ }
+ spec->align = 'z';
+ spec->fill[0] = *cp++;
+ spec->fill[1] = '\0';
+ }
+
+ /* minimum width */
+ if (isdigit((uchar)*cp)) {
+ if (*cp == '0') {
+ return 0;
+ }
+ errno = 0;
+ spec->min_width = mpd_strtossize(cp, &cp, 10);
+ if (errno == ERANGE || errno == EINVAL) {
+ return 0;
+ }
+ }
+
+ /* thousands separator */
+ if (*cp == ',') {
+ spec->dot = ".";
+ spec->sep = ",";
+ spec->grouping = "\003\003";
+ cp++;
+ }
+
+ /* fraction digits or significant digits */
+ if (*cp == '.') {
+ cp++;
+ if (!isdigit((uchar)*cp)) {
+ return 0;
+ }
+ errno = 0;
+ spec->prec = mpd_strtossize(cp, &cp, 10);
+ if (errno == ERANGE || errno == EINVAL) {
+ return 0;
+ }
+ }
+
+ /* type */
+ if (*cp == 'E' || *cp == 'e' || *cp == 'F' || *cp == 'f' ||
+ *cp == 'G' || *cp == 'g' || *cp == '%') {
+ spec->type = *cp++;
+ }
+ else if (*cp == 'N' || *cp == 'n') {
+ /* locale specific conversion */
+ struct lconv *lc;
+ /* separator has already been specified */
+ if (*spec->sep) {
+ return 0;
+ }
+ spec->type = *cp++;
+ spec->type = (spec->type == 'N') ? 'G' : 'g';
+ lc = localeconv();
+ spec->dot = lc->decimal_point;
+ spec->sep = lc->thousands_sep;
+ spec->grouping = lc->grouping;
+ if (mpd_validate_lconv(spec) < 0) {
+ return 0; /* GCOV_NOT_REACHED */
+ }
+ }
+
+ /* check correctness */
+ if (*cp != '\0') {
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * The following functions assume that spec->min_width <= MPD_MAX_PREC, which
+ * is made sure in mpd_qformat_spec. Then, even with a spec that inserts a
+ * four-byte separator after each digit, nbytes in the following struct
+ * cannot overflow.
+ */
+
+/* Multibyte string */
+typedef struct {
+ mpd_ssize_t nbytes; /* length in bytes */
+ mpd_ssize_t nchars; /* length in chars */
+ mpd_ssize_t cur; /* current write index */
+ char *data;
+} mpd_mbstr_t;
+
+static inline void
+_mpd_bcopy(char *dest, const char *src, mpd_ssize_t n)
+{
+ while (--n >= 0) {
+ dest[n] = src[n];
+ }
+}
+
+static inline void
+_mbstr_copy_char(mpd_mbstr_t *dest, const char *src, mpd_ssize_t n)
+{
+ dest->nbytes += n;
+ dest->nchars += (n > 0 ? 1 : 0);
+ dest->cur -= n;
+
+ if (dest->data != NULL) {
+ _mpd_bcopy(dest->data+dest->cur, src, n);
+ }
+}
+
+static inline void
+_mbstr_copy_ascii(mpd_mbstr_t *dest, const char *src, mpd_ssize_t n)
+{
+ dest->nbytes += n;
+ dest->nchars += n;
+ dest->cur -= n;
+
+ if (dest->data != NULL) {
+ _mpd_bcopy(dest->data+dest->cur, src, n);
+ }
+}
+
+static inline void
+_mbstr_copy_pad(mpd_mbstr_t *dest, mpd_ssize_t n)
+{
+ dest->nbytes += n;
+ dest->nchars += n;
+ dest->cur -= n;
+
+ if (dest->data != NULL) {
+ char *cp = dest->data + dest->cur;
+ while (--n >= 0) {
+ cp[n] = '0';
+ }
+ }
+}
+
+/*
+ * Copy a numeric string to dest->data, adding separators in the integer
+ * part according to spec->grouping. If leading zero padding is enabled
+ * and the result is smaller than spec->min_width, continue adding zeros
+ * and separators until the minimum width is reached.
+ *
+ * The final length of dest->data is stored in dest->nbytes. The number
+ * of UTF-8 characters is stored in dest->nchars.
+ *
+ * First run (dest->data == NULL): determine the length of the result
+ * string and store it in dest->nbytes.
+ *
+ * Second run (write to dest->data): data is written in chunks and in
+ * reverse order, starting with the rest of the numeric string.
+ */
+static void
+_mpd_add_sep_dot(mpd_mbstr_t *dest,
+ const char *sign, /* location of optional sign */
+ const char *src, mpd_ssize_t n_src, /* integer part and length */
+ const char *dot, /* location of optional decimal point */
+ const char *rest, mpd_ssize_t n_rest, /* remaining part and length */
+ const mpd_spec_t *spec)
+{
+ mpd_ssize_t n_sep, n_sign, consume;
+ const char *g;
+ int pad = 0;
+
+ n_sign = sign ? 1 : 0;
+ n_sep = (mpd_ssize_t)strlen(spec->sep);
+ /* Initial write index: set to location of '\0' in the output string.
+ * Irrelevant for the first run. */
+ dest->cur = dest->nbytes;
+ dest->nbytes = dest->nchars = 0;
+
+ _mbstr_copy_ascii(dest, rest, n_rest);
+
+ if (dot) {
+ _mbstr_copy_char(dest, dot, (mpd_ssize_t)strlen(dot));
+ }
+
+ g = spec->grouping;
+ consume = *g;
+ while (1) {
+ /* If the group length is 0 or CHAR_MAX or greater than the
+ * number of source bytes, consume all remaining bytes. */
+ if (*g == 0 || *g == CHAR_MAX || consume > n_src) {
+ consume = n_src;
+ }
+ n_src -= consume;
+ if (pad) {
+ _mbstr_copy_pad(dest, consume);
+ }
+ else {
+ _mbstr_copy_ascii(dest, src+n_src, consume);
+ }
+
+ if (n_src == 0) {
+ /* Either the real source of intpart digits or the virtual
+ * source of padding zeros is exhausted. */
+ if (spec->align == 'z' &&
+ dest->nchars + n_sign < spec->min_width) {
+ /* Zero padding is set and length < min_width:
+ * Generate n_src additional characters. */
+ n_src = spec->min_width - (dest->nchars + n_sign);
+ /* Next iteration:
+ * case *g == 0 || *g == CHAR_MAX:
+ * consume all padding characters
+ * case consume < g*:
+ * fill remainder of current group
+ * case consume == g*
+ * copying is a no-op */
+ consume = *g - consume;
+ /* Switch on virtual source of zeros. */
+ pad = 1;
+ continue;
+ }
+ break;
+ }
+
+ if (n_sep > 0) {
+ /* If padding is switched on, separators are counted
+ * as padding characters. This rule does not apply if
+ * the separator would be the first character of the
+ * result string. */
+ if (pad && n_src > 1) n_src -= 1;
+ _mbstr_copy_char(dest, spec->sep, n_sep);
+ }
+
+ /* If non-NUL, use the next value for grouping. */
+ if (*g && *(g+1)) g++;
+ consume = *g;
+ }
+
+ if (sign) {
+ _mbstr_copy_ascii(dest, sign, 1);
+ }
+
+ if (dest->data) {
+ dest->data[dest->nbytes] = '\0';
+ }
+}
+
+/*
+ * Convert a numeric-string to its locale-specific appearance.
+ * The string must have one of these forms:
+ *
+ * 1) [sign] digits [exponent-part]
+ * 2) [sign] digits '.' [digits] [exponent-part]
+ *
+ * Not allowed, since _mpd_to_string() never returns this form:
+ *
+ * 3) [sign] '.' digits [exponent-part]
+ *
+ * Input: result->data := original numeric string (ASCII)
+ * result->bytes := strlen(result->data)
+ * result->nchars := strlen(result->data)
+ *
+ * Output: result->data := modified or original string
+ * result->bytes := strlen(result->data)
+ * result->nchars := number of characters (possibly UTF-8)
+ */
+static int
+_mpd_apply_lconv(mpd_mbstr_t *result, const mpd_spec_t *spec, uint32_t *status)
+{
+ const char *sign = NULL, *intpart = NULL, *dot = NULL;
+ const char *rest, *dp;
+ char *decstring;
+ mpd_ssize_t n_int, n_rest;
+
+ /* original numeric string */
+ dp = result->data;
+
+ /* sign */
+ if (*dp == '+' || *dp == '-' || *dp == ' ') {
+ sign = dp++;
+ }
+ /* integer part */
+ assert(isdigit((uchar)*dp));
+ intpart = dp++;
+ while (isdigit((uchar)*dp)) {
+ dp++;
+ }
+ n_int = (mpd_ssize_t)(dp-intpart);
+ /* decimal point */
+ if (*dp == '.') {
+ dp++; dot = spec->dot;
+ }
+ /* rest */
+ rest = dp;
+ n_rest = result->nbytes - (mpd_ssize_t)(dp-result->data);
+
+ if (dot == NULL && (*spec->sep == '\0' || *spec->grouping == '\0')) {
+ /* _mpd_add_sep_dot() would not change anything */
+ return 1;
+ }
+
+ /* Determine the size of the new decimal string after inserting the
+ * decimal point, optional separators and optional padding. */
+ decstring = result->data;
+ result->data = NULL;
+ _mpd_add_sep_dot(result, sign, intpart, n_int, dot,
+ rest, n_rest, spec);
+
+ result->data = mpd_alloc(result->nbytes+1, 1);
+ if (result->data == NULL) {
+ *status |= MPD_Malloc_error;
+ mpd_free(decstring);
+ return 0;
+ }
+
+ /* Perform actual writes. */
+ _mpd_add_sep_dot(result, sign, intpart, n_int, dot,
+ rest, n_rest, spec);
+
+ mpd_free(decstring);
+ return 1;
+}
+
+/* Add padding to the formatted string if necessary. */
+static int
+_mpd_add_pad(mpd_mbstr_t *result, const mpd_spec_t *spec, uint32_t *status)
+{
+ if (result->nchars < spec->min_width) {
+ mpd_ssize_t add_chars, add_bytes;
+ size_t lpad = 0, rpad = 0;
+ size_t n_fill, len, i, j;
+ char align = spec->align;
+ uint8_t err = 0;
+ char *cp;
+
+ n_fill = strlen(spec->fill);
+ add_chars = (spec->min_width - result->nchars);
+ /* max value: MPD_MAX_PREC * 4 */
+ add_bytes = add_chars * (mpd_ssize_t)n_fill;
+
+ cp = result->data = mpd_realloc(result->data,
+ result->nbytes+add_bytes+1,
+ sizeof *result->data, &err);
+ if (err) {
+ *status |= MPD_Malloc_error;
+ mpd_free(result->data);
+ return 0;
+ }
+
+ if (align == 'z') {
+ align = '=';
+ }
+
+ if (align == '<') {
+ rpad = add_chars;
+ }
+ else if (align == '>' || align == '=') {
+ lpad = add_chars;
+ }
+ else { /* align == '^' */
+ lpad = add_chars/2;
+ rpad = add_chars-lpad;
+ }
+
+ len = result->nbytes;
+ if (align == '=' && (*cp == '-' || *cp == '+' || *cp == ' ')) {
+ /* leave sign in the leading position */
+ cp++; len--;
+ }
+
+ memmove(cp+n_fill*lpad, cp, len);
+ for (i = 0; i < lpad; i++) {
+ for (j = 0; j < n_fill; j++) {
+ cp[i*n_fill+j] = spec->fill[j];
+ }
+ }
+ cp += (n_fill*lpad + len);
+ for (i = 0; i < rpad; i++) {
+ for (j = 0; j < n_fill; j++) {
+ cp[i*n_fill+j] = spec->fill[j];
+ }
+ }
+
+ result->nbytes += add_bytes;
+ result->nchars += add_chars;
+ result->data[result->nbytes] = '\0';
+ }
+
+ return 1;
+}
+
+/* Round a number to prec digits. The adjusted exponent stays the same
+ or increases by one if rounding up crosses a power of ten boundary.
+ If result->digits would exceed MPD_MAX_PREC+1, MPD_Invalid_operation
+ is set and the result is NaN. */
+static inline void
+_mpd_round(mpd_t *result, const mpd_t *a, mpd_ssize_t prec,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_ssize_t exp = a->exp + a->digits - prec;
+
+ if (prec <= 0) {
+ mpd_seterror(result, MPD_Invalid_operation, status); /* GCOV_NOT_REACHED */
+ return; /* GCOV_NOT_REACHED */
+ }
+ if (mpd_isspecial(a) || mpd_iszero(a)) {
+ mpd_qcopy(result, a, status); /* GCOV_NOT_REACHED */
+ return; /* GCOV_NOT_REACHED */
+ }
+
+ mpd_qrescale_fmt(result, a, exp, ctx, status);
+ if (result->digits > prec) {
+ mpd_qrescale_fmt(result, result, exp+1, ctx, status);
+ }
+}
+
+/*
+ * Return the string representation of an mpd_t, formatted according to 'spec'.
+ * The format specification is assumed to be valid. Memory errors are indicated
+ * as usual. This function is quiet.
+ */
+char *
+mpd_qformat_spec(const mpd_t *dec, const mpd_spec_t *spec,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_uint_t dt[MPD_MINALLOC_MAX];
+ mpd_t tmp = {MPD_STATIC|MPD_STATIC_DATA,0,0,0,MPD_MINALLOC_MAX,dt};
+ mpd_ssize_t dplace = MPD_DEFAULT_DOTPLACE;
+ mpd_mbstr_t result;
+ mpd_spec_t stackspec;
+ char type = spec->type;
+ int flags = 0;
+
+
+ if (spec->min_width > MPD_MAX_PREC) {
+ *status |= MPD_Invalid_operation;
+ return NULL;
+ }
+
+ if (isupper((uchar)type)) {
+ type = tolower((uchar)type);
+ flags |= MPD_FMT_UPPER;
+ }
+ if (spec->sign == ' ') {
+ flags |= MPD_FMT_SIGN_SPACE;
+ }
+ else if (spec->sign == '+') {
+ flags |= MPD_FMT_SIGN_PLUS;
+ }
+
+ if (mpd_isspecial(dec)) {
+ if (spec->align == 'z') {
+ stackspec = *spec;
+ stackspec.fill[0] = ' ';
+ stackspec.fill[1] = '\0';
+ stackspec.align = '>';
+ spec = &stackspec;
+ }
+ }
+ else {
+ uint32_t workstatus = 0;
+ mpd_ssize_t prec;
+
+ switch (type) {
+ case 'g': flags |= MPD_FMT_TOSCI; break;
+ case 'e': flags |= MPD_FMT_EXP; break;
+ case '%': flags |= MPD_FMT_PERCENT;
+ if (!mpd_qcopy(&tmp, dec, status)) {
+ return NULL;
+ }
+ tmp.exp += 2;
+ dec = &tmp;
+ type = 'f'; /* fall through */
+ case 'f': flags |= MPD_FMT_FIXED; break;
+ default: abort(); /* debug: GCOV_NOT_REACHED */
+ }
+
+ if (spec->prec >= 0) {
+ if (spec->prec > MPD_MAX_PREC) {
+ *status |= MPD_Invalid_operation;
+ goto error;
+ }
+
+ switch (type) {
+ case 'g':
+ prec = (spec->prec == 0) ? 1 : spec->prec;
+ if (dec->digits > prec) {
+ _mpd_round(&tmp, dec, prec, ctx,
+ &workstatus);
+ dec = &tmp;
+ }
+ break;
+ case 'e':
+ if (mpd_iszero(dec)) {
+ dplace = 1-spec->prec;
+ }
+ else {
+ _mpd_round(&tmp, dec, spec->prec+1, ctx,
+ &workstatus);
+ dec = &tmp;
+ }
+ break;
+ case 'f':
+ mpd_qrescale(&tmp, dec, -spec->prec, ctx,
+ &workstatus);
+ dec = &tmp;
+ break;
+ }
+ }
+
+ if (type == 'f') {
+ if (mpd_iszero(dec) && dec->exp > 0) {
+ mpd_qrescale(&tmp, dec, 0, ctx, &workstatus);
+ dec = &tmp;
+ }
+ }
+
+ if (workstatus&MPD_Errors) {
+ *status |= (workstatus&MPD_Errors);
+ goto error;
+ }
+ }
+
+ /*
+ * At this point, for all scaled or non-scaled decimals:
+ * 1) 1 <= digits <= MAX_PREC+1
+ * 2) adjexp(scaled) = adjexp(orig) [+1]
+ * 3) case 'g': MIN_ETINY <= exp <= MAX_EMAX+1
+ * case 'e': MIN_ETINY-MAX_PREC <= exp <= MAX_EMAX+1
+ * case 'f': MIN_ETINY <= exp <= MAX_EMAX+1
+ * 4) max memory alloc in _mpd_to_string:
+ * case 'g': MAX_PREC+36
+ * case 'e': MAX_PREC+36
+ * case 'f': 2*MPD_MAX_PREC+30
+ */
+ result.nbytes = _mpd_to_string(&result.data, dec, flags, dplace);
+ result.nchars = result.nbytes;
+ if (result.nbytes < 0) {
+ *status |= MPD_Malloc_error;
+ goto error;
+ }
+
+ if (*spec->dot != '\0' && !mpd_isspecial(dec)) {
+ if (result.nchars > MPD_MAX_PREC+36) {
+ /* Since a group length of one is not explicitly
+ * disallowed, ensure that it is always possible to
+ * insert a four byte separator after each digit. */
+ *status |= MPD_Invalid_operation;
+ mpd_free(result.data);
+ goto error;
+ }
+ if (!_mpd_apply_lconv(&result, spec, status)) {
+ goto error;
+ }
+ }
+
+ if (spec->min_width) {
+ if (!_mpd_add_pad(&result, spec, status)) {
+ goto error;
+ }
+ }
+
+ mpd_del(&tmp);
+ return result.data;
+
+error:
+ mpd_del(&tmp);
+ return NULL;
+}
+
+char *
+mpd_qformat(const mpd_t *dec, const char *fmt, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ mpd_spec_t spec;
+
+ if (!mpd_parse_fmt_str(&spec, fmt, 1)) {
+ *status |= MPD_Invalid_operation;
+ return NULL;
+ }
+
+ return mpd_qformat_spec(dec, &spec, ctx, status);
+}
+
+/*
+ * The specification has a *condition* called Invalid_operation and an
+ * IEEE *signal* called Invalid_operation. The former corresponds to
+ * MPD_Invalid_operation, the latter to MPD_IEEE_Invalid_operation.
+ * MPD_IEEE_Invalid_operation comprises the following conditions:
+ *
+ * [MPD_Conversion_syntax, MPD_Division_impossible, MPD_Division_undefined,
+ * MPD_Fpu_error, MPD_Invalid_context, MPD_Invalid_operation,
+ * MPD_Malloc_error]
+ *
+ * In the following functions, 'flag' denotes the condition, 'signal'
+ * denotes the IEEE signal.
+ */
+
+static const char *mpd_flag_string[MPD_NUM_FLAGS] = {
+ "Clamped",
+ "Conversion_syntax",
+ "Division_by_zero",
+ "Division_impossible",
+ "Division_undefined",
+ "Fpu_error",
+ "Inexact",
+ "Invalid_context",
+ "Invalid_operation",
+ "Malloc_error",
+ "Not_implemented",
+ "Overflow",
+ "Rounded",
+ "Subnormal",
+ "Underflow",
+};
+
+static const char *mpd_signal_string[MPD_NUM_FLAGS] = {
+ "Clamped",
+ "IEEE_Invalid_operation",
+ "Division_by_zero",
+ "IEEE_Invalid_operation",
+ "IEEE_Invalid_operation",
+ "IEEE_Invalid_operation",
+ "Inexact",
+ "IEEE_Invalid_operation",
+ "IEEE_Invalid_operation",
+ "IEEE_Invalid_operation",
+ "Not_implemented",
+ "Overflow",
+ "Rounded",
+ "Subnormal",
+ "Underflow",
+};
+
+/* print conditions to buffer, separated by spaces */
+int
+mpd_snprint_flags(char *dest, int nmemb, uint32_t flags)
+{
+ char *cp;
+ int n, j;
+
+ assert(nmemb >= MPD_MAX_FLAG_STRING);
+
+ *dest = '\0'; cp = dest;
+ for (j = 0; j < MPD_NUM_FLAGS; j++) {
+ if (flags & (1U<<j)) {
+ n = snprintf(cp, nmemb, "%s ", mpd_flag_string[j]);
+ if (n < 0 || n >= nmemb) return -1;
+ cp += n; nmemb -= n;
+ }
+ }
+
+ if (cp != dest) {
+ *(--cp) = '\0';
+ }
+
+ return (int)(cp-dest);
+}
+
+/* print conditions to buffer, in list form */
+int
+mpd_lsnprint_flags(char *dest, int nmemb, uint32_t flags, const char *flag_string[])
+{
+ char *cp;
+ int n, j;
+
+ assert(nmemb >= MPD_MAX_FLAG_LIST);
+ if (flag_string == NULL) {
+ flag_string = mpd_flag_string;
+ }
+
+ *dest = '[';
+ *(dest+1) = '\0';
+ cp = dest+1;
+ --nmemb;
+
+ for (j = 0; j < MPD_NUM_FLAGS; j++) {
+ if (flags & (1U<<j)) {
+ n = snprintf(cp, nmemb, "%s, ", flag_string[j]);
+ if (n < 0 || n >= nmemb) return -1;
+ cp += n; nmemb -= n;
+ }
+ }
+
+ /* erase the last ", " */
+ if (cp != dest+1) {
+ cp -= 2;
+ }
+
+ *cp++ = ']';
+ *cp = '\0';
+
+ return (int)(cp-dest); /* strlen, without NUL terminator */
+}
+
+/* print signals to buffer, in list form */
+int
+mpd_lsnprint_signals(char *dest, int nmemb, uint32_t flags, const char *signal_string[])
+{
+ char *cp;
+ int n, j;
+ int ieee_invalid_done = 0;
+
+ assert(nmemb >= MPD_MAX_SIGNAL_LIST);
+ if (signal_string == NULL) {
+ signal_string = mpd_signal_string;
+ }
+
+ *dest = '[';
+ *(dest+1) = '\0';
+ cp = dest+1;
+ --nmemb;
+
+ for (j = 0; j < MPD_NUM_FLAGS; j++) {
+ uint32_t f = flags & (1U<<j);
+ if (f) {
+ if (f&MPD_IEEE_Invalid_operation) {
+ if (ieee_invalid_done) {
+ continue;
+ }
+ ieee_invalid_done = 1;
+ }
+ n = snprintf(cp, nmemb, "%s, ", signal_string[j]);
+ if (n < 0 || n >= nmemb) return -1;
+ cp += n; nmemb -= n;
+ }
+ }
+
+ /* erase the last ", " */
+ if (cp != dest+1) {
+ cp -= 2;
+ }
+
+ *cp++ = ']';
+ *cp = '\0';
+
+ return (int)(cp-dest); /* strlen, without NUL terminator */
+}
+
+/* The following two functions are mainly intended for debugging. */
+void
+mpd_fprint(FILE *file, const mpd_t *dec)
+{
+ char *decstring;
+
+ decstring = mpd_to_sci(dec, 1);
+ if (decstring != NULL) {
+ fprintf(file, "%s\n", decstring);
+ mpd_free(decstring);
+ }
+ else {
+ fputs("mpd_fprint: output error\n", file); /* GCOV_NOT_REACHED */
+ }
+}
+
+void
+mpd_print(const mpd_t *dec)
+{
+ char *decstring;
+
+ decstring = mpd_to_sci(dec, 1);
+ if (decstring != NULL) {
+ printf("%s\n", decstring);
+ mpd_free(decstring);
+ }
+ else {
+ fputs("mpd_fprint: output error\n", stderr); /* GCOV_NOT_REACHED */
+ }
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef IO_H
+#define IO_H
+
+
+#include <errno.h>
+#include "mpdecimal.h"
+
+
+#if SIZE_MAX == MPD_SIZE_MAX
+ #define mpd_strtossize _mpd_strtossize
+#else
+static inline mpd_ssize_t
+mpd_strtossize(const char *s, char **end, int base)
+{
+ int64_t retval;
+
+ errno = 0;
+ retval = _mpd_strtossize(s, end, base);
+ if (errno == 0 && (retval > MPD_SSIZE_MAX || retval < MPD_SSIZE_MIN)) {
+ errno = ERANGE;
+ }
+ if (errno == ERANGE) {
+ return (retval < 0) ? MPD_SSIZE_MIN : MPD_SSIZE_MAX;
+ }
+
+ return (mpd_ssize_t)retval;
+}
+#endif
+
+
+#endif
--- /dev/null
+
+
+This document contains links to the literature used in the process of
+creating the library. The list is probably not complete.
+
+
+Mike Cowlishaw: General Decimal Arithmetic Specification
+http://speleotrove.com/decimal/decarith.html
+
+
+Jean-Michel Muller: On the definition of ulp (x)
+lara.inist.fr/bitstream/2332/518/1/LIP-RR2005-09.pdf
+
+
+T. E. Hull, A. Abrham: Properly rounded variable precision square root
+http://portal.acm.org/citation.cfm?id=214413
+
+
+T. E. Hull, A. Abrham: Variable precision exponential function
+http://portal.acm.org/citation.cfm?id=6498
+
+
+Roman E. Maeder: Storage allocation for the Karatsuba integer multiplication
+algorithm. http://www.springerlink.com/content/w15058mj6v59t565/
+
+
+J. M. Pollard: The fast Fourier transform in a finite field
+http://www.ams.org/journals/mcom/1971-25-114/S0025-5718-1971-0301966-0/home.html
+
+
+David H. Bailey: FFTs in External or Hierarchical Memory
+http://crd.lbl.gov/~dhbailey/dhbpapers/
+
+
+W. Morven Gentleman: Matrix Multiplication and Fast Fourier Transforms
+http://www.alcatel-lucent.com/bstj/vol47-1968/articles/bstj47-6-1099.pdf
+
+
+Mikko Tommila: Apfloat documentation
+http://www.apfloat.org/apfloat/2.41/apfloat.pdf
+
+
+Joerg Arndt: "Matters Computational"
+http://www.jjj.de/fxt/
+
+
+Karl Hasselstrom: Fast Division of Large Integers
+www.treskal.com/kalle/exjobb/original-report.pdf
+
+
+
--- /dev/null
+
+
+Bignum support (Fast Number Theoretic Transform or FNT):
+========================================================
+
+Bignum arithmetic in libmpdec uses the scheme for fast convolution
+of integer sequences from:
+
+J. M. Pollard: The fast Fourier transform in a finite field
+http://www.ams.org/journals/mcom/1971-25-114/S0025-5718-1971-0301966-0/home.html
+
+
+The transform in a finite field can be used for convolution in the same
+way as the Fourier Transform. The main advantages of the Number Theoretic
+Transform are that it is both exact and very memory efficient.
+
+
+Convolution in pseudo-code:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ fnt_convolute(a, b):
+ x = fnt(a) # forward transform of a
+ y = fnt(b) # forward transform of b
+ z = pairwise multiply x[i] and y[i]
+ result = inv_fnt(z) # backward transform of z.
+
+
+Extending the maximum transform length (Chinese Remainder Theorem):
+-------------------------------------------------------------------
+
+The maximum transform length is quite limited when using a single
+prime field. However, it is possible to use multiple primes and
+recover the result using the Chinese Remainder Theorem.
+
+
+Multiplication in pseudo-code:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ _mpd_fntmul(u, v):
+ c1 = fnt_convolute(u, v, P1) # convolute modulo prime1
+ c2 = fnt_convolute(u, v, P2) # convolute modulo prime2
+ c3 = fnt_convolute(u, v, P3) # convolute modulo prime3
+ result = crt3(c1, c2, c3) # Chinese Remainder Theorem
+
+
+Optimized transform functions:
+------------------------------
+
+There are three different fnt() functions:
+
+ std_fnt: "standard" decimation in frequency transform for array lengths
+ of 2**n. Performs well up to 1024 words.
+
+ sixstep: Cache-friendly algorithm for array lengths of 2**n. Outperforms
+ std_fnt for large arrays.
+
+ fourstep: Algorithm for array lengths of 3 * 2**n. Also cache friendly
+ in large parts.
+
+
+List of bignum-only files:
+--------------------------
+
+Functions from these files are only used in _mpd_fntmul().
+
+ umodarith.h -> fast low level routines for unsigned modular arithmetic
+ numbertheory.c -> routines for setting up the FNT
+ difradix2.c -> decimation in frequency transform, used as the
+ "base case" by the following three files:
+
+ fnt.c -> standard transform for smaller arrays
+ sixstep.c -> transform large arrays of length 2**n
+ fourstep.c -> transform arrays of length 3 * 2**n
+
+ convolute.c -> do the actual fast convolution, using one of
+ the three transform functions.
+ transpose.c -> transpositions needed for the sixstep algorithm.
+ crt.c -> Chinese Remainder Theorem: use information from three
+ transforms modulo three different primes to get the
+ final result.
+
+
+
--- /dev/null
+#
+# Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+
+######################################################################
+# This file lists and checks some of the constants and limits used #
+# in libmpdec's Number Theoretic Transform. At the end of the file #
+# there is an example function for the plain DFT transform. #
+######################################################################
+
+
+#
+# Number theoretic transforms are done in subfields of F(p). P[i]
+# are the primes, D[i] = P[i] - 1 are highly composite and w[i]
+# are the respective primitive roots of F(p).
+#
+# The strategy is to convolute two coefficients modulo all three
+# primes, then use the Chinese Remainder Theorem on the three
+# result arrays to recover the result in the usual base RADIX
+# form.
+#
+
+# ======================================================================
+# Primitive roots
+# ======================================================================
+
+#
+# Verify primitive roots:
+#
+# For a prime field, r is a primitive root if and only if for all prime
+# factors f of p-1, r**((p-1)/f) =/= 1 (mod p).
+#
+def prod(F, E):
+ """Check that the factorization of P-1 is correct. F is the list of
+ factors of P-1, E lists the number of occurrences of each factor."""
+ x = 1
+ for y, z in zip(F, E):
+ x *= y**z
+ return x
+
+def is_primitive_root(r, p, factors, exponents):
+ """Check if r is a primitive root of F(p)."""
+ if p != prod(factors, exponents) + 1:
+ return False
+ for f in factors:
+ q, control = divmod(p-1, f)
+ if control != 0:
+ return False
+ if pow(r, q, p) == 1:
+ return False
+ return True
+
+
+# =================================================================
+# Constants and limits for the 64-bit version
+# =================================================================
+
+RADIX = 10**19
+
+# Primes P1, P2 and P3:
+P = [2**64-2**32+1, 2**64-2**34+1, 2**64-2**40+1]
+
+# P-1, highly composite. The transform length d is variable and
+# must divide D = P-1. Since all D are divisible by 3 * 2**32,
+# transform lengths can be 2**n or 3 * 2**n (where n <= 32).
+D = [2**32 * 3 * (5 * 17 * 257 * 65537),
+ 2**34 * 3**2 * (7 * 11 * 31 * 151 * 331),
+ 2**40 * 3**2 * (5 * 7 * 13 * 17 * 241)]
+
+# Prime factors of P-1 and their exponents:
+F = [(2,3,5,17,257,65537), (2,3,7,11,31,151,331), (2,3,5,7,13,17,241)]
+E = [(32,1,1,1,1,1), (34,2,1,1,1,1,1), (40,2,1,1,1,1,1)]
+
+# Maximum transform length for 2**n. Above that only 3 * 2**31
+# or 3 * 2**32 are possible.
+MPD_MAXTRANSFORM_2N = 2**32
+
+
+# Limits in the terminology of Pollard's paper:
+m2 = (MPD_MAXTRANSFORM_2N * 3) // 2 # Maximum length of the smaller array.
+M1 = M2 = RADIX-1 # Maximum value per single word.
+L = m2 * M1 * M2
+P[0] * P[1] * P[2] > 2 * L
+
+
+# Primitive roots of F(P1), F(P2) and F(P3):
+w = [7, 10, 19]
+
+# The primitive roots are correct:
+for i in range(3):
+ if not is_primitive_root(w[i], P[i], F[i], E[i]):
+ print("FAIL")
+
+
+# =================================================================
+# Constants and limits for the 32-bit version
+# =================================================================
+
+RADIX = 10**9
+
+# Primes P1, P2 and P3:
+P = [2113929217, 2013265921, 1811939329]
+
+# P-1, highly composite. All D = P-1 are divisible by 3 * 2**25,
+# allowing for transform lengths up to 3 * 2**25 words.
+D = [2**25 * 3**2 * 7,
+ 2**27 * 3 * 5,
+ 2**26 * 3**3]
+
+# Prime factors of P-1 and their exponents:
+F = [(2,3,7), (2,3,5), (2,3)]
+E = [(25,2,1), (27,1,1), (26,3)]
+
+# Maximum transform length for 2**n. Above that only 3 * 2**24 or
+# 3 * 2**25 are possible.
+MPD_MAXTRANSFORM_2N = 2**25
+
+
+# Limits in the terminology of Pollard's paper:
+m2 = (MPD_MAXTRANSFORM_2N * 3) // 2 # Maximum length of the smaller array.
+M1 = M2 = RADIX-1 # Maximum value per single word.
+L = m2 * M1 * M2
+P[0] * P[1] * P[2] > 2 * L
+
+
+# Primitive roots of F(P1), F(P2) and F(P3):
+w = [5, 31, 13]
+
+# The primitive roots are correct:
+for i in range(3):
+ if not is_primitive_root(w[i], P[i], F[i], E[i]):
+ print("FAIL")
+
+
+# ======================================================================
+# Example transform using a single prime
+# ======================================================================
+
+def ntt(lst, dir):
+ """Perform a transform on the elements of lst. len(lst) must
+ be 2**n or 3 * 2**n, where n <= 25. This is the slow DFT."""
+ p = 2113929217 # prime
+ d = len(lst) # transform length
+ d_prime = pow(d, (p-2), p) # inverse of d
+ xi = (p-1)//d
+ w = 5 # primitive root of F(p)
+ r = pow(w, xi, p) # primitive root of the subfield
+ r_prime = pow(w, (p-1-xi), p) # inverse of r
+ if dir == 1: # forward transform
+ a = lst # input array
+ A = [0] * d # transformed values
+ for i in range(d):
+ s = 0
+ for j in range(d):
+ s += a[j] * pow(r, i*j, p)
+ A[i] = s % p
+ return A
+ elif dir == -1: # backward transform
+ A = lst # input array
+ a = [0] * d # transformed values
+ for j in range(d):
+ s = 0
+ for i in range(d):
+ s += A[i] * pow(r_prime, i*j, p)
+ a[j] = (d_prime * s) % p
+ return a
+
+def ntt_convolute(a, b):
+ """convolute arrays a and b."""
+ assert(len(a) == len(b))
+ x = ntt(a, 1)
+ y = ntt(b, 1)
+ for i in range(len(a)):
+ y[i] = y[i] * x[i]
+ r = ntt(y, -1)
+ return r
+
+
+# Example: Two arrays representing 21 and 81 in little-endian:
+a = [1, 2, 0, 0]
+b = [1, 8, 0, 0]
+
+assert(ntt_convolute(a, b) == [1, 10, 16, 0])
+assert(21 * 81 == (1*10**0 + 10*10**1 + 16*10**2 + 0*10**3))
--- /dev/null
+
+
+(* Copyright (c) 2011 Stefan Krah. All rights reserved. *)
+
+
+The Matrix Fourier Transform:
+=============================
+
+In libmpdec, the Matrix Fourier Transform [1] is called four-step transform
+after a variant that appears in [2]. The algorithm requires that the input
+array can be viewed as an R*C matrix.
+
+All operations are done modulo p. For readability, the proofs drop all
+instances of (mod p).
+
+
+Algorithm four-step (forward transform):
+----------------------------------------
+
+ a := input array
+ d := len(a) = R * C
+ p := prime
+ w := primitive root of unity of the prime field
+ r := w**((p-1)/d)
+ A := output array
+
+ 1) Apply a length R FNT to each column.
+
+ 2) Multiply each matrix element (addressed by j*C+m) by r**(j*m).
+
+ 3) Apply a length C FNT to each row.
+
+ 4) Transpose the matrix.
+
+
+Proof (forward transform):
+--------------------------
+
+ The algorithm can be derived starting from the regular definition of
+ the finite-field transform of length d:
+
+ d-1
+ ,----
+ \
+ A[k] = | a[l] * r**(k * l)
+ /
+ `----
+ l = 0
+
+
+ The sum can be rearranged into the sum of the sums of columns:
+
+ C-1 R-1
+ ,---- ,----
+ \ \
+ = | | a[i * C + j] * r**(k * (i * C + j))
+ / /
+ `---- `----
+ j = 0 i = 0
+
+
+ Extracting a constant from the inner sum:
+
+ C-1 R-1
+ ,---- ,----
+ \ \
+ = | r**k*j * | a[i * C + j] * r**(k * i * C)
+ / /
+ `---- `----
+ j = 0 i = 0
+
+
+ Without any loss of generality, let k = n * R + m,
+ where n < C and m < R:
+
+ C-1 R-1
+ ,---- ,----
+ \ \
+ A[n*R+m] = | r**(R*n*j) * r**(m*j) * | a[i*C+j] * r**(R*C*n*i) * r**(C*m*i)
+ / /
+ `---- `----
+ j = 0 i = 0
+
+
+ Since r = w ** ((p-1) / (R*C)):
+
+ a) r**(R*C*n*i) = w**((p-1)*n*i) = 1
+
+ b) r**(C*m*i) = w**((p-1) / R) ** (m*i) = r_R ** (m*i)
+
+ c) r**(R*n*j) = w**((p-1) / C) ** (n*j) = r_C ** (n*j)
+
+ r_R := root of the subfield of length R.
+ r_C := root of the subfield of length C.
+
+
+ C-1 R-1
+ ,---- ,----
+ \ \
+ A[n*R+m] = | r_C**(n*j) * [ r**(m*j) * | a[i*C+j] * r_R**(m*i) ]
+ / ^ /
+ `---- | `---- 1) transform the columns
+ j = 0 | i = 0
+ ^ |
+ | `-- 2) multiply
+ |
+ `-- 3) transform the rows
+
+
+ Note that the entire RHS is a function of n and m and that the results
+ for each pair (n, m) are stored in Fortran order.
+
+ Let the term in square brackets be f(m, j). Step 1) and 2) precalculate
+ the term for all (m, j). After that, the original matrix is now a lookup
+ table with the mth element in the jth column at location m * C + j.
+
+ Let the complete RHS be g(m, n). Step 3) does an in-place transform of
+ length n on all rows. After that, the original matrix is now a lookup
+ table with the mth element in the nth column at location m * C + n.
+
+ But each (m, n) pair should be written to location n * R + m. Therefore,
+ step 4) transposes the result of step 3).
+
+
+
+Algorithm four-step (inverse transform):
+----------------------------------------
+
+ A := input array
+ d := len(A) = R * C
+ p := prime
+ d' := d**(p-2) # inverse of d
+ w := primitive root of unity of the prime field
+ r := w**((p-1)/d) # root of the subfield
+ r' := w**((p-1) - (p-1)/d) # inverse of r
+ a := output array
+
+ 0) View the matrix as a C*R matrix.
+
+ 1) Transpose the matrix, producing an R*C matrix.
+
+ 2) Apply a length C FNT to each row.
+
+ 3) Multiply each matrix element (addressed by i*C+n) by r**(i*n).
+
+ 4) Apply a length R FNT to each column.
+
+
+Proof (inverse transform):
+--------------------------
+
+ The algorithm can be derived starting from the regular definition of
+ the finite-field inverse transform of length d:
+
+ d-1
+ ,----
+ \
+ a[k] = d' * | A[l] * r' ** (k * l)
+ /
+ `----
+ l = 0
+
+
+ The sum can be rearranged into the sum of the sums of columns. Note
+ that at this stage we still have a C*R matrix, so C denotes the number
+ of rows:
+
+ R-1 C-1
+ ,---- ,----
+ \ \
+ = d' * | | a[j * R + i] * r' ** (k * (j * R + i))
+ / /
+ `---- `----
+ i = 0 j = 0
+
+
+ Extracting a constant from the inner sum:
+
+ R-1 C-1
+ ,---- ,----
+ \ \
+ = d' * | r' ** (k*i) * | a[j * R + i] * r' ** (k * j * R)
+ / /
+ `---- `----
+ i = 0 j = 0
+
+
+ Without any loss of generality, let k = m * C + n,
+ where m < R and n < C:
+
+ R-1 C-1
+ ,---- ,----
+ \ \
+ A[m*C+n] = d' * | r' ** (C*m*i) * r' ** (n*i) * | a[j*R+i] * r' ** (R*C*m*j) * r' ** (R*n*j)
+ / /
+ `---- `----
+ i = 0 j = 0
+
+
+ Since r' = w**((p-1) - (p-1)/d) and d = R*C:
+
+ a) r' ** (R*C*m*j) = w**((p-1)*R*C*m*j - (p-1)*m*j) = 1
+
+ b) r' ** (C*m*i) = w**((p-1)*C - (p-1)/R) ** (m*i) = r_R' ** (m*i)
+
+ c) r' ** (R*n*j) = r_C' ** (n*j)
+
+ d) d' = d**(p-2) = (R*C) ** (p-2) = R**(p-2) * C**(p-2) = R' * C'
+
+ r_R' := inverse of the root of the subfield of length R.
+ r_C' := inverse of the root of the subfield of length C.
+ R' := inverse of R
+ C' := inverse of C
+
+
+ R-1 C-1
+ ,---- ,---- 2) transform the rows of a^T
+ \ \
+ A[m*C+n] = R' * | r_R' ** (m*i) * [ r' ** (n*i) * C' * | a[j*R+i] * r_C' ** (n*j) ]
+ / ^ / ^
+ `---- | `---- |
+ i = 0 | j = 0 |
+ ^ | `-- 1) Transpose input matrix
+ | `-- 3) multiply to address elements by
+ | i * C + j
+ `-- 3) transform the columns
+
+
+
+ Note that the entire RHS is a function of m and n and that the results
+ for each pair (m, n) are stored in C order.
+
+ Let the term in square brackets be f(n, i). Without step 1), the sum
+ would perform a length C transform on the columns of the input matrix.
+ This is a) inefficient and b) the results are needed in C order, so
+ step 1) exchanges rows and columns.
+
+ Step 2) and 3) precalculate f(n, i) for all (n, i). After that, the
+ original matrix is now a lookup table with the ith element in the nth
+ column at location i * C + n.
+
+ Let the complete RHS be g(m, n). Step 4) does an in-place transform of
+ length m on all columns. After that, the original matrix is now a lookup
+ table with the mth element in the nth column at location m * C + n,
+ which means that all A[k] = A[m * C + n] are in the correct order.
+
+
+--
+
+ [1] Joerg Arndt: "Matters Computational"
+ http://www.jjj.de/fxt/
+ [2] David H. Bailey: FFTs in External or Hierarchical Memory
+ http://crd.lbl.gov/~dhbailey/dhbpapers/
+
+
+
--- /dev/null
+
+
+(* Copyright (c) 2011 Stefan Krah. All rights reserved. *)
+
+
+==========================================================================
+ Calculate (a * b) % p using special primes
+==========================================================================
+
+A description of the algorithm can be found in the apfloat manual by
+Tommila [1].
+
+
+Definitions:
+------------
+
+In the whole document, "==" stands for "is congruent with".
+
+Result of a * b in terms of high/low words:
+
+ (1) hi * 2**64 + lo = a * b
+
+Special primes:
+
+ (2) p = 2**64 - z + 1, where z = 2**n
+
+Single step modular reduction:
+
+ (3) R(hi, lo) = hi * z - hi + lo
+
+
+Strategy:
+---------
+
+ a) Set (hi, lo) to the result of a * b.
+
+ b) Set (hi', lo') to the result of R(hi, lo).
+
+ c) Repeat step b) until 0 <= hi' * 2**64 + lo' < 2*p.
+
+ d) If the result is less than p, return lo'. Otherwise return lo' - p.
+
+
+The reduction step b) preserves congruence:
+-------------------------------------------
+
+ hi * 2**64 + lo == hi * z - hi + lo (mod p)
+
+ Proof:
+ ~~~~~~
+
+ hi * 2**64 + lo = (2**64 - z + 1) * hi + z * hi - hi + lo
+
+ = p * hi + z * hi - hi + lo
+
+ == z * hi - hi + lo (mod p)
+
+
+Maximum numbers of step b):
+---------------------------
+
+# To avoid unneccessary formalism, define:
+
+def R(hi, lo, z):
+ return divmod(hi * z - hi + lo, 2**64)
+
+# For simplicity, assume hi=2**64-1, lo=2**64-1 after the
+# initial multiplication a * b. This is of course impossible
+# but certainly covers all cases.
+
+# Then, for p1:
+hi=2**64-1; lo=2**64-1; z=2**32
+p1 = 2**64 - z + 1
+
+hi, lo = R(hi, lo, z) # First reduction
+hi, lo = R(hi, lo, z) # Second reduction
+hi * 2**64 + lo < 2 * p1 # True
+
+# For p2:
+hi=2**64-1; lo=2**64-1; z=2**34
+p2 = 2**64 - z + 1
+
+hi, lo = R(hi, lo, z) # First reduction
+hi, lo = R(hi, lo, z) # Second reduction
+hi, lo = R(hi, lo, z) # Third reduction
+hi * 2**64 + lo < 2 * p2 # True
+
+# For p3:
+hi=2**64-1; lo=2**64-1; z=2**40
+p3 = 2**64 - z + 1
+
+hi, lo = R(hi, lo, z) # First reduction
+hi, lo = R(hi, lo, z) # Second reduction
+hi, lo = R(hi, lo, z) # Third reduction
+hi * 2**64 + lo < 2 * p3 # True
+
+
+Step d) preserves congruence and yields a result < p:
+-----------------------------------------------------
+
+ Case hi = 0:
+
+ Case lo < p: trivial.
+
+ Case lo >= p:
+
+ lo == lo - p (mod p) # result is congruent
+
+ p <= lo < 2*p -> 0 <= lo - p < p # result is in the correct range
+
+ Case hi = 1:
+
+ p < 2**64 /\ 2**64 + lo < 2*p -> lo < p # lo is always less than p
+
+ 2**64 + lo == 2**64 + (lo - p) (mod p) # result is congruent
+
+ = lo - p # exactly the same value as the previous RHS
+ # in uint64_t arithmetic.
+
+ p < 2**64 + lo < 2*p -> 0 < 2**64 + (lo - p) < p # correct range
+
+
+
+[1] http://www.apfloat.org/apfloat/2.40/apfloat.pdf
+
+
+
--- /dev/null
+
+
+(* Copyright (c) 2011 Stefan Krah. All rights reserved. *)
+
+
+========================================================================
+ Calculate (a * b) % p using the 80-bit x87 FPU
+========================================================================
+
+A description of the algorithm can be found in the apfloat manual by
+Tommila [1].
+
+The proof follows an argument made by Granlund/Montgomery in [2].
+
+
+Definitions and assumptions:
+----------------------------
+
+The 80-bit extended precision format uses 64 bits for the significand:
+
+ (1) F = 64
+
+The modulus is prime and less than 2**31:
+
+ (2) 2 <= p < 2**31
+
+The factors are less than p:
+
+ (3) 0 <= a < p
+ (4) 0 <= b < p
+
+The product a * b is less than 2**62 and is thus exact in 64 bits:
+
+ (5) n = a * b
+
+The product can be represented in terms of quotient and remainder:
+
+ (6) n = q * p + r
+
+Using (3), (4) and the fact that p is prime, the remainder is always
+greater than zero:
+
+ (7) 0 <= q < p /\ 1 <= r < p
+
+
+Strategy:
+---------
+
+Precalculate the 80-bit long double inverse of p, with a maximum
+relative error of 2**(1-F):
+
+ (8) pinv = (long double)1.0 / p
+
+Calculate an estimate for q = floor(n/p). The multiplication has another
+maximum relative error of 2**(1-F):
+
+ (9) qest = n * pinv
+
+If we can show that q < qest < q+1, then trunc(qest) = q. It is then
+easy to recover the remainder r. The complete algorithm is:
+
+ a) Set the control word to 64-bit precision and truncation mode.
+
+ b) n = a * b # Calculate exact product.
+
+ c) qest = n * pinv # Calculate estimate for the quotient.
+
+ d) q = (qest+2**63)-2**63 # Truncate qest to the exact quotient.
+
+ f) r = n - q * p # Calculate remainder.
+
+
+Proof for q < qest < q+1:
+-------------------------
+
+Using the cumulative error, the error bounds for qest are:
+
+ n n * (1 + 2**(1-F))**2
+ (9) --------------------- <= qest <= ---------------------
+ p * (1 + 2**(1-F))**2 p
+
+
+ Lemma 1:
+ --------
+ n q * p + r
+ (10) q < --------------------- = ---------------------
+ p * (1 + 2**(1-F))**2 p * (1 + 2**(1-F))**2
+
+
+ Proof:
+ ~~~~~~
+
+ (I) q * p * (1 + 2**(1-F))**2 < q * p + r
+
+ (II) q * p * 2**(2-F) + q * p * 2**(2-2*F) < r
+
+ Using (1) and (7), it is sufficient to show that:
+
+ (III) q * p * 2**(-62) + q * p * 2**(-126) < 1 <= r
+
+ (III) can easily be verified by substituting the largest possible
+ values p = 2**31-1 and q = 2**31-2.
+
+ The critical cases occur when r = 1, n = m * p + 1. These cases
+ can be exhaustively verified with a test program.
+
+
+ Lemma 2:
+ --------
+
+ n * (1 + 2**(1-F))**2 (q * p + r) * (1 + 2**(1-F))**2
+ (11) --------------------- = ------------------------------- < q + 1
+ p p
+
+ Proof:
+ ~~~~~~
+
+ (I) (q * p + r) + (q * p + r) * 2**(2-F) + (q * p + r) * 2**(2-2*F) < q * p + p
+
+ (II) (q * p + r) * 2**(2-F) + (q * p + r) * 2**(2-2*F) < p - r
+
+ Using (1) and (7), it is sufficient to show that:
+
+ (III) (q * p + r) * 2**(-62) + (q * p + r) * 2**(-126) < 1 <= p - r
+
+ (III) can easily be verified by substituting the largest possible
+ values p = 2**31-1, q = 2**31-2 and r = 2**31-2.
+
+ The critical cases occur when r = (p - 1), n = m * p - 1. These cases
+ can be exhaustively verified with a test program.
+
+
+[1] http://www.apfloat.org/apfloat/2.40/apfloat.pdf
+[2] http://gmplib.org/~tege/divcnst-pldi94.pdf
+ [Section 7: "Use of floating point"]
+
+
+
+(* Coq proof for (10) and (11) *)
+
+Require Import ZArith.
+Require Import QArith.
+Require Import Qpower.
+Require Import Qabs.
+Require Import Psatz.
+
+Open Scope Q_scope.
+
+
+Ltac qreduce T :=
+ rewrite <- (Qred_correct (T)); simpl (Qred (T)).
+
+Theorem Qlt_move_right :
+ forall x y z:Q, x + z < y <-> x < y - z.
+Proof.
+ intros.
+ split.
+ intros.
+ psatzl Q.
+ intros.
+ psatzl Q.
+Qed.
+
+Theorem Qlt_mult_by_z :
+ forall x y z:Q, 0 < z -> (x < y <-> x * z < y * z).
+Proof.
+ intros.
+ split.
+ intros.
+ apply Qmult_lt_compat_r. trivial. trivial.
+ intros.
+ rewrite <- (Qdiv_mult_l x z). rewrite <- (Qdiv_mult_l y z).
+ apply Qmult_lt_compat_r.
+ apply Qlt_shift_inv_l.
+ trivial. psatzl Q. trivial. psatzl Q. psatzl Q.
+Qed.
+
+Theorem Qle_mult_quad :
+ forall (a b c d:Q),
+ 0 <= a -> a <= c ->
+ 0 <= b -> b <= d ->
+ a * b <= c * d.
+ intros.
+ psatz Q.
+Qed.
+
+
+Theorem q_lt_qest:
+ forall (p q r:Q),
+ (0 < p) -> (p <= (2#1)^31 - 1) ->
+ (0 <= q) -> (q <= p - 1) ->
+ (1 <= r) -> (r <= p - 1) ->
+ q < (q * p + r) / (p * (1 + (2#1)^(-63))^2).
+Proof.
+ intros.
+ rewrite Qlt_mult_by_z with (z := (p * (1 + (2#1)^(-63))^2)).
+
+ unfold Qdiv.
+ rewrite <- Qmult_assoc.
+ rewrite (Qmult_comm (/ (p * (1 + (2 # 1) ^ (-63)) ^ 2)) (p * (1 + (2 # 1) ^ (-63)) ^ 2)).
+ rewrite Qmult_inv_r.
+ rewrite Qmult_1_r.
+
+ assert (q * (p * (1 + (2 # 1) ^ (-63)) ^ 2) == q * p + (q * p) * ((2 # 1) ^ (-62) + (2 # 1) ^ (-126))).
+ qreduce ((1 + (2 # 1) ^ (-63)) ^ 2).
+ qreduce ((2 # 1) ^ (-62) + (2 # 1) ^ (-126)).
+ ring_simplify.
+ reflexivity.
+ rewrite H5.
+
+ rewrite Qplus_comm.
+ rewrite Qlt_move_right.
+ ring_simplify (q * p + r - q * p).
+ qreduce ((2 # 1) ^ (-62) + (2 # 1) ^ (-126)).
+
+ apply Qlt_le_trans with (y := 1).
+ rewrite Qlt_mult_by_z with (z := 85070591730234615865843651857942052864 # 18446744073709551617).
+ ring_simplify.
+
+ apply Qle_lt_trans with (y := ((2 # 1) ^ 31 - (2#1)) * ((2 # 1) ^ 31 - 1)).
+ apply Qle_mult_quad.
+ assumption. psatzl Q. psatzl Q. psatzl Q. psatzl Q. psatzl Q. assumption. psatzl Q. psatzl Q.
+Qed.
+
+Theorem qest_lt_qplus1:
+ forall (p q r:Q),
+ (0 < p) -> (p <= (2#1)^31 - 1) ->
+ (0 <= q) -> (q <= p - 1) ->
+ (1 <= r) -> (r <= p - 1) ->
+ ((q * p + r) * (1 + (2#1)^(-63))^2) / p < q + 1.
+Proof.
+ intros.
+ rewrite Qlt_mult_by_z with (z := p).
+
+ unfold Qdiv.
+ rewrite <- Qmult_assoc.
+ rewrite (Qmult_comm (/ p) p).
+ rewrite Qmult_inv_r.
+ rewrite Qmult_1_r.
+
+ assert ((q * p + r) * (1 + (2 # 1) ^ (-63)) ^ 2 == q * p + r + (q * p + r) * ((2 # 1) ^ (-62) + (2 # 1) ^ (-126))).
+ qreduce ((1 + (2 # 1) ^ (-63)) ^ 2).
+ qreduce ((2 # 1) ^ (-62) + (2 # 1) ^ (-126)).
+ ring_simplify. reflexivity.
+ rewrite H5.
+
+ rewrite <- Qplus_assoc. rewrite <- Qplus_comm. rewrite Qlt_move_right.
+ ring_simplify ((q + 1) * p - q * p).
+
+ rewrite <- Qplus_comm. rewrite Qlt_move_right.
+
+ apply Qlt_le_trans with (y := 1).
+ qreduce ((2 # 1) ^ (-62) + (2 # 1) ^ (-126)).
+
+ rewrite Qlt_mult_by_z with (z := 85070591730234615865843651857942052864 # 18446744073709551617).
+ ring_simplify.
+
+ ring_simplify in H0.
+ apply Qle_lt_trans with (y := (2147483646 # 1) * (2147483647 # 1) + (2147483646 # 1)).
+
+ apply Qplus_le_compat.
+ apply Qle_mult_quad.
+ assumption. psatzl Q. auto with qarith. assumption. psatzl Q.
+ auto with qarith. auto with qarith.
+ psatzl Q. psatzl Q. assumption.
+Qed.
+
+
+
--- /dev/null
+
+
+(* Copyright (c) 2011 Stefan Krah. All rights reserved. *)
+
+
+The Six Step Transform:
+=======================
+
+In libmpdec, the six-step transform is the Matrix Fourier Transform (See
+matrix-transform.txt) in disguise. It is called six-step transform after
+a variant that appears in [1]. The algorithm requires that the input
+array can be viewed as an R*C matrix.
+
+
+Algorithm six-step (forward transform):
+---------------------------------------
+
+ 1a) Transpose the matrix.
+
+ 1b) Apply a length R FNT to each row.
+
+ 1c) Transpose the matrix.
+
+ 2) Multiply each matrix element (addressed by j*C+m) by r**(j*m).
+
+ 3) Apply a length C FNT to each row.
+
+ 4) Transpose the matrix.
+
+Note that steps 1a) - 1c) are exactly equivalent to step 1) of the Matrix
+Fourier Transform. For large R, it is faster to transpose twice and do
+a transform on the rows than to perform a column transpose directly.
+
+
+
+Algorithm six-step (inverse transform):
+---------------------------------------
+
+ 0) View the matrix as a C*R matrix.
+
+ 1) Transpose the matrix, producing an R*C matrix.
+
+ 2) Apply a length C FNT to each row.
+
+ 3) Multiply each matrix element (addressed by i*C+n) by r**(i*n).
+
+ 4a) Transpose the matrix.
+
+ 4b) Apply a length R FNT to each row.
+
+ 4c) Transpose the matrix.
+
+Again, steps 4a) - 4c) are equivalent to step 4) of the Matrix Fourier
+Transform.
+
+
+
+--
+
+ [1] David H. Bailey: FFTs in External or Hierarchical Memory
+ http://crd.lbl.gov/~dhbailey/dhbpapers/
+
+
--- /dev/null
+;
+; Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+;
+; Redistribution and use in source and binary forms, with or without
+; modification, are permitted provided that the following conditions
+; are met:
+;
+; 1. Redistributions of source code must retain the above copyright
+; notice, this list of conditions and the following disclaimer.
+;
+; 2. Redistributions in binary form must reproduce the above copyright
+; notice, this list of conditions and the following disclaimer in the
+; documentation and/or other materials provided with the distribution.
+;
+; THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+; SUCH DAMAGE.
+;
+
+
+(in-package "ACL2")
+
+(include-book "arithmetic/top-with-meta" :dir :system)
+(include-book "arithmetic-2/floor-mod/floor-mod" :dir :system)
+
+
+;; =====================================================================
+;; Proofs for several functions in umodarith.h
+;; =====================================================================
+
+
+
+;; =====================================================================
+;; Helper theorems
+;; =====================================================================
+
+(defthm elim-mod-m<x<2*m
+ (implies (and (<= m x)
+ (< x (* 2 m))
+ (rationalp x) (rationalp m))
+ (equal (mod x m)
+ (+ x (- m)))))
+
+(defthm modaux-1a
+ (implies (and (< x m) (< 0 x) (< 0 m)
+ (rationalp x) (rationalp m))
+ (equal (mod (- x) m)
+ (+ (- x) m))))
+
+(defthm modaux-1b
+ (implies (and (< (- x) m) (< x 0) (< 0 m)
+ (rationalp x) (rationalp m))
+ (equal (mod x m)
+ (+ x m)))
+ :hints (("Goal" :use ((:instance modaux-1a
+ (x (- x)))))))
+
+(defthm modaux-1c
+ (implies (and (< x m) (< 0 x) (< 0 m)
+ (rationalp x) (rationalp m))
+ (equal (mod x m)
+ x)))
+
+(defthm modaux-2a
+ (implies (and (< 0 b) (< b m)
+ (natp x) (natp b) (natp m)
+ (< (mod (+ b x) m) b))
+ (equal (mod (+ (- m) b x) m)
+ (+ (- m) b (mod x m)))))
+
+(defthm modaux-2b
+ (implies (and (< 0 b) (< b m)
+ (natp x) (natp b) (natp m)
+ (< (mod (+ b x) m) b))
+ (equal (mod (+ b x) m)
+ (+ (- m) b (mod x m))))
+ :hints (("Goal" :use (modaux-2a))))
+
+(defthm linear-mod-1
+ (implies (and (< x m) (< b m)
+ (natp x) (natp b)
+ (rationalp m))
+ (equal (< x (mod (+ (- b) x) m))
+ (< x b)))
+ :hints (("Goal" :use ((:instance modaux-1a
+ (x (+ b (- x))))))))
+
+(defthm linear-mod-2
+ (implies (and (< 0 b) (< b m)
+ (natp x) (natp b)
+ (natp m))
+ (equal (< (mod x m)
+ (mod (+ (- b) x) m))
+ (< (mod x m) b))))
+
+(defthm linear-mod-3
+ (implies (and (< x m) (< b m)
+ (natp x) (natp b)
+ (rationalp m))
+ (equal (<= b (mod (+ b x) m))
+ (< (+ b x) m)))
+ :hints (("Goal" :use ((:instance elim-mod-m<x<2*m
+ (x (+ b x)))))))
+
+(defthm modaux-2c
+ (implies (and (< 0 b) (< b m)
+ (natp x) (natp b) (natp m)
+ (<= b (mod (+ b x) m)))
+ (equal (mod (+ b x) m)
+ (+ b (mod x m))))
+ :hints (("Subgoal *1/8''" :use (linear-mod-3))))
+
+(defthmd modaux-2d
+ (implies (and (< x m) (< 0 x) (< 0 m)
+ (< (- m) b) (< b 0) (rationalp m)
+ (<= x (mod (+ b x) m))
+ (rationalp x) (rationalp b))
+ (equal (+ (- m) (mod (+ b x) m))
+ (+ b x)))
+ :hints (("Goal" :cases ((<= 0 (+ b x))))
+ ("Subgoal 2'" :use ((:instance modaux-1b
+ (x (+ b x)))))))
+
+(defthm mod-m-b
+ (implies (and (< 0 x) (< 0 b) (< 0 m)
+ (< x b) (< b m)
+ (natp x) (natp b) (natp m))
+ (equal (mod (+ (mod (- x) m) b) m)
+ (mod (- x) b))))
+
+
+;; =====================================================================
+;; addmod, submod
+;; =====================================================================
+
+(defun addmod (a b m base)
+ (let* ((s (mod (+ a b) base))
+ (s (if (< s a) (mod (- s m) base) s))
+ (s (if (>= s m) (mod (- s m) base) s)))
+ s))
+
+(defthmd addmod-correct
+ (implies (and (< 0 m) (< m base)
+ (< a m) (<= b m)
+ (natp m) (natp base)
+ (natp a) (natp b))
+ (equal (addmod a b m base)
+ (mod (+ a b) m)))
+ :hints (("Goal" :cases ((<= base (+ a b))))
+ ("Subgoal 2.1'" :use ((:instance elim-mod-m<x<2*m
+ (x (+ a b)))))))
+
+(defun submod (a b m base)
+ (let* ((d (mod (- a b) base))
+ (d (if (< a d) (mod (+ d m) base) d)))
+ d))
+
+(defthmd submod-aux1
+ (implies (and (< a (mod (+ a (- b)) base))
+ (< 0 base) (< a base) (<= b base)
+ (natp base) (natp a) (natp b))
+ (< a b))
+ :rule-classes :forward-chaining)
+
+(defthmd submod-aux2
+ (implies (and (<= (mod (+ a (- b)) base) a)
+ (< 0 base) (< a base) (< b base)
+ (natp base) (natp a) (natp b))
+ (<= b a))
+ :rule-classes :forward-chaining)
+
+(defthmd submod-correct
+ (implies (and (< 0 m) (< m base)
+ (< a m) (<= b m)
+ (natp m) (natp base)
+ (natp a) (natp b))
+ (equal (submod a b m base)
+ (mod (- a b) m)))
+ :hints (("Goal" :cases ((<= base (+ a b))))
+ ("Subgoal 2.2" :use ((:instance submod-aux1)))
+ ("Subgoal 2.2'''" :cases ((and (< 0 (+ a (- b) m))
+ (< (+ a (- b) m) m))))
+ ("Subgoal 2.1" :use ((:instance submod-aux2)))
+ ("Subgoal 1.2" :use ((:instance submod-aux1)))
+ ("Subgoal 1.1" :use ((:instance submod-aux2)))))
+
+
+(defun submod-2 (a b m base)
+ (let* ((d (mod (- a b) base))
+ (d (if (< a b) (mod (+ d m) base) d)))
+ d))
+
+(defthm submod-2-correct
+ (implies (and (< 0 m) (< m base)
+ (< a m) (<= b m)
+ (natp m) (natp base)
+ (natp a) (natp b))
+ (equal (submod-2 a b m base)
+ (mod (- a b) m)))
+ :hints (("Subgoal 2'" :cases ((and (< 0 (+ a (- b) m))
+ (< (+ a (- b) m) m))))))
+
+
+;; =========================================================================
+;; ext-submod is correct
+;; =========================================================================
+
+; a < 2*m, b < 2*m
+(defun ext-submod (a b m base)
+ (let* ((a (if (>= a m) (- a m) a))
+ (b (if (>= b m) (- b m) b))
+ (d (mod (- a b) base))
+ (d (if (< a b) (mod (+ d m) base) d)))
+ d))
+
+; a < 2*m, b < 2*m
+(defun ext-submod-2 (a b m base)
+ (let* ((a (mod a m))
+ (b (mod b m))
+ (d (mod (- a b) base))
+ (d (if (< a b) (mod (+ d m) base) d)))
+ d))
+
+(defthmd ext-submod-ext-submod-2-equal
+ (implies (and (< 0 m) (< m base)
+ (< a (* 2 m)) (< b (* 2 m))
+ (natp m) (natp base)
+ (natp a) (natp b))
+ (equal (ext-submod a b m base)
+ (ext-submod-2 a b m base))))
+
+(defthmd ext-submod-2-correct
+ (implies (and (< 0 m) (< m base)
+ (< a (* 2 m)) (< b (* 2 m))
+ (natp m) (natp base)
+ (natp a) (natp b))
+ (equal (ext-submod-2 a b m base)
+ (mod (- a b) m))))
+
+
+;; =========================================================================
+;; dw-reduce is correct
+;; =========================================================================
+
+(defun dw-reduce (hi lo m base)
+ (let* ((r1 (mod hi m))
+ (r2 (mod (+ (* r1 base) lo) m)))
+ r2))
+
+(defthmd dw-reduce-correct
+ (implies (and (< 0 m) (< m base)
+ (< hi base) (< lo base)
+ (natp m) (natp base)
+ (natp hi) (natp lo))
+ (equal (dw-reduce hi lo m base)
+ (mod (+ (* hi base) lo) m))))
+
+(defthmd <=-multiply-both-sides-by-z
+ (implies (and (rationalp x) (rationalp y)
+ (< 0 z) (rationalp z))
+ (equal (<= x y)
+ (<= (* z x) (* z y)))))
+
+(defthmd dw-reduce-aux1
+ (implies (and (< 0 m) (< m base)
+ (natp m) (natp base)
+ (< lo base) (natp lo)
+ (< x m) (natp x))
+ (< (+ lo (* base x)) (* base m)))
+ :hints (("Goal" :cases ((<= (+ x 1) m)))
+ ("Subgoal 1''" :cases ((<= (* base (+ x 1)) (* base m))))
+ ("subgoal 1.2" :use ((:instance <=-multiply-both-sides-by-z
+ (x (+ 1 x))
+ (y m)
+ (z base))))))
+
+(defthm dw-reduce-aux2
+ (implies (and (< x (* base m))
+ (< 0 m) (< m base)
+ (natp m) (natp base) (natp x))
+ (< (floor x m) base)))
+
+;; This is the necessary condition for using _mpd_div_words().
+(defthmd dw-reduce-second-quotient-fits-in-single-word
+ (implies (and (< 0 m) (< m base)
+ (< hi base) (< lo base)
+ (natp m) (natp base)
+ (natp hi) (natp lo)
+ (equal r1 (mod hi m)))
+ (< (floor (+ (* r1 base) lo) m)
+ base))
+ :hints (("Goal" :cases ((< r1 m)))
+ ("Subgoal 1''" :cases ((< (+ lo (* base (mod hi m))) (* base m))))
+ ("Subgoal 1.2" :use ((:instance dw-reduce-aux1
+ (x (mod hi m)))))))
+
+
+;; =========================================================================
+;; dw-submod is correct
+;; =========================================================================
+
+(defun dw-submod (a hi lo m base)
+ (let* ((r (dw-reduce hi lo m base))
+ (d (mod (- a r) base))
+ (d (if (< a r) (mod (+ d m) base) d)))
+ d))
+
+(defthmd dw-submod-aux1
+ (implies (and (natp a) (< 0 m) (natp m)
+ (natp x) (equal r (mod x m)))
+ (equal (mod (- a x) m)
+ (mod (- a r) m))))
+
+(defthmd dw-submod-correct
+ (implies (and (< 0 m) (< m base)
+ (natp a) (< a m)
+ (< hi base) (< lo base)
+ (natp m) (natp base)
+ (natp hi) (natp lo))
+ (equal (dw-submod a hi lo m base)
+ (mod (- a (+ (* base hi) lo)) m)))
+ :hints (("Goal" :in-theory (disable dw-reduce)
+ :use ((:instance dw-submod-aux1
+ (x (+ lo (* base hi)))
+ (r (dw-reduce hi lo m base)))
+ (:instance dw-reduce-correct)))))
+
+
+;; =========================================================================
+;; ANSI C arithmetic for uint64_t
+;; =========================================================================
+
+(defun add (a b)
+ (mod (+ a b)
+ (expt 2 64)))
+
+(defun sub (a b)
+ (mod (- a b)
+ (expt 2 64)))
+
+(defun << (w n)
+ (mod (* w (expt 2 n))
+ (expt 2 64)))
+
+(defun >> (w n)
+ (floor w (expt 2 n)))
+
+;; join upper and lower half of a double word, yielding a 128 bit number
+(defun join (hi lo)
+ (+ (* (expt 2 64) hi) lo))
+
+
+;; =============================================================================
+;; Fast modular reduction
+;; =============================================================================
+
+;; These are the three primes used in the Number Theoretic Transform.
+;; A fast modular reduction scheme exists for all of them.
+(defmacro p1 ()
+ (+ (expt 2 64) (- (expt 2 32)) 1))
+
+(defmacro p2 ()
+ (+ (expt 2 64) (- (expt 2 34)) 1))
+
+(defmacro p3 ()
+ (+ (expt 2 64) (- (expt 2 40)) 1))
+
+
+;; reduce the double word number hi*2**64 + lo (mod p1)
+(defun simple-mod-reduce-p1 (hi lo)
+ (+ (* (expt 2 32) hi) (- hi) lo))
+
+;; reduce the double word number hi*2**64 + lo (mod p2)
+(defun simple-mod-reduce-p2 (hi lo)
+ (+ (* (expt 2 34) hi) (- hi) lo))
+
+;; reduce the double word number hi*2**64 + lo (mod p3)
+(defun simple-mod-reduce-p3 (hi lo)
+ (+ (* (expt 2 40) hi) (- hi) lo))
+
+
+; ----------------------------------------------------------
+; The modular reductions given above are correct
+; ----------------------------------------------------------
+
+(defthmd congruence-p1-aux
+ (equal (* (expt 2 64) hi)
+ (+ (* (p1) hi)
+ (* (expt 2 32) hi)
+ (- hi))))
+
+(defthmd congruence-p2-aux
+ (equal (* (expt 2 64) hi)
+ (+ (* (p2) hi)
+ (* (expt 2 34) hi)
+ (- hi))))
+
+(defthmd congruence-p3-aux
+ (equal (* (expt 2 64) hi)
+ (+ (* (p3) hi)
+ (* (expt 2 40) hi)
+ (- hi))))
+
+(defthmd mod-augment
+ (implies (and (rationalp x)
+ (rationalp y)
+ (rationalp m))
+ (equal (mod (+ x y) m)
+ (mod (+ x (mod y m)) m))))
+
+(defthmd simple-mod-reduce-p1-congruent
+ (implies (and (integerp hi)
+ (integerp lo))
+ (equal (mod (simple-mod-reduce-p1 hi lo) (p1))
+ (mod (join hi lo) (p1))))
+ :hints (("Goal''" :use ((:instance congruence-p1-aux)
+ (:instance mod-augment
+ (m (p1))
+ (x (+ (- hi) lo (* (expt 2 32) hi)))
+ (y (* (p1) hi)))))))
+
+(defthmd simple-mod-reduce-p2-congruent
+ (implies (and (integerp hi)
+ (integerp lo))
+ (equal (mod (simple-mod-reduce-p2 hi lo) (p2))
+ (mod (join hi lo) (p2))))
+ :hints (("Goal''" :use ((:instance congruence-p2-aux)
+ (:instance mod-augment
+ (m (p2))
+ (x (+ (- hi) lo (* (expt 2 34) hi)))
+ (y (* (p2) hi)))))))
+
+(defthmd simple-mod-reduce-p3-congruent
+ (implies (and (integerp hi)
+ (integerp lo))
+ (equal (mod (simple-mod-reduce-p3 hi lo) (p3))
+ (mod (join hi lo) (p3))))
+ :hints (("Goal''" :use ((:instance congruence-p3-aux)
+ (:instance mod-augment
+ (m (p3))
+ (x (+ (- hi) lo (* (expt 2 40) hi)))
+ (y (* (p3) hi)))))))
+
+
+; ---------------------------------------------------------------------
+; We need a number less than 2*p, so that we can use the trick from
+; elim-mod-m<x<2*m for the final reduction.
+; For p1, two modular reductions are sufficient, for p2 and p3 three.
+; ---------------------------------------------------------------------
+
+;; p1: the first reduction is less than 2**96
+(defthmd simple-mod-reduce-p1-<-2**96
+ (implies (and (< hi (expt 2 64))
+ (< lo (expt 2 64))
+ (natp hi) (natp lo))
+ (< (simple-mod-reduce-p1 hi lo)
+ (expt 2 96))))
+
+;; p1: the second reduction is less than 2*p1
+(defthmd simple-mod-reduce-p1-<-2*p1
+ (implies (and (< hi (expt 2 64))
+ (< lo (expt 2 64))
+ (< (join hi lo) (expt 2 96))
+ (natp hi) (natp lo))
+ (< (simple-mod-reduce-p1 hi lo)
+ (* 2 (p1)))))
+
+
+;; p2: the first reduction is less than 2**98
+(defthmd simple-mod-reduce-p2-<-2**98
+ (implies (and (< hi (expt 2 64))
+ (< lo (expt 2 64))
+ (natp hi) (natp lo))
+ (< (simple-mod-reduce-p2 hi lo)
+ (expt 2 98))))
+
+;; p2: the second reduction is less than 2**69
+(defthmd simple-mod-reduce-p2-<-2*69
+ (implies (and (< hi (expt 2 64))
+ (< lo (expt 2 64))
+ (< (join hi lo) (expt 2 98))
+ (natp hi) (natp lo))
+ (< (simple-mod-reduce-p2 hi lo)
+ (expt 2 69))))
+
+;; p3: the third reduction is less than 2*p2
+(defthmd simple-mod-reduce-p2-<-2*p2
+ (implies (and (< hi (expt 2 64))
+ (< lo (expt 2 64))
+ (< (join hi lo) (expt 2 69))
+ (natp hi) (natp lo))
+ (< (simple-mod-reduce-p2 hi lo)
+ (* 2 (p2)))))
+
+
+;; p3: the first reduction is less than 2**104
+(defthmd simple-mod-reduce-p3-<-2**104
+ (implies (and (< hi (expt 2 64))
+ (< lo (expt 2 64))
+ (natp hi) (natp lo))
+ (< (simple-mod-reduce-p3 hi lo)
+ (expt 2 104))))
+
+;; p3: the second reduction is less than 2**81
+(defthmd simple-mod-reduce-p3-<-2**81
+ (implies (and (< hi (expt 2 64))
+ (< lo (expt 2 64))
+ (< (join hi lo) (expt 2 104))
+ (natp hi) (natp lo))
+ (< (simple-mod-reduce-p3 hi lo)
+ (expt 2 81))))
+
+;; p3: the third reduction is less than 2*p3
+(defthmd simple-mod-reduce-p3-<-2*p3
+ (implies (and (< hi (expt 2 64))
+ (< lo (expt 2 64))
+ (< (join hi lo) (expt 2 81))
+ (natp hi) (natp lo))
+ (< (simple-mod-reduce-p3 hi lo)
+ (* 2 (p3)))))
+
+
+; -------------------------------------------------------------------------
+; The simple modular reductions, adapted for compiler friendly C
+; -------------------------------------------------------------------------
+
+(defun mod-reduce-p1 (hi lo)
+ (let* ((y hi)
+ (x y)
+ (hi (>> hi 32))
+ (x (sub lo x))
+ (hi (if (> x lo) (+ hi -1) hi))
+ (y (<< y 32))
+ (lo (add y x))
+ (hi (if (< lo y) (+ hi 1) hi)))
+ (+ (* hi (expt 2 64)) lo)))
+
+(defun mod-reduce-p2 (hi lo)
+ (let* ((y hi)
+ (x y)
+ (hi (>> hi 30))
+ (x (sub lo x))
+ (hi (if (> x lo) (+ hi -1) hi))
+ (y (<< y 34))
+ (lo (add y x))
+ (hi (if (< lo y) (+ hi 1) hi)))
+ (+ (* hi (expt 2 64)) lo)))
+
+(defun mod-reduce-p3 (hi lo)
+ (let* ((y hi)
+ (x y)
+ (hi (>> hi 24))
+ (x (sub lo x))
+ (hi (if (> x lo) (+ hi -1) hi))
+ (y (<< y 40))
+ (lo (add y x))
+ (hi (if (< lo y) (+ hi 1) hi)))
+ (+ (* hi (expt 2 64)) lo)))
+
+
+; -------------------------------------------------------------------------
+; The compiler friendly versions are equal to the simple versions
+; -------------------------------------------------------------------------
+
+(defthm mod-reduce-aux1
+ (implies (and (<= 0 a) (natp a) (natp m)
+ (< (- m) b) (<= b 0)
+ (integerp b)
+ (< (mod (+ b a) m)
+ (mod a m)))
+ (equal (mod (+ b a) m)
+ (+ b (mod a m))))
+ :hints (("Subgoal 2" :use ((:instance modaux-1b
+ (x (+ a b)))))))
+
+(defthm mod-reduce-aux2
+ (implies (and (<= 0 a) (natp a) (natp m)
+ (< b m) (natp b)
+ (< (mod (+ b a) m)
+ (mod a m)))
+ (equal (+ m (mod (+ b a) m))
+ (+ b (mod a m)))))
+
+
+(defthm mod-reduce-aux3
+ (implies (and (< 0 a) (natp a) (natp m)
+ (< (- m) b) (< b 0)
+ (integerp b)
+ (<= (mod a m)
+ (mod (+ b a) m)))
+ (equal (+ (- m) (mod (+ b a) m))
+ (+ b (mod a m))))
+ :hints (("Subgoal 1.2'" :use ((:instance modaux-1b
+ (x b))))
+ ("Subgoal 1''" :use ((:instance modaux-2d
+ (x I))))))
+
+
+(defthm mod-reduce-aux4
+ (implies (and (< 0 a) (natp a) (natp m)
+ (< b m) (natp b)
+ (<= (mod a m)
+ (mod (+ b a) m)))
+ (equal (mod (+ b a) m)
+ (+ b (mod a m)))))
+
+
+(defthm mod-reduce-p1==simple-mod-reduce-p1
+ (implies (and (< hi (expt 2 64))
+ (< lo (expt 2 64))
+ (natp hi) (natp lo))
+ (equal (mod-reduce-p1 hi lo)
+ (simple-mod-reduce-p1 hi lo)))
+ :hints (("Goal" :in-theory (disable expt)
+ :cases ((< 0 hi)))
+ ("Subgoal 1.2.2'" :use ((:instance mod-reduce-aux1
+ (m (expt 2 64))
+ (b (+ (- HI) LO))
+ (a (* (expt 2 32) hi)))))
+ ("Subgoal 1.2.1'" :use ((:instance mod-reduce-aux3
+ (m (expt 2 64))
+ (b (+ (- HI) LO))
+ (a (* (expt 2 32) hi)))))
+ ("Subgoal 1.1.2'" :use ((:instance mod-reduce-aux2
+ (m (expt 2 64))
+ (b (+ (- HI) LO))
+ (a (* (expt 2 32) hi)))))
+ ("Subgoal 1.1.1'" :use ((:instance mod-reduce-aux4
+ (m (expt 2 64))
+ (b (+ (- HI) LO))
+ (a (* (expt 2 32) hi)))))))
+
+
+(defthm mod-reduce-p2==simple-mod-reduce-p2
+ (implies (and (< hi (expt 2 64))
+ (< lo (expt 2 64))
+ (natp hi) (natp lo))
+ (equal (mod-reduce-p2 hi lo)
+ (simple-mod-reduce-p2 hi lo)))
+ :hints (("Goal" :cases ((< 0 hi)))
+ ("Subgoal 1.2.2'" :use ((:instance mod-reduce-aux1
+ (m (expt 2 64))
+ (b (+ (- HI) LO))
+ (a (* (expt 2 34) hi)))))
+ ("Subgoal 1.2.1'" :use ((:instance mod-reduce-aux3
+ (m (expt 2 64))
+ (b (+ (- HI) LO))
+ (a (* (expt 2 34) hi)))))
+ ("Subgoal 1.1.2'" :use ((:instance mod-reduce-aux2
+ (m (expt 2 64))
+ (b (+ (- HI) LO))
+ (a (* (expt 2 34) hi)))))
+ ("Subgoal 1.1.1'" :use ((:instance mod-reduce-aux4
+ (m (expt 2 64))
+ (b (+ (- HI) LO))
+ (a (* (expt 2 34) hi)))))))
+
+
+(defthm mod-reduce-p3==simple-mod-reduce-p3
+ (implies (and (< hi (expt 2 64))
+ (< lo (expt 2 64))
+ (natp hi) (natp lo))
+ (equal (mod-reduce-p3 hi lo)
+ (simple-mod-reduce-p3 hi lo)))
+ :hints (("Goal" :cases ((< 0 hi)))
+ ("Subgoal 1.2.2'" :use ((:instance mod-reduce-aux1
+ (m (expt 2 64))
+ (b (+ (- HI) LO))
+ (a (* (expt 2 40) hi)))))
+ ("Subgoal 1.2.1'" :use ((:instance mod-reduce-aux3
+ (m (expt 2 64))
+ (b (+ (- HI) LO))
+ (a (* (expt 2 40) hi)))))
+ ("Subgoal 1.1.2'" :use ((:instance mod-reduce-aux2
+ (m (expt 2 64))
+ (b (+ (- HI) LO))
+ (a (* (expt 2 40) hi)))))
+ ("Subgoal 1.1.1'" :use ((:instance mod-reduce-aux4
+ (m (expt 2 64))
+ (b (+ (- HI) LO))
+ (a (* (expt 2 40) hi)))))))
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include "mpdecimal.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include "typearith.h"
+#include "memory.h"
+
+
+/* Guaranteed minimum allocation for a coefficient. May be changed once
+ at program start using mpd_setminalloc(). */
+mpd_ssize_t MPD_MINALLOC = MPD_MINALLOC_MIN;
+
+/* Custom allocation and free functions */
+void *(* mpd_mallocfunc)(size_t size) = malloc;
+void *(* mpd_reallocfunc)(void *ptr, size_t size) = realloc;
+void *(* mpd_callocfunc)(size_t nmemb, size_t size) = calloc;
+void (* mpd_free)(void *ptr) = free;
+
+
+/* emulate calloc if it is not available */
+void *
+mpd_callocfunc_em(size_t nmemb, size_t size)
+{
+ void *ptr;
+ size_t req;
+ mpd_size_t overflow;
+
+#if MPD_SIZE_MAX < SIZE_MAX
+ /* full_coverage test only */
+ if (nmemb > MPD_SIZE_MAX || size > MPD_SIZE_MAX) {
+ return NULL;
+ }
+#endif
+
+ req = mul_size_t_overflow((mpd_size_t)nmemb, (mpd_size_t)size,
+ &overflow);
+ if (overflow) {
+ return NULL;
+ }
+
+ ptr = mpd_mallocfunc(req);
+ if (ptr == NULL) {
+ return NULL;
+ }
+ /* used on uint32_t or uint64_t */
+ memset(ptr, 0, req);
+
+ return ptr;
+}
+
+
+/* malloc with overflow checking */
+void *
+mpd_alloc(mpd_size_t nmemb, mpd_size_t size)
+{
+ mpd_size_t req, overflow;
+
+ req = mul_size_t_overflow(nmemb, size, &overflow);
+ if (overflow) {
+ return NULL;
+ }
+
+ return mpd_mallocfunc(req);
+}
+
+/* calloc with overflow checking */
+void *
+mpd_calloc(mpd_size_t nmemb, mpd_size_t size)
+{
+ mpd_size_t overflow;
+
+ (void)mul_size_t_overflow(nmemb, size, &overflow);
+ if (overflow) {
+ return NULL;
+ }
+
+ return mpd_callocfunc(nmemb, size);
+}
+
+/* realloc with overflow checking */
+void *
+mpd_realloc(void *ptr, mpd_size_t nmemb, mpd_size_t size, uint8_t *err)
+{
+ void *new;
+ mpd_size_t req, overflow;
+
+ req = mul_size_t_overflow(nmemb, size, &overflow);
+ if (overflow) {
+ *err = 1;
+ return ptr;
+ }
+
+ new = mpd_reallocfunc(ptr, req);
+ if (new == NULL) {
+ *err = 1;
+ return ptr;
+ }
+
+ return new;
+}
+
+/* struct hack malloc with overflow checking */
+void *
+mpd_sh_alloc(mpd_size_t struct_size, mpd_size_t nmemb, mpd_size_t size)
+{
+ mpd_size_t req, overflow;
+
+ req = mul_size_t_overflow(nmemb, size, &overflow);
+ if (overflow) {
+ return NULL;
+ }
+
+ req = add_size_t_overflow(req, struct_size, &overflow);
+ if (overflow) {
+ return NULL;
+ }
+
+ return mpd_mallocfunc(req);
+}
+
+
+/* Allocate a new decimal with a coefficient of length 'nwords'. In case
+ of an error the return value is NULL. */
+mpd_t *
+mpd_qnew_size(mpd_ssize_t nwords)
+{
+ mpd_t *result;
+
+ nwords = (nwords < MPD_MINALLOC) ? MPD_MINALLOC : nwords;
+
+ result = mpd_alloc(1, sizeof *result);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ result->data = mpd_alloc(nwords, sizeof *result->data);
+ if (result->data == NULL) {
+ mpd_free(result);
+ return NULL;
+ }
+
+ result->flags = 0;
+ result->exp = 0;
+ result->digits = 0;
+ result->len = 0;
+ result->alloc = nwords;
+
+ return result;
+}
+
+/* Allocate a new decimal with a coefficient of length MPD_MINALLOC.
+ In case of an error the return value is NULL. */
+mpd_t *
+mpd_qnew(void)
+{
+ return mpd_qnew_size(MPD_MINALLOC);
+}
+
+/* Allocate new decimal. Caller can check for NULL or MPD_Malloc_error.
+ Raises on error. */
+mpd_t *
+mpd_new(mpd_context_t *ctx)
+{
+ mpd_t *result;
+
+ result = mpd_qnew();
+ if (result == NULL) {
+ mpd_addstatus_raise(ctx, MPD_Malloc_error);
+ }
+ return result;
+}
+
+/*
+ * Input: 'result' is a static mpd_t with a static coefficient.
+ * Assumption: 'nwords' >= result->alloc.
+ *
+ * Resize the static coefficient to a larger dynamic one and copy the
+ * existing data. If successful, the value of 'result' is unchanged.
+ * Otherwise, set 'result' to NaN and update 'status' with MPD_Malloc_error.
+ */
+int
+mpd_switch_to_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)
+{
+ mpd_uint_t *p = result->data;
+
+ assert(nwords >= result->alloc);
+
+ result->data = mpd_alloc(nwords, sizeof *result->data);
+ if (result->data == NULL) {
+ result->data = p;
+ mpd_set_qnan(result);
+ mpd_set_positive(result);
+ result->exp = result->digits = result->len = 0;
+ *status |= MPD_Malloc_error;
+ return 0;
+ }
+
+ memcpy(result->data, p, result->len * (sizeof *result->data));
+ result->alloc = nwords;
+ mpd_set_dynamic_data(result);
+ return 1;
+}
+
+/*
+ * Input: 'result' is a static mpd_t with a static coefficient.
+ *
+ * Convert the coefficient to a dynamic one that is initialized to zero. If
+ * malloc fails, set 'result' to NaN and update 'status' with MPD_Malloc_error.
+ */
+int
+mpd_switch_to_dyn_zero(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)
+{
+ mpd_uint_t *p = result->data;
+
+ result->data = mpd_calloc(nwords, sizeof *result->data);
+ if (result->data == NULL) {
+ result->data = p;
+ mpd_set_qnan(result);
+ mpd_set_positive(result);
+ result->exp = result->digits = result->len = 0;
+ *status |= MPD_Malloc_error;
+ return 0;
+ }
+
+ result->alloc = nwords;
+ mpd_set_dynamic_data(result);
+
+ return 1;
+}
+
+/*
+ * Input: 'result' is a static or a dynamic mpd_t with a dynamic coefficient.
+ * Resize the coefficient to length 'nwords':
+ * Case nwords > result->alloc:
+ * If realloc is successful:
+ * 'result' has a larger coefficient but the same value. Return 1.
+ * Otherwise:
+ * Set 'result' to NaN, update status with MPD_Malloc_error and return 0.
+ * Case nwords < result->alloc:
+ * If realloc is successful:
+ * 'result' has a smaller coefficient. result->len is undefined. Return 1.
+ * Otherwise (unlikely):
+ * 'result' is unchanged. Reuse the now oversized coefficient. Return 1.
+ */
+int
+mpd_realloc_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)
+{
+ uint8_t err = 0;
+
+ result->data = mpd_realloc(result->data, nwords, sizeof *result->data, &err);
+ if (!err) {
+ result->alloc = nwords;
+ }
+ else if (nwords > result->alloc) {
+ mpd_set_qnan(result);
+ mpd_set_positive(result);
+ result->exp = result->digits = result->len = 0;
+ *status |= MPD_Malloc_error;
+ return 0;
+ }
+
+ return 1;
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef MEMORY_H
+#define MEMORY_H
+
+
+#include "mpdecimal.h"
+
+
+int mpd_switch_to_dyn(mpd_t *result, mpd_ssize_t size, uint32_t *status);
+int mpd_switch_to_dyn_zero(mpd_t *result, mpd_ssize_t size, uint32_t *status);
+int mpd_realloc_dyn(mpd_t *result, mpd_ssize_t size, uint32_t *status);
+
+
+#endif
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include "mpdecimal.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <math.h>
+#include "basearith.h"
+#include "bits.h"
+#include "convolute.h"
+#include "crt.h"
+#include "errno.h"
+#include "memory.h"
+#include "typearith.h"
+#include "umodarith.h"
+
+#ifdef PPRO
+ #if defined(_MSC_VER)
+ #include <float.h>
+ #pragma fenv_access(on)
+ #elif !defined(__OpenBSD__) && !defined(__NetBSD__)
+ /* C99 */
+ #include <fenv.h>
+ #pragma STDC FENV_ACCESS ON
+ #endif
+#endif
+
+#if defined(__x86_64__) && defined(__GLIBC__) && !defined(__INTEL_COMPILER)
+ #define USE_80BIT_LONG_DOUBLE
+#endif
+
+#if defined(_MSC_VER)
+ #define ALWAYS_INLINE __forceinline
+#elif defined(LEGACY_COMPILER)
+ #define ALWAYS_INLINE
+ #undef inline
+ #define inline
+#else
+ #ifdef TEST_COVERAGE
+ #define ALWAYS_INLINE
+ #else
+ #define ALWAYS_INLINE inline __attribute__ ((always_inline))
+ #endif
+#endif
+
+
+#define MPD_NEWTONDIV_CUTOFF 1024L
+
+#define MPD_NEW_STATIC(name, flags, exp, digits, len) \
+ mpd_uint_t name##_data[MPD_MINALLOC_MAX]; \
+ mpd_t name = {flags|MPD_STATIC|MPD_STATIC_DATA, exp, digits, \
+ len, MPD_MINALLOC_MAX, name##_data}
+
+#define MPD_NEW_CONST(name, flags, exp, digits, len, alloc, initval) \
+ mpd_uint_t name##_data[alloc] = {initval}; \
+ mpd_t name = {flags|MPD_STATIC|MPD_CONST_DATA, exp, digits, \
+ len, alloc, name##_data}
+
+#define MPD_NEW_SHARED(name, a) \
+ mpd_t name = {(a->flags&~MPD_DATAFLAGS)|MPD_STATIC|MPD_SHARED_DATA, \
+ a->exp, a->digits, a->len, a->alloc, a->data}
+
+
+static mpd_uint_t data_one[1] = {1};
+static mpd_uint_t data_zero[1] = {0};
+static const mpd_t one = {MPD_STATIC|MPD_CONST_DATA, 0, 1, 1, 1, data_one};
+static const mpd_t minus_one = {MPD_NEG|MPD_STATIC|MPD_CONST_DATA, 0, 1, 1, 1,
+ data_one};
+static const mpd_t zero = {MPD_STATIC|MPD_CONST_DATA, 0, 1, 1, 1, data_zero};
+
+static inline void _mpd_check_exp(mpd_t *dec, const mpd_context_t *ctx,
+ uint32_t *status);
+static void _settriple(mpd_t *result, uint8_t sign, mpd_uint_t a,
+ mpd_ssize_t exp);
+static inline mpd_ssize_t _mpd_real_size(mpd_uint_t *data, mpd_ssize_t size);
+
+static void _mpd_qadd(mpd_t *result, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status);
+static inline void _mpd_qmul(mpd_t *result, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status);
+static void _mpd_qbarrett_divmod(mpd_t *q, mpd_t *r, const mpd_t *a,
+ const mpd_t *b, uint32_t *status);
+static inline void _mpd_qpow_uint(mpd_t *result, mpd_t *base, mpd_uint_t exp,
+ uint8_t resultsign, const mpd_context_t *ctx, uint32_t *status);
+
+mpd_uint_t mpd_qsshiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n);
+
+
+/******************************************************************************/
+/* Performance critical inline functions */
+/******************************************************************************/
+
+#ifdef CONFIG_64
+/* Digits in a word, primarily useful for the most significant word. */
+ALWAYS_INLINE int
+mpd_word_digits(mpd_uint_t word)
+{
+ if (word < mpd_pow10[9]) {
+ if (word < mpd_pow10[4]) {
+ if (word < mpd_pow10[2]) {
+ return (word < mpd_pow10[1]) ? 1 : 2;
+ }
+ return (word < mpd_pow10[3]) ? 3 : 4;
+ }
+ if (word < mpd_pow10[6]) {
+ return (word < mpd_pow10[5]) ? 5 : 6;
+ }
+ if (word < mpd_pow10[8]) {
+ return (word < mpd_pow10[7]) ? 7 : 8;
+ }
+ return 9;
+ }
+ if (word < mpd_pow10[14]) {
+ if (word < mpd_pow10[11]) {
+ return (word < mpd_pow10[10]) ? 10 : 11;
+ }
+ if (word < mpd_pow10[13]) {
+ return (word < mpd_pow10[12]) ? 12 : 13;
+ }
+ return 14;
+ }
+ if (word < mpd_pow10[18]) {
+ if (word < mpd_pow10[16]) {
+ return (word < mpd_pow10[15]) ? 15 : 16;
+ }
+ return (word < mpd_pow10[17]) ? 17 : 18;
+ }
+
+ return (word < mpd_pow10[19]) ? 19 : 20;
+}
+#else
+ALWAYS_INLINE int
+mpd_word_digits(mpd_uint_t word)
+{
+ if (word < mpd_pow10[4]) {
+ if (word < mpd_pow10[2]) {
+ return (word < mpd_pow10[1]) ? 1 : 2;
+ }
+ return (word < mpd_pow10[3]) ? 3 : 4;
+ }
+ if (word < mpd_pow10[6]) {
+ return (word < mpd_pow10[5]) ? 5 : 6;
+ }
+ if (word < mpd_pow10[8]) {
+ return (word < mpd_pow10[7]) ? 7 : 8;
+ }
+
+ return (word < mpd_pow10[9]) ? 9 : 10;
+}
+#endif
+
+
+/* Adjusted exponent */
+ALWAYS_INLINE mpd_ssize_t
+mpd_adjexp(const mpd_t *dec)
+{
+ return (dec->exp + dec->digits) - 1;
+}
+
+/* Etiny */
+ALWAYS_INLINE mpd_ssize_t
+mpd_etiny(const mpd_context_t *ctx)
+{
+ return ctx->emin - (ctx->prec - 1);
+}
+
+/* Etop: used for folding down in IEEE clamping */
+ALWAYS_INLINE mpd_ssize_t
+mpd_etop(const mpd_context_t *ctx)
+{
+ return ctx->emax - (ctx->prec - 1);
+}
+
+/* Most significant word */
+ALWAYS_INLINE mpd_uint_t
+mpd_msword(const mpd_t *dec)
+{
+ assert(dec->len > 0);
+ return dec->data[dec->len-1];
+}
+
+/* Most significant digit of a word */
+inline mpd_uint_t
+mpd_msd(mpd_uint_t word)
+{
+ int n;
+
+ n = mpd_word_digits(word);
+ return word / mpd_pow10[n-1];
+}
+
+/* Least significant digit of a word */
+ALWAYS_INLINE mpd_uint_t
+mpd_lsd(mpd_uint_t word)
+{
+ return word % 10;
+}
+
+/* Coefficient size needed to store 'digits' */
+ALWAYS_INLINE mpd_ssize_t
+mpd_digits_to_size(mpd_ssize_t digits)
+{
+ mpd_ssize_t q, r;
+
+ _mpd_idiv_word(&q, &r, digits, MPD_RDIGITS);
+ return (r == 0) ? q : q+1;
+}
+
+/* Number of digits in the exponent. Not defined for MPD_SSIZE_MIN. */
+inline int
+mpd_exp_digits(mpd_ssize_t exp)
+{
+ exp = (exp < 0) ? -exp : exp;
+ return mpd_word_digits(exp);
+}
+
+/* Canonical */
+ALWAYS_INLINE int
+mpd_iscanonical(const mpd_t *dec UNUSED)
+{
+ return 1;
+}
+
+/* Finite */
+ALWAYS_INLINE int
+mpd_isfinite(const mpd_t *dec)
+{
+ return !(dec->flags & MPD_SPECIAL);
+}
+
+/* Infinite */
+ALWAYS_INLINE int
+mpd_isinfinite(const mpd_t *dec)
+{
+ return dec->flags & MPD_INF;
+}
+
+/* NaN */
+ALWAYS_INLINE int
+mpd_isnan(const mpd_t *dec)
+{
+ return dec->flags & (MPD_NAN|MPD_SNAN);
+}
+
+/* Negative */
+ALWAYS_INLINE int
+mpd_isnegative(const mpd_t *dec)
+{
+ return dec->flags & MPD_NEG;
+}
+
+/* Positive */
+ALWAYS_INLINE int
+mpd_ispositive(const mpd_t *dec)
+{
+ return !(dec->flags & MPD_NEG);
+}
+
+/* qNaN */
+ALWAYS_INLINE int
+mpd_isqnan(const mpd_t *dec)
+{
+ return dec->flags & MPD_NAN;
+}
+
+/* Signed */
+ALWAYS_INLINE int
+mpd_issigned(const mpd_t *dec)
+{
+ return dec->flags & MPD_NEG;
+}
+
+/* sNaN */
+ALWAYS_INLINE int
+mpd_issnan(const mpd_t *dec)
+{
+ return dec->flags & MPD_SNAN;
+}
+
+/* Special */
+ALWAYS_INLINE int
+mpd_isspecial(const mpd_t *dec)
+{
+ return dec->flags & MPD_SPECIAL;
+}
+
+/* Zero */
+ALWAYS_INLINE int
+mpd_iszero(const mpd_t *dec)
+{
+ return !mpd_isspecial(dec) && mpd_msword(dec) == 0;
+}
+
+/* Test for zero when specials have been ruled out already */
+ALWAYS_INLINE int
+mpd_iszerocoeff(const mpd_t *dec)
+{
+ return mpd_msword(dec) == 0;
+}
+
+/* Normal */
+inline int
+mpd_isnormal(const mpd_t *dec, const mpd_context_t *ctx)
+{
+ if (mpd_isspecial(dec)) return 0;
+ if (mpd_iszerocoeff(dec)) return 0;
+
+ return mpd_adjexp(dec) >= ctx->emin;
+}
+
+/* Subnormal */
+inline int
+mpd_issubnormal(const mpd_t *dec, const mpd_context_t *ctx)
+{
+ if (mpd_isspecial(dec)) return 0;
+ if (mpd_iszerocoeff(dec)) return 0;
+
+ return mpd_adjexp(dec) < ctx->emin;
+}
+
+/* Odd word */
+ALWAYS_INLINE int
+mpd_isoddword(mpd_uint_t word)
+{
+ return word & 1;
+}
+
+/* Odd coefficient */
+ALWAYS_INLINE int
+mpd_isoddcoeff(const mpd_t *dec)
+{
+ return mpd_isoddword(dec->data[0]);
+}
+
+/* 0 if dec is positive, 1 if dec is negative */
+ALWAYS_INLINE uint8_t
+mpd_sign(const mpd_t *dec)
+{
+ return dec->flags & MPD_NEG;
+}
+
+/* 1 if dec is positive, -1 if dec is negative */
+ALWAYS_INLINE int
+mpd_arith_sign(const mpd_t *dec)
+{
+ return 1 - 2 * mpd_isnegative(dec);
+}
+
+/* Radix */
+ALWAYS_INLINE long
+mpd_radix(void)
+{
+ return 10;
+}
+
+/* Dynamic decimal */
+ALWAYS_INLINE int
+mpd_isdynamic(mpd_t *dec)
+{
+ return !(dec->flags & MPD_STATIC);
+}
+
+/* Static decimal */
+ALWAYS_INLINE int
+mpd_isstatic(mpd_t *dec)
+{
+ return dec->flags & MPD_STATIC;
+}
+
+/* Data of decimal is dynamic */
+ALWAYS_INLINE int
+mpd_isdynamic_data(mpd_t *dec)
+{
+ return !(dec->flags & MPD_DATAFLAGS);
+}
+
+/* Data of decimal is static */
+ALWAYS_INLINE int
+mpd_isstatic_data(mpd_t *dec)
+{
+ return dec->flags & MPD_STATIC_DATA;
+}
+
+/* Data of decimal is shared */
+ALWAYS_INLINE int
+mpd_isshared_data(mpd_t *dec)
+{
+ return dec->flags & MPD_SHARED_DATA;
+}
+
+/* Data of decimal is const */
+ALWAYS_INLINE int
+mpd_isconst_data(mpd_t *dec)
+{
+ return dec->flags & MPD_CONST_DATA;
+}
+
+
+/******************************************************************************/
+/* Inline memory handling */
+/******************************************************************************/
+
+/* Fill destination with zeros */
+ALWAYS_INLINE void
+mpd_uint_zero(mpd_uint_t *dest, mpd_size_t len)
+{
+ mpd_size_t i;
+
+ for (i = 0; i < len; i++) {
+ dest[i] = 0;
+ }
+}
+
+/* Free a decimal */
+ALWAYS_INLINE void
+mpd_del(mpd_t *dec)
+{
+ if (mpd_isdynamic_data(dec)) {
+ mpd_free(dec->data);
+ }
+ if (mpd_isdynamic(dec)) {
+ mpd_free(dec);
+ }
+}
+
+/*
+ * Resize the coefficient. Existing data up to 'nwords' is left untouched.
+ * Return 1 on success, 0 otherwise.
+ *
+ * Input invariants:
+ * 1) MPD_MINALLOC <= result->alloc.
+ * 2) 0 <= result->len <= result->alloc.
+ *
+ * Case nwords > result->alloc:
+ * Case realloc success:
+ * The value of 'result' does not change. Return 1.
+ * Case realloc failure:
+ * 'result' is NaN, status is updated with MPD_Malloc_error. Return 0.
+ *
+ * Case nwords < result->alloc:
+ * Case is_static_data or nwords < MPD_MINALLOC or realloc failure [1]:
+ * 'result' is unchanged. Return 1.
+ * Case realloc success:
+ * The value of result is undefined (expected). Return 1.
+ *
+ * Case nwords == result->alloc:
+ * 'result' is unchanged. Return 1.
+ *
+ * [1] In that case the old (now oversized) area is still valid.
+ */
+ALWAYS_INLINE int
+mpd_qresize(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)
+{
+ assert(!mpd_isconst_data(result)); /* illegal operation for a const */
+ assert(!mpd_isshared_data(result)); /* illegal operation for a shared */
+
+ if (mpd_isstatic_data(result)) {
+ if (nwords > result->alloc) {
+ return mpd_switch_to_dyn(result, nwords, status);
+ }
+ }
+ else if (nwords != result->alloc && nwords >= MPD_MINALLOC) {
+ return mpd_realloc_dyn(result, nwords, status);
+ }
+
+ return 1;
+}
+
+/* Same as mpd_qresize, but the complete coefficient (including the old
+ * memory area!) is initialized to zero. */
+ALWAYS_INLINE int
+mpd_qresize_zero(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)
+{
+ assert(!mpd_isconst_data(result)); /* illegal operation for a const */
+ assert(!mpd_isshared_data(result)); /* illegal operation for a shared */
+
+ if (mpd_isstatic_data(result)) {
+ if (nwords > result->alloc) {
+ return mpd_switch_to_dyn_zero(result, nwords, status);
+ }
+ }
+ else if (nwords != result->alloc && nwords >= MPD_MINALLOC) {
+ if (!mpd_realloc_dyn(result, nwords, status)) {
+ return 0;
+ }
+ }
+
+ mpd_uint_zero(result->data, nwords);
+
+ return 1;
+}
+
+/*
+ * Reduce memory size for the coefficient to MPD_MINALLOC. In theory,
+ * realloc may fail even when reducing the memory size. But in that case
+ * the old memory area is always big enough, so checking for MPD_Malloc_error
+ * is not imperative.
+ */
+ALWAYS_INLINE void
+mpd_minalloc(mpd_t *result)
+{
+ assert(!mpd_isconst_data(result)); /* illegal operation for a const */
+ assert(!mpd_isshared_data(result)); /* illegal operation for a shared */
+
+ if (!mpd_isstatic_data(result) && result->alloc > MPD_MINALLOC) {
+ uint8_t err = 0;
+ result->data = mpd_realloc(result->data, MPD_MINALLOC,
+ sizeof *result->data, &err);
+ if (!err) {
+ result->alloc = MPD_MINALLOC;
+ }
+ }
+}
+
+int
+mpd_resize(mpd_t *result, mpd_ssize_t nwords, mpd_context_t *ctx)
+{
+ uint32_t status = 0;
+ if (!mpd_qresize(result, nwords, &status)) {
+ mpd_addstatus_raise(ctx, status);
+ return 0;
+ }
+ return 1;
+}
+
+int
+mpd_resize_zero(mpd_t *result, mpd_ssize_t nwords, mpd_context_t *ctx)
+{
+ uint32_t status = 0;
+ if (!mpd_qresize_zero(result, nwords, &status)) {
+ mpd_addstatus_raise(ctx, status);
+ return 0;
+ }
+ return 1;
+}
+
+
+/******************************************************************************/
+/* Set attributes of a decimal */
+/******************************************************************************/
+
+/* Set digits. Assumption: result->len is initialized and > 0. */
+inline void
+mpd_setdigits(mpd_t *result)
+{
+ mpd_ssize_t wdigits = mpd_word_digits(mpd_msword(result));
+ result->digits = wdigits + (result->len-1) * MPD_RDIGITS;
+}
+
+/* Set sign */
+ALWAYS_INLINE void
+mpd_set_sign(mpd_t *result, uint8_t sign)
+{
+ result->flags &= ~MPD_NEG;
+ result->flags |= sign;
+}
+
+/* Copy sign from another decimal */
+ALWAYS_INLINE void
+mpd_signcpy(mpd_t *result, mpd_t *a)
+{
+ uint8_t sign = a->flags&MPD_NEG;
+
+ result->flags &= ~MPD_NEG;
+ result->flags |= sign;
+}
+
+/* Set infinity */
+ALWAYS_INLINE void
+mpd_set_infinity(mpd_t *result)
+{
+ result->flags &= ~MPD_SPECIAL;
+ result->flags |= MPD_INF;
+}
+
+/* Set qNaN */
+ALWAYS_INLINE void
+mpd_set_qnan(mpd_t *result)
+{
+ result->flags &= ~MPD_SPECIAL;
+ result->flags |= MPD_NAN;
+}
+
+/* Set sNaN */
+ALWAYS_INLINE void
+mpd_set_snan(mpd_t *result)
+{
+ result->flags &= ~MPD_SPECIAL;
+ result->flags |= MPD_SNAN;
+}
+
+/* Set to negative */
+ALWAYS_INLINE void
+mpd_set_negative(mpd_t *result)
+{
+ result->flags |= MPD_NEG;
+}
+
+/* Set to positive */
+ALWAYS_INLINE void
+mpd_set_positive(mpd_t *result)
+{
+ result->flags &= ~MPD_NEG;
+}
+
+/* Set to dynamic */
+ALWAYS_INLINE void
+mpd_set_dynamic(mpd_t *result)
+{
+ result->flags &= ~MPD_STATIC;
+}
+
+/* Set to static */
+ALWAYS_INLINE void
+mpd_set_static(mpd_t *result)
+{
+ result->flags |= MPD_STATIC;
+}
+
+/* Set data to dynamic */
+ALWAYS_INLINE void
+mpd_set_dynamic_data(mpd_t *result)
+{
+ result->flags &= ~MPD_DATAFLAGS;
+}
+
+/* Set data to static */
+ALWAYS_INLINE void
+mpd_set_static_data(mpd_t *result)
+{
+ result->flags &= ~MPD_DATAFLAGS;
+ result->flags |= MPD_STATIC_DATA;
+}
+
+/* Set data to shared */
+ALWAYS_INLINE void
+mpd_set_shared_data(mpd_t *result)
+{
+ result->flags &= ~MPD_DATAFLAGS;
+ result->flags |= MPD_SHARED_DATA;
+}
+
+/* Set data to const */
+ALWAYS_INLINE void
+mpd_set_const_data(mpd_t *result)
+{
+ result->flags &= ~MPD_DATAFLAGS;
+ result->flags |= MPD_CONST_DATA;
+}
+
+/* Clear flags, preserving memory attributes. */
+ALWAYS_INLINE void
+mpd_clear_flags(mpd_t *result)
+{
+ result->flags &= (MPD_STATIC|MPD_DATAFLAGS);
+}
+
+/* Set flags, preserving memory attributes. */
+ALWAYS_INLINE void
+mpd_set_flags(mpd_t *result, uint8_t flags)
+{
+ result->flags &= (MPD_STATIC|MPD_DATAFLAGS);
+ result->flags |= flags;
+}
+
+/* Copy flags, preserving memory attributes of result. */
+ALWAYS_INLINE void
+mpd_copy_flags(mpd_t *result, const mpd_t *a)
+{
+ uint8_t aflags = a->flags;
+ result->flags &= (MPD_STATIC|MPD_DATAFLAGS);
+ result->flags |= (aflags & ~(MPD_STATIC|MPD_DATAFLAGS));
+}
+
+/* Initialize a workcontext from ctx. Set traps, flags and newtrap to 0. */
+static inline void
+mpd_workcontext(mpd_context_t *workctx, const mpd_context_t *ctx)
+{
+ workctx->prec = ctx->prec;
+ workctx->emax = ctx->emax;
+ workctx->emin = ctx->emin;
+ workctx->round = ctx->round;
+ workctx->traps = 0;
+ workctx->status = 0;
+ workctx->newtrap = 0;
+ workctx->clamp = ctx->clamp;
+ workctx->allcr = ctx->allcr;
+}
+
+
+/******************************************************************************/
+/* Getting and setting parts of decimals */
+/******************************************************************************/
+
+/* Flip the sign of a decimal */
+static inline void
+_mpd_negate(mpd_t *dec)
+{
+ dec->flags ^= MPD_NEG;
+}
+
+/* Set coefficient to zero */
+void
+mpd_zerocoeff(mpd_t *result)
+{
+ mpd_minalloc(result);
+ result->digits = 1;
+ result->len = 1;
+ result->data[0] = 0;
+}
+
+/* Set the coefficient to all nines. */
+void
+mpd_qmaxcoeff(mpd_t *result, const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_ssize_t len, r;
+
+ _mpd_idiv_word(&len, &r, ctx->prec, MPD_RDIGITS);
+ len = (r == 0) ? len : len+1;
+
+ if (!mpd_qresize(result, len, status)) {
+ return;
+ }
+
+ result->len = len;
+ result->digits = ctx->prec;
+
+ --len;
+ if (r > 0) {
+ result->data[len--] = mpd_pow10[r]-1;
+ }
+ for (; len >= 0; --len) {
+ result->data[len] = MPD_RADIX-1;
+ }
+}
+
+/*
+ * Cut off the most significant digits so that the rest fits in ctx->prec.
+ * Cannot fail.
+ */
+static void
+_mpd_cap(mpd_t *result, const mpd_context_t *ctx)
+{
+ uint32_t dummy;
+ mpd_ssize_t len, r;
+
+ if (result->len > 0 && result->digits > ctx->prec) {
+ _mpd_idiv_word(&len, &r, ctx->prec, MPD_RDIGITS);
+ len = (r == 0) ? len : len+1;
+
+ if (r != 0) {
+ result->data[len-1] %= mpd_pow10[r];
+ }
+
+ len = _mpd_real_size(result->data, len);
+ /* resize to fewer words cannot fail */
+ mpd_qresize(result, len, &dummy);
+ result->len = len;
+ mpd_setdigits(result);
+ }
+ if (mpd_iszero(result)) {
+ _settriple(result, mpd_sign(result), 0, result->exp);
+ }
+}
+
+/*
+ * Cut off the most significant digits of a NaN payload so that the rest
+ * fits in ctx->prec - ctx->clamp. Cannot fail.
+ */
+static void
+_mpd_fix_nan(mpd_t *result, const mpd_context_t *ctx)
+{
+ uint32_t dummy;
+ mpd_ssize_t prec;
+ mpd_ssize_t len, r;
+
+ prec = ctx->prec - ctx->clamp;
+ if (result->len > 0 && result->digits > prec) {
+ if (prec == 0) {
+ mpd_minalloc(result);
+ result->len = result->digits = 0;
+ }
+ else {
+ _mpd_idiv_word(&len, &r, prec, MPD_RDIGITS);
+ len = (r == 0) ? len : len+1;
+
+ if (r != 0) {
+ result->data[len-1] %= mpd_pow10[r];
+ }
+
+ len = _mpd_real_size(result->data, len);
+ /* resize to fewer words cannot fail */
+ mpd_qresize(result, len, &dummy);
+ result->len = len;
+ mpd_setdigits(result);
+ if (mpd_iszerocoeff(result)) {
+ /* NaN0 is not a valid representation */
+ result->len = result->digits = 0;
+ }
+ }
+ }
+}
+
+/*
+ * Get n most significant digits from a decimal, where 0 < n <= MPD_UINT_DIGITS.
+ * Assumes MPD_UINT_DIGITS == MPD_RDIGITS+1, which is true for 32 and 64 bit
+ * machines.
+ *
+ * The result of the operation will be in lo. If the operation is impossible,
+ * hi will be nonzero. This is used to indicate an error.
+ */
+static inline void
+_mpd_get_msdigits(mpd_uint_t *hi, mpd_uint_t *lo, const mpd_t *dec,
+ unsigned int n)
+{
+ mpd_uint_t r, tmp;
+
+ assert(0 < n && n <= MPD_RDIGITS+1);
+
+ _mpd_div_word(&tmp, &r, dec->digits, MPD_RDIGITS);
+ r = (r == 0) ? MPD_RDIGITS : r; /* digits in the most significant word */
+
+ *hi = 0;
+ *lo = dec->data[dec->len-1];
+ if (n <= r) {
+ *lo /= mpd_pow10[r-n];
+ }
+ else if (dec->len > 1) {
+ /* at this point 1 <= r < n <= MPD_RDIGITS+1 */
+ _mpd_mul_words(hi, lo, *lo, mpd_pow10[n-r]);
+ tmp = dec->data[dec->len-2] / mpd_pow10[MPD_RDIGITS-(n-r)];
+ *lo = *lo + tmp;
+ if (*lo < tmp) (*hi)++;
+ }
+}
+
+
+/******************************************************************************/
+/* Gathering information about a decimal */
+/******************************************************************************/
+
+/* The real size of the coefficient without leading zero words. */
+static inline mpd_ssize_t
+_mpd_real_size(mpd_uint_t *data, mpd_ssize_t size)
+{
+ while (size > 1 && data[size-1] == 0) {
+ size--;
+ }
+
+ return size;
+}
+
+/* Return number of trailing zeros. No errors are possible. */
+mpd_ssize_t
+mpd_trail_zeros(const mpd_t *dec)
+{
+ mpd_uint_t word;
+ mpd_ssize_t i, tz = 0;
+
+ for (i=0; i < dec->len; ++i) {
+ if (dec->data[i] != 0) {
+ word = dec->data[i];
+ tz = i * MPD_RDIGITS;
+ while (word % 10 == 0) {
+ word /= 10;
+ tz++;
+ }
+ break;
+ }
+ }
+
+ return tz;
+}
+
+/* Integer: Undefined for specials */
+static int
+_mpd_isint(const mpd_t *dec)
+{
+ mpd_ssize_t tz;
+
+ if (mpd_iszerocoeff(dec)) {
+ return 1;
+ }
+
+ tz = mpd_trail_zeros(dec);
+ return (dec->exp + tz >= 0);
+}
+
+/* Integer */
+int
+mpd_isinteger(const mpd_t *dec)
+{
+ if (mpd_isspecial(dec)) {
+ return 0;
+ }
+ return _mpd_isint(dec);
+}
+
+/* Word is a power of 10 */
+static int
+mpd_word_ispow10(mpd_uint_t word)
+{
+ int n;
+
+ n = mpd_word_digits(word);
+ if (word == mpd_pow10[n-1]) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Coefficient is a power of 10 */
+static int
+mpd_coeff_ispow10(const mpd_t *dec)
+{
+ if (mpd_word_ispow10(mpd_msword(dec))) {
+ if (_mpd_isallzero(dec->data, dec->len-1)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* All digits of a word are nines */
+static int
+mpd_word_isallnine(mpd_uint_t word)
+{
+ int n;
+
+ n = mpd_word_digits(word);
+ if (word == mpd_pow10[n]-1) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/* All digits of the coefficient are nines */
+static int
+mpd_coeff_isallnine(const mpd_t *dec)
+{
+ if (mpd_word_isallnine(mpd_msword(dec))) {
+ if (_mpd_isallnine(dec->data, dec->len-1)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Odd decimal: Undefined for non-integers! */
+int
+mpd_isodd(const mpd_t *dec)
+{
+ mpd_uint_t q, r;
+ assert(mpd_isinteger(dec));
+ if (mpd_iszerocoeff(dec)) return 0;
+ if (dec->exp < 0) {
+ _mpd_div_word(&q, &r, -dec->exp, MPD_RDIGITS);
+ q = dec->data[q] / mpd_pow10[r];
+ return mpd_isoddword(q);
+ }
+ return dec->exp == 0 && mpd_isoddword(dec->data[0]);
+}
+
+/* Even: Undefined for non-integers! */
+int
+mpd_iseven(const mpd_t *dec)
+{
+ return !mpd_isodd(dec);
+}
+
+/******************************************************************************/
+/* Getting and setting decimals */
+/******************************************************************************/
+
+/* Internal function: Set a static decimal from a triple, no error checking. */
+static void
+_ssettriple(mpd_t *result, uint8_t sign, mpd_uint_t a, mpd_ssize_t exp)
+{
+ mpd_set_flags(result, sign);
+ result->exp = exp;
+ _mpd_div_word(&result->data[1], &result->data[0], a, MPD_RADIX);
+ result->len = (result->data[1] == 0) ? 1 : 2;
+ mpd_setdigits(result);
+}
+
+/* Internal function: Set a decimal from a triple, no error checking. */
+static void
+_settriple(mpd_t *result, uint8_t sign, mpd_uint_t a, mpd_ssize_t exp)
+{
+ mpd_minalloc(result);
+ mpd_set_flags(result, sign);
+ result->exp = exp;
+ _mpd_div_word(&result->data[1], &result->data[0], a, MPD_RADIX);
+ result->len = (result->data[1] == 0) ? 1 : 2;
+ mpd_setdigits(result);
+}
+
+/* Set a special number from a triple */
+void
+mpd_setspecial(mpd_t *result, uint8_t sign, uint8_t type)
+{
+ mpd_minalloc(result);
+ result->flags &= ~(MPD_NEG|MPD_SPECIAL);
+ result->flags |= (sign|type);
+ result->exp = result->digits = result->len = 0;
+}
+
+/* Set result of NaN with an error status */
+void
+mpd_seterror(mpd_t *result, uint32_t flags, uint32_t *status)
+{
+ mpd_minalloc(result);
+ mpd_set_qnan(result);
+ mpd_set_positive(result);
+ result->exp = result->digits = result->len = 0;
+ *status |= flags;
+}
+
+/* quietly set a static decimal from an mpd_ssize_t */
+void
+mpd_qsset_ssize(mpd_t *result, mpd_ssize_t a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ mpd_uint_t u;
+ uint8_t sign = MPD_POS;
+
+ if (a < 0) {
+ if (a == MPD_SSIZE_MIN) {
+ u = (mpd_uint_t)MPD_SSIZE_MAX +
+ (-(MPD_SSIZE_MIN+MPD_SSIZE_MAX));
+ }
+ else {
+ u = -a;
+ }
+ sign = MPD_NEG;
+ }
+ else {
+ u = a;
+ }
+ _ssettriple(result, sign, u, 0);
+ mpd_qfinalize(result, ctx, status);
+}
+
+/* quietly set a static decimal from an mpd_uint_t */
+void
+mpd_qsset_uint(mpd_t *result, mpd_uint_t a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ _ssettriple(result, MPD_POS, a, 0);
+ mpd_qfinalize(result, ctx, status);
+}
+
+/* quietly set a static decimal from an int32_t */
+void
+mpd_qsset_i32(mpd_t *result, int32_t a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ mpd_qsset_ssize(result, a, ctx, status);
+}
+
+/* quietly set a static decimal from a uint32_t */
+void
+mpd_qsset_u32(mpd_t *result, uint32_t a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ mpd_qsset_uint(result, a, ctx, status);
+}
+
+#ifdef CONFIG_64
+/* quietly set a static decimal from an int64_t */
+void
+mpd_qsset_i64(mpd_t *result, int64_t a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ mpd_qsset_ssize(result, a, ctx, status);
+}
+
+/* quietly set a static decimal from a uint64_t */
+void
+mpd_qsset_u64(mpd_t *result, uint64_t a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ mpd_qsset_uint(result, a, ctx, status);
+}
+#endif
+
+/* quietly set a decimal from an mpd_ssize_t */
+void
+mpd_qset_ssize(mpd_t *result, mpd_ssize_t a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ mpd_minalloc(result);
+ mpd_qsset_ssize(result, a, ctx, status);
+}
+
+/* quietly set a decimal from an mpd_uint_t */
+void
+mpd_qset_uint(mpd_t *result, mpd_uint_t a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ _settriple(result, MPD_POS, a, 0);
+ mpd_qfinalize(result, ctx, status);
+}
+
+/* quietly set a decimal from an int32_t */
+void
+mpd_qset_i32(mpd_t *result, int32_t a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ mpd_qset_ssize(result, a, ctx, status);
+}
+
+/* quietly set a decimal from a uint32_t */
+void
+mpd_qset_u32(mpd_t *result, uint32_t a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ mpd_qset_uint(result, a, ctx, status);
+}
+
+#if defined(CONFIG_32) && !defined(LEGACY_COMPILER)
+/* set a decimal from a uint64_t */
+static void
+_c32setu64(mpd_t *result, uint64_t u, uint8_t sign, uint32_t *status)
+{
+ mpd_uint_t w[3];
+ uint64_t q;
+ int i, len;
+
+ len = 0;
+ do {
+ q = u / MPD_RADIX;
+ w[len] = (mpd_uint_t)(u - q * MPD_RADIX);
+ u = q; len++;
+ } while (u != 0);
+
+ if (!mpd_qresize(result, len, status)) {
+ return;
+ }
+ for (i = 0; i < len; i++) {
+ result->data[i] = w[i];
+ }
+
+ mpd_set_sign(result, sign);
+ result->exp = 0;
+ result->len = len;
+ mpd_setdigits(result);
+}
+
+static void
+_c32_qset_u64(mpd_t *result, uint64_t a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ _c32setu64(result, a, MPD_POS, status);
+ mpd_qfinalize(result, ctx, status);
+}
+
+/* set a decimal from an int64_t */
+static void
+_c32_qset_i64(mpd_t *result, int64_t a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ uint64_t u;
+ uint8_t sign = MPD_POS;
+
+ if (a < 0) {
+ if (a == INT64_MIN) {
+ u = (uint64_t)INT64_MAX + (-(INT64_MIN+INT64_MAX));
+ }
+ else {
+ u = -a;
+ }
+ sign = MPD_NEG;
+ }
+ else {
+ u = a;
+ }
+ _c32setu64(result, u, sign, status);
+ mpd_qfinalize(result, ctx, status);
+}
+#endif /* CONFIG_32 && !LEGACY_COMPILER */
+
+#ifndef LEGACY_COMPILER
+/* quietly set a decimal from an int64_t */
+void
+mpd_qset_i64(mpd_t *result, int64_t a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+#ifdef CONFIG_64
+ mpd_qset_ssize(result, a, ctx, status);
+#else
+ _c32_qset_i64(result, a, ctx, status);
+#endif
+}
+
+/* quietly set a decimal from a uint64_t */
+void
+mpd_qset_u64(mpd_t *result, uint64_t a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+#ifdef CONFIG_64
+ mpd_qset_uint(result, a, ctx, status);
+#else
+ _c32_qset_u64(result, a, ctx, status);
+#endif
+}
+#endif /* !LEGACY_COMPILER */
+
+
+/*
+ * Quietly get an mpd_uint_t from a decimal. Assumes
+ * MPD_UINT_DIGITS == MPD_RDIGITS+1, which is true for
+ * 32 and 64 bit machines.
+ *
+ * If the operation is impossible, MPD_Invalid_operation is set.
+ */
+static mpd_uint_t
+_mpd_qget_uint(int use_sign, const mpd_t *a, uint32_t *status)
+{
+ mpd_t tmp;
+ mpd_uint_t tmp_data[2];
+ mpd_uint_t lo, hi;
+
+ if (mpd_isspecial(a)) {
+ *status |= MPD_Invalid_operation;
+ return MPD_UINT_MAX;
+ }
+ if (mpd_iszero(a)) {
+ return 0;
+ }
+ if (use_sign && mpd_isnegative(a)) {
+ *status |= MPD_Invalid_operation;
+ return MPD_UINT_MAX;
+ }
+
+ if (a->digits+a->exp > MPD_RDIGITS+1) {
+ *status |= MPD_Invalid_operation;
+ return MPD_UINT_MAX;
+ }
+
+ if (a->exp < 0) {
+ if (!_mpd_isint(a)) {
+ *status |= MPD_Invalid_operation;
+ return MPD_UINT_MAX;
+ }
+ /* At this point a->digits+a->exp <= MPD_RDIGITS+1,
+ * so the shift fits. */
+ tmp.data = tmp_data;
+ tmp.flags = MPD_STATIC|MPD_CONST_DATA;
+ mpd_qsshiftr(&tmp, a, -a->exp);
+ tmp.exp = 0;
+ a = &tmp;
+ }
+
+ _mpd_get_msdigits(&hi, &lo, a, MPD_RDIGITS+1);
+ if (hi) {
+ *status |= MPD_Invalid_operation;
+ return MPD_UINT_MAX;
+ }
+
+ if (a->exp > 0) {
+ _mpd_mul_words(&hi, &lo, lo, mpd_pow10[a->exp]);
+ if (hi) {
+ *status |= MPD_Invalid_operation;
+ return MPD_UINT_MAX;
+ }
+ }
+
+ return lo;
+}
+
+/*
+ * Sets Invalid_operation for:
+ * - specials
+ * - negative numbers (except negative zero)
+ * - non-integers
+ * - overflow
+ */
+mpd_uint_t
+mpd_qget_uint(const mpd_t *a, uint32_t *status)
+{
+ return _mpd_qget_uint(1, a, status);
+}
+
+/* Same as above, but gets the absolute value, i.e. the sign is ignored. */
+mpd_uint_t
+mpd_qabs_uint(const mpd_t *a, uint32_t *status)
+{
+ return _mpd_qget_uint(0, a, status);
+}
+
+/* quietly get an mpd_ssize_t from a decimal */
+mpd_ssize_t
+mpd_qget_ssize(const mpd_t *a, uint32_t *status)
+{
+ mpd_uint_t u;
+ int isneg;
+
+ u = mpd_qabs_uint(a, status);
+ if (*status&MPD_Invalid_operation) {
+ return MPD_SSIZE_MAX;
+ }
+
+ isneg = mpd_isnegative(a);
+ if (u <= MPD_SSIZE_MAX) {
+ return isneg ? -((mpd_ssize_t)u) : (mpd_ssize_t)u;
+ }
+ else if (isneg && u-1 == MPD_SSIZE_MAX) {
+ return MPD_SSIZE_MIN;
+ }
+
+ *status |= MPD_Invalid_operation;
+ return MPD_SSIZE_MAX;
+}
+
+#ifdef CONFIG_64
+/* quietly get a uint64_t from a decimal */
+uint64_t
+mpd_qget_u64(const mpd_t *a, uint32_t *status)
+{
+ return mpd_qget_uint(a, status);
+}
+
+/* quietly get an int64_t from a decimal */
+int64_t
+mpd_qget_i64(const mpd_t *a, uint32_t *status)
+{
+ return mpd_qget_ssize(a, status);
+}
+#else
+/* quietly get a uint32_t from a decimal */
+uint32_t
+mpd_qget_u32(const mpd_t *a, uint32_t *status)
+{
+ return mpd_qget_uint(a, status);
+}
+
+/* quietly get an int32_t from a decimal */
+int32_t
+mpd_qget_i32(const mpd_t *a, uint32_t *status)
+{
+ return mpd_qget_ssize(a, status);
+}
+#endif
+
+
+/******************************************************************************/
+/* Filtering input of functions, finalizing output of functions */
+/******************************************************************************/
+
+/*
+ * Check if the operand is NaN, copy to result and return 1 if this is
+ * the case. Copying can fail since NaNs are allowed to have a payload that
+ * does not fit in MPD_MINALLOC.
+ */
+int
+mpd_qcheck_nan(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ if (mpd_isnan(a)) {
+ *status |= mpd_issnan(a) ? MPD_Invalid_operation : 0;
+ mpd_qcopy(result, a, status);
+ mpd_set_qnan(result);
+ _mpd_fix_nan(result, ctx);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Check if either operand is NaN, copy to result and return 1 if this
+ * is the case. Copying can fail since NaNs are allowed to have a payload
+ * that does not fit in MPD_MINALLOC.
+ */
+int
+mpd_qcheck_nans(mpd_t *result, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ if ((a->flags|b->flags)&(MPD_NAN|MPD_SNAN)) {
+ const mpd_t *choice = b;
+ if (mpd_issnan(a)) {
+ choice = a;
+ *status |= MPD_Invalid_operation;
+ }
+ else if (mpd_issnan(b)) {
+ *status |= MPD_Invalid_operation;
+ }
+ else if (mpd_isqnan(a)) {
+ choice = a;
+ }
+ mpd_qcopy(result, choice, status);
+ mpd_set_qnan(result);
+ _mpd_fix_nan(result, ctx);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Check if one of the operands is NaN, copy to result and return 1 if this
+ * is the case. Copying can fail since NaNs are allowed to have a payload
+ * that does not fit in MPD_MINALLOC.
+ */
+static int
+mpd_qcheck_3nans(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ if ((a->flags|b->flags|c->flags)&(MPD_NAN|MPD_SNAN)) {
+ const mpd_t *choice = c;
+ if (mpd_issnan(a)) {
+ choice = a;
+ *status |= MPD_Invalid_operation;
+ }
+ else if (mpd_issnan(b)) {
+ choice = b;
+ *status |= MPD_Invalid_operation;
+ }
+ else if (mpd_issnan(c)) {
+ *status |= MPD_Invalid_operation;
+ }
+ else if (mpd_isqnan(a)) {
+ choice = a;
+ }
+ else if (mpd_isqnan(b)) {
+ choice = b;
+ }
+ mpd_qcopy(result, choice, status);
+ mpd_set_qnan(result);
+ _mpd_fix_nan(result, ctx);
+ return 1;
+ }
+ return 0;
+}
+
+/* Check if rounding digit 'rnd' leads to an increment. */
+static inline int
+_mpd_rnd_incr(const mpd_t *dec, mpd_uint_t rnd, const mpd_context_t *ctx)
+{
+ int ld;
+
+ switch (ctx->round) {
+ case MPD_ROUND_DOWN: case MPD_ROUND_TRUNC:
+ return 0;
+ case MPD_ROUND_HALF_UP:
+ return (rnd >= 5);
+ case MPD_ROUND_HALF_EVEN:
+ return (rnd > 5) || ((rnd == 5) && mpd_isoddcoeff(dec));
+ case MPD_ROUND_CEILING:
+ return !(rnd == 0 || mpd_isnegative(dec));
+ case MPD_ROUND_FLOOR:
+ return !(rnd == 0 || mpd_ispositive(dec));
+ case MPD_ROUND_HALF_DOWN:
+ return (rnd > 5);
+ case MPD_ROUND_UP:
+ return !(rnd == 0);
+ case MPD_ROUND_05UP:
+ ld = (int)mpd_lsd(dec->data[0]);
+ return (!(rnd == 0) && (ld == 0 || ld == 5));
+ default:
+ /* Without a valid context, further results will be undefined. */
+ return 0; /* GCOV_NOT_REACHED */
+ }
+}
+
+/*
+ * Apply rounding to a decimal that has been right-shifted into a full
+ * precision decimal. If an increment leads to an overflow of the precision,
+ * adjust the coefficient and the exponent and check the new exponent for
+ * overflow.
+ */
+static inline void
+_mpd_apply_round(mpd_t *dec, mpd_uint_t rnd, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ if (_mpd_rnd_incr(dec, rnd, ctx)) {
+ /* We have a number with exactly ctx->prec digits. The increment
+ * can only lead to an overflow if the decimal is all nines. In
+ * that case, the result is a power of ten with prec+1 digits.
+ *
+ * If the precision is a multiple of MPD_RDIGITS, this situation is
+ * detected by _mpd_baseincr returning a carry.
+ * If the precision is not a multiple of MPD_RDIGITS, we have to
+ * check if the result has one digit too many.
+ */
+ mpd_uint_t carry = _mpd_baseincr(dec->data, dec->len);
+ if (carry) {
+ dec->data[dec->len-1] = mpd_pow10[MPD_RDIGITS-1];
+ dec->exp += 1;
+ _mpd_check_exp(dec, ctx, status);
+ return;
+ }
+ mpd_setdigits(dec);
+ if (dec->digits > ctx->prec) {
+ mpd_qshiftr_inplace(dec, 1);
+ dec->exp += 1;
+ dec->digits = ctx->prec;
+ _mpd_check_exp(dec, ctx, status);
+ }
+ }
+}
+
+/*
+ * Apply rounding to a decimal. Allow overflow of the precision.
+ */
+static inline void
+_mpd_apply_round_excess(mpd_t *dec, mpd_uint_t rnd, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ if (_mpd_rnd_incr(dec, rnd, ctx)) {
+ mpd_uint_t carry = _mpd_baseincr(dec->data, dec->len);
+ if (carry) {
+ if (!mpd_qresize(dec, dec->len+1, status)) {
+ return;
+ }
+ dec->data[dec->len] = 1;
+ dec->len += 1;
+ }
+ mpd_setdigits(dec);
+ }
+}
+
+/*
+ * Apply rounding to a decimal that has been right-shifted into a decimal
+ * with full precision or less. Return failure if an increment would
+ * overflow the precision.
+ */
+static inline int
+_mpd_apply_round_fit(mpd_t *dec, mpd_uint_t rnd, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ if (_mpd_rnd_incr(dec, rnd, ctx)) {
+ mpd_uint_t carry = _mpd_baseincr(dec->data, dec->len);
+ if (carry) {
+ if (!mpd_qresize(dec, dec->len+1, status)) {
+ return 0;
+ }
+ dec->data[dec->len] = 1;
+ dec->len += 1;
+ }
+ mpd_setdigits(dec);
+ if (dec->digits > ctx->prec) {
+ mpd_seterror(dec, MPD_Invalid_operation, status);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* Check a normal number for overflow, underflow, clamping. If the operand
+ is modified, it will be zero, special or (sub)normal with a coefficient
+ that fits into the current context precision. */
+static inline void
+_mpd_check_exp(mpd_t *dec, const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_ssize_t adjexp, etiny, shift;
+ int rnd;
+
+ adjexp = mpd_adjexp(dec);
+ if (adjexp > ctx->emax) {
+
+ if (mpd_iszerocoeff(dec)) {
+ dec->exp = ctx->emax;
+ if (ctx->clamp) {
+ dec->exp -= (ctx->prec-1);
+ }
+ mpd_zerocoeff(dec);
+ *status |= MPD_Clamped;
+ return;
+ }
+
+ switch (ctx->round) {
+ case MPD_ROUND_HALF_UP: case MPD_ROUND_HALF_EVEN:
+ case MPD_ROUND_HALF_DOWN: case MPD_ROUND_UP:
+ case MPD_ROUND_TRUNC:
+ mpd_setspecial(dec, mpd_sign(dec), MPD_INF);
+ break;
+ case MPD_ROUND_DOWN: case MPD_ROUND_05UP:
+ mpd_qmaxcoeff(dec, ctx, status);
+ dec->exp = ctx->emax - ctx->prec + 1;
+ break;
+ case MPD_ROUND_CEILING:
+ if (mpd_isnegative(dec)) {
+ mpd_qmaxcoeff(dec, ctx, status);
+ dec->exp = ctx->emax - ctx->prec + 1;
+ }
+ else {
+ mpd_setspecial(dec, MPD_POS, MPD_INF);
+ }
+ break;
+ case MPD_ROUND_FLOOR:
+ if (mpd_ispositive(dec)) {
+ mpd_qmaxcoeff(dec, ctx, status);
+ dec->exp = ctx->emax - ctx->prec + 1;
+ }
+ else {
+ mpd_setspecial(dec, MPD_NEG, MPD_INF);
+ }
+ break;
+ default: /* debug */
+ abort(); /* GCOV_NOT_REACHED */
+ }
+
+ *status |= MPD_Overflow|MPD_Inexact|MPD_Rounded;
+
+ } /* fold down */
+ else if (ctx->clamp && dec->exp > mpd_etop(ctx)) {
+ /* At this point adjexp=exp+digits-1 <= emax and exp > etop=emax-prec+1:
+ * (1) shift = exp -emax+prec-1 > 0
+ * (2) digits+shift = exp+digits-1 - emax + prec <= prec */
+ shift = dec->exp - mpd_etop(ctx);
+ if (!mpd_qshiftl(dec, dec, shift, status)) {
+ return;
+ }
+ dec->exp -= shift;
+ *status |= MPD_Clamped;
+ if (!mpd_iszerocoeff(dec) && adjexp < ctx->emin) {
+ /* Underflow is impossible, since exp < etiny=emin-prec+1
+ * and exp > etop=emax-prec+1 would imply emax < emin. */
+ *status |= MPD_Subnormal;
+ }
+ }
+ else if (adjexp < ctx->emin) {
+
+ etiny = mpd_etiny(ctx);
+
+ if (mpd_iszerocoeff(dec)) {
+ if (dec->exp < etiny) {
+ dec->exp = etiny;
+ mpd_zerocoeff(dec);
+ *status |= MPD_Clamped;
+ }
+ return;
+ }
+
+ *status |= MPD_Subnormal;
+ if (dec->exp < etiny) {
+ /* At this point adjexp=exp+digits-1 < emin and exp < etiny=emin-prec+1:
+ * (1) shift = emin-prec+1 - exp > 0
+ * (2) digits-shift = exp+digits-1 - emin + prec < prec */
+ shift = etiny - dec->exp;
+ rnd = (int)mpd_qshiftr_inplace(dec, shift);
+ dec->exp = etiny;
+ /* We always have a spare digit in case of an increment. */
+ _mpd_apply_round_excess(dec, rnd, ctx, status);
+ *status |= MPD_Rounded;
+ if (rnd) {
+ *status |= (MPD_Inexact|MPD_Underflow);
+ if (mpd_iszerocoeff(dec)) {
+ mpd_zerocoeff(dec);
+ *status |= MPD_Clamped;
+ }
+ }
+ }
+ /* Case exp >= etiny=emin-prec+1:
+ * (1) adjexp=exp+digits-1 < emin
+ * (2) digits < emin-exp+1 <= prec */
+ }
+}
+
+/* Transcendental functions do not always set Underflow reliably,
+ * since they only use as much precision as is necessary for correct
+ * rounding. If a result like 1.0000000000e-101 is finalized, there
+ * is no rounding digit that would trigger Underflow. But we can
+ * assume Inexact, so a short check suffices. */
+static inline void
+mpd_check_underflow(mpd_t *dec, const mpd_context_t *ctx, uint32_t *status)
+{
+ if (mpd_adjexp(dec) < ctx->emin && !mpd_iszero(dec) &&
+ dec->exp < mpd_etiny(ctx)) {
+ *status |= MPD_Underflow;
+ }
+}
+
+/* Check if a normal number must be rounded after the exponent has been checked. */
+static inline void
+_mpd_check_round(mpd_t *dec, const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_uint_t rnd;
+ mpd_ssize_t shift;
+
+ /* must handle specials: _mpd_check_exp() can produce infinities or NaNs */
+ if (mpd_isspecial(dec)) {
+ return;
+ }
+
+ if (dec->digits > ctx->prec) {
+ shift = dec->digits - ctx->prec;
+ rnd = mpd_qshiftr_inplace(dec, shift);
+ dec->exp += shift;
+ _mpd_apply_round(dec, rnd, ctx, status);
+ *status |= MPD_Rounded;
+ if (rnd) {
+ *status |= MPD_Inexact;
+ }
+ }
+}
+
+/* Finalize all operations. */
+void
+mpd_qfinalize(mpd_t *result, const mpd_context_t *ctx, uint32_t *status)
+{
+ if (mpd_isspecial(result)) {
+ if (mpd_isnan(result)) {
+ _mpd_fix_nan(result, ctx);
+ }
+ return;
+ }
+
+ _mpd_check_exp(result, ctx, status);
+ _mpd_check_round(result, ctx, status);
+}
+
+
+/******************************************************************************/
+/* Copying */
+/******************************************************************************/
+
+/* Internal function: Copy a decimal, share data with src: USE WITH CARE! */
+static inline void
+_mpd_copy_shared(mpd_t *dest, const mpd_t *src)
+{
+ dest->flags = src->flags;
+ dest->exp = src->exp;
+ dest->digits = src->digits;
+ dest->len = src->len;
+ dest->alloc = src->alloc;
+ dest->data = src->data;
+
+ mpd_set_shared_data(dest);
+}
+
+/*
+ * Copy a decimal. In case of an error, status is set to MPD_Malloc_error.
+ */
+int
+mpd_qcopy(mpd_t *result, const mpd_t *a, uint32_t *status)
+{
+ if (result == a) return 1;
+
+ if (!mpd_qresize(result, a->len, status)) {
+ return 0;
+ }
+
+ mpd_copy_flags(result, a);
+ result->exp = a->exp;
+ result->digits = a->digits;
+ result->len = a->len;
+ memcpy(result->data, a->data, a->len * (sizeof *result->data));
+
+ return 1;
+}
+
+/*
+ * Copy to a decimal with a static buffer. The caller has to make sure that
+ * the buffer is big enough. Cannot fail.
+ */
+static void
+mpd_qcopy_static(mpd_t *result, const mpd_t *a)
+{
+ if (result == a) return;
+
+ memcpy(result->data, a->data, a->len * (sizeof *result->data));
+
+ mpd_copy_flags(result, a);
+ result->exp = a->exp;
+ result->digits = a->digits;
+ result->len = a->len;
+}
+
+/*
+ * Return a newly allocated copy of the operand. In case of an error,
+ * status is set to MPD_Malloc_error and the return value is NULL.
+ */
+mpd_t *
+mpd_qncopy(const mpd_t *a)
+{
+ mpd_t *result;
+
+ if ((result = mpd_qnew_size(a->len)) == NULL) {
+ return NULL;
+ }
+ memcpy(result->data, a->data, a->len * (sizeof *result->data));
+ mpd_copy_flags(result, a);
+ result->exp = a->exp;
+ result->digits = a->digits;
+ result->len = a->len;
+
+ return result;
+}
+
+/*
+ * Copy a decimal and set the sign to positive. In case of an error, the
+ * status is set to MPD_Malloc_error.
+ */
+int
+mpd_qcopy_abs(mpd_t *result, const mpd_t *a, uint32_t *status)
+{
+ if (!mpd_qcopy(result, a, status)) {
+ return 0;
+ }
+ mpd_set_positive(result);
+ return 1;
+}
+
+/*
+ * Copy a decimal and negate the sign. In case of an error, the
+ * status is set to MPD_Malloc_error.
+ */
+int
+mpd_qcopy_negate(mpd_t *result, const mpd_t *a, uint32_t *status)
+{
+ if (!mpd_qcopy(result, a, status)) {
+ return 0;
+ }
+ _mpd_negate(result);
+ return 1;
+}
+
+/*
+ * Copy a decimal, setting the sign of the first operand to the sign of the
+ * second operand. In case of an error, the status is set to MPD_Malloc_error.
+ */
+int
+mpd_qcopy_sign(mpd_t *result, const mpd_t *a, const mpd_t *b, uint32_t *status)
+{
+ uint8_t sign_b = mpd_sign(b); /* result may equal b! */
+
+ if (!mpd_qcopy(result, a, status)) {
+ return 0;
+ }
+ mpd_set_sign(result, sign_b);
+ return 1;
+}
+
+
+/******************************************************************************/
+/* Comparisons */
+/******************************************************************************/
+
+/*
+ * For all functions that compare two operands and return an int the usual
+ * convention applies to the return value:
+ *
+ * -1 if op1 < op2
+ * 0 if op1 == op2
+ * 1 if op1 > op2
+ *
+ * INT_MAX for error
+ */
+
+
+/* Convenience macro. If a and b are not equal, return from the calling
+ * function with the correct comparison value. */
+#define CMP_EQUAL_OR_RETURN(a, b) \
+ if (a != b) { \
+ if (a < b) { \
+ return -1; \
+ } \
+ return 1; \
+ }
+
+/*
+ * Compare the data of big and small. This function does the equivalent
+ * of first shifting small to the left and then comparing the data of
+ * big and small, except that no allocation for the left shift is needed.
+ */
+static int
+_mpd_basecmp(mpd_uint_t *big, mpd_uint_t *small, mpd_size_t n, mpd_size_t m,
+ mpd_size_t shift)
+{
+#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__)
+ /* spurious uninitialized warnings */
+ mpd_uint_t l=l, lprev=lprev, h=h;
+#else
+ mpd_uint_t l, lprev, h;
+#endif
+ mpd_uint_t q, r;
+ mpd_uint_t ph, x;
+
+ assert(m > 0 && n >= m && shift > 0);
+
+ _mpd_div_word(&q, &r, (mpd_uint_t)shift, MPD_RDIGITS);
+
+ if (r != 0) {
+
+ ph = mpd_pow10[r];
+
+ --m; --n;
+ _mpd_divmod_pow10(&h, &lprev, small[m--], MPD_RDIGITS-r);
+ if (h != 0) {
+ CMP_EQUAL_OR_RETURN(big[n], h)
+ --n;
+ }
+ for (; m != MPD_SIZE_MAX; m--,n--) {
+ _mpd_divmod_pow10(&h, &l, small[m], MPD_RDIGITS-r);
+ x = ph * lprev + h;
+ CMP_EQUAL_OR_RETURN(big[n], x)
+ lprev = l;
+ }
+ x = ph * lprev;
+ CMP_EQUAL_OR_RETURN(big[q], x)
+ }
+ else {
+ while (--m != MPD_SIZE_MAX) {
+ CMP_EQUAL_OR_RETURN(big[m+q], small[m])
+ }
+ }
+
+ return !_mpd_isallzero(big, q);
+}
+
+/* Compare two decimals with the same adjusted exponent. */
+static int
+_mpd_cmp_same_adjexp(const mpd_t *a, const mpd_t *b)
+{
+ mpd_ssize_t shift, i;
+
+ if (a->exp != b->exp) {
+ /* Cannot wrap: a->exp + a->digits = b->exp + b->digits, so
+ * a->exp - b->exp = b->digits - a->digits. */
+ shift = a->exp - b->exp;
+ if (shift > 0) {
+ return -1 * _mpd_basecmp(b->data, a->data, b->len, a->len, shift);
+ }
+ else {
+ return _mpd_basecmp(a->data, b->data, a->len, b->len, -shift);
+ }
+ }
+
+ /*
+ * At this point adjexp(a) == adjexp(b) and a->exp == b->exp,
+ * so a->digits == b->digits, therefore a->len == b->len.
+ */
+ for (i = a->len-1; i >= 0; --i) {
+ CMP_EQUAL_OR_RETURN(a->data[i], b->data[i])
+ }
+
+ return 0;
+}
+
+/* Compare two numerical values. */
+static int
+_mpd_cmp(const mpd_t *a, const mpd_t *b)
+{
+ mpd_ssize_t adjexp_a, adjexp_b;
+
+ /* equal pointers */
+ if (a == b) {
+ return 0;
+ }
+
+ /* infinities */
+ if (mpd_isinfinite(a)) {
+ if (mpd_isinfinite(b)) {
+ return mpd_isnegative(b) - mpd_isnegative(a);
+ }
+ return mpd_arith_sign(a);
+ }
+ if (mpd_isinfinite(b)) {
+ return -mpd_arith_sign(b);
+ }
+
+ /* zeros */
+ if (mpd_iszerocoeff(a)) {
+ if (mpd_iszerocoeff(b)) {
+ return 0;
+ }
+ return -mpd_arith_sign(b);
+ }
+ if (mpd_iszerocoeff(b)) {
+ return mpd_arith_sign(a);
+ }
+
+ /* different signs */
+ if (mpd_sign(a) != mpd_sign(b)) {
+ return mpd_sign(b) - mpd_sign(a);
+ }
+
+ /* different adjusted exponents */
+ adjexp_a = mpd_adjexp(a);
+ adjexp_b = mpd_adjexp(b);
+ if (adjexp_a != adjexp_b) {
+ if (adjexp_a < adjexp_b) {
+ return -1 * mpd_arith_sign(a);
+ }
+ return mpd_arith_sign(a);
+ }
+
+ /* same adjusted exponents */
+ return _mpd_cmp_same_adjexp(a, b) * mpd_arith_sign(a);
+}
+
+/* Compare the absolutes of two numerical values. */
+static int
+_mpd_cmp_abs(const mpd_t *a, const mpd_t *b)
+{
+ mpd_ssize_t adjexp_a, adjexp_b;
+
+ /* equal pointers */
+ if (a == b) {
+ return 0;
+ }
+
+ /* infinities */
+ if (mpd_isinfinite(a)) {
+ if (mpd_isinfinite(b)) {
+ return 0;
+ }
+ return 1;
+ }
+ if (mpd_isinfinite(b)) {
+ return -1;
+ }
+
+ /* zeros */
+ if (mpd_iszerocoeff(a)) {
+ if (mpd_iszerocoeff(b)) {
+ return 0;
+ }
+ return -1;
+ }
+ if (mpd_iszerocoeff(b)) {
+ return 1;
+ }
+
+ /* different adjusted exponents */
+ adjexp_a = mpd_adjexp(a);
+ adjexp_b = mpd_adjexp(b);
+ if (adjexp_a != adjexp_b) {
+ if (adjexp_a < adjexp_b) {
+ return -1;
+ }
+ return 1;
+ }
+
+ /* same adjusted exponents */
+ return _mpd_cmp_same_adjexp(a, b);
+}
+
+/* Compare two values and return an integer result. */
+int
+mpd_qcmp(const mpd_t *a, const mpd_t *b, uint32_t *status)
+{
+ if (mpd_isspecial(a) || mpd_isspecial(b)) {
+ if (mpd_isnan(a) || mpd_isnan(b)) {
+ *status |= MPD_Invalid_operation;
+ return INT_MAX;
+ }
+ }
+
+ return _mpd_cmp(a, b);
+}
+
+/*
+ * Compare a and b, convert the the usual integer result to a decimal and
+ * store it in 'result'. For convenience, the integer result of the comparison
+ * is returned. Comparisons involving NaNs return NaN/INT_MAX.
+ */
+int
+mpd_qcompare(mpd_t *result, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ int c;
+
+ if (mpd_isspecial(a) || mpd_isspecial(b)) {
+ if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+ return INT_MAX;
+ }
+ }
+
+ c = _mpd_cmp(a, b);
+ _settriple(result, (c < 0), (c != 0), 0);
+ return c;
+}
+
+/* Same as mpd_compare(), but signal for all NaNs, i.e. also for quiet NaNs. */
+int
+mpd_qcompare_signal(mpd_t *result, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ int c;
+
+ if (mpd_isspecial(a) || mpd_isspecial(b)) {
+ if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+ *status |= MPD_Invalid_operation;
+ return INT_MAX;
+ }
+ }
+
+ c = _mpd_cmp(a, b);
+ _settriple(result, (c < 0), (c != 0), 0);
+ return c;
+}
+
+/* Compare the operands using a total order. */
+int
+mpd_cmp_total(const mpd_t *a, const mpd_t *b)
+{
+ mpd_t aa, bb;
+ int nan_a, nan_b;
+ int c;
+
+ if (mpd_sign(a) != mpd_sign(b)) {
+ return mpd_sign(b) - mpd_sign(a);
+ }
+
+
+ if (mpd_isnan(a)) {
+ c = 1;
+ if (mpd_isnan(b)) {
+ nan_a = (mpd_isqnan(a)) ? 1 : 0;
+ nan_b = (mpd_isqnan(b)) ? 1 : 0;
+ if (nan_b == nan_a) {
+ if (a->len > 0 && b->len > 0) {
+ _mpd_copy_shared(&aa, a);
+ _mpd_copy_shared(&bb, b);
+ aa.exp = bb.exp = 0;
+ /* compare payload */
+ c = _mpd_cmp_abs(&aa, &bb);
+ }
+ else {
+ c = (a->len > 0) - (b->len > 0);
+ }
+ }
+ else {
+ c = nan_a - nan_b;
+ }
+ }
+ }
+ else if (mpd_isnan(b)) {
+ c = -1;
+ }
+ else {
+ c = _mpd_cmp_abs(a, b);
+ if (c == 0 && a->exp != b->exp) {
+ c = (a->exp < b->exp) ? -1 : 1;
+ }
+ }
+
+ return c * mpd_arith_sign(a);
+}
+
+/*
+ * Compare a and b according to a total order, convert the usual integer result
+ * to a decimal and store it in 'result'. For convenience, the integer result
+ * of the comparison is returned.
+ */
+int
+mpd_compare_total(mpd_t *result, const mpd_t *a, const mpd_t *b)
+{
+ int c;
+
+ c = mpd_cmp_total(a, b);
+ _settriple(result, (c < 0), (c != 0), 0);
+ return c;
+}
+
+/* Compare the magnitude of the operands using a total order. */
+int
+mpd_cmp_total_mag(const mpd_t *a, const mpd_t *b)
+{
+ mpd_t aa, bb;
+
+ _mpd_copy_shared(&aa, a);
+ _mpd_copy_shared(&bb, b);
+
+ mpd_set_positive(&aa);
+ mpd_set_positive(&bb);
+
+ return mpd_cmp_total(&aa, &bb);
+}
+
+/*
+ * Compare the magnitude of a and b according to a total order, convert the
+ * the usual integer result to a decimal and store it in 'result'.
+ * For convenience, the integer result of the comparison is returned.
+ */
+int
+mpd_compare_total_mag(mpd_t *result, const mpd_t *a, const mpd_t *b)
+{
+ int c;
+
+ c = mpd_cmp_total_mag(a, b);
+ _settriple(result, (c < 0), (c != 0), 0);
+ return c;
+}
+
+/* Determine an ordering for operands that are numerically equal. */
+static inline int
+_mpd_cmp_numequal(const mpd_t *a, const mpd_t *b)
+{
+ int sign_a, sign_b;
+ int c;
+
+ sign_a = mpd_sign(a);
+ sign_b = mpd_sign(b);
+ if (sign_a != sign_b) {
+ c = sign_b - sign_a;
+ }
+ else {
+ c = (a->exp < b->exp) ? -1 : 1;
+ c *= mpd_arith_sign(a);
+ }
+
+ return c;
+}
+
+
+/******************************************************************************/
+/* Shifting the coefficient */
+/******************************************************************************/
+
+/*
+ * Shift the coefficient of the operand to the left, no check for specials.
+ * Both operands may be the same pointer. If the result length has to be
+ * increased, mpd_qresize() might fail with MPD_Malloc_error.
+ */
+int
+mpd_qshiftl(mpd_t *result, const mpd_t *a, mpd_ssize_t n, uint32_t *status)
+{
+ mpd_ssize_t size;
+
+ assert(n >= 0);
+
+ if (mpd_iszerocoeff(a) || n == 0) {
+ return mpd_qcopy(result, a, status);
+ }
+
+ size = mpd_digits_to_size(a->digits+n);
+ if (!mpd_qresize(result, size, status)) {
+ return 0; /* result is NaN */
+ }
+
+ _mpd_baseshiftl(result->data, a->data, size, a->len, n);
+
+ mpd_copy_flags(result, a);
+ result->len = size;
+ result->exp = a->exp;
+ result->digits = a->digits+n;
+
+ return 1;
+}
+
+/* Determine the rounding indicator if all digits of the coefficient are shifted
+ * out of the picture. */
+static mpd_uint_t
+_mpd_get_rnd(const mpd_uint_t *data, mpd_ssize_t len, int use_msd)
+{
+ mpd_uint_t rnd = 0, rest = 0, word;
+
+ word = data[len-1];
+ /* special treatment for the most significant digit if shift == digits */
+ if (use_msd) {
+ _mpd_divmod_pow10(&rnd, &rest, word, mpd_word_digits(word)-1);
+ if (len > 1 && rest == 0) {
+ rest = !_mpd_isallzero(data, len-1);
+ }
+ }
+ else {
+ rest = !_mpd_isallzero(data, len);
+ }
+
+ return (rnd == 0 || rnd == 5) ? rnd + !!rest : rnd;
+}
+
+/*
+ * Same as mpd_qshiftr(), but 'result' is a static array. It is the
+ * caller's responsibility to make sure that the array is big enough.
+ * The function cannot fail.
+ */
+mpd_uint_t
+mpd_qsshiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n)
+{
+ mpd_uint_t rnd;
+ mpd_ssize_t size;
+
+ assert(n >= 0);
+
+ if (mpd_iszerocoeff(a) || n == 0) {
+ mpd_qcopy_static(result, a);
+ return 0;
+ }
+
+ if (n >= a->digits) {
+ rnd = _mpd_get_rnd(a->data, a->len, (n==a->digits));
+ mpd_zerocoeff(result);
+ result->digits = 1;
+ size = 1;
+ }
+ else {
+ result->digits = a->digits-n;
+ size = mpd_digits_to_size(result->digits);
+ rnd = _mpd_baseshiftr(result->data, a->data, a->len, n);
+ }
+
+ mpd_copy_flags(result, a);
+ result->exp = a->exp;
+ result->len = size;
+
+ return rnd;
+}
+
+/*
+ * Inplace shift of the coefficient to the right, no check for specials.
+ * Returns the rounding indicator for mpd_rnd_incr().
+ * The function cannot fail.
+ */
+mpd_uint_t
+mpd_qshiftr_inplace(mpd_t *result, mpd_ssize_t n)
+{
+ uint32_t dummy;
+ mpd_uint_t rnd;
+ mpd_ssize_t size;
+
+ assert(n >= 0);
+
+ if (mpd_iszerocoeff(result) || n == 0) {
+ return 0;
+ }
+
+ if (n >= result->digits) {
+ rnd = _mpd_get_rnd(result->data, result->len, (n==result->digits));
+ mpd_zerocoeff(result);
+ result->digits = 1;
+ size = 1;
+ }
+ else {
+ rnd = _mpd_baseshiftr(result->data, result->data, result->len, n);
+ result->digits -= n;
+ size = mpd_digits_to_size(result->digits);
+ /* reducing the size cannot fail */
+ mpd_qresize(result, size, &dummy);
+ }
+
+ result->len = size;
+
+ return rnd;
+}
+
+/*
+ * Shift the coefficient of the operand to the right, no check for specials.
+ * Both operands may be the same pointer. Returns the rounding indicator to
+ * be used by mpd_rnd_incr(). If the result length has to be increased,
+ * mpd_qcopy() or mpd_qresize() might fail with MPD_Malloc_error. In those
+ * cases, MPD_UINT_MAX is returned.
+ */
+mpd_uint_t
+mpd_qshiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n, uint32_t *status)
+{
+ mpd_uint_t rnd;
+ mpd_ssize_t size;
+
+ assert(n >= 0);
+
+ if (mpd_iszerocoeff(a) || n == 0) {
+ if (!mpd_qcopy(result, a, status)) {
+ return MPD_UINT_MAX;
+ }
+ return 0;
+ }
+
+ if (n >= a->digits) {
+ rnd = _mpd_get_rnd(a->data, a->len, (n==a->digits));
+ mpd_zerocoeff(result);
+ result->digits = 1;
+ size = 1;
+ }
+ else {
+ result->digits = a->digits-n;
+ size = mpd_digits_to_size(result->digits);
+ if (result == a) {
+ rnd = _mpd_baseshiftr(result->data, a->data, a->len, n);
+ /* reducing the size cannot fail */
+ mpd_qresize(result, size, status);
+ }
+ else {
+ if (!mpd_qresize(result, size, status)) {
+ return MPD_UINT_MAX;
+ }
+ rnd = _mpd_baseshiftr(result->data, a->data, a->len, n);
+ }
+ }
+
+ mpd_copy_flags(result, a);
+ result->exp = a->exp;
+ result->len = size;
+
+ return rnd;
+}
+
+
+/******************************************************************************/
+/* Miscellaneous operations */
+/******************************************************************************/
+
+/* Logical And */
+void
+mpd_qand(mpd_t *result, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ const mpd_t *big = a, *small = b;
+ mpd_uint_t x, y, z, xbit, ybit;
+ int k, mswdigits;
+ mpd_ssize_t i;
+
+ if (mpd_isspecial(a) || mpd_isspecial(b) ||
+ mpd_isnegative(a) || mpd_isnegative(b) ||
+ a->exp != 0 || b->exp != 0) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ if (b->digits > a->digits) {
+ big = b;
+ small = a;
+ }
+ if (!mpd_qresize(result, big->len, status)) {
+ return;
+ }
+
+
+ /* full words */
+ for (i = 0; i < small->len-1; i++) {
+ x = small->data[i];
+ y = big->data[i];
+ z = 0;
+ for (k = 0; k < MPD_RDIGITS; k++) {
+ xbit = x % 10;
+ x /= 10;
+ ybit = y % 10;
+ y /= 10;
+ if (xbit > 1 || ybit > 1) {
+ goto invalid_operation;
+ }
+ z += (xbit&ybit) ? mpd_pow10[k] : 0;
+ }
+ result->data[i] = z;
+ }
+ /* most significant word of small */
+ x = small->data[i];
+ y = big->data[i];
+ z = 0;
+ mswdigits = mpd_word_digits(x);
+ for (k = 0; k < mswdigits; k++) {
+ xbit = x % 10;
+ x /= 10;
+ ybit = y % 10;
+ y /= 10;
+ if (xbit > 1 || ybit > 1) {
+ goto invalid_operation;
+ }
+ z += (xbit&ybit) ? mpd_pow10[k] : 0;
+ }
+ result->data[i++] = z;
+
+ /* scan the rest of y for digit > 1 */
+ for (; k < MPD_RDIGITS; k++) {
+ ybit = y % 10;
+ y /= 10;
+ if (ybit > 1) {
+ goto invalid_operation;
+ }
+ }
+ /* scan the rest of big for digit > 1 */
+ for (; i < big->len; i++) {
+ y = big->data[i];
+ for (k = 0; k < MPD_RDIGITS; k++) {
+ ybit = y % 10;
+ y /= 10;
+ if (ybit > 1) {
+ goto invalid_operation;
+ }
+ }
+ }
+
+ mpd_clear_flags(result);
+ result->exp = 0;
+ result->len = _mpd_real_size(result->data, small->len);
+ mpd_qresize(result, result->len, status);
+ mpd_setdigits(result);
+ _mpd_cap(result, ctx);
+ return;
+
+invalid_operation:
+ mpd_seterror(result, MPD_Invalid_operation, status);
+}
+
+/* Class of an operand. Returns a pointer to the constant name. */
+const char *
+mpd_class(const mpd_t *a, const mpd_context_t *ctx)
+{
+ if (mpd_isnan(a)) {
+ if (mpd_isqnan(a))
+ return "NaN";
+ else
+ return "sNaN";
+ }
+ else if (mpd_ispositive(a)) {
+ if (mpd_isinfinite(a))
+ return "+Infinity";
+ else if (mpd_iszero(a))
+ return "+Zero";
+ else if (mpd_isnormal(a, ctx))
+ return "+Normal";
+ else
+ return "+Subnormal";
+ }
+ else {
+ if (mpd_isinfinite(a))
+ return "-Infinity";
+ else if (mpd_iszero(a))
+ return "-Zero";
+ else if (mpd_isnormal(a, ctx))
+ return "-Normal";
+ else
+ return "-Subnormal";
+ }
+}
+
+/* Logical Xor */
+void
+mpd_qinvert(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ mpd_uint_t x, z, xbit;
+ mpd_ssize_t i, digits, len;
+ mpd_ssize_t q, r;
+ int k;
+
+ if (mpd_isspecial(a) || mpd_isnegative(a) || a->exp != 0) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+
+ digits = (a->digits < ctx->prec) ? ctx->prec : a->digits;
+ _mpd_idiv_word(&q, &r, digits, MPD_RDIGITS);
+ len = (r == 0) ? q : q+1;
+ if (!mpd_qresize(result, len, status)) {
+ return;
+ }
+
+ for (i = 0; i < len; i++) {
+ x = (i < a->len) ? a->data[i] : 0;
+ z = 0;
+ for (k = 0; k < MPD_RDIGITS; k++) {
+ xbit = x % 10;
+ x /= 10;
+ if (xbit > 1) {
+ goto invalid_operation;
+ }
+ z += !xbit ? mpd_pow10[k] : 0;
+ }
+ result->data[i] = z;
+ }
+
+ mpd_clear_flags(result);
+ result->exp = 0;
+ result->len = _mpd_real_size(result->data, len);
+ mpd_qresize(result, result->len, status);
+ mpd_setdigits(result);
+ _mpd_cap(result, ctx);
+ return;
+
+invalid_operation:
+ mpd_seterror(result, MPD_Invalid_operation, status);
+}
+
+/* Exponent of the magnitude of the most significant digit of the operand. */
+void
+mpd_qlogb(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ if (mpd_isspecial(a)) {
+ if (mpd_qcheck_nan(result, a, ctx, status)) {
+ return;
+ }
+ mpd_setspecial(result, MPD_POS, MPD_INF);
+ }
+ else if (mpd_iszerocoeff(a)) {
+ mpd_setspecial(result, MPD_NEG, MPD_INF);
+ *status |= MPD_Division_by_zero;
+ }
+ else {
+ mpd_qset_ssize(result, mpd_adjexp(a), ctx, status);
+ }
+}
+
+/* Logical Or */
+void
+mpd_qor(mpd_t *result, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ const mpd_t *big = a, *small = b;
+ mpd_uint_t x, y, z, xbit, ybit;
+ int k, mswdigits;
+ mpd_ssize_t i;
+
+ if (mpd_isspecial(a) || mpd_isspecial(b) ||
+ mpd_isnegative(a) || mpd_isnegative(b) ||
+ a->exp != 0 || b->exp != 0) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ if (b->digits > a->digits) {
+ big = b;
+ small = a;
+ }
+ if (!mpd_qresize(result, big->len, status)) {
+ return;
+ }
+
+
+ /* full words */
+ for (i = 0; i < small->len-1; i++) {
+ x = small->data[i];
+ y = big->data[i];
+ z = 0;
+ for (k = 0; k < MPD_RDIGITS; k++) {
+ xbit = x % 10;
+ x /= 10;
+ ybit = y % 10;
+ y /= 10;
+ if (xbit > 1 || ybit > 1) {
+ goto invalid_operation;
+ }
+ z += (xbit|ybit) ? mpd_pow10[k] : 0;
+ }
+ result->data[i] = z;
+ }
+ /* most significant word of small */
+ x = small->data[i];
+ y = big->data[i];
+ z = 0;
+ mswdigits = mpd_word_digits(x);
+ for (k = 0; k < mswdigits; k++) {
+ xbit = x % 10;
+ x /= 10;
+ ybit = y % 10;
+ y /= 10;
+ if (xbit > 1 || ybit > 1) {
+ goto invalid_operation;
+ }
+ z += (xbit|ybit) ? mpd_pow10[k] : 0;
+ }
+
+ /* scan and copy the rest of y for digit > 1 */
+ for (; k < MPD_RDIGITS; k++) {
+ ybit = y % 10;
+ y /= 10;
+ if (ybit > 1) {
+ goto invalid_operation;
+ }
+ z += ybit*mpd_pow10[k];
+ }
+ result->data[i++] = z;
+ /* scan and copy the rest of big for digit > 1 */
+ for (; i < big->len; i++) {
+ y = big->data[i];
+ for (k = 0; k < MPD_RDIGITS; k++) {
+ ybit = y % 10;
+ y /= 10;
+ if (ybit > 1) {
+ goto invalid_operation;
+ }
+ }
+ result->data[i] = big->data[i];
+ }
+
+ mpd_clear_flags(result);
+ result->exp = 0;
+ result->len = _mpd_real_size(result->data, big->len);
+ mpd_qresize(result, result->len, status);
+ mpd_setdigits(result);
+ _mpd_cap(result, ctx);
+ return;
+
+invalid_operation:
+ mpd_seterror(result, MPD_Invalid_operation, status);
+}
+
+/*
+ * Rotate the coefficient of a by b->data digits. b must be an integer with
+ * exponent 0.
+ */
+void
+mpd_qrotate(mpd_t *result, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ uint32_t workstatus = 0;
+ MPD_NEW_STATIC(tmp,0,0,0,0);
+ MPD_NEW_STATIC(big,0,0,0,0);
+ MPD_NEW_STATIC(small,0,0,0,0);
+ mpd_ssize_t n, lshift, rshift;
+
+ if (mpd_isspecial(a) || mpd_isspecial(b)) {
+ if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+ return;
+ }
+ }
+ if (b->exp != 0 || mpd_isinfinite(b)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+
+ n = mpd_qget_ssize(b, &workstatus);
+ if (workstatus&MPD_Invalid_operation) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ if (n > ctx->prec || n < -ctx->prec) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ if (mpd_isinfinite(a)) {
+ mpd_qcopy(result, a, status);
+ return;
+ }
+
+ if (n >= 0) {
+ lshift = n;
+ rshift = ctx->prec-n;
+ }
+ else {
+ lshift = ctx->prec+n;
+ rshift = -n;
+ }
+
+ if (a->digits > ctx->prec) {
+ if (!mpd_qcopy(&tmp, a, status)) {
+ mpd_seterror(result, MPD_Malloc_error, status);
+ goto finish;
+ }
+ _mpd_cap(&tmp, ctx);
+ a = &tmp;
+ }
+
+ if (!mpd_qshiftl(&big, a, lshift, status)) {
+ mpd_seterror(result, MPD_Malloc_error, status);
+ goto finish;
+ }
+ _mpd_cap(&big, ctx);
+
+ if (mpd_qshiftr(&small, a, rshift, status) == MPD_UINT_MAX) {
+ mpd_seterror(result, MPD_Malloc_error, status);
+ goto finish;
+ }
+ _mpd_qadd(result, &big, &small, ctx, status);
+
+
+finish:
+ mpd_del(&tmp);
+ mpd_del(&big);
+ mpd_del(&small);
+}
+
+/*
+ * b must be an integer with exponent 0 and in the range +-2*(emax + prec).
+ * XXX: In my opinion +-(2*emax + prec) would be more sensible.
+ * The result is a with the value of b added to its exponent.
+ */
+void
+mpd_qscaleb(mpd_t *result, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ uint32_t workstatus = 0;
+ mpd_uint_t n, maxjump;
+#ifndef LEGACY_COMPILER
+ int64_t exp;
+#else
+ mpd_uint_t x;
+ int x_sign, n_sign;
+ mpd_ssize_t exp;
+#endif
+
+ if (mpd_isspecial(a) || mpd_isspecial(b)) {
+ if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+ return;
+ }
+ }
+ if (b->exp != 0 || mpd_isinfinite(b)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+
+ n = mpd_qabs_uint(b, &workstatus);
+ /* the spec demands this */
+ maxjump = 2 * (mpd_uint_t)(ctx->emax + ctx->prec);
+
+ if (n > maxjump || workstatus&MPD_Invalid_operation) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ if (mpd_isinfinite(a)) {
+ mpd_qcopy(result, a, status);
+ return;
+ }
+
+#ifndef LEGACY_COMPILER
+ exp = a->exp + (int64_t)n * mpd_arith_sign(b);
+ exp = (exp > MPD_EXP_INF) ? MPD_EXP_INF : exp;
+ exp = (exp < MPD_EXP_CLAMP) ? MPD_EXP_CLAMP : exp;
+#else
+ x = (a->exp < 0) ? -a->exp : a->exp;
+ x_sign = (a->exp < 0) ? 1 : 0;
+ n_sign = mpd_isnegative(b) ? 1 : 0;
+
+ if (x_sign == n_sign) {
+ x = x + n;
+ if (x < n) x = MPD_UINT_MAX;
+ }
+ else {
+ x_sign = (x >= n) ? x_sign : n_sign;
+ x = (x >= n) ? x - n : n - x;
+ }
+ if (!x_sign && x > MPD_EXP_INF) x = MPD_EXP_INF;
+ if (x_sign && x > -MPD_EXP_CLAMP) x = -MPD_EXP_CLAMP;
+ exp = x_sign ? -((mpd_ssize_t)x) : (mpd_ssize_t)x;
+#endif
+
+ mpd_qcopy(result, a, status);
+ result->exp = (mpd_ssize_t)exp;
+
+ mpd_qfinalize(result, ctx, status);
+}
+
+/*
+ * Shift the coefficient by n digits, positive n is a left shift. In the case
+ * of a left shift, the result is decapitated to fit the context precision. If
+ * you don't want that, use mpd_shiftl().
+ */
+void
+mpd_qshiftn(mpd_t *result, const mpd_t *a, mpd_ssize_t n, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ if (mpd_isspecial(a)) {
+ if (mpd_qcheck_nan(result, a, ctx, status)) {
+ return;
+ }
+ mpd_qcopy(result, a, status);
+ return;
+ }
+
+ if (n >= 0 && n <= ctx->prec) {
+ mpd_qshiftl(result, a, n, status);
+ _mpd_cap(result, ctx);
+ }
+ else if (n < 0 && n >= -ctx->prec) {
+ if (!mpd_qcopy(result, a, status)) {
+ return;
+ }
+ _mpd_cap(result, ctx);
+ mpd_qshiftr_inplace(result, -n);
+ }
+ else {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ }
+}
+
+/*
+ * Same as mpd_shiftn(), but the shift is specified by the decimal b, which
+ * must be an integer with a zero exponent. Infinities remain infinities.
+ */
+void
+mpd_qshift(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ uint32_t workstatus = 0;
+ mpd_ssize_t n;
+
+ if (mpd_isspecial(a) || mpd_isspecial(b)) {
+ if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+ return;
+ }
+ }
+ if (b->exp != 0 || mpd_isinfinite(b)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+
+ n = mpd_qget_ssize(b, &workstatus);
+ if (workstatus&MPD_Invalid_operation) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ if (n > ctx->prec || n < -ctx->prec) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ if (mpd_isinfinite(a)) {
+ mpd_qcopy(result, a, status);
+ return;
+ }
+
+ if (n >= 0) {
+ mpd_qshiftl(result, a, n, status);
+ _mpd_cap(result, ctx);
+ }
+ else {
+ if (!mpd_qcopy(result, a, status)) {
+ return;
+ }
+ _mpd_cap(result, ctx);
+ mpd_qshiftr_inplace(result, -n);
+ }
+}
+
+/* Logical Xor */
+void
+mpd_qxor(mpd_t *result, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ const mpd_t *big = a, *small = b;
+ mpd_uint_t x, y, z, xbit, ybit;
+ int k, mswdigits;
+ mpd_ssize_t i;
+
+ if (mpd_isspecial(a) || mpd_isspecial(b) ||
+ mpd_isnegative(a) || mpd_isnegative(b) ||
+ a->exp != 0 || b->exp != 0) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ if (b->digits > a->digits) {
+ big = b;
+ small = a;
+ }
+ if (!mpd_qresize(result, big->len, status)) {
+ return;
+ }
+
+
+ /* full words */
+ for (i = 0; i < small->len-1; i++) {
+ x = small->data[i];
+ y = big->data[i];
+ z = 0;
+ for (k = 0; k < MPD_RDIGITS; k++) {
+ xbit = x % 10;
+ x /= 10;
+ ybit = y % 10;
+ y /= 10;
+ if (xbit > 1 || ybit > 1) {
+ goto invalid_operation;
+ }
+ z += (xbit^ybit) ? mpd_pow10[k] : 0;
+ }
+ result->data[i] = z;
+ }
+ /* most significant word of small */
+ x = small->data[i];
+ y = big->data[i];
+ z = 0;
+ mswdigits = mpd_word_digits(x);
+ for (k = 0; k < mswdigits; k++) {
+ xbit = x % 10;
+ x /= 10;
+ ybit = y % 10;
+ y /= 10;
+ if (xbit > 1 || ybit > 1) {
+ goto invalid_operation;
+ }
+ z += (xbit^ybit) ? mpd_pow10[k] : 0;
+ }
+
+ /* scan and copy the rest of y for digit > 1 */
+ for (; k < MPD_RDIGITS; k++) {
+ ybit = y % 10;
+ y /= 10;
+ if (ybit > 1) {
+ goto invalid_operation;
+ }
+ z += ybit*mpd_pow10[k];
+ }
+ result->data[i++] = z;
+ /* scan and copy the rest of big for digit > 1 */
+ for (; i < big->len; i++) {
+ y = big->data[i];
+ for (k = 0; k < MPD_RDIGITS; k++) {
+ ybit = y % 10;
+ y /= 10;
+ if (ybit > 1) {
+ goto invalid_operation;
+ }
+ }
+ result->data[i] = big->data[i];
+ }
+
+ mpd_clear_flags(result);
+ result->exp = 0;
+ result->len = _mpd_real_size(result->data, big->len);
+ mpd_qresize(result, result->len, status);
+ mpd_setdigits(result);
+ _mpd_cap(result, ctx);
+ return;
+
+invalid_operation:
+ mpd_seterror(result, MPD_Invalid_operation, status);
+}
+
+
+/******************************************************************************/
+/* Arithmetic operations */
+/******************************************************************************/
+
+/*
+ * The absolute value of a. If a is negative, the result is the same
+ * as the result of the minus operation. Otherwise, the result is the
+ * result of the plus operation.
+ */
+void
+mpd_qabs(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ if (mpd_isspecial(a)) {
+ if (mpd_qcheck_nan(result, a, ctx, status)) {
+ return;
+ }
+ }
+
+ if (mpd_isnegative(a)) {
+ mpd_qminus(result, a, ctx, status);
+ }
+ else {
+ mpd_qplus(result, a, ctx, status);
+ }
+
+ mpd_qfinalize(result, ctx, status);
+}
+
+static inline void
+_mpd_ptrswap(mpd_t **a, mpd_t **b)
+{
+ mpd_t *t = *a;
+ *a = *b;
+ *b = t;
+}
+
+/* Add or subtract infinities. */
+static void
+_mpd_qaddsub_inf(mpd_t *result, const mpd_t *a, const mpd_t *b, uint8_t sign_b,
+ uint32_t *status)
+{
+ if (mpd_isinfinite(a)) {
+ if (mpd_sign(a) != sign_b && mpd_isinfinite(b)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ }
+ else {
+ mpd_setspecial(result, mpd_sign(a), MPD_INF);
+ }
+ return;
+ }
+ assert(mpd_isinfinite(b));
+ mpd_setspecial(result, sign_b, MPD_INF);
+}
+
+/* Add or subtract non-special numbers. */
+static void
+_mpd_qaddsub(mpd_t *result, const mpd_t *a, const mpd_t *b, uint8_t sign_b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_t *big, *small;
+ MPD_NEW_STATIC(big_aligned,0,0,0,0);
+ MPD_NEW_CONST(tiny,0,0,0,1,1,1);
+ mpd_uint_t carry;
+ mpd_ssize_t newsize, shift;
+ mpd_ssize_t exp, i;
+ int swap = 0;
+
+
+ /* compare exponents */
+ big = (mpd_t *)a; small = (mpd_t *)b;
+ if (big->exp != small->exp) {
+ if (small->exp > big->exp) {
+ _mpd_ptrswap(&big, &small);
+ swap++;
+ }
+ if (!mpd_iszerocoeff(big)) {
+ /* Test for adjexp(small) + big->digits < adjexp(big), if big-digits > prec
+ * Test for adjexp(small) + prec + 1 < adjexp(big), if big-digits <= prec
+ * If true, the magnitudes of the numbers are so far apart that one can as
+ * well add or subtract 1*10**big->exp. */
+ exp = big->exp - 1;
+ exp += (big->digits > ctx->prec) ? 0 : big->digits-ctx->prec-1;
+ if (mpd_adjexp(small) < exp) {
+ mpd_copy_flags(&tiny, small);
+ tiny.exp = exp;
+ tiny.digits = 1;
+ tiny.len = 1;
+ tiny.data[0] = mpd_iszerocoeff(small) ? 0 : 1;
+ small = &tiny;
+ }
+ /* this cannot wrap: the difference is positive and <= maxprec+1 */
+ shift = big->exp - small->exp;
+ if (!mpd_qshiftl(&big_aligned, big, shift, status)) {
+ mpd_seterror(result, MPD_Malloc_error, status);
+ goto finish;
+ }
+ big = &big_aligned;
+ }
+ }
+ result->exp = small->exp;
+
+
+ /* compare length of coefficients */
+ if (big->len < small->len) {
+ _mpd_ptrswap(&big, &small);
+ swap++;
+ }
+
+ newsize = big->len;
+ if (!mpd_qresize(result, newsize, status)) {
+ goto finish;
+ }
+
+ if (mpd_sign(a) == sign_b) {
+
+ carry = _mpd_baseadd(result->data, big->data, small->data,
+ big->len, small->len);
+
+ if (carry) {
+ newsize = big->len + 1;
+ if (!mpd_qresize(result, newsize, status)) {
+ goto finish;
+ }
+ result->data[newsize-1] = carry;
+ }
+
+ result->len = newsize;
+ mpd_set_flags(result, sign_b);
+ }
+ else {
+ if (big->len == small->len) {
+ for (i=big->len-1; i >= 0; --i) {
+ if (big->data[i] != small->data[i]) {
+ if (big->data[i] < small->data[i]) {
+ _mpd_ptrswap(&big, &small);
+ swap++;
+ }
+ break;
+ }
+ }
+ }
+
+ _mpd_basesub(result->data, big->data, small->data,
+ big->len, small->len);
+ newsize = _mpd_real_size(result->data, big->len);
+ /* resize to smaller cannot fail */
+ (void)mpd_qresize(result, newsize, status);
+
+ result->len = newsize;
+ sign_b = (swap & 1) ? sign_b : mpd_sign(a);
+ mpd_set_flags(result, sign_b);
+
+ if (mpd_iszerocoeff(result)) {
+ mpd_set_positive(result);
+ if (ctx->round == MPD_ROUND_FLOOR) {
+ mpd_set_negative(result);
+ }
+ }
+ }
+
+ mpd_setdigits(result);
+
+finish:
+ mpd_del(&big_aligned);
+}
+
+/* Add a and b. No specials, no finalizing. */
+static void
+_mpd_qadd(mpd_t *result, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ _mpd_qaddsub(result, a, b, mpd_sign(b), ctx, status);
+}
+
+/* Subtract b from a. No specials, no finalizing. */
+static void
+_mpd_qsub(mpd_t *result, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ _mpd_qaddsub(result, a, b, !mpd_sign(b), ctx, status);
+}
+
+/* Add a and b. */
+void
+mpd_qadd(mpd_t *result, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ if (mpd_isspecial(a) || mpd_isspecial(b)) {
+ if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+ return;
+ }
+ _mpd_qaddsub_inf(result, a, b, mpd_sign(b), status);
+ return;
+ }
+
+ _mpd_qaddsub(result, a, b, mpd_sign(b), ctx, status);
+ mpd_qfinalize(result, ctx, status);
+}
+
+/* Subtract b from a. */
+void
+mpd_qsub(mpd_t *result, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ if (mpd_isspecial(a) || mpd_isspecial(b)) {
+ if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+ return;
+ }
+ _mpd_qaddsub_inf(result, a, b, !mpd_sign(b), status);
+ return;
+ }
+
+ _mpd_qaddsub(result, a, b, !mpd_sign(b), ctx, status);
+ mpd_qfinalize(result, ctx, status);
+}
+
+/* Add decimal and mpd_ssize_t. */
+void
+mpd_qadd_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_context_t maxcontext;
+ MPD_NEW_STATIC(bb,0,0,0,0);
+
+ mpd_maxcontext(&maxcontext);
+ mpd_qsset_ssize(&bb, b, &maxcontext, status);
+ mpd_qadd(result, a, &bb, ctx, status);
+ mpd_del(&bb);
+}
+
+/* Add decimal and mpd_uint_t. */
+void
+mpd_qadd_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_context_t maxcontext;
+ MPD_NEW_STATIC(bb,0,0,0,0);
+
+ mpd_maxcontext(&maxcontext);
+ mpd_qsset_uint(&bb, b, &maxcontext, status);
+ mpd_qadd(result, a, &bb, ctx, status);
+ mpd_del(&bb);
+}
+
+/* Subtract mpd_ssize_t from decimal. */
+void
+mpd_qsub_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_context_t maxcontext;
+ MPD_NEW_STATIC(bb,0,0,0,0);
+
+ mpd_maxcontext(&maxcontext);
+ mpd_qsset_ssize(&bb, b, &maxcontext, status);
+ mpd_qsub(result, a, &bb, ctx, status);
+ mpd_del(&bb);
+}
+
+/* Subtract mpd_uint_t from decimal. */
+void
+mpd_qsub_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_context_t maxcontext;
+ MPD_NEW_STATIC(bb,0,0,0,0);
+
+ mpd_maxcontext(&maxcontext);
+ mpd_qsset_uint(&bb, b, &maxcontext, status);
+ mpd_qsub(result, a, &bb, ctx, status);
+ mpd_del(&bb);
+}
+
+/* Add decimal and int32_t. */
+void
+mpd_qadd_i32(mpd_t *result, const mpd_t *a, int32_t b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_qadd_ssize(result, a, b, ctx, status);
+}
+
+/* Add decimal and uint32_t. */
+void
+mpd_qadd_u32(mpd_t *result, const mpd_t *a, uint32_t b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_qadd_uint(result, a, b, ctx, status);
+}
+
+#ifdef CONFIG_64
+/* Add decimal and int64_t. */
+void
+mpd_qadd_i64(mpd_t *result, const mpd_t *a, int64_t b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_qadd_ssize(result, a, b, ctx, status);
+}
+
+/* Add decimal and uint64_t. */
+void
+mpd_qadd_u64(mpd_t *result, const mpd_t *a, uint64_t b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_qadd_uint(result, a, b, ctx, status);
+}
+#endif
+
+/* Subtract int32_t from decimal. */
+void
+mpd_qsub_i32(mpd_t *result, const mpd_t *a, int32_t b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_qsub_ssize(result, a, b, ctx, status);
+}
+
+/* Subtract uint32_t from decimal. */
+void
+mpd_qsub_u32(mpd_t *result, const mpd_t *a, uint32_t b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_qsub_uint(result, a, b, ctx, status);
+}
+
+#ifdef CONFIG_64
+/* Subtract int64_t from decimal. */
+void
+mpd_qsub_i64(mpd_t *result, const mpd_t *a, int64_t b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_qsub_ssize(result, a, b, ctx, status);
+}
+
+/* Subtract uint64_t from decimal. */
+void
+mpd_qsub_u64(mpd_t *result, const mpd_t *a, uint64_t b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_qsub_uint(result, a, b, ctx, status);
+}
+#endif
+
+
+/* Divide infinities. */
+static void
+_mpd_qdiv_inf(mpd_t *result, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ if (mpd_isinfinite(a)) {
+ if (mpd_isinfinite(b)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ mpd_setspecial(result, mpd_sign(a)^mpd_sign(b), MPD_INF);
+ return;
+ }
+ assert(mpd_isinfinite(b));
+ _settriple(result, mpd_sign(a)^mpd_sign(b), 0, mpd_etiny(ctx));
+ *status |= MPD_Clamped;
+}
+
+enum {NO_IDEAL_EXP, SET_IDEAL_EXP};
+/* Divide a by b. */
+static void
+_mpd_qdiv(int action, mpd_t *q, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ MPD_NEW_STATIC(aligned,0,0,0,0);
+ mpd_uint_t ld;
+ mpd_ssize_t shift, exp, tz;
+ mpd_ssize_t newsize;
+ mpd_ssize_t ideal_exp;
+ mpd_uint_t rem;
+ uint8_t sign_a = mpd_sign(a);
+ uint8_t sign_b = mpd_sign(b);
+
+
+ if (mpd_isspecial(a) || mpd_isspecial(b)) {
+ if (mpd_qcheck_nans(q, a, b, ctx, status)) {
+ return;
+ }
+ _mpd_qdiv_inf(q, a, b, ctx, status);
+ return;
+ }
+ if (mpd_iszerocoeff(b)) {
+ if (mpd_iszerocoeff(a)) {
+ mpd_seterror(q, MPD_Division_undefined, status);
+ }
+ else {
+ mpd_setspecial(q, sign_a^sign_b, MPD_INF);
+ *status |= MPD_Division_by_zero;
+ }
+ return;
+ }
+ if (mpd_iszerocoeff(a)) {
+ exp = a->exp - b->exp;
+ _settriple(q, sign_a^sign_b, 0, exp);
+ mpd_qfinalize(q, ctx, status);
+ return;
+ }
+
+ shift = (b->digits - a->digits) + ctx->prec + 1;
+ ideal_exp = a->exp - b->exp;
+ exp = ideal_exp - shift;
+ if (shift > 0) {
+ if (!mpd_qshiftl(&aligned, a, shift, status)) {
+ mpd_seterror(q, MPD_Malloc_error, status);
+ goto finish;
+ }
+ a = &aligned;
+ }
+ else if (shift < 0) {
+ shift = -shift;
+ if (!mpd_qshiftl(&aligned, b, shift, status)) {
+ mpd_seterror(q, MPD_Malloc_error, status);
+ goto finish;
+ }
+ b = &aligned;
+ }
+
+
+ newsize = a->len - b->len + 1;
+ if ((q != b && q != a) || (q == b && newsize > b->len)) {
+ if (!mpd_qresize(q, newsize, status)) {
+ mpd_seterror(q, MPD_Malloc_error, status);
+ goto finish;
+ }
+ }
+
+
+ if (b->len == 1) {
+ rem = _mpd_shortdiv(q->data, a->data, a->len, b->data[0]);
+ }
+ else if (a->len < 2*MPD_NEWTONDIV_CUTOFF &&
+ b->len < MPD_NEWTONDIV_CUTOFF) {
+ int ret = _mpd_basedivmod(q->data, NULL, a->data, b->data,
+ a->len, b->len);
+ if (ret < 0) {
+ mpd_seterror(q, MPD_Malloc_error, status);
+ goto finish;
+ }
+ rem = ret;
+ }
+ else {
+ MPD_NEW_STATIC(r,0,0,0,0);
+ _mpd_qbarrett_divmod(q, &r, a, b, status);
+ if (mpd_isspecial(q) || mpd_isspecial(&r)) {
+ mpd_del(&r);
+ goto finish;
+ }
+ rem = !mpd_iszerocoeff(&r);
+ mpd_del(&r);
+ newsize = q->len;
+ }
+
+ newsize = _mpd_real_size(q->data, newsize);
+ /* resize to smaller cannot fail */
+ mpd_qresize(q, newsize, status);
+ q->len = newsize;
+ mpd_setdigits(q);
+
+ shift = ideal_exp - exp;
+ if (rem) {
+ ld = mpd_lsd(q->data[0]);
+ if (ld == 0 || ld == 5) {
+ q->data[0] += 1;
+ }
+ }
+ else if (action == SET_IDEAL_EXP && shift > 0) {
+ tz = mpd_trail_zeros(q);
+ shift = (tz > shift) ? shift : tz;
+ mpd_qshiftr_inplace(q, shift);
+ exp += shift;
+ }
+
+ mpd_set_flags(q, sign_a^sign_b);
+ q->exp = exp;
+
+
+finish:
+ mpd_del(&aligned);
+ mpd_qfinalize(q, ctx, status);
+}
+
+/* Divide a by b. */
+void
+mpd_qdiv(mpd_t *q, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ _mpd_qdiv(SET_IDEAL_EXP, q, a, b, ctx, status);
+}
+
+/* Internal function. */
+static void
+_mpd_qdivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ MPD_NEW_STATIC(aligned,0,0,0,0);
+ mpd_ssize_t qsize, rsize;
+ mpd_ssize_t ideal_exp, expdiff, shift;
+ uint8_t sign_a = mpd_sign(a);
+ uint8_t sign_ab = mpd_sign(a)^mpd_sign(b);
+
+
+ ideal_exp = (a->exp > b->exp) ? b->exp : a->exp;
+ if (mpd_iszerocoeff(a)) {
+ if (!mpd_qcopy(r, a, status)) {
+ goto nanresult; /* GCOV_NOT_REACHED */
+ }
+ r->exp = ideal_exp;
+ _settriple(q, sign_ab, 0, 0);
+ return;
+ }
+
+ expdiff = mpd_adjexp(a) - mpd_adjexp(b);
+ if (expdiff < 0) {
+ if (a->exp > b->exp) {
+ /* positive and less than b->digits - a->digits */
+ shift = a->exp - b->exp;
+ if (!mpd_qshiftl(r, a, shift, status)) {
+ goto nanresult;
+ }
+ r->exp = ideal_exp;
+ }
+ else {
+ if (!mpd_qcopy(r, a, status)) {
+ goto nanresult;
+ }
+ }
+ _settriple(q, sign_ab, 0, 0);
+ return;
+ }
+ if (expdiff > ctx->prec) {
+ *status |= MPD_Division_impossible;
+ goto nanresult;
+ }
+
+
+ /*
+ * At this point we have:
+ * (1) 0 <= a->exp + a->digits - b->exp - b->digits <= prec
+ * (2) a->exp - b->exp >= b->digits - a->digits
+ * (3) a->exp - b->exp <= prec + b->digits - a->digits
+ */
+ if (a->exp != b->exp) {
+ shift = a->exp - b->exp;
+ if (shift > 0) {
+ /* by (3), after the shift a->digits <= prec + b->digits */
+ if (!mpd_qshiftl(&aligned, a, shift, status)) {
+ goto nanresult;
+ }
+ a = &aligned;
+ }
+ else {
+ shift = -shift;
+ /* by (2), after the shift b->digits <= a->digits */
+ if (!mpd_qshiftl(&aligned, b, shift, status)) {
+ goto nanresult;
+ }
+ b = &aligned;
+ }
+ }
+
+
+ qsize = a->len - b->len + 1;
+ if (!(q == a && qsize < a->len) && !(q == b && qsize < b->len)) {
+ if (!mpd_qresize(q, qsize, status)) {
+ goto nanresult;
+ }
+ }
+
+ rsize = b->len;
+ if (!(r == a && rsize < a->len)) {
+ if (!mpd_qresize(r, rsize, status)) {
+ goto nanresult;
+ }
+ }
+
+ if (b->len == 1) {
+ if (a->len == 1) {
+ _mpd_div_word(&q->data[0], &r->data[0], a->data[0], b->data[0]);
+ }
+ else {
+ r->data[0] = _mpd_shortdiv(q->data, a->data, a->len, b->data[0]);
+ }
+ }
+ else if (a->len < 2*MPD_NEWTONDIV_CUTOFF &&
+ b->len < MPD_NEWTONDIV_CUTOFF) {
+ int ret;
+ ret = _mpd_basedivmod(q->data, r->data, a->data, b->data,
+ a->len, b->len);
+ if (ret == -1) {
+ *status |= MPD_Malloc_error;
+ goto nanresult;
+ }
+ }
+ else {
+ _mpd_qbarrett_divmod(q, r, a, b, status);
+ if (mpd_isspecial(q) || mpd_isspecial(r)) {
+ goto nanresult;
+ }
+ if (mpd_isinfinite(q) || q->digits > ctx->prec) {
+ *status |= MPD_Division_impossible;
+ goto nanresult;
+ }
+ qsize = q->len;
+ rsize = r->len;
+ }
+
+ qsize = _mpd_real_size(q->data, qsize);
+ /* resize to smaller cannot fail */
+ mpd_qresize(q, qsize, status);
+ q->len = qsize;
+ mpd_setdigits(q);
+ mpd_set_flags(q, sign_ab);
+ q->exp = 0;
+ if (q->digits > ctx->prec) {
+ *status |= MPD_Division_impossible;
+ goto nanresult;
+ }
+
+ rsize = _mpd_real_size(r->data, rsize);
+ /* resize to smaller cannot fail */
+ mpd_qresize(r, rsize, status);
+ r->len = rsize;
+ mpd_setdigits(r);
+ mpd_set_flags(r, sign_a);
+ r->exp = ideal_exp;
+
+out:
+ mpd_del(&aligned);
+ return;
+
+nanresult:
+ mpd_setspecial(q, MPD_POS, MPD_NAN);
+ mpd_setspecial(r, MPD_POS, MPD_NAN);
+ goto out;
+}
+
+/* Integer division with remainder. */
+void
+mpd_qdivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ uint8_t sign = mpd_sign(a)^mpd_sign(b);
+
+ if (mpd_isspecial(a) || mpd_isspecial(b)) {
+ if (mpd_qcheck_nans(q, a, b, ctx, status)) {
+ mpd_qcopy(r, q, status);
+ return;
+ }
+ if (mpd_isinfinite(a)) {
+ if (mpd_isinfinite(b)) {
+ mpd_setspecial(q, MPD_POS, MPD_NAN);
+ }
+ else {
+ mpd_setspecial(q, sign, MPD_INF);
+ }
+ mpd_setspecial(r, MPD_POS, MPD_NAN);
+ *status |= MPD_Invalid_operation;
+ return;
+ }
+ if (mpd_isinfinite(b)) {
+ if (!mpd_qcopy(r, a, status)) {
+ mpd_seterror(q, MPD_Malloc_error, status);
+ return;
+ }
+ mpd_qfinalize(r, ctx, status);
+ _settriple(q, sign, 0, 0);
+ return;
+ }
+ /* debug */
+ abort(); /* GCOV_NOT_REACHED */
+ }
+ if (mpd_iszerocoeff(b)) {
+ if (mpd_iszerocoeff(a)) {
+ mpd_setspecial(q, MPD_POS, MPD_NAN);
+ mpd_setspecial(r, MPD_POS, MPD_NAN);
+ *status |= MPD_Division_undefined;
+ }
+ else {
+ mpd_setspecial(q, sign, MPD_INF);
+ mpd_setspecial(r, MPD_POS, MPD_NAN);
+ *status |= (MPD_Division_by_zero|MPD_Invalid_operation);
+ }
+ return;
+ }
+
+ _mpd_qdivmod(q, r, a, b, ctx, status);
+ mpd_qfinalize(q, ctx, status);
+ mpd_qfinalize(r, ctx, status);
+}
+
+void
+mpd_qdivint(mpd_t *q, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ MPD_NEW_STATIC(r,0,0,0,0);
+ uint8_t sign = mpd_sign(a)^mpd_sign(b);
+
+ if (mpd_isspecial(a) || mpd_isspecial(b)) {
+ if (mpd_qcheck_nans(q, a, b, ctx, status)) {
+ return;
+ }
+ if (mpd_isinfinite(a) && mpd_isinfinite(b)) {
+ mpd_seterror(q, MPD_Invalid_operation, status);
+ return;
+ }
+ if (mpd_isinfinite(a)) {
+ mpd_setspecial(q, sign, MPD_INF);
+ return;
+ }
+ if (mpd_isinfinite(b)) {
+ _settriple(q, sign, 0, 0);
+ return;
+ }
+ /* debug */
+ abort(); /* GCOV_NOT_REACHED */
+ }
+ if (mpd_iszerocoeff(b)) {
+ if (mpd_iszerocoeff(a)) {
+ mpd_seterror(q, MPD_Division_undefined, status);
+ }
+ else {
+ mpd_setspecial(q, sign, MPD_INF);
+ *status |= MPD_Division_by_zero;
+ }
+ return;
+ }
+
+
+ _mpd_qdivmod(q, &r, a, b, ctx, status);
+ mpd_del(&r);
+ mpd_qfinalize(q, ctx, status);
+}
+
+/* Divide decimal by mpd_ssize_t. */
+void
+mpd_qdiv_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_context_t maxcontext;
+ MPD_NEW_STATIC(bb,0,0,0,0);
+
+ mpd_maxcontext(&maxcontext);
+ mpd_qsset_ssize(&bb, b, &maxcontext, status);
+ mpd_qdiv(result, a, &bb, ctx, status);
+ mpd_del(&bb);
+}
+
+/* Divide decimal by mpd_uint_t. */
+void
+mpd_qdiv_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_context_t maxcontext;
+ MPD_NEW_STATIC(bb,0,0,0,0);
+
+ mpd_maxcontext(&maxcontext);
+ mpd_qsset_uint(&bb, b, &maxcontext, status);
+ mpd_qdiv(result, a, &bb, ctx, status);
+ mpd_del(&bb);
+}
+
+/* Divide decimal by int32_t. */
+void
+mpd_qdiv_i32(mpd_t *result, const mpd_t *a, int32_t b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_qdiv_ssize(result, a, b, ctx, status);
+}
+
+/* Divide decimal by uint32_t. */
+void
+mpd_qdiv_u32(mpd_t *result, const mpd_t *a, uint32_t b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_qdiv_uint(result, a, b, ctx, status);
+}
+
+#ifdef CONFIG_64
+/* Divide decimal by int64_t. */
+void
+mpd_qdiv_i64(mpd_t *result, const mpd_t *a, int64_t b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_qdiv_ssize(result, a, b, ctx, status);
+}
+
+/* Divide decimal by uint64_t. */
+void
+mpd_qdiv_u64(mpd_t *result, const mpd_t *a, uint64_t b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_qdiv_uint(result, a, b, ctx, status);
+}
+#endif
+
+#if defined(_MSC_VER)
+ /* conversion from 'double' to 'mpd_ssize_t', possible loss of data */
+ #pragma warning(disable:4244)
+#endif
+/*
+ * Get the number of iterations for the Horner scheme in _mpd_qexp().
+ */
+static inline mpd_ssize_t
+_mpd_get_exp_iterations(const mpd_t *a, mpd_ssize_t prec)
+{
+ mpd_uint_t dummy;
+ mpd_uint_t msdigits;
+ double f;
+
+ /* 9 is MPD_RDIGITS for 32 bit platforms */
+ _mpd_get_msdigits(&dummy, &msdigits, a, 9);
+ f = ((double)msdigits + 1) / mpd_pow10[mpd_word_digits(msdigits)];
+
+#ifdef CONFIG_64
+ #ifdef USE_80BIT_LONG_DOUBLE
+ return ceill((1.435*(long double)prec - 1.182)
+ / log10l((long double)prec/f));
+ #else
+ /* prec > floor((1ULL<<53) / 1.435) */
+ if (prec > 6276793905742851LL) {
+ return MPD_SSIZE_MAX;
+ }
+ return ceil((1.435*(double)prec - 1.182) / log10((double)prec/f));
+ #endif
+#else /* CONFIG_32 */
+ return ceil((1.435*(double)prec - 1.182) / log10((double)prec/f));
+ #if defined(_MSC_VER)
+ #pragma warning(default:4244)
+ #endif
+#endif
+}
+
+/*
+ * Internal function, specials have been dealt with.
+ *
+ * The algorithm is from Hull&Abrham, Variable Precision Exponential Function,
+ * ACM Transactions on Mathematical Software, Vol. 12, No. 2, June 1986.
+ *
+ * Main differences:
+ *
+ * - The number of iterations for the Horner scheme is calculated using the
+ * C log10() function.
+ *
+ * - The analysis for early abortion has been adapted for the mpd_t
+ * ranges.
+ */
+static void
+_mpd_qexp(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ mpd_context_t workctx;
+ MPD_NEW_STATIC(tmp,0,0,0,0);
+ MPD_NEW_STATIC(sum,0,0,0,0);
+ MPD_NEW_CONST(word,0,0,0,1,1,1);
+ mpd_ssize_t j, n, t;
+
+ assert(!mpd_isspecial(a));
+
+ /*
+ * We are calculating e^x = e^(r*10^t) = (e^r)^(10^t), where r < 1 and t >= 0.
+ *
+ * If t > 0, we have:
+ *
+ * (1) 0.1 <= r < 1, so e^r >= e^0.1. Overflow in the final power operation
+ * will occur when (e^0.1)^(10^t) > 10^(emax+1). If we consider MAX_EMAX,
+ * this will happen for t > 10 (32 bit) or (t > 19) (64 bit).
+ *
+ * (2) -1 < r <= -0.1, so e^r > e^-1. Underflow in the final power operation
+ * will occur when (e^-1)^(10^t) < 10^(etiny-1). If we consider MIN_ETINY,
+ * this will also happen for t > 10 (32 bit) or (t > 19) (64 bit).
+ */
+#if defined(CONFIG_64)
+ #define MPD_EXP_MAX_T 19
+#elif defined(CONFIG_32)
+ #define MPD_EXP_MAX_T 10
+#endif
+ t = a->digits + a->exp;
+ t = (t > 0) ? t : 0;
+ if (t > MPD_EXP_MAX_T) {
+ if (mpd_ispositive(a)) {
+ mpd_setspecial(result, MPD_POS, MPD_INF);
+ *status |= MPD_Overflow|MPD_Inexact|MPD_Rounded;
+ }
+ else {
+ _settriple(result, MPD_POS, 0, mpd_etiny(ctx));
+ *status |= (MPD_Inexact|MPD_Rounded|MPD_Subnormal|
+ MPD_Underflow|MPD_Clamped);
+ }
+ return;
+ }
+
+ mpd_maxcontext(&workctx);
+ workctx.prec = ctx->prec + t + 2;
+ workctx.prec = (workctx.prec < 9) ? 9 : workctx.prec;
+ workctx.round = MPD_ROUND_HALF_EVEN;
+
+ if ((n = _mpd_get_exp_iterations(a, workctx.prec)) == MPD_SSIZE_MAX) {
+ mpd_seterror(result, MPD_Invalid_operation, status); /* GCOV_UNLIKELY */
+ goto finish; /* GCOV_UNLIKELY */
+ }
+
+ if (!mpd_qcopy(result, a, status)) {
+ goto finish;
+ }
+ result->exp -= t;
+
+ _settriple(&sum, MPD_POS, 1, 0);
+
+ for (j = n-1; j >= 1; j--) {
+ word.data[0] = j;
+ mpd_setdigits(&word);
+ mpd_qdiv(&tmp, result, &word, &workctx, &workctx.status);
+ mpd_qmul(&sum, &sum, &tmp, &workctx, &workctx.status);
+ mpd_qadd(&sum, &sum, &one, &workctx, &workctx.status);
+ }
+
+#ifdef CONFIG_64
+ _mpd_qpow_uint(result, &sum, mpd_pow10[t], MPD_POS, &workctx, status);
+#else
+ if (t <= MPD_MAX_POW10) {
+ _mpd_qpow_uint(result, &sum, mpd_pow10[t], MPD_POS, &workctx, status);
+ }
+ else {
+ t -= MPD_MAX_POW10;
+ _mpd_qpow_uint(&tmp, &sum, mpd_pow10[MPD_MAX_POW10], MPD_POS,
+ &workctx, status);
+ _mpd_qpow_uint(result, &tmp, mpd_pow10[t], MPD_POS, &workctx, status);
+ }
+#endif
+
+
+finish:
+ mpd_del(&tmp);
+ mpd_del(&sum);
+ *status |= (workctx.status&MPD_Errors);
+ *status |= (MPD_Inexact|MPD_Rounded);
+}
+
+/* exp(a) */
+void
+mpd_qexp(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ mpd_context_t workctx;
+
+ if (mpd_isspecial(a)) {
+ if (mpd_qcheck_nan(result, a, ctx, status)) {
+ return;
+ }
+ if (mpd_isnegative(a)) {
+ _settriple(result, MPD_POS, 0, 0);
+ }
+ else {
+ mpd_setspecial(result, MPD_POS, MPD_INF);
+ }
+ return;
+ }
+ if (mpd_iszerocoeff(a)) {
+ _settriple(result, MPD_POS, 1, 0);
+ return;
+ }
+
+ workctx = *ctx;
+ workctx.round = MPD_ROUND_HALF_EVEN;
+
+ if (ctx->allcr) {
+ MPD_NEW_STATIC(t1, 0,0,0,0);
+ MPD_NEW_STATIC(t2, 0,0,0,0);
+ MPD_NEW_STATIC(ulp, 0,0,0,0);
+ MPD_NEW_STATIC(aa, 0,0,0,0);
+ mpd_ssize_t prec;
+
+ if (result == a) {
+ if (!mpd_qcopy(&aa, a, status)) {
+ mpd_seterror(result, MPD_Malloc_error, status);
+ return;
+ }
+ a = &aa;
+ }
+
+ workctx.clamp = 0;
+ prec = ctx->prec + 3;
+ while (1) {
+ workctx.prec = prec;
+ _mpd_qexp(result, a, &workctx, status);
+ _ssettriple(&ulp, MPD_POS, 1,
+ result->exp + result->digits-workctx.prec-1);
+
+ workctx.prec = ctx->prec;
+ mpd_qadd(&t1, result, &ulp, &workctx, &workctx.status);
+ mpd_qsub(&t2, result, &ulp, &workctx, &workctx.status);
+ if (mpd_isspecial(result) || mpd_iszerocoeff(result) ||
+ mpd_qcmp(&t1, &t2, status) == 0) {
+ workctx.clamp = ctx->clamp;
+ mpd_check_underflow(result, &workctx, status);
+ mpd_qfinalize(result, &workctx, status);
+ break;
+ }
+ prec += MPD_RDIGITS;
+ }
+ mpd_del(&t1);
+ mpd_del(&t2);
+ mpd_del(&ulp);
+ mpd_del(&aa);
+ }
+ else {
+ _mpd_qexp(result, a, &workctx, status);
+ mpd_check_underflow(result, &workctx, status);
+ mpd_qfinalize(result, &workctx, status);
+ }
+}
+
+/* Fused multiply-add: (a * b) + c, with a single final rounding. */
+void
+mpd_qfma(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ uint32_t workstatus = 0;
+ mpd_t *cc = (mpd_t *)c;
+
+ if (result == c) {
+ if ((cc = mpd_qncopy(c)) == NULL) {
+ mpd_seterror(result, MPD_Malloc_error, status);
+ return;
+ }
+ }
+
+ _mpd_qmul(result, a, b, ctx, &workstatus);
+ if (!(workstatus&MPD_Invalid_operation)) {
+ mpd_qadd(result, result, cc, ctx, &workstatus);
+ }
+
+ if (cc != c) mpd_del(cc);
+ *status |= workstatus;
+}
+
+static inline int
+ln_schedule_prec(mpd_ssize_t klist[MPD_MAX_PREC_LOG2], mpd_ssize_t maxprec,
+ mpd_ssize_t initprec)
+{
+ mpd_ssize_t k;
+ int i;
+
+ assert(maxprec >= 2 && initprec >= 2);
+ if (maxprec <= initprec) return -1;
+
+ i = 0; k = maxprec;
+ do {
+ k = (k+2) / 2;
+ klist[i++] = k;
+ } while (k > initprec);
+
+ return i-1;
+}
+
+#ifdef CONFIG_64
+#if MPD_RDIGITS != 19
+ #error "mpdecimal.c: MPD_RDIGITS must be 19."
+#endif
+static const mpd_uint_t mpd_ln10_data[MPD_MINALLOC_MAX] = {
+ 6983716328982174407ULL, 9089704281976336583ULL, 1515961135648465461ULL,
+ 4416816335727555703ULL, 2900988039194170265ULL, 2307925037472986509ULL,
+ 107598438319191292ULL, 3466624107184669231ULL, 4450099781311469159ULL,
+ 9807828059751193854ULL, 7713456862091670584ULL, 1492198849978748873ULL,
+ 6528728696511086257ULL, 2385392051446341972ULL, 8692180205189339507ULL,
+ 6518769751037497088ULL, 2375253577097505395ULL, 9095610299291824318ULL,
+ 982748238504564801ULL, 5438635917781170543ULL, 7547331541421808427ULL,
+ 752371033310119785ULL, 3171643095059950878ULL, 9785265383207606726ULL,
+ 2932258279850258550ULL, 5497347726624257094ULL, 2976979522110718264ULL,
+ 9221477656763693866ULL, 1979650047149510504ULL, 6674183485704422507ULL,
+ 9702766860595249671ULL, 9278096762712757753ULL, 9314848524948644871ULL,
+ 6826928280848118428ULL, 754403708474699401ULL, 230105703089634572ULL,
+ 1929203337658714166ULL, 7589402567763113569ULL, 4208241314695689016ULL,
+ 2922455440575892572ULL, 9356734206705811364ULL, 2684916746550586856ULL,
+ 644507064800027750ULL, 9476834636167921018ULL, 5659121373450747856ULL,
+ 2835522011480466371ULL, 6470806855677432162ULL, 7141748003688084012ULL,
+ 9619404400222105101ULL, 5504893431493939147ULL, 6674744042432743651ULL,
+ 2287698219886746543ULL, 7773262884616336622ULL, 1985283935053089653ULL,
+ 4680843799894826233ULL, 8168948290720832555ULL, 8067566662873690987ULL,
+ 6248633409525465082ULL, 9829834196778404228ULL, 3524802359972050895ULL,
+ 3327900967572609677ULL, 110148862877297603ULL, 179914546843642076ULL,
+ 2302585092994045684ULL
+};
+#else
+#if MPD_RDIGITS != 9
+ #error "mpdecimal.c: MPD_RDIGITS must be 9."
+#endif
+static const mpd_uint_t mpd_ln10_data[MPD_MINALLOC_MAX] = {
+ 401682692UL, 708474699UL, 720754403UL, 30896345UL, 602301057UL, 765871416UL,
+ 192920333UL, 763113569UL, 589402567UL, 956890167UL, 82413146UL, 589257242UL,
+ 245544057UL, 811364292UL, 734206705UL, 868569356UL, 167465505UL, 775026849UL,
+ 706480002UL, 18064450UL, 636167921UL, 569476834UL, 734507478UL, 156591213UL,
+ 148046637UL, 283552201UL, 677432162UL, 470806855UL, 880840126UL, 417480036UL,
+ 210510171UL, 940440022UL, 939147961UL, 893431493UL, 436515504UL, 440424327UL,
+ 654366747UL, 821988674UL, 622228769UL, 884616336UL, 537773262UL, 350530896UL,
+ 319852839UL, 989482623UL, 468084379UL, 720832555UL, 168948290UL, 736909878UL,
+ 675666628UL, 546508280UL, 863340952UL, 404228624UL, 834196778UL, 508959829UL,
+ 23599720UL, 967735248UL, 96757260UL, 603332790UL, 862877297UL, 760110148UL,
+ 468436420UL, 401799145UL, 299404568UL, 230258509UL
+};
+#endif
+/* _mpd_ln10 is used directly for precisions smaller than MINALLOC_MAX*RDIGITS.
+ Otherwise, it serves as the initial approximation for calculating ln(10). */
+static const mpd_t _mpd_ln10 = {
+ MPD_STATIC|MPD_CONST_DATA, -(MPD_MINALLOC_MAX*MPD_RDIGITS-1),
+ MPD_MINALLOC_MAX*MPD_RDIGITS, MPD_MINALLOC_MAX, MPD_MINALLOC_MAX,
+ (mpd_uint_t *)mpd_ln10_data
+};
+
+/* Set 'result' to ln(10), with 'prec' digits, using ROUND_HALF_EVEN. */
+void
+mpd_qln10(mpd_t *result, mpd_ssize_t prec, uint32_t *status)
+{
+ mpd_context_t varcontext, maxcontext;
+ MPD_NEW_STATIC(tmp, 0,0,0,0);
+ MPD_NEW_CONST(static10, 0,0,2,1,1,10);
+ mpd_ssize_t klist[MPD_MAX_PREC_LOG2];
+ mpd_uint_t rnd;
+ mpd_ssize_t shift;
+ int i;
+
+ assert(prec >= 1);
+
+ shift = MPD_MINALLOC_MAX*MPD_RDIGITS-prec;
+ shift = shift < 0 ? 0 : shift;
+
+ rnd = mpd_qshiftr(result, &_mpd_ln10, shift, status);
+ if (rnd == MPD_UINT_MAX) {
+ mpd_seterror(result, MPD_Malloc_error, status);
+ return;
+ }
+ result->exp = -(result->digits-1);
+
+ mpd_maxcontext(&maxcontext);
+ if (prec < MPD_MINALLOC_MAX*MPD_RDIGITS) {
+ maxcontext.prec = prec;
+ _mpd_apply_round_excess(result, rnd, &maxcontext, status);
+ *status |= (MPD_Inexact|MPD_Rounded);
+ return;
+ }
+
+ mpd_maxcontext(&varcontext);
+ varcontext.round = MPD_ROUND_TRUNC;
+
+ i = ln_schedule_prec(klist, prec+2, result->digits);
+ for (; i >= 0; i--) {
+ varcontext.prec = 2*klist[i]+3;
+ result->flags ^= MPD_NEG;
+ _mpd_qexp(&tmp, result, &varcontext, status);
+ result->flags ^= MPD_NEG;
+ mpd_qmul(&tmp, &static10, &tmp, &varcontext, status);
+ mpd_qsub(&tmp, &tmp, &one, &maxcontext, status);
+ mpd_qadd(result, result, &tmp, &maxcontext, status);
+ if (mpd_isspecial(result)) {
+ break;
+ }
+ }
+
+ mpd_del(&tmp);
+ maxcontext.prec = prec;
+ mpd_qfinalize(result, &maxcontext, status);
+}
+
+/* Initial approximations for the ln() iteration */
+static const uint16_t lnapprox[900] = {
+ /* index 0 - 400: log((i+100)/100) * 1000 */
+ 0, 10, 20, 30, 39, 49, 58, 68, 77, 86, 95, 104, 113, 122, 131, 140, 148, 157,
+ 166, 174, 182, 191, 199, 207, 215, 223, 231, 239, 247, 255, 262, 270, 278,
+ 285, 293, 300, 308, 315, 322, 329, 336, 344, 351, 358, 365, 372, 378, 385,
+ 392, 399, 406, 412, 419, 425, 432, 438, 445, 451, 457, 464, 470, 476, 482,
+ 489, 495, 501, 507, 513, 519, 525, 531, 536, 542, 548, 554, 560, 565, 571,
+ 577, 582, 588, 593, 599, 604, 610, 615, 621, 626, 631, 637, 642, 647, 652,
+ 658, 663, 668, 673, 678, 683, 688, 693, 698, 703, 708, 713, 718, 723, 728,
+ 732, 737, 742, 747, 751, 756, 761, 766, 770, 775, 779, 784, 788, 793, 798,
+ 802, 806, 811, 815, 820, 824, 829, 833, 837, 842, 846, 850, 854, 859, 863,
+ 867, 871, 876, 880, 884, 888, 892, 896, 900, 904, 908, 912, 916, 920, 924,
+ 928, 932, 936, 940, 944, 948, 952, 956, 959, 963, 967, 971, 975, 978, 982,
+ 986, 990, 993, 997, 1001, 1004, 1008, 1012, 1015, 1019, 1022, 1026, 1030,
+ 1033, 1037, 1040, 1044, 1047, 1051, 1054, 1058, 1061, 1065, 1068, 1072, 1075,
+ 1078, 1082, 1085, 1089, 1092, 1095, 1099, 1102, 1105, 1109, 1112, 1115, 1118,
+ 1122, 1125, 1128, 1131, 1135, 1138, 1141, 1144, 1147, 1151, 1154, 1157, 1160,
+ 1163, 1166, 1169, 1172, 1176, 1179, 1182, 1185, 1188, 1191, 1194, 1197, 1200,
+ 1203, 1206, 1209, 1212, 1215, 1218, 1221, 1224, 1227, 1230, 1233, 1235, 1238,
+ 1241, 1244, 1247, 1250, 1253, 1256, 1258, 1261, 1264, 1267, 1270, 1273, 1275,
+ 1278, 1281, 1284, 1286, 1289, 1292, 1295, 1297, 1300, 1303, 1306, 1308, 1311,
+ 1314, 1316, 1319, 1322, 1324, 1327, 1330, 1332, 1335, 1338, 1340, 1343, 1345,
+ 1348, 1351, 1353, 1356, 1358, 1361, 1364, 1366, 1369, 1371, 1374, 1376, 1379,
+ 1381, 1384, 1386, 1389, 1391, 1394, 1396, 1399, 1401, 1404, 1406, 1409, 1411,
+ 1413, 1416, 1418, 1421, 1423, 1426, 1428, 1430, 1433, 1435, 1437, 1440, 1442,
+ 1445, 1447, 1449, 1452, 1454, 1456, 1459, 1461, 1463, 1466, 1468, 1470, 1472,
+ 1475, 1477, 1479, 1482, 1484, 1486, 1488, 1491, 1493, 1495, 1497, 1500, 1502,
+ 1504, 1506, 1509, 1511, 1513, 1515, 1517, 1520, 1522, 1524, 1526, 1528, 1530,
+ 1533, 1535, 1537, 1539, 1541, 1543, 1545, 1548, 1550, 1552, 1554, 1556, 1558,
+ 1560, 1562, 1564, 1567, 1569, 1571, 1573, 1575, 1577, 1579, 1581, 1583, 1585,
+ 1587, 1589, 1591, 1593, 1595, 1597, 1599, 1601, 1603, 1605, 1607, 1609,
+ /* index 401 - 899: -log((i+100)/1000) * 1000 */
+ 691, 689, 687, 685, 683, 681, 679, 677, 675, 673, 671, 669, 668, 666, 664,
+ 662, 660, 658, 656, 654, 652, 650, 648, 646, 644, 642, 641, 639, 637, 635,
+ 633, 631, 629, 627, 626, 624, 622, 620, 618, 616, 614, 612, 611, 609, 607,
+ 605, 603, 602, 600, 598, 596, 594, 592, 591, 589, 587, 585, 583, 582, 580,
+ 578, 576, 574, 573, 571, 569, 567, 566, 564, 562, 560, 559, 557, 555, 553,
+ 552, 550, 548, 546, 545, 543, 541, 540, 538, 536, 534, 533, 531, 529, 528,
+ 526, 524, 523, 521, 519, 518, 516, 514, 512, 511, 509, 508, 506, 504, 502,
+ 501, 499, 498, 496, 494, 493, 491, 489, 488, 486, 484, 483, 481, 480, 478,
+ 476, 475, 473, 472, 470, 468, 467, 465, 464, 462, 460, 459, 457, 456, 454,
+ 453, 451, 449, 448, 446, 445, 443, 442, 440, 438, 437, 435, 434, 432, 431,
+ 429, 428, 426, 425, 423, 422, 420, 419, 417, 416, 414, 412, 411, 410, 408,
+ 406, 405, 404, 402, 400, 399, 398, 396, 394, 393, 392, 390, 389, 387, 386,
+ 384, 383, 381, 380, 378, 377, 375, 374, 372, 371, 370, 368, 367, 365, 364,
+ 362, 361, 360, 358, 357, 355, 354, 352, 351, 350, 348, 347, 345, 344, 342,
+ 341, 340, 338, 337, 336, 334, 333, 331, 330, 328, 327, 326, 324, 323, 322,
+ 320, 319, 318, 316, 315, 313, 312, 311, 309, 308, 306, 305, 304, 302, 301,
+ 300, 298, 297, 296, 294, 293, 292, 290, 289, 288, 286, 285, 284, 282, 281,
+ 280, 278, 277, 276, 274, 273, 272, 270, 269, 268, 267, 265, 264, 263, 261,
+ 260, 259, 258, 256, 255, 254, 252, 251, 250, 248, 247, 246, 245, 243, 242,
+ 241, 240, 238, 237, 236, 234, 233, 232, 231, 229, 228, 227, 226, 224, 223,
+ 222, 221, 219, 218, 217, 216, 214, 213, 212, 211, 210, 208, 207, 206, 205,
+ 203, 202, 201, 200, 198, 197, 196, 195, 194, 192, 191, 190, 189, 188, 186,
+ 185, 184, 183, 182, 180, 179, 178, 177, 176, 174, 173, 172, 171, 170, 168,
+ 167, 166, 165, 164, 162, 161, 160, 159, 158, 157, 156, 154, 153, 152, 151,
+ 150, 148, 147, 146, 145, 144, 143, 142, 140, 139, 138, 137, 136, 135, 134,
+ 132, 131, 130, 129, 128, 127, 126, 124, 123, 122, 121, 120, 119, 118, 116,
+ 115, 114, 113, 112, 111, 110, 109, 108, 106, 105, 104, 103, 102, 101, 100,
+ 99, 98, 97, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 84, 83, 82, 81, 80, 79,
+ 78, 77, 76, 75, 74, 73, 72, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59,
+ 58, 57, 56, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39,
+ 38, 37, 36, 35, 34, 33, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19,
+ 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
+};
+
+/* Internal ln() function that does not check for specials, zero or one. */
+static void
+_mpd_qln(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ mpd_context_t varcontext, maxcontext;
+ mpd_t *z = (mpd_t *) result;
+ MPD_NEW_STATIC(v,0,0,0,0);
+ MPD_NEW_STATIC(vtmp,0,0,0,0);
+ MPD_NEW_STATIC(tmp,0,0,0,0);
+ mpd_ssize_t klist[MPD_MAX_PREC_LOG2];
+ mpd_ssize_t maxprec, shift, t;
+ mpd_ssize_t a_digits, a_exp;
+ mpd_uint_t dummy, x;
+ int i;
+
+ assert(!mpd_isspecial(a) && !mpd_iszerocoeff(a));
+
+ /*
+ * We are calculating ln(a) = ln(v * 10^t) = ln(v) + t*ln(10),
+ * where 0.5 < v <= 5.
+ */
+ if (!mpd_qcopy(&v, a, status)) {
+ mpd_seterror(result, MPD_Malloc_error, status);
+ goto finish;
+ }
+
+ /* Initial approximation: we have at least one non-zero digit */
+ _mpd_get_msdigits(&dummy, &x, &v, 3);
+ if (x < 10) x *= 10;
+ if (x < 100) x *= 10;
+ x -= 100;
+
+ /* a may equal z */
+ a_digits = a->digits;
+ a_exp = a->exp;
+
+ mpd_minalloc(z);
+ mpd_clear_flags(z);
+ z->data[0] = lnapprox[x];
+ z->len = 1;
+ z->exp = -3;
+ mpd_setdigits(z);
+
+ if (x <= 400) {
+ v.exp = -(a_digits - 1);
+ t = a_exp + a_digits - 1;
+ }
+ else {
+ v.exp = -a_digits;
+ t = a_exp + a_digits;
+ mpd_set_negative(z);
+ }
+
+ mpd_maxcontext(&maxcontext);
+ mpd_maxcontext(&varcontext);
+ varcontext.round = MPD_ROUND_TRUNC;
+
+ maxprec = ctx->prec + 2;
+ if (x <= 10 || x >= 805) {
+ /* v is close to 1: Estimate the magnitude of the logarithm.
+ * If v = 1 or ln(v) will underflow, skip the loop. Otherwise,
+ * adjust the precision upwards in order to obtain a sufficient
+ * number of significant digits.
+ *
+ * 1) x/(1+x) < ln(1+x) < x, for x > -1, x != 0
+ *
+ * 2) (v-1)/v < ln(v) < v-1
+ */
+ mpd_t *lower = &tmp;
+ mpd_t *upper = &vtmp;
+ int cmp = _mpd_cmp(&v, &one);
+
+ varcontext.round = MPD_ROUND_CEILING;
+ varcontext.prec = maxprec;
+ mpd_qsub(upper, &v, &one, &varcontext, &varcontext.status);
+ varcontext.round = MPD_ROUND_FLOOR;
+ mpd_qdiv(lower, upper, &v, &varcontext, &varcontext.status);
+ varcontext.round = MPD_ROUND_TRUNC;
+
+ if (cmp < 0) {
+ _mpd_ptrswap(&upper, &lower);
+ }
+ if (mpd_adjexp(upper) < mpd_etiny(ctx)) {
+ _settriple(z, (cmp<0), 1, mpd_etiny(ctx)-1);
+ goto postloop;
+ }
+ /* XXX optimization: t == 0 && mpd_adjexp(lower) < 0 */
+ if (mpd_adjexp(lower) < 0) {
+ maxprec = maxprec - mpd_adjexp(lower);
+ }
+ }
+
+ i = ln_schedule_prec(klist, maxprec, 2);
+ for (; i >= 0; i--) {
+ varcontext.prec = 2*klist[i]+3;
+ z->flags ^= MPD_NEG;
+ _mpd_qexp(&tmp, z, &varcontext, status);
+ z->flags ^= MPD_NEG;
+
+ if (v.digits > varcontext.prec) {
+ shift = v.digits - varcontext.prec;
+ mpd_qshiftr(&vtmp, &v, shift, status);
+ vtmp.exp += shift;
+ mpd_qmul(&tmp, &vtmp, &tmp, &varcontext, status);
+ }
+ else {
+ mpd_qmul(&tmp, &v, &tmp, &varcontext, status);
+ }
+
+ mpd_qsub(&tmp, &tmp, &one, &maxcontext, status);
+ mpd_qadd(z, z, &tmp, &maxcontext, status);
+ if (mpd_isspecial(z)) {
+ break;
+ }
+ }
+
+postloop:
+ mpd_qln10(&v, maxprec+2, status);
+ mpd_qmul_ssize(&tmp, &v, t, &maxcontext, status);
+ varcontext.prec = maxprec+2;
+ mpd_qadd(result, &tmp, z, &varcontext, status);
+
+
+finish:
+ mpd_del(&v);
+ mpd_del(&vtmp);
+ mpd_del(&tmp);
+}
+
+/* ln(a) */
+void
+mpd_qln(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ mpd_context_t workctx;
+ mpd_ssize_t adjexp, t;
+
+ if (mpd_isspecial(a)) {
+ if (mpd_qcheck_nan(result, a, ctx, status)) {
+ return;
+ }
+ if (mpd_isnegative(a)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ mpd_setspecial(result, MPD_POS, MPD_INF);
+ return;
+ }
+ if (mpd_iszerocoeff(a)) {
+ mpd_setspecial(result, MPD_NEG, MPD_INF);
+ return;
+ }
+ if (mpd_isnegative(a)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ if (_mpd_cmp(a, &one) == 0) {
+ _settriple(result, MPD_POS, 0, 0);
+ return;
+ }
+ /* Check if the result will overflow.
+ *
+ * 1) adjexp(a) + 1 > log10(a) >= adjexp(a)
+ *
+ * 2) |log10(a)| >= adjexp(a), if adjexp(a) >= 0
+ * |log10(a)| > -adjexp(a)-1, if adjexp(a) < 0
+ *
+ * 3) |log(a)| > 2*|log10(a)|
+ */
+ adjexp = mpd_adjexp(a);
+ t = (adjexp < 0) ? -adjexp-1 : adjexp;
+ t *= 2;
+ if (mpd_exp_digits(t)-1 > ctx->emax) {
+ *status |= MPD_Overflow|MPD_Inexact|MPD_Rounded;
+ mpd_setspecial(result, (adjexp<0), MPD_INF);
+ return;
+ }
+
+ workctx = *ctx;
+ workctx.round = MPD_ROUND_HALF_EVEN;
+
+ if (ctx->allcr) {
+ MPD_NEW_STATIC(t1, 0,0,0,0);
+ MPD_NEW_STATIC(t2, 0,0,0,0);
+ MPD_NEW_STATIC(ulp, 0,0,0,0);
+ MPD_NEW_STATIC(aa, 0,0,0,0);
+ mpd_ssize_t prec;
+
+ if (result == a) {
+ if (!mpd_qcopy(&aa, a, status)) {
+ mpd_seterror(result, MPD_Malloc_error, status);
+ return;
+ }
+ a = &aa;
+ }
+
+ workctx.clamp = 0;
+ prec = ctx->prec + 3;
+ while (1) {
+ workctx.prec = prec;
+ _mpd_qln(result, a, &workctx, status);
+ _ssettriple(&ulp, MPD_POS, 1,
+ result->exp + result->digits-workctx.prec-1);
+
+ workctx.prec = ctx->prec;
+ mpd_qadd(&t1, result, &ulp, &workctx, &workctx.status);
+ mpd_qsub(&t2, result, &ulp, &workctx, &workctx.status);
+ if (mpd_isspecial(result) || mpd_iszerocoeff(result) ||
+ mpd_qcmp(&t1, &t2, status) == 0) {
+ workctx.clamp = ctx->clamp;
+ mpd_check_underflow(result, &workctx, status);
+ mpd_qfinalize(result, &workctx, status);
+ break;
+ }
+ prec += MPD_RDIGITS;
+ }
+ mpd_del(&t1);
+ mpd_del(&t2);
+ mpd_del(&ulp);
+ mpd_del(&aa);
+ }
+ else {
+ _mpd_qln(result, a, &workctx, status);
+ mpd_check_underflow(result, &workctx, status);
+ mpd_qfinalize(result, &workctx, status);
+ }
+}
+
+/* Internal log10() function that does not check for specials, zero, ... */
+static void
+_mpd_qlog10(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ mpd_context_t workctx;
+ MPD_NEW_STATIC(ln10,0,0,0,0);
+
+ mpd_maxcontext(&workctx);
+ workctx.prec = ctx->prec + 3;
+ _mpd_qln(result, a, &workctx, status);
+ mpd_qln10(&ln10, workctx.prec, status);
+
+ workctx = *ctx;
+ workctx.round = MPD_ROUND_HALF_EVEN;
+ _mpd_qdiv(NO_IDEAL_EXP, result, result, &ln10, &workctx, status);
+
+ mpd_del(&ln10);
+}
+
+/* log10(a) */
+void
+mpd_qlog10(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ mpd_context_t workctx;
+ mpd_ssize_t adjexp, t;
+
+ workctx = *ctx;
+ workctx.round = MPD_ROUND_HALF_EVEN;
+
+ if (mpd_isspecial(a)) {
+ if (mpd_qcheck_nan(result, a, ctx, status)) {
+ return;
+ }
+ if (mpd_isnegative(a)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ mpd_setspecial(result, MPD_POS, MPD_INF);
+ return;
+ }
+ if (mpd_iszerocoeff(a)) {
+ mpd_setspecial(result, MPD_NEG, MPD_INF);
+ return;
+ }
+ if (mpd_isnegative(a)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ if (mpd_coeff_ispow10(a)) {
+ uint8_t sign = 0;
+ adjexp = mpd_adjexp(a);
+ if (adjexp < 0) {
+ sign = 1;
+ adjexp = -adjexp;
+ }
+ _settriple(result, sign, adjexp, 0);
+ mpd_qfinalize(result, &workctx, status);
+ return;
+ }
+ /* Check if the result will overflow.
+ *
+ * 1) adjexp(a) + 1 > log10(a) >= adjexp(a)
+ *
+ * 2) |log10(a)| >= adjexp(a), if adjexp(a) >= 0
+ * |log10(a)| > -adjexp(a)-1, if adjexp(a) < 0
+ */
+ adjexp = mpd_adjexp(a);
+ t = (adjexp < 0) ? -adjexp-1 : adjexp;
+ if (mpd_exp_digits(t)-1 > ctx->emax) {
+ *status |= MPD_Overflow|MPD_Inexact|MPD_Rounded;
+ mpd_setspecial(result, (adjexp<0), MPD_INF);
+ return;
+ }
+
+ if (ctx->allcr) {
+ MPD_NEW_STATIC(t1, 0,0,0,0);
+ MPD_NEW_STATIC(t2, 0,0,0,0);
+ MPD_NEW_STATIC(ulp, 0,0,0,0);
+ MPD_NEW_STATIC(aa, 0,0,0,0);
+ mpd_ssize_t prec;
+
+ if (result == a) {
+ if (!mpd_qcopy(&aa, a, status)) {
+ mpd_seterror(result, MPD_Malloc_error, status);
+ return;
+ }
+ a = &aa;
+ }
+
+ workctx.clamp = 0;
+ prec = ctx->prec + 3;
+ while (1) {
+ workctx.prec = prec;
+ _mpd_qlog10(result, a, &workctx, status);
+ _ssettriple(&ulp, MPD_POS, 1,
+ result->exp + result->digits-workctx.prec-1);
+
+ workctx.prec = ctx->prec;
+ mpd_qadd(&t1, result, &ulp, &workctx, &workctx.status);
+ mpd_qsub(&t2, result, &ulp, &workctx, &workctx.status);
+ if (mpd_isspecial(result) || mpd_iszerocoeff(result) ||
+ mpd_qcmp(&t1, &t2, status) == 0) {
+ workctx.clamp = ctx->clamp;
+ mpd_check_underflow(result, &workctx, status);
+ mpd_qfinalize(result, &workctx, status);
+ break;
+ }
+ prec += MPD_RDIGITS;
+ }
+ mpd_del(&t1);
+ mpd_del(&t2);
+ mpd_del(&ulp);
+ mpd_del(&aa);
+ }
+ else {
+ _mpd_qlog10(result, a, &workctx, status);
+ mpd_check_underflow(result, &workctx, status);
+ }
+}
+
+/*
+ * Maximum of the two operands. Attention: If one operand is a quiet NaN and the
+ * other is numeric, the numeric operand is returned. This may not be what one
+ * expects.
+ */
+void
+mpd_qmax(mpd_t *result, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ int c;
+
+ if (mpd_isqnan(a) && !mpd_isnan(b)) {
+ mpd_qcopy(result, b, status);
+ }
+ else if (mpd_isqnan(b) && !mpd_isnan(a)) {
+ mpd_qcopy(result, a, status);
+ }
+ else if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+ return;
+ }
+ else {
+ c = _mpd_cmp(a, b);
+ if (c == 0) {
+ c = _mpd_cmp_numequal(a, b);
+ }
+
+ if (c < 0) {
+ mpd_qcopy(result, b, status);
+ }
+ else {
+ mpd_qcopy(result, a, status);
+ }
+ }
+
+ mpd_qfinalize(result, ctx, status);
+}
+
+/*
+ * Maximum magnitude: Same as mpd_max(), but compares the operands with their
+ * sign ignored.
+ */
+void
+mpd_qmax_mag(mpd_t *result, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ int c;
+
+ if (mpd_isqnan(a) && !mpd_isnan(b)) {
+ mpd_qcopy(result, b, status);
+ }
+ else if (mpd_isqnan(b) && !mpd_isnan(a)) {
+ mpd_qcopy(result, a, status);
+ }
+ else if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+ return;
+ }
+ else {
+ c = _mpd_cmp_abs(a, b);
+ if (c == 0) {
+ c = _mpd_cmp_numequal(a, b);
+ }
+
+ if (c < 0) {
+ mpd_qcopy(result, b, status);
+ }
+ else {
+ mpd_qcopy(result, a, status);
+ }
+ }
+
+ mpd_qfinalize(result, ctx, status);
+}
+
+/*
+ * Minimum of the two operands. Attention: If one operand is a quiet NaN and the
+ * other is numeric, the numeric operand is returned. This may not be what one
+ * expects.
+ */
+void
+mpd_qmin(mpd_t *result, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ int c;
+
+ if (mpd_isqnan(a) && !mpd_isnan(b)) {
+ mpd_qcopy(result, b, status);
+ }
+ else if (mpd_isqnan(b) && !mpd_isnan(a)) {
+ mpd_qcopy(result, a, status);
+ }
+ else if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+ return;
+ }
+ else {
+ c = _mpd_cmp(a, b);
+ if (c == 0) {
+ c = _mpd_cmp_numequal(a, b);
+ }
+
+ if (c < 0) {
+ mpd_qcopy(result, a, status);
+ }
+ else {
+ mpd_qcopy(result, b, status);
+ }
+ }
+
+ mpd_qfinalize(result, ctx, status);
+}
+
+/*
+ * Minimum magnitude: Same as mpd_min(), but compares the operands with their
+ * sign ignored.
+ */
+void
+mpd_qmin_mag(mpd_t *result, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ int c;
+
+ if (mpd_isqnan(a) && !mpd_isnan(b)) {
+ mpd_qcopy(result, b, status);
+ }
+ else if (mpd_isqnan(b) && !mpd_isnan(a)) {
+ mpd_qcopy(result, a, status);
+ }
+ else if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+ return;
+ }
+ else {
+ c = _mpd_cmp_abs(a, b);
+ if (c == 0) {
+ c = _mpd_cmp_numequal(a, b);
+ }
+
+ if (c < 0) {
+ mpd_qcopy(result, a, status);
+ }
+ else {
+ mpd_qcopy(result, b, status);
+ }
+ }
+
+ mpd_qfinalize(result, ctx, status);
+}
+
+/* Minimum space needed for the result array in _karatsuba_rec(). */
+static inline mpd_size_t
+_kmul_resultsize(mpd_size_t la, mpd_size_t lb)
+{
+ mpd_size_t n, m;
+
+ n = add_size_t(la, lb);
+ n = add_size_t(n, 1);
+
+ m = (la+1)/2 + 1;
+ m = mul_size_t(m, 3);
+
+ return (m > n) ? m : n;
+}
+
+/* Work space needed in _karatsuba_rec(). lim >= 4 */
+static inline mpd_size_t
+_kmul_worksize(mpd_size_t n, mpd_size_t lim)
+{
+ mpd_size_t m;
+
+ if (n <= lim) {
+ return 0;
+ }
+
+ m = (n+1)/2 + 1;
+
+ return add_size_t(mul_size_t(m, 2), _kmul_worksize(m, lim));
+}
+
+
+#define MPD_KARATSUBA_BASECASE 16 /* must be >= 4 */
+
+/*
+ * Add the product of a and b to c.
+ * c must be _kmul_resultsize(la, lb) in size.
+ * w is used as a work array and must be _kmul_worksize(a, lim) in size.
+ * Roman E. Maeder, Storage Allocation for the Karatsuba Integer Multiplication
+ * Algorithm. In "Design and implementation of symbolic computation systems",
+ * Springer, 1993, ISBN 354057235X, 9783540572350.
+ */
+static void
+_karatsuba_rec(mpd_uint_t *c, const mpd_uint_t *a, const mpd_uint_t *b,
+ mpd_uint_t *w, mpd_size_t la, mpd_size_t lb)
+{
+ mpd_size_t m, lt;
+
+ assert(la >= lb && lb > 0);
+ assert(la <= MPD_KARATSUBA_BASECASE || w != NULL);
+
+ if (la <= MPD_KARATSUBA_BASECASE) {
+ _mpd_basemul(c, a, b, la, lb);
+ return;
+ }
+
+ m = (la+1)/2; // ceil(la/2)
+
+ /* lb <= m < la */
+ if (lb <= m) {
+
+ /* lb can now be larger than la-m */
+ if (lb > la-m) {
+ lt = lb + lb + 1; // space needed for result array
+ mpd_uint_zero(w, lt); // clear result array
+ _karatsuba_rec(w, b, a+m, w+lt, lb, la-m); // b*ah
+ }
+ else {
+ lt = (la-m) + (la-m) + 1; // space needed for result array
+ mpd_uint_zero(w, lt); // clear result array
+ _karatsuba_rec(w, a+m, b, w+lt, la-m, lb); // ah*b
+ }
+ _mpd_baseaddto(c+m, w, (la-m)+lb); // add ah*b*B**m
+
+ lt = m + m + 1; // space needed for the result array
+ mpd_uint_zero(w, lt); // clear result array
+ _karatsuba_rec(w, a, b, w+lt, m, lb); // al*b
+ _mpd_baseaddto(c, w, m+lb); // add al*b
+
+ return;
+ }
+
+ /* la >= lb > m */
+ memcpy(w, a, m * sizeof *w);
+ w[m] = 0;
+ _mpd_baseaddto(w, a+m, la-m);
+
+ memcpy(w+(m+1), b, m * sizeof *w);
+ w[m+1+m] = 0;
+ _mpd_baseaddto(w+(m+1), b+m, lb-m);
+
+ _karatsuba_rec(c+m, w, w+(m+1), w+2*(m+1), m+1, m+1);
+
+ lt = (la-m) + (la-m) + 1;
+ mpd_uint_zero(w, lt);
+
+ _karatsuba_rec(w, a+m, b+m, w+lt, la-m, lb-m);
+
+ _mpd_baseaddto(c+2*m, w, (la-m) + (lb-m));
+ _mpd_basesubfrom(c+m, w, (la-m) + (lb-m));
+
+ lt = m + m + 1;
+ mpd_uint_zero(w, lt);
+
+ _karatsuba_rec(w, a, b, w+lt, m, m);
+ _mpd_baseaddto(c, w, m+m);
+ _mpd_basesubfrom(c+m, w, m+m);
+
+ return;
+}
+
+/*
+ * Multiply u and v, using Karatsuba multiplication. Returns a pointer
+ * to the result or NULL in case of failure (malloc error).
+ * Conditions: ulen >= vlen, ulen >= 4
+ */
+mpd_uint_t *
+_mpd_kmul(const mpd_uint_t *u, const mpd_uint_t *v,
+ mpd_size_t ulen, mpd_size_t vlen,
+ mpd_size_t *rsize)
+{
+ mpd_uint_t *result = NULL, *w = NULL;
+ mpd_size_t m;
+
+ assert(ulen >= 4);
+ assert(ulen >= vlen);
+
+ *rsize = _kmul_resultsize(ulen, vlen);
+ if ((result = mpd_calloc(*rsize, sizeof *result)) == NULL) {
+ return NULL;
+ }
+
+ m = _kmul_worksize(ulen, MPD_KARATSUBA_BASECASE);
+ if (m && ((w = mpd_calloc(m, sizeof *w)) == NULL)) {
+ mpd_free(result);
+ return NULL;
+ }
+
+ _karatsuba_rec(result, u, v, w, ulen, vlen);
+
+
+ if (w) mpd_free(w);
+ return result;
+}
+
+
+/* Determine the minimum length for the number theoretic transform. */
+static inline mpd_size_t
+_mpd_get_transform_len(mpd_size_t rsize)
+{
+ mpd_size_t log2rsize;
+ mpd_size_t x, step;
+
+ assert(rsize >= 4);
+ log2rsize = mpd_bsr(rsize);
+
+ if (rsize <= 1024) {
+ x = ((mpd_size_t)1)<<log2rsize;
+ return (rsize == x) ? x : x<<1;
+ }
+ else if (rsize <= MPD_MAXTRANSFORM_2N) {
+ x = ((mpd_size_t)1)<<log2rsize;
+ if (rsize == x) return x;
+ step = x>>1;
+ x += step;
+ return (rsize <= x) ? x : x + step;
+ }
+ else if (rsize <= MPD_MAXTRANSFORM_2N+MPD_MAXTRANSFORM_2N/2) {
+ return MPD_MAXTRANSFORM_2N+MPD_MAXTRANSFORM_2N/2;
+ }
+ else if (rsize <= 3*MPD_MAXTRANSFORM_2N) {
+ return 3*MPD_MAXTRANSFORM_2N;
+ }
+ else {
+ return MPD_SIZE_MAX;
+ }
+}
+
+#ifdef PPRO
+#ifndef _MSC_VER
+static inline unsigned short
+_mpd_get_control87(void)
+{
+ unsigned short cw;
+
+ __asm__ __volatile__ ("fnstcw %0" : "=m" (cw));
+ return cw;
+}
+
+static inline void
+_mpd_set_control87(unsigned short cw)
+{
+ __asm__ __volatile__ ("fldcw %0" : : "m" (cw));
+}
+#endif
+
+unsigned int
+mpd_set_fenv(void)
+{
+ unsigned int cw;
+#ifdef _MSC_VER
+ unsigned int flags =
+ _EM_INVALID|_EM_DENORMAL|_EM_ZERODIVIDE|_EM_OVERFLOW|
+ _EM_UNDERFLOW|_EM_INEXACT|_RC_CHOP|_PC_64;
+ unsigned int mask = _MCW_EM|_MCW_RC|_MCW_PC;
+ unsigned int dummy;
+
+ __control87_2(0, 0, &cw, NULL);
+ __control87_2(flags, mask, &dummy, NULL);
+#else
+ cw = _mpd_get_control87();
+ _mpd_set_control87(cw|0xF3F);
+#endif
+ return cw;
+}
+
+void
+mpd_restore_fenv(unsigned int cw)
+{
+#ifdef _MSC_VER
+ unsigned int mask = _MCW_EM|_MCW_RC|_MCW_PC;
+ unsigned int dummy;
+
+ __control87_2(cw, mask, &dummy, NULL);
+#else
+ _mpd_set_control87((unsigned short)cw);
+#endif
+}
+#endif /* PPRO */
+
+/*
+ * Multiply u and v, using the fast number theoretic transform. Returns
+ * a pointer to the result or NULL in case of failure (malloc error).
+ */
+mpd_uint_t *
+_mpd_fntmul(const mpd_uint_t *u, const mpd_uint_t *v,
+ mpd_size_t ulen, mpd_size_t vlen,
+ mpd_size_t *rsize)
+{
+ mpd_uint_t *c1 = NULL, *c2 = NULL, *c3 = NULL, *vtmp = NULL;
+ mpd_size_t n;
+
+#ifdef PPRO
+ unsigned int cw;
+ cw = mpd_set_fenv();
+#endif
+
+ *rsize = add_size_t(ulen, vlen);
+ if ((n = _mpd_get_transform_len(*rsize)) == MPD_SIZE_MAX) {
+ goto malloc_error;
+ }
+
+ if ((c1 = mpd_calloc(sizeof *c1, n)) == NULL) {
+ goto malloc_error;
+ }
+ if ((c2 = mpd_calloc(sizeof *c2, n)) == NULL) {
+ goto malloc_error;
+ }
+ if ((c3 = mpd_calloc(sizeof *c3, n)) == NULL) {
+ goto malloc_error;
+ }
+
+ memcpy(c1, u, ulen * (sizeof *c1));
+ memcpy(c2, u, ulen * (sizeof *c2));
+ memcpy(c3, u, ulen * (sizeof *c3));
+
+ if (u == v) {
+ if (!fnt_autoconvolute(c1, n, P1) ||
+ !fnt_autoconvolute(c2, n, P2) ||
+ !fnt_autoconvolute(c3, n, P3)) {
+ goto malloc_error;
+ }
+ }
+ else {
+ if ((vtmp = mpd_calloc(sizeof *vtmp, n)) == NULL) {
+ goto malloc_error;
+ }
+
+ memcpy(vtmp, v, vlen * (sizeof *vtmp));
+ if (!fnt_convolute(c1, vtmp, n, P1)) {
+ mpd_free(vtmp);
+ goto malloc_error;
+ }
+
+ memcpy(vtmp, v, vlen * (sizeof *vtmp));
+ mpd_uint_zero(vtmp+vlen, n-vlen);
+ if (!fnt_convolute(c2, vtmp, n, P2)) {
+ mpd_free(vtmp);
+ goto malloc_error;
+ }
+
+ memcpy(vtmp, v, vlen * (sizeof *vtmp));
+ mpd_uint_zero(vtmp+vlen, n-vlen);
+ if (!fnt_convolute(c3, vtmp, n, P3)) {
+ mpd_free(vtmp);
+ goto malloc_error;
+ }
+
+ mpd_free(vtmp);
+ }
+
+ crt3(c1, c2, c3, *rsize);
+
+out:
+#ifdef PPRO
+ mpd_restore_fenv(cw);
+#endif
+ if (c2) mpd_free(c2);
+ if (c3) mpd_free(c3);
+ return c1;
+
+malloc_error:
+ if (c1) mpd_free(c1);
+ c1 = NULL;
+ goto out;
+}
+
+
+/*
+ * Karatsuba multiplication with FNT/basemul as the base case.
+ */
+static int
+_karatsuba_rec_fnt(mpd_uint_t *c, const mpd_uint_t *a, const mpd_uint_t *b,
+ mpd_uint_t *w, mpd_size_t la, mpd_size_t lb)
+{
+ mpd_size_t m, lt;
+
+ assert(la >= lb && lb > 0);
+ assert(la <= 3*(MPD_MAXTRANSFORM_2N/2) || w != NULL);
+
+ if (la <= 3*(MPD_MAXTRANSFORM_2N/2)) {
+
+ if (lb <= 192) {
+ _mpd_basemul(c, b, a, lb, la);
+ }
+ else {
+ mpd_uint_t *result;
+ mpd_size_t dummy;
+
+ if ((result = _mpd_fntmul(a, b, la, lb, &dummy)) == NULL) {
+ return 0;
+ }
+ memcpy(c, result, (la+lb) * (sizeof *result));
+ mpd_free(result);
+ }
+ return 1;
+ }
+
+ m = (la+1)/2; // ceil(la/2)
+
+ /* lb <= m < la */
+ if (lb <= m) {
+
+ /* lb can now be larger than la-m */
+ if (lb > la-m) {
+ lt = lb + lb + 1; // space needed for result array
+ mpd_uint_zero(w, lt); // clear result array
+ if (!_karatsuba_rec_fnt(w, b, a+m, w+lt, lb, la-m)) { // b*ah
+ return 0; /* GCOV_UNLIKELY */
+ }
+ }
+ else {
+ lt = (la-m) + (la-m) + 1; // space needed for result array
+ mpd_uint_zero(w, lt); // clear result array
+ if (!_karatsuba_rec_fnt(w, a+m, b, w+lt, la-m, lb)) { // ah*b
+ return 0; /* GCOV_UNLIKELY */
+ }
+ }
+ _mpd_baseaddto(c+m, w, (la-m)+lb); // add ah*b*B**m
+
+ lt = m + m + 1; // space needed for the result array
+ mpd_uint_zero(w, lt); // clear result array
+ if (!_karatsuba_rec_fnt(w, a, b, w+lt, m, lb)) { // al*b
+ return 0; /* GCOV_UNLIKELY */
+ }
+ _mpd_baseaddto(c, w, m+lb); // add al*b
+
+ return 1;
+ }
+
+ /* la >= lb > m */
+ memcpy(w, a, m * sizeof *w);
+ w[m] = 0;
+ _mpd_baseaddto(w, a+m, la-m);
+
+ memcpy(w+(m+1), b, m * sizeof *w);
+ w[m+1+m] = 0;
+ _mpd_baseaddto(w+(m+1), b+m, lb-m);
+
+ if (!_karatsuba_rec_fnt(c+m, w, w+(m+1), w+2*(m+1), m+1, m+1)) {
+ return 0; /* GCOV_UNLIKELY */
+ }
+
+ lt = (la-m) + (la-m) + 1;
+ mpd_uint_zero(w, lt);
+
+ if (!_karatsuba_rec_fnt(w, a+m, b+m, w+lt, la-m, lb-m)) {
+ return 0; /* GCOV_UNLIKELY */
+ }
+
+ _mpd_baseaddto(c+2*m, w, (la-m) + (lb-m));
+ _mpd_basesubfrom(c+m, w, (la-m) + (lb-m));
+
+ lt = m + m + 1;
+ mpd_uint_zero(w, lt);
+
+ if (!_karatsuba_rec_fnt(w, a, b, w+lt, m, m)) {
+ return 0; /* GCOV_UNLIKELY */
+ }
+ _mpd_baseaddto(c, w, m+m);
+ _mpd_basesubfrom(c+m, w, m+m);
+
+ return 1;
+}
+
+/*
+ * Multiply u and v, using Karatsuba multiplication with the FNT as the
+ * base case. Returns a pointer to the result or NULL in case of failure
+ * (malloc error). Conditions: ulen >= vlen, ulen >= 4.
+ */
+mpd_uint_t *
+_mpd_kmul_fnt(const mpd_uint_t *u, const mpd_uint_t *v,
+ mpd_size_t ulen, mpd_size_t vlen,
+ mpd_size_t *rsize)
+{
+ mpd_uint_t *result = NULL, *w = NULL;
+ mpd_size_t m;
+
+ assert(ulen >= 4);
+ assert(ulen >= vlen);
+
+ *rsize = _kmul_resultsize(ulen, vlen);
+ if ((result = mpd_calloc(*rsize, sizeof *result)) == NULL) {
+ return NULL;
+ }
+
+ m = _kmul_worksize(ulen, 3*(MPD_MAXTRANSFORM_2N/2));
+ if (m && ((w = mpd_calloc(m, sizeof *w)) == NULL)) {
+ mpd_free(result); /* GCOV_UNLIKELY */
+ return NULL; /* GCOV_UNLIKELY */
+ }
+
+ if (!_karatsuba_rec_fnt(result, u, v, w, ulen, vlen)) {
+ mpd_free(result);
+ result = NULL;
+ }
+
+
+ if (w) mpd_free(w);
+ return result;
+}
+
+
+/* Deal with the special cases of multiplying infinities. */
+static void
+_mpd_qmul_inf(mpd_t *result, const mpd_t *a, const mpd_t *b, uint32_t *status)
+{
+ if (mpd_isinfinite(a)) {
+ if (mpd_iszero(b)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ }
+ else {
+ mpd_setspecial(result, mpd_sign(a)^mpd_sign(b), MPD_INF);
+ }
+ return;
+ }
+ assert(mpd_isinfinite(b));
+ if (mpd_iszero(a)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ }
+ else {
+ mpd_setspecial(result, mpd_sign(a)^mpd_sign(b), MPD_INF);
+ }
+}
+
+/*
+ * Internal function: Multiply a and b. _mpd_qmul deals with specials but
+ * does NOT finalize the result. This is for use in mpd_fma().
+ */
+static inline void
+_mpd_qmul(mpd_t *result, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_t *big = (mpd_t *)a, *small = (mpd_t *)b;
+ mpd_uint_t *rdata = NULL;
+ mpd_uint_t rbuf[MPD_MINALLOC_MAX];
+ mpd_size_t rsize, i;
+
+
+ if (mpd_isspecial(a) || mpd_isspecial(b)) {
+ if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+ return;
+ }
+ _mpd_qmul_inf(result, a, b, status);
+ return;
+ }
+
+ if (small->len > big->len) {
+ _mpd_ptrswap(&big, &small);
+ }
+
+ rsize = big->len + small->len;
+
+ if (big->len == 1) {
+ _mpd_singlemul(result->data, big->data[0], small->data[0]);
+ goto finish;
+ }
+ if (rsize <= (mpd_size_t)MPD_MINALLOC_MAX) {
+ if (big->len == 2) {
+ _mpd_mul_2_le2(rbuf, big->data, small->data, small->len);
+ }
+ else {
+ mpd_uint_zero(rbuf, rsize);
+ if (small->len == 1) {
+ _mpd_shortmul(rbuf, big->data, big->len, small->data[0]);
+ }
+ else {
+ _mpd_basemul(rbuf, small->data, big->data, small->len, big->len);
+ }
+ }
+ if (!mpd_qresize(result, rsize, status)) {
+ return;
+ }
+ for(i = 0; i < rsize; i++) {
+ result->data[i] = rbuf[i];
+ }
+ goto finish;
+ }
+
+
+ if (small->len == 1) {
+ if ((rdata = mpd_calloc(rsize, sizeof *rdata)) == NULL) {
+ mpd_seterror(result, MPD_Malloc_error, status);
+ return;
+ }
+ _mpd_shortmul(rdata, big->data, big->len, small->data[0]);
+ }
+ else if (rsize <= 1024) {
+ rdata = _mpd_kmul(big->data, small->data, big->len, small->len, &rsize);
+ if (rdata == NULL) {
+ mpd_seterror(result, MPD_Malloc_error, status);
+ return;
+ }
+ }
+ else if (rsize <= 3*MPD_MAXTRANSFORM_2N) {
+ rdata = _mpd_fntmul(big->data, small->data, big->len, small->len, &rsize);
+ if (rdata == NULL) {
+ mpd_seterror(result, MPD_Malloc_error, status);
+ return;
+ }
+ }
+ else {
+ rdata = _mpd_kmul_fnt(big->data, small->data, big->len, small->len, &rsize);
+ if (rdata == NULL) {
+ mpd_seterror(result, MPD_Malloc_error, status); /* GCOV_UNLIKELY */
+ return; /* GCOV_UNLIKELY */
+ }
+ }
+
+ if (mpd_isdynamic_data(result)) {
+ mpd_free(result->data);
+ }
+ result->data = rdata;
+ result->alloc = rsize;
+ mpd_set_dynamic_data(result);
+
+
+finish:
+ mpd_set_flags(result, mpd_sign(a)^mpd_sign(b));
+ result->exp = big->exp + small->exp;
+ result->len = _mpd_real_size(result->data, rsize);
+ /* resize to smaller cannot fail */
+ mpd_qresize(result, result->len, status);
+ mpd_setdigits(result);
+}
+
+/* Multiply a and b. */
+void
+mpd_qmul(mpd_t *result, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ _mpd_qmul(result, a, b, ctx, status);
+ mpd_qfinalize(result, ctx, status);
+}
+
+/* Multiply decimal and mpd_ssize_t. */
+void
+mpd_qmul_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_context_t maxcontext;
+ MPD_NEW_STATIC(bb,0,0,0,0);
+
+ mpd_maxcontext(&maxcontext);
+ mpd_qsset_ssize(&bb, b, &maxcontext, status);
+ mpd_qmul(result, a, &bb, ctx, status);
+ mpd_del(&bb);
+}
+
+/* Multiply decimal and mpd_uint_t. */
+void
+mpd_qmul_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_context_t maxcontext;
+ MPD_NEW_STATIC(bb,0,0,0,0);
+
+ mpd_maxcontext(&maxcontext);
+ mpd_qsset_uint(&bb, b, &maxcontext, status);
+ mpd_qmul(result, a, &bb, ctx, status);
+ mpd_del(&bb);
+}
+
+void
+mpd_qmul_i32(mpd_t *result, const mpd_t *a, int32_t b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_qmul_ssize(result, a, b, ctx, status);
+}
+
+void
+mpd_qmul_u32(mpd_t *result, const mpd_t *a, uint32_t b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_qmul_uint(result, a, b, ctx, status);
+}
+
+#ifdef CONFIG_64
+void
+mpd_qmul_i64(mpd_t *result, const mpd_t *a, int64_t b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_qmul_ssize(result, a, b, ctx, status);
+}
+
+void
+mpd_qmul_u64(mpd_t *result, const mpd_t *a, uint64_t b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_qmul_uint(result, a, b, ctx, status);
+}
+#endif
+
+/* Like the minus operator. */
+void
+mpd_qminus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ if (mpd_isspecial(a)) {
+ if (mpd_qcheck_nan(result, a, ctx, status)) {
+ return;
+ }
+ }
+
+ if (mpd_iszero(a) && ctx->round != MPD_ROUND_FLOOR) {
+ mpd_qcopy_abs(result, a, status);
+ }
+ else {
+ mpd_qcopy_negate(result, a, status);
+ }
+
+ mpd_qfinalize(result, ctx, status);
+}
+
+/* Like the plus operator. */
+void
+mpd_qplus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ if (mpd_isspecial(a)) {
+ if (mpd_qcheck_nan(result, a, ctx, status)) {
+ return;
+ }
+ }
+
+ if (mpd_iszero(a) && ctx->round != MPD_ROUND_FLOOR) {
+ mpd_qcopy_abs(result, a, status);
+ }
+ else {
+ mpd_qcopy(result, a, status);
+ }
+
+ mpd_qfinalize(result, ctx, status);
+}
+
+/* The largest representable number that is smaller than the operand. */
+void
+mpd_qnext_minus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ mpd_context_t workctx; /* function context */
+ MPD_NEW_CONST(tiny,MPD_POS,mpd_etiny(ctx)-1,1,1,1,1);
+
+ if (mpd_isspecial(a)) {
+ if (mpd_qcheck_nan(result, a, ctx, status)) {
+ return;
+ }
+ if (mpd_isinfinite(a)) {
+ if (mpd_isnegative(a)) {
+ mpd_qcopy(result, a, status);
+ return;
+ }
+ else {
+ mpd_clear_flags(result);
+ mpd_qmaxcoeff(result, ctx, status);
+ if (mpd_isnan(result)) {
+ return;
+ }
+ result->exp = ctx->emax - ctx->prec + 1;
+ return;
+ }
+ }
+ /* debug */
+ abort(); /* GCOV_NOT_REACHED */
+ }
+
+ mpd_workcontext(&workctx, ctx);
+ workctx.round = MPD_ROUND_FLOOR;
+
+ if (!mpd_qcopy(result, a, status)) {
+ return;
+ }
+
+ mpd_qfinalize(result, &workctx, &workctx.status);
+ if (workctx.status&(MPD_Inexact|MPD_Errors)) {
+ *status |= (workctx.status&MPD_Errors);
+ return;
+ }
+
+ workctx.status = 0;
+ mpd_qsub(result, a, &tiny, &workctx, &workctx.status);
+ *status |= (workctx.status&MPD_Errors);
+}
+
+/* The smallest representable number that is larger than the operand. */
+void
+mpd_qnext_plus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ mpd_context_t workctx;
+ MPD_NEW_CONST(tiny,MPD_POS,mpd_etiny(ctx)-1,1,1,1,1);
+
+ if (mpd_isspecial(a)) {
+ if (mpd_qcheck_nan(result, a, ctx, status)) {
+ return;
+ }
+ if (mpd_isinfinite(a)) {
+ if (mpd_ispositive(a)) {
+ mpd_qcopy(result, a, status);
+ }
+ else {
+ mpd_clear_flags(result);
+ mpd_qmaxcoeff(result, ctx, status);
+ if (mpd_isnan(result)) {
+ return;
+ }
+ mpd_set_flags(result, MPD_NEG);
+ result->exp = mpd_etop(ctx);
+ }
+ return;
+ }
+ }
+
+ mpd_workcontext(&workctx, ctx);
+ workctx.round = MPD_ROUND_CEILING;
+
+ if (!mpd_qcopy(result, a, status)) {
+ return;
+ }
+
+ mpd_qfinalize(result, &workctx, &workctx.status);
+ if (workctx.status & (MPD_Inexact|MPD_Errors)) {
+ *status |= (workctx.status&MPD_Errors);
+ return;
+ }
+
+ workctx.status = 0;
+ mpd_qadd(result, a, &tiny, &workctx, &workctx.status);
+ *status |= (workctx.status&MPD_Errors);
+}
+
+/*
+ * The number closest to the first operand that is in the direction towards
+ * the second operand.
+ */
+void
+mpd_qnext_toward(mpd_t *result, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ int c;
+
+ if (mpd_isnan(a) || mpd_isnan(b)) {
+ if (mpd_qcheck_nans(result, a, b, ctx, status))
+ return;
+ }
+
+ c = _mpd_cmp(a, b);
+ if (c == 0) {
+ mpd_qcopy_sign(result, a, b, status);
+ return;
+ }
+
+ if (c < 0) {
+ mpd_qnext_plus(result, a, ctx, status);
+ }
+ else {
+ mpd_qnext_minus(result, a, ctx, status);
+ }
+
+ if (mpd_isinfinite(result)) {
+ *status |= (MPD_Overflow|MPD_Rounded|MPD_Inexact);
+ }
+ else if (mpd_adjexp(result) < ctx->emin) {
+ *status |= (MPD_Underflow|MPD_Subnormal|MPD_Rounded|MPD_Inexact);
+ if (mpd_iszero(result)) {
+ *status |= MPD_Clamped;
+ }
+ }
+}
+
+/*
+ * Internal function: Integer power with mpd_uint_t exponent, base is modified!
+ * Function can fail with MPD_Malloc_error.
+ */
+static inline void
+_mpd_qpow_uint(mpd_t *result, mpd_t *base, mpd_uint_t exp, uint8_t resultsign,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ uint32_t workstatus = 0;
+ mpd_uint_t n;
+
+ if (exp == 0) {
+ _settriple(result, resultsign, 1, 0); /* GCOV_NOT_REACHED */
+ return; /* GCOV_NOT_REACHED */
+ }
+
+ if (!mpd_qcopy(result, base, status)) {
+ return;
+ }
+
+ n = mpd_bits[mpd_bsr(exp)];
+ while (n >>= 1) {
+ mpd_qmul(result, result, result, ctx, &workstatus);
+ if (exp & n) {
+ mpd_qmul(result, result, base, ctx, &workstatus);
+ }
+ if (workstatus & (MPD_Overflow|MPD_Clamped)) {
+ break;
+ }
+ }
+
+ *status |= workstatus;
+ mpd_set_sign(result, resultsign);
+}
+
+/*
+ * Internal function: Integer power with mpd_t exponent, tbase and texp
+ * are modified!! Function can fail with MPD_Malloc_error.
+ */
+static inline void
+_mpd_qpow_mpd(mpd_t *result, mpd_t *tbase, mpd_t *texp, uint8_t resultsign,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ uint32_t workstatus = 0;
+ mpd_context_t maxctx;
+ MPD_NEW_CONST(two,0,0,1,1,1,2);
+
+
+ mpd_maxcontext(&maxctx);
+
+ /* resize to smaller cannot fail */
+ mpd_qcopy(result, &one, status);
+
+ while (!mpd_iszero(texp)) {
+ if (mpd_isodd(texp)) {
+ mpd_qmul(result, result, tbase, ctx, &workstatus);
+ *status |= workstatus;
+ if (workstatus & (MPD_Overflow|MPD_Clamped)) {
+ break;
+ }
+ }
+ mpd_qmul(tbase, tbase, tbase, ctx, &workstatus);
+ mpd_qdivint(texp, texp, &two, &maxctx, &workstatus);
+ if (mpd_isnan(tbase) || mpd_isnan(texp)) {
+ mpd_seterror(result, workstatus&MPD_Errors, status);
+ return;
+ }
+ }
+ mpd_set_sign(result, resultsign);
+}
+
+/*
+ * The power function for integer exponents.
+ */
+static void
+_mpd_qpow_int(mpd_t *result, const mpd_t *base, const mpd_t *exp,
+ uint8_t resultsign,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_context_t workctx;
+ MPD_NEW_STATIC(tbase,0,0,0,0);
+ MPD_NEW_STATIC(texp,0,0,0,0);
+ mpd_ssize_t n;
+
+
+ mpd_workcontext(&workctx, ctx);
+ workctx.prec += (exp->digits + exp->exp + 2);
+ workctx.round = MPD_ROUND_HALF_EVEN;
+ workctx.clamp = 0;
+ if (mpd_isnegative(exp)) {
+ mpd_qdiv(&tbase, &one, base, &workctx, status);
+ if (*status&MPD_Errors) {
+ mpd_setspecial(result, MPD_POS, MPD_NAN);
+ goto finish;
+ }
+ }
+ else {
+ if (!mpd_qcopy(&tbase, base, status)) {
+ mpd_setspecial(result, MPD_POS, MPD_NAN);
+ goto finish;
+ }
+ }
+
+ n = mpd_qabs_uint(exp, &workctx.status);
+ if (workctx.status&MPD_Invalid_operation) {
+ if (!mpd_qcopy(&texp, exp, status)) {
+ mpd_setspecial(result, MPD_POS, MPD_NAN); /* GCOV_UNLIKELY */
+ goto finish; /* GCOV_UNLIKELY */
+ }
+ _mpd_qpow_mpd(result, &tbase, &texp, resultsign, &workctx, status);
+ }
+ else {
+ _mpd_qpow_uint(result, &tbase, n, resultsign, &workctx, status);
+ }
+
+ if (mpd_isinfinite(result)) {
+ /* for ROUND_DOWN, ROUND_FLOOR, etc. */
+ _settriple(result, resultsign, 1, MPD_EXP_INF);
+ }
+
+finish:
+ mpd_del(&tbase);
+ mpd_del(&texp);
+ mpd_qfinalize(result, ctx, status);
+}
+
+/*
+ * This is an internal function that does not check for NaNs.
+ */
+static int
+_qcheck_pow_one_inf(mpd_t *result, const mpd_t *base, uint8_t resultsign,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_ssize_t shift;
+ int cmp;
+
+ if ((cmp = _mpd_cmp(base, &one)) == 0) {
+ shift = ctx->prec-1;
+ mpd_qshiftl(result, &one, shift, status);
+ result->exp = -shift;
+ mpd_set_flags(result, resultsign);
+ *status |= (MPD_Inexact|MPD_Rounded);
+ }
+
+ return cmp;
+}
+
+/*
+ * If base equals one, calculate the correct power of one result.
+ * Otherwise, result is undefined. Return the value of the comparison
+ * against 1.
+ *
+ * This is an internal function that does not check for specials.
+ */
+static int
+_qcheck_pow_one(mpd_t *result, const mpd_t *base, const mpd_t *exp,
+ uint8_t resultsign,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ uint32_t workstatus = 0;
+ mpd_ssize_t shift;
+ int cmp;
+
+ if ((cmp = _mpd_cmp_abs(base, &one)) == 0) {
+ if (_mpd_isint(exp)) {
+ if (mpd_isnegative(exp)) {
+ _settriple(result, resultsign, 1, 0);
+ return 0;
+ }
+ /* 1.000**3 = 1.000000000 */
+ mpd_qmul_ssize(result, exp, -base->exp, ctx, &workstatus);
+ if (workstatus&MPD_Errors) {
+ *status |= (workstatus&MPD_Errors);
+ return 0;
+ }
+ /* digits-1 after exponentiation */
+ shift = mpd_qget_ssize(result, &workstatus);
+ /* shift is MPD_SSIZE_MAX if result is too large */
+ if (shift > ctx->prec-1) {
+ shift = ctx->prec-1;
+ *status |= MPD_Rounded;
+ }
+ }
+ else if (mpd_ispositive(base)) {
+ shift = ctx->prec-1;
+ *status |= (MPD_Inexact|MPD_Rounded);
+ }
+ else {
+ return -2; /* GCOV_NOT_REACHED */
+ }
+ if (!mpd_qshiftl(result, &one, shift, status)) {
+ return 0;
+ }
+ result->exp = -shift;
+ mpd_set_flags(result, resultsign);
+ }
+
+ return cmp;
+}
+
+/*
+ * Detect certain over/underflow of x**y.
+ * ACL2 proof: pow_bounds.lisp.
+ *
+ * Symbols:
+ *
+ * e: EXP_INF or EXP_CLAMP
+ * x: base
+ * y: exponent
+ *
+ * omega(e) = log10(abs(e))
+ * zeta(x) = log10(abs(log10(x)))
+ * theta(y) = log10(abs(y))
+ *
+ * Upper and lower bounds:
+ *
+ * ub_omega(e) = ceil(log10(abs(e)))
+ * lb_theta(y) = floor(log10(abs(y)))
+ *
+ * | floor(log10(floor(abs(log10(x))))) if x < 1/10 or x >= 10
+ * lb_zeta(x) = | floor(log10(abs(x-1)/10)) if 1/10 <= x < 1
+ * | floor(log10(abs((x-1)/100))) if 1 < x < 10
+ *
+ * ub_omega(e) and lb_theta(y) are obviously upper and lower bounds
+ * for omega(e) and theta(y).
+ *
+ * lb_zeta is a lower bound for zeta(x):
+ *
+ * x < 1/10 or x >= 10:
+ *
+ * abs(log10(x)) >= 1, so the outer log10 is well defined. Since log10
+ * is strictly increasing, the end result is a lower bound.
+ *
+ * 1/10 <= x < 1:
+ *
+ * We use: log10(x) <= (x-1)/log(10)
+ * abs(log10(x)) >= abs(x-1)/log(10)
+ * abs(log10(x)) >= abs(x-1)/10
+ *
+ * 1 < x < 10:
+ *
+ * We use: (x-1)/(x*log(10)) < log10(x)
+ * abs((x-1)/100) < abs(log10(x))
+ *
+ * XXX: abs((x-1)/10) would work, need ACL2 proof.
+ *
+ *
+ * Let (0 < x < 1 and y < 0) or (x > 1 and y > 0). (H1)
+ * Let ub_omega(exp_inf) < lb_zeta(x) + lb_theta(y) (H2)
+ *
+ * Then:
+ * log10(abs(exp_inf)) < log10(abs(log10(x))) + log10(abs(y)). (1)
+ * exp_inf < log10(x) * y (2)
+ * 10**exp_inf < x**y (3)
+ *
+ * Let (0 < x < 1 and y > 0) or (x > 1 and y < 0). (H3)
+ * Let ub_omega(exp_clamp) < lb_zeta(x) + lb_theta(y) (H4)
+ *
+ * Then:
+ * log10(abs(exp_clamp)) < log10(abs(log10(x))) + log10(abs(y)). (4)
+ * log10(x) * y < exp_clamp (5)
+ * x**y < 10**exp_clamp (6)
+ *
+ */
+static mpd_ssize_t
+_lower_bound_zeta(const mpd_t *x, uint32_t *status)
+{
+ mpd_context_t maxctx;
+ MPD_NEW_STATIC(scratch,0,0,0,0);
+ mpd_ssize_t t, u;
+
+ t = mpd_adjexp(x);
+ if (t > 0) {
+ /* x >= 10 -> floor(log10(floor(abs(log10(x))))) */
+ return mpd_exp_digits(t) - 1;
+ }
+ else if (t < -1) {
+ /* x < 1/10 -> floor(log10(floor(abs(log10(x))))) */
+ return mpd_exp_digits(t+1) - 1;
+ }
+ else {
+ mpd_maxcontext(&maxctx);
+ mpd_qsub(&scratch, x, &one, &maxctx, status);
+ if (mpd_isspecial(&scratch)) {
+ mpd_del(&scratch);
+ return MPD_SSIZE_MAX;
+ }
+ u = mpd_adjexp(&scratch);
+ mpd_del(&scratch);
+
+ /* t == -1, 1/10 <= x < 1 -> floor(log10(abs(x-1)/10))
+ * t == 0, 1 < x < 10 -> floor(log10(abs(x-1)/100)) */
+ return (t == 0) ? u-2 : u-1;
+ }
+}
+
+/*
+ * Detect cases of certain overflow/underflow in the power function.
+ * Assumptions: x != 1, y != 0. The proof above is for positive x.
+ * If x is negative and y is an odd integer, x**y == -(abs(x)**y),
+ * so the analysis does not change.
+ */
+static int
+_qcheck_pow_bounds(mpd_t *result, const mpd_t *x, const mpd_t *y,
+ uint8_t resultsign,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ MPD_NEW_SHARED(abs_x, x);
+ mpd_ssize_t ub_omega, lb_zeta, lb_theta;
+ uint8_t sign;
+
+ mpd_set_positive(&abs_x);
+
+ lb_theta = mpd_adjexp(y);
+ lb_zeta = _lower_bound_zeta(&abs_x, status);
+ if (lb_zeta == MPD_SSIZE_MAX) {
+ mpd_seterror(result, MPD_Malloc_error, status);
+ return 1;
+ }
+
+ sign = (mpd_adjexp(&abs_x) < 0) ^ mpd_sign(y);
+ if (sign == 0) {
+ /* (0 < |x| < 1 and y < 0) or (|x| > 1 and y > 0) */
+ ub_omega = mpd_exp_digits(ctx->emax);
+ if (ub_omega < lb_zeta + lb_theta) {
+ _settriple(result, resultsign, 1, MPD_EXP_INF);
+ mpd_qfinalize(result, ctx, status);
+ return 1;
+ }
+ }
+ else {
+ /* (0 < |x| < 1 and y > 0) or (|x| > 1 and y < 0). */
+ ub_omega = mpd_exp_digits(mpd_etiny(ctx));
+ if (ub_omega < lb_zeta + lb_theta) {
+ _settriple(result, resultsign, 1, mpd_etiny(ctx)-1);
+ mpd_qfinalize(result, ctx, status);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * TODO: Implement algorithm for computing exact powers from decimal.py.
+ * In order to prevent infinite loops, this has to be called before
+ * using Ziv's strategy for correct rounding.
+ */
+/*
+static int
+_mpd_qpow_exact(mpd_t *result, const mpd_t *base, const mpd_t *exp,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ return 0;
+}
+*/
+
+/* The power function for real exponents */
+static void
+_mpd_qpow_real(mpd_t *result, const mpd_t *base, const mpd_t *exp,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_context_t workctx;
+ MPD_NEW_STATIC(texp,0,0,0,0);
+
+ if (!mpd_qcopy(&texp, exp, status)) {
+ mpd_seterror(result, MPD_Malloc_error, status);
+ return;
+ }
+
+ mpd_maxcontext(&workctx);
+ workctx.prec = (base->digits > ctx->prec) ? base->digits : ctx->prec;
+ workctx.prec += (4 + MPD_EXPDIGITS);
+ workctx.round = MPD_ROUND_HALF_EVEN;
+ workctx.allcr = ctx->allcr;
+
+ mpd_qln(result, base, &workctx, &workctx.status);
+ mpd_qmul(result, result, &texp, &workctx, &workctx.status);
+ mpd_qexp(result, result, &workctx, status);
+
+ mpd_del(&texp);
+ *status |= (workctx.status&MPD_Errors);
+ *status |= (MPD_Inexact|MPD_Rounded);
+}
+
+/* The power function: base**exp */
+void
+mpd_qpow(mpd_t *result, const mpd_t *base, const mpd_t *exp,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ uint8_t resultsign = 0;
+ int intexp = 0;
+ int cmp;
+
+ if (mpd_isspecial(base) || mpd_isspecial(exp)) {
+ if (mpd_qcheck_nans(result, base, exp, ctx, status)) {
+ return;
+ }
+ }
+ if (mpd_isinteger(exp)) {
+ intexp = 1;
+ resultsign = mpd_isnegative(base) && mpd_isodd(exp);
+ }
+
+ if (mpd_iszero(base)) {
+ if (mpd_iszero(exp)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ }
+ else if (mpd_isnegative(exp)) {
+ mpd_setspecial(result, resultsign, MPD_INF);
+ }
+ else {
+ _settriple(result, resultsign, 0, 0);
+ }
+ return;
+ }
+ if (mpd_isnegative(base)) {
+ if (!intexp || mpd_isinfinite(exp)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ }
+ if (mpd_isinfinite(exp)) {
+ /* power of one */
+ cmp = _qcheck_pow_one_inf(result, base, resultsign, ctx, status);
+ if (cmp == 0) {
+ return;
+ }
+ else {
+ cmp *= mpd_arith_sign(exp);
+ if (cmp < 0) {
+ _settriple(result, resultsign, 0, 0);
+ }
+ else {
+ mpd_setspecial(result, resultsign, MPD_INF);
+ }
+ }
+ return;
+ }
+ if (mpd_isinfinite(base)) {
+ if (mpd_iszero(exp)) {
+ _settriple(result, resultsign, 1, 0);
+ }
+ else if (mpd_isnegative(exp)) {
+ _settriple(result, resultsign, 0, 0);
+ }
+ else {
+ mpd_setspecial(result, resultsign, MPD_INF);
+ }
+ return;
+ }
+ if (mpd_iszero(exp)) {
+ _settriple(result, resultsign, 1, 0);
+ return;
+ }
+ if (_qcheck_pow_one(result, base, exp, resultsign, ctx, status) == 0) {
+ return;
+ }
+ if (_qcheck_pow_bounds(result, base, exp, resultsign, ctx, status)) {
+ return;
+ }
+
+ if (intexp) {
+ _mpd_qpow_int(result, base, exp, resultsign, ctx, status);
+ }
+ else {
+ _mpd_qpow_real(result, base, exp, ctx, status);
+ if (!mpd_isspecial(result) && _mpd_cmp(result, &one) == 0) {
+ mpd_ssize_t shift = ctx->prec-1;
+ mpd_qshiftl(result, &one, shift, status);
+ result->exp = -shift;
+ }
+ if (mpd_isinfinite(result)) {
+ /* for ROUND_DOWN, ROUND_FLOOR, etc. */
+ _settriple(result, MPD_POS, 1, MPD_EXP_INF);
+ }
+ mpd_qfinalize(result, ctx, status);
+ }
+}
+
+/*
+ * Internal function: Integer powmod with mpd_uint_t exponent, base is modified!
+ * Function can fail with MPD_Malloc_error.
+ */
+static inline void
+_mpd_qpowmod_uint(mpd_t *result, mpd_t *base, mpd_uint_t exp,
+ mpd_t *mod, uint32_t *status)
+{
+ mpd_context_t maxcontext;
+
+ mpd_maxcontext(&maxcontext);
+
+ /* resize to smaller cannot fail */
+ mpd_qcopy(result, &one, status);
+
+ while (exp > 0) {
+ if (exp & 1) {
+ mpd_qmul(result, result, base, &maxcontext, status);
+ mpd_qrem(result, result, mod, &maxcontext, status);
+ }
+ mpd_qmul(base, base, base, &maxcontext, status);
+ mpd_qrem(base, base, mod, &maxcontext, status);
+ exp >>= 1;
+ }
+}
+
+/* The powmod function: (base**exp) % mod */
+void
+mpd_qpowmod(mpd_t *result, const mpd_t *base, const mpd_t *exp,
+ const mpd_t *mod,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_context_t maxcontext;
+ MPD_NEW_STATIC(tbase,0,0,0,0);
+ MPD_NEW_STATIC(texp,0,0,0,0);
+ MPD_NEW_STATIC(tmod,0,0,0,0);
+ MPD_NEW_STATIC(tmp,0,0,0,0);
+ MPD_NEW_CONST(two,0,0,1,1,1,2);
+ mpd_ssize_t tbase_exp, texp_exp;
+ mpd_ssize_t i;
+ mpd_t t;
+ mpd_uint_t r;
+ uint8_t sign;
+
+
+ if (mpd_isspecial(base) || mpd_isspecial(exp) || mpd_isspecial(mod)) {
+ if (mpd_qcheck_3nans(result, base, exp, mod, ctx, status)) {
+ return;
+ }
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+
+
+ if (!_mpd_isint(base) || !_mpd_isint(exp) || !_mpd_isint(mod)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ if (mpd_iszerocoeff(mod)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ if (mod->digits+mod->exp > ctx->prec) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+
+ sign = (mpd_isnegative(base)) && (mpd_isodd(exp));
+ if (mpd_iszerocoeff(exp)) {
+ if (mpd_iszerocoeff(base)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ r = (_mpd_cmp_abs(mod, &one)==0) ? 0 : 1;
+ _settriple(result, sign, r, 0);
+ return;
+ }
+ if (mpd_isnegative(exp)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ if (mpd_iszerocoeff(base)) {
+ _settriple(result, sign, 0, 0);
+ return;
+ }
+
+ if (!mpd_qcopy(&tmod, mod, status)) {
+ goto mpd_errors;
+ }
+ mpd_set_positive(&tmod);
+
+ mpd_maxcontext(&maxcontext);
+
+ mpd_qround_to_int(&tbase, base, &maxcontext, status);
+ mpd_qround_to_int(&texp, exp, &maxcontext, status);
+ mpd_qround_to_int(&tmod, &tmod, &maxcontext, status);
+
+ tbase_exp = tbase.exp;
+ tbase.exp = 0;
+ texp_exp = texp.exp;
+ texp.exp = 0;
+
+ /* base = (base.int % modulo * pow(10, base.exp, modulo)) % modulo */
+ mpd_qrem(&tbase, &tbase, &tmod, &maxcontext, status);
+ _settriple(result, MPD_POS, 1, tbase_exp);
+ mpd_qrem(result, result, &tmod, &maxcontext, status);
+ mpd_qmul(&tbase, &tbase, result, &maxcontext, status);
+ mpd_qrem(&tbase, &tbase, &tmod, &maxcontext, status);
+ if (mpd_isspecial(&tbase) ||
+ mpd_isspecial(&texp) ||
+ mpd_isspecial(&tmod)) {
+ goto mpd_errors;
+ }
+
+ for (i = 0; i < texp_exp; i++) {
+ _mpd_qpowmod_uint(&tmp, &tbase, 10, &tmod, status);
+ t = tmp;
+ tmp = tbase;
+ tbase = t;
+ }
+ if (mpd_isspecial(&tbase)) {
+ goto mpd_errors; /* GCOV_UNLIKELY */
+ }
+
+ /* resize to smaller cannot fail */
+ mpd_qcopy(result, &one, status);
+ while (mpd_isfinite(&texp) && !mpd_iszero(&texp)) {
+ if (mpd_isodd(&texp)) {
+ mpd_qmul(result, result, &tbase, &maxcontext, status);
+ mpd_qrem(result, result, &tmod, &maxcontext, status);
+ }
+ mpd_qmul(&tbase, &tbase, &tbase, &maxcontext, status);
+ mpd_qrem(&tbase, &tbase, &tmod, &maxcontext, status);
+ mpd_qdivint(&texp, &texp, &two, &maxcontext, status);
+ }
+ if (mpd_isspecial(&texp) || mpd_isspecial(&tbase) ||
+ mpd_isspecial(&tmod) || mpd_isspecial(result)) {
+ /* MPD_Malloc_error */
+ goto mpd_errors;
+ }
+ else {
+ mpd_set_sign(result, sign);
+ }
+
+out:
+ mpd_del(&tbase);
+ mpd_del(&texp);
+ mpd_del(&tmod);
+ mpd_del(&tmp);
+ mpd_qfinalize(result, ctx, status);
+ return;
+
+mpd_errors:
+ mpd_setspecial(result, MPD_POS, MPD_NAN);
+ goto out;
+}
+
+void
+mpd_qquantize(mpd_t *result, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ uint32_t workstatus = 0;
+ mpd_ssize_t b_exp = b->exp;
+ mpd_ssize_t expdiff, shift;
+ mpd_uint_t rnd;
+
+ if (mpd_isspecial(a) || mpd_isspecial(b)) {
+ if (mpd_qcheck_nans(result, a, b, ctx, status)) {
+ return;
+ }
+ if (mpd_isinfinite(a) && mpd_isinfinite(b)) {
+ mpd_qcopy(result, a, status);
+ return;
+ }
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+
+ if (b->exp > ctx->emax || b->exp < mpd_etiny(ctx)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+
+ if (mpd_iszero(a)) {
+ _settriple(result, mpd_sign(a), 0, b->exp);
+ mpd_qfinalize(result, ctx, status);
+ return;
+ }
+
+
+ expdiff = a->exp - b->exp;
+ if (a->digits + expdiff > ctx->prec) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+
+ if (expdiff >= 0) {
+ shift = expdiff;
+ if (!mpd_qshiftl(result, a, shift, status)) {
+ return;
+ }
+ result->exp = b_exp;
+ }
+ else {
+ /* At this point expdiff < 0 and a->digits+expdiff <= prec,
+ * so the shift before an increment will fit in prec. */
+ shift = -expdiff;
+ rnd = mpd_qshiftr(result, a, shift, status);
+ if (rnd == MPD_UINT_MAX) {
+ return;
+ }
+ result->exp = b_exp;
+ if (!_mpd_apply_round_fit(result, rnd, ctx, status)) {
+ return;
+ }
+ workstatus |= MPD_Rounded;
+ if (rnd) {
+ workstatus |= MPD_Inexact;
+ }
+ }
+
+ if (mpd_adjexp(result) > ctx->emax ||
+ mpd_adjexp(result) < mpd_etiny(ctx)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+
+ *status |= workstatus;
+ mpd_qfinalize(result, ctx, status);
+}
+
+void
+mpd_qreduce(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ mpd_ssize_t shift, maxexp, maxshift;
+ uint8_t sign_a = mpd_sign(a);
+
+ if (mpd_isspecial(a)) {
+ if (mpd_qcheck_nan(result, a, ctx, status)) {
+ return;
+ }
+ mpd_qcopy(result, a, status);
+ return;
+ }
+
+ if (!mpd_qcopy(result, a, status)) {
+ return;
+ }
+ mpd_qfinalize(result, ctx, status);
+ if (mpd_isspecial(result)) {
+ return;
+ }
+ if (mpd_iszero(result)) {
+ _settriple(result, sign_a, 0, 0);
+ return;
+ }
+
+ shift = mpd_trail_zeros(result);
+ maxexp = (ctx->clamp) ? mpd_etop(ctx) : ctx->emax;
+ /* After the finalizing above result->exp <= maxexp. */
+ maxshift = maxexp - result->exp;
+ shift = (shift > maxshift) ? maxshift : shift;
+
+ mpd_qshiftr_inplace(result, shift);
+ result->exp += shift;
+}
+
+void
+mpd_qrem(mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ MPD_NEW_STATIC(q,0,0,0,0);
+
+ if (mpd_isspecial(a) || mpd_isspecial(b)) {
+ if (mpd_qcheck_nans(r, a, b, ctx, status)) {
+ return;
+ }
+ if (mpd_isinfinite(a)) {
+ mpd_seterror(r, MPD_Invalid_operation, status);
+ return;
+ }
+ if (mpd_isinfinite(b)) {
+ mpd_qcopy(r, a, status);
+ mpd_qfinalize(r, ctx, status);
+ return;
+ }
+ /* debug */
+ abort(); /* GCOV_NOT_REACHED */
+ }
+ if (mpd_iszerocoeff(b)) {
+ if (mpd_iszerocoeff(a)) {
+ mpd_seterror(r, MPD_Division_undefined, status);
+ }
+ else {
+ mpd_seterror(r, MPD_Invalid_operation, status);
+ }
+ return;
+ }
+
+ _mpd_qdivmod(&q, r, a, b, ctx, status);
+ mpd_del(&q);
+ mpd_qfinalize(r, ctx, status);
+}
+
+void
+mpd_qrem_near(mpd_t *r, const mpd_t *a, const mpd_t *b,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_context_t workctx;
+ MPD_NEW_STATIC(btmp,0,0,0,0);
+ MPD_NEW_STATIC(q,0,0,0,0);
+ mpd_ssize_t expdiff, floordigits;
+ int cmp, isodd, allnine;
+
+ if (mpd_isspecial(a) || mpd_isspecial(b)) {
+ if (mpd_qcheck_nans(r, a, b, ctx, status)) {
+ return;
+ }
+ if (mpd_isinfinite(a)) {
+ mpd_seterror(r, MPD_Invalid_operation, status);
+ return;
+ }
+ if (mpd_isinfinite(b)) {
+ mpd_qcopy(r, a, status);
+ mpd_qfinalize(r, ctx, status);
+ return;
+ }
+ /* debug */
+ abort(); /* GCOV_NOT_REACHED */
+ }
+ if (mpd_iszerocoeff(b)) {
+ if (mpd_iszerocoeff(a)) {
+ mpd_seterror(r, MPD_Division_undefined, status);
+ }
+ else {
+ mpd_seterror(r, MPD_Invalid_operation, status);
+ }
+ return;
+ }
+
+ if (r == b) {
+ if (!mpd_qcopy(&btmp, b, status)) {
+ mpd_seterror(r, MPD_Malloc_error, status);
+ return;
+ }
+ b = &btmp;
+ }
+
+ workctx = *ctx;
+ workctx.prec = a->digits;
+ workctx.prec = (workctx.prec > ctx->prec) ? workctx.prec : ctx->prec;
+
+ _mpd_qdivmod(&q, r, a, b, &workctx, status);
+ if (mpd_isnan(&q) || mpd_isnan(r) || q.digits > ctx->prec) {
+ mpd_seterror(r, MPD_Division_impossible, status);
+ goto finish;
+ }
+ if (mpd_iszerocoeff(r)) {
+ goto finish;
+ }
+
+ /* Deal with cases like rmnx078:
+ * remaindernear 999999999.5 1 -> NaN Division_impossible */
+ expdiff = mpd_adjexp(b) - mpd_adjexp(r);
+ if (-1 <= expdiff && expdiff <= 1) {
+
+ mpd_qtrunc(&q, &q, &workctx, &workctx.status);
+ allnine = mpd_coeff_isallnine(&q);
+ floordigits = q.digits;
+ isodd = mpd_isodd(&q);
+
+ mpd_maxcontext(&workctx);
+ if (mpd_sign(a) == mpd_sign(b)) {
+ _mpd_qsub(&q, r, b, &workctx, &workctx.status);
+ if (workctx.status&MPD_Errors) {
+ mpd_seterror(r, workctx.status&MPD_Errors, status);
+ goto finish;
+ }
+ }
+ else {
+ _mpd_qadd(&q, r, b, &workctx, &workctx.status);
+ if (workctx.status&MPD_Errors) {
+ mpd_seterror(r, workctx.status&MPD_Errors, status);
+ goto finish;
+ }
+ }
+
+ cmp = mpd_cmp_total_mag(&q, r);
+ if (cmp < 0 || (cmp == 0 && isodd)) {
+ if (allnine && floordigits == ctx->prec) {
+ mpd_seterror(r, MPD_Division_impossible, status);
+ goto finish;
+ }
+ mpd_qcopy(r, &q, status);
+ *status &= ~MPD_Rounded;
+ }
+ }
+
+
+finish:
+ mpd_del(&btmp);
+ mpd_del(&q);
+ mpd_qfinalize(r, ctx, status);
+}
+
+static void
+_mpd_qrescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_ssize_t expdiff, shift;
+ mpd_uint_t rnd;
+
+ if (mpd_isspecial(a)) {
+ mpd_qcopy(result, a, status);
+ return;
+ }
+
+ if (mpd_iszero(a)) {
+ _settriple(result, mpd_sign(a), 0, exp);
+ return;
+ }
+
+ expdiff = a->exp - exp;
+ if (expdiff >= 0) {
+ shift = expdiff;
+ if (a->digits + shift > MPD_MAX_PREC+1) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ if (!mpd_qshiftl(result, a, shift, status)) {
+ return;
+ }
+ result->exp = exp;
+ }
+ else {
+ shift = -expdiff;
+ rnd = mpd_qshiftr(result, a, shift, status);
+ if (rnd == MPD_UINT_MAX) {
+ return;
+ }
+ result->exp = exp;
+ _mpd_apply_round_excess(result, rnd, ctx, status);
+ *status |= MPD_Rounded;
+ if (rnd) {
+ *status |= MPD_Inexact;
+ }
+ }
+
+ if (mpd_issubnormal(result, ctx)) {
+ *status |= MPD_Subnormal;
+ }
+}
+
+/*
+ * Rescale a number so that it has exponent 'exp'. Does not regard context
+ * precision, emax, emin, but uses the rounding mode. Special numbers are
+ * quietly copied. Restrictions:
+ *
+ * MPD_MIN_ETINY <= exp <= MPD_MAX_EMAX+1
+ * result->digits <= MPD_MAX_PREC+1
+ */
+void
+mpd_qrescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ if (exp > MPD_MAX_EMAX+1 || exp < MPD_MIN_ETINY) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+
+ _mpd_qrescale(result, a, exp, ctx, status);
+}
+
+/*
+ * Same as mpd_qrescale, but with relaxed restrictions. The result of this
+ * function should only be used for formatting a number and never as input
+ * for other operations.
+ *
+ * MPD_MIN_ETINY-MPD_MAX_PREC <= exp <= MPD_MAX_EMAX+1
+ * result->digits <= MPD_MAX_PREC+1
+ */
+void
+mpd_qrescale_fmt(mpd_t *result, const mpd_t *a, mpd_ssize_t exp,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ if (exp > MPD_MAX_EMAX+1 || exp < MPD_MIN_ETINY-MPD_MAX_PREC) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+
+ _mpd_qrescale(result, a, exp, ctx, status);
+}
+
+/* Round to an integer according to 'action' and ctx->round. */
+enum {TO_INT_EXACT, TO_INT_SILENT, TO_INT_TRUNC};
+static void
+_mpd_qround_to_integral(int action, mpd_t *result, const mpd_t *a,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_uint_t rnd;
+
+ if (mpd_isspecial(a)) {
+ if (mpd_qcheck_nan(result, a, ctx, status)) {
+ return;
+ }
+ mpd_qcopy(result, a, status);
+ return;
+ }
+ if (a->exp >= 0) {
+ mpd_qcopy(result, a, status);
+ return;
+ }
+ if (mpd_iszerocoeff(a)) {
+ _settriple(result, mpd_sign(a), 0, 0);
+ return;
+ }
+
+ rnd = mpd_qshiftr(result, a, -a->exp, status);
+ if (rnd == MPD_UINT_MAX) {
+ return;
+ }
+ result->exp = 0;
+
+ if (action == TO_INT_EXACT || action == TO_INT_SILENT) {
+ _mpd_apply_round_excess(result, rnd, ctx, status);
+ if (action == TO_INT_EXACT) {
+ *status |= MPD_Rounded;
+ if (rnd) {
+ *status |= MPD_Inexact;
+ }
+ }
+ }
+}
+
+void
+mpd_qround_to_intx(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ (void)_mpd_qround_to_integral(TO_INT_EXACT, result, a, ctx, status);
+}
+
+void
+mpd_qround_to_int(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ (void)_mpd_qround_to_integral(TO_INT_SILENT, result, a, ctx, status);
+}
+
+void
+mpd_qtrunc(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ (void)_mpd_qround_to_integral(TO_INT_TRUNC, result, a, ctx, status);
+}
+
+void
+mpd_qfloor(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ mpd_context_t workctx = *ctx;
+ workctx.round = MPD_ROUND_FLOOR;
+ (void)_mpd_qround_to_integral(TO_INT_SILENT, result, a,
+ &workctx, status);
+}
+
+void
+mpd_qceil(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ mpd_context_t workctx = *ctx;
+ workctx.round = MPD_ROUND_CEILING;
+ (void)_mpd_qround_to_integral(TO_INT_SILENT, result, a,
+ &workctx, status);
+}
+
+int
+mpd_same_quantum(const mpd_t *a, const mpd_t *b)
+{
+ if (mpd_isspecial(a) || mpd_isspecial(b)) {
+ return ((mpd_isnan(a) && mpd_isnan(b)) ||
+ (mpd_isinfinite(a) && mpd_isinfinite(b)));
+ }
+
+ return a->exp == b->exp;
+}
+
+/* Schedule the increase in precision for the Newton iteration. */
+static inline int
+recpr_schedule_prec(mpd_ssize_t klist[MPD_MAX_PREC_LOG2],
+ mpd_ssize_t maxprec, mpd_ssize_t initprec)
+{
+ mpd_ssize_t k;
+ int i;
+
+ assert(maxprec > 0 && initprec > 0);
+ if (maxprec <= initprec) return -1;
+
+ i = 0; k = maxprec;
+ do {
+ k = (k+1) / 2;
+ klist[i++] = k;
+ } while (k > initprec);
+
+ return i-1;
+}
+
+/*
+ * Initial approximation for the reciprocal. Result has MPD_RDIGITS-2
+ * significant digits.
+ */
+static void
+_mpd_qreciprocal_approx(mpd_t *z, const mpd_t *v, uint32_t *status)
+{
+ mpd_uint_t p10data[2] = {0, mpd_pow10[MPD_RDIGITS-2]}; /* 10**(2*MPD_RDIGITS-2) */
+ mpd_uint_t dummy, word;
+ int n;
+
+ _mpd_get_msdigits(&dummy, &word, v, MPD_RDIGITS);
+ n = mpd_word_digits(word);
+ word *= mpd_pow10[MPD_RDIGITS-n];
+
+ mpd_qresize(z, 2, status);
+ (void)_mpd_shortdiv(z->data, p10data, 2, word);
+
+ mpd_clear_flags(z);
+ z->exp = -(v->exp + v->digits) - (MPD_RDIGITS-2);
+ z->len = (z->data[1] == 0) ? 1 : 2;
+ mpd_setdigits(z);
+}
+
+/* Reciprocal, calculated with Newton's Method. Assumption: result != a. */
+static void
+_mpd_qreciprocal(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ mpd_context_t varcontext, maxcontext;
+ mpd_t *z = result; /* current approximation */
+ mpd_t *v; /* a, normalized to a number between 0.1 and 1 */
+ MPD_NEW_SHARED(vtmp, a); /* by default v will share data with a */
+ MPD_NEW_STATIC(s,0,0,0,0); /* temporary variable */
+ MPD_NEW_STATIC(t,0,0,0,0); /* temporary variable */
+ MPD_NEW_CONST(two,0,0,1,1,1,2); /* const 2 */
+ mpd_ssize_t klist[MPD_MAX_PREC_LOG2];
+ mpd_ssize_t adj, maxprec, initprec;
+ uint8_t sign = mpd_sign(a);
+ int i;
+
+ v = &vtmp;
+ assert(result != a);
+
+ mpd_clear_flags(v);
+ adj = v->digits + v->exp;
+ v->exp = -v->digits;
+
+ /* initial approximation */
+ _mpd_qreciprocal_approx(z, v, status);
+
+ mpd_maxcontext(&varcontext);
+ mpd_maxcontext(&maxcontext);
+ varcontext.round = MPD_ROUND_TRUNC;
+ maxcontext.round = MPD_ROUND_TRUNC;
+
+ maxprec = (v->digits > ctx->prec) ? v->digits : ctx->prec;
+ maxprec += 2;
+ initprec = MPD_RDIGITS-3;
+
+ i = recpr_schedule_prec(klist, maxprec, initprec);
+ for (; i >= 0; i--) {
+ mpd_qmul(&s, z, z, &maxcontext, status);
+ varcontext.prec = 2*klist[i] + 5;
+ if (v->digits > varcontext.prec) {
+ mpd_qshiftr(&t, v, v->digits-varcontext.prec, status);
+ t.exp = -varcontext.prec;
+ mpd_qmul(&t, &t, &s, &varcontext, status);
+ }
+ else {
+ mpd_qmul(&t, v, &s, &varcontext, status);
+ }
+ mpd_qmul(&s, z, &two, &maxcontext, status);
+ mpd_qsub(z, &s, &t, &maxcontext, status);
+ }
+
+ if (!mpd_isspecial(z)) {
+ z->exp -= adj;
+ mpd_set_flags(z, sign);
+ }
+
+ mpd_del(&s);
+ mpd_del(&t);
+ mpd_qfinalize(z, ctx, status);
+}
+
+/*
+ * Integer division with remainder of the coefficients: coeff(a) / coeff(b).
+ * This function is for large numbers where it is faster to divide by
+ * multiplying the dividend by the reciprocal of the divisor.
+ * The inexact result is fixed by a small loop, which should not take
+ * more than 2 iterations.
+ */
+static void
+_mpd_qbarrett_divmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b,
+ uint32_t *status)
+{
+ mpd_context_t workctx;
+ mpd_t *qq = q, *rr = r;
+ mpd_t aa, bb;
+ int k;
+
+ mpd_maxcontext(&workctx);
+ _mpd_copy_shared(&aa, a);
+ _mpd_copy_shared(&bb, b);
+
+ mpd_set_positive(&aa);
+ mpd_set_positive(&bb);
+ aa.exp = 0;
+ bb.exp = 0;
+
+ if (q == a || q == b) {
+ if ((qq = mpd_qnew()) == NULL) {
+ *status |= MPD_Malloc_error;
+ goto nanresult;
+ }
+ }
+ if (r == a || r == b) {
+ if ((rr = mpd_qnew()) == NULL) {
+ *status |= MPD_Malloc_error;
+ goto nanresult;
+ }
+ }
+
+ /* maximum length of q + 3 digits */
+ workctx.prec = aa.digits - bb.digits + 1 + 3;
+ /* we get the reciprocal with precision maxlen(q) + 3 */
+ _mpd_qreciprocal(rr, &bb, &workctx, &workctx.status);
+
+ mpd_qmul(qq, &aa, rr, &workctx, &workctx.status);
+ mpd_qtrunc(qq, qq, &workctx, &workctx.status);
+
+ workctx.prec = aa.digits + 3;
+ /* get the remainder */
+ mpd_qmul(rr, &bb, qq, &workctx, &workctx.status);
+ mpd_qsub(rr, &aa, rr, &workctx, &workctx.status);
+
+ /* Fix the result. Algorithm from: Karl Hasselstrom, Fast Division of Large Integers */
+ for (k = 0;; k++) {
+ if (mpd_isspecial(rr)) {
+ *status |= (workctx.status&MPD_Errors);
+ goto nanresult;
+ }
+ if (k > 2) {
+ mpd_err_warn("libmpdec: internal error in " /* GCOV_NOT_REACHED */
+ "_mpd_qbarrett_divmod: please report"); /* GCOV_NOT_REACHED */
+ *status |= MPD_Invalid_operation; /* GCOV_NOT_REACHED */
+ goto nanresult; /* GCOV_NOT_REACHED */
+ }
+ else if (_mpd_cmp(&zero, rr) == 1) {
+ mpd_qadd(rr, rr, &bb, &workctx, &workctx.status);
+ mpd_qadd(qq, qq, &minus_one, &workctx, &workctx.status);
+ }
+ else if (_mpd_cmp(rr, &bb) == -1) {
+ break;
+ }
+ else {
+ mpd_qsub(rr, rr, &bb, &workctx, &workctx.status);
+ mpd_qadd(qq, qq, &one, &workctx, &workctx.status);
+ }
+ }
+
+ if (qq != q) {
+ if (!mpd_qcopy(q, qq, status)) {
+ goto nanresult; /* GCOV_UNLIKELY */
+ }
+ mpd_del(qq);
+ }
+ if (rr != r) {
+ if (!mpd_qcopy(r, rr, status)) {
+ goto nanresult; /* GCOV_UNLIKELY */
+ }
+ mpd_del(rr);
+ }
+
+ *status |= (workctx.status&MPD_Errors);
+ return;
+
+
+nanresult:
+ if (qq && qq != q) mpd_del(qq);
+ if (rr && rr != r) mpd_del(rr);
+ mpd_setspecial(q, MPD_POS, MPD_NAN);
+ mpd_setspecial(r, MPD_POS, MPD_NAN);
+}
+
+static inline int
+invroot_schedule_prec(mpd_ssize_t klist[MPD_MAX_PREC_LOG2],
+ mpd_ssize_t maxprec, mpd_ssize_t initprec)
+{
+ mpd_ssize_t k;
+ int i;
+
+ assert(maxprec >= 3 && initprec >= 3);
+ if (maxprec <= initprec) return -1;
+
+ i = 0; k = maxprec;
+ do {
+ k = (k+3) / 2;
+ klist[i++] = k;
+ } while (k > initprec);
+
+ return i-1;
+}
+
+/*
+ * Initial approximation for the inverse square root.
+ *
+ * Input:
+ * v := 7 or 8 decimal digits with an implicit exponent of 10**-6,
+ * representing a number 1 <= x < 100.
+ *
+ * Output:
+ * An approximation to 1/sqrt(v)
+ */
+static inline void
+_invroot_init_approx(mpd_t *z, mpd_uint_t v)
+{
+ mpd_uint_t lo = 1000;
+ mpd_uint_t hi = 10000;
+ mpd_uint_t a, sq;
+
+ assert(v >= lo*lo && v < (hi+1)*(hi+1));
+
+ for(;;) {
+ a = (lo + hi) / 2;
+ sq = a * a;
+ if (v >= sq) {
+ if (v < sq + 2*a + 1) {
+ break;
+ }
+ lo = a + 1;
+ }
+ else {
+ hi = a - 1;
+ }
+ }
+
+ /* At this point a/1000 is an approximation to sqrt(v). */
+ mpd_minalloc(z);
+ mpd_clear_flags(z);
+ z->data[0] = 1000000000UL / a;
+ z->len = 1;
+ z->exp = -6;
+ mpd_setdigits(z);
+}
+
+static void
+_mpd_qinvroot(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ uint32_t workstatus = 0;
+ mpd_context_t varcontext, maxcontext;
+ mpd_t *z = result; /* current approximation */
+ mpd_t *v; /* a, normalized to a number between 1 and 100 */
+ MPD_NEW_SHARED(vtmp, a); /* by default v will share data with a */
+ MPD_NEW_STATIC(s,0,0,0,0); /* temporary variable */
+ MPD_NEW_STATIC(t,0,0,0,0); /* temporary variable */
+ MPD_NEW_CONST(one_half,0,-1,1,1,1,5);
+ MPD_NEW_CONST(three,0,0,1,1,1,3);
+ mpd_ssize_t klist[MPD_MAX_PREC_LOG2];
+ mpd_ssize_t ideal_exp, shift;
+ mpd_ssize_t adj, tz;
+ mpd_ssize_t maxprec, fracdigits;
+ mpd_uint_t x, dummy;
+ int i, n;
+
+
+ ideal_exp = -(a->exp - (a->exp & 1)) / 2;
+
+ v = &vtmp;
+ if (result == a) {
+ if ((v = mpd_qncopy(a)) == NULL) {
+ mpd_seterror(result, MPD_Malloc_error, status);
+ return;
+ }
+ }
+
+ /* normalize a to 1 <= v < 100 */
+ if ((v->digits+v->exp) & 1) {
+ fracdigits = v->digits - 1;
+ v->exp = -fracdigits;
+ n = (v->digits > 7) ? 7 : (int)v->digits;
+ _mpd_get_msdigits(&dummy, &x, v, n);
+ if (n < 7) {
+ x *= mpd_pow10[7-n];
+ }
+ }
+ else {
+ fracdigits = v->digits - 2;
+ v->exp = -fracdigits;
+ n = (v->digits > 8) ? 8 : (int)v->digits;
+ _mpd_get_msdigits(&dummy, &x, v, n);
+ if (n < 8) {
+ x *= mpd_pow10[8-n];
+ }
+ }
+ adj = (a->exp-v->exp) / 2;
+
+ /* initial approximation */
+ _invroot_init_approx(z, x);
+
+ mpd_maxcontext(&maxcontext);
+ mpd_maxcontext(&varcontext);
+ varcontext.round = MPD_ROUND_TRUNC;
+ maxprec = ctx->prec + 2;
+
+ i = invroot_schedule_prec(klist, maxprec, 3);
+ for (; i >= 0; i--) {
+ varcontext.prec = 2*klist[i]+2;
+ mpd_qmul(&s, z, z, &maxcontext, &workstatus);
+ if (v->digits > varcontext.prec) {
+ shift = v->digits - varcontext.prec;
+ mpd_qshiftr(&t, v, shift, &workstatus);
+ t.exp += shift;
+ mpd_qmul(&t, &t, &s, &varcontext, &workstatus);
+ }
+ else {
+ mpd_qmul(&t, v, &s, &varcontext, &workstatus);
+ }
+ mpd_qsub(&t, &three, &t, &maxcontext, &workstatus);
+ mpd_qmul(z, z, &t, &varcontext, &workstatus);
+ mpd_qmul(z, z, &one_half, &maxcontext, &workstatus);
+ }
+
+ z->exp -= adj;
+
+ tz = mpd_trail_zeros(result);
+ shift = ideal_exp - result->exp;
+ shift = (tz > shift) ? shift : tz;
+ if (shift > 0) {
+ mpd_qshiftr_inplace(result, shift);
+ result->exp += shift;
+ }
+
+
+ mpd_del(&s);
+ mpd_del(&t);
+ if (v != &vtmp) mpd_del(v);
+ *status |= (workstatus&MPD_Errors);
+ varcontext = *ctx;
+ varcontext.round = MPD_ROUND_HALF_EVEN;
+ mpd_qfinalize(result, &varcontext, status);
+}
+
+void
+mpd_qinvroot(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+
+ if (mpd_isspecial(a)) {
+ if (mpd_qcheck_nan(result, a, ctx, status)) {
+ return;
+ }
+ if (mpd_isnegative(a)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ /* positive infinity */
+ _settriple(result, MPD_POS, 0, mpd_etiny(ctx));
+ *status |= MPD_Clamped;
+ return;
+ }
+ if (mpd_iszero(a)) {
+ mpd_setspecial(result, mpd_sign(a), MPD_INF);
+ *status |= MPD_Division_by_zero;
+ return;
+ }
+ if (mpd_isnegative(a)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+
+ _mpd_qinvroot(result, a, ctx, status);
+}
+
+/*
+ * Ensure correct rounding. Algorithm after Hull & Abrham, "Properly Rounded
+ * Variable Precision Square Root", ACM Transactions on Mathematical Software,
+ * Vol. 11, No. 3.
+ */
+static void
+_mpd_fix_sqrt(mpd_t *result, const mpd_t *a, mpd_t *tmp,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_context_t maxctx;
+ MPD_NEW_CONST(u,0,0,1,1,1,5);
+
+ mpd_maxcontext(&maxctx);
+ u.exp = u.digits - ctx->prec + result->exp - 1;
+
+ _mpd_qsub(tmp, result, &u, &maxctx, status);
+ if (*status&MPD_Errors) goto nanresult;
+
+ _mpd_qmul(tmp, tmp, tmp, &maxctx, status);
+ if (*status&MPD_Errors) goto nanresult;
+
+ if (_mpd_cmp(tmp, a) == 1) {
+ u.exp += 1;
+ u.data[0] = 1;
+ _mpd_qsub(result, result, &u, &maxctx, status);
+ }
+ else {
+ _mpd_qadd(tmp, result, &u, &maxctx, status);
+ if (*status&MPD_Errors) goto nanresult;
+
+ _mpd_qmul(tmp, tmp, tmp, &maxctx, status);
+ if (*status&MPD_Errors) goto nanresult;
+
+ if (_mpd_cmp(tmp, a) == -1) {
+ u.exp += 1;
+ u.data[0] = 1;
+ _mpd_qadd(result, result, &u, &maxctx, status);
+ }
+ }
+
+ return;
+
+nanresult:
+ mpd_setspecial(result, MPD_POS, MPD_NAN);
+}
+
+void
+mpd_qsqrt(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx,
+ uint32_t *status)
+{
+ uint32_t workstatus = 0;
+ mpd_context_t varcontext;
+ mpd_t *z = result; /* current approximation */
+ MPD_NEW_STATIC(v,0,0,0,0); /* a, normalized to a number between 1 and 10 */
+ MPD_NEW_STATIC(vtmp,0,0,0,0);
+ MPD_NEW_STATIC(tmp,0,0,0,0);
+ mpd_ssize_t ideal_exp, shift;
+ mpd_ssize_t target_prec, fracdigits;
+ mpd_ssize_t a_exp, a_digits;
+ mpd_ssize_t adj, tz;
+ mpd_uint_t dummy, t;
+ int exact = 0;
+
+
+ varcontext = *ctx;
+ varcontext.round = MPD_ROUND_HALF_EVEN;
+ ideal_exp = (a->exp - (a->exp & 1)) / 2;
+
+ if (mpd_isspecial(a)) {
+ if (mpd_qcheck_nan(result, a, ctx, status)) {
+ return;
+ }
+ if (mpd_isnegative(a)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ mpd_setspecial(result, MPD_POS, MPD_INF);
+ return;
+ }
+ if (mpd_iszero(a)) {
+ _settriple(result, mpd_sign(a), 0, ideal_exp);
+ mpd_qfinalize(result, ctx, status);
+ return;
+ }
+ if (mpd_isnegative(a)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+
+ if (!mpd_qcopy(&v, a, status)) {
+ mpd_seterror(result, MPD_Malloc_error, status);
+ goto finish;
+ }
+
+ a_exp = a->exp;
+ a_digits = a->digits;
+
+ /* normalize a to 1 <= v < 100 */
+ if ((v.digits+v.exp) & 1) {
+ fracdigits = v.digits - 1;
+ v.exp = -fracdigits;
+ _mpd_get_msdigits(&dummy, &t, &v, 3);
+ t = t < 100 ? t*10 : t;
+ t = t < 100 ? t*10 : t;
+ }
+ else {
+ fracdigits = v.digits - 2;
+ v.exp = -fracdigits;
+ _mpd_get_msdigits(&dummy, &t, &v, 4);
+ t = t < 1000 ? t*10 : t;
+ t = t < 1000 ? t*10 : t;
+ t = t < 1000 ? t*10 : t;
+ }
+ adj = (a_exp-v.exp) / 2;
+
+
+ /* use excess digits */
+ target_prec = (a_digits > ctx->prec) ? a_digits : ctx->prec;
+ target_prec += 2;
+ varcontext.prec = target_prec + 3;
+
+ /* invroot is much faster for large numbers */
+ _mpd_qinvroot(&tmp, &v, &varcontext, &workstatus);
+
+ varcontext.prec = target_prec;
+ _mpd_qdiv(NO_IDEAL_EXP, z, &one, &tmp, &varcontext, &workstatus);
+
+
+ tz = mpd_trail_zeros(result);
+ if ((result->digits-tz)*2-1 <= v.digits) {
+ _mpd_qmul(&tmp, result, result, &varcontext, &workstatus);
+ if (workstatus&MPD_Errors) {
+ mpd_seterror(result, workstatus&MPD_Errors, status);
+ goto finish;
+ }
+ exact = (_mpd_cmp(&tmp, &v) == 0);
+ }
+ *status |= (workstatus&MPD_Errors);
+
+ if (!exact && !mpd_isspecial(result) && !mpd_iszero(result)) {
+ _mpd_fix_sqrt(result, &v, &tmp, &varcontext, status);
+ if (mpd_isspecial(result)) goto finish;
+ *status |= (MPD_Rounded|MPD_Inexact);
+ }
+
+ result->exp += adj;
+ if (exact) {
+ shift = ideal_exp - result->exp;
+ shift = (tz > shift) ? shift : tz;
+ if (shift > 0) {
+ mpd_qshiftr_inplace(result, shift);
+ result->exp += shift;
+ }
+ }
+
+
+finish:
+ mpd_del(&v);
+ mpd_del(&vtmp);
+ mpd_del(&tmp);
+ varcontext.prec = ctx->prec;
+ mpd_qfinalize(result, &varcontext, status);
+}
+
+
+/******************************************************************************/
+/* Base conversions */
+/******************************************************************************/
+
+/*
+ * Returns the space needed to represent an integer mpd_t in base 'base'.
+ * The result is undefined for non-integers.
+ *
+ * Max space needed:
+ *
+ * base^n >= 10^(digits+exp)
+ * n >= log10(10^(digits+exp))/log10(base) = (digits+exp) / log10(base)
+ */
+size_t
+mpd_sizeinbase(mpd_t *a, uint32_t base)
+{
+ size_t x;
+
+ assert(mpd_isinteger(a));
+ if (mpd_iszero(a)) {
+ return 1;
+ }
+
+ x = a->digits+a->exp;
+
+#ifdef CONFIG_64
+ #ifdef USE_80BIT_LONG_DOUBLE
+ return (long double)x / log10(base) + 3;
+ #else
+ /* x > floor(((1ULL<<53)-3) * log10(2)) */
+ if (x > 2711437152599294ULL) {
+ return SIZE_MAX;
+ }
+ return (double)x / log10(base) + 3;
+ #endif
+#else /* CONFIG_32 */
+{
+ double y = x / log10(base) + 3;
+ return (y > SIZE_MAX) ? SIZE_MAX : (size_t)y;
+}
+#endif
+}
+
+/*
+ * Returns the space needed to import a base 'base' integer of length 'srclen'.
+ */
+static inline mpd_ssize_t
+_mpd_importsize(size_t srclen, uint32_t base)
+{
+#if SIZE_MAX == UINT64_MAX
+ #ifdef USE_80BIT_LONG_DOUBLE
+ long double x = (long double)srclen * (log10(base)/MPD_RDIGITS) + 3;
+ #else
+ double x;
+ if (srclen > (1ULL<<53)) {
+ return MPD_SSIZE_MAX;
+ }
+ x = (double)srclen * (log10(base)/MPD_RDIGITS) + 3;
+ #endif
+#else
+ double x = srclen * (log10(base)/MPD_RDIGITS) + 3;
+#endif
+ return (x > MPD_MAXIMPORT) ? MPD_SSIZE_MAX : (mpd_ssize_t)x;
+}
+
+
+static inline size_t
+_to_base_u16(uint16_t *w, size_t wlen, mpd_uint_t wbase,
+ mpd_uint_t *u, mpd_ssize_t ulen)
+{
+ size_t n = 0;
+
+ assert(wlen > 0 && ulen > 0);
+
+ do {
+ w[n++] = (uint16_t)_mpd_shortdiv(u, u, ulen, wbase);
+ /* ulen will be at least 1. u[ulen-1] can only be zero if ulen == 1 */
+ ulen = _mpd_real_size(u, ulen);
+
+ } while (u[ulen-1] != 0 && n < wlen);
+
+ /* proper termination condition */
+ assert(u[ulen-1] == 0);
+
+ return n;
+}
+
+static inline void
+_from_base_u16(mpd_uint_t *w, mpd_ssize_t wlen,
+ const mpd_uint_t *u, size_t ulen, uint32_t ubase)
+{
+ mpd_ssize_t m = 1;
+ mpd_uint_t carry;
+
+ assert(wlen > 0 && ulen > 0);
+
+ w[0] = u[--ulen];
+ while (--ulen != SIZE_MAX && m < wlen) {
+ _mpd_shortmul(w, w, m, ubase);
+ m = _mpd_real_size(w, m+1);
+ carry = _mpd_shortadd(w, m, u[ulen]);
+ if (carry) w[m++] = carry;
+ }
+
+ /* proper termination condition */
+ assert(ulen == SIZE_MAX);
+}
+
+/* target base wbase <= source base ubase */
+static inline size_t
+_baseconv_to_smaller(uint32_t *w, size_t wlen, mpd_uint_t wbase,
+ mpd_uint_t *u, mpd_ssize_t ulen, mpd_uint_t ubase)
+{
+ size_t n = 0;
+
+ assert(wlen > 0 && ulen > 0);
+
+ do {
+ w[n++] = (uint32_t)_mpd_shortdiv_b(u, u, ulen, wbase, ubase);
+ /* ulen will be at least 1. u[ulen-1] can only be zero if ulen == 1 */
+ ulen = _mpd_real_size(u, ulen);
+
+ } while (u[ulen-1] != 0 && n < wlen);
+
+ /* proper termination condition */
+ assert(u[ulen-1] == 0);
+
+ return n;
+}
+
+/* target base wbase >= source base ubase */
+static inline void
+_baseconv_to_larger(mpd_uint_t *w, mpd_ssize_t wlen, mpd_uint_t wbase,
+ const mpd_uint_t *u, size_t ulen, mpd_uint_t ubase)
+{
+ mpd_ssize_t m = 1;
+ mpd_uint_t carry;
+
+ assert(wlen > 0 && ulen > 0);
+
+ w[0] = u[--ulen];
+ while (--ulen != SIZE_MAX && m < wlen) {
+ _mpd_shortmul_b(w, w, m, ubase, wbase);
+ m = _mpd_real_size(w, m+1);
+ carry = _mpd_shortadd_b(w, m, u[ulen], wbase);
+ if (carry) w[m++] = carry;
+ }
+
+ /* proper termination condition */
+ assert(ulen == SIZE_MAX);
+}
+
+
+/*
+ * Converts an integer mpd_t to a multiprecision integer with
+ * base <= UINT16_MAX+1. The least significant word of the result
+ * is rdata[0].
+ */
+size_t
+mpd_qexport_u16(uint16_t *rdata, size_t rlen, uint32_t rbase,
+ const mpd_t *src, uint32_t *status)
+{
+ mpd_t *tsrc;
+ size_t n;
+
+ assert(rbase <= (1U<<16));
+ assert(rlen <= SIZE_MAX/(sizeof *rdata));
+
+ if (mpd_isspecial(src) || !_mpd_isint(src)) {
+ *status |= MPD_Invalid_operation;
+ return SIZE_MAX;
+ }
+
+ memset(rdata, 0, rlen * (sizeof *rdata));
+
+ if (mpd_iszero(src)) {
+ return 1;
+ }
+
+ if ((tsrc = mpd_qnew()) == NULL) {
+ *status |= MPD_Malloc_error;
+ return SIZE_MAX;
+ }
+
+ if (src->exp >= 0) {
+ if (!mpd_qshiftl(tsrc, src, src->exp, status)) {
+ mpd_del(tsrc);
+ return SIZE_MAX;
+ }
+ }
+ else {
+ if (mpd_qshiftr(tsrc, src, -src->exp, status) == MPD_UINT_MAX) {
+ mpd_del(tsrc);
+ return SIZE_MAX;
+ }
+ }
+
+ n = _to_base_u16(rdata, rlen, rbase, tsrc->data, tsrc->len);
+
+ mpd_del(tsrc);
+ return n;
+}
+
+/*
+ * Converts an integer mpd_t to a multiprecision integer with
+ * base <= UINT32_MAX. The least significant word of the result
+ * is rdata[0].
+ */
+size_t
+mpd_qexport_u32(uint32_t *rdata, size_t rlen, uint32_t rbase,
+ const mpd_t *src, uint32_t *status)
+{
+ mpd_t *tsrc;
+ size_t n;
+
+ if (mpd_isspecial(src) || !_mpd_isint(src)) {
+ *status |= MPD_Invalid_operation;
+ return SIZE_MAX;
+ }
+#if MPD_SIZE_MAX < SIZE_MAX
+ if (rlen > MPD_SSIZE_MAX) {
+ *status |= MPD_Invalid_operation;
+ return SIZE_MAX;
+ }
+#endif
+
+ assert(rlen <= SIZE_MAX/(sizeof *rdata));
+ memset(rdata, 0, rlen * (sizeof *rdata));
+
+ if (mpd_iszero(src)) {
+ return 1;
+ }
+
+ if ((tsrc = mpd_qnew()) == NULL) {
+ *status |= MPD_Malloc_error;
+ return SIZE_MAX;
+ }
+
+ if (src->exp >= 0) {
+ if (!mpd_qshiftl(tsrc, src, src->exp, status)) {
+ mpd_del(tsrc);
+ return SIZE_MAX;
+ }
+ }
+ else {
+ if (mpd_qshiftr(tsrc, src, -src->exp, status) == MPD_UINT_MAX) {
+ mpd_del(tsrc);
+ return SIZE_MAX;
+ }
+ }
+
+#ifdef CONFIG_64
+ n = _baseconv_to_smaller(rdata, rlen, rbase,
+ tsrc->data, tsrc->len, MPD_RADIX);
+#else
+ if (rbase <= MPD_RADIX) {
+ n = _baseconv_to_smaller(rdata, rlen, rbase,
+ tsrc->data, tsrc->len, MPD_RADIX);
+ }
+ else {
+ _baseconv_to_larger(rdata, (mpd_ssize_t)rlen, rbase,
+ tsrc->data, tsrc->len, MPD_RADIX);
+ n = _mpd_real_size(rdata, (mpd_ssize_t)rlen);
+ }
+#endif
+
+ mpd_del(tsrc);
+ return n;
+}
+
+
+/*
+ * Converts a multiprecision integer with base <= UINT16_MAX+1 to an mpd_t.
+ * The least significant word of the source is srcdata[0].
+ */
+void
+mpd_qimport_u16(mpd_t *result,
+ const uint16_t *srcdata, size_t srclen,
+ uint8_t srcsign, uint32_t srcbase,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_uint_t *usrc; /* uint16_t src copied to an mpd_uint_t array */
+ mpd_ssize_t rlen; /* length of the result */
+ size_t n = 0;
+
+ assert(srclen > 0);
+ assert(srcbase <= (1U<<16));
+
+ if ((rlen = _mpd_importsize(srclen, srcbase)) == MPD_SSIZE_MAX) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ if (srclen > MPD_SIZE_MAX/(sizeof *usrc)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ if ((usrc = mpd_alloc((mpd_size_t)srclen, sizeof *usrc)) == NULL) {
+ mpd_seterror(result, MPD_Malloc_error, status);
+ return;
+ }
+ for (n = 0; n < srclen; n++) {
+ usrc[n] = srcdata[n];
+ }
+
+ /* result->data is initialized to zero */
+ if (!mpd_qresize_zero(result, rlen, status)) {
+ goto finish;
+ }
+
+ _from_base_u16(result->data, rlen, usrc, srclen, srcbase);
+
+ mpd_set_flags(result, srcsign);
+ result->exp = 0;
+ result->len = _mpd_real_size(result->data, rlen);
+ mpd_setdigits(result);
+
+ mpd_qresize(result, result->len, status);
+ mpd_qfinalize(result, ctx, status);
+
+
+finish:
+ mpd_free(usrc);
+}
+
+/*
+ * Converts a multiprecision integer with base <= UINT32_MAX to an mpd_t.
+ * The least significant word of the source is srcdata[0].
+ */
+void
+mpd_qimport_u32(mpd_t *result,
+ const uint32_t *srcdata, size_t srclen,
+ uint8_t srcsign, uint32_t srcbase,
+ const mpd_context_t *ctx, uint32_t *status)
+{
+ mpd_uint_t *usrc; /* uint32_t src copied to an mpd_uint_t array */
+ mpd_ssize_t rlen; /* length of the result */
+ size_t n = 0;
+
+ assert(srclen > 0);
+
+ if ((rlen = _mpd_importsize(srclen, srcbase)) == MPD_SSIZE_MAX) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ if (srclen > MPD_SIZE_MAX/(sizeof *usrc)) {
+ mpd_seterror(result, MPD_Invalid_operation, status);
+ return;
+ }
+ if ((usrc = mpd_alloc((mpd_size_t)srclen, sizeof *usrc)) == NULL) {
+ mpd_seterror(result, MPD_Malloc_error, status);
+ return;
+ }
+ for (n = 0; n < srclen; n++) {
+ usrc[n] = srcdata[n];
+ }
+
+ /* result->data is initialized to zero */
+ if (!mpd_qresize_zero(result, rlen, status)) {
+ goto finish;
+ }
+
+#ifdef CONFIG_64
+ _baseconv_to_larger(result->data, rlen, MPD_RADIX,
+ usrc, srclen, srcbase);
+#else
+ if (srcbase <= MPD_RADIX) {
+ _baseconv_to_larger(result->data, rlen, MPD_RADIX,
+ usrc, srclen, srcbase);
+ }
+ else {
+ _baseconv_to_smaller(result->data, rlen, MPD_RADIX,
+ usrc, (mpd_ssize_t)srclen, srcbase);
+ }
+#endif
+
+ mpd_set_flags(result, srcsign);
+ result->exp = 0;
+ result->len = _mpd_real_size(result->data, rlen);
+ mpd_setdigits(result);
+
+ mpd_qresize(result, result->len, status);
+ mpd_qfinalize(result, ctx, status);
+
+
+finish:
+ mpd_free(usrc);
+}
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef MPDECIMAL_H
+#define MPDECIMAL_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#define __STDC_LIMIT_MACROS
+#endif
+
+
+#ifndef _MSC_VER
+ #include "pyconfig.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <assert.h>
+
+#ifdef _MSC_VER
+ #include "vccompat.h"
+ #ifndef UNUSED
+ #define UNUSED
+ #endif
+ #define EXTINLINE extern inline
+#else
+ #ifdef HAVE_STDINT_H
+ #include <stdint.h>
+ #endif
+ #ifdef HAVE_INTTYPES_H
+ #include <inttypes.h>
+ #endif
+ #ifndef __GNUC_STDC_INLINE__
+ #define __GNUC_STDC_INLINE__
+ #endif
+ #if defined(__GNUC__) && !defined(__INTEL_COMPILER)
+ #define UNUSED __attribute__((unused))
+ #else
+ #define UNUSED
+ #endif
+ #define EXTINLINE
+#endif
+
+
+#if !defined(LEGACY_COMPILER)
+ #if !defined(UINT64_MAX)
+ /* The following #error is just a warning. If the compiler indeed does
+ * not have uint64_t, it is perfectly safe to comment out the #error. */
+ #error "Warning: Compiler without uint64_t. Comment out this line."
+ #define LEGACY_COMPILER
+ #endif
+#endif
+
+
+/******************************************************************************/
+/* Configuration */
+/******************************************************************************/
+
+#if defined(UNIVERSAL)
+ #if defined(CONFIG_64) || defined(CONFIG_32)
+ #error "cannot use CONFIG_64 or CONFIG_32 with UNIVERSAL."
+ #endif
+ #if defined(__ppc__)
+ #define CONFIG_32
+ #define ANSI
+ #elif defined(__ppc64__)
+ #define CONFIG_64
+ #define ANSI
+ #elif defined(__i386__)
+ #define CONFIG_32
+ #define ANSI
+ #elif defined(__x86_64__)
+ #define CONFIG_64
+ #define ASM
+ #else
+ #error "unknown architecture for universal build."
+ #endif
+#endif
+
+
+/* BEGIN CONFIG_64 */
+#if defined(CONFIG_64)
+/* types for modular and base arithmetic */
+#define MPD_UINT_MAX UINT64_MAX
+#define MPD_BITS_PER_UINT 64
+typedef uint64_t mpd_uint_t; /* unsigned mod type */
+
+#define MPD_SIZE_MAX SIZE_MAX
+typedef size_t mpd_size_t; /* unsigned size type */
+
+/* type for exp, digits, len, prec */
+#define MPD_SSIZE_MAX INT64_MAX
+#define MPD_SSIZE_MIN INT64_MIN
+typedef int64_t mpd_ssize_t;
+#define _mpd_strtossize strtoll
+
+/* decimal arithmetic */
+#define MPD_RADIX 10000000000000000000ULL /* 10**19 */
+#define MPD_RDIGITS 19
+#define MPD_MAX_POW10 19
+#define MPD_EXPDIGITS 19 /* MPD_EXPDIGITS <= MPD_RDIGITS+1 */
+
+#define MPD_MAXTRANSFORM_2N 4294967296ULL /* 2**32 */
+#define MPD_MAX_PREC 999999999999999999LL
+#define MPD_MAX_PREC_LOG2 64
+#define MPD_ELIMIT 1000000000000000000LL
+#define MPD_MAX_EMAX 999999999999999999LL /* ELIMIT-1 */
+#define MPD_MIN_EMIN (-999999999999999999LL) /* -EMAX */
+#define MPD_MIN_ETINY (MPD_MIN_EMIN-(MPD_MAX_PREC-1))
+#define MPD_EXP_INF 2000000000000000001LL
+#define MPD_EXP_CLAMP (-4000000000000000001LL)
+#define MPD_MAXIMPORT 105263157894736842L /* ceil((2*MPD_MAX_PREC)/MPD_RDIGITS) */
+
+/* conversion specifiers */
+#define PRI_mpd_uint_t PRIu64
+#define PRI_mpd_ssize_t PRIi64
+/* END CONFIG_64 */
+
+
+/* BEGIN CONFIG_32 */
+#elif defined(CONFIG_32)
+/* types for modular and base arithmetic */
+#define MPD_UINT_MAX UINT32_MAX
+#define MPD_BITS_PER_UINT 32
+typedef uint32_t mpd_uint_t; /* unsigned mod type */
+
+#ifndef LEGACY_COMPILER
+#define MPD_UUINT_MAX UINT64_MAX
+typedef uint64_t mpd_uuint_t; /* double width unsigned mod type */
+#endif
+
+#define MPD_SIZE_MAX SIZE_MAX
+typedef size_t mpd_size_t; /* unsigned size type */
+
+/* type for dec->len, dec->exp, ctx->prec */
+#define MPD_SSIZE_MAX INT32_MAX
+#define MPD_SSIZE_MIN INT32_MIN
+typedef int32_t mpd_ssize_t;
+#define _mpd_strtossize strtol
+
+/* decimal arithmetic */
+#define MPD_RADIX 1000000000UL /* 10**9 */
+#define MPD_RDIGITS 9
+#define MPD_MAX_POW10 9
+#define MPD_EXPDIGITS 10 /* MPD_EXPDIGITS <= MPD_RDIGITS+1 */
+
+#define MPD_MAXTRANSFORM_2N 33554432UL /* 2**25 */
+#define MPD_MAX_PREC 425000000L
+#define MPD_MAX_PREC_LOG2 32
+#define MPD_ELIMIT 425000001L
+#define MPD_MAX_EMAX 425000000L /* ELIMIT-1 */
+#define MPD_MIN_EMIN (-425000000L) /* -EMAX */
+#define MPD_MIN_ETINY (MPD_MIN_EMIN-(MPD_MAX_PREC-1))
+#define MPD_EXP_INF 1000000001L /* allows for emax=999999999 in the tests */
+#define MPD_EXP_CLAMP (-2000000001L) /* allows for emin=-999999999 in the tests */
+#define MPD_MAXIMPORT 94444445L /* ceil((2*MPD_MAX_PREC)/MPD_RDIGITS) */
+
+/* conversion specifiers */
+#define PRI_mpd_uint_t PRIu32
+#define PRI_mpd_ssize_t PRIi32
+/* END CONFIG_32 */
+
+#else
+ #error "define CONFIG_64 or CONFIG_32"
+#endif
+/* END CONFIG */
+
+
+#if MPD_SIZE_MAX != MPD_UINT_MAX
+ #error "unsupported platform: need mpd_size_t == mpd_uint_t"
+#endif
+
+
+/******************************************************************************/
+/* Context */
+/******************************************************************************/
+
+enum {
+ MPD_ROUND_UP, /* round away from 0 */
+ MPD_ROUND_DOWN, /* round toward 0 (truncate) */
+ MPD_ROUND_CEILING, /* round toward +infinity */
+ MPD_ROUND_FLOOR, /* round toward -infinity */
+ MPD_ROUND_HALF_UP, /* 0.5 is rounded up */
+ MPD_ROUND_HALF_DOWN, /* 0.5 is rounded down */
+ MPD_ROUND_HALF_EVEN, /* 0.5 is rounded to even */
+ MPD_ROUND_05UP, /* round zero or five away from 0 */
+ MPD_ROUND_TRUNC, /* truncate, but set infinity */
+ MPD_ROUND_GUARD
+};
+
+enum { MPD_CLAMP_DEFAULT, MPD_CLAMP_IEEE_754, MPD_CLAMP_GUARD };
+
+extern const char *mpd_round_string[MPD_ROUND_GUARD];
+extern const char *mpd_clamp_string[MPD_CLAMP_GUARD];
+
+
+typedef struct {
+ mpd_ssize_t prec; /* precision */
+ mpd_ssize_t emax; /* max positive exp */
+ mpd_ssize_t emin; /* min negative exp */
+ uint32_t traps; /* status events that should be trapped */
+ uint32_t status; /* status flags */
+ uint32_t newtrap; /* set by mpd_addstatus_raise() */
+ int round; /* rounding mode */
+ int clamp; /* clamp mode */
+ int allcr; /* all functions correctly rounded */
+} mpd_context_t;
+
+
+/* Status flags */
+#define MPD_Clamped 0x00000001U
+#define MPD_Conversion_syntax 0x00000002U
+#define MPD_Division_by_zero 0x00000004U
+#define MPD_Division_impossible 0x00000008U
+#define MPD_Division_undefined 0x00000010U
+#define MPD_Fpu_error 0x00000020U
+#define MPD_Inexact 0x00000040U
+#define MPD_Invalid_context 0x00000080U
+#define MPD_Invalid_operation 0x00000100U
+#define MPD_Malloc_error 0x00000200U
+#define MPD_Not_implemented 0x00000400U
+#define MPD_Overflow 0x00000800U
+#define MPD_Rounded 0x00001000U
+#define MPD_Subnormal 0x00002000U
+#define MPD_Underflow 0x00004000U
+#define MPD_Max_status (0x00008000U-1U)
+
+/* Conditions that result in an IEEE 754 exception */
+#define MPD_IEEE_Invalid_operation (MPD_Conversion_syntax | \
+ MPD_Division_impossible | \
+ MPD_Division_undefined | \
+ MPD_Fpu_error | \
+ MPD_Invalid_context | \
+ MPD_Invalid_operation | \
+ MPD_Malloc_error) \
+
+/* Errors that require the result of an operation to be set to NaN */
+#define MPD_Errors (MPD_IEEE_Invalid_operation | \
+ MPD_Division_by_zero)
+
+/* Default traps */
+#define MPD_Traps (MPD_IEEE_Invalid_operation | \
+ MPD_Division_by_zero | \
+ MPD_Overflow | \
+ MPD_Underflow)
+
+/* Official name */
+#define MPD_Insufficient_storage MPD_Malloc_error
+
+/* IEEE 754 interchange format contexts */
+#define MPD_IEEE_CONTEXT_MAX_BITS 512 /* 16*(log2(MPD_MAX_EMAX / 3)-3) */
+#define MPD_DECIMAL32 32
+#define MPD_DECIMAL64 64
+#define MPD_DECIMAL128 128
+
+
+#define MPD_MINALLOC_MIN 2
+#define MPD_MINALLOC_MAX 64
+extern mpd_ssize_t MPD_MINALLOC;
+extern void (* mpd_traphandler)(mpd_context_t *);
+void mpd_dflt_traphandler(mpd_context_t *);
+
+void mpd_setminalloc(mpd_ssize_t n);
+void mpd_init(mpd_context_t *ctx, mpd_ssize_t prec);
+
+void mpd_maxcontext(mpd_context_t *ctx);
+void mpd_defaultcontext(mpd_context_t *ctx);
+void mpd_basiccontext(mpd_context_t *ctx);
+int mpd_ieee_context(mpd_context_t *ctx, int bits);
+
+mpd_ssize_t mpd_getprec(const mpd_context_t *ctx);
+mpd_ssize_t mpd_getemax(const mpd_context_t *ctx);
+mpd_ssize_t mpd_getemin(const mpd_context_t *ctx);
+int mpd_getround(const mpd_context_t *ctx);
+uint32_t mpd_gettraps(const mpd_context_t *ctx);
+uint32_t mpd_getstatus(const mpd_context_t *ctx);
+int mpd_getclamp(const mpd_context_t *ctx);
+int mpd_getcr(const mpd_context_t *ctx);
+
+int mpd_qsetprec(mpd_context_t *ctx, mpd_ssize_t prec);
+int mpd_qsetemax(mpd_context_t *ctx, mpd_ssize_t emax);
+int mpd_qsetemin(mpd_context_t *ctx, mpd_ssize_t emin);
+int mpd_qsetround(mpd_context_t *ctx, int newround);
+int mpd_qsettraps(mpd_context_t *ctx, uint32_t flags);
+int mpd_qsetstatus(mpd_context_t *ctx, uint32_t flags);
+int mpd_qsetclamp(mpd_context_t *ctx, int c);
+int mpd_qsetcr(mpd_context_t *ctx, int c);
+void mpd_addstatus_raise(mpd_context_t *ctx, uint32_t flags);
+
+
+/******************************************************************************/
+/* Decimal Arithmetic */
+/******************************************************************************/
+
+/* mpd_t flags */
+#define MPD_POS ((uint8_t)0)
+#define MPD_NEG ((uint8_t)1)
+#define MPD_INF ((uint8_t)2)
+#define MPD_NAN ((uint8_t)4)
+#define MPD_SNAN ((uint8_t)8)
+#define MPD_SPECIAL (MPD_INF|MPD_NAN|MPD_SNAN)
+#define MPD_STATIC ((uint8_t)16)
+#define MPD_STATIC_DATA ((uint8_t)32)
+#define MPD_SHARED_DATA ((uint8_t)64)
+#define MPD_CONST_DATA ((uint8_t)128)
+#define MPD_DATAFLAGS (MPD_STATIC_DATA|MPD_SHARED_DATA|MPD_CONST_DATA)
+
+/* mpd_t */
+typedef struct {
+ uint8_t flags;
+ mpd_ssize_t exp;
+ mpd_ssize_t digits;
+ mpd_ssize_t len;
+ mpd_ssize_t alloc;
+ mpd_uint_t *data;
+} mpd_t;
+
+
+typedef unsigned char uchar;
+
+
+/******************************************************************************/
+/* Quiet, thread-safe functions */
+/******************************************************************************/
+
+/* format specification */
+typedef struct {
+ mpd_ssize_t min_width; /* minimum field width */
+ mpd_ssize_t prec; /* fraction digits or significant digits */
+ char type; /* conversion specifier */
+ char align; /* alignment */
+ char sign; /* sign printing/alignment */
+ char fill[5]; /* fill character */
+ const char *dot; /* decimal point */
+ const char *sep; /* thousands separator */
+ const char *grouping; /* grouping of digits */
+} mpd_spec_t;
+
+/* output to a string */
+char *mpd_to_sci(const mpd_t *dec, int fmt);
+char *mpd_to_eng(const mpd_t *dec, int fmt);
+mpd_ssize_t mpd_to_sci_size(char **res, const mpd_t *dec, int fmt);
+mpd_ssize_t mpd_to_eng_size(char **res, const mpd_t *dec, int fmt);
+int mpd_validate_lconv(mpd_spec_t *spec);
+int mpd_parse_fmt_str(mpd_spec_t *spec, const char *fmt, int caps);
+char * mpd_qformat_spec(const mpd_t *dec, const mpd_spec_t *spec, const mpd_context_t *ctx, uint32_t *status);
+char *mpd_qformat(const mpd_t *dec, const char *fmt, const mpd_context_t *ctx, uint32_t *status);
+
+#define MPD_NUM_FLAGS 15
+#define MPD_MAX_FLAG_STRING 208
+#define MPD_MAX_FLAG_LIST (MPD_MAX_FLAG_STRING+18)
+#define MPD_MAX_SIGNAL_LIST 121
+int mpd_snprint_flags(char *dest, int nmemb, uint32_t flags);
+int mpd_lsnprint_flags(char *dest, int nmemb, uint32_t flags, const char *flag_string[]);
+int mpd_lsnprint_signals(char *dest, int nmemb, uint32_t flags, const char *signal_string[]);
+
+/* output to a file */
+void mpd_fprint(FILE *file, const mpd_t *dec);
+void mpd_print(const mpd_t *dec);
+
+/* assignment from a string */
+void mpd_qset_string(mpd_t *dec, const char *s, const mpd_context_t *ctx, uint32_t *status);
+
+/* set to NaN with error flags */
+void mpd_seterror(mpd_t *result, uint32_t flags, uint32_t *status);
+/* set a special with sign and type */
+void mpd_setspecial(mpd_t *dec, uint8_t sign, uint8_t type);
+/* set coefficient to zero or all nines */
+void mpd_zerocoeff(mpd_t *result);
+void mpd_qmaxcoeff(mpd_t *result, const mpd_context_t *ctx, uint32_t *status);
+
+/* quietly assign a C integer type to an mpd_t */
+void mpd_qset_ssize(mpd_t *result, mpd_ssize_t a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qset_i32(mpd_t *result, int32_t a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qset_uint(mpd_t *result, mpd_uint_t a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qset_u32(mpd_t *result, uint32_t a, const mpd_context_t *ctx, uint32_t *status);
+#ifndef LEGACY_COMPILER
+void mpd_qset_i64(mpd_t *result, int64_t a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qset_u64(mpd_t *result, uint64_t a, const mpd_context_t *ctx, uint32_t *status);
+#endif
+
+/* quietly assign a C integer type to an mpd_t with a static coefficient */
+void mpd_qsset_ssize(mpd_t *result, mpd_ssize_t a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsset_i32(mpd_t *result, int32_t a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsset_uint(mpd_t *result, mpd_uint_t a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsset_u32(mpd_t *result, uint32_t a, const mpd_context_t *ctx, uint32_t *status);
+
+/* quietly get a C integer type from an mpd_t */
+mpd_ssize_t mpd_qget_ssize(const mpd_t *dec, uint32_t *status);
+mpd_uint_t mpd_qget_uint(const mpd_t *dec, uint32_t *status);
+mpd_uint_t mpd_qabs_uint(const mpd_t *dec, uint32_t *status);
+
+
+/* quiet functions */
+int mpd_qcheck_nan(mpd_t *nanresult, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+int mpd_qcheck_nans(mpd_t *nanresult, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qfinalize(mpd_t *result, const mpd_context_t *ctx, uint32_t *status);
+
+const char * mpd_class(const mpd_t *a, const mpd_context_t *ctx);
+
+int mpd_qcopy(mpd_t *result, const mpd_t *a, uint32_t *status);
+mpd_t *mpd_qncopy(const mpd_t *a);
+int mpd_qcopy_abs(mpd_t *result, const mpd_t *a, uint32_t *status);
+int mpd_qcopy_negate(mpd_t *result, const mpd_t *a, uint32_t *status);
+int mpd_qcopy_sign(mpd_t *result, const mpd_t *a, const mpd_t *b, uint32_t *status);
+
+void mpd_qand(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qinvert(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qlogb(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qor(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qscaleb(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qxor(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+int mpd_same_quantum(const mpd_t *a, const mpd_t *b);
+
+void mpd_qrotate(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+int mpd_qshiftl(mpd_t *result, const mpd_t *a, mpd_ssize_t n, uint32_t *status);
+mpd_uint_t mpd_qshiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n, uint32_t *status);
+mpd_uint_t mpd_qshiftr_inplace(mpd_t *result, mpd_ssize_t n);
+void mpd_qshift(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qshiftn(mpd_t *result, const mpd_t *a, mpd_ssize_t n, const mpd_context_t *ctx, uint32_t *status);
+
+int mpd_qcmp(const mpd_t *a, const mpd_t *b, uint32_t *status);
+int mpd_qcompare(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+int mpd_qcompare_signal(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+int mpd_cmp_total(const mpd_t *a, const mpd_t *b);
+int mpd_cmp_total_mag(const mpd_t *a, const mpd_t *b);
+int mpd_compare_total(mpd_t *result, const mpd_t *a, const mpd_t *b);
+int mpd_compare_total_mag(mpd_t *result, const mpd_t *a, const mpd_t *b);
+
+void mpd_qround_to_intx(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qround_to_int(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qtrunc(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qfloor(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qceil(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+
+void mpd_qabs(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmax(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmax_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmin(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmin_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qminus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qplus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qnext_minus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qnext_plus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qnext_toward(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qquantize(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qrescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qrescale_fmt(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qreduce(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qadd(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qadd_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qadd_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qadd_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qadd_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsub(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsub_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsub_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsub_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsub_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmul(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmul_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmul_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmul_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmul_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qfma(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qdiv(mpd_t *q, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qdiv_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qdiv_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qdiv_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qdiv_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qdivint(mpd_t *q, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qrem(mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qrem_near(mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qdivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qpow(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qpowmod(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_t *mod, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qexp(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qln10(mpd_t *result, mpd_ssize_t prec, uint32_t *status);
+void mpd_qln(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qlog10(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsqrt(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qinvroot(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status);
+
+
+size_t mpd_sizeinbase(mpd_t *a, uint32_t base);
+void mpd_qimport_u16(mpd_t *result, const uint16_t *srcdata, size_t srclen,
+ uint8_t srcsign, uint32_t srcbase,
+ const mpd_context_t *ctx, uint32_t *status);
+void mpd_qimport_u32(mpd_t *result, const uint32_t *srcdata, size_t srclen,
+ uint8_t srcsign, uint32_t srcbase,
+ const mpd_context_t *ctx, uint32_t *status);
+size_t mpd_qexport_u16(uint16_t *rdata, size_t rlen, uint32_t base,
+ const mpd_t *src, uint32_t *status);
+size_t mpd_qexport_u32(uint32_t *rdata, size_t rlen, uint32_t base,
+ const mpd_t *src, uint32_t *status);
+
+
+/******************************************************************************/
+/* Signalling functions */
+/******************************************************************************/
+
+char * mpd_format(const mpd_t *dec, const char *fmt, mpd_context_t *ctx);
+void mpd_import_u16(mpd_t *result, const uint16_t *srcdata, size_t srclen, uint8_t srcsign, uint32_t base, mpd_context_t *ctx);
+void mpd_import_u32(mpd_t *result, const uint32_t *srcdata, size_t srclen, uint8_t srcsign, uint32_t base, mpd_context_t *ctx);
+size_t mpd_export_u16(uint16_t *rdata, size_t rlen, uint32_t base, const mpd_t *src, mpd_context_t *ctx);
+size_t mpd_export_u32(uint32_t *rdata, size_t rlen, uint32_t base, const mpd_t *src, mpd_context_t *ctx);
+void mpd_finalize(mpd_t *result, mpd_context_t *ctx);
+int mpd_check_nan(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+int mpd_check_nans(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_set_string(mpd_t *result, const char *s, mpd_context_t *ctx);
+void mpd_maxcoeff(mpd_t *result, mpd_context_t *ctx);
+void mpd_sset_ssize(mpd_t *result, mpd_ssize_t a, mpd_context_t *ctx);
+void mpd_sset_i32(mpd_t *result, int32_t a, mpd_context_t *ctx);
+void mpd_sset_uint(mpd_t *result, mpd_uint_t a, mpd_context_t *ctx);
+void mpd_sset_u32(mpd_t *result, uint32_t a, mpd_context_t *ctx);
+void mpd_set_ssize(mpd_t *result, mpd_ssize_t a, mpd_context_t *ctx);
+void mpd_set_i32(mpd_t *result, int32_t a, mpd_context_t *ctx);
+void mpd_set_uint(mpd_t *result, mpd_uint_t a, mpd_context_t *ctx);
+void mpd_set_u32(mpd_t *result, uint32_t a, mpd_context_t *ctx);
+#ifndef LEGACY_COMPILER
+void mpd_set_i64(mpd_t *result, int64_t a, mpd_context_t *ctx);
+void mpd_set_u64(mpd_t *result, uint64_t a, mpd_context_t *ctx);
+#endif
+mpd_ssize_t mpd_get_ssize(const mpd_t *a, mpd_context_t *ctx);
+mpd_uint_t mpd_get_uint(const mpd_t *a, mpd_context_t *ctx);
+mpd_uint_t mpd_abs_uint(const mpd_t *a, mpd_context_t *ctx);
+void mpd_and(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_copy(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_canonical(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_copy_abs(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_copy_negate(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_copy_sign(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_invert(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_logb(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_or(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_rotate(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_scaleb(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_shiftl(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx);
+mpd_uint_t mpd_shiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx);
+void mpd_shiftn(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx);
+void mpd_shift(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_xor(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_abs(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+int mpd_cmp(const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+int mpd_compare(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+int mpd_compare_signal(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_add(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_add_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx);
+void mpd_add_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx);
+void mpd_add_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx);
+void mpd_add_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx);
+void mpd_sub(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_sub_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx);
+void mpd_sub_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx);
+void mpd_sub_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx);
+void mpd_sub_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx);
+void mpd_div(mpd_t *q, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_div_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx);
+void mpd_div_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx);
+void mpd_div_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx);
+void mpd_div_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx);
+void mpd_divmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_divint(mpd_t *q, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_exp(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_fma(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c, mpd_context_t *ctx);
+void mpd_ln(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_log10(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_max(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_max_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_min(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_min_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_minus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_mul(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_mul_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx);
+void mpd_mul_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx);
+void mpd_mul_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx);
+void mpd_mul_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx);
+void mpd_next_minus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_next_plus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_next_toward(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_plus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_pow(mpd_t *result, const mpd_t *base, const mpd_t *exp, mpd_context_t *ctx);
+void mpd_powmod(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_t *mod, mpd_context_t *ctx);
+void mpd_quantize(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_rescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, mpd_context_t *ctx);
+void mpd_reduce(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_rem(mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_rem_near(mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx);
+void mpd_round_to_intx(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_round_to_int(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_trunc(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_floor(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_ceil(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_sqrt(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+void mpd_invroot(mpd_t *result, const mpd_t *a, mpd_context_t *ctx);
+
+
+/******************************************************************************/
+/* Configuration specific */
+/******************************************************************************/
+
+#ifdef CONFIG_64
+void mpd_qsset_i64(mpd_t *result, int64_t a, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsset_u64(mpd_t *result, uint64_t a, const mpd_context_t *ctx, uint32_t *status);
+int64_t mpd_qget_i64(const mpd_t *dec, uint32_t *status);
+uint64_t mpd_qget_u64(const mpd_t *dec, uint32_t *status);
+
+void mpd_qadd_i64(mpd_t *result, const mpd_t *a, int64_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qadd_u64(mpd_t *result, const mpd_t *a, uint64_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsub_i64(mpd_t *result, const mpd_t *a, int64_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qsub_u64(mpd_t *result, const mpd_t *a, uint64_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmul_i64(mpd_t *result, const mpd_t *a, int64_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qmul_u64(mpd_t *result, const mpd_t *a, uint64_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qdiv_i64(mpd_t *result, const mpd_t *a, int64_t b, const mpd_context_t *ctx, uint32_t *status);
+void mpd_qdiv_u64(mpd_t *result, const mpd_t *a, uint64_t b, const mpd_context_t *ctx, uint32_t *status);
+
+void mpd_sset_i64(mpd_t *result, int64_t a, mpd_context_t *ctx);
+void mpd_sset_u64(mpd_t *result, uint64_t a, mpd_context_t *ctx);
+int64_t mpd_get_i64(const mpd_t *a, mpd_context_t *ctx);
+uint64_t mpd_get_u64(const mpd_t *a, mpd_context_t *ctx);
+
+void mpd_add_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx);
+void mpd_add_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx);
+void mpd_sub_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx);
+void mpd_sub_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx);
+void mpd_div_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx);
+void mpd_div_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx);
+void mpd_mul_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx);
+void mpd_mul_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx);
+#else
+int32_t mpd_qget_i32(const mpd_t *dec, uint32_t *status);
+uint32_t mpd_qget_u32(const mpd_t *dec, uint32_t *status);
+int32_t mpd_get_i32(const mpd_t *a, mpd_context_t *ctx);
+uint32_t mpd_get_u32(const mpd_t *a, mpd_context_t *ctx);
+#endif
+
+
+/******************************************************************************/
+/* Get attributes of a decimal */
+/******************************************************************************/
+
+EXTINLINE mpd_ssize_t mpd_adjexp(const mpd_t *dec);
+EXTINLINE mpd_ssize_t mpd_etiny(const mpd_context_t *ctx);
+EXTINLINE mpd_ssize_t mpd_etop(const mpd_context_t *ctx);
+EXTINLINE mpd_uint_t mpd_msword(const mpd_t *dec);
+EXTINLINE int mpd_word_digits(mpd_uint_t word);
+/* most significant digit of a word */
+EXTINLINE mpd_uint_t mpd_msd(mpd_uint_t word);
+/* least significant digit of a word */
+EXTINLINE mpd_uint_t mpd_lsd(mpd_uint_t word);
+/* coefficient size needed to store 'digits' */
+EXTINLINE mpd_ssize_t mpd_digits_to_size(mpd_ssize_t digits);
+/* number of digits in the exponent, undefined for MPD_SSIZE_MIN */
+EXTINLINE int mpd_exp_digits(mpd_ssize_t exp);
+EXTINLINE int mpd_iscanonical(const mpd_t *dec UNUSED);
+EXTINLINE int mpd_isfinite(const mpd_t *dec);
+EXTINLINE int mpd_isinfinite(const mpd_t *dec);
+EXTINLINE int mpd_isinteger(const mpd_t *dec);
+EXTINLINE int mpd_isnan(const mpd_t *dec);
+EXTINLINE int mpd_isnegative(const mpd_t *dec);
+EXTINLINE int mpd_ispositive(const mpd_t *dec);
+EXTINLINE int mpd_isqnan(const mpd_t *dec);
+EXTINLINE int mpd_issigned(const mpd_t *dec);
+EXTINLINE int mpd_issnan(const mpd_t *dec);
+EXTINLINE int mpd_isspecial(const mpd_t *dec);
+EXTINLINE int mpd_iszero(const mpd_t *dec);
+/* undefined for special numbers */
+EXTINLINE int mpd_iszerocoeff(const mpd_t *dec);
+EXTINLINE int mpd_isnormal(const mpd_t *dec, const mpd_context_t *ctx);
+EXTINLINE int mpd_issubnormal(const mpd_t *dec, const mpd_context_t *ctx);
+/* odd word */
+EXTINLINE int mpd_isoddword(mpd_uint_t word);
+/* odd coefficient */
+EXTINLINE int mpd_isoddcoeff(const mpd_t *dec);
+/* odd decimal, only defined for integers */
+int mpd_isodd(const mpd_t *dec);
+/* even decimal, only defined for integers */
+int mpd_iseven(const mpd_t *dec);
+/* 0 if dec is positive, 1 if dec is negative */
+EXTINLINE uint8_t mpd_sign(const mpd_t *dec);
+/* 1 if dec is positive, -1 if dec is negative */
+EXTINLINE int mpd_arith_sign(const mpd_t *dec);
+EXTINLINE long mpd_radix(void);
+EXTINLINE int mpd_isdynamic(mpd_t *dec);
+EXTINLINE int mpd_isstatic(mpd_t *dec);
+EXTINLINE int mpd_isdynamic_data(mpd_t *dec);
+EXTINLINE int mpd_isstatic_data(mpd_t *dec);
+EXTINLINE int mpd_isshared_data(mpd_t *dec);
+EXTINLINE int mpd_isconst_data(mpd_t *dec);
+EXTINLINE mpd_ssize_t mpd_trail_zeros(const mpd_t *dec);
+
+
+/******************************************************************************/
+/* Set attributes of a decimal */
+/******************************************************************************/
+
+/* set number of decimal digits in the coefficient */
+EXTINLINE void mpd_setdigits(mpd_t *result);
+EXTINLINE void mpd_set_sign(mpd_t *result, uint8_t sign);
+/* copy sign from another decimal */
+EXTINLINE void mpd_signcpy(mpd_t *result, mpd_t *a);
+EXTINLINE void mpd_set_infinity(mpd_t *result);
+EXTINLINE void mpd_set_qnan(mpd_t *result);
+EXTINLINE void mpd_set_snan(mpd_t *result);
+EXTINLINE void mpd_set_negative(mpd_t *result);
+EXTINLINE void mpd_set_positive(mpd_t *result);
+EXTINLINE void mpd_set_dynamic(mpd_t *result);
+EXTINLINE void mpd_set_static(mpd_t *result);
+EXTINLINE void mpd_set_dynamic_data(mpd_t *result);
+EXTINLINE void mpd_set_static_data(mpd_t *result);
+EXTINLINE void mpd_set_shared_data(mpd_t *result);
+EXTINLINE void mpd_set_const_data(mpd_t *result);
+EXTINLINE void mpd_clear_flags(mpd_t *result);
+EXTINLINE void mpd_set_flags(mpd_t *result, uint8_t flags);
+EXTINLINE void mpd_copy_flags(mpd_t *result, const mpd_t *a);
+
+
+/******************************************************************************/
+/* Error Macros */
+/******************************************************************************/
+
+#define mpd_err_fatal(...) \
+ do {fprintf(stderr, "%s:%d: error: ", __FILE__, __LINE__); \
+ fprintf(stderr, __VA_ARGS__); fputc('\n', stderr); \
+ exit(1); \
+ } while (0)
+#define mpd_err_warn(...) \
+ do {fprintf(stderr, "%s:%d: warning: ", __FILE__, __LINE__); \
+ fprintf(stderr, __VA_ARGS__); fputc('\n', stderr); \
+ } while (0)
+
+
+/******************************************************************************/
+/* Memory handling */
+/******************************************************************************/
+
+extern void *(* mpd_mallocfunc)(size_t size);
+extern void *(* mpd_callocfunc)(size_t nmemb, size_t size);
+extern void *(* mpd_reallocfunc)(void *ptr, size_t size);
+extern void (* mpd_free)(void *ptr);
+
+void *mpd_callocfunc_em(size_t nmemb, size_t size);
+
+void *mpd_alloc(mpd_size_t nmemb, mpd_size_t size);
+void *mpd_calloc(mpd_size_t nmemb, mpd_size_t size);
+void *mpd_realloc(void *ptr, mpd_size_t nmemb, mpd_size_t size, uint8_t *err);
+void *mpd_sh_alloc(mpd_size_t struct_size, mpd_size_t nmemb, mpd_size_t size);
+
+mpd_t *mpd_qnew(void);
+mpd_t *mpd_new(mpd_context_t *ctx);
+mpd_t *mpd_qnew_size(mpd_ssize_t size);
+void mpd_del(mpd_t *dec);
+
+void mpd_uint_zero(mpd_uint_t *dest, mpd_size_t len);
+int mpd_qresize(mpd_t *result, mpd_ssize_t size, uint32_t *status);
+int mpd_qresize_zero(mpd_t *result, mpd_ssize_t size, uint32_t *status);
+void mpd_minalloc(mpd_t *result);
+
+int mpd_resize(mpd_t *result, mpd_ssize_t size, mpd_context_t *ctx);
+int mpd_resize_zero(mpd_t *result, mpd_ssize_t size, mpd_context_t *ctx);
+
+
+#ifdef __cplusplus
+} /* END extern "C" */
+#endif
+
+
+#endif /* MPDECIMAL_H */
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include "mpdecimal.h"
+#include <stdlib.h>
+#include <assert.h>
+#include "bits.h"
+#include "umodarith.h"
+#include "numbertheory.h"
+
+
+/* Bignum: Initialize the Number Theoretic Transform. */
+
+
+/*
+ * Return the nth root of unity in F(p). This corresponds to e**((2*pi*i)/n)
+ * in the Fourier transform. We have w**n == 1 (mod p).
+ * n := transform length.
+ * sign := -1 for forward transform, 1 for backward transform.
+ * modnum := one of {P1, P2, P3}.
+ */
+mpd_uint_t
+_mpd_getkernel(mpd_uint_t n, int sign, int modnum)
+{
+ mpd_uint_t umod, p, r, xi;
+#ifdef PPRO
+ double dmod;
+ uint32_t dinvmod[3];
+#endif
+
+ SETMODULUS(modnum);
+ r = mpd_roots[modnum]; /* primitive root of F(p) */
+ p = umod;
+ xi = (p-1) / n;
+
+ if (sign == -1)
+ return POWMOD(r, (p-1-xi));
+ else
+ return POWMOD(r, xi);
+}
+
+/*
+ * Initialize and return transform parameters.
+ * n := transform length.
+ * sign := -1 for forward transform, 1 for backward transform.
+ * modnum := one of {P1, P2, P3}.
+ */
+struct fnt_params *
+_mpd_init_fnt_params(mpd_size_t n, int sign, int modnum)
+{
+ struct fnt_params *tparams;
+ mpd_uint_t umod;
+#ifdef PPRO
+ double dmod;
+ uint32_t dinvmod[3];
+#endif
+ mpd_uint_t kernel, w;
+ mpd_uint_t i;
+ mpd_size_t nhalf;
+
+ assert(ispower2(n));
+ assert(sign == -1 || sign == 1);
+ assert(P1 <= modnum && modnum <= P3);
+
+ nhalf = n/2;
+ tparams = mpd_sh_alloc(sizeof *tparams, nhalf, sizeof (mpd_uint_t));
+ if (tparams == NULL) {
+ return NULL;
+ }
+
+ SETMODULUS(modnum);
+ kernel = _mpd_getkernel(n, sign, modnum);
+
+ tparams->modnum = modnum;
+ tparams->modulus = umod;
+ tparams->kernel = kernel;
+
+ /* wtable[] := w**0, w**1, ..., w**(nhalf-1) */
+ w = 1;
+ for (i = 0; i < nhalf; i++) {
+ tparams->wtable[i] = w;
+ w = MULMOD(w, kernel);
+ }
+
+ return tparams;
+}
+
+/* Initialize wtable of size three. */
+void
+_mpd_init_w3table(mpd_uint_t w3table[3], int sign, int modnum)
+{
+ mpd_uint_t umod;
+#ifdef PPRO
+ double dmod;
+ uint32_t dinvmod[3];
+#endif
+ mpd_uint_t kernel;
+
+ SETMODULUS(modnum);
+ kernel = _mpd_getkernel(3, sign, modnum);
+
+ w3table[0] = 1;
+ w3table[1] = kernel;
+ w3table[2] = POWMOD(kernel, 2);
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef NUMBER_THEORY_H
+#define NUMBER_THEORY_H
+
+
+#include "constants.h"
+#include "mpdecimal.h"
+
+
+/* transform parameters */
+struct fnt_params {
+ int modnum;
+ mpd_uint_t modulus;
+ mpd_uint_t kernel;
+ mpd_uint_t wtable[];
+};
+
+
+mpd_uint_t _mpd_getkernel(mpd_uint_t n, int sign, int modnum);
+struct fnt_params *_mpd_init_fnt_params(mpd_size_t n, int sign, int modnum);
+void _mpd_init_w3table(mpd_uint_t w3table[3], int sign, int modnum);
+
+
+#ifdef PPRO
+static inline void
+ppro_setmodulus(int modnum, mpd_uint_t *umod, double *dmod, uint32_t dinvmod[3])
+{
+ *dmod = *umod = mpd_moduli[modnum];
+ dinvmod[0] = mpd_invmoduli[modnum][0];
+ dinvmod[1] = mpd_invmoduli[modnum][1];
+ dinvmod[2] = mpd_invmoduli[modnum][2];
+}
+#else
+static inline void
+std_setmodulus(int modnum, mpd_uint_t *umod)
+{
+ *umod = mpd_moduli[modnum];
+}
+#endif
+
+
+#endif
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include "mpdecimal.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "bits.h"
+#include "difradix2.h"
+#include "numbertheory.h"
+#include "transpose.h"
+#include "umodarith.h"
+#include "sixstep.h"
+
+
+/* Bignum: Cache efficient Matrix Fourier Transform for arrays of the
+ form 2**n (See literature/six-step.txt). */
+
+
+/* forward transform with sign = -1 */
+int
+six_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum)
+{
+ struct fnt_params *tparams;
+ mpd_size_t log2n, C, R;
+ mpd_uint_t kernel;
+ mpd_uint_t umod;
+#ifdef PPRO
+ double dmod;
+ uint32_t dinvmod[3];
+#endif
+ mpd_uint_t *x, w0, w1, wstep;
+ mpd_size_t i, k;
+
+
+ assert(ispower2(n));
+ assert(n >= 16);
+ assert(n <= MPD_MAXTRANSFORM_2N);
+
+ log2n = mpd_bsr(n);
+ C = ((mpd_size_t)1) << (log2n / 2); /* number of columns */
+ R = ((mpd_size_t)1) << (log2n - (log2n / 2)); /* number of rows */
+
+
+ /* Transpose the matrix. */
+ if (!transpose_pow2(a, R, C)) {
+ return 0;
+ }
+
+ /* Length R transform on the rows. */
+ if ((tparams = _mpd_init_fnt_params(R, -1, modnum)) == NULL) {
+ return 0;
+ }
+ for (x = a; x < a+n; x += R) {
+ fnt_dif2(x, R, tparams);
+ }
+
+ /* Transpose the matrix. */
+ if (!transpose_pow2(a, C, R)) {
+ mpd_free(tparams);
+ return 0;
+ }
+
+ /* Multiply each matrix element (addressed by i*C+k) by r**(i*k). */
+ SETMODULUS(modnum);
+ kernel = _mpd_getkernel(n, -1, modnum);
+ for (i = 1; i < R; i++) {
+ w0 = 1; /* r**(i*0): initial value for k=0 */
+ w1 = POWMOD(kernel, i); /* r**(i*1): initial value for k=1 */
+ wstep = MULMOD(w1, w1); /* r**(2*i) */
+ for (k = 0; k < C; k += 2) {
+ mpd_uint_t x0 = a[i*C+k];
+ mpd_uint_t x1 = a[i*C+k+1];
+ MULMOD2(&x0, w0, &x1, w1);
+ MULMOD2C(&w0, &w1, wstep); /* r**(i*(k+2)) = r**(i*k) * r**(2*i) */
+ a[i*C+k] = x0;
+ a[i*C+k+1] = x1;
+ }
+ }
+
+ /* Length C transform on the rows. */
+ if (C != R) {
+ mpd_free(tparams);
+ if ((tparams = _mpd_init_fnt_params(C, -1, modnum)) == NULL) {
+ return 0;
+ }
+ }
+ for (x = a; x < a+n; x += C) {
+ fnt_dif2(x, C, tparams);
+ }
+ mpd_free(tparams);
+
+#if 0 /* An unordered transform is sufficient for convolution. */
+ /* Transpose the matrix. */
+ if (!transpose_pow2(a, R, C)) {
+ return 0;
+ }
+#endif
+
+ return 1;
+}
+
+
+/* reverse transform, sign = 1 */
+int
+inv_six_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum)
+{
+ struct fnt_params *tparams;
+ mpd_size_t log2n, C, R;
+ mpd_uint_t kernel;
+ mpd_uint_t umod;
+#ifdef PPRO
+ double dmod;
+ uint32_t dinvmod[3];
+#endif
+ mpd_uint_t *x, w0, w1, wstep;
+ mpd_size_t i, k;
+
+
+ assert(ispower2(n));
+ assert(n >= 16);
+ assert(n <= MPD_MAXTRANSFORM_2N);
+
+ log2n = mpd_bsr(n);
+ C = ((mpd_size_t)1) << (log2n / 2); /* number of columns */
+ R = ((mpd_size_t)1) << (log2n - (log2n / 2)); /* number of rows */
+
+
+#if 0 /* An unordered transform is sufficient for convolution. */
+ /* Transpose the matrix, producing an R*C matrix. */
+ if (!transpose_pow2(a, C, R)) {
+ return 0;
+ }
+#endif
+
+ /* Length C transform on the rows. */
+ if ((tparams = _mpd_init_fnt_params(C, 1, modnum)) == NULL) {
+ return 0;
+ }
+ for (x = a; x < a+n; x += C) {
+ fnt_dif2(x, C, tparams);
+ }
+
+ /* Multiply each matrix element (addressed by i*C+k) by r**(i*k). */
+ SETMODULUS(modnum);
+ kernel = _mpd_getkernel(n, 1, modnum);
+ for (i = 1; i < R; i++) {
+ w0 = 1;
+ w1 = POWMOD(kernel, i);
+ wstep = MULMOD(w1, w1);
+ for (k = 0; k < C; k += 2) {
+ mpd_uint_t x0 = a[i*C+k];
+ mpd_uint_t x1 = a[i*C+k+1];
+ MULMOD2(&x0, w0, &x1, w1);
+ MULMOD2C(&w0, &w1, wstep);
+ a[i*C+k] = x0;
+ a[i*C+k+1] = x1;
+ }
+ }
+
+ /* Transpose the matrix. */
+ if (!transpose_pow2(a, R, C)) {
+ mpd_free(tparams);
+ return 0;
+ }
+
+ /* Length R transform on the rows. */
+ if (R != C) {
+ mpd_free(tparams);
+ if ((tparams = _mpd_init_fnt_params(R, 1, modnum)) == NULL) {
+ return 0;
+ }
+ }
+ for (x = a; x < a+n; x += R) {
+ fnt_dif2(x, R, tparams);
+ }
+ mpd_free(tparams);
+
+ /* Transpose the matrix. */
+ if (!transpose_pow2(a, C, R)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef SIX_STEP_H
+#define SIX_STEP_H
+
+
+#include "mpdecimal.h"
+#include <stdio.h>
+
+
+int six_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum);
+int inv_six_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum);
+
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include "mpdecimal.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <assert.h>
+#include "bits.h"
+#include "constants.h"
+#include "typearith.h"
+#include "transpose.h"
+
+
+#define BUFSIZE 4096
+#define SIDE 128
+
+
+/* Bignum: The transpose functions are used for very large transforms
+ in sixstep.c and fourstep.c. */
+
+
+/* Definition of the matrix transpose */
+void
+std_trans(mpd_uint_t dest[], mpd_uint_t src[], mpd_size_t rows, mpd_size_t cols)
+{
+ mpd_size_t idest, isrc;
+ mpd_size_t r, c;
+
+ for (r = 0; r < rows; r++) {
+ isrc = r * cols;
+ idest = r;
+ for (c = 0; c < cols; c++) {
+ dest[idest] = src[isrc];
+ isrc += 1;
+ idest += rows;
+ }
+ }
+}
+
+/*
+ * Swap half-rows of 2^n * (2*2^n) matrix.
+ * FORWARD_CYCLE: even/odd permutation of the halfrows.
+ * BACKWARD_CYCLE: reverse the even/odd permutation.
+ */
+static int
+swap_halfrows_pow2(mpd_uint_t *matrix, mpd_size_t rows, mpd_size_t cols, int dir)
+{
+ mpd_uint_t buf1[BUFSIZE];
+ mpd_uint_t buf2[BUFSIZE];
+ mpd_uint_t *readbuf, *writebuf, *hp;
+ mpd_size_t *done, dbits;
+ mpd_size_t b = BUFSIZE, stride;
+ mpd_size_t hn, hmax; /* halfrow number */
+ mpd_size_t m, r=0;
+ mpd_size_t offset;
+ mpd_size_t next;
+
+
+ assert(cols == mul_size_t(2, rows));
+
+ if (dir == FORWARD_CYCLE) {
+ r = rows;
+ }
+ else if (dir == BACKWARD_CYCLE) {
+ r = 2;
+ }
+ else {
+ abort(); /* GCOV_NOT_REACHED */
+ }
+
+ m = cols - 1;
+ hmax = rows; /* cycles start at odd halfrows */
+ dbits = 8 * sizeof *done;
+ if ((done = mpd_calloc(hmax/(sizeof *done) + 1, sizeof *done)) == NULL) {
+ return 0;
+ }
+
+ for (hn = 1; hn <= hmax; hn += 2) {
+
+ if (done[hn/dbits] & mpd_bits[hn%dbits]) {
+ continue;
+ }
+
+ readbuf = buf1; writebuf = buf2;
+
+ for (offset = 0; offset < cols/2; offset += b) {
+
+ stride = (offset + b < cols/2) ? b : cols/2-offset;
+
+ hp = matrix + hn*cols/2;
+ memcpy(readbuf, hp+offset, stride*(sizeof *readbuf));
+ pointerswap(&readbuf, &writebuf);
+
+ next = mulmod_size_t(hn, r, m);
+ hp = matrix + next*cols/2;
+
+ while (next != hn) {
+
+ memcpy(readbuf, hp+offset, stride*(sizeof *readbuf));
+ memcpy(hp+offset, writebuf, stride*(sizeof *writebuf));
+ pointerswap(&readbuf, &writebuf);
+
+ done[next/dbits] |= mpd_bits[next%dbits];
+
+ next = mulmod_size_t(next, r, m);
+ hp = matrix + next*cols/2;
+
+ }
+
+ memcpy(hp+offset, writebuf, stride*(sizeof *writebuf));
+
+ done[hn/dbits] |= mpd_bits[hn%dbits];
+ }
+ }
+
+ mpd_free(done);
+ return 1;
+}
+
+/* In-place transpose of a square matrix */
+static inline void
+squaretrans(mpd_uint_t *buf, mpd_size_t cols)
+{
+ mpd_uint_t tmp;
+ mpd_size_t idest, isrc;
+ mpd_size_t r, c;
+
+ for (r = 0; r < cols; r++) {
+ c = r+1;
+ isrc = r*cols + c;
+ idest = c*cols + r;
+ for (c = r+1; c < cols; c++) {
+ tmp = buf[isrc];
+ buf[isrc] = buf[idest];
+ buf[idest] = tmp;
+ isrc += 1;
+ idest += cols;
+ }
+ }
+}
+
+/*
+ * Transpose 2^n * 2^n matrix. For cache efficiency, the matrix is split into
+ * square blocks with side length 'SIDE'. First, the blocks are transposed,
+ * then a square tranposition is done on each individual block.
+ */
+static void
+squaretrans_pow2(mpd_uint_t *matrix, mpd_size_t size)
+{
+ mpd_uint_t buf1[SIDE*SIDE];
+ mpd_uint_t buf2[SIDE*SIDE];
+ mpd_uint_t *to, *from;
+ mpd_size_t b = size;
+ mpd_size_t r, c;
+ mpd_size_t i;
+
+ while (b > SIDE) b >>= 1;
+
+ for (r = 0; r < size; r += b) {
+
+ for (c = r; c < size; c += b) {
+
+ from = matrix + r*size + c;
+ to = buf1;
+ for (i = 0; i < b; i++) {
+ memcpy(to, from, b*(sizeof *to));
+ from += size;
+ to += b;
+ }
+ squaretrans(buf1, b);
+
+ if (r == c) {
+ to = matrix + r*size + c;
+ from = buf1;
+ for (i = 0; i < b; i++) {
+ memcpy(to, from, b*(sizeof *to));
+ from += b;
+ to += size;
+ }
+ continue;
+ }
+ else {
+ from = matrix + c*size + r;
+ to = buf2;
+ for (i = 0; i < b; i++) {
+ memcpy(to, from, b*(sizeof *to));
+ from += size;
+ to += b;
+ }
+ squaretrans(buf2, b);
+
+ to = matrix + c*size + r;
+ from = buf1;
+ for (i = 0; i < b; i++) {
+ memcpy(to, from, b*(sizeof *to));
+ from += b;
+ to += size;
+ }
+
+ to = matrix + r*size + c;
+ from = buf2;
+ for (i = 0; i < b; i++) {
+ memcpy(to, from, b*(sizeof *to));
+ from += b;
+ to += size;
+ }
+ }
+ }
+ }
+
+}
+
+/*
+ * In-place transposition of a 2^n x 2^n or a 2^n x (2*2^n)
+ * or a (2*2^n) x 2^n matrix.
+ */
+int
+transpose_pow2(mpd_uint_t *matrix, mpd_size_t rows, mpd_size_t cols)
+{
+ mpd_size_t size = mul_size_t(rows, cols);
+
+ assert(ispower2(rows));
+ assert(ispower2(cols));
+
+ if (cols == rows) {
+ squaretrans_pow2(matrix, rows);
+ }
+ else if (cols == mul_size_t(2, rows)) {
+ if (!swap_halfrows_pow2(matrix, rows, cols, FORWARD_CYCLE)) {
+ return 0;
+ }
+ squaretrans_pow2(matrix, rows);
+ squaretrans_pow2(matrix+(size/2), rows);
+ }
+ else if (rows == mul_size_t(2, cols)) {
+ squaretrans_pow2(matrix, cols);
+ squaretrans_pow2(matrix+(size/2), cols);
+ if (!swap_halfrows_pow2(matrix, cols, rows, BACKWARD_CYCLE)) {
+ return 0;
+ }
+ }
+ else {
+ abort(); /* GCOV_NOT_REACHED */
+ }
+
+ return 1;
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef TRANSPOSE_H
+#define TRANSPOSE_H
+
+
+#include "mpdecimal.h"
+#include <stdio.h>
+
+
+enum {FORWARD_CYCLE, BACKWARD_CYCLE};
+
+
+void std_trans(mpd_uint_t dest[], mpd_uint_t src[], mpd_size_t rows, mpd_size_t cols);
+int transpose_pow2(mpd_uint_t *matrix, mpd_size_t rows, mpd_size_t cols);
+void transpose_3xpow2(mpd_uint_t *matrix, mpd_size_t rows, mpd_size_t cols);
+
+
+static inline void pointerswap(mpd_uint_t **a, mpd_uint_t **b)
+{
+ mpd_uint_t *tmp;
+
+ tmp = *b;
+ *b = *a;
+ *a = tmp;
+}
+
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef TYPEARITH_H
+#define TYPEARITH_H
+
+
+#include "mpdecimal.h"
+
+
+/*****************************************************************************/
+/* Low level native arithmetic on basic types */
+/*****************************************************************************/
+
+
+/** ------------------------------------------------------------
+ ** Double width multiplication and division
+ ** ------------------------------------------------------------
+ */
+
+#if defined(CONFIG_64)
+#if defined(ANSI)
+#if defined(HAVE_UINT128_T)
+static inline void
+_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b)
+{
+ __uint128_t hl;
+
+ hl = (__uint128_t)a * b;
+
+ *hi = hl >> 64;
+ *lo = (mpd_uint_t)hl;
+}
+
+static inline void
+_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo,
+ mpd_uint_t d)
+{
+ __uint128_t hl;
+
+ hl = ((__uint128_t)hi<<64) + lo;
+ *q = (mpd_uint_t)(hl / d); /* quotient is known to fit */
+ *r = (mpd_uint_t)(hl - (__uint128_t)(*q) * d);
+}
+#else
+static inline void
+_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b)
+{
+ uint32_t w[4], carry;
+ uint32_t ah, al, bh, bl;
+ uint64_t hl;
+
+ ah = (uint32_t)(a>>32); al = (uint32_t)a;
+ bh = (uint32_t)(b>>32); bl = (uint32_t)b;
+
+ hl = (uint64_t)al * bl;
+ w[0] = (uint32_t)hl;
+ carry = (uint32_t)(hl>>32);
+
+ hl = (uint64_t)ah * bl + carry;
+ w[1] = (uint32_t)hl;
+ w[2] = (uint32_t)(hl>>32);
+
+ hl = (uint64_t)al * bh + w[1];
+ w[1] = (uint32_t)hl;
+ carry = (uint32_t)(hl>>32);
+
+ hl = ((uint64_t)ah * bh + w[2]) + carry;
+ w[2] = (uint32_t)hl;
+ w[3] = (uint32_t)(hl>>32);
+
+ *hi = ((uint64_t)w[3]<<32) + w[2];
+ *lo = ((uint64_t)w[1]<<32) + w[0];
+}
+
+/*
+ * By Henry S. Warren: http://www.hackersdelight.org/HDcode/divlu.c.txt
+ * http://www.hackersdelight.org/permissions.htm:
+ * "You are free to use, copy, and distribute any of the code on this web
+ * site, whether modified by you or not. You need not give attribution."
+ *
+ * Slightly modified, comments are mine.
+ */
+static inline int
+nlz(uint64_t x)
+{
+ int n;
+
+ if (x == 0) return(64);
+
+ n = 0;
+ if (x <= 0x00000000FFFFFFFF) {n = n +32; x = x <<32;}
+ if (x <= 0x0000FFFFFFFFFFFF) {n = n +16; x = x <<16;}
+ if (x <= 0x00FFFFFFFFFFFFFF) {n = n + 8; x = x << 8;}
+ if (x <= 0x0FFFFFFFFFFFFFFF) {n = n + 4; x = x << 4;}
+ if (x <= 0x3FFFFFFFFFFFFFFF) {n = n + 2; x = x << 2;}
+ if (x <= 0x7FFFFFFFFFFFFFFF) {n = n + 1;}
+
+ return n;
+}
+
+static inline void
+_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t u1, mpd_uint_t u0,
+ mpd_uint_t v)
+{
+ const mpd_uint_t b = 4294967296;
+ mpd_uint_t un1, un0,
+ vn1, vn0,
+ q1, q0,
+ un32, un21, un10,
+ rhat, t;
+ int s;
+
+ assert(u1 < v);
+
+ s = nlz(v);
+ v = v << s;
+ vn1 = v >> 32;
+ vn0 = v & 0xFFFFFFFF;
+
+ t = (s == 0) ? 0 : u0 >> (64 - s);
+ un32 = (u1 << s) | t;
+ un10 = u0 << s;
+
+ un1 = un10 >> 32;
+ un0 = un10 & 0xFFFFFFFF;
+
+ q1 = un32 / vn1;
+ rhat = un32 - q1*vn1;
+again1:
+ if (q1 >= b || q1*vn0 > b*rhat + un1) {
+ q1 = q1 - 1;
+ rhat = rhat + vn1;
+ if (rhat < b) goto again1;
+ }
+
+ /*
+ * Before again1 we had:
+ * (1) q1*vn1 + rhat = un32
+ * (2) q1*vn1*b + rhat*b + un1 = un32*b + un1
+ *
+ * The statements inside the if-clause do not change the value
+ * of the left-hand side of (2), and the loop is only exited
+ * if q1*vn0 <= rhat*b + un1, so:
+ *
+ * (3) q1*vn1*b + q1*vn0 <= un32*b + un1
+ * (4) q1*v <= un32*b + un1
+ * (5) 0 <= un32*b + un1 - q1*v
+ *
+ * By (5) we are certain that the possible add-back step from
+ * Knuth's algorithm D is never required.
+ *
+ * Since the final quotient is less than 2**64, the following
+ * must be true:
+ *
+ * (6) un32*b + un1 - q1*v <= UINT64_MAX
+ *
+ * This means that in the following line, the high words
+ * of un32*b and q1*v can be discarded without any effect
+ * on the result.
+ */
+ un21 = un32*b + un1 - q1*v;
+
+ q0 = un21 / vn1;
+ rhat = un21 - q0*vn1;
+again2:
+ if (q0 >= b || q0*vn0 > b*rhat + un0) {
+ q0 = q0 - 1;
+ rhat = rhat + vn1;
+ if (rhat < b) goto again2;
+ }
+
+ *q = q1*b + q0;
+ *r = (un21*b + un0 - q0*v) >> s;
+}
+#endif
+
+/* END ANSI */
+#elif defined(ASM)
+static inline void
+_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b)
+{
+ mpd_uint_t h, l;
+
+ asm ( "mulq %3\n\t"
+ : "=d" (h), "=a" (l)
+ : "%a" (a), "rm" (b)
+ : "cc"
+ );
+
+ *hi = h;
+ *lo = l;
+}
+
+static inline void
+_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo,
+ mpd_uint_t d)
+{
+ mpd_uint_t qq, rr;
+
+ asm ( "divq %4\n\t"
+ : "=a" (qq), "=d" (rr)
+ : "a" (lo), "d" (hi), "rm" (d)
+ : "cc"
+ );
+
+ *q = qq;
+ *r = rr;
+}
+/* END GCC ASM */
+#elif defined(MASM)
+#include <intrin.h>
+#pragma intrinsic(_umul128)
+
+static inline void
+_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b)
+{
+ *lo = _umul128(a, b, hi);
+}
+
+void _mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo,
+ mpd_uint_t d);
+
+/* END MASM (_MSC_VER) */
+#else
+ #error "need platform specific 128 bit multiplication and division"
+#endif
+
+#define DIVMOD(q, r, v, d) *q = v / d; *r = v - *q * d
+static inline void
+_mpd_divmod_pow10(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t v, mpd_uint_t exp)
+{
+ assert(exp <= 19);
+
+ if (exp <= 9) {
+ if (exp <= 4) {
+ switch (exp) {
+ case 0: *q = v; *r = 0; break;
+ case 1: DIVMOD(q, r, v, 10UL); break;
+ case 2: DIVMOD(q, r, v, 100UL); break;
+ case 3: DIVMOD(q, r, v, 1000UL); break;
+ case 4: DIVMOD(q, r, v, 10000UL); break;
+ }
+ }
+ else {
+ switch (exp) {
+ case 5: DIVMOD(q, r, v, 100000UL); break;
+ case 6: DIVMOD(q, r, v, 1000000UL); break;
+ case 7: DIVMOD(q, r, v, 10000000UL); break;
+ case 8: DIVMOD(q, r, v, 100000000UL); break;
+ case 9: DIVMOD(q, r, v, 1000000000UL); break;
+ }
+ }
+ }
+ else {
+ if (exp <= 14) {
+ switch (exp) {
+ case 10: DIVMOD(q, r, v, 10000000000ULL); break;
+ case 11: DIVMOD(q, r, v, 100000000000ULL); break;
+ case 12: DIVMOD(q, r, v, 1000000000000ULL); break;
+ case 13: DIVMOD(q, r, v, 10000000000000ULL); break;
+ case 14: DIVMOD(q, r, v, 100000000000000ULL); break;
+ }
+ }
+ else {
+ switch (exp) {
+ case 15: DIVMOD(q, r, v, 1000000000000000ULL); break;
+ case 16: DIVMOD(q, r, v, 10000000000000000ULL); break;
+ case 17: DIVMOD(q, r, v, 100000000000000000ULL); break;
+ case 18: DIVMOD(q, r, v, 1000000000000000000ULL); break;
+ case 19: DIVMOD(q, r, v, 10000000000000000000ULL); break; /* GCOV_NOT_REACHED */
+ }
+ }
+ }
+}
+
+/* END CONFIG_64 */
+#elif defined(CONFIG_32)
+#if defined(ANSI)
+#if !defined(LEGACY_COMPILER)
+static inline void
+_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b)
+{
+ mpd_uuint_t hl;
+
+ hl = (mpd_uuint_t)a * b;
+
+ *hi = hl >> 32;
+ *lo = (mpd_uint_t)hl;
+}
+
+static inline void
+_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo,
+ mpd_uint_t d)
+{
+ mpd_uuint_t hl;
+
+ hl = ((mpd_uuint_t)hi<<32) + lo;
+ *q = (mpd_uint_t)(hl / d); /* quotient is known to fit */
+ *r = (mpd_uint_t)(hl - (mpd_uuint_t)(*q) * d);
+}
+/* END ANSI + uint64_t */
+#else
+static inline void
+_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b)
+{
+ uint16_t w[4], carry;
+ uint16_t ah, al, bh, bl;
+ uint32_t hl;
+
+ ah = (uint16_t)(a>>16); al = (uint16_t)a;
+ bh = (uint16_t)(b>>16); bl = (uint16_t)b;
+
+ hl = (uint32_t)al * bl;
+ w[0] = (uint16_t)hl;
+ carry = (uint16_t)(hl>>16);
+
+ hl = (uint32_t)ah * bl + carry;
+ w[1] = (uint16_t)hl;
+ w[2] = (uint16_t)(hl>>16);
+
+ hl = (uint32_t)al * bh + w[1];
+ w[1] = (uint16_t)hl;
+ carry = (uint16_t)(hl>>16);
+
+ hl = ((uint32_t)ah * bh + w[2]) + carry;
+ w[2] = (uint16_t)hl;
+ w[3] = (uint16_t)(hl>>16);
+
+ *hi = ((uint32_t)w[3]<<16) + w[2];
+ *lo = ((uint32_t)w[1]<<16) + w[0];
+}
+
+/*
+ * By Henry S. Warren: http://www.hackersdelight.org/HDcode/divlu.c.txt
+ * http://www.hackersdelight.org/permissions.htm:
+ * "You are free to use, copy, and distribute any of the code on this web
+ * site, whether modified by you or not. You need not give attribution."
+ *
+ * Slightly modified, comments are mine.
+ */
+static inline int
+nlz(uint32_t x)
+{
+ int n;
+
+ if (x == 0) return(32);
+
+ n = 0;
+ if (x <= 0x0000FFFF) {n = n +16; x = x <<16;}
+ if (x <= 0x00FFFFFF) {n = n + 8; x = x << 8;}
+ if (x <= 0x0FFFFFFF) {n = n + 4; x = x << 4;}
+ if (x <= 0x3FFFFFFF) {n = n + 2; x = x << 2;}
+ if (x <= 0x7FFFFFFF) {n = n + 1;}
+
+ return n;
+}
+
+static inline void
+_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t u1, mpd_uint_t u0,
+ mpd_uint_t v)
+{
+ const mpd_uint_t b = 65536;
+ mpd_uint_t un1, un0,
+ vn1, vn0,
+ q1, q0,
+ un32, un21, un10,
+ rhat, t;
+ int s;
+
+ assert(u1 < v);
+
+ s = nlz(v);
+ v = v << s;
+ vn1 = v >> 16;
+ vn0 = v & 0xFFFF;
+
+ t = (s == 0) ? 0 : u0 >> (32 - s);
+ un32 = (u1 << s) | t;
+ un10 = u0 << s;
+
+ un1 = un10 >> 16;
+ un0 = un10 & 0xFFFF;
+
+ q1 = un32 / vn1;
+ rhat = un32 - q1*vn1;
+again1:
+ if (q1 >= b || q1*vn0 > b*rhat + un1) {
+ q1 = q1 - 1;
+ rhat = rhat + vn1;
+ if (rhat < b) goto again1;
+ }
+
+ /*
+ * Before again1 we had:
+ * (1) q1*vn1 + rhat = un32
+ * (2) q1*vn1*b + rhat*b + un1 = un32*b + un1
+ *
+ * The statements inside the if-clause do not change the value
+ * of the left-hand side of (2), and the loop is only exited
+ * if q1*vn0 <= rhat*b + un1, so:
+ *
+ * (3) q1*vn1*b + q1*vn0 <= un32*b + un1
+ * (4) q1*v <= un32*b + un1
+ * (5) 0 <= un32*b + un1 - q1*v
+ *
+ * By (5) we are certain that the possible add-back step from
+ * Knuth's algorithm D is never required.
+ *
+ * Since the final quotient is less than 2**32, the following
+ * must be true:
+ *
+ * (6) un32*b + un1 - q1*v <= UINT32_MAX
+ *
+ * This means that in the following line, the high words
+ * of un32*b and q1*v can be discarded without any effect
+ * on the result.
+ */
+ un21 = un32*b + un1 - q1*v;
+
+ q0 = un21 / vn1;
+ rhat = un21 - q0*vn1;
+again2:
+ if (q0 >= b || q0*vn0 > b*rhat + un0) {
+ q0 = q0 - 1;
+ rhat = rhat + vn1;
+ if (rhat < b) goto again2;
+ }
+
+ *q = q1*b + q0;
+ *r = (un21*b + un0 - q0*v) >> s;
+}
+#endif /* END ANSI + LEGACY_COMPILER */
+
+/* END ANSI */
+#elif defined(ASM)
+static inline void
+_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b)
+{
+ mpd_uint_t h, l;
+
+ asm ( "mull %3\n\t"
+ : "=d" (h), "=a" (l)
+ : "%a" (a), "rm" (b)
+ : "cc"
+ );
+
+ *hi = h;
+ *lo = l;
+}
+
+static inline void
+_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo,
+ mpd_uint_t d)
+{
+ mpd_uint_t qq, rr;
+
+ asm ( "divl %4\n\t"
+ : "=a" (qq), "=d" (rr)
+ : "a" (lo), "d" (hi), "rm" (d)
+ : "cc"
+ );
+
+ *q = qq;
+ *r = rr;
+}
+/* END GCC ASM */
+#elif defined(MASM)
+static inline void __cdecl
+_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b)
+{
+ mpd_uint_t h, l;
+
+ __asm {
+ mov eax, a
+ mul b
+ mov h, edx
+ mov l, eax
+ }
+
+ *hi = h;
+ *lo = l;
+}
+
+static inline void __cdecl
+_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo,
+ mpd_uint_t d)
+{
+ mpd_uint_t qq, rr;
+
+ __asm {
+ mov eax, lo
+ mov edx, hi
+ div d
+ mov qq, eax
+ mov rr, edx
+ }
+
+ *q = qq;
+ *r = rr;
+}
+/* END MASM (_MSC_VER) */
+#else
+ #error "need platform specific 64 bit multiplication and division"
+#endif
+
+#define DIVMOD(q, r, v, d) *q = v / d; *r = v - *q * d
+static inline void
+_mpd_divmod_pow10(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t v, mpd_uint_t exp)
+{
+ assert(exp <= 9);
+
+ if (exp <= 4) {
+ switch (exp) {
+ case 0: *q = v; *r = 0; break;
+ case 1: DIVMOD(q, r, v, 10UL); break;
+ case 2: DIVMOD(q, r, v, 100UL); break;
+ case 3: DIVMOD(q, r, v, 1000UL); break;
+ case 4: DIVMOD(q, r, v, 10000UL); break;
+ }
+ }
+ else {
+ switch (exp) {
+ case 5: DIVMOD(q, r, v, 100000UL); break;
+ case 6: DIVMOD(q, r, v, 1000000UL); break;
+ case 7: DIVMOD(q, r, v, 10000000UL); break;
+ case 8: DIVMOD(q, r, v, 100000000UL); break;
+ case 9: DIVMOD(q, r, v, 1000000000UL); break; /* GCOV_NOT_REACHED */
+ }
+ }
+}
+/* END CONFIG_32 */
+
+/* NO CONFIG */
+#else
+ #error "define CONFIG_64 or CONFIG_32"
+#endif /* CONFIG */
+
+
+static inline void
+_mpd_div_word(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t v, mpd_uint_t d)
+{
+ *q = v / d;
+ *r = v - *q * d;
+}
+
+static inline void
+_mpd_idiv_word(mpd_ssize_t *q, mpd_ssize_t *r, mpd_ssize_t v, mpd_ssize_t d)
+{
+ *q = v / d;
+ *r = v - *q * d;
+}
+
+
+/** ------------------------------------------------------------
+ ** Arithmetic with overflow checking
+ ** ------------------------------------------------------------
+ */
+
+/* The following macros do call exit() in case of an overflow.
+ If the library is used correctly (i.e. with valid context
+ parameters), such overflows cannot occur. The macros are used
+ as sanity checks in a couple of strategic places and should
+ be viewed as a handwritten version of gcc's -ftrapv option. */
+
+static inline mpd_size_t
+add_size_t(mpd_size_t a, mpd_size_t b)
+{
+ if (a > MPD_SIZE_MAX - b) {
+ mpd_err_fatal("add_size_t(): overflow: check the context"); /* GCOV_NOT_REACHED */
+ }
+ return a + b;
+}
+
+static inline mpd_size_t
+sub_size_t(mpd_size_t a, mpd_size_t b)
+{
+ if (b > a) {
+ mpd_err_fatal("sub_size_t(): overflow: check the context"); /* GCOV_NOT_REACHED */
+ }
+ return a - b;
+}
+
+#if MPD_SIZE_MAX != MPD_UINT_MAX
+ #error "adapt mul_size_t() and mulmod_size_t()"
+#endif
+
+static inline mpd_size_t
+mul_size_t(mpd_size_t a, mpd_size_t b)
+{
+ mpd_uint_t hi, lo;
+
+ _mpd_mul_words(&hi, &lo, (mpd_uint_t)a, (mpd_uint_t)b);
+ if (hi) {
+ mpd_err_fatal("mul_size_t(): overflow: check the context"); /* GCOV_NOT_REACHED */
+ }
+ return lo;
+}
+
+static inline mpd_size_t
+add_size_t_overflow(mpd_size_t a, mpd_size_t b, mpd_size_t *overflow)
+{
+ mpd_size_t ret;
+
+ *overflow = 0;
+ ret = a + b;
+ if (ret < a) *overflow = 1;
+ return ret;
+}
+
+static inline mpd_size_t
+mul_size_t_overflow(mpd_size_t a, mpd_size_t b, mpd_size_t *overflow)
+{
+ mpd_uint_t lo;
+
+ _mpd_mul_words((mpd_uint_t *)overflow, &lo, (mpd_uint_t)a,
+ (mpd_uint_t)b);
+ return lo;
+}
+
+static inline mpd_ssize_t
+mod_mpd_ssize_t(mpd_ssize_t a, mpd_ssize_t m)
+{
+ mpd_ssize_t r = a % m;
+ return (r < 0) ? r + m : r;
+}
+
+static inline mpd_size_t
+mulmod_size_t(mpd_size_t a, mpd_size_t b, mpd_size_t m)
+{
+ mpd_uint_t hi, lo;
+ mpd_uint_t q, r;
+
+ _mpd_mul_words(&hi, &lo, (mpd_uint_t)a, (mpd_uint_t)b);
+ _mpd_div_words(&q, &r, hi, lo, (mpd_uint_t)m);
+
+ return r;
+}
+
+
+#endif /* TYPEARITH_H */
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef UMODARITH_H
+#define UMODARITH_H
+
+
+#include "constants.h"
+#include "mpdecimal.h"
+#include "typearith.h"
+
+
+/* Bignum: Low level routines for unsigned modular arithmetic. These are
+ used in the fast convolution functions for very large coefficients. */
+
+
+/**************************************************************************/
+/* ANSI modular arithmetic */
+/**************************************************************************/
+
+
+/*
+ * Restrictions: a < m and b < m
+ * ACL2 proof: umodarith.lisp: addmod-correct
+ */
+static inline mpd_uint_t
+addmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m)
+{
+ mpd_uint_t s;
+
+ s = a + b;
+ s = (s < a) ? s - m : s;
+ s = (s >= m) ? s - m : s;
+
+ return s;
+}
+
+/*
+ * Restrictions: a < m and b < m
+ * ACL2 proof: umodarith.lisp: submod-2-correct
+ */
+static inline mpd_uint_t
+submod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m)
+{
+ mpd_uint_t d;
+
+ d = a - b;
+ d = (a < b) ? d + m : d;
+
+ return d;
+}
+
+/*
+ * Restrictions: a < 2m and b < 2m
+ * ACL2 proof: umodarith.lisp: section ext-submod
+ */
+static inline mpd_uint_t
+ext_submod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m)
+{
+ mpd_uint_t d;
+
+ a = (a >= m) ? a - m : a;
+ b = (b >= m) ? b - m : b;
+
+ d = a - b;
+ d = (a < b) ? d + m : d;
+
+ return d;
+}
+
+/*
+ * Reduce double word modulo m.
+ * Restrictions: m != 0
+ * ACL2 proof: umodarith.lisp: section dw-reduce
+ */
+static inline mpd_uint_t
+dw_reduce(mpd_uint_t hi, mpd_uint_t lo, mpd_uint_t m)
+{
+ mpd_uint_t r1, r2, w;
+
+ _mpd_div_word(&w, &r1, hi, m);
+ _mpd_div_words(&w, &r2, r1, lo, m);
+
+ return r2;
+}
+
+/*
+ * Subtract double word from a.
+ * Restrictions: a < m
+ * ACL2 proof: umodarith.lisp: section dw-submod
+ */
+static inline mpd_uint_t
+dw_submod(mpd_uint_t a, mpd_uint_t hi, mpd_uint_t lo, mpd_uint_t m)
+{
+ mpd_uint_t d, r;
+
+ r = dw_reduce(hi, lo, m);
+ d = a - r;
+ d = (a < r) ? d + m : d;
+
+ return d;
+}
+
+#ifdef CONFIG_64
+
+/**************************************************************************/
+/* 64-bit modular arithmetic */
+/**************************************************************************/
+
+/*
+ * A proof of the algorithm is in literature/mulmod-64.txt. An ACL2
+ * proof is in umodarith.lisp: section "Fast modular reduction".
+ *
+ * Algorithm: calculate (a * b) % p:
+ *
+ * a) hi, lo <- a * b # Calculate a * b.
+ *
+ * b) hi, lo <- R(hi, lo) # Reduce modulo p.
+ *
+ * c) Repeat step b) until 0 <= hi * 2**64 + lo < 2*p.
+ *
+ * d) If the result is less than p, return lo. Otherwise return lo - p.
+ */
+
+static inline mpd_uint_t
+x64_mulmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m)
+{
+ mpd_uint_t hi, lo, x, y;
+
+
+ _mpd_mul_words(&hi, &lo, a, b);
+
+ if (m & (1ULL<<32)) { /* P1 */
+
+ /* first reduction */
+ x = y = hi;
+ hi >>= 32;
+
+ x = lo - x;
+ if (x > lo) hi--;
+
+ y <<= 32;
+ lo = y + x;
+ if (lo < y) hi++;
+
+ /* second reduction */
+ x = y = hi;
+ hi >>= 32;
+
+ x = lo - x;
+ if (x > lo) hi--;
+
+ y <<= 32;
+ lo = y + x;
+ if (lo < y) hi++;
+
+ return (hi || lo >= m ? lo - m : lo);
+ }
+ else if (m & (1ULL<<34)) { /* P2 */
+
+ /* first reduction */
+ x = y = hi;
+ hi >>= 30;
+
+ x = lo - x;
+ if (x > lo) hi--;
+
+ y <<= 34;
+ lo = y + x;
+ if (lo < y) hi++;
+
+ /* second reduction */
+ x = y = hi;
+ hi >>= 30;
+
+ x = lo - x;
+ if (x > lo) hi--;
+
+ y <<= 34;
+ lo = y + x;
+ if (lo < y) hi++;
+
+ /* third reduction */
+ x = y = hi;
+ hi >>= 30;
+
+ x = lo - x;
+ if (x > lo) hi--;
+
+ y <<= 34;
+ lo = y + x;
+ if (lo < y) hi++;
+
+ return (hi || lo >= m ? lo - m : lo);
+ }
+ else { /* P3 */
+
+ /* first reduction */
+ x = y = hi;
+ hi >>= 24;
+
+ x = lo - x;
+ if (x > lo) hi--;
+
+ y <<= 40;
+ lo = y + x;
+ if (lo < y) hi++;
+
+ /* second reduction */
+ x = y = hi;
+ hi >>= 24;
+
+ x = lo - x;
+ if (x > lo) hi--;
+
+ y <<= 40;
+ lo = y + x;
+ if (lo < y) hi++;
+
+ /* third reduction */
+ x = y = hi;
+ hi >>= 24;
+
+ x = lo - x;
+ if (x > lo) hi--;
+
+ y <<= 40;
+ lo = y + x;
+ if (lo < y) hi++;
+
+ return (hi || lo >= m ? lo - m : lo);
+ }
+}
+
+static inline void
+x64_mulmod2c(mpd_uint_t *a, mpd_uint_t *b, mpd_uint_t w, mpd_uint_t m)
+{
+ *a = x64_mulmod(*a, w, m);
+ *b = x64_mulmod(*b, w, m);
+}
+
+static inline void
+x64_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1,
+ mpd_uint_t m)
+{
+ *a0 = x64_mulmod(*a0, b0, m);
+ *a1 = x64_mulmod(*a1, b1, m);
+}
+
+static inline mpd_uint_t
+x64_powmod(mpd_uint_t base, mpd_uint_t exp, mpd_uint_t umod)
+{
+ mpd_uint_t r = 1;
+
+ while (exp > 0) {
+ if (exp & 1)
+ r = x64_mulmod(r, base, umod);
+ base = x64_mulmod(base, base, umod);
+ exp >>= 1;
+ }
+
+ return r;
+}
+
+/* END CONFIG_64 */
+#else /* CONFIG_32 */
+
+
+/**************************************************************************/
+/* 32-bit modular arithmetic */
+/**************************************************************************/
+
+#if defined(ANSI)
+#if !defined(LEGACY_COMPILER)
+/* HAVE_UINT64_T */
+static inline mpd_uint_t
+std_mulmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m)
+{
+ return ((mpd_uuint_t) a * b) % m;
+}
+
+static inline void
+std_mulmod2c(mpd_uint_t *a, mpd_uint_t *b, mpd_uint_t w, mpd_uint_t m)
+{
+ *a = ((mpd_uuint_t) *a * w) % m;
+ *b = ((mpd_uuint_t) *b * w) % m;
+}
+
+static inline void
+std_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1,
+ mpd_uint_t m)
+{
+ *a0 = ((mpd_uuint_t) *a0 * b0) % m;
+ *a1 = ((mpd_uuint_t) *a1 * b1) % m;
+}
+/* END HAVE_UINT64_T */
+#else
+/* LEGACY_COMPILER */
+static inline mpd_uint_t
+std_mulmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m)
+{
+ mpd_uint_t hi, lo, q, r;
+ _mpd_mul_words(&hi, &lo, a, b);
+ _mpd_div_words(&q, &r, hi, lo, m);
+ return r;
+}
+
+static inline void
+std_mulmod2c(mpd_uint_t *a, mpd_uint_t *b, mpd_uint_t w, mpd_uint_t m)
+{
+ *a = std_mulmod(*a, w, m);
+ *b = std_mulmod(*b, w, m);
+}
+
+static inline void
+std_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1,
+ mpd_uint_t m)
+{
+ *a0 = std_mulmod(*a0, b0, m);
+ *a1 = std_mulmod(*a1, b1, m);
+}
+/* END LEGACY_COMPILER */
+#endif
+
+static inline mpd_uint_t
+std_powmod(mpd_uint_t base, mpd_uint_t exp, mpd_uint_t umod)
+{
+ mpd_uint_t r = 1;
+
+ while (exp > 0) {
+ if (exp & 1)
+ r = std_mulmod(r, base, umod);
+ base = std_mulmod(base, base, umod);
+ exp >>= 1;
+ }
+
+ return r;
+}
+#endif /* ANSI CONFIG_32 */
+
+
+/**************************************************************************/
+/* Pentium Pro modular arithmetic */
+/**************************************************************************/
+
+/*
+ * A proof of the algorithm is in literature/mulmod-ppro.txt. The FPU
+ * control word must be set to 64-bit precision and truncation mode
+ * prior to using these functions.
+ *
+ * Algorithm: calculate (a * b) % p:
+ *
+ * p := prime < 2**31
+ * pinv := (long double)1.0 / p (precalculated)
+ *
+ * a) n = a * b # Calculate exact product.
+ * b) qest = n * pinv # Calculate estimate for q = n / p.
+ * c) q = (qest+2**63)-2**63 # Truncate qest to the exact quotient.
+ * d) r = n - q * p # Calculate remainder.
+ *
+ * Remarks:
+ *
+ * - p = dmod and pinv = dinvmod.
+ * - dinvmod points to an array of three uint32_t, which is interpreted
+ * as an 80 bit long double by fldt.
+ * - Intel compilers prior to version 11 do not seem to handle the
+ * __GNUC__ inline assembly correctly.
+ * - random tests are provided in tests/extended/ppro_mulmod.c
+ */
+
+#if defined(PPRO)
+#if defined(ASM)
+
+/* Return (a * b) % dmod */
+static inline mpd_uint_t
+ppro_mulmod(mpd_uint_t a, mpd_uint_t b, double *dmod, uint32_t *dinvmod)
+{
+ mpd_uint_t retval;
+
+ asm (
+ "fildl %2\n\t"
+ "fildl %1\n\t"
+ "fmulp %%st, %%st(1)\n\t"
+ "fldt (%4)\n\t"
+ "fmul %%st(1), %%st\n\t"
+ "flds %5\n\t"
+ "fadd %%st, %%st(1)\n\t"
+ "fsubrp %%st, %%st(1)\n\t"
+ "fldl (%3)\n\t"
+ "fmulp %%st, %%st(1)\n\t"
+ "fsubrp %%st, %%st(1)\n\t"
+ "fistpl %0\n\t"
+ : "=m" (retval)
+ : "m" (a), "m" (b), "r" (dmod), "r" (dinvmod), "m" (MPD_TWO63)
+ : "st", "memory"
+ );
+
+ return retval;
+}
+
+/*
+ * Two modular multiplications in parallel:
+ * *a0 = (*a0 * w) % dmod
+ * *a1 = (*a1 * w) % dmod
+ */
+static inline void
+ppro_mulmod2c(mpd_uint_t *a0, mpd_uint_t *a1, mpd_uint_t w,
+ double *dmod, uint32_t *dinvmod)
+{
+ asm (
+ "fildl %2\n\t"
+ "fildl (%1)\n\t"
+ "fmul %%st(1), %%st\n\t"
+ "fxch %%st(1)\n\t"
+ "fildl (%0)\n\t"
+ "fmulp %%st, %%st(1) \n\t"
+ "fldt (%4)\n\t"
+ "flds %5\n\t"
+ "fld %%st(2)\n\t"
+ "fmul %%st(2)\n\t"
+ "fadd %%st(1)\n\t"
+ "fsub %%st(1)\n\t"
+ "fmull (%3)\n\t"
+ "fsubrp %%st, %%st(3)\n\t"
+ "fxch %%st(2)\n\t"
+ "fistpl (%0)\n\t"
+ "fmul %%st(2)\n\t"
+ "fadd %%st(1)\n\t"
+ "fsubp %%st, %%st(1)\n\t"
+ "fmull (%3)\n\t"
+ "fsubrp %%st, %%st(1)\n\t"
+ "fistpl (%1)\n\t"
+ : : "r" (a0), "r" (a1), "m" (w),
+ "r" (dmod), "r" (dinvmod),
+ "m" (MPD_TWO63)
+ : "st", "memory"
+ );
+}
+
+/*
+ * Two modular multiplications in parallel:
+ * *a0 = (*a0 * b0) % dmod
+ * *a1 = (*a1 * b1) % dmod
+ */
+static inline void
+ppro_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1,
+ double *dmod, uint32_t *dinvmod)
+{
+ asm (
+ "fildl %3\n\t"
+ "fildl (%2)\n\t"
+ "fmulp %%st, %%st(1)\n\t"
+ "fildl %1\n\t"
+ "fildl (%0)\n\t"
+ "fmulp %%st, %%st(1)\n\t"
+ "fldt (%5)\n\t"
+ "fld %%st(2)\n\t"
+ "fmul %%st(1), %%st\n\t"
+ "fxch %%st(1)\n\t"
+ "fmul %%st(2), %%st\n\t"
+ "flds %6\n\t"
+ "fldl (%4)\n\t"
+ "fxch %%st(3)\n\t"
+ "fadd %%st(1), %%st\n\t"
+ "fxch %%st(2)\n\t"
+ "fadd %%st(1), %%st\n\t"
+ "fxch %%st(2)\n\t"
+ "fsub %%st(1), %%st\n\t"
+ "fxch %%st(2)\n\t"
+ "fsubp %%st, %%st(1)\n\t"
+ "fxch %%st(1)\n\t"
+ "fmul %%st(2), %%st\n\t"
+ "fxch %%st(1)\n\t"
+ "fmulp %%st, %%st(2)\n\t"
+ "fsubrp %%st, %%st(3)\n\t"
+ "fsubrp %%st, %%st(1)\n\t"
+ "fxch %%st(1)\n\t"
+ "fistpl (%2)\n\t"
+ "fistpl (%0)\n\t"
+ : : "r" (a0), "m" (b0), "r" (a1), "m" (b1),
+ "r" (dmod), "r" (dinvmod),
+ "m" (MPD_TWO63)
+ : "st", "memory"
+ );
+}
+/* END PPRO GCC ASM */
+#elif defined(MASM)
+
+/* Return (a * b) % dmod */
+static inline mpd_uint_t __cdecl
+ppro_mulmod(mpd_uint_t a, mpd_uint_t b, double *dmod, uint32_t *dinvmod)
+{
+ mpd_uint_t retval;
+
+ __asm {
+ mov eax, dinvmod
+ mov edx, dmod
+ fild b
+ fild a
+ fmulp st(1), st
+ fld TBYTE PTR [eax]
+ fmul st, st(1)
+ fld MPD_TWO63
+ fadd st(1), st
+ fsubp st(1), st
+ fld QWORD PTR [edx]
+ fmulp st(1), st
+ fsubp st(1), st
+ fistp retval
+ }
+
+ return retval;
+}
+
+/*
+ * Two modular multiplications in parallel:
+ * *a0 = (*a0 * w) % dmod
+ * *a1 = (*a1 * w) % dmod
+ */
+static inline mpd_uint_t __cdecl
+ppro_mulmod2c(mpd_uint_t *a0, mpd_uint_t *a1, mpd_uint_t w,
+ double *dmod, uint32_t *dinvmod)
+{
+ __asm {
+ mov ecx, dmod
+ mov edx, a1
+ mov ebx, dinvmod
+ mov eax, a0
+ fild w
+ fild DWORD PTR [edx]
+ fmul st, st(1)
+ fxch st(1)
+ fild DWORD PTR [eax]
+ fmulp st(1), st
+ fld TBYTE PTR [ebx]
+ fld MPD_TWO63
+ fld st(2)
+ fmul st, st(2)
+ fadd st, st(1)
+ fsub st, st(1)
+ fmul QWORD PTR [ecx]
+ fsubp st(3), st
+ fxch st(2)
+ fistp DWORD PTR [eax]
+ fmul st, st(2)
+ fadd st, st(1)
+ fsubrp st(1), st
+ fmul QWORD PTR [ecx]
+ fsubp st(1), st
+ fistp DWORD PTR [edx]
+ }
+}
+
+/*
+ * Two modular multiplications in parallel:
+ * *a0 = (*a0 * b0) % dmod
+ * *a1 = (*a1 * b1) % dmod
+ */
+static inline void __cdecl
+ppro_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1,
+ double *dmod, uint32_t *dinvmod)
+{
+ __asm {
+ mov ecx, dmod
+ mov edx, a1
+ mov ebx, dinvmod
+ mov eax, a0
+ fild b1
+ fild DWORD PTR [edx]
+ fmulp st(1), st
+ fild b0
+ fild DWORD PTR [eax]
+ fmulp st(1), st
+ fld TBYTE PTR [ebx]
+ fld st(2)
+ fmul st, st(1)
+ fxch st(1)
+ fmul st, st(2)
+ fld DWORD PTR MPD_TWO63
+ fld QWORD PTR [ecx]
+ fxch st(3)
+ fadd st, st(1)
+ fxch st(2)
+ fadd st, st(1)
+ fxch st(2)
+ fsub st, st(1)
+ fxch st(2)
+ fsubrp st(1), st
+ fxch st(1)
+ fmul st, st(2)
+ fxch st(1)
+ fmulp st(2), st
+ fsubp st(3), st
+ fsubp st(1), st
+ fxch st(1)
+ fistp DWORD PTR [edx]
+ fistp DWORD PTR [eax]
+ }
+}
+#endif /* PPRO MASM (_MSC_VER) */
+
+
+/* Return (base ** exp) % dmod */
+static inline mpd_uint_t
+ppro_powmod(mpd_uint_t base, mpd_uint_t exp, double *dmod, uint32_t *dinvmod)
+{
+ mpd_uint_t r = 1;
+
+ while (exp > 0) {
+ if (exp & 1)
+ r = ppro_mulmod(r, base, dmod, dinvmod);
+ base = ppro_mulmod(base, base, dmod, dinvmod);
+ exp >>= 1;
+ }
+
+ return r;
+}
+#endif /* PPRO */
+#endif /* CONFIG_32 */
+
+
+#endif /* UMODARITH_H */
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef VCCOMPAT_H
+#define VCCOMPAT_H
+
+
+/* Visual C fixes: no stdint.h, no snprintf ... */
+#ifdef _MSC_VER
+ #include "vcstdint.h"
+ #undef inline
+ #define inline __inline
+ #undef random
+ #define random rand
+ #undef srandom
+ #define srandom srand
+ #undef snprintf
+ #define snprintf sprintf_s
+ #define HAVE_SNPRINTF
+ #undef strncasecmp
+ #define strncasecmp _strnicmp
+ #undef strcasecmp
+ #define strcasecmp _stricmp
+ #undef strtoll
+ #define strtoll _strtoi64
+ #define strdup _strdup
+ #define PRIi64 "I64i"
+ #define PRIu64 "I64u"
+ #define PRIi32 "I32i"
+ #define PRIu32 "I32u"
+#endif
+
+
+#endif /* VCCOMPAT_H */
+
+
+
--- /dev/null
+;
+; Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+;
+; Redistribution and use in source and binary forms, with or without
+; modification, are permitted provided that the following conditions
+; are met:
+;
+; 1. Redistributions of source code must retain the above copyright
+; notice, this list of conditions and the following disclaimer.
+;
+; 2. Redistributions in binary form must reproduce the above copyright
+; notice, this list of conditions and the following disclaimer in the
+; documentation and/or other materials provided with the distribution.
+;
+; THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+; SUCH DAMAGE.
+;
+
+
+PUBLIC _mpd_div_words
+_TEXT SEGMENT
+q$ = 8
+r$ = 16
+hi$ = 24
+lo$ = 32
+d$ = 40
+_mpd_div_words PROC
+ mov r10, rdx
+ mov rdx, r8
+ mov rax, r9
+ div QWORD PTR d$[rsp]
+ mov QWORD PTR [r10], rdx
+ mov QWORD PTR [rcx], rax
+ ret 0
+_mpd_div_words ENDP
+_TEXT ENDS
+END
+
+
--- /dev/null
+// ISO C9x compliant stdint.h for Microsoft Visual Studio
+// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
+//
+// Copyright (c) 2006-2008 Alexander Chemeris
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// 3. The name of the author may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef _MSC_VER // [
+#error "Use this header only with Microsoft Visual C++ compilers!"
+#endif // _MSC_VER ]
+
+#ifndef _MSC_STDINT_H_ // [
+#define _MSC_STDINT_H_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#include <limits.h>
+
+// For Visual Studio 6 in C++ mode wrap <wchar.h> include with 'extern "C++" {}'
+// or compiler give many errors like this:
+// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
+#if (_MSC_VER < 1300) && defined(__cplusplus)
+ extern "C++" {
+#endif
+# include <wchar.h>
+#if (_MSC_VER < 1300) && defined(__cplusplus)
+ }
+#endif
+
+// Define _W64 macros to mark types changing their size, like intptr_t.
+#ifndef _W64
+# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
+# define _W64 __w64
+# else
+# define _W64
+# endif
+#endif
+
+
+// 7.18.1 Integer types
+
+// 7.18.1.1 Exact-width integer types
+typedef __int8 int8_t;
+typedef __int16 int16_t;
+typedef __int32 int32_t;
+typedef __int64 int64_t;
+typedef unsigned __int8 uint8_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+
+// 7.18.1.2 Minimum-width integer types
+typedef int8_t int_least8_t;
+typedef int16_t int_least16_t;
+typedef int32_t int_least32_t;
+typedef int64_t int_least64_t;
+typedef uint8_t uint_least8_t;
+typedef uint16_t uint_least16_t;
+typedef uint32_t uint_least32_t;
+typedef uint64_t uint_least64_t;
+
+// 7.18.1.3 Fastest minimum-width integer types
+typedef int8_t int_fast8_t;
+typedef int16_t int_fast16_t;
+typedef int32_t int_fast32_t;
+typedef int64_t int_fast64_t;
+typedef uint8_t uint_fast8_t;
+typedef uint16_t uint_fast16_t;
+typedef uint32_t uint_fast32_t;
+typedef uint64_t uint_fast64_t;
+
+// 7.18.1.4 Integer types capable of holding object pointers
+#ifdef _WIN64 // [
+ typedef __int64 intptr_t;
+ typedef unsigned __int64 uintptr_t;
+#else // _WIN64 ][
+ typedef _W64 int intptr_t;
+ typedef _W64 unsigned int uintptr_t;
+#endif // _WIN64 ]
+
+// 7.18.1.5 Greatest-width integer types
+typedef int64_t intmax_t;
+typedef uint64_t uintmax_t;
+
+
+// 7.18.2 Limits of specified-width integer types
+
+#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
+
+// 7.18.2.1 Limits of exact-width integer types
+#define INT8_MIN ((int8_t)_I8_MIN)
+#define INT8_MAX _I8_MAX
+#define INT16_MIN ((int16_t)_I16_MIN)
+#define INT16_MAX _I16_MAX
+#define INT32_MIN ((int32_t)_I32_MIN)
+#define INT32_MAX _I32_MAX
+#define INT64_MIN ((int64_t)_I64_MIN)
+#define INT64_MAX _I64_MAX
+#define UINT8_MAX _UI8_MAX
+#define UINT16_MAX _UI16_MAX
+#define UINT32_MAX _UI32_MAX
+#define UINT64_MAX _UI64_MAX
+
+// 7.18.2.2 Limits of minimum-width integer types
+#define INT_LEAST8_MIN INT8_MIN
+#define INT_LEAST8_MAX INT8_MAX
+#define INT_LEAST16_MIN INT16_MIN
+#define INT_LEAST16_MAX INT16_MAX
+#define INT_LEAST32_MIN INT32_MIN
+#define INT_LEAST32_MAX INT32_MAX
+#define INT_LEAST64_MIN INT64_MIN
+#define INT_LEAST64_MAX INT64_MAX
+#define UINT_LEAST8_MAX UINT8_MAX
+#define UINT_LEAST16_MAX UINT16_MAX
+#define UINT_LEAST32_MAX UINT32_MAX
+#define UINT_LEAST64_MAX UINT64_MAX
+
+// 7.18.2.3 Limits of fastest minimum-width integer types
+#define INT_FAST8_MIN INT8_MIN
+#define INT_FAST8_MAX INT8_MAX
+#define INT_FAST16_MIN INT16_MIN
+#define INT_FAST16_MAX INT16_MAX
+#define INT_FAST32_MIN INT32_MIN
+#define INT_FAST32_MAX INT32_MAX
+#define INT_FAST64_MIN INT64_MIN
+#define INT_FAST64_MAX INT64_MAX
+#define UINT_FAST8_MAX UINT8_MAX
+#define UINT_FAST16_MAX UINT16_MAX
+#define UINT_FAST32_MAX UINT32_MAX
+#define UINT_FAST64_MAX UINT64_MAX
+
+// 7.18.2.4 Limits of integer types capable of holding object pointers
+#ifdef _WIN64 // [
+# define INTPTR_MIN INT64_MIN
+# define INTPTR_MAX INT64_MAX
+# define UINTPTR_MAX UINT64_MAX
+#else // _WIN64 ][
+# define INTPTR_MIN INT32_MIN
+# define INTPTR_MAX INT32_MAX
+# define UINTPTR_MAX UINT32_MAX
+#endif // _WIN64 ]
+
+// 7.18.2.5 Limits of greatest-width integer types
+#define INTMAX_MIN INT64_MIN
+#define INTMAX_MAX INT64_MAX
+#define UINTMAX_MAX UINT64_MAX
+
+// 7.18.3 Limits of other integer types
+
+#ifdef _WIN64 // [
+# define PTRDIFF_MIN _I64_MIN
+# define PTRDIFF_MAX _I64_MAX
+#else // _WIN64 ][
+# define PTRDIFF_MIN _I32_MIN
+# define PTRDIFF_MAX _I32_MAX
+#endif // _WIN64 ]
+
+#define SIG_ATOMIC_MIN INT_MIN
+#define SIG_ATOMIC_MAX INT_MAX
+
+#ifndef SIZE_MAX // [
+# ifdef _WIN64 // [
+# define SIZE_MAX _UI64_MAX
+# else // _WIN64 ][
+# define SIZE_MAX _UI32_MAX
+# endif // _WIN64 ]
+#endif // SIZE_MAX ]
+
+// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
+#ifndef WCHAR_MIN // [
+# define WCHAR_MIN 0
+#endif // WCHAR_MIN ]
+#ifndef WCHAR_MAX // [
+# define WCHAR_MAX _UI16_MAX
+#endif // WCHAR_MAX ]
+
+#define WINT_MIN 0
+#define WINT_MAX _UI16_MAX
+
+#endif // __STDC_LIMIT_MACROS ]
+
+
+// 7.18.4 Limits of other integer types
+
+#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
+
+// 7.18.4.1 Macros for minimum-width integer constants
+
+#define INT8_C(val) val##i8
+#define INT16_C(val) val##i16
+#define INT32_C(val) val##i32
+#define INT64_C(val) val##i64
+
+#define UINT8_C(val) val##ui8
+#define UINT16_C(val) val##ui16
+#define UINT32_C(val) val##ui32
+#define UINT64_C(val) val##ui64
+
+// 7.18.4.2 Macros for greatest-width integer constants
+#define INTMAX_C INT64_C
+#define UINTMAX_C UINT64_C
+
+#endif // __STDC_CONSTANT_MACROS ]
+
+
+#endif // _MSC_STDINT_H_ ]
--- /dev/null
+
+
+This directory contains extended tests and a benchmark against decimal.py:
+
+ bench.py -> Benchmark for small and large precisions.
+ Usage: ../../../python bench.py
+
+ formathelper.py ->
+ randdec.py -> Generate test cases for deccheck.py.
+ randfloat.py ->
+
+ deccheck.py -> Run extended tests.
+ Usage: ../../../python deccheck.py [--short|--medium|--long|--all]
+
+
--- /dev/null
+#!/usr/bin/env python
+
+#
+# Copyright (C) 2001-2012 Python Software Foundation. All Rights Reserved.
+# Modified and extended by Stefan Krah.
+#
+
+# Usage: ../../../python bench.py
+
+
+import time
+from math import log, ceil
+from test.support import import_fresh_module
+
+C = import_fresh_module('decimal', fresh=['_decimal'])
+P = import_fresh_module('decimal', blocked=['_decimal'])
+
+
+# Pi function from the decimal.py documentation
+def pi_float(prec):
+ """native float"""
+ lasts, t, s, n, na, d, da = 0, 3.0, 3, 1, 0, 0, 24
+ while s != lasts:
+ lasts = s
+ n, na = n+na, na+8
+ d, da = d+da, da+32
+ t = (t * n) / d
+ s += t
+ return s
+
+def pi_cdecimal(prec):
+ """cdecimal"""
+ C.getcontext().prec = prec
+ D = C.Decimal
+ lasts, t, s, n, na, d, da = D(0), D(3), D(3), D(1), D(0), D(0), D(24)
+ while s != lasts:
+ lasts = s
+ n, na = n+na, na+8
+ d, da = d+da, da+32
+ t = (t * n) / d
+ s += t
+ return s
+
+def pi_decimal(prec):
+ """decimal"""
+ P.getcontext().prec = prec
+ D = P.Decimal
+ lasts, t, s, n, na, d, da = D(0), D(3), D(3), D(1), D(0), D(0), D(24)
+ while s != lasts:
+ lasts = s
+ n, na = n+na, na+8
+ d, da = d+da, da+32
+ t = (t * n) / d
+ s += t
+ return s
+
+def factorial(n, m):
+ if (n > m):
+ return factorial(m, n)
+ elif m == 0:
+ return 1
+ elif n == m:
+ return n
+ else:
+ return factorial(n, (n+m)//2) * factorial((n+m)//2 + 1, m)
+
+
+print("\n# ======================================================================")
+print("# Calculating pi, 10000 iterations")
+print("# ======================================================================\n")
+
+for prec in [9, 19]:
+ print("\nPrecision: %d decimal digits\n" % prec)
+ for func in [pi_float, pi_cdecimal, pi_decimal]:
+ start = time.time()
+ for i in range(10000):
+ x = func(prec)
+ print("%s:" % func.__name__.replace("pi_", ""))
+ print("result: %s" % str(x))
+ print("time: %fs\n" % (time.time()-start))
+
+
+print("\n# ======================================================================")
+print("# Factorial")
+print("# ======================================================================\n")
+
+C.getcontext().prec = C.MAX_PREC
+
+for n in [100000, 1000000]:
+
+ print("n = %d\n" % n)
+
+ # C version of decimal
+ start_calc = time.time()
+ x = factorial(C.Decimal(n), 0)
+ end_calc = time.time()
+ start_conv = time.time()
+ sx = str(x)
+ end_conv = time.time()
+ print("cdecimal:")
+ print("calculation time: %fs" % (end_calc-start_calc))
+ print("conversion time: %fs\n" % (end_conv-start_conv))
+
+ # Python integers
+ start_calc = time.time()
+ y = factorial(n, 0)
+ end_calc = time.time()
+ start_conv = time.time()
+ sy = str(y)
+ end_conv = time.time()
+
+ print("int:")
+ print("calculation time: %fs" % (end_calc-start_calc))
+ print("conversion time: %fs\n\n" % (end_conv-start_conv))
+
+ assert(sx == sy)
--- /dev/null
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+#
+# Usage: python deccheck.py [--short|--medium|--long|--all]
+#
+
+import sys, random
+from copy import copy
+from collections import defaultdict
+from test.support import import_fresh_module
+from randdec import randfloat, all_unary, all_binary, all_ternary
+from formathelper import rand_format, rand_locale
+
+C = import_fresh_module('decimal', fresh=['_decimal'])
+P = import_fresh_module('decimal', blocked=['_decimal'])
+EXIT_STATUS = 0
+
+
+# Contains all categories of Decimal methods.
+Functions = {
+ # Plain unary:
+ 'unary': (
+ '__abs__', '__bool__', '__ceil__', '__complex__', '__copy__',
+ '__floor__', '__float__', '__hash__', '__int__', '__neg__',
+ '__pos__', '__reduce__', '__repr__', '__str__', '__trunc__',
+ 'adjusted', 'as_tuple', 'canonical', 'conjugate', 'copy_abs',
+ 'copy_negate', 'is_canonical', 'is_finite', 'is_infinite',
+ 'is_nan', 'is_qnan', 'is_signed', 'is_snan', 'is_zero', 'radix'
+ ),
+ # Unary with optional context:
+ 'unary_ctx': (
+ 'exp', 'is_normal', 'is_subnormal', 'ln', 'log10', 'logb',
+ 'logical_invert', 'next_minus', 'next_plus', 'normalize',
+ 'number_class', 'sqrt', 'to_eng_string'
+ ),
+ # Unary with optional rounding mode and context:
+ 'unary_rnd_ctx': ('to_integral', 'to_integral_exact', 'to_integral_value'),
+ # Plain binary:
+ 'binary': (
+ '__add__', '__divmod__', '__eq__', '__floordiv__', '__ge__', '__gt__',
+ '__le__', '__lt__', '__mod__', '__mul__', '__ne__', '__pow__',
+ '__radd__', '__rdivmod__', '__rfloordiv__', '__rmod__', '__rmul__',
+ '__rpow__', '__rsub__', '__rtruediv__', '__sub__', '__truediv__',
+ 'compare_total', 'compare_total_mag', 'copy_sign', 'quantize',
+ 'same_quantum'
+ ),
+ # Binary with optional context:
+ 'binary_ctx': (
+ 'compare', 'compare_signal', 'logical_and', 'logical_or', 'logical_xor',
+ 'max', 'max_mag', 'min', 'min_mag', 'next_toward', 'remainder_near',
+ 'rotate', 'scaleb', 'shift'
+ ),
+ # Plain ternary:
+ 'ternary': ('__pow__',),
+ # Ternary with optional context:
+ 'ternary_ctx': ('fma',),
+ # Special:
+ 'special': ('__format__', '__reduce_ex__', '__round__', 'from_float',
+ 'quantize'),
+ # Properties:
+ 'property': ('real', 'imag')
+}
+
+# Contains all categories of Context methods. The n-ary classification
+# applies to the number of Decimal arguments.
+ContextFunctions = {
+ # Plain nullary:
+ 'nullary': ('context.__hash__', 'context.__reduce__', 'context.radix'),
+ # Plain unary:
+ 'unary': ('context.abs', 'context.canonical', 'context.copy_abs',
+ 'context.copy_decimal', 'context.copy_negate',
+ 'context.create_decimal', 'context.exp', 'context.is_canonical',
+ 'context.is_finite', 'context.is_infinite', 'context.is_nan',
+ 'context.is_normal', 'context.is_qnan', 'context.is_signed',
+ 'context.is_snan', 'context.is_subnormal', 'context.is_zero',
+ 'context.ln', 'context.log10', 'context.logb',
+ 'context.logical_invert', 'context.minus', 'context.next_minus',
+ 'context.next_plus', 'context.normalize', 'context.number_class',
+ 'context.plus', 'context.sqrt', 'context.to_eng_string',
+ 'context.to_integral', 'context.to_integral_exact',
+ 'context.to_integral_value', 'context.to_sci_string'
+ ),
+ # Plain binary:
+ 'binary': ('context.add', 'context.compare', 'context.compare_signal',
+ 'context.compare_total', 'context.compare_total_mag',
+ 'context.copy_sign', 'context.divide', 'context.divide_int',
+ 'context.divmod', 'context.logical_and', 'context.logical_or',
+ 'context.logical_xor', 'context.max', 'context.max_mag',
+ 'context.min', 'context.min_mag', 'context.multiply',
+ 'context.next_toward', 'context.power', 'context.quantize',
+ 'context.remainder', 'context.remainder_near', 'context.rotate',
+ 'context.same_quantum', 'context.scaleb', 'context.shift',
+ 'context.subtract'
+ ),
+ # Plain ternary:
+ 'ternary': ('context.fma', 'context.power'),
+ # Special:
+ 'special': ('context.__reduce_ex__', 'context.create_decimal_from_float')
+}
+
+# Functions that require a restricted exponent range for reasonable runtimes.
+UnaryRestricted = [
+ '__ceil__', '__floor__', '__int__', '__long__', '__trunc__',
+ 'to_integral', 'to_integral_value'
+]
+
+BinaryRestricted = ['__round__']
+
+TernaryRestricted = ['__pow__', 'context.power']
+
+
+# ======================================================================
+# Unified Context
+# ======================================================================
+
+# Translate symbols.
+CondMap = {
+ C.Clamped: P.Clamped,
+ C.ConversionSyntax: P.ConversionSyntax,
+ C.DivisionByZero: P.DivisionByZero,
+ C.DivisionImpossible: P.InvalidOperation,
+ C.DivisionUndefined: P.DivisionUndefined,
+ C.Inexact: P.Inexact,
+ C.InvalidContext: P.InvalidContext,
+ C.InvalidOperation: P.InvalidOperation,
+ C.Overflow: P.Overflow,
+ C.Rounded: P.Rounded,
+ C.Subnormal: P.Subnormal,
+ C.Underflow: P.Underflow,
+ C.FloatOperation: P.FloatOperation,
+}
+
+RoundMap = {
+ C.ROUND_UP: P.ROUND_UP,
+ C.ROUND_DOWN: P.ROUND_DOWN,
+ C.ROUND_CEILING: P.ROUND_CEILING,
+ C.ROUND_FLOOR: P.ROUND_FLOOR,
+ C.ROUND_HALF_UP: P.ROUND_HALF_UP,
+ C.ROUND_HALF_DOWN: P.ROUND_HALF_DOWN,
+ C.ROUND_HALF_EVEN: P.ROUND_HALF_EVEN,
+ C.ROUND_05UP: P.ROUND_05UP
+}
+RoundModes = RoundMap.items()
+
+
+class Context(object):
+ """Provides a convenient way of syncing the C and P contexts"""
+
+ __slots__ = ['c', 'p']
+
+ def __init__(self, c_ctx=None, p_ctx=None):
+ """Initialization is from the C context"""
+ self.c = C.getcontext() if c_ctx is None else c_ctx
+ self.p = P.getcontext() if p_ctx is None else p_ctx
+ self.p.prec = self.c.prec
+ self.p.Emin = self.c.Emin
+ self.p.Emax = self.c.Emax
+ self.p.rounding = RoundMap[self.c.rounding]
+ self.p.capitals = self.c.capitals
+ self.settraps([sig for sig in self.c.traps if self.c.traps[sig]])
+ self.setstatus([sig for sig in self.c.flags if self.c.flags[sig]])
+ self.p.clamp = self.c.clamp
+
+ def __str__(self):
+ return str(self.c) + '\n' + str(self.p)
+
+ def getprec(self):
+ assert(self.c.prec == self.p.prec)
+ return self.c.prec
+
+ def setprec(self, val):
+ self.c.prec = val
+ self.p.prec = val
+
+ def getemin(self):
+ assert(self.c.Emin == self.p.Emin)
+ return self.c.Emin
+
+ def setemin(self, val):
+ self.c.Emin = val
+ self.p.Emin = val
+
+ def getemax(self):
+ assert(self.c.Emax == self.p.Emax)
+ return self.c.Emax
+
+ def setemax(self, val):
+ self.c.Emax = val
+ self.p.Emax = val
+
+ def getround(self):
+ assert(self.c.rounding == RoundMap[self.p.rounding])
+ return self.c.rounding
+
+ def setround(self, val):
+ self.c.rounding = val
+ self.p.rounding = RoundMap[val]
+
+ def getcapitals(self):
+ assert(self.c.capitals == self.p.capitals)
+ return self.c.capitals
+
+ def setcapitals(self, val):
+ self.c.capitals = val
+ self.p.capitals = val
+
+ def getclamp(self):
+ assert(self.c.clamp == self.p.clamp)
+ return self.c.clamp
+
+ def setclamp(self, val):
+ self.c.clamp = val
+ self.p.clamp = val
+
+ prec = property(getprec, setprec)
+ Emin = property(getemin, setemin)
+ Emax = property(getemax, setemax)
+ rounding = property(getround, setround)
+ clamp = property(getclamp, setclamp)
+ capitals = property(getcapitals, setcapitals)
+
+ def clear_traps(self):
+ self.c.clear_traps()
+ for trap in self.p.traps:
+ self.p.traps[trap] = False
+
+ def clear_status(self):
+ self.c.clear_flags()
+ self.p.clear_flags()
+
+ def settraps(self, lst):
+ """lst: C signal list"""
+ self.clear_traps()
+ for signal in lst:
+ self.c.traps[signal] = True
+ self.p.traps[CondMap[signal]] = True
+
+ def setstatus(self, lst):
+ """lst: C signal list"""
+ self.clear_status()
+ for signal in lst:
+ self.c.flags[signal] = True
+ self.p.flags[CondMap[signal]] = True
+
+ def assert_eq_status(self):
+ """assert equality of C and P status"""
+ for signal in self.c.flags:
+ if self.c.flags[signal] == (not self.p.flags[CondMap[signal]]):
+ return False
+ return True
+
+
+# We don't want exceptions so that we can compare the status flags.
+context = Context()
+context.Emin = C.MIN_EMIN
+context.Emax = C.MAX_EMAX
+context.clear_traps()
+
+# When creating decimals, _decimal is ultimately limited by the maximum
+# context values. We emulate this restriction for decimal.py.
+maxcontext = P.Context(
+ prec=C.MAX_PREC,
+ Emin=C.MIN_EMIN,
+ Emax=C.MAX_EMAX,
+ rounding=P.ROUND_HALF_UP,
+ capitals=1
+)
+maxcontext.clamp = 0
+
+def RestrictedDecimal(value):
+ maxcontext.traps = copy(context.p.traps)
+ maxcontext.clear_flags()
+ if isinstance(value, str):
+ value = value.strip()
+ dec = maxcontext.create_decimal(value)
+ if maxcontext.flags[P.Inexact] or \
+ maxcontext.flags[P.Rounded] or \
+ maxcontext.flags[P.InvalidOperation]:
+ return context.p._raise_error(P.InvalidOperation)
+ if maxcontext.flags[P.FloatOperation]:
+ context.p.flags[P.FloatOperation] = True
+ return dec
+
+
+# ======================================================================
+# TestSet: Organize data and events during a single test case
+# ======================================================================
+
+class RestrictedList(list):
+ """List that can only be modified by appending items."""
+ def __getattribute__(self, name):
+ if name != 'append':
+ raise AttributeError("unsupported operation")
+ return list.__getattribute__(self, name)
+ def unsupported(self, *_):
+ raise AttributeError("unsupported operation")
+ __add__ = __delattr__ = __delitem__ = __iadd__ = __imul__ = unsupported
+ __mul__ = __reversed__ = __rmul__ = __setattr__ = __setitem__ = unsupported
+
+class TestSet(object):
+ """A TestSet contains the original input operands, converted operands,
+ Python exceptions that occurred either during conversion or during
+ execution of the actual function, and the final results.
+
+ For safety, most attributes are lists that only support the append
+ operation.
+
+ If a function name is prefixed with 'context.', the corresponding
+ context method is called.
+ """
+ def __init__(self, funcname, operands):
+ if funcname.startswith("context."):
+ self.funcname = funcname.replace("context.", "")
+ self.contextfunc = True
+ else:
+ self.funcname = funcname
+ self.contextfunc = False
+ self.op = operands # raw operand tuple
+ self.context = context # context used for the operation
+ self.cop = RestrictedList() # converted C.Decimal operands
+ self.cex = RestrictedList() # Python exceptions for C.Decimal
+ self.cresults = RestrictedList() # C.Decimal results
+ self.pop = RestrictedList() # converted P.Decimal operands
+ self.pex = RestrictedList() # Python exceptions for P.Decimal
+ self.presults = RestrictedList() # P.Decimal results
+
+
+# ======================================================================
+# SkipHandler: skip known discrepancies
+# ======================================================================
+
+class SkipHandler:
+ """Handle known discrepancies between decimal.py and _decimal.so.
+ These are either ULP differences in the power function or
+ extremely minor issues."""
+
+ def __init__(self):
+ self.ulpdiff = 0
+ self.powmod_zeros = 0
+ self.maxctx = P.Context(Emax=10**18, Emin=-10**18)
+
+ def default(self, t):
+ return False
+ __ge__ = __gt__ = __le__ = __lt__ = __ne__ = __eq__ = default
+ __reduce__ = __format__ = __repr__ = __str__ = default
+
+ def harrison_ulp(self, dec):
+ """ftp://ftp.inria.fr/INRIA/publication/publi-pdf/RR/RR-5504.pdf"""
+ a = dec.next_plus()
+ b = dec.next_minus()
+ return abs(a - b)
+
+ def standard_ulp(self, dec, prec):
+ return P._dec_from_triple(0, '1', dec._exp+len(dec._int)-prec)
+
+ def rounding_direction(self, x, mode):
+ """Determine the effective direction of the rounding when
+ the exact result x is rounded according to mode.
+ Return -1 for downwards, 0 for undirected, 1 for upwards,
+ 2 for ROUND_05UP."""
+ cmp = 1 if x.compare_total(P.Decimal("+0")) >= 0 else -1
+
+ if mode in (P.ROUND_HALF_EVEN, P.ROUND_HALF_UP, P.ROUND_HALF_DOWN):
+ return 0
+ elif mode == P.ROUND_CEILING:
+ return 1
+ elif mode == P.ROUND_FLOOR:
+ return -1
+ elif mode == P.ROUND_UP:
+ return cmp
+ elif mode == P.ROUND_DOWN:
+ return -cmp
+ elif mode == P.ROUND_05UP:
+ return 2
+ else:
+ raise ValueError("Unexpected rounding mode: %s" % mode)
+
+ def check_ulpdiff(self, exact, rounded):
+ # current precision
+ p = context.p.prec
+
+ # Convert infinities to the largest representable number + 1.
+ x = exact
+ if exact.is_infinite():
+ x = P._dec_from_triple(exact._sign, '10', context.p.Emax)
+ y = rounded
+ if rounded.is_infinite():
+ y = P._dec_from_triple(rounded._sign, '10', context.p.Emax)
+
+ # err = (rounded - exact) / ulp(rounded)
+ self.maxctx.prec = p * 2
+ t = self.maxctx.subtract(y, x)
+ if context.c.flags[C.Clamped] or \
+ context.c.flags[C.Underflow]:
+ # The standard ulp does not work in Underflow territory.
+ ulp = self.harrison_ulp(y)
+ else:
+ ulp = self.standard_ulp(y, p)
+ # Error in ulps.
+ err = self.maxctx.divide(t, ulp)
+
+ dir = self.rounding_direction(x, context.p.rounding)
+ if dir == 0:
+ if P.Decimal("-0.6") < err < P.Decimal("0.6"):
+ return True
+ elif dir == 1: # directed, upwards
+ if P.Decimal("-0.1") < err < P.Decimal("1.1"):
+ return True
+ elif dir == -1: # directed, downwards
+ if P.Decimal("-1.1") < err < P.Decimal("0.1"):
+ return True
+ else: # ROUND_05UP
+ if P.Decimal("-1.1") < err < P.Decimal("1.1"):
+ return True
+
+ print("ulp: %s error: %s exact: %s c_rounded: %s"
+ % (ulp, err, exact, rounded))
+ return False
+
+ def bin_resolve_ulp(self, t):
+ """Check if results of _decimal's power function are within the
+ allowed ulp ranges."""
+ # NaNs are beyond repair.
+ if t.rc.is_nan() or t.rp.is_nan():
+ return False
+
+ # "exact" result, double precision, half_even
+ self.maxctx.prec = context.p.prec * 2
+
+ op1, op2 = t.pop[0], t.pop[1]
+ if t.contextfunc:
+ exact = getattr(self.maxctx, t.funcname)(op1, op2)
+ else:
+ exact = getattr(op1, t.funcname)(op2, context=self.maxctx)
+
+ # _decimal's rounded result
+ rounded = P.Decimal(t.cresults[0])
+
+ self.ulpdiff += 1
+ return self.check_ulpdiff(exact, rounded)
+
+ ############################ Correct rounding #############################
+ def resolve_underflow(self, t):
+ """In extremely rare cases where the infinite precision result is just
+ below etiny, cdecimal does not set Subnormal/Underflow. Example:
+
+ setcontext(Context(prec=21, rounding=ROUND_UP, Emin=-55, Emax=85))
+ Decimal("1.00000000000000000000000000000000000000000000000"
+ "0000000100000000000000000000000000000000000000000"
+ "0000000000000025").ln()
+ """
+ if t.cresults != t.presults:
+ return False # Results must be identical.
+ if context.c.flags[C.Rounded] and \
+ context.c.flags[C.Inexact] and \
+ context.p.flags[P.Rounded] and \
+ context.p.flags[P.Inexact]:
+ return True # Subnormal/Underflow may be missing.
+ return False
+
+ def exp(self, t):
+ """Resolve Underflow or ULP difference."""
+ return self.resolve_underflow(t)
+
+ def log10(self, t):
+ """Resolve Underflow or ULP difference."""
+ return self.resolve_underflow(t)
+
+ def ln(self, t):
+ """Resolve Underflow or ULP difference."""
+ return self.resolve_underflow(t)
+
+ def __pow__(self, t):
+ """Always calls the resolve function. C.Decimal does not have correct
+ rounding for the power function."""
+ if context.c.flags[C.Rounded] and \
+ context.c.flags[C.Inexact] and \
+ context.p.flags[P.Rounded] and \
+ context.p.flags[P.Inexact]:
+ return self.bin_resolve_ulp(t)
+ else:
+ return False
+ power = __rpow__ = __pow__
+
+ ############################## Technicalities #############################
+ def __float__(self, t):
+ """NaN comparison in the verify() function obviously gives an
+ incorrect answer: nan == nan -> False"""
+ if t.cop[0].is_nan() and t.pop[0].is_nan():
+ return True
+ return False
+ __complex__ = __float__
+
+ def __radd__(self, t):
+ """decimal.py gives precedence to the first NaN; this is
+ not important, as __radd__ will not be called for
+ two decimal arguments."""
+ if t.rc.is_nan() and t.rp.is_nan():
+ return True
+ return False
+ __rmul__ = __radd__
+
+ ################################ Various ##################################
+ def __round__(self, t):
+ """Exception: Decimal('1').__round__(-100000000000000000000000000)
+ Should it really be InvalidOperation?"""
+ if t.rc is None and t.rp.is_nan():
+ return True
+ return False
+
+shandler = SkipHandler()
+def skip_error(t):
+ return getattr(shandler, t.funcname, shandler.default)(t)
+
+
+# ======================================================================
+# Handling verification errors
+# ======================================================================
+
+class VerifyError(Exception):
+ """Verification failed."""
+ pass
+
+def function_as_string(t):
+ if t.contextfunc:
+ cargs = t.cop
+ pargs = t.pop
+ cfunc = "c_func: %s(" % t.funcname
+ pfunc = "p_func: %s(" % t.funcname
+ else:
+ cself, cargs = t.cop[0], t.cop[1:]
+ pself, pargs = t.pop[0], t.pop[1:]
+ cfunc = "c_func: %s.%s(" % (repr(cself), t.funcname)
+ pfunc = "p_func: %s.%s(" % (repr(pself), t.funcname)
+
+ err = cfunc
+ for arg in cargs:
+ err += "%s, " % repr(arg)
+ err = err.rstrip(", ")
+ err += ")\n"
+
+ err += pfunc
+ for arg in pargs:
+ err += "%s, " % repr(arg)
+ err = err.rstrip(", ")
+ err += ")"
+
+ return err
+
+def raise_error(t):
+ global EXIT_STATUS
+
+ if skip_error(t):
+ return
+ EXIT_STATUS = 1
+
+ err = "Error in %s:\n\n" % t.funcname
+ err += "input operands: %s\n\n" % (t.op,)
+ err += function_as_string(t)
+ err += "\n\nc_result: %s\np_result: %s\n\n" % (t.cresults, t.presults)
+ err += "c_exceptions: %s\np_exceptions: %s\n\n" % (t.cex, t.pex)
+ err += "%s\n\n" % str(t.context)
+
+ raise VerifyError(err)
+
+
+# ======================================================================
+# Main testing functions
+#
+# The procedure is always (t is the TestSet):
+#
+# convert(t) -> Initialize the TestSet as necessary.
+#
+# Return 0 for early abortion (e.g. if a TypeError
+# occurs during conversion, there is nothing to test).
+#
+# Return 1 for continuing with the test case.
+#
+# callfuncs(t) -> Call the relevant function for each implementation
+# and record the results in the TestSet.
+#
+# verify(t) -> Verify the results. If verification fails, details
+# are printed to stdout.
+# ======================================================================
+
+def convert(t, convstr=True):
+ """ t is the testset. At this stage the testset contains a tuple of
+ operands t.op of various types. For decimal methods the first
+ operand (self) is always converted to Decimal. If 'convstr' is
+ true, string operands are converted as well.
+
+ Context operands are of type deccheck.Context, rounding mode
+ operands are given as a tuple (C.rounding, P.rounding).
+
+ Other types (float, int, etc.) are left unchanged.
+ """
+ for i, op in enumerate(t.op):
+
+ context.clear_status()
+
+ if not t.contextfunc and i == 0 or \
+ convstr and isinstance(op, str):
+ try:
+ c = C.Decimal(op)
+ cex = None
+ except (TypeError, ValueError, OverflowError) as e:
+ c = None
+ cex = e.__class__
+
+ try:
+ p = RestrictedDecimal(op)
+ pex = None
+ except (TypeError, ValueError, OverflowError) as e:
+ p = None
+ pex = e.__class__
+
+ t.cop.append(c)
+ t.cex.append(cex)
+ t.pop.append(p)
+ t.pex.append(pex)
+
+ if cex is pex:
+ if str(c) != str(p) or not context.assert_eq_status():
+ raise_error(t)
+ if cex and pex:
+ # nothing to test
+ return 0
+ else:
+ raise_error(t)
+
+ elif isinstance(op, Context):
+ t.context = op
+ t.cop.append(op.c)
+ t.pop.append(op.p)
+
+ elif op in RoundModes:
+ t.cop.append(op[0])
+ t.pop.append(op[1])
+
+ else:
+ t.cop.append(op)
+ t.pop.append(op)
+
+ return 1
+
+def callfuncs(t):
+ """ t is the testset. At this stage the testset contains operand lists
+ t.cop and t.pop for the C and Python versions of decimal.
+ For Decimal methods, the first operands are of type C.Decimal and
+ P.Decimal respectively. The remaining operands can have various types.
+ For Context methods, all operands can have any type.
+
+ t.rc and t.rp are the results of the operation.
+ """
+ context.clear_status()
+
+ try:
+ if t.contextfunc:
+ cargs = t.cop
+ t.rc = getattr(context.c, t.funcname)(*cargs)
+ else:
+ cself = t.cop[0]
+ cargs = t.cop[1:]
+ t.rc = getattr(cself, t.funcname)(*cargs)
+ t.cex.append(None)
+ except (TypeError, ValueError, OverflowError, MemoryError) as e:
+ t.rc = None
+ t.cex.append(e.__class__)
+
+ try:
+ if t.contextfunc:
+ pargs = t.pop
+ t.rp = getattr(context.p, t.funcname)(*pargs)
+ else:
+ pself = t.pop[0]
+ pargs = t.pop[1:]
+ t.rp = getattr(pself, t.funcname)(*pargs)
+ t.pex.append(None)
+ except (TypeError, ValueError, OverflowError, MemoryError) as e:
+ t.rp = None
+ t.pex.append(e.__class__)
+
+def verify(t, stat):
+ """ t is the testset. At this stage the testset contains the following
+ tuples:
+
+ t.op: original operands
+ t.cop: C.Decimal operands (see convert for details)
+ t.pop: P.Decimal operands (see convert for details)
+ t.rc: C result
+ t.rp: Python result
+
+ t.rc and t.rp can have various types.
+ """
+ t.cresults.append(str(t.rc))
+ t.presults.append(str(t.rp))
+ if isinstance(t.rc, C.Decimal) and isinstance(t.rp, P.Decimal):
+ # General case: both results are Decimals.
+ t.cresults.append(t.rc.to_eng_string())
+ t.cresults.append(t.rc.as_tuple())
+ t.cresults.append(str(t.rc.imag))
+ t.cresults.append(str(t.rc.real))
+ t.presults.append(t.rp.to_eng_string())
+ t.presults.append(t.rp.as_tuple())
+ t.presults.append(str(t.rp.imag))
+ t.presults.append(str(t.rp.real))
+
+ nc = t.rc.number_class().lstrip('+-s')
+ stat[nc] += 1
+ else:
+ # Results from e.g. __divmod__ can only be compared as strings.
+ if not isinstance(t.rc, tuple) and not isinstance(t.rp, tuple):
+ if t.rc != t.rp:
+ raise_error(t)
+ stat[type(t.rc).__name__] += 1
+
+ # The return value lists must be equal.
+ if t.cresults != t.presults:
+ raise_error(t)
+ # The Python exception lists (TypeError, etc.) must be equal.
+ if t.cex != t.pex:
+ raise_error(t)
+ # The context flags must be equal.
+ if not t.context.assert_eq_status():
+ raise_error(t)
+
+
+# ======================================================================
+# Main test loops
+#
+# test_method(method, testspecs, testfunc) ->
+#
+# Loop through various context settings. The degree of
+# thoroughness is determined by 'testspec'. For each
+# setting, call 'testfunc'. Generally, 'testfunc' itself
+# a loop, iterating through many test cases generated
+# by the functions in randdec.py.
+#
+# test_n-ary(method, prec, exp_range, restricted_range, itr, stat) ->
+#
+# 'test_unary', 'test_binary' and 'test_ternary' are the
+# main test functions passed to 'test_method'. They deal
+# with the regular cases. The thoroughness of testing is
+# determined by 'itr'.
+#
+# 'prec', 'exp_range' and 'restricted_range' are passed
+# to the test-generating functions and limit the generated
+# values. In some cases, for reasonable run times a
+# maximum exponent of 9999 is required.
+#
+# The 'stat' parameter is passed down to the 'verify'
+# function, which records statistics for the result values.
+# ======================================================================
+
+def log(fmt, args=None):
+ if args:
+ sys.stdout.write(''.join((fmt, '\n')) % args)
+ else:
+ sys.stdout.write(''.join((str(fmt), '\n')))
+ sys.stdout.flush()
+
+def test_method(method, testspecs, testfunc):
+ """Iterate a test function through many context settings."""
+ log("testing %s ...", method)
+ stat = defaultdict(int)
+ for spec in testspecs:
+ if 'samples' in spec:
+ spec['prec'] = sorted(random.sample(range(1, 101),
+ spec['samples']))
+ for prec in spec['prec']:
+ context.prec = prec
+ for expts in spec['expts']:
+ emin, emax = expts
+ if emin == 'rand':
+ context.Emin = random.randrange(-1000, 0)
+ context.Emax = random.randrange(prec, 1000)
+ else:
+ context.Emin, context.Emax = emin, emax
+ if prec > context.Emax: continue
+ log(" prec: %d emin: %d emax: %d",
+ (context.prec, context.Emin, context.Emax))
+ restr_range = 9999 if context.Emax > 9999 else context.Emax+99
+ for rounding in sorted(RoundMap):
+ context.rounding = rounding
+ context.capitals = random.randrange(2)
+ if spec['clamp'] == 'rand':
+ context.clamp = random.randrange(2)
+ else:
+ context.clamp = spec['clamp']
+ exprange = context.c.Emax
+ testfunc(method, prec, exprange, restr_range,
+ spec['iter'], stat)
+ log(" result types: %s" % sorted([t for t in stat.items()]))
+
+def test_unary(method, prec, exp_range, restricted_range, itr, stat):
+ """Iterate a unary function through many test cases."""
+ if method in UnaryRestricted:
+ exp_range = restricted_range
+ for op in all_unary(prec, exp_range, itr):
+ t = TestSet(method, op)
+ try:
+ if not convert(t):
+ continue
+ callfuncs(t)
+ verify(t, stat)
+ except VerifyError as err:
+ log(err)
+
+def test_binary(method, prec, exp_range, restricted_range, itr, stat):
+ """Iterate a binary function through many test cases."""
+ if method in BinaryRestricted:
+ exp_range = restricted_range
+ for op in all_binary(prec, exp_range, itr):
+ t = TestSet(method, op)
+ try:
+ if not convert(t):
+ continue
+ callfuncs(t)
+ verify(t, stat)
+ except VerifyError as err:
+ log(err)
+
+def test_ternary(method, prec, exp_range, restricted_range, itr, stat):
+ """Iterate a ternary function through many test cases."""
+ if method in TernaryRestricted:
+ exp_range = restricted_range
+ for op in all_ternary(prec, exp_range, itr):
+ t = TestSet(method, op)
+ try:
+ if not convert(t):
+ continue
+ callfuncs(t)
+ verify(t, stat)
+ except VerifyError as err:
+ log(err)
+
+def test_format(method, prec, exp_range, restricted_range, itr, stat):
+ """Iterate the __format__ method through many test cases."""
+ for op in all_unary(prec, exp_range, itr):
+ fmt1 = rand_format(chr(random.randrange(32, 128)), 'EeGgn')
+ fmt2 = rand_locale()
+ for fmt in (fmt1, fmt2):
+ fmtop = (op[0], fmt)
+ t = TestSet(method, fmtop)
+ try:
+ if not convert(t, convstr=False):
+ continue
+ callfuncs(t)
+ verify(t, stat)
+ except VerifyError as err:
+ log(err)
+ for op in all_unary(prec, 9999, itr):
+ fmt1 = rand_format(chr(random.randrange(32, 128)), 'Ff%')
+ fmt2 = rand_locale()
+ for fmt in (fmt1, fmt2):
+ fmtop = (op[0], fmt)
+ t = TestSet(method, fmtop)
+ try:
+ if not convert(t, convstr=False):
+ continue
+ callfuncs(t)
+ verify(t, stat)
+ except VerifyError as err:
+ log(err)
+
+def test_round(method, prec, exprange, restricted_range, itr, stat):
+ """Iterate the __round__ method through many test cases."""
+ for op in all_unary(prec, 9999, itr):
+ n = random.randrange(10)
+ roundop = (op[0], n)
+ t = TestSet(method, roundop)
+ try:
+ if not convert(t):
+ continue
+ callfuncs(t)
+ verify(t, stat)
+ except VerifyError as err:
+ log(err)
+
+def test_from_float(method, prec, exprange, restricted_range, itr, stat):
+ """Iterate the __float__ method through many test cases."""
+ for rounding in sorted(RoundMap):
+ context.rounding = rounding
+ for i in range(1000):
+ f = randfloat()
+ op = (f,) if method.startswith("context.") else ("sNaN", f)
+ t = TestSet(method, op)
+ try:
+ if not convert(t):
+ continue
+ callfuncs(t)
+ verify(t, stat)
+ except VerifyError as err:
+ log(err)
+
+def randcontext(exprange):
+ c = Context(C.Context(), P.Context())
+ c.Emax = random.randrange(1, exprange+1)
+ c.Emin = random.randrange(-exprange, 0)
+ maxprec = 100 if c.Emax >= 100 else c.Emax
+ c.prec = random.randrange(1, maxprec+1)
+ c.clamp = random.randrange(2)
+ c.clear_traps()
+ return c
+
+def test_quantize_api(method, prec, exprange, restricted_range, itr, stat):
+ """Iterate the 'quantize' method through many test cases, using
+ the optional arguments."""
+ for op in all_binary(prec, restricted_range, itr):
+ for rounding in RoundModes:
+ c = randcontext(exprange)
+ quantizeop = (op[0], op[1], rounding, c)
+ t = TestSet(method, quantizeop)
+ try:
+ if not convert(t):
+ continue
+ callfuncs(t)
+ verify(t, stat)
+ except VerifyError as err:
+ log(err)
+
+
+def check_untested(funcdict, c_cls, p_cls):
+ """Determine untested, C-only and Python-only attributes.
+ Uncomment print lines for debugging."""
+ c_attr = set(dir(c_cls))
+ p_attr = set(dir(p_cls))
+ intersect = c_attr & p_attr
+
+ funcdict['c_only'] = tuple(sorted(c_attr-intersect))
+ funcdict['p_only'] = tuple(sorted(p_attr-intersect))
+
+ tested = set()
+ for lst in funcdict.values():
+ for v in lst:
+ v = v.replace("context.", "") if c_cls == C.Context else v
+ tested.add(v)
+
+ funcdict['untested'] = tuple(sorted(intersect-tested))
+
+ #for key in ('untested', 'c_only', 'p_only'):
+ # s = 'Context' if c_cls == C.Context else 'Decimal'
+ # print("\n%s %s:\n%s" % (s, key, funcdict[key]))
+
+
+if __name__ == '__main__':
+
+ import time
+
+ randseed = int(time.time())
+ random.seed(randseed)
+
+ # Set up the testspecs list. A testspec is simply a dictionary
+ # that determines the amount of different contexts that 'test_method'
+ # will generate.
+ base_expts = [(C.MIN_EMIN, C.MAX_EMAX)]
+ if C.MAX_EMAX == 999999999999999999:
+ base_expts.append((-999999999, 999999999))
+
+ # Basic contexts.
+ base = {
+ 'expts': base_expts,
+ 'prec': [],
+ 'clamp': 'rand',
+ 'iter': None,
+ 'samples': None,
+ }
+ # Contexts with small values for prec, emin, emax.
+ small = {
+ 'prec': [1, 2, 3, 4, 5],
+ 'expts': [(-1, 1), (-2, 2), (-3, 3), (-4, 4), (-5, 5)],
+ 'clamp': 'rand',
+ 'iter': None
+ }
+ # IEEE interchange format.
+ ieee = [
+ # DECIMAL32
+ {'prec': [7], 'expts': [(-95, 96)], 'clamp': 1, 'iter': None},
+ # DECIMAL64
+ {'prec': [16], 'expts': [(-383, 384)], 'clamp': 1, 'iter': None},
+ # DECIMAL128
+ {'prec': [34], 'expts': [(-6143, 6144)], 'clamp': 1, 'iter': None}
+ ]
+
+ if '--medium' in sys.argv:
+ base['expts'].append(('rand', 'rand'))
+ # 5 random precisions
+ base['samples'] = 5
+ testspecs = [small] + ieee + [base]
+ if '--long' in sys.argv:
+ base['expts'].append(('rand', 'rand'))
+ # 10 random precisions
+ base['samples'] = 10
+ testspecs = [small] + ieee + [base]
+ elif '--all' in sys.argv:
+ base['expts'].append(('rand', 'rand'))
+ # All precisions in [1, 100]
+ base['samples'] = 100
+ testspecs = [small] + ieee + [base]
+ else: # --short
+ rand_ieee = random.choice(ieee)
+ base['iter'] = small['iter'] = rand_ieee['iter'] = 1
+ # 1 random precision and exponent pair
+ base['samples'] = 1
+ base['expts'] = [random.choice(base_expts)]
+ # 1 random precision and exponent pair
+ prec = random.randrange(1, 6)
+ small['prec'] = [prec]
+ small['expts'] = [(-prec, prec)]
+ testspecs = [small, rand_ieee, base]
+
+ check_untested(Functions, C.Decimal, P.Decimal)
+ check_untested(ContextFunctions, C.Context, P.Context)
+
+
+ log("\n\nRandom seed: %d\n\n", randseed)
+
+ # Decimal methods:
+ for method in Functions['unary'] + Functions['unary_ctx'] + \
+ Functions['unary_rnd_ctx']:
+ test_method(method, testspecs, test_unary)
+
+ for method in Functions['binary'] + Functions['binary_ctx']:
+ test_method(method, testspecs, test_binary)
+
+ for method in Functions['ternary'] + Functions['ternary_ctx']:
+ test_method(method, testspecs, test_ternary)
+
+ test_method('__format__', testspecs, test_format)
+ test_method('__round__', testspecs, test_round)
+ test_method('from_float', testspecs, test_from_float)
+ test_method('quantize', testspecs, test_quantize_api)
+
+ # Context methods:
+ for method in ContextFunctions['unary']:
+ test_method(method, testspecs, test_unary)
+
+ for method in ContextFunctions['binary']:
+ test_method(method, testspecs, test_binary)
+
+ for method in ContextFunctions['ternary']:
+ test_method(method, testspecs, test_ternary)
+
+ test_method('context.create_decimal_from_float', testspecs, test_from_float)
+
+
+ sys.exit(EXIT_STATUS)
--- /dev/null
+#
+# Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+
+# Generate PEP-3101 format strings.
+
+
+import os, sys, locale, random
+import platform, subprocess
+from test.support import import_fresh_module
+from distutils.spawn import find_executable
+
+C = import_fresh_module('decimal', fresh=['_decimal'])
+P = import_fresh_module('decimal', blocked=['_decimal'])
+
+
+windows_lang_strings = [
+ "chinese", "chinese-simplified", "chinese-traditional", "czech", "danish",
+ "dutch", "belgian", "english", "australian", "canadian", "english-nz",
+ "english-uk", "english-us", "finnish", "french", "french-belgian",
+ "french-canadian", "french-swiss", "german", "german-austrian",
+ "german-swiss", "greek", "hungarian", "icelandic", "italian", "italian-swiss",
+ "japanese", "korean", "norwegian", "norwegian-bokmal", "norwegian-nynorsk",
+ "polish", "portuguese", "portuguese-brazil", "russian", "slovak", "spanish",
+ "spanish-mexican", "spanish-modern", "swedish", "turkish",
+]
+
+preferred_encoding = {
+ 'cs_CZ': 'ISO8859-2',
+ 'cs_CZ.iso88592': 'ISO8859-2',
+ 'czech': 'ISO8859-2',
+ 'eesti': 'ISO8859-1',
+ 'estonian': 'ISO8859-1',
+ 'et_EE': 'ISO8859-15',
+ 'et_EE.ISO-8859-15': 'ISO8859-15',
+ 'et_EE.iso885915': 'ISO8859-15',
+ 'et_EE.iso88591': 'ISO8859-1',
+ 'fi_FI.iso88591': 'ISO8859-1',
+ 'fi_FI': 'ISO8859-15',
+ 'fi_FI@euro': 'ISO8859-15',
+ 'fi_FI.iso885915@euro': 'ISO8859-15',
+ 'finnish': 'ISO8859-1',
+ 'lv_LV': 'ISO8859-13',
+ 'lv_LV.iso885913': 'ISO8859-13',
+ 'nb_NO': 'ISO8859-1',
+ 'nb_NO.iso88591': 'ISO8859-1',
+ 'bokmal': 'ISO8859-1',
+ 'nn_NO': 'ISO8859-1',
+ 'nn_NO.iso88591': 'ISO8859-1',
+ 'no_NO': 'ISO8859-1',
+ 'norwegian': 'ISO8859-1',
+ 'nynorsk': 'ISO8859-1',
+ 'ru_RU': 'ISO8859-5',
+ 'ru_RU.iso88595': 'ISO8859-5',
+ 'russian': 'ISO8859-5',
+ 'ru_RU.KOI8-R': 'KOI8-R',
+ 'ru_RU.koi8r': 'KOI8-R',
+ 'ru_RU.CP1251': 'CP1251',
+ 'ru_RU.cp1251': 'CP1251',
+ 'sk_SK': 'ISO8859-2',
+ 'sk_SK.iso88592': 'ISO8859-2',
+ 'slovak': 'ISO8859-2',
+ 'sv_FI': 'ISO8859-1',
+ 'sv_FI.iso88591': 'ISO8859-1',
+ 'sv_FI@euro': 'ISO8859-15',
+ 'sv_FI.iso885915@euro': 'ISO8859-15',
+ 'uk_UA': 'KOI8-U',
+ 'uk_UA.koi8u': 'KOI8-U'
+}
+
+integers = [
+ "",
+ "1",
+ "12",
+ "123",
+ "1234",
+ "12345",
+ "123456",
+ "1234567",
+ "12345678",
+ "123456789",
+ "1234567890",
+ "12345678901",
+ "123456789012",
+ "1234567890123",
+ "12345678901234",
+ "123456789012345",
+ "1234567890123456",
+ "12345678901234567",
+ "123456789012345678",
+ "1234567890123456789",
+ "12345678901234567890",
+ "123456789012345678901",
+ "1234567890123456789012",
+]
+
+numbers = [
+ "0", "-0", "+0",
+ "0.0", "-0.0", "+0.0",
+ "0e0", "-0e0", "+0e0",
+ ".0", "-.0",
+ ".1", "-.1",
+ "1.1", "-1.1",
+ "1e1", "-1e1"
+]
+
+# Get the list of available locales.
+if platform.system() == 'Windows':
+ locale_list = windows_lang_strings
+else:
+ locale_list = ['C']
+ if os.path.isfile("/var/lib/locales/supported.d/local"):
+ # On Ubuntu, `locale -a` gives the wrong case for some locales,
+ # so we get the correct names directly:
+ with open("/var/lib/locales/supported.d/local") as f:
+ locale_list = [loc.split()[0] for loc in f.readlines() \
+ if not loc.startswith('#')]
+ elif find_executable('locale'):
+ locale_list = subprocess.Popen(["locale", "-a"],
+ stdout=subprocess.PIPE).communicate()[0]
+ try:
+ locale_list = locale_list.decode()
+ except UnicodeDecodeError:
+ # Some distributions insist on using latin-1 characters
+ # in their locale names.
+ locale_list = locale_list.decode('latin-1')
+ locale_list = locale_list.split('\n')
+try:
+ locale_list.remove('')
+except ValueError:
+ pass
+
+# Debian
+if os.path.isfile("/etc/locale.alias"):
+ with open("/etc/locale.alias") as f:
+ while 1:
+ try:
+ line = f.readline()
+ except UnicodeDecodeError:
+ continue
+ if line == "":
+ break
+ if line.startswith('#'):
+ continue
+ x = line.split()
+ if len(x) == 2:
+ if x[0] in locale_list:
+ locale_list.remove(x[0])
+
+# FreeBSD
+if platform.system() == 'FreeBSD':
+ # http://www.freebsd.org/cgi/query-pr.cgi?pr=142173
+ # en_GB.US-ASCII has 163 as the currency symbol.
+ for loc in ['it_CH.ISO8859-1', 'it_CH.ISO8859-15', 'it_CH.UTF-8',
+ 'it_IT.ISO8859-1', 'it_IT.ISO8859-15', 'it_IT.UTF-8',
+ 'sl_SI.ISO8859-2', 'sl_SI.UTF-8',
+ 'en_GB.US-ASCII']:
+ try:
+ locale_list.remove(loc)
+ except ValueError:
+ pass
+
+# Print a testcase in the format of the IBM tests (for runtest.c):
+def get_preferred_encoding():
+ loc = locale.setlocale(locale.LC_CTYPE)
+ if loc in preferred_encoding:
+ return preferred_encoding[loc]
+ else:
+ return locale.getpreferredencoding()
+
+def printit(testno, s, fmt, encoding=None):
+ if not encoding:
+ encoding = get_preferred_encoding()
+ try:
+ result = format(P.Decimal(s), fmt)
+ fmt = str(fmt.encode(encoding))[2:-1]
+ result = str(result.encode(encoding))[2:-1]
+ if "'" in result:
+ sys.stdout.write("xfmt%d format %s '%s' -> \"%s\"\n"
+ % (testno, s, fmt, result))
+ else:
+ sys.stdout.write("xfmt%d format %s '%s' -> '%s'\n"
+ % (testno, s, fmt, result))
+ except Exception as err:
+ sys.stderr.write("%s %s %s\n" % (err, s, fmt))
+
+
+# Check if an integer can be converted to a valid fill character.
+def check_fillchar(i):
+ try:
+ c = chr(i)
+ c.encode('utf-8').decode()
+ format(P.Decimal(0), c + '<19g')
+ if c in ("'", '"', '\\'):
+ return None
+ return c
+ except:
+ return None
+
+# Generate all unicode characters that are accepted as
+# fill characters by decimal.py.
+def all_fillchars():
+ for i in range(32, 0x110002):
+ c = check_fillchar(i)
+ if c: yield c
+
+# Return random fill character.
+def rand_fillchar():
+ while 1:
+ i = random.randrange(32, 0x110002)
+ c = check_fillchar(i)
+ if c: return c
+
+# Generate random format strings
+# [[fill]align][sign][#][0][width][.precision][type]
+def rand_format(fill, typespec='EeGgFfn%'):
+ active = sorted(random.sample(range(7), random.randrange(8)))
+ have_align = 0
+ s = ''
+ for elem in active:
+ if elem == 0: # fill+align
+ s += fill
+ s += random.choice('<>=^')
+ have_align = 1
+ elif elem == 1: # sign
+ s += random.choice('+- ')
+ elif elem == 2 and not have_align: # zeropad
+ s += '0'
+ elif elem == 3: # width
+ s += str(random.randrange(1, 100))
+ elif elem == 4: # thousands separator
+ s += ','
+ elif elem == 5: # prec
+ s += '.'
+ s += str(random.randrange(100))
+ elif elem == 6:
+ if 4 in active: c = typespec.replace('n', '')
+ else: c = typespec
+ s += random.choice(c)
+ return s
+
+# Partially brute force all possible format strings containing a thousands
+# separator. Fall back to random where the runtime would become excessive.
+# [[fill]align][sign][#][0][width][,][.precision][type]
+def all_format_sep():
+ for align in ('', '<', '>', '=', '^'):
+ for fill in ('', 'x'):
+ if align == '': fill = ''
+ for sign in ('', '+', '-', ' '):
+ for zeropad in ('', '0'):
+ if align != '': zeropad = ''
+ for width in ['']+[str(y) for y in range(1, 15)]+['101']:
+ for prec in ['']+['.'+str(y) for y in range(15)]:
+ # for type in ('', 'E', 'e', 'G', 'g', 'F', 'f', '%'):
+ type = random.choice(('', 'E', 'e', 'G', 'g', 'F', 'f', '%'))
+ yield ''.join((fill, align, sign, zeropad, width, ',', prec, type))
+
+# Partially brute force all possible format strings with an 'n' specifier.
+# [[fill]align][sign][#][0][width][,][.precision][type]
+def all_format_loc():
+ for align in ('', '<', '>', '=', '^'):
+ for fill in ('', 'x'):
+ if align == '': fill = ''
+ for sign in ('', '+', '-', ' '):
+ for zeropad in ('', '0'):
+ if align != '': zeropad = ''
+ for width in ['']+[str(y) for y in range(1, 20)]+['101']:
+ for prec in ['']+['.'+str(y) for y in range(1, 20)]:
+ yield ''.join((fill, align, sign, zeropad, width, prec, 'n'))
+
+# Generate random format strings with a unicode fill character
+# [[fill]align][sign][#][0][width][,][.precision][type]
+def randfill(fill):
+ active = sorted(random.sample(range(5), random.randrange(6)))
+ s = ''
+ s += str(fill)
+ s += random.choice('<>=^')
+ for elem in active:
+ if elem == 0: # sign
+ s += random.choice('+- ')
+ elif elem == 1: # width
+ s += str(random.randrange(1, 100))
+ elif elem == 2: # thousands separator
+ s += ','
+ elif elem == 3: # prec
+ s += '.'
+ s += str(random.randrange(100))
+ elif elem == 4:
+ if 2 in active: c = 'EeGgFf%'
+ else: c = 'EeGgFfn%'
+ s += random.choice(c)
+ return s
+
+# Generate random format strings with random locale setting
+# [[fill]align][sign][#][0][width][,][.precision][type]
+def rand_locale():
+ try:
+ loc = random.choice(locale_list)
+ locale.setlocale(locale.LC_ALL, loc)
+ except locale.Error as err:
+ pass
+ active = sorted(random.sample(range(5), random.randrange(6)))
+ s = ''
+ have_align = 0
+ for elem in active:
+ if elem == 0: # fill+align
+ s += chr(random.randrange(32, 128))
+ s += random.choice('<>=^')
+ have_align = 1
+ elif elem == 1: # sign
+ s += random.choice('+- ')
+ elif elem == 2 and not have_align: # zeropad
+ s += '0'
+ elif elem == 3: # width
+ s += str(random.randrange(1, 100))
+ elif elem == 4: # prec
+ s += '.'
+ s += str(random.randrange(100))
+ s += 'n'
+ return s
--- /dev/null
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+
+# Generate test cases for deccheck.py.
+
+
+#
+# Grammar from http://speleotrove.com/decimal/daconvs.html
+#
+# sign ::= '+' | '-'
+# digit ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' |
+# '8' | '9'
+# indicator ::= 'e' | 'E'
+# digits ::= digit [digit]...
+# decimal-part ::= digits '.' [digits] | ['.'] digits
+# exponent-part ::= indicator [sign] digits
+# infinity ::= 'Infinity' | 'Inf'
+# nan ::= 'NaN' [digits] | 'sNaN' [digits]
+# numeric-value ::= decimal-part [exponent-part] | infinity
+# numeric-string ::= [sign] numeric-value | [sign] nan
+#
+
+
+from random import randrange, sample
+from fractions import Fraction
+from randfloat import un_randfloat, bin_randfloat, tern_randfloat
+
+
+def sign():
+ if randrange(2):
+ if randrange(2): return '+'
+ return ''
+ return '-'
+
+def indicator():
+ return "eE"[randrange(2)]
+
+def digits(maxprec):
+ if maxprec == 0: return ''
+ return str(randrange(10**maxprec))
+
+def dot():
+ if randrange(2): return '.'
+ return ''
+
+def decimal_part(maxprec):
+ if randrange(100) > 60: # integers
+ return digits(maxprec)
+ if randrange(2):
+ intlen = randrange(1, maxprec+1)
+ fraclen = maxprec-intlen
+ intpart = digits(intlen)
+ fracpart = digits(fraclen)
+ return ''.join((intpart, '.', fracpart))
+ else:
+ return ''.join((dot(), digits(maxprec)))
+
+def expdigits(maxexp):
+ return str(randrange(maxexp))
+
+def exponent_part(maxexp):
+ return ''.join((indicator(), sign(), expdigits(maxexp)))
+
+def infinity():
+ if randrange(2): return 'Infinity'
+ return 'Inf'
+
+def nan():
+ d = ''
+ if randrange(2):
+ d = digits(randrange(99))
+ if randrange(2):
+ return ''.join(('NaN', d))
+ else:
+ return ''.join(('sNaN', d))
+
+def numeric_value(maxprec, maxexp):
+ if randrange(100) > 90:
+ return infinity()
+ exp_part = ''
+ if randrange(100) > 60:
+ exp_part = exponent_part(maxexp)
+ return ''.join((decimal_part(maxprec), exp_part))
+
+def numeric_string(maxprec, maxexp):
+ if randrange(100) > 95:
+ return ''.join((sign(), nan()))
+ else:
+ return ''.join((sign(), numeric_value(maxprec, maxexp)))
+
+def randdec(maxprec, maxexp):
+ return numeric_string(maxprec, maxexp)
+
+def rand_adjexp(maxprec, maxadjexp):
+ d = digits(maxprec)
+ maxexp = maxadjexp-len(d)+1
+ if maxexp == 0: maxexp = 1
+ exp = str(randrange(maxexp-2*(abs(maxexp)), maxexp))
+ return ''.join((sign(), d, 'E', exp))
+
+
+def ndigits(n):
+ if n < 1: return 0
+ return randrange(10**(n-1), 10**n)
+
+def randtuple(maxprec, maxexp):
+ n = randrange(100)
+ sign = randrange(2)
+ coeff = ndigits(maxprec)
+ if n >= 95:
+ coeff = ()
+ exp = 'F'
+ elif n >= 85:
+ coeff = tuple(map(int, str(ndigits(maxprec))))
+ exp = "nN"[randrange(2)]
+ else:
+ coeff = tuple(map(int, str(ndigits(maxprec))))
+ exp = randrange(-maxexp, maxexp)
+ return (sign, coeff, exp)
+
+def from_triple(sign, coeff, exp):
+ return ''.join((str(sign*coeff), indicator(), str(exp)))
+
+
+# Close to 10**n
+def un_close_to_pow10(prec, maxexp, itr=None):
+ if itr is None:
+ lst = range(prec+30)
+ else:
+ lst = sample(range(prec+30), itr)
+ nines = [10**n - 1 for n in lst]
+ pow10 = [10**n for n in lst]
+ for coeff in nines:
+ yield coeff
+ yield -coeff
+ yield from_triple(1, coeff, randrange(2*maxexp))
+ yield from_triple(-1, coeff, randrange(2*maxexp))
+ for coeff in pow10:
+ yield coeff
+ yield -coeff
+
+# Close to 10**n
+def bin_close_to_pow10(prec, maxexp, itr=None):
+ if itr is None:
+ lst = range(prec+30)
+ else:
+ lst = sample(range(prec+30), itr)
+ nines = [10**n - 1 for n in lst]
+ pow10 = [10**n for n in lst]
+ for coeff in nines:
+ yield coeff, 1
+ yield -coeff, -1
+ yield 1, coeff
+ yield -1, -coeff
+ yield from_triple(1, coeff, randrange(2*maxexp)), 1
+ yield from_triple(-1, coeff, randrange(2*maxexp)), -1
+ yield 1, from_triple(1, coeff, -randrange(2*maxexp))
+ yield -1, from_triple(-1, coeff, -randrange(2*maxexp))
+ for coeff in pow10:
+ yield coeff, -1
+ yield -coeff, 1
+ yield 1, -coeff
+ yield -coeff, 1
+
+# Close to 1:
+def close_to_one_greater(prec, emax, emin):
+ rprec = 10**prec
+ return ''.join(("1.", '0'*randrange(prec),
+ str(randrange(rprec))))
+
+def close_to_one_less(prec, emax, emin):
+ rprec = 10**prec
+ return ''.join(("0.9", '9'*randrange(prec),
+ str(randrange(rprec))))
+
+# Close to 0:
+def close_to_zero_greater(prec, emax, emin):
+ rprec = 10**prec
+ return ''.join(("0.", '0'*randrange(prec),
+ str(randrange(rprec))))
+
+def close_to_zero_less(prec, emax, emin):
+ rprec = 10**prec
+ return ''.join(("-0.", '0'*randrange(prec),
+ str(randrange(rprec))))
+
+# Close to emax:
+def close_to_emax_less(prec, emax, emin):
+ rprec = 10**prec
+ return ''.join(("9.", '9'*randrange(prec),
+ str(randrange(rprec)), "E", str(emax)))
+
+def close_to_emax_greater(prec, emax, emin):
+ rprec = 10**prec
+ return ''.join(("1.", '0'*randrange(prec),
+ str(randrange(rprec)), "E", str(emax+1)))
+
+# Close to emin:
+def close_to_emin_greater(prec, emax, emin):
+ rprec = 10**prec
+ return ''.join(("1.", '0'*randrange(prec),
+ str(randrange(rprec)), "E", str(emin)))
+
+def close_to_emin_less(prec, emax, emin):
+ rprec = 10**prec
+ return ''.join(("9.", '9'*randrange(prec),
+ str(randrange(rprec)), "E", str(emin-1)))
+
+# Close to etiny:
+def close_to_etiny_greater(prec, emax, emin):
+ rprec = 10**prec
+ etiny = emin - (prec - 1)
+ return ''.join(("1.", '0'*randrange(prec),
+ str(randrange(rprec)), "E", str(etiny)))
+
+def close_to_etiny_less(prec, emax, emin):
+ rprec = 10**prec
+ etiny = emin - (prec - 1)
+ return ''.join(("9.", '9'*randrange(prec),
+ str(randrange(rprec)), "E", str(etiny-1)))
+
+
+def close_to_min_etiny_greater(prec, max_prec, min_emin):
+ rprec = 10**prec
+ etiny = min_emin - (max_prec - 1)
+ return ''.join(("1.", '0'*randrange(prec),
+ str(randrange(rprec)), "E", str(etiny)))
+
+def close_to_min_etiny_less(prec, max_prec, min_emin):
+ rprec = 10**prec
+ etiny = min_emin - (max_prec - 1)
+ return ''.join(("9.", '9'*randrange(prec),
+ str(randrange(rprec)), "E", str(etiny-1)))
+
+
+close_funcs = [
+ close_to_one_greater, close_to_one_less, close_to_zero_greater,
+ close_to_zero_less, close_to_emax_less, close_to_emax_greater,
+ close_to_emin_greater, close_to_emin_less, close_to_etiny_greater,
+ close_to_etiny_less, close_to_min_etiny_greater, close_to_min_etiny_less
+]
+
+
+def un_close_numbers(prec, emax, emin, itr=None):
+ if itr is None:
+ itr = 1000
+ for _ in range(itr):
+ for func in close_funcs:
+ yield func(prec, emax, emin)
+
+def bin_close_numbers(prec, emax, emin, itr=None):
+ if itr is None:
+ itr = 1000
+ for _ in range(itr):
+ for func1 in close_funcs:
+ for func2 in close_funcs:
+ yield func1(prec, emax, emin), func2(prec, emax, emin)
+ for func in close_funcs:
+ yield randdec(prec, emax), func(prec, emax, emin)
+ yield func(prec, emax, emin), randdec(prec, emax)
+
+def tern_close_numbers(prec, emax, emin, itr):
+ if itr is None:
+ itr = 1000
+ for _ in range(itr):
+ for func1 in close_funcs:
+ for func2 in close_funcs:
+ for func3 in close_funcs:
+ yield (func1(prec, emax, emin), func2(prec, emax, emin),
+ func3(prec, emax, emin))
+ for func in close_funcs:
+ yield (randdec(prec, emax), func(prec, emax, emin),
+ func(prec, emax, emin))
+ yield (func(prec, emax, emin), randdec(prec, emax),
+ func(prec, emax, emin))
+ yield (func(prec, emax, emin), func(prec, emax, emin),
+ randdec(prec, emax))
+ for func in close_funcs:
+ yield (randdec(prec, emax), randdec(prec, emax),
+ func(prec, emax, emin))
+ yield (randdec(prec, emax), func(prec, emax, emin),
+ randdec(prec, emax))
+ yield (func(prec, emax, emin), randdec(prec, emax),
+ randdec(prec, emax))
+
+
+# If itr == None, test all digit lengths up to prec + 30
+def un_incr_digits(prec, maxexp, itr):
+ if itr is None:
+ lst = range(prec+30)
+ else:
+ lst = sample(range(prec+30), itr)
+ for m in lst:
+ yield from_triple(1, ndigits(m), 0)
+ yield from_triple(-1, ndigits(m), 0)
+ yield from_triple(1, ndigits(m), randrange(maxexp))
+ yield from_triple(-1, ndigits(m), randrange(maxexp))
+
+# If itr == None, test all digit lengths up to prec + 30
+# Also output decimals im tuple form.
+def un_incr_digits_tuple(prec, maxexp, itr):
+ if itr is None:
+ lst = range(prec+30)
+ else:
+ lst = sample(range(prec+30), itr)
+ for m in lst:
+ yield from_triple(1, ndigits(m), 0)
+ yield from_triple(-1, ndigits(m), 0)
+ yield from_triple(1, ndigits(m), randrange(maxexp))
+ yield from_triple(-1, ndigits(m), randrange(maxexp))
+ # test from tuple
+ yield (0, tuple(map(int, str(ndigits(m)))), 0)
+ yield (1, tuple(map(int, str(ndigits(m)))), 0)
+ yield (0, tuple(map(int, str(ndigits(m)))), randrange(maxexp))
+ yield (1, tuple(map(int, str(ndigits(m)))), randrange(maxexp))
+
+# If itr == None, test all combinations of digit lengths up to prec + 30
+def bin_incr_digits(prec, maxexp, itr):
+ if itr is None:
+ lst1 = range(prec+30)
+ lst2 = range(prec+30)
+ else:
+ lst1 = sample(range(prec+30), itr)
+ lst2 = sample(range(prec+30), itr)
+ for m in lst1:
+ x = from_triple(1, ndigits(m), 0)
+ yield x, x
+ x = from_triple(-1, ndigits(m), 0)
+ yield x, x
+ x = from_triple(1, ndigits(m), randrange(maxexp))
+ yield x, x
+ x = from_triple(-1, ndigits(m), randrange(maxexp))
+ yield x, x
+ for m in lst1:
+ for n in lst2:
+ x = from_triple(1, ndigits(m), 0)
+ y = from_triple(1, ndigits(n), 0)
+ yield x, y
+ x = from_triple(-1, ndigits(m), 0)
+ y = from_triple(1, ndigits(n), 0)
+ yield x, y
+ x = from_triple(1, ndigits(m), 0)
+ y = from_triple(-1, ndigits(n), 0)
+ yield x, y
+ x = from_triple(-1, ndigits(m), 0)
+ y = from_triple(-1, ndigits(n), 0)
+ yield x, y
+ x = from_triple(1, ndigits(m), randrange(maxexp))
+ y = from_triple(1, ndigits(n), randrange(maxexp))
+ yield x, y
+ x = from_triple(-1, ndigits(m), randrange(maxexp))
+ y = from_triple(1, ndigits(n), randrange(maxexp))
+ yield x, y
+ x = from_triple(1, ndigits(m), randrange(maxexp))
+ y = from_triple(-1, ndigits(n), randrange(maxexp))
+ yield x, y
+ x = from_triple(-1, ndigits(m), randrange(maxexp))
+ y = from_triple(-1, ndigits(n), randrange(maxexp))
+ yield x, y
+
+
+def randsign():
+ return (1, -1)[randrange(2)]
+
+# If itr == None, test all combinations of digit lengths up to prec + 30
+def tern_incr_digits(prec, maxexp, itr):
+ if itr is None:
+ lst1 = range(prec+30)
+ lst2 = range(prec+30)
+ lst3 = range(prec+30)
+ else:
+ lst1 = sample(range(prec+30), itr)
+ lst2 = sample(range(prec+30), itr)
+ lst3 = sample(range(prec+30), itr)
+ for m in lst1:
+ for n in lst2:
+ for p in lst3:
+ x = from_triple(randsign(), ndigits(m), 0)
+ y = from_triple(randsign(), ndigits(n), 0)
+ z = from_triple(randsign(), ndigits(p), 0)
+ yield x, y, z
+
+
+# Tests for the 'logical' functions
+def bindigits(prec):
+ z = 0
+ for i in range(prec):
+ z += randrange(2) * 10**i
+ return z
+
+def logical_un_incr_digits(prec, itr):
+ if itr is None:
+ lst = range(prec+30)
+ else:
+ lst = sample(range(prec+30), itr)
+ for m in lst:
+ yield from_triple(1, bindigits(m), 0)
+
+def logical_bin_incr_digits(prec, itr):
+ if itr is None:
+ lst1 = range(prec+30)
+ lst2 = range(prec+30)
+ else:
+ lst1 = sample(range(prec+30), itr)
+ lst2 = sample(range(prec+30), itr)
+ for m in lst1:
+ x = from_triple(1, bindigits(m), 0)
+ yield x, x
+ for m in lst1:
+ for n in lst2:
+ x = from_triple(1, bindigits(m), 0)
+ y = from_triple(1, bindigits(n), 0)
+ yield x, y
+
+
+def randint():
+ p = randrange(1, 100)
+ return ndigits(p) * (1,-1)[randrange(2)]
+
+def randfloat():
+ p = randrange(1, 100)
+ s = numeric_value(p, 383)
+ try:
+ f = float(numeric_value(p, 383))
+ except ValueError:
+ f = 0.0
+ return f
+
+def randcomplex():
+ real = randfloat()
+ if randrange(100) > 30:
+ imag = 0.0
+ else:
+ imag = randfloat()
+ return complex(real, imag)
+
+def randfraction():
+ num = randint()
+ denom = randint()
+ if denom == 0:
+ denom = 1
+ return Fraction(num, denom)
+
+number_funcs = [randint, randfloat, randcomplex, randfraction]
+
+def un_random_mixed_op(itr=None):
+ if itr is None:
+ itr = 1000
+ for _ in range(itr):
+ for func in number_funcs:
+ yield func()
+ # Test garbage input
+ for x in (['x'], ('y',), {'z'}, {1:'z'}):
+ yield x
+
+def bin_random_mixed_op(prec, emax, emin, itr=None):
+ if itr is None:
+ itr = 1000
+ for _ in range(itr):
+ for func in number_funcs:
+ yield randdec(prec, emax), func()
+ yield func(), randdec(prec, emax)
+ for number in number_funcs:
+ for dec in close_funcs:
+ yield dec(prec, emax, emin), number()
+ # Test garbage input
+ for x in (['x'], ('y',), {'z'}, {1:'z'}):
+ for y in (['x'], ('y',), {'z'}, {1:'z'}):
+ yield x, y
+
+def tern_random_mixed_op(prec, emax, emin, itr):
+ if itr is None:
+ itr = 1000
+ for _ in range(itr):
+ for func in number_funcs:
+ yield randdec(prec, emax), randdec(prec, emax), func()
+ yield randdec(prec, emax), func(), func()
+ yield func(), func(), func()
+ # Test garbage input
+ for x in (['x'], ('y',), {'z'}, {1:'z'}):
+ for y in (['x'], ('y',), {'z'}, {1:'z'}):
+ for z in (['x'], ('y',), {'z'}, {1:'z'}):
+ yield x, y, z
+
+def all_unary(prec, exp_range, itr):
+ for a in un_close_to_pow10(prec, exp_range, itr):
+ yield (a,)
+ for a in un_close_numbers(prec, exp_range, -exp_range, itr):
+ yield (a,)
+ for a in un_incr_digits_tuple(prec, exp_range, itr):
+ yield (a,)
+ for a in un_randfloat():
+ yield (a,)
+ for a in un_random_mixed_op(itr):
+ yield (a,)
+ for a in logical_un_incr_digits(prec, itr):
+ yield (a,)
+ for _ in range(100):
+ yield (randdec(prec, exp_range),)
+ for _ in range(100):
+ yield (randtuple(prec, exp_range),)
+
+def all_binary(prec, exp_range, itr):
+ for a, b in bin_close_to_pow10(prec, exp_range, itr):
+ yield a, b
+ for a, b in bin_close_numbers(prec, exp_range, -exp_range, itr):
+ yield a, b
+ for a, b in bin_incr_digits(prec, exp_range, itr):
+ yield a, b
+ for a, b in bin_randfloat():
+ yield a, b
+ for a, b in bin_random_mixed_op(prec, exp_range, -exp_range, itr):
+ yield a, b
+ for a, b in logical_bin_incr_digits(prec, itr):
+ yield a, b
+ for _ in range(100):
+ yield randdec(prec, exp_range), randdec(prec, exp_range)
+
+def all_ternary(prec, exp_range, itr):
+ for a, b, c in tern_close_numbers(prec, exp_range, -exp_range, itr):
+ yield a, b, c
+ for a, b, c in tern_incr_digits(prec, exp_range, itr):
+ yield a, b, c
+ for a, b, c in tern_randfloat():
+ yield a, b, c
+ for a, b, c in tern_random_mixed_op(prec, exp_range, -exp_range, itr):
+ yield a, b, c
+ for _ in range(100):
+ a = randdec(prec, 2*exp_range)
+ b = randdec(prec, 2*exp_range)
+ c = randdec(prec, 2*exp_range)
+ yield a, b, c
--- /dev/null
+# Copyright (c) 2010 Python Software Foundation. All Rights Reserved.
+# Adapted from Python's Lib/test/test_strtod.py (by Mark Dickinson)
+
+# More test cases for deccheck.py.
+
+import random
+
+TEST_SIZE = 2
+
+
+def test_short_halfway_cases():
+ # exact halfway cases with a small number of significant digits
+ for k in 0, 5, 10, 15, 20:
+ # upper = smallest integer >= 2**54/5**k
+ upper = -(-2**54//5**k)
+ # lower = smallest odd number >= 2**53/5**k
+ lower = -(-2**53//5**k)
+ if lower % 2 == 0:
+ lower += 1
+ for i in range(10 * TEST_SIZE):
+ # Select a random odd n in [2**53/5**k,
+ # 2**54/5**k). Then n * 10**k gives a halfway case
+ # with small number of significant digits.
+ n, e = random.randrange(lower, upper, 2), k
+
+ # Remove any additional powers of 5.
+ while n % 5 == 0:
+ n, e = n // 5, e + 1
+ assert n % 10 in (1, 3, 7, 9)
+
+ # Try numbers of the form n * 2**p2 * 10**e, p2 >= 0,
+ # until n * 2**p2 has more than 20 significant digits.
+ digits, exponent = n, e
+ while digits < 10**20:
+ s = '{}e{}'.format(digits, exponent)
+ yield s
+ # Same again, but with extra trailing zeros.
+ s = '{}e{}'.format(digits * 10**40, exponent - 40)
+ yield s
+ digits *= 2
+
+ # Try numbers of the form n * 5**p2 * 10**(e - p5), p5
+ # >= 0, with n * 5**p5 < 10**20.
+ digits, exponent = n, e
+ while digits < 10**20:
+ s = '{}e{}'.format(digits, exponent)
+ yield s
+ # Same again, but with extra trailing zeros.
+ s = '{}e{}'.format(digits * 10**40, exponent - 40)
+ yield s
+ digits *= 5
+ exponent -= 1
+
+def test_halfway_cases():
+ # test halfway cases for the round-half-to-even rule
+ for i in range(1000):
+ for j in range(TEST_SIZE):
+ # bit pattern for a random finite positive (or +0.0) float
+ bits = random.randrange(2047*2**52)
+
+ # convert bit pattern to a number of the form m * 2**e
+ e, m = divmod(bits, 2**52)
+ if e:
+ m, e = m + 2**52, e - 1
+ e -= 1074
+
+ # add 0.5 ulps
+ m, e = 2*m + 1, e - 1
+
+ # convert to a decimal string
+ if e >= 0:
+ digits = m << e
+ exponent = 0
+ else:
+ # m * 2**e = (m * 5**-e) * 10**e
+ digits = m * 5**-e
+ exponent = e
+ s = '{}e{}'.format(digits, exponent)
+ yield s
+
+def test_boundaries():
+ # boundaries expressed as triples (n, e, u), where
+ # n*10**e is an approximation to the boundary value and
+ # u*10**e is 1ulp
+ boundaries = [
+ (10000000000000000000, -19, 1110), # a power of 2 boundary (1.0)
+ (17976931348623159077, 289, 1995), # overflow boundary (2.**1024)
+ (22250738585072013831, -327, 4941), # normal/subnormal (2.**-1022)
+ (0, -327, 4941), # zero
+ ]
+ for n, e, u in boundaries:
+ for j in range(1000):
+ for i in range(TEST_SIZE):
+ digits = n + random.randrange(-3*u, 3*u)
+ exponent = e
+ s = '{}e{}'.format(digits, exponent)
+ yield s
+ n *= 10
+ u *= 10
+ e -= 1
+
+def test_underflow_boundary():
+ # test values close to 2**-1075, the underflow boundary; similar
+ # to boundary_tests, except that the random error doesn't scale
+ # with n
+ for exponent in range(-400, -320):
+ base = 10**-exponent // 2**1075
+ for j in range(TEST_SIZE):
+ digits = base + random.randrange(-1000, 1000)
+ s = '{}e{}'.format(digits, exponent)
+ yield s
+
+def test_bigcomp():
+ for ndigs in 5, 10, 14, 15, 16, 17, 18, 19, 20, 40, 41, 50:
+ dig10 = 10**ndigs
+ for i in range(100 * TEST_SIZE):
+ digits = random.randrange(dig10)
+ exponent = random.randrange(-400, 400)
+ s = '{}e{}'.format(digits, exponent)
+ yield s
+
+def test_parsing():
+ # make '0' more likely to be chosen than other digits
+ digits = '000000123456789'
+ signs = ('+', '-', '')
+
+ # put together random short valid strings
+ # \d*[.\d*]?e
+ for i in range(1000):
+ for j in range(TEST_SIZE):
+ s = random.choice(signs)
+ intpart_len = random.randrange(5)
+ s += ''.join(random.choice(digits) for _ in range(intpart_len))
+ if random.choice([True, False]):
+ s += '.'
+ fracpart_len = random.randrange(5)
+ s += ''.join(random.choice(digits)
+ for _ in range(fracpart_len))
+ else:
+ fracpart_len = 0
+ if random.choice([True, False]):
+ s += random.choice(['e', 'E'])
+ s += random.choice(signs)
+ exponent_len = random.randrange(1, 4)
+ s += ''.join(random.choice(digits)
+ for _ in range(exponent_len))
+
+ if intpart_len + fracpart_len:
+ yield s
+
+test_particular = [
+ # squares
+ '1.00000000100000000025',
+ '1.0000000000000000000000000100000000000000000000000' #...
+ '00025',
+ '1.0000000000000000000000000000000000000000000010000' #...
+ '0000000000000000000000000000000000000000025',
+ '1.0000000000000000000000000000000000000000000000000' #...
+ '000001000000000000000000000000000000000000000000000' #...
+ '000000000025',
+ '0.99999999900000000025',
+ '0.9999999999999999999999999999999999999999999999999' #...
+ '999000000000000000000000000000000000000000000000000' #...
+ '000025',
+ '0.9999999999999999999999999999999999999999999999999' #...
+ '999999999999999999999999999999999999999999999999999' #...
+ '999999999999999999999999999999999999999990000000000' #...
+ '000000000000000000000000000000000000000000000000000' #...
+ '000000000000000000000000000000000000000000000000000' #...
+ '0000000000000000000000000000025',
+
+ '1.0000000000000000000000000000000000000000000000000' #...
+ '000000000000000000000000000000000000000000000000000' #...
+ '100000000000000000000000000000000000000000000000000' #...
+ '000000000000000000000000000000000000000000000000001',
+ '1.0000000000000000000000000000000000000000000000000' #...
+ '000000000000000000000000000000000000000000000000000' #...
+ '500000000000000000000000000000000000000000000000000' #...
+ '000000000000000000000000000000000000000000000000005',
+ '1.0000000000000000000000000000000000000000000000000' #...
+ '000000000100000000000000000000000000000000000000000' #...
+ '000000000000000000250000000000000002000000000000000' #...
+ '000000000000000000000000000000000000000000010000000' #...
+ '000000000000000000000000000000000000000000000000000' #...
+ '0000000000000000001',
+ '1.0000000000000000000000000000000000000000000000000' #...
+ '000000000100000000000000000000000000000000000000000' #...
+ '000000000000000000249999999999999999999999999999999' #...
+ '999999999999979999999999999999999999999999999999999' #...
+ '999999999999999999999900000000000000000000000000000' #...
+ '000000000000000000000000000000000000000000000000000' #...
+ '00000000000000000000000001',
+
+ '0.9999999999999999999999999999999999999999999999999' #...
+ '999999999900000000000000000000000000000000000000000' #...
+ '000000000000000000249999999999999998000000000000000' #...
+ '000000000000000000000000000000000000000000010000000' #...
+ '000000000000000000000000000000000000000000000000000' #...
+ '0000000000000000001',
+ '0.9999999999999999999999999999999999999999999999999' #...
+ '999999999900000000000000000000000000000000000000000' #...
+ '000000000000000000250000001999999999999999999999999' #...
+ '999999999999999999999999999999999990000000000000000' #...
+ '000000000000000000000000000000000000000000000000000' #...
+ '1',
+
+ # tough cases for ln etc.
+ '1.000000000000000000000000000000000000000000000000' #...
+ '00000000000000000000000000000000000000000000000000' #...
+ '00100000000000000000000000000000000000000000000000' #...
+ '00000000000000000000000000000000000000000000000000' #...
+ '0001',
+ '0.999999999999999999999999999999999999999999999999' #...
+ '99999999999999999999999999999999999999999999999999' #...
+ '99899999999999999999999999999999999999999999999999' #...
+ '99999999999999999999999999999999999999999999999999' #...
+ '99999999999999999999999999999999999999999999999999' #...
+ '9999'
+ ]
+
+
+TESTCASES = [
+ [x for x in test_short_halfway_cases()],
+ [x for x in test_halfway_cases()],
+ [x for x in test_boundaries()],
+ [x for x in test_underflow_boundary()],
+ [x for x in test_bigcomp()],
+ [x for x in test_parsing()],
+ test_particular
+]
+
+def un_randfloat():
+ for i in range(1000):
+ l = random.choice(TESTCASES[:6])
+ yield random.choice(l)
+ for v in test_particular:
+ yield v
+
+def bin_randfloat():
+ for i in range(1000):
+ l1 = random.choice(TESTCASES)
+ l2 = random.choice(TESTCASES)
+ yield random.choice(l1), random.choice(l2)
+
+def tern_randfloat():
+ for i in range(1000):
+ l1 = random.choice(TESTCASES)
+ l2 = random.choice(TESTCASES)
+ l3 = random.choice(TESTCASES)
+ yield random.choice(l1), random.choice(l2), random.choice(l3)
--- /dev/null
+#!/bin/sh
+
+#
+# Purpose: test with and without threads, all machine configurations, pydebug,
+# refleaks, release build and release build with valgrind.
+#
+# Synopsis: ./runall-memorydebugger.sh [--all-configs64 | --all-configs32]
+#
+# Requirements: valgrind
+#
+
+# Set additional CFLAGS for ./configure
+ADD_CFLAGS=
+
+
+CONFIGS_64="x64 uint128 ansi64 universal"
+CONFIGS_32="ppro ansi32 ansi-legacy universal"
+
+VALGRIND="valgrind --tool=memcheck --leak-resolution=high \
+ --db-attach=yes --suppressions=Misc/valgrind-python.supp"
+
+# Get args
+case $@ in
+ *--all-configs64*)
+ CONFIGS=$CONFIGS_64
+ ;;
+ *--all-configs32*)
+ CONFIGS=$CONFIGS_32
+ ;;
+ *)
+ CONFIGS="auto"
+ ;;
+esac
+
+# gmake required
+GMAKE=`which gmake`
+if [ X"$GMAKE" = X"" ]; then
+ GMAKE=make
+fi
+
+# Pretty print configurations
+print_config ()
+{
+ len=`echo $@ | wc -c`
+ margin="#%"`expr \( 74 - $len \) / 2`"s"
+
+ echo ""
+ echo "# ========================================================================"
+ printf $margin ""
+ echo $@
+ echo "# ========================================================================"
+ echo ""
+}
+
+
+cd ..
+
+# test_decimal: refleak, regular and Valgrind tests
+for args in "--without-threads" ""; do
+ for config in $CONFIGS; do
+
+ unset PYTHON_DECIMAL_WITH_MACHINE
+ libmpdec_config=$config
+ if [ X"$config" != X"auto" ]; then
+ PYTHON_DECIMAL_WITH_MACHINE=$config
+ export PYTHON_DECIMAL_WITH_MACHINE
+ else
+ libmpdec_config=""
+ fi
+
+ ############ refleak tests ###########
+ print_config "refleak tests: config=$config" $args
+ printf "\nbuilding python ...\n\n"
+
+ cd ../../
+ $GMAKE distclean > /dev/null 2>&1
+ ./configure CFLAGS="$ADD_CFLAGS" --with-pydebug $args > /dev/null 2>&1
+ $GMAKE | grep _decimal
+
+ printf "\n\n# ======================== refleak tests ===========================\n\n"
+ ./python -m test -uall -R 2:2 test_decimal
+
+
+ ############ regular tests ###########
+ print_config "regular tests: config=$config" $args
+ printf "\nbuilding python ...\n\n"
+
+ $GMAKE distclean > /dev/null 2>&1
+ ./configure CFLAGS="$ADD_CFLAGS" $args > /dev/null 2>&1
+ $GMAKE | grep _decimal
+
+ printf "\n\n# ======================== regular tests ===========================\n\n"
+ ./python -m test -uall test_decimal
+
+
+ ########### valgrind tests ###########
+ valgrind=$VALGRIND
+ case "$config" in
+ # Valgrind has no support for 80 bit long double arithmetic.
+ ppro) valgrind= ;;
+ auto) case `uname -m` in
+ i386|i486|i586|i686) valgrind= ;;
+ esac
+ esac
+
+ print_config "valgrind tests: config=$config" $args
+ printf "\nbuilding python ...\n\n"
+ $GMAKE distclean > /dev/null 2>&1
+ ./configure CFLAGS="$ADD_CFLAGS" --without-pymalloc $args > /dev/null 2>&1
+ $GMAKE | grep _decimal
+
+ printf "\n\n# ======================== valgrind tests ===========================\n\n"
+ $valgrind ./python -m test -uall test_decimal
+
+ cd Modules/_decimal
+ done
+done
+
+# deccheck
+cd ../../
+for config in $CONFIGS; do
+ for args in "--without-threads" ""; do
+
+ unset PYTHON_DECIMAL_WITH_MACHINE
+ if [ X"$config" != X"auto" ]; then
+ PYTHON_DECIMAL_WITH_MACHINE=$config
+ export PYTHON_DECIMAL_WITH_MACHINE
+ fi
+
+ ############ debug ############
+ print_config "deccheck: config=$config --with-pydebug" $args
+ printf "\nbuilding python ...\n\n"
+
+ $GMAKE distclean > /dev/null 2>&1
+ ./configure CFLAGS="$ADD_CFLAGS" --with-pydebug $args > /dev/null 2>&1
+ $GMAKE | grep _decimal
+
+ printf "\n\n# ========================== debug ===========================\n\n"
+ ./python Modules/_decimal/tests/deccheck.py
+
+ ########### regular ###########
+ print_config "deccheck: config=$config " $args
+ printf "\nbuilding python ...\n\n"
+
+ $GMAKE distclean > /dev/null 2>&1
+ ./configure CFLAGS="$ADD_CFLAGS" $args > /dev/null 2>&1
+ $GMAKE | grep _decimal
+
+ printf "\n\n# ======================== regular ===========================\n\n"
+ ./python Modules/_decimal/tests/deccheck.py
+
+ ########### valgrind ###########
+ valgrind=$VALGRIND
+ case "$config" in
+ # Valgrind has no support for 80 bit long double arithmetic.
+ ppro) valgrind= ;;
+ auto) case `uname -m` in
+ i386|i486|i586|i686) valgrind= ;;
+ esac
+ esac
+
+ print_config "valgrind deccheck: config=$config " $args
+ printf "\nbuilding python ...\n\n"
+
+ $GMAKE distclean > /dev/null 2>&1
+ ./configure CFLAGS="$ADD_CFLAGS" --without-pymalloc $args > /dev/null 2>&1
+ $GMAKE | grep _decimal
+
+ printf "\n\n# ======================== valgrind ==========================\n\n"
+ $valgrind ./python Modules/_decimal/tests/deccheck.py
+ done
+done
+
+
+
--- /dev/null
+@ECHO OFF
+
+rem Test all machine configurations, pydebug, refleaks, release build.
+
+cd ..
+
+call vcvarsall x64
+echo.
+echo # ======================================================================
+echo # test_decimal: platform=x64
+echo # ======================================================================
+echo.
+
+cd ..\..\PCbuild
+echo # ==================== refleak tests =======================
+echo.
+echo building python ...
+echo.
+vcbuild /clean pcbuild.sln > NUL 2>&1
+vcbuild pcbuild.sln "Debug|x64" > NUL 2>&1
+amd64\python_d.exe -m test -uall -R 2:2 test_decimal
+echo.
+
+echo # ==================== regular tests =======================
+echo.
+echo building python ...
+echo.
+vcbuild /clean pcbuild.sln > NUL 2>&1
+vcbuild pcbuild.sln "Release|x64" > NUL 2>&1
+amd64\python.exe -m test -uall test_decimal
+echo.
+echo.
+
+
+call vcvarsall x86
+echo.
+echo # ======================================================================
+echo # test_decimal: platform=x86
+echo # ======================================================================
+echo.
+
+echo # ==================== refleak tests =======================
+echo.
+echo building python ...
+echo.
+vcbuild /clean pcbuild.sln > NUL 2>&1
+vcbuild pcbuild.sln "Debug|win32" > NUL 2>&1
+python_d.exe -m test -uall -R 2:2 test_decimal
+echo.
+
+echo # ==================== regular tests =======================
+echo.
+echo building python ...
+echo.
+vcbuild /clean pcbuild.sln > NUL 2>&1
+vcbuild pcbuild.sln "Release|win32" > NUL 2>&1
+python.exe -m test -uall test_decimal
+echo.
+echo.
+
+
+call vcvarsall x64
+echo.
+echo # ======================================================================
+echo # deccheck: platform=x64
+echo # ======================================================================
+echo.
+echo.
+echo # ==================== debug build =======================
+echo.
+echo building python ...
+echo.
+vcbuild /clean pcbuild.sln > NUL 2>&1
+vcbuild pcbuild.sln "Debug|x64" > NUL 2>&1
+amd64\python_d.exe ..\Modules\_decimal\tests\deccheck.py
+echo.
+echo.
+
+echo # =================== release build ======================
+echo.
+echo building python ...
+echo.
+vcbuild /clean pcbuild.sln > NUL 2>&1
+vcbuild pcbuild.sln "Release|x64" > NUL 2>&1
+amd64\python.exe ..\Modules\_decimal\tests\deccheck.py
+echo.
+echo.
+
+
+call vcvarsall x86
+echo.
+echo # ======================================================================
+echo # deccheck: platform=x86
+echo # ======================================================================
+echo.
+echo.
+echo # ==================== debug build =======================
+echo.
+echo building python ...
+echo.
+vcbuild /clean pcbuild.sln > NUL 2>&1
+vcbuild pcbuild.sln "Debug|win32" > NUL 2>&1
+python_d.exe ..\Modules\_decimal\tests\deccheck.py
+echo.
+echo.
+
+echo # =================== release build ======================
+echo.
+echo building python ...
+echo.
+vcbuild /clean pcbuild.sln > NUL 2>&1
+vcbuild pcbuild.sln "Release|win32" > NUL 2>&1
+python.exe ..\Modules\_decimal\tests\deccheck.py
+echo.
+echo.
+
+
+cd ..\Modules\_decimal\tests
+
+
+
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="_decimal"
+ ProjectGUID="{0E9791DB-593A-465F-98BC-681011311617}"
+ RootNamespace="_decimal"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="2"
+ InheritedPropertySheets=".\pyd_d.vsprops"
+ CharacterSet="0"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\Modules\_decimal;..\Modules\_decimal\libmpdec"
+ AdditionalOptions="/D_CRT_SECURE_NO_WARNINGS /DCONFIG_32 /DPPRO /DMASM"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ BaseAddress="0x1D1A0000"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ ConfigurationType="2"
+ InheritedPropertySheets=".\pyd_d.vsprops;.\x64.vsprops"
+ CharacterSet="0"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\Modules\_decimal;..\Modules\_decimal\libmpdec"
+ AdditionalOptions="/D_CRT_SECURE_NO_WARNINGS /DCONFIG_64 /DMASM"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ BaseAddress="0x1D1A0000"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="2"
+ InheritedPropertySheets=".\pyd.vsprops"
+ CharacterSet="0"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\Modules\_decimal;..\Modules\_decimal\libmpdec"
+ AdditionalOptions="/D_CRT_SECURE_NO_WARNINGS /DCONFIG_32 /DPPRO /DMASM"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ SubSystem="0"
+ BaseAddress="0x1D1A0000"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ ConfigurationType="2"
+ InheritedPropertySheets=".\pyd.vsprops;.\x64.vsprops"
+ CharacterSet="0"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\Modules\_decimal;..\Modules\_decimal\libmpdec"
+ AdditionalOptions="/D_CRT_SECURE_NO_WARNINGS /DCONFIG_64 /DMASM"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ SubSystem="0"
+ BaseAddress="0x1D1A0000"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="PGInstrument|Win32"
+ ConfigurationType="2"
+ InheritedPropertySheets=".\pyd.vsprops;.\pginstrument.vsprops"
+ CharacterSet="0"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\Modules\_decimal;..\Modules\_decimal\libmpdec"
+ AdditionalOptions="/DCONFIG_32 /DPPRO /D_CRT_SECURE_NO_WARNINGS"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ SubSystem="0"
+ BaseAddress="0x1D1A0000"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="PGInstrument|x64"
+ ConfigurationType="2"
+ InheritedPropertySheets=".\pyd.vsprops;.\x64.vsprops;.\pginstrument.vsprops"
+ CharacterSet="0"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\Modules\_decimal;..\Modules\_decimal\libmpdec"
+ AdditionalOptions="/DCONFIG_64 /D_CRT_SECURE_NO_WARNINGS"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ SubSystem="0"
+ BaseAddress="0x1D1A0000"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="PGUpdate|Win32"
+ ConfigurationType="2"
+ InheritedPropertySheets=".\pyd.vsprops;.\pgupdate.vsprops"
+ CharacterSet="0"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\Modules\_decimal;..\Modules\_decimal\libmpdec"
+ AdditionalOptions="/DCONFIG_32 /DPPRO /D_CRT_SECURE_NO_WARNINGS"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ SubSystem="0"
+ BaseAddress="0x1D1A0000"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="PGUpdate|x64"
+ ConfigurationType="2"
+ InheritedPropertySheets=".\pyd.vsprops;.\x64.vsprops;.\pgupdate.vsprops"
+ CharacterSet="0"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\Modules\_decimal;..\Modules\_decimal\libmpdec"
+ AdditionalOptions="/DCONFIG_64 /D_CRT_SECURE_NO_WARNINGS"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ SubSystem="0"
+ BaseAddress="0x1D1A0000"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Header Files"
+ >
+ <File
+ RelativePath="..\Modules\_decimal\docstrings.h"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\basearith.h"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\bits.h"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\constants.h"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\convolute.h"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\crt.h"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\difradix2.h"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\fnt.h"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\fourstep.h"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\mpdecimal.h"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\numbertheory.h"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\sixstep.h"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\transpose.h"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\typearith.h"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\umodarith.h"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\vccompat.h"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\vcstdint.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Source Files"
+ >
+ <File
+ RelativePath="..\Modules\_decimal\_decimal.c"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\basearith.c"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\constants.c"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\context.c"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\convolute.c"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\crt.c"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\difradix2.c"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\fnt.c"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\fourstep.c"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\io.c"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\memory.c"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\mpdecimal.c"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\numbertheory.c"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\sixstep.c"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\transpose.c"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\_decimal\libmpdec\vcdiv64.asm"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ CommandLine="ml64 /nologo /c /Zi /Fo "$(IntDir)\vcdiv64.obj" "$(InputPath)"
"
+ Outputs="$(IntDir)\vcdiv64.obj"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ CommandLine="ml64 /nologo /c /Zi /Fo "$(IntDir)\vcdiv64.obj" "$(InputPath)"
"
+ Outputs="$(IntDir)\vcdiv64.obj"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="PGInstrument|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="PGInstrument|x64"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ CommandLine="ml64 /nologo /c /Fo "$(IntDir)\vcdiv64.obj" "$(InputPath)"
"
+ Outputs="$(IntDir)\vcdiv64.obj"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="PGUpdate|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="PGUpdate|x64"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ CommandLine="ml64 /nologo /c /Fo "$(IntDir)\vcdiv64.obj" "$(InputPath)"
"
+ Outputs="$(IntDir)\vcdiv64.obj"
+ />
+ </FileConfiguration>
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
{CF7AC3D1-E2DF-41D2-BEA6-1E2556CDEA26} = {CF7AC3D1-E2DF-41D2-BEA6-1E2556CDEA26}\r
EndProjectSection\r
EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_decimal", "_decimal.vcproj", "{0E9791DB-593A-465F-98BC-681011311617}"\r
+ ProjectSection(ProjectDependencies) = postProject\r
+ {CF7AC3D1-E2DF-41D2-BEA6-1E2556CDEA26} = {CF7AC3D1-E2DF-41D2-BEA6-1E2556CDEA26}\r
+ EndProjectSection\r
+EndProject\r
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_ctypes", "_ctypes.vcproj", "{0E9791DB-593A-465F-98BC-681011311618}"\r
ProjectSection(ProjectDependencies) = postProject\r
{CF7AC3D1-E2DF-41D2-BEA6-1E2556CDEA26} = {CF7AC3D1-E2DF-41D2-BEA6-1E2556CDEA26}\r
{28B5D777-DDF2-4B6B-B34F-31D938813856}.Release|Win32.Build.0 = Release|Win32\r
{28B5D777-DDF2-4B6B-B34F-31D938813856}.Release|x64.ActiveCfg = Release|x64\r
{28B5D777-DDF2-4B6B-B34F-31D938813856}.Release|x64.Build.0 = Release|x64\r
+ {0E9791DB-593A-465F-98BC-681011311617}.Debug|Win32.ActiveCfg = Debug|Win32\r
+ {0E9791DB-593A-465F-98BC-681011311617}.Debug|Win32.Build.0 = Debug|Win32\r
+ {0E9791DB-593A-465F-98BC-681011311617}.Debug|x64.ActiveCfg = Debug|x64\r
+ {0E9791DB-593A-465F-98BC-681011311617}.Debug|x64.Build.0 = Debug|x64\r
+ {0E9791DB-593A-465F-98BC-681011311617}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32\r
+ {0E9791DB-593A-465F-98BC-681011311617}.PGInstrument|Win32.Build.0 = PGInstrument|Win32\r
+ {0E9791DB-593A-465F-98BC-681011311617}.PGInstrument|x64.ActiveCfg = PGInstrument|x64\r
+ {0E9791DB-593A-465F-98BC-681011311617}.PGInstrument|x64.Build.0 = PGInstrument|x64\r
+ {0E9791DB-593A-465F-98BC-681011311617}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32\r
+ {0E9791DB-593A-465F-98BC-681011311617}.PGUpdate|Win32.Build.0 = PGUpdate|Win32\r
+ {0E9791DB-593A-465F-98BC-681011311617}.PGUpdate|x64.ActiveCfg = PGUpdate|x64\r
+ {0E9791DB-593A-465F-98BC-681011311617}.PGUpdate|x64.Build.0 = PGUpdate|x64\r
+ {0E9791DB-593A-465F-98BC-681011311617}.Release|Win32.ActiveCfg = Release|Win32\r
+ {0E9791DB-593A-465F-98BC-681011311617}.Release|Win32.Build.0 = Release|Win32\r
+ {0E9791DB-593A-465F-98BC-681011311617}.Release|x64.ActiveCfg = Release|x64\r
+ {0E9791DB-593A-465F-98BC-681011311617}.Release|x64.Build.0 = Release|x64\r
{0E9791DB-593A-465F-98BC-681011311618}.Debug|Win32.ActiveCfg = Debug|Win32\r
{0E9791DB-593A-465F-98BC-681011311618}.Debug|Win32.Build.0 = Debug|Win32\r
{0E9791DB-593A-465F-98BC-681011311618}.Debug|x64.ActiveCfg = Debug|x64\r
fi
+ac_fn_c_check_type "$LINENO" "__uint128_t" "ac_cv_type___uint128_t" "$ac_includes_default"
+if test "x$ac_cv_type___uint128_t" = xyes; then :
+
+$as_echo "#define HAVE_GCC_UINT128_T 1" >>confdefs.h
+
+fi
+
# Sizes of various common basic types
# ANSI C requires sizeof(char) == 1, so no need to check it
fi
+# **************************************
+# * Check for gcc x64 inline assembler *
+# **************************************
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for x64 gcc inline assembler" >&5
+$as_echo_n "checking for x64 gcc inline assembler... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ __asm__ __volatile__ ("movq %rcx, %rax");
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ have_gcc_asm_for_x64=yes
+else
+ have_gcc_asm_for_x64=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_gcc_asm_for_x64" >&5
+$as_echo "$have_gcc_asm_for_x64" >&6; }
+if test "$have_gcc_asm_for_x64" = yes
+then
+
+$as_echo "#define HAVE_GCC_ASM_FOR_X64 1" >>confdefs.h
+
+fi
+
# **************************************************
# * Check for various properties of floating point *
# **************************************************
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5
$as_echo "done" >&6; }
+# Availability of -O2:
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for -O2" >&5
+$as_echo_n "checking for -O2... " >&6; }
+saved_cflags="$CFLAGS"
+CFLAGS="-O2"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ have_O2=yes
+else
+ have_O2=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_O2" >&5
+$as_echo "$have_O2" >&6; }
+CFLAGS="$saved_cflags"
+
+# _FORTIFY_SOURCE wrappers for memmove and bcopy are incorrect:
+# http://sourceware.org/ml/libc-alpha/2010-12/msg00009.html
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for glibc _FORTIFY_SOURCE/memmove bug" >&5
+$as_echo_n "checking for glibc _FORTIFY_SOURCE/memmove bug... " >&6; }
+saved_cflags="$CFLAGS"
+CFLAGS="-O2 -D_FORTIFY_SOURCE=2"
+if test "$have_O2" = no; then
+ CFLAGS=""
+fi
+if test "$cross_compiling" = yes; then :
+ have_glibc_memmove_bug=undefined
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+void foo(void *p, void *q) { memmove(p, q, 19); }
+int main() {
+ char a[32] = "123456789000000000";
+ foo(&a[9], a);
+ if (strcmp(a, "123456789123456789000000000") != 0)
+ return 1;
+ foo(a, &a[9]);
+ if (strcmp(a, "123456789000000000") != 0)
+ return 1;
+ return 0;
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ have_glibc_memmove_bug=no
+else
+ have_glibc_memmove_bug=yes
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+CFLAGS="$saved_cflags"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_glibc_memmove_bug" >&5
+$as_echo "$have_glibc_memmove_bug" >&6; }
+if test "$have_glibc_memmove_bug" = yes; then
+
+$as_echo "#define HAVE_GLIBC_MEMMOVE_BUG 1" >>confdefs.h
+
+fi
+
+if test "$have_gcc_asm_for_x87" = yes; then
+ # Some versions of gcc miscompile inline asm:
+ # http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46491
+ # http://gcc.gnu.org/ml/gcc/2010-11/msg00366.html
+ case $CC in
+ *gcc*)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gcc ipa-pure-const bug" >&5
+$as_echo_n "checking for gcc ipa-pure-const bug... " >&6; }
+ saved_cflags="$CFLAGS"
+ CFLAGS="-O2"
+ if test "$cross_compiling" = yes; then :
+ have_ipa_pure_const_bug=undefined
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+ __attribute__((noinline)) int
+ foo(int *p) {
+ int r;
+ asm ( "movl \$6, (%1)\n\t"
+ "xorl %0, %0\n\t"
+ : "=r" (r) : "r" (p) : "memory"
+ );
+ return r;
+ }
+ int main() {
+ int p = 8;
+ if ((foo(&p) ? : p) != 6)
+ return 1;
+ return 0;
+ }
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ have_ipa_pure_const_bug=no
+else
+ have_ipa_pure_const_bug=yes
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+ CFLAGS="$saved_cflags"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_ipa_pure_const_bug" >&5
+$as_echo "$have_ipa_pure_const_bug" >&6; }
+ if test "$have_ipa_pure_const_bug" = yes; then
+
+$as_echo "#define HAVE_IPA_PURE_CONST_BUG 1" >>confdefs.h
+
+ fi
+ ;;
+ esac
+fi
+
# generate output files
ac_config_files="$ac_config_files Makefile.pre Modules/Setup.config Misc/python.pc"
AC_TYPE_INT64_T
AC_CHECK_TYPE(ssize_t,
AC_DEFINE(HAVE_SSIZE_T, 1, [Define if your compiler provides ssize_t]),,)
+AC_CHECK_TYPE(__uint128_t,
+ AC_DEFINE(HAVE_GCC_UINT128_T, 1, [Define if your compiler provides __uint128_t]),,)
# Sizes of various common basic types
# ANSI C requires sizeof(char) == 1, so no need to check it
fi],
[AC_MSG_RESULT(default LIBC="$LIBC")])
+# **************************************
+# * Check for gcc x64 inline assembler *
+# **************************************
+
+AC_MSG_CHECKING(for x64 gcc inline assembler)
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[
+ __asm__ __volatile__ ("movq %rcx, %rax");
+]])],[have_gcc_asm_for_x64=yes],[have_gcc_asm_for_x64=no])
+AC_MSG_RESULT($have_gcc_asm_for_x64)
+if test "$have_gcc_asm_for_x64" = yes
+then
+ AC_DEFINE(HAVE_GCC_ASM_FOR_X64, 1,
+ [Define if we can use x64 gcc inline assembler])
+fi
+
# **************************************************
# * Check for various properties of floating point *
# **************************************************
done
AC_MSG_RESULT(done)
+# Availability of -O2:
+AC_MSG_CHECKING(for -O2)
+saved_cflags="$CFLAGS"
+CFLAGS="-O2"
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[
+]])],[have_O2=yes],[have_O2=no])
+AC_MSG_RESULT($have_O2)
+CFLAGS="$saved_cflags"
+
+# _FORTIFY_SOURCE wrappers for memmove and bcopy are incorrect:
+# http://sourceware.org/ml/libc-alpha/2010-12/msg00009.html
+AC_MSG_CHECKING(for glibc _FORTIFY_SOURCE/memmove bug)
+saved_cflags="$CFLAGS"
+CFLAGS="-O2 -D_FORTIFY_SOURCE=2"
+if test "$have_O2" = no; then
+ CFLAGS=""
+fi
+AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+void foo(void *p, void *q) { memmove(p, q, 19); }
+int main() {
+ char a[32] = "123456789000000000";
+ foo(&a[9], a);
+ if (strcmp(a, "123456789123456789000000000") != 0)
+ return 1;
+ foo(a, &a[9]);
+ if (strcmp(a, "123456789000000000") != 0)
+ return 1;
+ return 0;
+}
+]])],
+[have_glibc_memmove_bug=no],
+[have_glibc_memmove_bug=yes],
+[have_glibc_memmove_bug=undefined])
+CFLAGS="$saved_cflags"
+AC_MSG_RESULT($have_glibc_memmove_bug)
+if test "$have_glibc_memmove_bug" = yes; then
+ AC_DEFINE(HAVE_GLIBC_MEMMOVE_BUG, 1,
+ [Define if glibc has incorrect _FORTIFY_SOURCE wrappers
+ for memmove and bcopy.])
+fi
+
+if test "$have_gcc_asm_for_x87" = yes; then
+ # Some versions of gcc miscompile inline asm:
+ # http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46491
+ # http://gcc.gnu.org/ml/gcc/2010-11/msg00366.html
+ case $CC in
+ *gcc*)
+ AC_MSG_CHECKING(for gcc ipa-pure-const bug)
+ saved_cflags="$CFLAGS"
+ CFLAGS="-O2"
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
+ __attribute__((noinline)) int
+ foo(int *p) {
+ int r;
+ asm ( "movl \$6, (%1)\n\t"
+ "xorl %0, %0\n\t"
+ : "=r" (r) : "r" (p) : "memory"
+ );
+ return r;
+ }
+ int main() {
+ int p = 8;
+ if ((foo(&p) ? : p) != 6)
+ return 1;
+ return 0;
+ }
+ ]])],
+ [have_ipa_pure_const_bug=no],
+ [have_ipa_pure_const_bug=yes],
+ [have_ipa_pure_const_bug=undefined])
+ CFLAGS="$saved_cflags"
+ AC_MSG_RESULT($have_ipa_pure_const_bug)
+ if test "$have_ipa_pure_const_bug" = yes; then
+ AC_DEFINE(HAVE_IPA_PURE_CONST_BUG, 1,
+ [Define if gcc has the ipa-pure-const bug.])
+ fi
+ ;;
+ esac
+fi
+
# generate output files
AC_CONFIG_FILES(Makefile.pre Modules/Setup.config Misc/python.pc)
AC_CONFIG_FILES([Modules/ld_so_aix], [chmod +x Modules/ld_so_aix])
/* Define to 1 if you have the `gamma' function. */
#undef HAVE_GAMMA
+/* Define if we can use x64 gcc inline assembler */
+#undef HAVE_GCC_ASM_FOR_X64
+
/* Define if we can use gcc inline assembler to get and set x87 control word
*/
#undef HAVE_GCC_ASM_FOR_X87
+/* Define if your compiler provides __uint128_t */
+#undef HAVE_GCC_UINT128_T
+
/* Define if you have the getaddrinfo function. */
#undef HAVE_GETADDRINFO
/* Define to 1 if you have the `getwd' function. */
#undef HAVE_GETWD
+/* Define if glibc has incorrect _FORTIFY_SOURCE wrappers for memmove and
+ bcopy. */
+#undef HAVE_GLIBC_MEMMOVE_BUG
+
/* Define to 1 if you have the <grp.h> header file. */
#undef HAVE_GRP_H
/* Define to 1 if you have the <io.h> header file. */
#undef HAVE_IO_H
+/* Define if gcc has the ipa-pure-const bug. */
+#undef HAVE_IPA_PURE_CONST_BUG
+
/* Define to 1 if you have the `kill' function. */
#undef HAVE_KILL
exts.append(Extension('_codecs_%s' % loc,
['cjkcodecs/_codecs_%s.c' % loc]))
+ # Stefan Krah's _decimal module
+ exts.append(self._decimal_ext())
+
# Thomas Heller's _ctypes module
self.detect_ctypes(inc_dirs, lib_dirs)
ext.libraries.append(ffi_lib)
self.use_system_libffi = True
+ def _decimal_ext(self):
+ sources = [
+ '_decimal/_decimal.c',
+ '_decimal/libmpdec/basearith.c',
+ '_decimal/libmpdec/constants.c',
+ '_decimal/libmpdec/context.c',
+ '_decimal/libmpdec/convolute.c',
+ '_decimal/libmpdec/crt.c',
+ '_decimal/libmpdec/difradix2.c',
+ '_decimal/libmpdec/fnt.c',
+ '_decimal/libmpdec/fourstep.c',
+ '_decimal/libmpdec/io.c',
+ '_decimal/libmpdec/memory.c',
+ '_decimal/libmpdec/mpdecimal.c',
+ '_decimal/libmpdec/numbertheory.c',
+ '_decimal/libmpdec/sixstep.c',
+ '_decimal/libmpdec/transpose.c',
+ ]
+ depends = [
+ '_decimal/docstrings.h',
+ '_decimal/libmpdec/basearith.h',
+ '_decimal/libmpdec/bits.h',
+ '_decimal/libmpdec/constants.h',
+ '_decimal/libmpdec/convolute.h',
+ '_decimal/libmpdec/crt.h',
+ '_decimal/libmpdec/difradix2.h',
+ '_decimal/libmpdec/fnt.h',
+ '_decimal/libmpdec/fourstep.h',
+ '_decimal/libmpdec/io.h',
+ '_decimal/libmpdec/memory.h',
+ '_decimal/libmpdec/mpdecimal.h',
+ '_decimal/libmpdec/numbertheory.h',
+ '_decimal/libmpdec/sixstep.h',
+ '_decimal/libmpdec/transpose.h',
+ '_decimal/libmpdec/typearith.h',
+ '_decimal/libmpdec/umodarith.h',
+ ]
+ config = {
+ 'x64': [('CONFIG_64','1'), ('ASM','1')],
+ 'uint128': [('CONFIG_64','1'), ('ANSI','1'), ('HAVE_UINT128_T','1')],
+ 'ansi64': [('CONFIG_64','1'), ('ANSI','1')],
+ 'ppro': [('CONFIG_32','1'), ('PPRO','1'), ('ASM','1')],
+ 'ansi32': [('CONFIG_32','1'), ('ANSI','1')],
+ 'ansi-legacy': [('CONFIG_32','1'), ('ANSI','1'),
+ ('LEGACY_COMPILER','1')],
+ 'universal': [('UNIVERSAL','1')]
+ }
+
+ include_dirs = ['./Modules/_decimal/libmpdec']
+ extra_compile_args = []
+ undef_macros=['NDEBUG']
+
+ platform = self.get_platform()
+ cc = sysconfig.get_config_var('CC')
+ sizeof_size_t = sysconfig.get_config_var('SIZEOF_SIZE_T')
+ machine = os.environ.get('PYTHON_DECIMAL_WITH_MACHINE')
+
+ if machine:
+ # Override automatic configuration to facilitate testing.
+ define_macros = config[machine]
+ elif platform == 'darwin':
+ # Universal here means: build with the same options Python
+ # was built with.
+ define_macros = config['universal']
+ elif sizeof_size_t == 8:
+ if sysconfig.get_config_var('HAVE_GCC_ASM_FOR_X64'):
+ define_macros = config['x64']
+ elif sysconfig.get_config_var('HAVE_GCC_UINT128_T'):
+ define_macros = config['uint128']
+ else:
+ define_macros = config['ansi64']
+ elif sizeof_size_t == 4:
+ ppro = sysconfig.get_config_var('HAVE_GCC_ASM_FOR_X87')
+ if ppro and ('gcc' in cc or 'clang' in cc) and \
+ not 'sunos' in platform:
+ # solaris: problems with register allocation.
+ # icc >= 11.0 works as well.
+ define_macros = config['ppro']
+ else:
+ define_macros = config['ansi32']
+ else:
+ raise DistutilsError("_decimal: unsupported architecture")
+
+ # Workarounds for toolchain bugs:
+ if sysconfig.get_config_var('HAVE_IPA_PURE_CONST_BUG'):
+ # Some versions of gcc miscompile inline asm:
+ # http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46491
+ # http://gcc.gnu.org/ml/gcc/2010-11/msg00366.html
+ extra_compile_args.append('-fno-ipa-pure-const')
+ if sysconfig.get_config_var('HAVE_GLIBC_MEMMOVE_BUG'):
+ # _FORTIFY_SOURCE wrappers for memmove and bcopy are incorrect:
+ # http://sourceware.org/ml/libc-alpha/2010-12/msg00009.html
+ undef_macros.append('_FORTIFY_SOURCE')
+
+ # Faster version without thread local contexts:
+ if not sysconfig.get_config_var('WITH_THREAD'):
+ define_macros.append(('WITHOUT_THREADS', 1))
+
+ # Uncomment for extra functionality:
+ #define_macros.append(('EXTRA_FUNCTIONALITY', 1))
+ ext = Extension (
+ '_decimal',
+ include_dirs=include_dirs,
+ define_macros=define_macros,
+ undef_macros=undef_macros,
+ extra_compile_args=extra_compile_args,
+ sources=sources,
+ depends=depends
+ )
+ return ext
class PyBuildInstall(install):
# Suppress the warning about installation into the lib_dynload