diff -r 203407036e46 -r 49433f35a5f8 .hgeol --- a/.hgeol Fri Jun 17 21:10:21 2011 +0200 +++ b/.hgeol Sat Jun 18 13:21:59 2011 +0200 @@ -31,6 +31,7 @@ Lib/test/decimaltestdata/*.decTest = BIN Lib/test/sndhdrdata/sndhdr.* = BIN Lib/test/test_email/data/msg_26.txt = BIN +**dnloop.patch = BIN # All other files (which presumably are human-editable) are "native". # This must be the last rule! diff -r 203407036e46 -r 49433f35a5f8 .hgignore --- a/.hgignore Fri Jun 17 21:10:21 2011 +0200 +++ b/.hgignore Sat Jun 18 13:21:59 2011 +0200 @@ -15,7 +15,7 @@ db_home platform$ pyconfig.h$ -python$ +^python$ python.exe$ reflog.txt$ tags$ diff -r 203407036e46 -r 49433f35a5f8 Doc/library/numeric.rst --- a/Doc/library/numeric.rst Fri Jun 17 21:10:21 2011 +0200 +++ b/Doc/library/numeric.rst Sat Jun 18 13:21:59 2011 +0200 @@ -8,9 +8,9 @@ The modules described in this chapter provide numeric and math-related functions and data types. The :mod:`numbers` module defines an abstract hierarchy of numeric types. The :mod:`math` and :mod:`cmath` modules contain various -mathematical functions for floating-point and complex numbers. For users more -interested in decimal accuracy than in speed, the :mod:`decimal` module supports -exact representations of decimal numbers. +mathematical functions for floating-point and complex numbers. The :mod:`decimal` +module supports exact representations of decimal numbers, using arbitrary precision +arithmetic. The following modules are documented in this chapter: @@ -20,6 +20,7 @@ numbers.rst math.rst cmath.rst + cdecimal.rst decimal.rst fractions.rst random.rst diff -r 203407036e46 -r 49433f35a5f8 Include/longintrepr.h --- a/Include/longintrepr.h Fri Jun 17 21:10:21 2011 +0200 +++ b/Include/longintrepr.h Sat Jun 18 13:21:59 2011 +0200 @@ -6,7 +6,7 @@ #endif -/* This is published for the benefit of "friend" marshal.c only. */ +/* This is published for the benefit of "friends" marshal.c and _decimal.c. */ /* Parameters of the long integer representation. There are two different sets of parameters: one set for 30-bit digits, stored in an unsigned 32-bit diff -r 203407036e46 -r 49433f35a5f8 Lib/decimal.py --- a/Lib/decimal.py Fri Jun 17 21:10:21 2011 +0200 +++ b/Lib/decimal.py Sat Jun 18 13:21:59 2011 +0200 @@ -46,8 +46,8 @@ Decimal('-0.0123') >>> Decimal(123456) Decimal('123456') ->>> Decimal('123.45e12345678901234567890') -Decimal('1.2345E+12345678901234567892') +>>> Decimal('123.45e12345678') +Decimal('1.2345E+12345680') >>> Decimal('1.33') + Decimal('1.27') Decimal('2.60') >>> Decimal('12.34') + Decimal('3.87') - Decimal('18.41') @@ -380,6 +380,10 @@ DivisionUndefined:InvalidOperation, InvalidContext:InvalidOperation} +# Valid rounding modes +_rounding_modes = (ROUND_DOWN, ROUND_HALF_UP, ROUND_HALF_EVEN, ROUND_CEILING, + ROUND_FLOOR, ROUND_UP, ROUND_HALF_DOWN, ROUND_05UP) + ##### Context Functions ################################################## # The getcontext() and setcontext() function manage access to a thread-local @@ -684,7 +688,9 @@ """ if isinstance(f, int): # handle integer inputs return cls(f) - if _math.isinf(f) or _math.isnan(f): # raises TypeError if not a float + if not isinstance(f, float): + raise TypeError("argument must be int or float.") + if _math.isinf(f) or _math.isnan(f): return cls(repr(f)) if _math.copysign(1.0, f) == 1.0: sign = 0 @@ -3832,11 +3838,9 @@ clamp - If 1, change exponents if too high (Default 0) """ - def __init__(self, prec=None, rounding=None, - traps=None, flags=None, - Emin=None, Emax=None, - capitals=None, clamp=None, - _ignored_flags=None): + def __init__(self, prec=None, rounding=None, Emin=None, Emax=None, + capitals=None, clamp=None, flags=None, traps=None, + _ignored_flags=None): # Set defaults; for everything except flags and _ignored_flags, # inherit from DefaultContext. try: @@ -3859,17 +3863,78 @@ if traps is None: self.traps = dc.traps.copy() elif not isinstance(traps, dict): - self.traps = dict((s, int(s in traps)) for s in _signals) + self.traps = dict((s, int(s in traps)) for s in _signals + traps) else: self.traps = traps if flags is None: self.flags = dict.fromkeys(_signals, 0) elif not isinstance(flags, dict): - self.flags = dict((s, int(s in flags)) for s in _signals) + self.flags = dict((s, int(s in flags)) for s in _signals + flags) else: self.flags = flags + def _set_integer_check(self, name, value, vmin, vmax): + if not isinstance(value, int): + raise TypeError("%s must be an integer" % name) + if vmin == '-inf': + if value > vmax: + raise ValueError("%s must be in [%s, %d]. got: %s" % (name, vmin, vmax, value)) + elif vmax == 'inf': + if value < vmin: + raise ValueError("%s must be in [%d, %s]. got: %s" % (name, vmin, vmax, value)) + else: + if value < vmin or value > vmax: + raise ValueError("%s must be in [%d, %d]. got %s" % (name, vmin, vmax, value)) + return object.__setattr__(self, name, value) + + def _set_signal_dict(self, name, d): + if not isinstance(d, dict): + raise TypeError("%s must be a signal dict" % d) + for key in d: + if not key in _signals: + raise TypeError("%s is not a valid signal dict" % d) + for key in _signals: + if not key in d: + raise TypeError("%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 = [] @@ -3890,41 +3955,20 @@ 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 - # _clamp is provided for backwards compatibility with third-party - # code. May be removed in Python >= 3.3. - def _get_clamp(self): - "_clamp mirrors the clamp attribute. Its use is deprecated." - import warnings - warnings.warn('Use of the _clamp attribute is deprecated. ' - 'Please use clamp instead.', - DeprecationWarning) - return self.clamp - - def _set_clamp(self, clamp): - "_clamp mirrors the clamp attribute. Its use is deprecated." - import warnings - warnings.warn('Use of the _clamp attribute is deprecated. ' - 'Please use clamp instead.', - DeprecationWarning) - self.clamp = clamp - - # don't bother with _del_clamp; no sane 3rd party code should - # be deleting the _clamp attribute - _clamp = property(_get_clamp, _set_clamp) - def _raise_error(self, condition, explanation = None, *args): """Handles an error @@ -4084,6 +4128,8 @@ >>> ExtendedContext.canonical(Decimal('2.50')) Decimal('2.50') """ + if not isinstance(a, Decimal): + raise TypeError("canonical requires a Decimal as an argument.") return a.canonical(context=self) def compare(self, a, b): @@ -4394,6 +4440,8 @@ >>> ExtendedContext.is_canonical(Decimal('2.50')) True """ + if not isinstance(a, Decimal): + raise TypeError("is_canonical requires a Decimal as an argument.") return a.is_canonical() def is_finite(self, a): @@ -4986,7 +5034,7 @@ +Normal +Infinity - >>> c = Context(ExtendedContext) + >>> c = ExtendedContext.copy() >>> c.Emin = -999 >>> c.Emax = 999 >>> c.number_class(Decimal('Infinity')) @@ -6281,11 +6329,21 @@ # 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) +if not '_decimal' in sys.modules: + try: + import _decimal + s1 = set(dir()) + s2 = set(dir(_decimal)) + for name in s1 - s2: + del globals()[name] + del s1, s2, name + from _decimal import * + except: + del sys if __name__ == '__main__': import doctest, decimal diff -r 203407036e46 -r 49433f35a5f8 Lib/test/support.py --- a/Lib/test/support.py Fri Jun 17 21:10:21 2011 +0200 +++ b/Lib/test/support.py Sat Jun 18 13:21:59 2011 +0200 @@ -1283,7 +1283,7 @@ #======================================================================= # doctest driver. -def run_doctest(module, verbosity=None): +def run_doctest(module, verbosity=None, optionflags=0): """Run doctest on the given module. Return (#failures, #tests). If optional argument verbosity is not specified (or is None), pass @@ -1303,7 +1303,8 @@ save_stdout = sys.stdout sys.stdout = get_original_stdout() try: - 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)) finally: diff -r 203407036e46 -r 49433f35a5f8 Lib/test/test_decimal.py --- a/Lib/test/test_decimal.py Fri Jun 17 21:10:21 2011 +0200 +++ b/Lib/test/test_decimal.py Sat Jun 18 13:21:59 2011 +0200 @@ -16,7 +16,7 @@ Cowlishaw's tests can be downloaded from: - www2.hursley.ibm.com/decimal/dectest.zip + http://speleotrove.com/decimal/dectest.zip This test module can be called from command line with one parameter (Arithmetic or Behaviour) to test each part, or without parameter to test both parts. If @@ -30,37 +30,145 @@ import warnings import pickle, copy import unittest -from decimal import * import numbers +import locale from test.support import (run_unittest, run_doctest, is_resource_enabled, requires_IEEE_754) -from test.support import check_warnings +from test.support import check_warnings, import_fresh_module, TestFailed import random +import time +import warnings try: import threading except ImportError: threading = None + +C = import_fresh_module('decimal', fresh=['_decimal']) +P = import_fresh_module('decimal', blocked=['_decimal']) +orig_sys_decimal = sys.modules['decimal'] + +# fractions module must import the correct decimal module. +cfractions = import_fresh_module('fractions', fresh=['fractions']) +sys.modules['decimal'] = P +pfractions = import_fresh_module('fractions', fresh=['fractions']) +sys.modules['decimal'] = C +fractions = {C:cfractions, P:pfractions} +sys.modules['decimal'] = orig_sys_decimal + +# Testing all failures of API functions for _decimal. First, +# the number of API calls in a test case is determined. Then, +# the testcase is run with all possible API failures, checking +# that FailAPIException is properly raised. +def assertRaises(expEx, func, *args, **kwargs): + """assertRaises has to reraise FailAPIException.""" + try: + func(*args, **kwargs) + except Exception as e: + if e.__class__ is C.FailAPIException: + raise C.FailAPIException + if not e.__class__ is expEx: + raise e + +def withFailpoint(func): + """Wrap a function for testing all possible API failures.""" + def iter_failpoint(testcase, *args): + # These tests will not work. + setattr(testcase, 'assertRaises', assertRaises) + # Determine number of API calls. + C.setapicalls(0) + C.setfailpoint(0) + func(testcase, *args) + n = C.getapicalls() + # Fail at each possible API call. + for i in range(1, n+1): + C.setapicalls(0) + C.setfailpoint(i) + try: + func(testcase, *args) + except C.FailAPIException: + continue + # Error: FailAPIException was not raised + raise TestFailed("FailAPIException not raised in: %s" % func) + C.setapicalls(0) + C.setfailpoint(0) + return iter_failpoint + +class ProtectFail(object): + """Protect code regions that modify global state (e.g. lines + that set or restore global context values). Otherwise it + would not be possible to rerun a test case several times.""" + def __enter__(self): + if hasattr(C, 'setfailpoint'): + self.calls = C.getapicalls() + self.fpoint = C.getfailpoint() + C.setfailpoint(0) + def __exit__(self, *_): + if hasattr(C, 'setfailpoint'): + C.setfailpoint(self.fpoint) + if self.fpoint: + if self.calls < self.fpoint <= C.getapicalls(): + # Pretend that API calls in the protected block failed. + raise C.FailAPIException + +def protectfail(): + return ProtectFail() + +if hasattr(C, 'setfailpoint'): + # Functions that are iterated several times must use + # the same random sequence each time. + randseed = int(time.time()) + # Implicit initialization of the module context must + # be tested first. + for i in range(1, 100): + C.setapicalls(0) + C.setfailpoint(i) + try: + C.getcontext() + except C.FailAPIException as e: + continue + C.setapicalls(0) + C.setfailpoint(0) + # Useful Test Constant -Signals = tuple(getcontext().flags.keys()) - +Signals = { + C: tuple(C.getcontext().flags.keys()) if C else None, + P: tuple(P.getcontext().flags.keys()) +} # Signals ordered with respect to precedence: when an operation # produces multiple signals, signals occurring later in the list # should be handled before those occurring earlier in the list. -OrderedSignals = (Clamped, Rounded, Inexact, Subnormal, - Underflow, Overflow, DivisionByZero, InvalidOperation) +OrderedSignals = { + C: [C.Clamped, C.Rounded, C.Inexact, C.Subnormal, C.Underflow, + C.Overflow, C.DivisionByZero, C.InvalidOperation] if C else None, + P: [P.Clamped, P.Rounded, P.Inexact, P.Subnormal, P.Underflow, + P.Overflow, P.DivisionByZero, P.InvalidOperation] +} +def assert_signals(cls, context, attr, expected): + d = getattr(context, attr) + cls.assertTrue(all(d[s] if s in expected else not d[s] for s in d)) + +RoundingModes = { + C: (C.ROUND_UP, C.ROUND_DOWN, C.ROUND_CEILING, C.ROUND_FLOOR, + C.ROUND_HALF_UP, C.ROUND_HALF_DOWN, C.ROUND_HALF_EVEN, + C.ROUND_05UP) if C else None, + P: (P.ROUND_UP, P.ROUND_DOWN, P.ROUND_CEILING, P.ROUND_FLOOR, + P.ROUND_HALF_UP, P.ROUND_HALF_DOWN, P.ROUND_HALF_EVEN, + P.ROUND_05UP) +} # Tests are built around these assumed context defaults. # test_main() restores the original context. -def init(): - global ORIGINAL_CONTEXT - ORIGINAL_CONTEXT = getcontext().copy() - DefaultTestContext = Context( - prec = 9, - rounding = ROUND_HALF_EVEN, - traps = dict.fromkeys(Signals, 0) - ) - setcontext(DefaultTestContext) +ORIGINAL_CONTEXT = { + C: C.getcontext().copy() if C else None, + P: P.getcontext().copy() +} +def init(m): + if not m: return + DefaultTestContext = m.Context( + prec=9, rounding=m.ROUND_HALF_EVEN, traps=dict.fromkeys(Signals[m], 0) + ) + m.setcontext(DefaultTestContext) TESTDATADIR = 'decimaltestdata' if __name__ == '__main__': @@ -72,149 +180,168 @@ skip_expected = not os.path.isdir(directory) -# list of individual .decTest test ids that correspond to tests that -# we're skipping for one reason or another. -skipped_test_ids = set([ - # Skip implementation-specific scaleb tests. - 'scbx164', - 'scbx165', - - # For some operations (currently exp, ln, log10, power), the decNumber - # reference implementation imposes additional restrictions on the context - # and operands. These restrictions are not part of the specification; - # however, the effect of these restrictions does show up in some of the - # testcases. We skip testcases that violate these restrictions, since - # Decimal behaves differently from decNumber for these testcases so these - # testcases would otherwise fail. - 'expx901', - 'expx902', - 'expx903', - 'expx905', - 'lnx901', - 'lnx902', - 'lnx903', - 'lnx905', - 'logx901', - 'logx902', - 'logx903', - 'logx905', - 'powx1183', - 'powx1184', - 'powx4001', - 'powx4002', - 'powx4003', - 'powx4005', - 'powx4008', - 'powx4010', - 'powx4012', - 'powx4014', - ]) - # Make sure it actually raises errors when not expected and caught in flags # Slower, since it runs some things several times. EXTENDEDERRORTEST = False -#Map the test cases' error names to the actual errors -ErrorNames = {'clamped' : Clamped, - 'conversion_syntax' : InvalidOperation, - 'division_by_zero' : DivisionByZero, - 'division_impossible' : InvalidOperation, - 'division_undefined' : InvalidOperation, - 'inexact' : Inexact, - 'invalid_context' : InvalidOperation, - 'invalid_operation' : InvalidOperation, - 'overflow' : Overflow, - 'rounded' : Rounded, - 'subnormal' : Subnormal, - 'underflow' : Underflow} - - -def Nonfunction(*args): - """Doesn't do anything.""" - return None - -RoundingDict = {'ceiling' : ROUND_CEILING, #Maps test-case names to roundings. - 'down' : ROUND_DOWN, - 'floor' : ROUND_FLOOR, - 'half_down' : ROUND_HALF_DOWN, - 'half_even' : ROUND_HALF_EVEN, - 'half_up' : ROUND_HALF_UP, - 'up' : ROUND_UP, - '05up' : ROUND_05UP} - -# Name adapter to be able to change the Decimal and Context -# interface without changing the test files from Cowlishaw -nameAdapter = {'and':'logical_and', - 'apply':'_apply', - 'class':'number_class', - 'comparesig':'compare_signal', - 'comparetotal':'compare_total', - 'comparetotmag':'compare_total_mag', - 'copy':'copy_decimal', - 'copyabs':'copy_abs', - 'copynegate':'copy_negate', - 'copysign':'copy_sign', - 'divideint':'divide_int', - 'invert':'logical_invert', - 'iscanonical':'is_canonical', - 'isfinite':'is_finite', - 'isinfinite':'is_infinite', - 'isnan':'is_nan', - 'isnormal':'is_normal', - 'isqnan':'is_qnan', - 'issigned':'is_signed', - 'issnan':'is_snan', - 'issubnormal':'is_subnormal', - 'iszero':'is_zero', - 'maxmag':'max_mag', - 'minmag':'min_mag', - 'nextminus':'next_minus', - 'nextplus':'next_plus', - 'nexttoward':'next_toward', - 'or':'logical_or', - 'reduce':'normalize', - 'remaindernear':'remainder_near', - 'samequantum':'same_quantum', - 'squareroot':'sqrt', - 'toeng':'to_eng_string', - 'tointegral':'to_integral_value', - 'tointegralx':'to_integral_exact', - 'tosci':'to_sci_string', - 'xor':'logical_xor', - } - -# The following functions return True/False rather than a Decimal instance - -LOGICAL_FUNCTIONS = ( - 'is_canonical', - 'is_finite', - 'is_infinite', - 'is_nan', - 'is_normal', - 'is_qnan', - 'is_signed', - 'is_snan', - 'is_subnormal', - 'is_zero', - 'same_quantum', - ) - -class DecimalTest(unittest.TestCase): - """Class which tests the Decimal class against the test cases. - - Changed for unittest. - """ + +class IBMTestCases(unittest.TestCase): + """Class which tests the Decimal class against the IBM test cases.""" + def setUp(self): - self.context = Context() + self.context = self.decimal.Context() + self.readcontext = self.decimal.Context() self.ignore_list = ['#'] - # Basically, a # means return NaN InvalidOperation. - # Different from a sNaN in trim - + + # List of individual .decTest test ids that correspond to tests that + # we're skipping for one reason or another. + self.skipped_test_ids = set([ + # Skip implementation-specific scaleb tests. + 'scbx164', + 'scbx165', + + # For some operations (currently exp, ln, log10, power), the decNumber + # reference implementation imposes additional restrictions on the context + # and operands. These restrictions are not part of the specification; + # however, the effect of these restrictions does show up in some of the + # testcases. We skip testcases that violate these restrictions, since + # Decimal behaves differently from decNumber for these testcases so these + # testcases would otherwise fail. + 'expx901', + 'expx902', + 'expx903', + 'expx905', + 'lnx901', + 'lnx902', + 'lnx903', + 'lnx905', + 'logx901', + 'logx902', + 'logx903', + 'logx905', + 'powx1183', + 'powx1184', + 'powx4001', + 'powx4002', + 'powx4003', + 'powx4005', + 'powx4008', + 'powx4010', + 'powx4012', + 'powx4014', + ]) + + if self.decimal == C: + # status has additional Subnormal, Underflow + self.skipped_test_ids.add('pwsx803') + self.skipped_test_ids.add('pwsx805') + # Correct rounding (skipped for decNumber, too) + self.skipped_test_ids.add('powx4302') + self.skipped_test_ids.add('powx4303') + self.skipped_test_ids.add('powx4342') + self.skipped_test_ids.add('powx4343') + # http://bugs.python.org/issue7049 + self.skipped_test_ids.add('pwmx325') + self.skipped_test_ids.add('pwmx326') + + # Map test directives to setter functions. self.ChangeDict = {'precision' : self.change_precision, - 'rounding' : self.change_rounding_method, - 'maxexponent' : self.change_max_exponent, - 'minexponent' : self.change_min_exponent, - 'clamp' : self.change_clamp} + 'rounding' : self.change_rounding_method, + 'maxexponent' : self.change_max_exponent, + 'minexponent' : self.change_min_exponent, + 'clamp' : self.change_clamp} + + # Name adapter to be able to change the Decimal and Context + # interface without changing the test files from Cowlishaw. + self.NameAdapter = {'and':'logical_and', + 'apply':'_apply', + 'class':'number_class', + 'comparesig':'compare_signal', + 'comparetotal':'compare_total', + 'comparetotmag':'compare_total_mag', + 'copy':'copy_decimal', + 'copyabs':'copy_abs', + 'copynegate':'copy_negate', + 'copysign':'copy_sign', + 'divideint':'divide_int', + 'invert':'logical_invert', + 'iscanonical':'is_canonical', + 'isfinite':'is_finite', + 'isinfinite':'is_infinite', + 'isnan':'is_nan', + 'isnormal':'is_normal', + 'isqnan':'is_qnan', + 'issigned':'is_signed', + 'issnan':'is_snan', + 'issubnormal':'is_subnormal', + 'iszero':'is_zero', + 'maxmag':'max_mag', + 'minmag':'min_mag', + 'nextminus':'next_minus', + 'nextplus':'next_plus', + 'nexttoward':'next_toward', + 'or':'logical_or', + 'reduce':'normalize', + 'remaindernear':'remainder_near', + 'samequantum':'same_quantum', + 'squareroot':'sqrt', + 'toeng':'to_eng_string', + 'tointegral':'to_integral_value', + 'tointegralx':'to_integral_exact', + 'tosci':'to_sci_string', + 'xor':'logical_xor'} + + # Map test-case names to roundings. + self.RoundingDict = {'ceiling' : self.decimal.ROUND_CEILING, + 'down' : self.decimal.ROUND_DOWN, + 'floor' : self.decimal.ROUND_FLOOR, + 'half_down' : self.decimal.ROUND_HALF_DOWN, + 'half_even' : self.decimal.ROUND_HALF_EVEN, + 'half_up' : self.decimal.ROUND_HALF_UP, + 'up' : self.decimal.ROUND_UP, + '05up' : self.decimal.ROUND_05UP} + + # Map the test cases' error names to the actual errors. + self.ErrorNames = {'clamped' : self.decimal.Clamped, + 'conversion_syntax' : self.decimal.InvalidOperation, + 'division_by_zero' : self.decimal.DivisionByZero, + 'division_impossible' : self.decimal.InvalidOperation, + 'division_undefined' : self.decimal.InvalidOperation, + 'inexact' : self.decimal.Inexact, + 'invalid_context' : self.decimal.InvalidOperation, + 'invalid_operation' : self.decimal.InvalidOperation, + 'overflow' : self.decimal.Overflow, + 'rounded' : self.decimal.Rounded, + 'subnormal' : self.decimal.Subnormal, + 'underflow' : self.decimal.Underflow} + + # The following functions return True/False rather than a + # Decimal instance. + self.LogicalFunctions = ('is_canonical', + 'is_finite', + 'is_infinite', + 'is_nan', + 'is_normal', + 'is_qnan', + 'is_signed', + 'is_snan', + 'is_subnormal', + 'is_zero', + 'same_quantum') + + def read_unlimited(self, v, context): + """Work around the limitations of the 32-bit _decimal version. The + guaranteed maximum values for prec, Emax etc. are 425000000, + but higher values usually work, except for rare corner cases. + In particular, all of the IBM tests pass with maximum values + of 1070000000.""" + if self.decimal == C and self.decimal.MAX_EMAX == 425000000: + self.readcontext.unsafe_setprec(1070000000) + self.readcontext.unsafe_setemax(1070000000) + self.readcontext.unsafe_setemin(-1070000000) + return self.readcontext.create_decimal(v) + else: + return self.decimal.Decimal(v, context) def eval_file(self, file): global skip_expected @@ -227,7 +354,7 @@ #print line try: t = self.eval_line(line) - except DecimalException as exception: + except self.decimal.DecimalException as exception: #Exception raised where there shouldn't have been one. self.fail('Exception "'+exception.__class__.__name__ + '" raised on line '+line) @@ -254,23 +381,25 @@ 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: + + if not TEST_ALL and not hasattr(C, 'setfailpoint') and \ + random.random() < 0.90: return + with protectfail(): + self.context.clear_flags() + try: Sides = s.split('->') L = Sides[0].strip().split() @@ -283,26 +412,26 @@ ans = L[0] exceptions = L[1:] except (TypeError, AttributeError, IndexError): - raise InvalidOperation + raise self.decimal.InvalidOperation def FixQuotes(val): val = val.replace("''", 'SingleQuote').replace('""', 'DoubleQuote') val = val.replace("'", '').replace('"', '') val = val.replace('SingleQuote', "'").replace('DoubleQuote', '"') return val - if id in skipped_test_ids: + if id in self.skipped_test_ids: return - fname = nameAdapter.get(funct, funct) + fname = self.NameAdapter.get(funct, funct) if fname == 'rescale': return funct = getattr(self.context, fname) vals = [] conglomerate = '' quote = 0 - theirexceptions = [ErrorNames[x.lower()] for x in exceptions] - - for exception in Signals: + theirexceptions = [self.ErrorNames[x.lower()] for x in exceptions] + + for exception in Signals[self.decimal]: self.context.traps[exception] = 1 #Catch these bugs... for exception in theirexceptions: self.context.traps[exception] = 0 @@ -324,7 +453,7 @@ funct(self.context.create_decimal(v)) except error: pass - except Signals as e: + except Signals[self.decimal] as e: self.fail("Raised %s in %s when %s disabled" % \ (e, s, error)) else: @@ -332,11 +461,18 @@ 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) + # three argument power/powmod (deprecated) + if self.decimal == C: + if fname == 'power' and len(vals) == 3: + # name is different + fname = 'powmod' + funct = getattr(self.context, fname) + if EXTENDEDERRORTEST and fname not in ('to_sci_string', 'to_eng_string'): for error in theirexceptions: self.context.traps[error] = 1 @@ -344,7 +480,7 @@ funct(*vals) except error: pass - except Signals as e: + except Signals[self.decimal] as e: self.fail("Raised %s in %s when %s disabled" % \ (e, s, error)) else: @@ -352,14 +488,14 @@ self.context.traps[error] = 0 # as above, but add traps cumulatively, to check precedence - ordered_errors = [e for e in OrderedSignals if e in theirexceptions] + ordered_errors = [e for e in OrderedSignals[self.decimal] if e in theirexceptions] for error in ordered_errors: self.context.traps[error] = 1 try: funct(*vals) except error: pass - except Signals as e: + except Signals[self.decimal] as e: self.fail("Raised %s in %s; expected %s" % (type(e), s, error)) else: @@ -373,54 +509,71 @@ 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 + except Exception as e: #Catch any error long enough to state the test case. + # Errors are expected with failpoints. + if not hasattr(C, 'setfailpoint'): + print("ERROR:", s) + raise e.__class__ myexceptions = self.getexceptions() - self.context.clear_flags() myexceptions.sort(key=repr) theirexceptions.sort(key=repr) self.assertEqual(result, ans, 'Incorrect answer for ' + s + ' -- got ' + result) + self.assertEqual(myexceptions, theirexceptions, 'Incorrect flags set in ' + s + ' -- got ' + str(myexceptions)) return def getexceptions(self): - return [e for e in Signals if self.context.flags[e]] + return [e for e in Signals[self.decimal] if self.context.flags[e]] def change_precision(self, prec): - self.context.prec = prec + if self.decimal == C and self.decimal.MAX_PREC == 425000000: + self.context.unsafe_setprec(prec) + else: + self.context.prec = prec def change_rounding_method(self, rounding): self.context.rounding = rounding def change_min_exponent(self, exp): - self.context.Emin = exp + if self.decimal == C and self.decimal.MAX_PREC == 425000000: + self.context.unsafe_setemin(exp) + else: + self.context.Emin = exp def change_max_exponent(self, exp): - self.context.Emax = exp + if self.decimal == C and self.decimal.MAX_PREC == 425000000: + self.context.unsafe_setemax(exp) + else: + self.context.Emax = exp def change_clamp(self, clamp): self.context.clamp = clamp - +class CIBMTestCases(IBMTestCases): + decimal = C +class PyIBMTestCases(IBMTestCases): + decimal = P # The following classes test the behaviour of Decimal according to PEP 327 -class DecimalExplicitConstructionTest(unittest.TestCase): +class ExplicitConstructionTest(unittest.TestCase): '''Unit tests for Explicit Construction cases of Decimal.''' def test_explicit_empty(self): + Decimal = self.decimal.Decimal self.assertEqual(Decimal(), Decimal("0")) def test_explicit_from_None(self): + Decimal = self.decimal.Decimal self.assertRaises(TypeError, Decimal, None) def test_explicit_from_int(self): + Decimal = self.decimal.Decimal #positive d = Decimal(45) @@ -438,7 +591,18 @@ d = Decimal(0) self.assertEqual(str(d), '0') + # single word longs + for n in range(0, 32): + for sign in (-1, 1): + for x in range(-5, 5): + i = sign * (2**n + x) + d = Decimal(i) + self.assertEqual(str(d), str(i)) + def test_explicit_from_string(self): + Decimal = self.decimal.Decimal + InvalidOperation = self.decimal.InvalidOperation + localcontext = self.decimal.localcontext #empty self.assertEqual(str(Decimal('')), 'NaN') @@ -458,8 +622,17 @@ #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') + + 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") def test_explicit_from_tuples(self): + Decimal = self.decimal.Decimal #zero d = Decimal( (0, (0,), 0) ) @@ -477,6 +650,10 @@ d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) ) self.assertEqual(str(d), '-4.34913534E-17') + #inf + d = Decimal( (0, (), "F") ) + self.assertEqual(str(d), 'Infinity') + #wrong number of items self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1)) ) @@ -491,45 +668,50 @@ 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_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): + if hasattr(C, 'setfailpoint'): + random.seed(randseed) + + Decimal = self.decimal.Decimal + r = Decimal(0.1) self.assertEqual(type(r), Decimal) self.assertEqual(str(r), @@ -550,8 +732,11 @@ self.assertEqual(x, float(Decimal(x))) # roundtrip def test_explicit_context_create_decimal(self): - - nc = copy.copy(getcontext()) + Decimal = self.decimal.Decimal + InvalidOperation = self.decimal.InvalidOperation + Rounded = self.decimal.Rounded + + nc = copy.copy(self.decimal.getcontext()) nc.prec = 3 # empty @@ -592,7 +777,75 @@ 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): + if hasattr(C, 'setfailpoint'): + random.seed(randseed) + + Decimal = self.decimal.Decimal + + nc = self.decimal.Context() + r = nc.create_decimal(0.1) + self.assertEqual(type(r), Decimal) + self.assertEqual(str(r), '0.1000000000000000055511151231') + self.assertTrue(nc.create_decimal(float('nan')).is_qnan()) + self.assertTrue(nc.create_decimal(float('inf')).is_infinite()) + self.assertTrue(nc.create_decimal(float('-inf')).is_infinite()) + self.assertEqual(str(nc.create_decimal(float('nan'))), + str(nc.create_decimal('NaN'))) + self.assertEqual(str(nc.create_decimal(float('inf'))), + str(nc.create_decimal('Infinity'))) + self.assertEqual(str(nc.create_decimal(float('-inf'))), + str(nc.create_decimal('-Infinity'))) + self.assertEqual(str(nc.create_decimal(float('-0.0'))), + str(nc.create_decimal('-0'))) + nc.prec = 100 + for i in range(200): + x = random.expovariate(0.01) * (random.random() * 2.0 - 1.0) + self.assertEqual(x, float(nc.create_decimal(x))) # roundtrip + def test_unicode_digits(self): + Decimal = self.decimal.Decimal + test_values = { '\uff11': '1', '\u0660.\u0660\u0663\u0667\u0662e-\u0663' : '0.0000372', @@ -601,29 +854,41 @@ for input, expected in test_values.items(): self.assertEqual(str(Decimal(input)), expected) - -class DecimalImplicitConstructionTest(unittest.TestCase): +class CExplicitConstructionTest(ExplicitConstructionTest): + decimal = C +class PyExplicitConstructionTest(ExplicitConstructionTest): + decimal = P + +class ImplicitConstructionTest(unittest.TestCase): '''Unit tests for Implicit Construction cases of Decimal.''' def test_implicit_from_None(self): - self.assertRaises(TypeError, eval, 'Decimal(5) + None', globals()) + Decimal = self.decimal.Decimal + self.assertRaises(TypeError, eval, 'Decimal(5) + None', locals()) def test_implicit_from_int(self): + Decimal = self.decimal.Decimal + #normal self.assertEqual(str(Decimal(5) + 45), '50') #exceeding precision self.assertEqual(Decimal(5) + 123456789000, Decimal(123456789000)) def test_implicit_from_string(self): - self.assertRaises(TypeError, eval, 'Decimal(5) + "3"', globals()) + Decimal = self.decimal.Decimal + self.assertRaises(TypeError, eval, 'Decimal(5) + "3"', locals()) def test_implicit_from_float(self): - self.assertRaises(TypeError, eval, 'Decimal(5) + 2.2', globals()) + Decimal = self.decimal.Decimal + self.assertRaises(TypeError, eval, 'Decimal(5) + 2.2', locals()) def test_implicit_from_Decimal(self): + Decimal = self.decimal.Decimal self.assertEqual(Decimal(5) + Decimal(45), Decimal(50)) def test_rop(self): + Decimal = self.decimal.Decimal + # Allow other classes to be trained to interact with Decimals class E: def __divmod__(self, other): @@ -671,10 +936,16 @@ self.assertEqual(eval('Decimal(10)' + sym + 'E()'), '10' + rop + 'str') - -class DecimalFormatTest(unittest.TestCase): +class CImplicitConstructionTest(ImplicitConstructionTest): + decimal = C +class PyImplicitConstructionTest(ImplicitConstructionTest): + decimal = P + +class FormatTest(unittest.TestCase): '''Unit tests for the format function.''' def test_formatting(self): + Decimal = self.decimal.Decimal + # triples giving a format, a Decimal, and the expected result test_values = [ ('e', '0E-15', '0e-15'), @@ -814,56 +1085,60 @@ # issue 6850 ('a=-7.0', '0.12345', 'aaaa0.1'), - - # Issue 7094: Alternate formatting (specified by #) - ('.0e', '1.0', '1e+0'), - ('#.0e', '1.0', '1.e+0'), - ('.0f', '1.0', '1'), - ('#.0f', '1.0', '1.'), - ('g', '1.1', '1.1'), - ('#g', '1.1', '1.1'), - ('.0g', '1', '1'), - ('#.0g', '1', '1.'), - ('.0%', '1.0', '100%'), - ('#.0%', '1.0', '100.%'), ] for fmt, d, result in test_values: self.assertEqual(format(Decimal(d), fmt), result) + # bytes format argument + self.assertRaises(TypeError, Decimal(1).__format__, b'-020') + def test_n_format(self): + Decimal = self.decimal.Decimal + try: from locale import CHAR_MAX except ImportError: return + def make_grouping(lst): + return ''.join([chr(x) for x in lst]) if self.decimal == C else lst + + def get_fmt(x, override=None, fmt='n'): + if self.decimal == C: + return Decimal(x).__format__(fmt, override) + else: + return Decimal(x).__format__(fmt, _localeconv=override) + # Set up some localeconv-like dictionaries en_US = { 'decimal_point' : '.', - 'grouping' : [3, 3, 0], - 'thousands_sep': ',' + 'grouping' : make_grouping([3, 3, 0]), + 'thousands_sep' : ',' } fr_FR = { 'decimal_point' : ',', - 'grouping' : [CHAR_MAX], + 'grouping' : make_grouping([CHAR_MAX]), 'thousands_sep' : '' } ru_RU = { 'decimal_point' : ',', - 'grouping' : [3, 3, 0], + 'grouping': make_grouping([3, 3, 0]), 'thousands_sep' : ' ' } crazy = { 'decimal_point' : '&', - 'grouping' : [1, 4, 2, CHAR_MAX], + 'grouping': make_grouping([1, 4, 2, CHAR_MAX]), 'thousands_sep' : '-' } - - def get_fmt(x, locale, fmt='n'): - return Decimal.__format__(Decimal(x), fmt, _localeconv=locale) + dotsep_wide = { + 'decimal_point' : b'\xc2\xbf'.decode('utf-8'), + 'grouping': make_grouping([3, 3, 0]), + 'thousands_sep' : b'\xc2\xb4'.decode('utf-8') + } self.assertEqual(get_fmt(Decimal('12.7'), en_US), '12.7') self.assertEqual(get_fmt(Decimal('12.7'), fr_FR), '12,7') @@ -902,11 +1177,33 @@ self.assertEqual(get_fmt(123456, crazy, '012n'), '00-01-2345-6') self.assertEqual(get_fmt(123456, crazy, '013n'), '000-01-2345-6') - -class DecimalArithmeticOperatorsTest(unittest.TestCase): + # wide char separator and decimal point + self.assertEqual(get_fmt(Decimal('-1.5'), dotsep_wide, '020n'), + '-0´000´000´000´001¿5') + + 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٬000٬000٫123') + locale.resetlocale() + +class CFormatTest(FormatTest): + decimal = C +class PyFormatTest(FormatTest): + decimal = P + +class ArithmeticOperatorsTest(unittest.TestCase): '''Unit tests for all arithmetic operators, binary and unary.''' def test_addition(self): + Decimal = self.decimal.Decimal d1 = Decimal('-11.1') d2 = Decimal('22.2') @@ -934,6 +1231,7 @@ self.assertEqual(d1, Decimal('16.1')) def test_subtraction(self): + Decimal = self.decimal.Decimal d1 = Decimal('-11.1') d2 = Decimal('22.2') @@ -961,6 +1259,7 @@ self.assertEqual(d1, Decimal('-38.3')) def test_multiplication(self): + Decimal = self.decimal.Decimal d1 = Decimal('-5') d2 = Decimal('3') @@ -988,6 +1287,7 @@ self.assertEqual(d1, Decimal('-75')) def test_division(self): + Decimal = self.decimal.Decimal d1 = Decimal('-5') d2 = Decimal('2') @@ -1015,6 +1315,7 @@ self.assertEqual(d1, Decimal('-0.625')) def test_floor_division(self): + Decimal = self.decimal.Decimal d1 = Decimal('5') d2 = Decimal('2') @@ -1042,6 +1343,7 @@ self.assertEqual(d1, Decimal('1')) def test_powering(self): + Decimal = self.decimal.Decimal d1 = Decimal('5') d2 = Decimal('2') @@ -1069,6 +1371,7 @@ self.assertEqual(d1, Decimal('390625')) def test_module(self): + Decimal = self.decimal.Decimal d1 = Decimal('5') d2 = Decimal('2') @@ -1096,6 +1399,7 @@ self.assertEqual(d1, Decimal('1')) def test_floor_div_module(self): + Decimal = self.decimal.Decimal d1 = Decimal('5') d2 = Decimal('2') @@ -1122,6 +1426,8 @@ self.assertEqual(type(q), type(d1)) def test_unary_operators(self): + Decimal = self.decimal.Decimal + self.assertEqual(+Decimal(45), Decimal(+45)) # + self.assertEqual(-Decimal(45), Decimal(-45)) # - self.assertEqual(abs(Decimal(45)), abs(Decimal(-45))) # abs @@ -1134,6 +1440,9 @@ # equality comparisons (==, !=) involving only quiet nans # don't signal, but return False or True respectively. + Decimal = self.decimal.Decimal + InvalidOperation = self.decimal.InvalidOperation + localcontext = self.decimal.localcontext n = Decimal('NaN') s = Decimal('sNaN') @@ -1179,76 +1488,162 @@ self.assertRaises(InvalidOperation, op, x, y) def test_copy_sign(self): + Decimal = self.decimal.Decimal + d = Decimal(1).copy_sign(Decimal(-2)) - self.assertEqual(Decimal(1).copy_sign(-2), d) self.assertRaises(TypeError, Decimal(1).copy_sign, '-2') +class CArithmeticOperatorsTest(ArithmeticOperatorsTest): + decimal = C +class PyArithmeticOperatorsTest(ArithmeticOperatorsTest): + decimal = P + # The following are two functions used to test threading in the next class def thfunc1(cls): + Decimal = cls.decimal.Decimal + InvalidOperation = cls.decimal.InvalidOperation + DivisionByZero = cls.decimal.DivisionByZero + Overflow = cls.decimal.Overflow + Underflow = cls.decimal.Underflow + Inexact = cls.decimal.Inexact + getcontext = cls.decimal.getcontext + localcontext = cls.decimal.localcontext + d1 = Decimal(1) d3 = Decimal(3) test1 = d1/d3 + + cls.finish1.set() cls.synchro.wait() + test2 = d1/d3 - cls.finish1.set() - - cls.assertEqual(test1, Decimal('0.3333333333333333333333333333')) - cls.assertEqual(test2, Decimal('0.3333333333333333333333333333')) + with localcontext() as c2: + cls.assertTrue(c2.flags[Inexact]) + cls.assertRaises(DivisionByZero, c2.divide, d1, 0) + cls.assertTrue(c2.flags[DivisionByZero]) + with localcontext() as c3: + cls.assertTrue(c3.flags[Inexact]) + cls.assertTrue(c3.flags[DivisionByZero]) + cls.assertRaises(InvalidOperation, c3.compare, d1, Decimal('sNaN')) + cls.assertTrue(c3.flags[InvalidOperation]) + del c3 + cls.assertFalse(c2.flags[InvalidOperation]) + del c2 + + cls.assertEqual(test1, Decimal('0.333333333333333333333333')) + cls.assertEqual(test2, Decimal('0.333333333333333333333333')) + + c1 = getcontext() + cls.assertTrue(c1.flags[Inexact]) + for sig in Overflow, Underflow, DivisionByZero, InvalidOperation: + cls.assertFalse(c1.flags[sig]) return def thfunc2(cls): + Decimal = cls.decimal.Decimal + InvalidOperation = cls.decimal.InvalidOperation + DivisionByZero = cls.decimal.DivisionByZero + Overflow = cls.decimal.Overflow + Underflow = cls.decimal.Underflow + Inexact = cls.decimal.Inexact + getcontext = cls.decimal.getcontext + localcontext = cls.decimal.localcontext + d1 = Decimal(1) d3 = Decimal(3) test1 = d1/d3 + thiscontext = getcontext() thiscontext.prec = 18 test2 = d1/d3 + + with localcontext() as c2: + cls.assertTrue(c2.flags[Inexact]) + cls.assertRaises(Overflow, c2.multiply, Decimal('1e425000000'), 999) + cls.assertTrue(c2.flags[Overflow]) + with localcontext(thiscontext) as c3: + cls.assertTrue(c3.flags[Inexact]) + cls.assertFalse(c3.flags[Overflow]) + c3.traps[Underflow] = True + cls.assertRaises(Underflow, c3.divide, Decimal('1e-425000000'), 999) + cls.assertTrue(c3.flags[Underflow]) + del c3 + cls.assertFalse(c2.flags[Underflow]) + cls.assertFalse(c2.traps[Underflow]) + del c2 + cls.synchro.set() cls.finish2.set() - cls.assertEqual(test1, Decimal('0.3333333333333333333333333333')) + cls.assertEqual(test1, Decimal('0.333333333333333333333333')) cls.assertEqual(test2, Decimal('0.333333333333333333')) + + cls.assertFalse(thiscontext.traps[Underflow]) + cls.assertTrue(thiscontext.flags[Inexact]) + for sig in Overflow, Underflow, DivisionByZero, InvalidOperation: + cls.assertFalse(thiscontext.flags[sig]) return - -class DecimalUseOfContextTest(unittest.TestCase): - '''Unit tests for Use of Context cases in Decimal.''' - - try: - import threading - except ImportError: - threading = None +class ThreadingTest(unittest.TestCase): + '''Unit tests for thread local contexts in Decimal.''' # Take care executing this test from IDLE, there's an issue in threading # that hangs IDLE and I couldn't find it def test_threading(self): - #Test the "threading isolation" of a Context. - - self.synchro = threading.Event() - self.finish1 = threading.Event() - self.finish2 = threading.Event() - - th1 = threading.Thread(target=thfunc1, args=(self,)) - th2 = threading.Thread(target=thfunc2, args=(self,)) - - th1.start() - th2.start() - - self.finish1.wait() - self.finish2.wait() + 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 + + with protectfail(): + self.synchro = threading.Event() + self.finish1 = threading.Event() + self.finish2 = threading.Event() + + th1 = threading.Thread(target=thfunc1, args=(self,)) + th2 = threading.Thread(target=thfunc2, args=(self,)) + + th1.start() + th2.start() + + self.finish1.wait() + self.finish2.wait() + + for sig in Signals[self.decimal]: + self.assertFalse(DefaultContext.flags[sig]) + + DefaultContext.prec = save_prec + DefaultContext.Emax = save_emax + DefaultContext.Emin = save_emin return - if threading is None: - del test_threading - - -class DecimalUsabilityTest(unittest.TestCase): +@unittest.skipUnless(threading, 'threading required') +class CThreadingTest(ThreadingTest): + decimal = C +@unittest.skipUnless(threading, 'threading required') +class PyThreadingTest(ThreadingTest): + decimal = P + +class UsabilityTest(unittest.TestCase): '''Unit tests for Usability cases of Decimal.''' def test_comparison_operators(self): + if hasattr(C, 'setfailpoint'): + random.seed(randseed) + + Decimal = self.decimal.Decimal da = Decimal('23.42') db = Decimal('23.42') @@ -1283,6 +1678,8 @@ self.assertEqual(a, b) def test_decimal_float_comparison(self): + Decimal = self.decimal.Decimal + da = Decimal('0.25') db = Decimal('3.0') self.assertLess(da, 3.0) @@ -1299,7 +1696,71 @@ self.assertEqual(3.0, db) self.assertNotEqual(0.1, Decimal('0.1')) + def test_decimal_complex_comparison(self): + Decimal = self.decimal.Decimal + + da = Decimal('0.25') + db = Decimal('3.0') + self.assertNotEqual(da, (1.5+0j)) + self.assertNotEqual((1.5+0j), da) + self.assertEqual(da, (0.25+0j)) + self.assertEqual((0.25+0j), da) + self.assertEqual((3.0+0j), db) + self.assertEqual(db, (3.0+0j)) + + self.assertNotEqual(db, (3.0+1j)) + self.assertNotEqual((3.0+1j), db) + + self.assertIs(db.__lt__(3.0+0j), NotImplemented) + self.assertIs(db.__le__(3.0+0j), NotImplemented) + self.assertIs(db.__gt__(3.0+0j), NotImplemented) + self.assertIs(db.__le__(3.0+0j), NotImplemented) + + def test_decimal_fraction_comparison(self): + D = self.decimal.Decimal + F = fractions[self.decimal].Fraction + Context = self.decimal.Context + localcontext = self.decimal.localcontext + InvalidOperation = self.decimal.InvalidOperation + + + emax = C.MAX_EMAX if C else 999999999 + emin = C.MIN_EMIN if C else -999999999 + etiny = C.MIN_ETINY if C else -1999999997 + c = Context(Emax=emax, Emin=emin) + + with localcontext(c): + c.prec = emax + self.assertLess(D(0), F(1,9999999999999999999999999999999999999)) + self.assertLess(F(-1,9999999999999999999999999999999999999), D(0)) + self.assertLess(F(0,1), D("1e" + str(etiny))) + self.assertLess(D("-1e" + str(etiny)), F(0,1)) + self.assertLess(F(0,9999999999999999999999999), D("1e" + str(etiny))) + self.assertLess(D("-1e" + str(etiny)), F(0,9999999999999999999999999)) + + self.assertEqual(D("0.1"), F(1,10)) + self.assertEqual(F(1,10), D("0.1")) + + c.prec = 300 + self.assertNotEqual(D(1)/3, F(1,3)) + self.assertNotEqual(F(1,3), D(1)/3) + + self.assertLessEqual(F(120984237, 9999999999), D("9e" + str(emax))) + self.assertGreaterEqual(D("9e" + str(emax)), F(120984237, 9999999999)) + + self.assertGreater(D('inf'), F(99999999999,123)) + self.assertGreater(D('inf'), F(-99999999999,123)) + self.assertLess(D('-inf'), F(99999999999,123)) + self.assertLess(D('-inf'), F(-99999999999,123)) + + self.assertRaises(InvalidOperation, D('nan').__gt__, F(-9,123)) + self.assertIs(NotImplemented, F(-9,123).__lt__(D('nan'))) + self.assertNotEqual(D('nan'), F(-9,123)) + self.assertNotEqual(F(-9,123), D('nan')) + def test_copy_and_deepcopy_methods(self): + Decimal = self.decimal.Decimal + d = Decimal('43.24') c = copy.copy(d) self.assertEqual(id(c), id(d)) @@ -1307,6 +1768,12 @@ self.assertEqual(id(dc), id(d)) def test_hash_method(self): + if hasattr(C, 'setfailpoint'): + random.seed(randseed) + + Decimal = self.decimal.Decimal + localcontext = self.decimal.localcontext + def hashit(d): a = hash(d) b = d.__hash__() @@ -1325,6 +1792,8 @@ 32, 33, 61, 62, 63, 64, 65, 66] for n in range(-10, 10) for sign in [-1, 1]] + if hasattr(C, 'setfailpoint'): + test_values = random.sample(test_values, 10) test_values.extend([ Decimal("-1"), # ==> -2 Decimal("-0"), # zeros @@ -1367,24 +1836,27 @@ d = Decimal(s) self.assertEqual(hashit(f), hashit(d)) - # check that the value of the hash doesn't depend on the - # current context (issue #1757) - c = getcontext() - old_precision = c.prec - x = Decimal("123456789.1") - - c.prec = 6 - h1 = hashit(x) - c.prec = 10 - h2 = hashit(x) - c.prec = 16 - h3 = hashit(x) - - self.assertEqual(h1, h2) - self.assertEqual(h1, h3) - c.prec = old_precision + with localcontext() as c: + # check that the value of the hash doesn't depend on the + # current context (issue #1757) + x = Decimal("123456789.1") + + c.prec = 6 + h1 = hashit(x) + c.prec = 10 + h2 = hashit(x) + c.prec = 16 + h3 = hashit(x) + + self.assertEqual(h1, h2) + self.assertEqual(h1, h3) + + c.prec = 10000 + x = 1100 ** 1248 + self.assertEqual(hashit(Decimal(x)), hashit(x)) def test_min_and_max_methods(self): + Decimal = self.decimal.Decimal d1 = Decimal('15.32') d2 = Decimal('28.5') @@ -1404,6 +1876,8 @@ self.assertIs(max(d2,l1), d2) def test_as_nonzero(self): + Decimal = self.decimal.Decimal + #as false self.assertFalse(Decimal(0)) #as true @@ -1411,6 +1885,7 @@ def test_tostring_methods(self): #Test str and repr methods. + Decimal = self.decimal.Decimal d = Decimal('15.32') self.assertEqual(str(d), '15.32') # str @@ -1418,6 +1893,7 @@ def test_tonum_methods(self): #Test float and int methods. + Decimal = self.decimal.Decimal d1 = Decimal('66') d2 = Decimal('15.32') @@ -1516,9 +1992,8 @@ for d, n, r in test_triples: self.assertEqual(str(round(Decimal(d), n)), r) - - def test_eval_round_trip(self): + Decimal = self.decimal.Decimal #with zero d = Decimal( (0, (0,), 0) ) @@ -1537,6 +2012,7 @@ self.assertEqual(d, eval(repr(d))) def test_as_tuple(self): + Decimal = self.decimal.Decimal #with zero d = Decimal(0) @@ -1550,7 +2026,7 @@ d = Decimal("-4.34913534E-17") self.assertEqual(d.as_tuple(), (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) ) - #inf + # XXX non-compliant infinity payload. d = Decimal("Infinity") self.assertEqual(d.as_tuple(), (0, (0,), 'F') ) @@ -1570,14 +2046,1695 @@ d = Decimal( (1, (), 'n') ) self.assertEqual(d.as_tuple(), (1, (), 'n') ) - #coefficient in infinity should be ignored - d = Decimal( (0, (4, 5, 3, 4), 'F') ) - self.assertEqual(d.as_tuple(), (0, (0,), 'F')) - d = Decimal( (1, (0, 2, 7, 1), 'F') ) - self.assertEqual(d.as_tuple(), (1, (0,), 'F')) - - def test_immutability_operations(self): + # XXX coefficient in infinity should raise an error + if self.decimal == P: + d = Decimal( (0, (4, 5, 3, 4), 'F') ) + self.assertEqual(d.as_tuple(), (0, (0,), 'F')) + d = Decimal( (1, (0, 2, 7, 1), 'F') ) + self.assertEqual(d.as_tuple(), (1, (0,), 'F')) + + def test_subclassing(self): + # Different behaviours when subclassing Decimal + Decimal = self.decimal.Decimal + + class MyDecimal(Decimal): + pass + + d1 = MyDecimal(1) + d2 = MyDecimal(2) + d = d1 + d2 + self.assertIs(type(d), Decimal) + + d = d1.max(d2) + self.assertIs(type(d), Decimal) + + d = copy.copy(d1) + self.assertIs(type(d), MyDecimal) + self.assertEqual(d, d1) + + d = copy.deepcopy(d1) + self.assertIs(type(d), MyDecimal) + self.assertEqual(d, d1) + + def test_implicit_context(self): + Decimal = self.decimal.Decimal + getcontext = self.decimal.getcontext + + # Check results when context given implicitly. (Issue 2478) + c = getcontext() + self.assertEqual(str(Decimal(0).sqrt()), + str(c.sqrt(Decimal(0)))) + + def test_conversions_from_int(self): + # Check that methods taking a second Decimal argument will + # always accept an integer in place of a Decimal. + Decimal = self.decimal.Decimal + + self.assertEqual(Decimal(4).compare(3), + Decimal(4).compare(Decimal(3))) + self.assertEqual(Decimal(4).compare_signal(3), + Decimal(4).compare_signal(Decimal(3))) + self.assertEqual(Decimal(4).compare_total(3), + Decimal(4).compare_total(Decimal(3))) + self.assertEqual(Decimal(4).compare_total_mag(3), + Decimal(4).compare_total_mag(Decimal(3))) + self.assertEqual(Decimal(10101).logical_and(1001), + Decimal(10101).logical_and(Decimal(1001))) + self.assertEqual(Decimal(10101).logical_or(1001), + Decimal(10101).logical_or(Decimal(1001))) + self.assertEqual(Decimal(10101).logical_xor(1001), + Decimal(10101).logical_xor(Decimal(1001))) + self.assertEqual(Decimal(567).max(123), + Decimal(567).max(Decimal(123))) + self.assertEqual(Decimal(567).max_mag(123), + Decimal(567).max_mag(Decimal(123))) + self.assertEqual(Decimal(567).min(123), + Decimal(567).min(Decimal(123))) + self.assertEqual(Decimal(567).min_mag(123), + Decimal(567).min_mag(Decimal(123))) + self.assertEqual(Decimal(567).next_toward(123), + Decimal(567).next_toward(Decimal(123))) + self.assertEqual(Decimal(1234).quantize(100), + Decimal(1234).quantize(Decimal(100))) + self.assertEqual(Decimal(768).remainder_near(1234), + Decimal(768).remainder_near(Decimal(1234))) + self.assertEqual(Decimal(123).rotate(1), + Decimal(123).rotate(Decimal(1))) + self.assertEqual(Decimal(1234).same_quantum(1000), + Decimal(1234).same_quantum(Decimal(1000))) + self.assertEqual(Decimal('9.123').scaleb(-100), + Decimal('9.123').scaleb(Decimal(-100))) + self.assertEqual(Decimal(456).shift(-1), + Decimal(456).shift(Decimal(-1))) + + self.assertEqual(Decimal(-12).fma(Decimal(45), 67), + Decimal(-12).fma(Decimal(45), Decimal(67))) + self.assertEqual(Decimal(-12).fma(45, 67), + Decimal(-12).fma(Decimal(45), Decimal(67))) + self.assertEqual(Decimal(-12).fma(45, Decimal(67)), + Decimal(-12).fma(Decimal(45), Decimal(67))) + +class CUsabilityTest(UsabilityTest): + decimal = C +class PyUsabilityTest(UsabilityTest): + decimal = P + +class PythonAPItests(unittest.TestCase): + + def test_abc(self): + Decimal = self.decimal.Decimal + + self.assertTrue(issubclass(Decimal, numbers.Number)) + self.assertFalse(issubclass(Decimal, numbers.Real)) + self.assertIsInstance(Decimal(0), numbers.Number) + self.assertNotIsInstance(Decimal(0), numbers.Real) + + def test_pickle(self): + Decimal = self.decimal.Decimal + + savedecimal = sys.modules['decimal'] + + # Round trip + sys.modules['decimal'] = self.decimal + d = Decimal('-3.141590000') + p = pickle.dumps(d) + e = pickle.loads(p) + self.assertEqual(d, e) + + if C: + # Test interchangeability + x = C.Decimal('-3.123e81723') + y = P.Decimal('-3.123e81723') + + sys.modules['decimal'] = C + sx = pickle.dumps(x) + sys.modules['decimal'] = P + r = pickle.loads(sx) + self.assertIsInstance(r, P.Decimal) + self.assertEqual(r, y) + + sys.modules['decimal'] = P + sy = pickle.dumps(y) + sys.modules['decimal'] = C + r = pickle.loads(sy) + self.assertIsInstance(r, C.Decimal) + self.assertEqual(r, x) + + sys.modules['decimal'] = savedecimal + + def test_int(self): + Decimal = self.decimal.Decimal + ROUND_DOWN = self.decimal.ROUND_DOWN + + lim = 10 if hasattr(C, 'setfailpoint') else 250 + for x in range(-lim, lim): + s = '%0.2f' % (x / 100.0) + # should work the same as for floats + self.assertEqual(int(Decimal(s)), int(float(s))) + # should work the same as to_integral in the ROUND_DOWN mode + d = Decimal(s) + r = d.to_integral(ROUND_DOWN) + self.assertEqual(Decimal(int(d)), r) + + self.assertRaises(ValueError, int, Decimal('-nan')) + self.assertRaises(ValueError, int, Decimal('snan')) + self.assertRaises(OverflowError, int, Decimal('inf')) + self.assertRaises(OverflowError, int, Decimal('-inf')) + + def test_trunc(self): + Decimal = self.decimal.Decimal + ROUND_DOWN = self.decimal.ROUND_DOWN + + lim = 10 if hasattr(C, 'setfailpoint') else 250 + for x in range(-lim, lim): + s = '%0.2f' % (x / 100.0) + # should work the same as for floats + self.assertEqual(int(Decimal(s)), int(float(s))) + # should work the same as to_integral in the ROUND_DOWN mode + d = Decimal(s) + r = d.to_integral(ROUND_DOWN) + self.assertEqual(Decimal(math.trunc(d)), r) + + def test_from_float(self): + if hasattr(C, 'setfailpoint'): + random.seed(randseed) + + Decimal = self.decimal.Decimal + + class MyDecimal(Decimal): + pass + + self.assertTrue(issubclass(MyDecimal, Decimal)) + + r = MyDecimal.from_float(0.1) + self.assertEqual(type(r), MyDecimal) + self.assertEqual(str(r), + '0.1000000000000000055511151231257827021181583404541015625') + bigint = 12345678901234567890123456789 + self.assertEqual(MyDecimal.from_float(bigint), MyDecimal(bigint)) + self.assertTrue(MyDecimal.from_float(float('nan')).is_qnan()) + self.assertTrue(MyDecimal.from_float(float('inf')).is_infinite()) + self.assertTrue(MyDecimal.from_float(float('-inf')).is_infinite()) + self.assertEqual(str(MyDecimal.from_float(float('nan'))), + str(Decimal('NaN'))) + self.assertEqual(str(MyDecimal.from_float(float('inf'))), + str(Decimal('Infinity'))) + self.assertEqual(str(MyDecimal.from_float(float('-inf'))), + str(Decimal('-Infinity'))) + self.assertRaises(TypeError, MyDecimal.from_float, 'abc') + lim = 10 if hasattr(C, 'setfailpoint') else 200 + for i in range(lim): + x = random.expovariate(0.01) * (random.random() * 2.0 - 1.0) + self.assertEqual(x, float(MyDecimal.from_float(x))) # roundtrip + + def test_create_decimal_from_float(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + ROUND_DOWN = self.decimal.ROUND_DOWN + ROUND_UP = self.decimal.ROUND_UP + Inexact = self.decimal.Inexact + + context = Context(prec=5, rounding=ROUND_DOWN) + self.assertEqual( + context.create_decimal_from_float(math.pi), + Decimal('3.1415') + ) + context = Context(prec=5, rounding=ROUND_UP) + self.assertEqual( + context.create_decimal_from_float(math.pi), + Decimal('3.1416') + ) + context = Context(prec=5, traps=[Inexact]) + self.assertRaises( + Inexact, + context.create_decimal_from_float, + math.pi + ) + self.assertEqual(repr(context.create_decimal_from_float(-0.0)), + "Decimal('-0')") + self.assertEqual(repr(context.create_decimal_from_float(1.0)), + "Decimal('1')") + self.assertEqual(repr(context.create_decimal_from_float(10)), + "Decimal('10')") + + def test_quantize(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + InvalidOperation = self.decimal.InvalidOperation + ROUND_DOWN = self.decimal.ROUND_DOWN + + c = Context(Emax=99999, Emin=-99999) + self.assertEqual( + Decimal('7.335').quantize(Decimal('.01')), + Decimal('7.34') + ) + self.assertEqual( + Decimal('7.335').quantize(Decimal('.01'), rounding=ROUND_DOWN), + Decimal('7.33') + ) + self.assertRaises( + InvalidOperation, + Decimal("10e99999").quantize, Decimal('1e100000'), context=c + ) + + c = Context() + d = Decimal("0.871831e800") + x = d.quantize(context=c, exp=Decimal("1e797"), rounding=ROUND_DOWN) + self.assertEqual(x, Decimal('8.71E+799')) + + def test_complex(self): + Decimal = self.decimal.Decimal + + x = Decimal("9.8182731e181273") + self.assertEqual(x.real, x) + self.assertEqual(x.imag, 0) + self.assertEqual(x.conjugate(), x) + + x = Decimal("1") + self.assertEqual(complex(x), complex(float(1))) + + self.assertRaises(AttributeError, setattr, x, 'real', 100) + self.assertRaises(AttributeError, setattr, x, 'imag', 100) + self.assertRaises(AttributeError, setattr, x, 'conjugate', 100) + self.assertRaises(AttributeError, setattr, x, '__complex__', 100) + +class CPythonAPItests(PythonAPItests): + decimal = C +class PyPythonAPItests(PythonAPItests): + decimal = P + +class ContextAPItests(unittest.TestCase): + + def test_pickle(self): + if hasattr(C, 'setfailpoint'): + random.seed(randseed) + + Context = self.decimal.Context + + savedecimal = sys.modules['decimal'] + + # Round trip + sys.modules['decimal'] = self.decimal + c = Context() + e = pickle.loads(pickle.dumps(c)) + + 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]): + if hasattr(C, 'setfailpoint') and ri > 0: continue + for fi, _ in enumerate(OrderedSignals[dumper]): + if hasattr(C, 'setfailpoint') and fi > 0: continue + for ti, _ in enumerate(OrderedSignals[dumper]): + if hasattr(C, 'setfailpoint') and ti > 0: continue + + 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_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.assertEqual(c.add(Decimal(1), 1), d) + self.assertEqual(c.add(1, Decimal(1)), d) + self.assertRaises(TypeError, c.add, '1', 1) + 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.assertEqual(c.compare(Decimal(1), 1), d) + self.assertEqual(c.compare(1, Decimal(1)), d) + self.assertRaises(TypeError, c.compare, '1', 1) + 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.assertEqual(c.compare_signal(Decimal(1), 1), d) + self.assertEqual(c.compare_signal(1, Decimal(1)), d) + self.assertRaises(TypeError, c.compare_signal, '1', 1) + 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.assertEqual(c.compare_total(Decimal(1), 1), d) + self.assertEqual(c.compare_total(1, Decimal(1)), d) + self.assertRaises(TypeError, c.compare_total, '1', 1) + 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.assertEqual(c.compare_total_mag(Decimal(1), 1), d) + self.assertEqual(c.compare_total_mag(1, Decimal(1)), d) + self.assertRaises(TypeError, c.compare_total_mag, '1', 1) + 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.assertEqual(c.copy_sign(Decimal(1), -2), d) + self.assertEqual(c.copy_sign(1, Decimal(-2)), d) + self.assertRaises(TypeError, c.copy_sign, '1', -2) + 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.assertEqual(c.divide(Decimal(1), 2), d) + self.assertEqual(c.divide(1, Decimal(2)), d) + self.assertRaises(TypeError, c.divide, '1', 2) + 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.assertEqual(c.divide_int(Decimal(1), 2), d) + self.assertEqual(c.divide_int(1, Decimal(2)), d) + self.assertRaises(TypeError, c.divide_int, '1', 2) + 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.assertEqual(c.divmod(Decimal(1), 2), d) + self.assertEqual(c.divmod(1, Decimal(2)), d) + self.assertRaises(TypeError, c.divmod, '1', 2) + 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) + self.assertEqual(c.fma(Decimal(2), 3, 4), d) + self.assertEqual(c.fma(2, Decimal(3), 4), d) + self.assertEqual(c.fma(2, 3, Decimal(4)), d) + self.assertEqual(c.fma(Decimal(2), Decimal(3), 4), d) + self.assertRaises(TypeError, c.fma, '2', 3, 4) + self.assertRaises(TypeError, c.fma, 2, '3', 4) + self.assertRaises(TypeError, c.fma, 2, 3, '4') + + # Issue 12079 for Context.fma ... + self.assertRaises(TypeError, c.fma, + Decimal('Infinity'), Decimal(0), "not a decimal") + self.assertRaises(TypeError, c.fma, + Decimal(1), Decimal('snan'), 1.222) + # ... and for Decimal.fma. + self.assertRaises(TypeError, Decimal('Infinity').fma, + Decimal(0), "not a decimal") + self.assertRaises(TypeError, Decimal(1).fma, + 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.assertEqual(c.logical_and(Decimal(1), 1), d) + self.assertEqual(c.logical_and(1, Decimal(1)), d) + self.assertRaises(TypeError, c.logical_and, '1', 1) + 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.assertEqual(c.logical_or(Decimal(1), 1), d) + self.assertEqual(c.logical_or(1, Decimal(1)), d) + self.assertRaises(TypeError, c.logical_or, '1', 1) + 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.assertEqual(c.logical_xor(Decimal(1), 1), d) + self.assertEqual(c.logical_xor(1, Decimal(1)), d) + self.assertRaises(TypeError, c.logical_xor, '1', 1) + 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.assertEqual(c.max(Decimal(1), 2), d) + self.assertEqual(c.max(1, Decimal(2)), d) + self.assertRaises(TypeError, c.max, '1', 2) + 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.assertEqual(c.max_mag(Decimal(1), 2), d) + self.assertEqual(c.max_mag(1, Decimal(2)), d) + self.assertRaises(TypeError, c.max_mag, '1', 2) + 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.assertEqual(c.min(Decimal(1), 2), d) + self.assertEqual(c.min(1, Decimal(2)), d) + self.assertRaises(TypeError, c.min, '1', 2) + 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.assertEqual(c.min_mag(Decimal(1), 2), d) + self.assertEqual(c.min_mag(1, Decimal(2)), d) + self.assertRaises(TypeError, c.min_mag, '1', 2) + 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.assertEqual(c.multiply(Decimal(1), 2), d) + self.assertEqual(c.multiply(1, Decimal(2)), d) + self.assertRaises(TypeError, c.multiply, '1', 2) + 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.assertEqual(c.next_toward(Decimal(1), 2), d) + self.assertEqual(c.next_toward(1, Decimal(2)), d) + self.assertRaises(TypeError, c.next_toward, '1', 2) + 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_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') + + 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.assertEqual(c.quantize(Decimal(1), 2), d) + self.assertEqual(c.quantize(1, Decimal(2)), d) + self.assertRaises(TypeError, c.quantize, '1', 2) + 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.assertEqual(c.remainder(Decimal(1), 2), d) + self.assertEqual(c.remainder(1, Decimal(2)), d) + self.assertRaises(TypeError, c.remainder, '1', 2) + 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.assertEqual(c.remainder_near(Decimal(1), 2), d) + self.assertEqual(c.remainder_near(1, Decimal(2)), d) + self.assertRaises(TypeError, c.remainder_near, '1', 2) + 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.assertEqual(c.rotate(Decimal(1), 2), d) + self.assertEqual(c.rotate(1, Decimal(2)), d) + self.assertRaises(TypeError, c.rotate, '1', 2) + 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.assertEqual(c.same_quantum(Decimal(1), 2), d) + self.assertEqual(c.same_quantum(1, Decimal(2)), d) + self.assertRaises(TypeError, c.same_quantum, '1', 2) + 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.assertEqual(c.scaleb(Decimal(1), 2), d) + self.assertEqual(c.scaleb(1, Decimal(2)), d) + self.assertRaises(TypeError, c.scaleb, '1', 2) + 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.assertEqual(c.shift(Decimal(1), 2), d) + self.assertEqual(c.shift(1, Decimal(2)), d) + self.assertRaises(TypeError, c.shift, '1', 2) + 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.assertEqual(c.subtract(Decimal(1), 2), d) + self.assertEqual(c.subtract(1, Decimal(2)), d) + self.assertRaises(TypeError, c.subtract, '1', 2) + 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 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() + final_ctx = getcontext() + self.assertIs(orig_ctx, final_ctx, 'did not restore context correctly') + self.assertIsNot(orig_ctx, set_ctx, 'did not copy the context') + self.assertIs(set_ctx, enter_ctx, '__enter__ returned wrong context') + + 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: + set_ctx = getcontext() + final_ctx = getcontext() + self.assertIs(orig_ctx, final_ctx, 'did not restore context correctly') + self.assertEqual(set_ctx.prec, new_ctx.prec, 'did not set correct context') + 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 = -425000000, Emax = 425000000, + rounding=ROUND_HALF_EVEN, traps=[], flags=[]) + + # operations that raise various flags, in the form (function, arglist) + operations = [ + (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")]), + (context.subtract, [Decimal("1.23456789"), Decimal("9.87654321")]), + ] + + # try various flags individually, then a whole lot at once + flagsets = [[Inexact], [Rounded], [Underflow], [Clamped], [Subnormal], + [Inexact, Rounded, Underflow, Clamped, Subnormal]] + + for fn, args in operations: + # find answer and flags raised using a clean context + context.clear_flags() + ans = fn(*args) + flags = [k for k, v in context.flags.items() if v] + + for extra_flags in flagsets: + # set flags, before calling operation + context.clear_flags() + for flag in extra_flags: + raise_error(context, flag) + new_ans = fn(*args) + + # flags that we expect to be set after the operation + expected_flags = list(flags) + for flag in extra_flags: + if flag not in expected_flags: + expected_flags.append(flag) + expected_flags.sort(key=id) + + # flags we actually got + new_flags = [k for k,v in context.flags.items() if v] + new_flags.sort(key=id) + + self.assertEqual(ans, new_ans, + "operation produces different answers depending on flags set: " + + "expected %s, got %s." % (ans, new_ans)) + self.assertEqual(new_flags, expected_flags, + "operation raises different flags depending on flags set: " + + "expected %s, got %s" % (expected_flags, new_flags)) + +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: + with protectfail(): + 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: + with protectfail(): + 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(TypeError, setattr, c, 'flags', {}) + self.assertRaises(TypeError, 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(TypeError, Context, flags=["P"]) + self.assertRaises(TypeError, 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 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') @@ -1649,724 +3806,878 @@ checkSameDec("to_eng_string") checkSameDec("to_integral") - def test_subclassing(self): - # Different behaviours when subclassing Decimal - - class MyDecimal(Decimal): - pass - - d1 = MyDecimal(1) - d2 = MyDecimal(2) - d = d1 + d2 - self.assertIs(type(d), Decimal) - - d = d1.max(d2) - self.assertIs(type(d), Decimal) - - def test_implicit_context(self): - # Check results when context given implicitly. (Issue 2478) - c = getcontext() - self.assertEqual(str(Decimal(0).sqrt()), - str(c.sqrt(Decimal(0)))) - - def test_conversions_from_int(self): - # Check that methods taking a second Decimal argument will - # always accept an integer in place of a Decimal. - self.assertEqual(Decimal(4).compare(3), - Decimal(4).compare(Decimal(3))) - self.assertEqual(Decimal(4).compare_signal(3), - Decimal(4).compare_signal(Decimal(3))) - self.assertEqual(Decimal(4).compare_total(3), - Decimal(4).compare_total(Decimal(3))) - self.assertEqual(Decimal(4).compare_total_mag(3), - Decimal(4).compare_total_mag(Decimal(3))) - self.assertEqual(Decimal(10101).logical_and(1001), - Decimal(10101).logical_and(Decimal(1001))) - self.assertEqual(Decimal(10101).logical_or(1001), - Decimal(10101).logical_or(Decimal(1001))) - self.assertEqual(Decimal(10101).logical_xor(1001), - Decimal(10101).logical_xor(Decimal(1001))) - self.assertEqual(Decimal(567).max(123), - Decimal(567).max(Decimal(123))) - self.assertEqual(Decimal(567).max_mag(123), - Decimal(567).max_mag(Decimal(123))) - self.assertEqual(Decimal(567).min(123), - Decimal(567).min(Decimal(123))) - self.assertEqual(Decimal(567).min_mag(123), - Decimal(567).min_mag(Decimal(123))) - self.assertEqual(Decimal(567).next_toward(123), - Decimal(567).next_toward(Decimal(123))) - self.assertEqual(Decimal(1234).quantize(100), - Decimal(1234).quantize(Decimal(100))) - self.assertEqual(Decimal(768).remainder_near(1234), - Decimal(768).remainder_near(Decimal(1234))) - self.assertEqual(Decimal(123).rotate(1), - Decimal(123).rotate(Decimal(1))) - self.assertEqual(Decimal(1234).same_quantum(1000), - Decimal(1234).same_quantum(Decimal(1000))) - self.assertEqual(Decimal('9.123').scaleb(-100), - Decimal('9.123').scaleb(Decimal(-100))) - self.assertEqual(Decimal(456).shift(-1), - Decimal(456).shift(Decimal(-1))) - - self.assertEqual(Decimal(-12).fma(Decimal(45), 67), - Decimal(-12).fma(Decimal(45), Decimal(67))) - self.assertEqual(Decimal(-12).fma(45, 67), - Decimal(-12).fma(Decimal(45), Decimal(67))) - self.assertEqual(Decimal(-12).fma(45, Decimal(67)), - Decimal(-12).fma(Decimal(45), Decimal(67))) - - -class DecimalPythonAPItests(unittest.TestCase): - - def test_abc(self): - 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): - d = Decimal('-3.141590000') - p = pickle.dumps(d) - e = pickle.loads(p) - self.assertEqual(d, e) - - def test_int(self): - for x in range(-250, 250): - s = '%0.2f' % (x / 100.0) - # should work the same as for floats - self.assertEqual(int(Decimal(s)), int(float(s))) - # should work the same as to_integral in the ROUND_DOWN mode - d = Decimal(s) - r = d.to_integral(ROUND_DOWN) - self.assertEqual(Decimal(int(d)), r) - - self.assertRaises(ValueError, int, Decimal('-nan')) - self.assertRaises(ValueError, int, Decimal('snan')) - self.assertRaises(OverflowError, int, Decimal('inf')) - self.assertRaises(OverflowError, int, Decimal('-inf')) - - def test_trunc(self): - for x in range(-250, 250): - s = '%0.2f' % (x / 100.0) - # should work the same as for floats - self.assertEqual(int(Decimal(s)), int(float(s))) - # should work the same as to_integral in the ROUND_DOWN mode - d = Decimal(s) - r = d.to_integral(ROUND_DOWN) - self.assertEqual(Decimal(math.trunc(d)), r) - - def test_from_float(self): - - class MyDecimal(Decimal): - pass - - r = MyDecimal.from_float(0.1) - self.assertEqual(type(r), MyDecimal) - self.assertEqual(str(r), - '0.1000000000000000055511151231257827021181583404541015625') - bigint = 12345678901234567890123456789 - self.assertEqual(MyDecimal.from_float(bigint), MyDecimal(bigint)) - self.assertTrue(MyDecimal.from_float(float('nan')).is_qnan()) - self.assertTrue(MyDecimal.from_float(float('inf')).is_infinite()) - self.assertTrue(MyDecimal.from_float(float('-inf')).is_infinite()) - self.assertEqual(str(MyDecimal.from_float(float('nan'))), - str(Decimal('NaN'))) - self.assertEqual(str(MyDecimal.from_float(float('inf'))), - str(Decimal('Infinity'))) - self.assertEqual(str(MyDecimal.from_float(float('-inf'))), - str(Decimal('-Infinity'))) - self.assertRaises(TypeError, MyDecimal.from_float, 'abc') - for i in range(200): - x = random.expovariate(0.01) * (random.random() * 2.0 - 1.0) - self.assertEqual(x, float(MyDecimal.from_float(x))) # roundtrip - - def test_create_decimal_from_float(self): - context = Context(prec=5, rounding=ROUND_DOWN) + 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""" + + 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) + + def test_apply(self): + # Decimal("9.9999999").apply() applies the current context. + Decimal = C.Decimal + localcontext = C.localcontext + + with localcontext() as c: + c.prec = 5 + c.Emax = 99999 + c.Emin = -99999 + + d = c.copy() + d.prec = 4 + + x = Decimal("123456") + self.assertEqual(str(x.apply()), "1.2346E+5") + self.assertEqual(str(c.apply(x)), "1.2346E+5") + + self.assertEqual(str(x.apply(d)), "1.235E+5") + self.assertEqual(str(d.apply(x)), "1.235E+5") + + self.assertRaises(TypeError, x.apply, "p") + self.assertRaises(TypeError, x.apply, "p", "q") + self.assertRaises(TypeError, c.apply, "p") + + x = Decimal(1171**2221) + self.assertEqual(str(x.apply()), "1.8402E+6815") + self.assertEqual(str(c.apply(x)), "1.8402E+6815") + self.assertEqual(str(d.apply(x)), "1.840E+6815") + + @requires_IEEE_754 + def test_c_float_operation(self): + Decimal = C.Decimal + FloatOperation= C.FloatOperation + localcontext = C.localcontext + + with localcontext() as c: + c.clear_flags() + self.assertEqual(Decimal(7.5), 7.5) + self.assertTrue(c.flags[FloatOperation]) + + c.clear_flags() + c.traps[FloatOperation] = True + self.assertRaises(FloatOperation, Decimal, 7.5) + self.assertTrue(c.flags[FloatOperation]) + + c.clear_flags() + c.traps[FloatOperation] = False + self.assertEqual(c.create_decimal(7.5), 7.5) + self.assertTrue(c.flags[FloatOperation]) + + c.clear_flags() + c.traps[FloatOperation] = True + self.assertRaises(FloatOperation, c.create_decimal, 7.5) + self.assertTrue(c.flags[FloatOperation]) + c.traps[FloatOperation] = False + + def test_c_float_comparison(self): + Decimal = C.Decimal + Context = C.Context + FloatOperation= C.FloatOperation + localcontext = C.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): + 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: + sig = None + self.assertFalse(c.traps[FloatOperation]) + doit(c, signal=sig) + test_containers(c, sig) + + c.traps[FloatOperation] = True + doit(c, signal=FloatOperation) + test_containers(c, FloatOperation) + + def test_c_float_operation_default(self): + Decimal = C.Decimal + Context = C.Context + Inexact = C.Inexact + DecInexact = C.DecInexact + FloatOperation= C.FloatOperation + DecFloatOperation= C.DecFloatOperation + + context = Context() + self.assertFalse(context.flags[FloatOperation]) + self.assertFalse(context.traps[FloatOperation]) + self.assertFalse(context._flags&DecFloatOperation) + self.assertFalse(context._traps&DecFloatOperation) + + context.settraps([Inexact, FloatOperation]) + self.assertEqual(context._traps, DecInexact|DecFloatOperation) + self.assertTrue(context.traps[FloatOperation]) + self.assertTrue(context.traps[Inexact]) + + def test_c_powmod(self): + Decimal = C.Decimal + Context = C.Context + + c = Context() + d = c.powmod(Decimal(1), Decimal(4), Decimal(2)) + self.assertEqual(c.powmod(1, 4, 2), d) + self.assertEqual(c.powmod(Decimal(1), 4, 2), d) + self.assertEqual(c.powmod(1, Decimal(4), 2), d) + self.assertEqual(c.powmod(1, 4, Decimal(2)), d) + self.assertEqual(c.powmod(Decimal(1), Decimal(4), 2), d) + self.assertRaises(TypeError, c.powmod, '1', 4, 2) + self.assertRaises(TypeError, c.powmod, 1, '4', 2) + self.assertRaises(TypeError, c.powmod, 1, 4, '2') + + 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) + + def test_sundry(self): + Decimal = C.Decimal + + # mpd_isinteger + self.assertTrue(Decimal("1.234e5").is_integer()) + self.assertTrue(Decimal("snan").is_special()) + + # Extra functions + self.assertEqual(Decimal(-1).abs(), 1) + self.assertEqual(Decimal(1).minus(), -1) + self.assertEqual(Decimal(1).plus(), 1) + self.assertEqual(Decimal(1).add(1), 2) + self.assertEqual(Decimal(12).div(2), 6) + self.assertEqual(Decimal(10).divint(7), 1) + self.assertEqual(Decimal(10).mul(12), 120) + self.assertEqual(Decimal(10).rem(7), 3) + self.assertEqual(Decimal(10).sub(7), 3) + self.assertEqual(Decimal(10).divmod(7), (1, 3)) + + 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 + ) + + # Architecture dependent context limits + if C.MAX_EMAX > 425000000: + self.assertEqual(C.MAX_PREC, 999999999999999999) + self.assertEqual(C.MAX_EMAX, 999999999999999999) + self.assertEqual(C.MIN_EMIN, -999999999999999999) + self.assertEqual(C.MIN_ETINY, -1999999999999999997) + else: + self.assertEqual(C.MAX_PREC, 425000000) + self.assertEqual(C.MAX_EMAX, 425000000) + self.assertEqual(C.MIN_EMIN, -425000000) + self.assertEqual(C.MIN_ETINY, -849999999) + + # 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< 425000000) + + c = Context() + + # SignalDict: input validation + self.assertRaises(TypeError, c.flags.__setitem__, 801, 0) + self.assertRaises(TypeError, 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(TypeError, setattr, c, 'flags', {0:1}) + self.assertRaises(TypeError, setattr, c, 'traps', {0:1}) + + self.assertRaises(TypeError, c.setflags, ['x']) + self.assertRaises(TypeError, c.settraps, ['y']) + self.assertRaises(TypeError, c.setflags, 'x') + self.assertRaises(TypeError, c.settraps, 'y') + + # 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) + self.assertRaises(OverflowError, Context, _allcr=int_max+1) + + # OverflowError, general ValueError + for attr in ('prec', 'Emin', 'Emax', 'capitals', 'clamp', '_allcr'): + 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', '_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) + + # OverflowError: unsafe_prec, unsafe_emin, unsafe_emax + 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) + + # capitals, clamp, _allcr + for attr in ['capitals', 'clamp', '_allcr']: + 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) + + self.assertRaises(ValueError, Context, _allcr=2) + + # _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 + DecClamped = C.DecClamped + DecUnderflow = C.DecUnderflow + DecInexact = C.DecInexact + DecRounded = C.DecRounded + DecSubnormal = C.DecSubnormal + + 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 + c._flags = DecUnderflow + c._traps = DecClamped + c._allcr = 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._flags, DecUnderflow) + self.assertEqual(c._traps, DecClamped) + self.assertEqual(c._allcr, 0) + + self.assertEqual(c.Etiny(), -3033) + self.assertEqual(c.Etop(), 2967) + + # Set traps/flags from list + c.settraps([Clamped, Underflow]) + self.assertEqual(c._traps, DecClamped|DecUnderflow) + + c.setflags([Inexact, Rounded, Subnormal]) + self.assertEqual(c._flags, DecInexact|DecRounded|DecSubnormal) + + # Exercise all unsafe setters + 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) + + 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_sci_string(), '9.99E+10') + 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).divmod, 8, "x", "y") + self.assertRaises(TypeError, Decimal(9).same_quantum, 3, "x", "y") + self.assertRaises(TypeError, Decimal(9).to_sci, 3, "x", "y") + self.assertRaises(TypeError, Decimal(9).to_eng, 3, "x", "y") + + self.assertEqual(Decimal("1.234e2007").sign(), 1) + self.assertEqual(Decimal("-1.234e2007").sign(), -1) + + 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, 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, 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) + + c.prec = 10 + x = Decimal(2).invroot() + self.assertEqual(str(x), '0.7071067812') + + x = c.invroot(3) + self.assertEqual(str(x), '0.5773502692') + + c.prec = 28 + x = Decimal(2).power(8) + self.assertEqual(str(x), '256') + + x = Decimal(2).powmod(8, 31) + self.assertEqual(str(x), '8') + + def test_c_context_templates(self): self.assertEqual( - context.create_decimal_from_float(math.pi), - Decimal('3.1415') + C.BasicContext._traps, + C.DecIEEEInvalidOperation|C.DecDivisionByZero|C.DecOverflow| + C.DecUnderflow|C.DecClamped ) - context = Context(prec=5, rounding=ROUND_UP) self.assertEqual( - context.create_decimal_from_float(math.pi), - Decimal('3.1416') + C.DefaultContext._traps, + C.DecIEEEInvalidOperation|C.DecDivisionByZero|C.DecOverflow ) - context = Context(prec=5, traps=[Inexact]) - self.assertRaises( - Inexact, - context.create_decimal_from_float, - math.pi - ) - self.assertEqual(repr(context.create_decimal_from_float(-0.0)), - "Decimal('-0')") - self.assertEqual(repr(context.create_decimal_from_float(1.0)), - "Decimal('1')") - self.assertEqual(repr(context.create_decimal_from_float(10)), - "Decimal('10')") - -class ContextAPItests(unittest.TestCase): - - def test_pickle(self): - 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) - - def test_equality_with_other_types(self): - 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 - 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)) - - def test__clamp(self): - # In Python 3.2, the private attribute `_clamp` was made - # public (issue 8540), with the old `_clamp` becoming a - # property wrapping `clamp`. For the duration of Python 3.2 - # only, the attribute should be gettable/settable via both - # `clamp` and `_clamp`; in Python 3.3, `_clamp` should be - # removed. - c = Context(clamp = 0) - self.assertEqual(c.clamp, 0) - - with check_warnings(("", DeprecationWarning)): - c._clamp = 1 - self.assertEqual(c.clamp, 1) - with check_warnings(("", DeprecationWarning)): - self.assertEqual(c._clamp, 1) - c.clamp = 0 - self.assertEqual(c.clamp, 0) - with check_warnings(("", DeprecationWarning)): - self.assertEqual(c._clamp, 0) - - def test_abs(self): - c = Context() - d = c.abs(Decimal(-1)) - self.assertEqual(c.abs(-1), d) - self.assertRaises(TypeError, c.abs, '-1') - - def test_add(self): - c = Context() - d = c.add(Decimal(1), Decimal(1)) - self.assertEqual(c.add(1, 1), d) - self.assertEqual(c.add(Decimal(1), 1), d) - self.assertEqual(c.add(1, Decimal(1)), d) - self.assertRaises(TypeError, c.add, '1', 1) - self.assertRaises(TypeError, c.add, 1, '1') - - def test_compare(self): - c = Context() - d = c.compare(Decimal(1), Decimal(1)) - self.assertEqual(c.compare(1, 1), d) - self.assertEqual(c.compare(Decimal(1), 1), d) - self.assertEqual(c.compare(1, Decimal(1)), d) - self.assertRaises(TypeError, c.compare, '1', 1) - self.assertRaises(TypeError, c.compare, 1, '1') - - def test_compare_signal(self): - c = Context() - d = c.compare_signal(Decimal(1), Decimal(1)) - self.assertEqual(c.compare_signal(1, 1), d) - self.assertEqual(c.compare_signal(Decimal(1), 1), d) - self.assertEqual(c.compare_signal(1, Decimal(1)), d) - self.assertRaises(TypeError, c.compare_signal, '1', 1) - self.assertRaises(TypeError, c.compare_signal, 1, '1') - - def test_compare_total(self): - c = Context() - d = c.compare_total(Decimal(1), Decimal(1)) - self.assertEqual(c.compare_total(1, 1), d) - self.assertEqual(c.compare_total(Decimal(1), 1), d) - self.assertEqual(c.compare_total(1, Decimal(1)), d) - self.assertRaises(TypeError, c.compare_total, '1', 1) - self.assertRaises(TypeError, c.compare_total, 1, '1') - - def test_compare_total_mag(self): - c = Context() - d = c.compare_total_mag(Decimal(1), Decimal(1)) - self.assertEqual(c.compare_total_mag(1, 1), d) - self.assertEqual(c.compare_total_mag(Decimal(1), 1), d) - self.assertEqual(c.compare_total_mag(1, Decimal(1)), d) - self.assertRaises(TypeError, c.compare_total_mag, '1', 1) - self.assertRaises(TypeError, c.compare_total_mag, 1, '1') - - def test_copy_abs(self): - 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): - 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): - 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): - c = Context() - d = c.copy_sign(Decimal(1), Decimal(-2)) - self.assertEqual(c.copy_sign(1, -2), d) - self.assertEqual(c.copy_sign(Decimal(1), -2), d) - self.assertEqual(c.copy_sign(1, Decimal(-2)), d) - self.assertRaises(TypeError, c.copy_sign, '1', -2) - self.assertRaises(TypeError, c.copy_sign, 1, '-2') - - def test_divide(self): - c = Context() - d = c.divide(Decimal(1), Decimal(2)) - self.assertEqual(c.divide(1, 2), d) - self.assertEqual(c.divide(Decimal(1), 2), d) - self.assertEqual(c.divide(1, Decimal(2)), d) - self.assertRaises(TypeError, c.divide, '1', 2) - self.assertRaises(TypeError, c.divide, 1, '2') - - def test_divide_int(self): - c = Context() - d = c.divide_int(Decimal(1), Decimal(2)) - self.assertEqual(c.divide_int(1, 2), d) - self.assertEqual(c.divide_int(Decimal(1), 2), d) - self.assertEqual(c.divide_int(1, Decimal(2)), d) - self.assertRaises(TypeError, c.divide_int, '1', 2) - self.assertRaises(TypeError, c.divide_int, 1, '2') - - def test_divmod(self): - c = Context() - d = c.divmod(Decimal(1), Decimal(2)) - self.assertEqual(c.divmod(1, 2), d) - self.assertEqual(c.divmod(Decimal(1), 2), d) - self.assertEqual(c.divmod(1, Decimal(2)), d) - self.assertRaises(TypeError, c.divmod, '1', 2) - self.assertRaises(TypeError, c.divmod, 1, '2') - - def test_exp(self): - c = Context() - d = c.exp(Decimal(10)) - self.assertEqual(c.exp(10), d) - self.assertRaises(TypeError, c.exp, '10') - - def test_fma(self): - c = Context() - d = c.fma(Decimal(2), Decimal(3), Decimal(4)) - self.assertEqual(c.fma(2, 3, 4), d) - self.assertEqual(c.fma(Decimal(2), 3, 4), d) - self.assertEqual(c.fma(2, Decimal(3), 4), d) - self.assertEqual(c.fma(2, 3, Decimal(4)), d) - self.assertEqual(c.fma(Decimal(2), Decimal(3), 4), d) - self.assertRaises(TypeError, c.fma, '2', 3, 4) - self.assertRaises(TypeError, c.fma, 2, '3', 4) - self.assertRaises(TypeError, c.fma, 2, 3, '4') - - # Issue 12079 for Context.fma ... - self.assertRaises(TypeError, c.fma, - Decimal('Infinity'), Decimal(0), "not a decimal") - self.assertRaises(TypeError, c.fma, - Decimal(1), Decimal('snan'), 1.222) - # ... and for Decimal.fma. - self.assertRaises(TypeError, Decimal('Infinity').fma, - Decimal(0), "not a decimal") - self.assertRaises(TypeError, Decimal(1).fma, - Decimal('snan'), 1.222) - - def test_is_finite(self): - 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): - 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): - 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): - 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): - 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): - 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): - 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): - 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): - 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): - c = Context() - d = c.ln(Decimal(10)) - self.assertEqual(c.ln(10), d) - self.assertRaises(TypeError, c.ln, '10') - - def test_log10(self): - c = Context() - d = c.log10(Decimal(10)) - self.assertEqual(c.log10(10), d) - self.assertRaises(TypeError, c.log10, '10') - - def test_logb(self): - c = Context() - d = c.logb(Decimal(10)) - self.assertEqual(c.logb(10), d) - self.assertRaises(TypeError, c.logb, '10') - - def test_logical_and(self): - c = Context() - d = c.logical_and(Decimal(1), Decimal(1)) - self.assertEqual(c.logical_and(1, 1), d) - self.assertEqual(c.logical_and(Decimal(1), 1), d) - self.assertEqual(c.logical_and(1, Decimal(1)), d) - self.assertRaises(TypeError, c.logical_and, '1', 1) - self.assertRaises(TypeError, c.logical_and, 1, '1') - - def test_logical_invert(self): - 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): - c = Context() - d = c.logical_or(Decimal(1), Decimal(1)) - self.assertEqual(c.logical_or(1, 1), d) - self.assertEqual(c.logical_or(Decimal(1), 1), d) - self.assertEqual(c.logical_or(1, Decimal(1)), d) - self.assertRaises(TypeError, c.logical_or, '1', 1) - self.assertRaises(TypeError, c.logical_or, 1, '1') - - def test_logical_xor(self): - c = Context() - d = c.logical_xor(Decimal(1), Decimal(1)) - self.assertEqual(c.logical_xor(1, 1), d) - self.assertEqual(c.logical_xor(Decimal(1), 1), d) - self.assertEqual(c.logical_xor(1, Decimal(1)), d) - self.assertRaises(TypeError, c.logical_xor, '1', 1) - self.assertRaises(TypeError, c.logical_xor, 1, '1') - - def test_max(self): - c = Context() - d = c.max(Decimal(1), Decimal(2)) - self.assertEqual(c.max(1, 2), d) - self.assertEqual(c.max(Decimal(1), 2), d) - self.assertEqual(c.max(1, Decimal(2)), d) - self.assertRaises(TypeError, c.max, '1', 2) - self.assertRaises(TypeError, c.max, 1, '2') - - def test_max_mag(self): - c = Context() - d = c.max_mag(Decimal(1), Decimal(2)) - self.assertEqual(c.max_mag(1, 2), d) - self.assertEqual(c.max_mag(Decimal(1), 2), d) - self.assertEqual(c.max_mag(1, Decimal(2)), d) - self.assertRaises(TypeError, c.max_mag, '1', 2) - self.assertRaises(TypeError, c.max_mag, 1, '2') - - def test_min(self): - c = Context() - d = c.min(Decimal(1), Decimal(2)) - self.assertEqual(c.min(1, 2), d) - self.assertEqual(c.min(Decimal(1), 2), d) - self.assertEqual(c.min(1, Decimal(2)), d) - self.assertRaises(TypeError, c.min, '1', 2) - self.assertRaises(TypeError, c.min, 1, '2') - - def test_min_mag(self): - c = Context() - d = c.min_mag(Decimal(1), Decimal(2)) - self.assertEqual(c.min_mag(1, 2), d) - self.assertEqual(c.min_mag(Decimal(1), 2), d) - self.assertEqual(c.min_mag(1, Decimal(2)), d) - self.assertRaises(TypeError, c.min_mag, '1', 2) - self.assertRaises(TypeError, c.min_mag, 1, '2') - - def test_minus(self): - c = Context() - d = c.minus(Decimal(10)) - self.assertEqual(c.minus(10), d) - self.assertRaises(TypeError, c.minus, '10') - - def test_multiply(self): - c = Context() - d = c.multiply(Decimal(1), Decimal(2)) - self.assertEqual(c.multiply(1, 2), d) - self.assertEqual(c.multiply(Decimal(1), 2), d) - self.assertEqual(c.multiply(1, Decimal(2)), d) - self.assertRaises(TypeError, c.multiply, '1', 2) - self.assertRaises(TypeError, c.multiply, 1, '2') - - def test_next_minus(self): - 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): - 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): - c = Context() - d = c.next_toward(Decimal(1), Decimal(2)) - self.assertEqual(c.next_toward(1, 2), d) - self.assertEqual(c.next_toward(Decimal(1), 2), d) - self.assertEqual(c.next_toward(1, Decimal(2)), d) - self.assertRaises(TypeError, c.next_toward, '1', 2) - self.assertRaises(TypeError, c.next_toward, 1, '2') - - def test_normalize(self): - c = Context() - d = c.normalize(Decimal(10)) - self.assertEqual(c.normalize(10), d) - self.assertRaises(TypeError, c.normalize, '10') - - def test_number_class(self): - 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): - c = Context() - d = c.plus(Decimal(10)) - self.assertEqual(c.plus(10), d) - self.assertRaises(TypeError, c.plus, '10') - - def test_quantize(self): - c = Context() - d = c.quantize(Decimal(1), Decimal(2)) - self.assertEqual(c.quantize(1, 2), d) - self.assertEqual(c.quantize(Decimal(1), 2), d) - self.assertEqual(c.quantize(1, Decimal(2)), d) - self.assertRaises(TypeError, c.quantize, '1', 2) - self.assertRaises(TypeError, c.quantize, 1, '2') - - def test_remainder(self): - c = Context() - d = c.remainder(Decimal(1), Decimal(2)) - self.assertEqual(c.remainder(1, 2), d) - self.assertEqual(c.remainder(Decimal(1), 2), d) - self.assertEqual(c.remainder(1, Decimal(2)), d) - self.assertRaises(TypeError, c.remainder, '1', 2) - self.assertRaises(TypeError, c.remainder, 1, '2') - - def test_remainder_near(self): - c = Context() - d = c.remainder_near(Decimal(1), Decimal(2)) - self.assertEqual(c.remainder_near(1, 2), d) - self.assertEqual(c.remainder_near(Decimal(1), 2), d) - self.assertEqual(c.remainder_near(1, Decimal(2)), d) - self.assertRaises(TypeError, c.remainder_near, '1', 2) - self.assertRaises(TypeError, c.remainder_near, 1, '2') - - def test_rotate(self): - c = Context() - d = c.rotate(Decimal(1), Decimal(2)) - self.assertEqual(c.rotate(1, 2), d) - self.assertEqual(c.rotate(Decimal(1), 2), d) - self.assertEqual(c.rotate(1, Decimal(2)), d) - self.assertRaises(TypeError, c.rotate, '1', 2) - self.assertRaises(TypeError, c.rotate, 1, '2') - - def test_sqrt(self): - c = Context() - d = c.sqrt(Decimal(10)) - self.assertEqual(c.sqrt(10), d) - self.assertRaises(TypeError, c.sqrt, '10') - - def test_same_quantum(self): - c = Context() - d = c.same_quantum(Decimal(1), Decimal(2)) - self.assertEqual(c.same_quantum(1, 2), d) - self.assertEqual(c.same_quantum(Decimal(1), 2), d) - self.assertEqual(c.same_quantum(1, Decimal(2)), d) - self.assertRaises(TypeError, c.same_quantum, '1', 2) - self.assertRaises(TypeError, c.same_quantum, 1, '2') - - def test_scaleb(self): - c = Context() - d = c.scaleb(Decimal(1), Decimal(2)) - self.assertEqual(c.scaleb(1, 2), d) - self.assertEqual(c.scaleb(Decimal(1), 2), d) - self.assertEqual(c.scaleb(1, Decimal(2)), d) - self.assertRaises(TypeError, c.scaleb, '1', 2) - self.assertRaises(TypeError, c.scaleb, 1, '2') - - def test_shift(self): - c = Context() - d = c.shift(Decimal(1), Decimal(2)) - self.assertEqual(c.shift(1, 2), d) - self.assertEqual(c.shift(Decimal(1), 2), d) - self.assertEqual(c.shift(1, Decimal(2)), d) - self.assertRaises(TypeError, c.shift, '1', 2) - self.assertRaises(TypeError, c.shift, 1, '2') - - def test_subtract(self): - c = Context() - d = c.subtract(Decimal(1), Decimal(2)) - self.assertEqual(c.subtract(1, 2), d) - self.assertEqual(c.subtract(Decimal(1), 2), d) - self.assertEqual(c.subtract(1, Decimal(2)), d) - self.assertRaises(TypeError, c.subtract, '1', 2) - self.assertRaises(TypeError, c.subtract, 1, '2') - - def test_to_eng_string(self): - 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): - 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): - 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): - 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') - -class WithStatementTest(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 - orig_ctx = getcontext() - with localcontext() as enter_ctx: - set_ctx = getcontext() - final_ctx = getcontext() - self.assertIs(orig_ctx, final_ctx, 'did not restore context correctly') - self.assertIsNot(orig_ctx, set_ctx, 'did not copy the context') - self.assertIs(set_ctx, enter_ctx, '__enter__ returned wrong context') - - def test_localcontextarg(self): - # Use a copy of the supplied context in the block - orig_ctx = getcontext() - new_ctx = Context(prec=42) - with localcontext(new_ctx) as enter_ctx: - set_ctx = getcontext() - final_ctx = getcontext() - self.assertIs(orig_ctx, final_ctx, 'did not restore context correctly') - self.assertEqual(set_ctx.prec, new_ctx.prec, 'did not set correct context') - self.assertIsNot(new_ctx, set_ctx, 'did not copy the context') - self.assertIs(set_ctx, enter_ctx, '__enter__ returned wrong context') - -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 - - context = Context(prec=9, Emin = -999999999, Emax = 999999999, - rounding=ROUND_HALF_EVEN, traps=[], flags=[]) - - # operations that raise various flags, in the form (function, arglist) - operations = [ - (context._apply, [Decimal("100E-1000000009")]), - (context.sqrt, [Decimal(2)]), - (context.add, [Decimal("1.23456789"), Decimal("9.87654321")]), - (context.multiply, [Decimal("1.23456789"), Decimal("9.87654321")]), - (context.subtract, [Decimal("1.23456789"), Decimal("9.87654321")]), - ] - - # try various flags individually, then a whole lot at once - flagsets = [[Inexact], [Rounded], [Underflow], [Clamped], [Subnormal], - [Inexact, Rounded, Underflow, Clamped, Subnormal]] - - for fn, args in operations: - # find answer and flags raised using a clean context - context.clear_flags() - ans = fn(*args) - flags = [k for k, v in context.flags.items() if v] - - for extra_flags in flagsets: - # set flags, before calling operation - context.clear_flags() - for flag in extra_flags: - context._raise_error(flag) - new_ans = fn(*args) - - # flags that we expect to be set after the operation - expected_flags = list(flags) - for flag in extra_flags: - if flag not in expected_flags: - expected_flags.append(flag) - expected_flags.sort(key=id) - - # flags we actually got - new_flags = [k for k,v in context.flags.items() if v] - new_flags.sort(key=id) - - self.assertEqual(ans, new_ans, - "operation produces different answers depending on flags set: " + - "expected %s, got %s." % (ans, new_ans)) - self.assertEqual(new_flags, expected_flags, - "operation raises different flags depending on flags set: " + - "expected %s, got %s" % (expected_flags, new_flags)) + + def test_c_signal_dict(self): + if hasattr(C, 'setfailpoint'): + random.seed(randseed) + + # 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) + self.assertTrue(c.traps.has_key(Overflow)) + 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 = 1 if hasattr(C, 'setfailpoint') else 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), _allcr=cr) + + 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) + self.assertEqual(c._allcr, cr) + + 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) + + +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, + 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] + +# Wrap test functions for testing api failures. Doing this in +# test_main() causes spurious refleaks, so it is done here. +if hasattr(C, 'setapicalls'): + for cls in all_tests: + if cls == CIBMTestCases or cls == PyIBMTestCases: + newfunc = withFailpoint(getattr(cls, 'eval_equation')) + setattr(cls, 'eval_equation', newfunc) + else: + for attr in dir(cls): + if attr.startswith('test_'): + if attr == 'test_threading': + continue + newfunc = withFailpoint(getattr(cls, attr)) + setattr(cls, attr, newfunc) def test_main(arith=False, verbose=None, todo_tests=None, debug=None): """ Execute the tests. @@ -2375,27 +4686,16 @@ 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 @@ -2407,17 +4707,32 @@ 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 diff -r 203407036e46 -r 49433f35a5f8 Lib/test/test_fractions.py --- a/Lib/test/test_fractions.py Fri Jun 17 21:10:21 2011 +0200 +++ b/Lib/test/test_fractions.py Sat Jun 18 13:21:59 2011 +0200 @@ -391,10 +391,8 @@ 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.assertRaises(TypeError, operator.add, + F(3,11), Decimal('3.1415926')) def testComparisons(self): self.assertTrue(F(1, 2) < F(2, 3)) diff -r 203407036e46 -r 49433f35a5f8 Lib/test/test_numeric_tower.py --- a/Lib/test/test_numeric_tower.py Fri Jun 17 21:10:21 2011 +0200 +++ b/Lib/test/test_numeric_tower.py Sat Jun 18 13:21:59 2011 +0200 @@ -150,7 +150,7 @@ # int, float, Fraction, Decimal test_values = [ float('-inf'), - D('-1e999999999'), + D('-1e425000000'), -1e308, F(-22, 7), -3.14, diff -r 203407036e46 -r 49433f35a5f8 Misc/valgrind-python.supp --- a/Misc/valgrind-python.supp Fri Jun 17 21:10:21 2011 +0200 +++ b/Misc/valgrind-python.supp Sat Jun 18 13:21:59 2011 +0200 @@ -389,3 +389,9 @@ } +# Additional suppressions for the unified decimal tests: +{ + test_decimal + Memcheck:Addr4 + fun:PyUnicodeUCS2_FSConverter +} diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/LIBTEST.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/LIBTEST.txt Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,169 @@ + +# ====================================================================== +# Unix: build Python +# ====================================================================== + +# First, do the regular Python build: +cd ../../ +./configure +make +cd Modules/_decimal + + +# ====================================================================== +# Unix: short library tests +# ====================================================================== + +./configure +make # gmake +make check + + +# ====================================================================== +# Unix: long library tests +# ====================================================================== + +# Some of these tests require libgmp-dev. +./configure +make extended # gmake +cd tests && ./runalltests.sh + + +# ====================================================================== +# Unix: tests against decNumber +# ====================================================================== + +# It is assumed that libmpdec has been built already. + +# Get decNumber and apply required patch: +./getdn.sh +make deccheck # gmake + +# Run the tests: +# --short: random tests +# --exit: exit on error (by default the error is just printed). +# --minalloc: iterate through all possible values for MPD_MINALLOC. +./deccheck [--short|--minalloc|--exit] + + +# ====================================================================== +# Unix: all configurations +# ====================================================================== + +# +# Here is the list of possible configurations: +# +# 1. x64 - 64-bit OS with x86_64 processor (AMD, Intel) +# +# 2. uint128 - 64-bit OS, compiler has __uint128_t (should be +# the case for gcc) +# +# 3. ansi64 - 64-bit OS, double width multiplication and +# division in ANSI C. +# +# 3. ppro - 32-bit OS with any x86 processor that has at +# least PentiumPro capabilities +# +# 5. ansi32 - 32-bit or 64-bit OS, produces 32-bit library +# +# 6. ansi-legacy - 32-bit OS, compiler without uint64_t +# +# TESTING ONLY: +# +# 7. full_coverage - 64bit OS, produces 64-bit library, +# using CONFIG_32. +# + +# On amd64 with multilib it is possible to run all configs. Otherwise, +# edit runallconfigs.sh and delete configs as appropriate. +cd tests && ./runallconfigs.sh + +# With Valgrind: +cd tests && ./runallconfigs.sh --valgrind + + +# ====================================================================== +# Unix: code coverage tests +# ====================================================================== + +# These tests require gcc/gcov and take quite long. To achieve 100% +# coverage of the various multiplication functions, 4-8GB of RAM is +# required for CONFIG_32, 1TB for CONFIG_64. +# +# So, realistically it is only possible to get 100% coverage using +# ./configure MACHINE=full_coverage on a 64-bit machine with 8GB of +# memory. +# +# !!! MACHINE=full_coverage is for testing only and does not work +# for pycoverage. + +# Library coverage: +./configure +make libcoverage # gmake +python tests/covreport.py +make distclean + +# 100% library coverage (64-bit OS): +./configure MACHINE=full_coverage +patch < tests/fullcov_header.patch +make libcoverage # gmake +python tests/covreport.py +make distclean +patch -R < tests/fullcov_header.patch + +# Module coverage: +./configure +make pycoverage # gmake +python tests/covreport.py +make distclean + +# lib/pycoverage: +./configure +make coverage # gmake +python tests/covreport.py +make distclean + + +# ====================================================================== +# 64-bit Windows +# ====================================================================== + +copy Makefile.vc Makefile + +# Find the path to vcvarsamd64.bat (or vcvars64.bat) and execute it. Example: +"C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\amd64\vcvarsamd64.bat" + +# Build static library: +nmake MACHINE=x64 +# Short tests +nmake MACHINE=x64 check +# Long tests +make MACHINE=x64 extended +cd tests && runalltests.bat +# Long tests requires gmp, edit paths to gmp libs in Makefile: +make MACHINE=x64 extended_gmp +cd tests && runalltests.bat + + +# ====================================================================== +# 32-bit Windows +# ====================================================================== + +copy Makefile.vc Makefile + +# Find the path to vcvars32.bat and execute it. Example: +"C:\Program Files\Microsoft Visual Studio 9.0\VC\bin\vcvars32.bat" + +# Build static library: +nmake MACHINE=ppro +# Short tests +nmake MACHINE=ppro check +# Long tests +make MACHINE=ppro extended +cd tests && runalltests.bat +# Long tests requires gmp, edit paths to gmp libs in Makefile: +make MACHINE=x64 extended_gmp +cd tests && runalltests.bat + + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/Makefile.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/Makefile.in Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,267 @@ + +# ============================================================================== +# Unix Makefile for libmpdec +# ============================================================================== + +include vars.mk + +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +LIBSTATIC = libmpdec.a +LIBSONAME = @LIBSHARED@ +LIBSHARED = @LIBSHARED@ + +CC = @CC@ +LD = @LD@ +AR = @AR@ +GCOV ?= gcov +INC= -I../../ -I../../Include +prefix = @prefix@ +exec_prefix = @exec_prefix@ +includedir = @includedir@ +libdir = @libdir@ +datarootdir = @datarootdir@ +docdir = @docdir@ + +MPD_HEADER = @MPD_HEADER@ +MPD_WARN = @MPD_WARN@ +MPD_CONFIG = @MPD_CONFIG@ +MPD_OPT = @MPD_OPT@ + +MPD_CCOV = @MPD_CCOV@ +MPD_LDCOV = @MPD_LDCOV@ +MPD_PGEN = @MPD_PGEN@ +MPD_PUSE = @MPD_PUSE@ +MPD_PREC = @MPD_PREC@ +MPD_DPREC = @MPD_DPREC@ + +ifneq (, $(findstring debug, $(MAKECMDGOALS))) + CFLAGS = $(MPD_WARN) $(MPD_CONFIG) -O0 -g -fpic +endif +ifeq ($(MAKECMDGOALS), check) + ifdef SAVED_CFLAGS + CFLAGS = $(SAVED_CFLAGS) + endif + ifdef SAVED_LDFLAGS + LDFLAGS = $(SAVED_LDFLAGS) + endif +endif +ifeq ($(MAKECMDGOALS), libcoverage) + CFLAGS = $(MPD_WARN) $(MPD_CONFIG) $(MPD_CCOV) + LDFLAGS = $(MPD_LDCOV) +endif +ifeq ($(MAKECMDGOALS), pycoverage) + CFLAGS = $(MPD_WARN) $(MPD_CONFIG) $(MPD_CCOV) + LDFLAGS = $(MPD_LDCOV) +endif +ifeq ($(MAKECMDGOALS), coverage) + CFLAGS = $(MPD_WARN) $(MPD_CONFIG) $(MPD_CCOV) + LDFLAGS = $(MPD_LDCOV) +endif + +CONFIGURE_CFLAGS = @CONFIGURE_CFLAGS@ +CFLAGS ?= $(CONFIGURE_CFLAGS) + +CONFIGURE_LDFLAGS = @CONFIGURE_LDFLAGS@ +LDFLAGS ?= $(CONFIGURE_LDFLAGS) + +ifeq ($(MAKECMDGOALS), profile_gen) + CFLAGS += $(MPD_PGEN) + LDFLAGS += $(MPD_PGEN) +endif +ifeq ($(MAKECMDGOALS), profile_use) + CFLAGS += $(MPD_PUSE) + LDFLAGS += $(MPD_PUSE) +endif + +CC := $(strip $(CC)) +CFLAGS := $(strip $(CFLAGS)) +LDFLAGS := $(strip $(LDFLAGS)) + +NEWVARS = +ifneq ($(SAVED_CC), $(CC)) + NEWVARS = NEWVARS +endif +ifneq ($(SAVED_CFLAGS), $(CFLAGS)) + NEWVARS = NEWVARS +endif +ifneq ($(SAVED_LDFLAGS), $(LDFLAGS)) + NEWVARS = NEWVARS +endif + + +default: $(LIBSTATIC) $(LIBSHARED) +debug: default + + +OBJS := basearith.o context.o constants.o convolute.o crt.o mpdecimal.o \ + mpsignal.o difradix2.o fnt.o fourstep.o io.o memory.o numbertheory.o \ + sixstep.o transpose.o + +$(LIBSTATIC): Makefile vars.mk $(OBJS) + $(AR) rc $(LIBSTATIC) $(OBJS) + ranlib $(LIBSTATIC) + +$(LIBSHARED): Makefile vars.mk $(OBJS) + $(LD) $(LDFLAGS) -shared -Wl,-soname,$(LIBSONAME) -o $(LIBSHARED) $(OBJS) -lm + + +$(NEWVARS): + +vars.mk: $(NEWVARS) + @echo "SAVED_CC=$(CC)" > vars.mk + @echo "SAVED_CFLAGS=$(CFLAGS)" >> vars.mk + @echo "SAVED_LDFLAGS=$(LDFLAGS)" >> vars.mk + + +basearith.o:\ +Makefile vars.mk basearith.c mpdecimal.h constants.h memory.h \ +typearith.h basearith.h + $(CC) $(INC) $(CFLAGS) -c basearith.c + +constants.o:\ +Makefile vars.mk constants.c mpdecimal.h constants.h + $(CC) $(INC) $(CFLAGS) -c constants.c + +context.o:\ +Makefile vars.mk context.c mpdecimal.h + $(CC) $(INC) $(CFLAGS) -c context.c + +convolute.o:\ +Makefile vars.mk convolute.c mpdecimal.h bits.h constants.h fnt.h fourstep.h \ +numbertheory.h sixstep.h umodarith.h typearith.h convolute.h vccompat.h + $(CC) $(INC) $(CFLAGS) -c convolute.c + +crt.o:\ +Makefile vars.mk crt.c mpdecimal.h numbertheory.h constants.h umodarith.h \ +typearith.h crt.h + $(CC) $(INC) $(CFLAGS) -c crt.c + +difradix2.o:\ +Makefile vars.mk difradix2.c mpdecimal.h bits.h numbertheory.h constants.h \ +umodarith.h typearith.h difradix2.h vccompat.h + $(CC) $(INC) $(CFLAGS) -c difradix2.c + +fnt.o:\ +Makefile vars.mk fnt.c bits.h mpdecimal.h difradix2.h numbertheory.h \ +constants.h fnt.h vccompat.h + $(CC) $(INC) $(CFLAGS) -c fnt.c + +fourstep.o:\ +Makefile vars.mk fourstep.c mpdecimal.h numbertheory.h constants.h sixstep.h \ +transpose.h umodarith.h typearith.h fourstep.h + $(CC) $(INC) $(CFLAGS) -c fourstep.c + +io.o:\ +Makefile vars.mk io.c mpdecimal.h bits.h constants.h memory.h typearith.h \ +io.h vccompat.h + $(CC) $(INC) $(CFLAGS) -c io.c + +memory.o:\ +Makefile vars.mk memory.c mpdecimal.h typearith.h memory.h + $(CC) $(INC) $(CFLAGS) -c memory.c + +mpdecimal.o:\ +Makefile vars.mk mpdecimal.c basearith.h mpdecimal.h typearith.h bits.h \ +convolute.h crt.h memory.h umodarith.h constants.h mptest.h mptypes.h \ +vccompat.h + $(CC) $(INC) $(CFLAGS) -c mpdecimal.c + +mpsignal.o:\ +Makefile vars.mk mpdecimal.c mpdecimal.h vccompat.h + $(CC) $(INC) $(CFLAGS) -c mpsignal.c + +numbertheory.o:\ +Makefile vars.mk numbertheory.c mpdecimal.h bits.h umodarith.h constants.h \ +typearith.h numbertheory.h vccompat.h + $(CC) $(INC) $(CFLAGS) -c numbertheory.c + +sixstep.o:\ +Makefile vars.mk sixstep.c mpdecimal.h bits.h difradix2.h numbertheory.h \ +constants.h mptypes.h transpose.h umodarith.h typearith.h sixstep.h \ +vccompat.h + $(CC) $(INC) $(CFLAGS) -c sixstep.c + +transpose.o:\ +Makefile vars.mk transpose.c mpdecimal.h bits.h constants.h typearith.h \ +transpose.h vccompat.h + $(CC) $(INC) $(CFLAGS) -c transpose.c + + +# pycoverage +_decimal.o:\ +Makefile vars.mk _decimal.c mpdecimal.h docstrings.h memory.h mptypes.h + $(CC) $(INC) -Wno-missing-field-initializers $(CFLAGS) -pthread -c _decimal.c + +_decimal.so:\ +Makefile vars.mk _decimal.o $(LIBSTATIC) + $(CC) $(INC) $(LDFLAGS) -pthread -shared -o _decimal.so _decimal.o $(LIBSTATIC) +# end pycoverage + + +check: $(LIBSTATIC) FORCE + cd tests && $(MAKE) "CFLAGS=$(CFLAGS)" "LDFLAGS=$(LDFLAGS)" && ./runshort.sh + +extended:\ +Makefile $(LIBSTATIC) + cd tests && $(MAKE) extended + +build_libcoverage: clean $(LIBSTATIC) + cd tests && $(MAKE) build_libcoverage + +build_pycoverage: build_libcoverage _decimal.so + +libcoverage: build_libcoverage + @echo "" + @cd tests && ./runshort.sh && ./cov && ./test_transpose && ./fntcov + @for file in *.c; do $(GCOV) -l "$$file" > /dev/null 2>&1; done + @echo "" + @./tests/covreport.py + +pycoverage: build_pycoverage + @echo "" + @cd ../../ && PYTHONPATH=Modules/_decimal ./python -m test -uall test_decimal + @for file in *.c; do $(GCOV) -l "$$file" > /dev/null 2>&1; done + @echo "" + @./tests/covreport.py + +coverage: build_libcoverage build_pycoverage libcoverage pycoverage + + +FORCE: + +bench: FORCE $(LIBSTATIC) + $(CC) $(INC) $(CFLAGS) -o bench bench.c $(LIBSTATIC) -lm + +profile_gen: clean bench + ./bench $(MPD_PREC) 1000 + ./bench $(MPD_DPREC) 1000 + rm -f *.o *.gch $(LIBSTATIC) $(LIBSHARED) bench runtest + +profile_use: bench + ./bench $(MPD_PREC) 1000 + ./bench $(MPD_DPREC) 1000 + +profile: + $(MAKE) profile_gen + $(MAKE) profile_use + +install: FORCE + mkdir -p $(DESTDIR)$(includedir) && cp mpdecimal.h $(DESTDIR)$(includedir) + mkdir -p $(DESTDIR)$(libdir) && cp $(LIBSTATIC) $(LIBSHARED) $(DESTDIR)$(libdir) + mkdir -p $(DESTDIR)$(docdir) && cp -R doc/* $(DESTDIR)$(docdir) + +clean: FORCE + rm -f *.o *.so *.gch *.gcda *.gcno *.gcov *.dyn *.dpi *.lock \ + $(LIBSTATIC) $(LIBSHARED) bench + rm -rf build + cd python && rm -f _decimal*.so *.pyc + cd tests && if [ -f Makefile ]; then $(MAKE) clean; else exit 0; fi + +distclean: clean + rm -rf config.h config.log config.status Makefile + echo "" > vars.mk + cd tests && if [ -f Makefile ]; then $(MAKE) distclean; else exit 0; fi + cd python && rm -rf decimaltestdata build __pycache__ + + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/Makefile.vc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/Makefile.vc Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,239 @@ + +# ====================================================================== +# Visual C (nmake) Makefile for libmpdec +# ====================================================================== + + +INSTALLDIR = . +LIBSTATIC = libmpdec-@RELEASE_VERSION@.lib +LIBIMPORT = libmpdec-@RELEASE_VERSION@.dll.lib +LIBSHARED = libmpdec-@RELEASE_VERSION@.dll + + +OBJS = basearith.obj context.obj constants.obj convolute.obj crt.obj \ + mpdecimal.obj mpsignal.obj difradix2.obj fnt.obj fourstep.obj \ + io.obj memory.obj numbertheory.obj sixstep.obj transpose.obj + + +MPD_PREC = 9 +MPD_DPREC = 18 +GMPLIB = "C:\Program Files (x86)\gmp\gmp.lib" +GMPINC = "C:\Program Files (x86)\gmp" + +!if "$(MACHINE)" == "x64" +CONFIG = /DCONFIG_64 /DMASM +MPD_PREC = 19 +MPD_DPREC = 38 +GMPLIB = "C:\Program Files\gmp\gmp.lib" +GMPINC = "C:\Program Files\gmp" +OBJS = $(OBJS) vcdiv64.obj +!endif +!if "$(MACHINE)" == "ansi64" +CONFIG = /DCONFIG_64 /DANSI +MPD_PREC = 19 +MPD_DPREC = 38 +GMPLIB = "C:\Program Files\gmp\gmp.lib" +GMPINC = "C:\Program Files\gmp" +!endif +!if "$(MACHINE)" == "full_coverage" +CONFIG = /DTEST_COVERAGE /DCONFIG_32 /DANSI +GMPLIB = "C:\Program Files\gmp\gmp.lib" +GMPINC = "C:\Program Files\gmp" +!endif +!if "$(MACHINE)" == "ppro" +CONFIG = /DCONFIG_32 /DPPRO /DMASM +!endif +!if "$(MACHINE)" == "ansi32" +CONFIG = /DCONFIG_32 /DANSI +!endif +!if "$(MACHINE)" == "ansi-legacy" +CONFIG = /DCONFIG_32 /DANSI /DLEGACY_COMPILER +!endif + +!if "$(DLL)" == "1" +BFLAGS= /DBUILD_DLL +UFLAGS= /DUSE_DLL +LFLAGS= $(LFLAGS) /DLL +BUILDLIB = $(LIBSHARED) +USELIB = $(LIBIMPORT) +!if "$(DEBUG)" == "1" +OPT = /MDd /Od /Zi +!else +OPT = /MD /Ox /GS /EHsc +!endif +!else +BUILDLIB = $(LIBSTATIC) +USELIB = $(LIBSTATIC) +!if "$(DEBUG)" == "1" +OPT = /MTd /Od /Zi +!else +OPT = /MT /Ox /GS /EHsc +!endif +!endif + +CFLAGS = /W3 /D_CRT_SECURE_NO_WARNINGS /nologo $(CONFIG) $(OPT) + + +default: $(BUILDLIB) + + +$(LIBSTATIC): Makefile $(OBJS) + -@if exist $@ del $(LIBSTATIC) + lib $(LFLAGS) /out:$(LIBSTATIC) $(OBJS) + + +$(LIBSHARED): Makefile $(OBJS) + -@if exist $@ del $(LIBSHARED) + link $(LFLAGS) /out:$(LIBSHARED) /implib:$(LIBIMPORT) $(OBJS) + mt -manifest $(LIBSHARED).manifest -outputresource:$(LIBSHARED);2 + + +basearith.obj:\ +Makefile basearith.c constants.h mpdecimal.h memory.h typearith.h \ +basearith.h + $(CC) $(BFLAGS) $(CFLAGS) $(PFLAGS) -c basearith.c + +constants.obj:\ +Makefile constants.c mpdecimal.h constants.h + $(CC) $(BFLAGS) $(CFLAGS) $(PFLAGS) -c constants.c + +context.obj:\ +Makefile context.c mpdecimal.h + $(CC) $(BFLAGS) $(CFLAGS) $(PFLAGS) -c context.c + +convolute.obj:\ +Makefile convolute.c bits.h mpdecimal.h constants.h fnt.h fourstep.h \ +numbertheory.h sixstep.h umodarith.h typearith.h convolute.h vccompat.h + $(CC) $(BFLAGS) $(CFLAGS) $(PFLAGS) -c convolute.c + +crt.obj:\ +Makefile crt.c mpdecimal.h numbertheory.h constants.h umodarith.h \ +typearith.h crt.h + $(CC) $(BFLAGS) $(CFLAGS) $(PFLAGS) -c crt.c + +difradix2.obj:\ +Makefile difradix2.c bits.h mpdecimal.h numbertheory.h constants.h \ +umodarith.h typearith.h difradix2.h vccompat.h + $(CC) $(BFLAGS) $(CFLAGS) $(PFLAGS) -c difradix2.c + +fnt.obj:\ +Makefile fnt.c bits.h mpdecimal.h difradix2.h numbertheory.h constants.h \ +fnt.h vccompat.h + $(CC) $(BFLAGS) $(CFLAGS) $(PFLAGS) -c fnt.c + +fourstep.obj:\ +Makefile fourstep.c mpdecimal.h numbertheory.h constants.h sixstep.h \ +transpose.h umodarith.h typearith.h fourstep.h + $(CC) $(BFLAGS) $(CFLAGS) $(PFLAGS) -c fourstep.c + +io.obj:\ +Makefile io.c bits.h mpdecimal.h constants.h memory.h typearith.h io.h \ +vccompat.h + $(CC) $(BFLAGS) $(CFLAGS) $(PFLAGS) -c io.c + +memory.obj:\ +Makefile memory.c mpdecimal.h typearith.h memory.h + $(CC) $(BFLAGS) $(CFLAGS) $(PFLAGS) -c memory.c + +mpdecimal.obj:\ +Makefile mpdecimal.c basearith.h mpdecimal.h typearith.h bits.h \ +convolute.h crt.h memory.h umodarith.h constants.h mptest.h mptypes.h \ +vccompat.h + $(CC) $(BFLAGS) $(CFLAGS) $(PFLAGS) -c mpdecimal.c + +mpsignal.obj:\ +Makefile mpdecimal.c mpdecimal.h vccompat.h + $(CC) $(BFLAGS) $(CFLAGS) $(PFLAGS) -c mpsignal.c + +numbertheory.obj:\ +Makefile numbertheory.c bits.h mpdecimal.h umodarith.h constants.h \ +typearith.h numbertheory.h vccompat.h + $(CC) $(BFLAGS) $(CFLAGS) $(PFLAGS) -c numbertheory.c + +sixstep.obj:\ +Makefile sixstep.c bits.h mpdecimal.h difradix2.h numbertheory.h \ +constants.h mptypes.h transpose.h umodarith.h typearith.h sixstep.h \ +vccompat.h + $(CC) $(BFLAGS) $(CFLAGS) $(PFLAGS) -c sixstep.c + +transpose.obj:\ +Makefile transpose.c bits.h mpdecimal.h constants.h typearith.h \ +transpose.h vccompat.h + $(CC) $(BFLAGS) $(CFLAGS) $(PFLAGS) -c transpose.c + +vcdiv64.obj:\ +Makefile vcdiv64.asm + ml64 /c /Cx vcdiv64.asm + + +check:\ +$(BUILDLIB) + -@if exist $(LIBSHARED) copy /y $(LIBSHARED) tests + -@cd tests + -@copy /y Makefile.vc Makefile + -@nmake UFLAGS="$(UFLAGS)" CFLAGS="$(CFLAGS)" USELIB="$(USELIB)" + -@runshort.bat + +extended:\ +Makefile $(BUILDLIB) + -@if exist $(LIBSHARED) copy /y $(LIBSHARED) tests + -@cd tests + -@copy /y Makefile.vc Makefile + -@nmake UFLAGS="$(UFLAGS)" CFLAGS="$(CFLAGS)" USELIB="$(USELIB)" extended + +extended_gmp:\ +Makefile $(BUILDLIB) + -@if exist $(LIBSHARED) copy /y $(LIBSHARED) tests + -@cd tests + -@copy /y Makefile.vc Makefile + -@nmake UFLAGS="$(UFLAGS)" CFLAGS="$(CFLAGS)" USELIB="$(USELIB)" GMPINC=$(GMPINC) GMPLIB=$(GMPLIB) extended_gmp + +extended_deccheck:\ +Makefile $(BUILDLIB) + -@if exist $(LIBSHARED) copy /y $(LIBSHARED) tests + -@cd tests + -@copy /y Makefile.vc Makefile + -@nmake UFLAGS="$(UFLAGS)" CFLAGS="$(CFLAGS)" USELIB="$(USELIB)" GMPINC=$(GMPINC) GMPLIB=$(GMPLIB) extended_deccheck + + +FORCE: + +bench.exe: Makefile bench.obj + $(CC) $(UFLAGS) $(CFLAGS) $(PFLAGS) bench.obj $(USELIB) + +bench.obj: FORCE + $(CC) $(UFLAGS) $(CFLAGS) $(PFLAGS) -c bench.c + +profile: FORCE + nmake clean + nmake "DLL=1" "PFLAGS=/GL" "LFLAGS=/DLL /LTCG:PGI" + nmake "DLL=1" "PFLAGS=/GL" bench.exe + bench.exe $(MPD_PREC) 1000 + bench.exe $(MPD_DPREC) 1000 + del /Q *.dll bench.exe + link /DLL /LTCG:PGO /out:$(LIBSHARED) /implib:$(LIBIMPORT) $(OBJS) + mt -manifest $(LIBSHARED).manifest -outputresource:$(LIBSHARED);2 + $(CC) $(UFLAGS) $(CFLAGS) $(PFLAGS) bench.c $(USELIB) + bench.exe $(MPD_PREC) 1000 + bench.exe $(MPD_DPREC) 1000 + +clean: FORCE + -@if exist *.obj del *.obj + -@if exist *.dll del *.dll + -@if exist *.exp del *.exp + -@if exist *.lib del *.lib + -@if exist *.ilk del *.ilk + -@if exist *.pdb del *.pdb + -@if exist *.pgc del *.pgc + -@if exist *.pgd del *.pgd + -@if exist *.manifest del *.manifest + -@if exist *.exe del *.exe + -@if exist python\_decimal.pyd del python\_decimal.pyd + -@if exist python\*.pyc del python\*.pyc + -@cd tests && copy /y Makefile.vc Makefile && nmake clean + +distclean: FORCE + nmake clean + -@if exist Makefile del Makefile + -@cd tests && nmake distclean + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/PYTEST.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/PYTEST.txt Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,18 @@ + + +Assuming that the module has been built as part of the Python build: + + +cd python + +# gettests.bat +./gettests.sh + +../../../python deccheck.py [--short|--medium|--long|--all] + + +Small benchmark: + +../../../python bench.py + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/_decimal.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/_decimal.c Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,5588 @@ +/* + * Copyright (c) 2008-2010 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 +#include "longintrepr.h" +#include "pythread.h" +#include "structmember.h" +#include "complexobject.h" +#include "mpdecimal.h" + +#include + +#include "docstrings.h" +#include "memory.h" +#include "mptypes.h" + + +#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 + +#if defined(_MSC_VER) && defined (CONFIG_64) + #define _PyLong_AsMpdSsize PyLong_AsLongLong + #define _PyLong_FromMpdSsize PyLong_FromSsize_t +#else + #define _PyLong_AsMpdSsize PyLong_AsLong + #define _PyLong_FromMpdSsize PyLong_FromLong +#endif + +#define Dec_INCREF_TRUE (Py_INCREF(Py_True), Py_True) +#define Dec_INCREF_FALSE (Py_INCREF(Py_False), Py_False) + +#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 { + PyDictObject dict; + 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) (Py_TYPE(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) + + +#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; + + +typedef struct { + const char *name; + const char *fqname; + uint32_t mpd_cond; + PyObject *dec_cond; +} DecCondMap; + +/* Top level Exception; inherits from ArithmeticError */ +static PyObject *DecimalException = NULL; + +/* Exceptions that correspond to IEEE signals; inherit from DecimalException */ +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}, + {"FpuError", "decimal.FpuError", MPD_Fpu_error, 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]."; + +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]"; + +static int +value_error_int(const char *mesg) +{ + PyErr_SetString(PyExc_ValueError, mesg); + return -1; +} + +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->mpd_cond) { + return cm->dec_cond; + } + } + + INTERNAL_ERROR_PTR("flags_as_exception"); /* GCOV_NOT_REACHED */ +} + +static uint32_t +exception_as_flags(PyObject *ex) +{ + DecCondMap *cm; + + for (cm = signal_map; cm->name != NULL; cm++) { + if (cm->dec_cond == ex) { + return cm->mpd_cond; + } + } + + PyErr_SetString(PyExc_TypeError, invalid_signals_err); + return UINT32_MAX; +} + +static PyObject * +flags_as_list(uint32_t flags) +{ + PyObject *list; + DecCondMap *cm; + + if ((list = PyList_New(0)) == NULL) { + return NULL; + } + + for (cm = cond_map; cm->name != NULL; cm++) { + if (flags&cm->mpd_cond) { + if (PyList_Append(list, cm->dec_cond) < 0) { + goto error; + } + } + } + for (cm = signal_map+1; cm->name != NULL; cm++) { + if (flags&cm->mpd_cond) { + if (PyList_Append(list, cm->dec_cond) < 0) { + goto error; + } + } + } + + return list; + +error: + Py_DECREF(list); + return NULL; +} + +static PyObject * +signals_as_list(uint32_t flags) +{ + PyObject *list; + DecCondMap *cm; + + if ((list = PyList_New(0)) == NULL) { + return NULL; + } + + for (cm = signal_map; cm->name != NULL; cm++) { + if (flags&cm->mpd_cond) { + if (PyList_Append(list, cm->dec_cond) < 0) { + goto error; + } + } + } + + return list; + +error: + Py_DECREF(list); + return NULL; +} + +static uint32_t +list_as_flags(PyObject *list) +{ + PyObject *item; + uint32_t flags, x; + ssize_t n, j; + + if (!PyList_Check(list)) { + PyErr_SetString(PyExc_TypeError, + "argument must be a list of signals."); + return UINT32_MAX; + } + + n = PyList_Size(list); + flags = 0; + for (j = 0; j < n; j++) { + item = PyList_GetItem(list, j); + if ((x = exception_as_flags(item)) == UINT32_MAX) { + return UINT32_MAX; + } + flags |= x; + } + + return flags; +} + +static int +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 -1; + } + + for (cm = signal_map; cm->name != NULL; cm++) { + if ((b = PyDict_GetItemWithError(val, cm->dec_cond)) == NULL) { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, + "invalid signal dict."); + } + return UINT32_MAX; + } + + if ((x = PyObject_IsTrue(b)) < 0) { + return UINT32_MAX; + } + if (x == 1) { + flags |= cm->mpd_cond; + } + } + + return flags; +} + +static uint32_t +long_as_flags(PyObject *v) +{ + long x; + + x = PyLong_AsLong(v); + if (PyErr_Occurred()) { + return UINT32_MAX; + } + if (x < 0 || x > (long)MPD_Max_status) { + PyErr_SetString(PyExc_TypeError, invalid_flags_err); + return UINT32_MAX; + } + + return x; +} + +static mpd_ssize_t +long_as_mpd_ssize(PyObject *v) +{ + mpd_ssize_t x; + + if (!PyLong_Check(v)) { + PyErr_SetString(PyExc_TypeError, + "integer argument required."); + return MPD_SSIZE_MAX; + } + + x = _PyLong_AsMpdSsize(v); + if (PyErr_Occurred()) { + return MPD_SSIZE_MAX; + } + + return x; +} + +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 */ +/******************************************************************************/ + +static int +signaldict_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + if (PyDict_Type.tp_init(self, args, kwds) < 0) { + return -1; + } + + SdFlagAddr(self) = NULL; + return 0; +} + +/* sync flags and dictionary, using the flags as the master */ +static int +signaldict_update(PyObject *self) +{ + PyObject *b; + DecCondMap *cm; + uint32_t flags; + + flags = SdFlags(self); + + for (cm = signal_map; cm->name != NULL; cm++) { + b = (flags&cm->mpd_cond) ? Py_True : Py_False; + if (PyDict_SetItem(self, cm->dec_cond, b) < 0) { + return -1; + } + } + return 0; +} + +/* set all flags to false */ +static int +signaldict_clear_all(PyObject *self) +{ + DecCondMap *cm; + + SdFlags(self) = 0; + + for (cm = signal_map; cm->name != NULL; cm++) { + if (PyDict_SetItem(self, cm->dec_cond, Py_False) < 0) { + return -1; + } + } + return 0; +} + +static int +signaldict_setitem(PyObject *self, PyObject *key, PyObject *value) +{ + uint32_t flag; + int x; + + if ((flag = exception_as_flags(key)) == UINT_MAX) { + return -1; + } + + if ((x = PyObject_IsTrue(value)) < 0) { + return -1; + } + if (x == 1) { + SdFlags(self) |= flag; + if (PyDict_SetItem(self, key, Py_True) < 0) { + return -1; + } + return 0; + } + else { + SdFlags(self) &= ~flag; + if (PyDict_SetItem(self, key, Py_False) < 0) { + return -1; + } + return 0; + } +} + +static PyObject * +signaldict_call_unary(PyObject *self, char *name) +{ + if (signaldict_update(self) < 0) { + return NULL; + } + return PyObject_CallMethod((PyObject *)&PyDict_Type, name, "O", self); +} + +static PyObject * +signaldict_richcompare(PyObject *a, PyObject *b, int op) +{ + if (PyDecSignalDict_Check(a)) { + if (signaldict_update(a) < 0) { + return NULL; + } + } + if (PyDecSignalDict_Check(b)) { + if (signaldict_update(b) < 0) { + return NULL; + } + } + return PyDict_Type.tp_richcompare(a, b, op); +} + +static int +signaldict_contains(PyObject *self, PyObject *key) +{ + if (signaldict_update(self) < 0) { + return -1; + } + return PyDict_Contains(self, key); +} + +static PyObject * +signaldict_copy(PyObject *self) +{ + if (signaldict_update(self) < 0) { + return NULL; + } + return PyDict_Copy(self); +} + +static PyObject * +signaldict_get(PyObject *self, PyObject *args) +{ + PyObject *key = NULL, *failobj = NULL; + if (!PyArg_ParseTuple(args, "O|O", &key, &failobj)) { + return NULL; /* GCOV_NOT_REACHED (why?) */ + } + if (signaldict_update(self) < 0) { + return NULL; + } + if (failobj) { + return PyObject_CallMethod((PyObject *)&PyDict_Type, "get", + "OOO", self, key, failobj); + } + return PyObject_CallMethod((PyObject *)&PyDict_Type, "get", + "OO", self, key); +} + +static PyObject * +signaldict_has_key(PyObject *self, PyObject *key) +{ + int ret; + if (signaldict_update(self) < 0) { + return NULL; + } + ret = PyDict_Contains(self, key); + return ret < 0 ? NULL : PyBool_FromLong(ret); +} + +static PyObject * +signaldict_items(PyObject *self) +{ + if (signaldict_update(self) < 0) { + return NULL; + } + return PyDict_Items(self); +} + +static PyObject * +signaldict_iter(PyObject *self) +{ + if (signaldict_update(self) < 0) { + return NULL; + } + return PyDict_Type.tp_iter(self); +} + +static PyObject * +signaldict_keys(PyObject *self) +{ + if (signaldict_update(self) < 0) { + return NULL; + } + return PyDict_Keys(self); +} + +static Py_ssize_t +signaldict_length(PyObject *self) +{ + if (signaldict_update(self) < 0) { + return -1; + } + return PyDict_Type.tp_as_mapping->mp_length(self); +} + +static int +signaldict_print(PyObject *self, FILE *fp, int flags) /* GCOV_NOT_REACHED */ +{ + if (signaldict_update(self) < 0) { /* GCOV_NOT_REACHED */ + return -1; /* GCOV_NOT_REACHED */ + } + return PyDict_Type.tp_print(self, fp, flags); /* GCOV_NOT_REACHED */ +} + +static PyObject * +signaldict_repr(PyObject *self) +{ + if (signaldict_update(self) < 0) { + return NULL; + } + return PyDict_Type.tp_repr(self); +} + +static PyObject * +signaldict_sizeof(PyObject *self) +{ + return signaldict_call_unary(self, "__sizeof__"); +} + +static int +signaldict_ass_sub(PyObject *self, PyObject *v, PyObject *w) +{ + if (w == NULL) { + return value_error_int("signal keys cannot be deleted."); + } + else { + return signaldict_setitem(self, v, w); + } +} + +static PyObject * +signaldict_subscript(PyObject *self, PyObject *key) +{ + if (signaldict_update(self) < 0) { + return NULL; + } + return PyDict_Type.tp_as_mapping->mp_subscript(self, key); +} + +static PyObject * +signaldict_values(PyObject *self) +{ + if (signaldict_update(self) < 0) { + return NULL; + } + return PyDict_Values(self); +} + + +static PyMappingMethods signaldict_as_mapping = { + (lenfunc)signaldict_length, /*mp_length*/ + (binaryfunc)signaldict_subscript, /*mp_subscript*/ + (objobjargproc)signaldict_ass_sub /*mp_ass_subscript*/ +}; + +static PySequenceMethods signaldict_as_sequence = { + 0, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + signaldict_contains, /* sq_contains */ + 0, /* sq_inplace_concat */ + 0, /* sq_inplace_repeat */ +}; + +static PyMethodDef mapp_methods[] = { + {"__contains__", (PyCFunction)signaldict_contains, METH_O|METH_COEXIST, NULL}, + {"__getitem__", (PyCFunction)signaldict_subscript, METH_O|METH_COEXIST, NULL}, + {"__sizeof__", (PyCFunction)signaldict_sizeof, METH_NOARGS, NULL}, + {"has_key", (PyCFunction)signaldict_has_key, METH_O, NULL}, + {"get", (PyCFunction)signaldict_get, METH_VARARGS, NULL}, + {"keys", (PyCFunction)signaldict_keys, METH_NOARGS, NULL}, + {"items", (PyCFunction)signaldict_items, METH_NOARGS, NULL}, + {"values", (PyCFunction)signaldict_values, METH_NOARGS, NULL}, + {"copy", (PyCFunction)signaldict_copy, METH_NOARGS, NULL}, + {NULL, NULL} +}; + +static PyTypeObject PyDecSignalDict_Type = +{ + PyVarObject_HEAD_INIT(0, 0) + "decimal.SignalDict", /* tp_name */ + sizeof(PyDecSignalDictObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + (printfunc)signaldict_print, /* tp_print */ + (getattrfunc) 0, /* tp_getattr */ + (setattrfunc) 0, /* tp_setattr */ + 0, /* tp_reserved */ + (reprfunc) signaldict_repr, /* tp_repr */ + 0, /* tp_as_number */ + &signaldict_as_sequence, /* tp_as_sequence */ + &signaldict_as_mapping, /* tp_as_mapping */ + (hashfunc) PyObject_HashNotImplemented, /* tp_hash */ + 0, /* tp_call */ + (reprfunc) 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 */ + signaldict_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)signaldict_iter, /* tp_iter */ + 0, /* tp_iternext */ + mapp_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 */ +}; + + +/******************************************************************************/ +/* Context Object, Part 1 */ +/******************************************************************************/ + +static PyObject * +context_getprec(PyObject *self, void *closure UNUSED) +{ + mpd_context_t *ctx; + + ctx = CTX(self); + return Py_BuildValue(CONV_mpd_ssize_t, mpd_getprec(ctx)); +} + +static PyObject * +context_getemax(PyObject *self, void *closure UNUSED) +{ + mpd_context_t *ctx; + + ctx = CTX(self); + return Py_BuildValue(CONV_mpd_ssize_t, mpd_getemax(ctx)); +} + +static PyObject * +context_getemin(PyObject *self, void *closure UNUSED) +{ + mpd_context_t *ctx; + + ctx = CTX(self); + return Py_BuildValue(CONV_mpd_ssize_t, mpd_getemin(ctx)); +} + +static PyObject * +context_getetiny(PyObject *self, PyObject *dummy UNUSED) +{ + mpd_context_t *ctx; + + ctx = CTX(self); + return Py_BuildValue(CONV_mpd_ssize_t, mpd_etiny(ctx)); +} + +static PyObject * +context_getetop(PyObject *self, PyObject *dummy UNUSED) +{ + mpd_context_t *ctx; + + ctx = CTX(self); + return Py_BuildValue(CONV_mpd_ssize_t, mpd_etop(ctx)); +} + +static PyObject * +context_getround(PyObject *self, void *closure UNUSED) +{ + mpd_context_t *ctx; + + ctx = CTX(self); + return Py_BuildValue("i", mpd_getround(ctx)); +} + +static PyObject * +context_getcapitals(PyObject *self, void *closure UNUSED) +{ + return Py_BuildValue("i", CtxCaps(self)); +} + +static PyObject * +context_gettraps(PyObject *self, void *closure UNUSED) +{ + mpd_context_t *ctx; + + ctx = CTX(self); + return Py_BuildValue("i", mpd_gettraps(ctx)); +} + +static PyObject * +context_getstatus(PyObject *self, void *closure UNUSED) +{ + mpd_context_t *ctx; + + ctx = CTX(self); + return Py_BuildValue("i", mpd_getstatus(ctx)); +} + +static PyObject * +context_getclamp(PyObject *self, void *closure UNUSED) +{ + mpd_context_t *ctx; + + ctx = CTX(self); + return Py_BuildValue("i", mpd_getclamp(ctx)); +} + +static PyObject * +context_getallcr(PyObject *self, void *closure UNUSED) +{ + mpd_context_t *ctx; + + ctx = CTX(self); + return Py_BuildValue("i", mpd_getcr(ctx)); +} + +static int +context_setprec(PyObject *self, PyObject *value, void *closure UNUSED) +{ + mpd_context_t *ctx; + mpd_ssize_t x; + + x = long_as_mpd_ssize(value); + if (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 = long_as_mpd_ssize(value); + if (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 = long_as_mpd_ssize(value); + if (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; +} + +static PyObject * +context_unsafe_setprec(PyObject *self, PyObject *value) +{ + mpd_context_t *ctx = CTX(self); + mpd_ssize_t x; + + x = long_as_mpd_ssize(value); + if (PyErr_Occurred()) { + return NULL; + } + + 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 = long_as_mpd_ssize(value); + if (PyErr_Occurred()) { + return NULL; + } + + 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 = long_as_mpd_ssize(value); + if (PyErr_Occurred()) { + return NULL; + } + + ctx->emax = x; + Py_RETURN_NONE; +} + +static int +context_setround(PyObject *self, PyObject *value, void *closure UNUSED) +{ + mpd_context_t *ctx; + mpd_ssize_t x; + + x = long_as_mpd_ssize(value); + if (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 = long_as_mpd_ssize(value); + if (PyErr_Occurred()) { + return -1; + } + BOUNDS_CHECK(x, INT_MIN, INT_MAX); + + if (x != 0 && x != 1) { + return value_error_int( + "valid values for capitals are 0 or 1."); + } + CtxCaps(self) = (int)x; + + return 0; +} + +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 == UINT32_MAX) { + return -1; + } + + ctx = CTX(self); + if (!mpd_qsettraps(ctx, flags)) { + INTERNAL_ERROR_INT("context_settraps"); + } + + return 0; +} + +static int +context_settraps_list(PyObject *self, PyObject *value) +{ + mpd_context_t *ctx; + uint32_t flags; + + flags = list_as_flags(value); + if (flags == UINT32_MAX) { + 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; + + flags = dict_as_flags(value); + if (flags == UINT32_MAX) { + return -1; + } + + ctx = CTX(self); + if (!mpd_qsettraps(ctx, flags)) { + INTERNAL_ERROR_INT("context_settraps_dict"); + } + + return 0; +} + +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 == UINT32_MAX) { + return -1; + } + + ctx = CTX(self); + if (!mpd_qsetstatus(ctx, flags)) { + INTERNAL_ERROR_INT("context_setstatus"); + } + + return 0; +} + +static int +context_setstatus_list(PyObject *self, PyObject *value) +{ + mpd_context_t *ctx; + uint32_t flags; + + flags = list_as_flags(value); + if (flags == UINT32_MAX) { + 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; + + flags = dict_as_flags(value); + if (flags == UINT32_MAX) { + 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 = long_as_mpd_ssize(value); + if (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; +} + +static int +context_setallcr(PyObject *self, PyObject *value, void *closure UNUSED) +{ + mpd_context_t *ctx; + mpd_ssize_t x; + + x = long_as_mpd_ssize(value); + if (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; +} + +static PyObject * +context_getattr(PyObject *self, PyObject *name) +{ + PyObject *retval; + + if (!PyUnicode_Check(name)) { + PyErr_Format(PyExc_TypeError, /* GCOV_NOT_REACHED (why?) */ + "attribute name must be string, not '%.200s'", + name->ob_type->tp_name); /* GCOV_NOT_REACHED */ + return NULL; /* GCOV_NOT_REACHED */ + } + + if (PyUnicode_CompareWithASCIIString(name, "traps") == 0) { + retval = ((PyDecContextObject *)self)->traps; + Py_INCREF(retval); + return retval; + } + else if (PyUnicode_CompareWithASCIIString(name, "flags") == 0) { + retval = ((PyDecContextObject *)self)->flags; + Py_INCREF(retval); + return retval; + } + else { + return PyObject_GenericGetAttr(self, name); + } +} + +static int +context_setattr(PyObject *self, PyObject *name, PyObject *value) +{ + if (!PyUnicode_Check(name)) { + PyErr_Format(PyExc_TypeError, /* GCOV_NOT_REACHED (why?) */ + "attribute name must be string, not '%.200s'", + name->ob_type->tp_name); /* GCOV_NOT_REACHED */ + return -1; /* GCOV_NOT_REACHED */ + } + if (value == NULL) { + PyErr_SetString(PyExc_AttributeError, + "context attributes cannot be deleted."); + return -1; + } + + if (PyUnicode_CompareWithASCIIString(name, "traps") == 0) { + return context_settraps_dict(self, value); + } + else if (PyUnicode_CompareWithASCIIString(name, "flags") == 0) { + return context_setstatus_dict(self, value); + } + else { + return PyObject_GenericSetAttr(self, name, value); + } +} + +static PyObject * +context_clear_traps(PyObject *self, PyObject *dummy UNUSED) +{ + PyDecContextObject *decctx = (PyDecContextObject *)self; + + if (signaldict_clear_all(decctx->traps) < 0) { + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject * +context_clear_flags(PyObject *self, PyObject *dummy UNUSED) +{ + PyDecContextObject *decctx = (PyDecContextObject *)self; + + if (signaldict_clear_all(decctx->flags) < 0) { + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject * +context_new(PyTypeObject *type UNUSED, PyObject *args UNUSED, + PyObject *kwds UNUSED) +{ + PyDecContextObject *self = NULL; + mpd_context_t *ctx; + + self = PyObject_New(PyDecContextObject, &PyDecContext_Type); + 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); + SdFlagAddr(self->traps) = &ctx->traps; + SdFlagAddr(self->flags) = &ctx->status; + + return (PyObject *)self; +} + +static void +context_dealloc(PyDecContextObject *self) +{ + Py_XDECREF(self->traps); + Py_XDECREF(self->flags); + PyObject_Del(self); +} + +#ifdef CONFIG_64 + #define DEC_DFLT_EMAX 999999999 + #define DEC_DFLT_EMIN -999999999 +#else + #define DEC_DFLT_EMAX MPD_MAX_EMAX + #define DEC_DFLT_EMIN MPD_MIN_EMIN +#endif + +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 int +getround(PyObject *v) +{ + const char *s; + long x; + int i; + + if (PyLong_Check(v)) { + x = PyLong_AsLong(v); + if (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", "_allcr", NULL + }; + PyObject *rounding = NULL; + PyObject *traps = NULL; + PyObject *status = NULL; + mpd_context_t *ctx, t=dflt_ctx; + int capitals = 1; + int ret; + + assert(PyTuple_Check(args)); + ctx = CTX(self); + + if (default_context_template) { + t = *CTX(default_context_template); + } + if (!PyArg_ParseTupleAndKeywords( + args, kwds, + "|" CONV_mpd_ssize_t "O" CONV_mpd_ssize_t CONV_mpd_ssize_t "ii" + "OOi", kwlist, + &t.prec, &rounding, &t.emin, &t.emax, &capitals, &t.clamp, + &status, &traps, &t.allcr + )) { + return -1; + } + if (rounding != NULL) { + if ((t.round = getround(rounding)) < 0) { + return -1; + } + } + + if (!mpd_qsetprec(ctx, t.prec) || + !mpd_qsetemin(ctx, t.emin) || + !mpd_qsetemax(ctx, t.emax) || + !mpd_qsetclamp(ctx, t.clamp) || + !mpd_qsetcr(ctx, t.allcr)) { + 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 (PyLong_Check(traps)) { + ret = context_settraps(self, traps, NULL); + } + else if (PyList_Check(traps)) { + ret = context_settraps_list(self, traps); + } + else { + ret = context_settraps_dict(self, traps); + } + if (ret < 0) { + return ret; + } + } + if (status != NULL) { + if (PyLong_Check(status)) { + ret = context_setstatus(self, status, NULL); + } + else if (PyList_Check(status)) { + ret = context_setstatus_list(self, status); + } + else { + ret = context_setstatus_dict(self, status); + } + if (ret < 0) { + return ret; + } + } + + return 0; +} + +#define FD_CTX_LEN 432 +static PyObject * +context_repr(PyDecContextObject *self) +{ + mpd_context_t *ctx; + char s[FD_CTX_LEN]; + char *cp; + int n, mem; + + assert(PyDecContext_Check(self)); + ctx = CTX(self); + + cp = s; mem = FD_CTX_LEN; + n = snprintf(cp, mem, + "Context(prec=%"PRI_mpd_ssize_t", rounding=%s, " + "Emin=%"PRI_mpd_ssize_t", Emax=%"PRI_mpd_ssize_t", " + "capitals=%d, clamp=%d, flags=", + ctx->prec, mpd_round_string[ctx->round], + ctx->emin, ctx->emax, + self->capitals, ctx->clamp); + if (n < 0 || n >= mem) goto error; + cp += n; mem -= n; + + n = mpd_lsnprint_signals(cp, mem, ctx->status, dec_signal_string); + if (n < 0 || n >= mem) goto error; + cp += n; mem -= n; + + n = snprintf(cp, mem, ", traps="); + if (n < 0 || n >= mem) goto error; + cp += n; mem -= n; + + n = mpd_lsnprint_signals(cp, mem, ctx->traps, dec_signal_string); + if (n < 0 || n >= mem) goto error; + cp += n; mem -= n; + + n = snprintf(cp, mem, ")"); + if (n < 0 || n >= mem) goto error; + + return PyUnicode_FromString(s); + +error: + INTERNAL_ERROR_PTR("context_repr"); +} + +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; +} + +/* 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 = long_as_mpd_ssize(v); + if (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; +} + +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); + + if ((flags = signals_as_list(ctx->status)) == NULL) { + return NULL; + } + if ((traps = signals_as_list(ctx->traps)) == NULL) { + Py_DECREF(flags); + return NULL; + } + + ret = Py_BuildValue( + "O(" CONV_mpd_ssize_t "s" CONV_mpd_ssize_t CONV_mpd_ssize_t + "iiOO)", + 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 PyObject * +PyDec_SetStatusFromList(PyObject *self, PyObject *value) +{ + if (context_setstatus_list(self, value) < 0) { + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject * +PyDec_SetTrapsFromList(PyObject *self, PyObject *value) +{ + if (context_settraps_list(self, value) < 0) { + return NULL; + } + Py_RETURN_NONE; +} + + +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}, + { "_clamp", (getter)context_getclamp, (setter)context_setclamp, NULL, NULL}, + { "_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}, + {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) { + if ((v = context_copy(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) \ + if ((ctxobj = current_context()) == 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 *obj; + + if ((obj = current_context()) == NULL) { + return NULL; + } + + Py_INCREF(obj); + return obj; +} + +/* 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) { + if ((v = context_copy(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 */ +/******************************************************************************/ + +/* 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; +} + +/* Wrap PyUnicode_EncodeDecimal. */ +static char * +dec_unicode_as_str(const PyObject *u) +{ + char *s; + + s = PyMem_Malloc(PyUnicode_GET_SIZE(u)+1); + if (s == NULL) { + PyErr_NoMemory(); + return NULL; + } + if (PyUnicode_EncodeDecimal(PyUnicode_AS_UNICODE(u), + PyUnicode_GET_SIZE(u), + s, NULL)) { + PyMem_Free(s); + return NULL; + } + + return s; +} + +/* Strip leading and trailing whitespace. Return x unchanged if no + whitespace is found, otherwise return a newly allocated string + with whitespace stripped. */ +static char * +strip_ws(const char *x) +{ + char *s, *t; + char *y; + size_t n; + + s = (char *)x; + while (isspace((unsigned char)*s)) + s++; + + t = y = s+strlen(s); + while (t > s && isspace((unsigned char)*(t-1))) + t--; + + if (s != x || t != y) { + n = t-s; + if ((y = PyMem_Malloc(n+1)) == NULL) { + PyErr_NoMemory(); + return NULL; + } + strncpy(y, s, n); + y[n] = '\0'; + return y; + } + + return (char *)x; +} + +/* 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 = dec_unicode_as_str(u); + 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, *stripped; + + s = dec_unicode_as_str(u); + if (s == NULL) { + return NULL; + } + + stripped = strip_ws(s); + if (stripped == NULL) { + PyMem_Free(s); + return NULL; + } + + dec = PyDecType_FromCStringExact(type, stripped, context); + if (stripped != s) { + PyMem_Free(stripped); + } + PyMem_Free(s); + + return dec; +} + +/* Set PyDecObject from triple without any error checking. */ +static ALWAYS_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); /* GCOV_NOT_REACHED */ + } + 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 = long_as_mpd_ssize(tmp); + Py_DECREF(tmp); + if (k == MPD_SSIZE_MAX) { + 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); /* GCOV_NOT_REACHED */ + mpd_del(d2); /* GCOV_NOT_REACHED */ + Py_DECREF(dec); /* GCOV_NOT_REACHED */ + return NULL; /* GCOV_NOT_REACHED */ + } + + /* 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); /* GCOV_NOT_REACHED */ + return NULL; /* GCOV_NOT_REACHED */ + } + /* 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; +} + +/* Return a new C string representation of a DecimalTuple. */ +static char * +dectuple_as_str(PyObject *dectuple) +{ + PyObject *digits, *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 (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 = long_as_mpd_ssize(tmp); + if (PyErr_Occurred()) { + goto error; + } + } + + /* coefficient */ + digits = PyTuple_GET_ITEM(dectuple, 1); + if (!PyTuple_Check(digits)) { + PyErr_SetString(PyExc_ValueError, + "coefficient must be a tuple of digits."); + 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 (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; + } + } + + return decstring; + + +error: + if (decstring) PyMem_Free(decstring); + return NULL; +} + +static PyObject * +sequence_as_tuple(PyObject *v) +{ + if (PyTuple_Check(v)) { + Py_INCREF(v); + return v; + } + if (PyList_Check(v)) { + return PyList_AsTuple(v); + } + + return type_error_ptr("argument must be tuple or list."); /* GCOV_NOT_REACHED */ +} + +/* 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); + 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); + 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 UNUSED) +{ + PyObject *v = NULL; + PyObject *context; + + CURRENT_CONTEXT(context); + if (!PyArg_ParseTuple(args, "|OO", &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 +static 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, /* GCOV_NOT_REACHED */ + "exact conversion for comparison failed."); /* GCOV_NOT_REACHED */ + Py_DECREF(result); /* GCOV_NOT_REACHED */ + return NULL; /* GCOV_NOT_REACHED */ + } + + 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 (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 */ +/******************************************************************************/ + +/* PyDecObject as a string. The default module context is only used for + the value of 'capitals'. */ +static PyObject * +dec_str(PyObject *dec) +{ + PyObject *s, *c; + char *res; + + CURRENT_CONTEXT(c); + res = mpd_to_sci(MPD(dec), CtxCaps(c)); + if (res == NULL) { + PyErr_NoMemory(); + return NULL; + } + + s = PyUnicode_FromString(res); + mpd_free(res); + + return s; +} + +static const char *dtag = "Decimal('"; +static const size_t dtaglen = 9; /* without NUL terminator */ + +/* Representation of a PyDecObject. */ +static PyObject * +dec_repr(PyObject *dec) +{ + PyObject *s, *c; + uint8_t err; + char *cp; + size_t declen; + + CURRENT_CONTEXT(c); + cp = mpd_to_sci(MPD(dec), CtxCaps(c)); + if (cp == NULL) { + PyErr_NoMemory(); + return NULL; + } + declen = strlen(cp); + + err = 0; + cp = mpd_realloc(cp, (mpd_size_t)(declen+dtaglen+3), sizeof *cp, &err); + if (err) { + mpd_free(cp); + PyErr_NoMemory(); + return NULL; + } + + memmove(cp+dtaglen, cp, declen); + memcpy(cp, dtag, dtaglen); + cp[declen+dtaglen] = '\''; + cp[declen+dtaglen+1] = ')'; + cp[declen+dtaglen+2] = '\0'; + + s = PyUnicode_FromString(cp); + + mpd_free(cp); + return s; +} + +/* 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 *tmp; + PyObject *context; + mpd_spec_t spec; + wchar_t buf[2]; + 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)) { + if ((fmt = PyUnicode_AsUTF8String(fmtarg)) == 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) { + 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); + } + } + else { + n = strlen(spec.dot); + if (n > 1 || (n == 1 && !isascii((uchar)spec.dot[0]))) { + n = mbstowcs(buf, spec.dot, 2); + if (n != 1) { + PyErr_SetString(PyExc_ValueError, + "invalid decimal point or unsupported " + "combination of LC_CTYPE and LC_NUMERIC."); + goto finish; + } + if ((tmp = PyUnicode_FromWideChar(buf, n)) == NULL) { + goto finish; + } + if ((dot = PyUnicode_AsUTF8String(tmp)) == NULL) { + Py_DECREF(tmp); + goto finish; + } + spec.dot = PyBytes_AS_STRING(dot); + Py_DECREF(tmp); + } + n = strlen(spec.sep); + if (n > 1 || (n == 1 && !isascii((uchar)spec.sep[0]))) { + n = mbstowcs(buf, spec.sep, 2); + if (n != 1) { + PyErr_SetString(PyExc_ValueError, + "invalid thousands separator or unsupported " + "combination of LC_CTYPE and LC_NUMERIC."); + goto finish; + } + if ((tmp = PyUnicode_FromWideChar(buf, n)) == NULL) { + goto finish; + } + if ((sep = PyUnicode_AsUTF8String(tmp)) == NULL) { + Py_DECREF(tmp); + goto finish; + } + spec.sep = PyBytes_AS_STRING(sep); + Py_DECREF(tmp); + } + } + + + 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; + } + + if ((x = mpd_qnew()) == 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; + } + if ((pylong = _PyLong_New(maxsize)) == 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); + } + } + + if ((result = dec_alloc()) == 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); + } + } + + if ((result = dec_alloc()) == 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; + + if ((s = dec_str(dec)) == 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 = long_as_mpd_ssize(x); + if (PyErr_Occurred()) { + return NULL; + } + if ((result = dec_alloc()) == 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; + + + if ((x = mpd_qncopy(MPD(dec))) == NULL) { + PyErr_NoMemory(); + goto out; + } + + sign = Py_BuildValue("i", mpd_sign(MPD(dec))); + if (sign == NULL) goto out; + + if (mpd_isinfinite(x)) { + if ((expt = Py_BuildValue("s", "F")) == NULL) { + goto out; + } + /* decimal.py has non-compliant infinity payloads. */ + if ((coeff = Py_BuildValue("(i)", 0)) == NULL) { + goto out; + } + } + else { + if (mpd_isnan(x)) { + expt = Py_BuildValue("s", mpd_isqnan(x)?"n":"N"); + } + else { + expt = Py_BuildValue(CONV_mpd_ssize_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); + if ((coeff = PyTuple_New(intlen)) == NULL) { + goto out; + } + + for (i = 0; i < intlen; i++) { + tmp = Py_BuildValue("i", intstring[i]-'0'); + if (tmp == NULL) goto out; + PyTuple_SET_ITEM(coeff, i, tmp); + } + } + else { + if ((coeff = PyTuple_New(0)) == 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 *v, PyObject *w) \ +{ \ + PyObject *a, *b; \ + PyObject *result; \ + PyObject *context; \ + uint32_t status = 0; \ + \ + CURRENT_CONTEXT(context) ; \ + CONVERT_BINOP(&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; \ +} + +/* Boolean function without a context arg. */ +#define Dec_BoolFunc(MPDFUNC) \ +static PyObject * \ +dec_##MPDFUNC(PyObject *self, PyObject *dummy UNUSED) \ +{ \ + return MPDFUNC(MPD(self)) ? Dec_INCREF_TRUE : Dec_INCREF_FALSE; \ +} + +/* Boolean function with an optional context arg. */ +#define Dec_BoolFuncVA(MPDFUNC) \ +static PyObject * \ +dec_##MPDFUNC(PyObject *self, PyObject *args) \ +{ \ + PyObject *context; \ + \ + CURRENT_CONTEXT(context); \ + if (!PyArg_ParseTuple(args, "|O", &context)) { \ + return NULL; \ + } \ + CONTEXT_CHECK_VA(context); \ + \ + return MPDFUNC(MPD(self), CTX(context)) \ + ? Dec_INCREF_TRUE : Dec_INCREF_FALSE; \ +} + +/* Unary function with an optional context arg. */ +#define Dec_UnaryFuncVA(MPDFUNC) \ +static PyObject * \ +dec_##MPDFUNC(PyObject *self, PyObject *args) \ +{ \ + PyObject *result; \ + PyObject *context; \ + uint32_t status = 0; \ + \ + CURRENT_CONTEXT(context); \ + if (!PyArg_ParseTuple(args, "|O", &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; \ +} + +/* Binary function with an optional context arg. */ +#define Dec_BinaryFuncVA(MPDFUNC) \ +static PyObject * \ +dec_##MPDFUNC(PyObject *v, PyObject *args) \ +{ \ + PyObject *w, *context; \ + PyObject *a, *b; \ + PyObject *result; \ + uint32_t status = 0; \ + \ + CURRENT_CONTEXT(context); \ + if (!PyArg_ParseTuple(args, "O|O", &w, &context)) { \ + return NULL; \ + } \ + CONTEXT_CHECK_VA(context); \ + 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 function with an optional context arg. Actual MPDFUNC does + NOT take a context. Uses optional context for conversion only. */ +#define Dec_BinaryFuncVA_NO_CTX(MPDFUNC) \ +static PyObject * \ +dec_##MPDFUNC(PyObject *v, PyObject *args) \ +{ \ + PyObject *w, *context; \ + PyObject *a, *b; \ + PyObject *result; \ + \ + CURRENT_CONTEXT(context); \ + if (!PyArg_ParseTuple(args, "O|O", &w, &context)) { \ + return NULL; \ + } \ + CONTEXT_CHECK_VA(context); \ + 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 function with an optional context arg. */ +#define Dec_TernaryFuncVA(MPDFUNC) \ +static PyObject * \ +dec_##MPDFUNC(PyObject *v, PyObject *args) \ +{ \ + PyObject *w, *x, *context; \ + PyObject *a, *b, *c; \ + PyObject *result; \ + uint32_t status = 0; \ + \ + CURRENT_CONTEXT(context); \ + if (!PyArg_ParseTuple(args, "OO|O", &w, &x, &context)) { \ + return NULL; \ + } \ + CONTEXT_CHECK_VA(context); \ + 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; \ +} + + +/**********************************************/ +/* 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); + + if ((q = dec_alloc()) == NULL) { + Py_DECREF(a); + Py_DECREF(b); + return NULL; + } + if ((r = dec_alloc()) == 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 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; + } + } + + if ((result = dec_alloc()) == 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); + Py_DECREF(c); + } + Py_DECREF(a); + Py_DECREF(b); + if (dec_addstatus(context, status)) { + Py_DECREF(result); + return NULL; + } + + return result; +} + + +/******************************************************************************/ +/* Decimal Methods */ +/******************************************************************************/ + +/* Arithmetic operations */ +Dec_UnaryFuncVA(mpd_qabs) +Dec_UnaryFuncVA(mpd_qexp) +Dec_UnaryFuncVA(mpd_qinvroot) +Dec_UnaryFuncVA(mpd_qln) +Dec_UnaryFuncVA(mpd_qlog10) +Dec_UnaryFuncVA(mpd_qminus) +Dec_UnaryFuncVA(mpd_qnext_minus) +Dec_UnaryFuncVA(mpd_qnext_plus) +Dec_UnaryFuncVA(mpd_qplus) +Dec_UnaryFuncVA(mpd_qreduce) +Dec_UnaryFuncVA(mpd_qsqrt) + +Dec_BinaryFuncVA(mpd_qadd) +Dec_BinaryFuncVA(mpd_qcompare) +Dec_BinaryFuncVA(mpd_qcompare_signal) +Dec_BinaryFuncVA(mpd_qdiv) +Dec_BinaryFuncVA(mpd_qdivint) +Dec_BinaryFuncVA(mpd_qmax) +Dec_BinaryFuncVA(mpd_qmax_mag) +Dec_BinaryFuncVA(mpd_qmin) +Dec_BinaryFuncVA(mpd_qmin_mag) +Dec_BinaryFuncVA(mpd_qmul) +Dec_BinaryFuncVA(mpd_qnext_toward) +Dec_BinaryFuncVA(mpd_qpow) +Dec_BinaryFuncVA(mpd_qrem) +Dec_BinaryFuncVA(mpd_qrem_near) +Dec_BinaryFuncVA(mpd_qsub) + +Dec_TernaryFuncVA(mpd_qfma) +Dec_TernaryFuncVA(mpd_qpowmod) + +/* Miscellaneous */ +Dec_BoolFunc(mpd_iscanonical) +Dec_BoolFunc(mpd_isfinite) +Dec_BoolFunc(mpd_isinfinite) +Dec_BoolFunc(mpd_isinteger) +Dec_BoolFunc(mpd_isnan) +Dec_BoolFunc(mpd_isqnan) +Dec_BoolFunc(mpd_issnan) +Dec_BoolFunc(mpd_issigned) +Dec_BoolFunc(mpd_isspecial) +Dec_BoolFunc(mpd_iszero) + +Dec_BoolFuncVA(mpd_isnormal) +Dec_BoolFuncVA(mpd_issubnormal) + +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_FromMpdSsize(retval); +} + +/* Apply either the current context or the context provided as an optional + argument. */ +static PyObject * +PyDec_Apply(PyObject *dec, PyObject *args) +{ + PyObject *context; + + CURRENT_CONTEXT(context); + if (!PyArg_ParseTuple(args, "|O", &context)) { + return NULL; + } + CONTEXT_CHECK_VA(context); + + return dec_apply(dec, context); +} + +static PyObject * +dec_mpd_qcopy_abs(PyObject *self, PyObject *dummy UNUSED) +{ + PyObject *result; + PyObject *context; + uint32_t status = 0; + + CURRENT_CONTEXT(context); + if ((result = dec_alloc()) == NULL) { + return NULL; + } + + mpd_qcopy_abs(MPD(result), MPD(self), &status); + if (dec_addstatus(context, status)) { + Py_DECREF(result); + return NULL; + } + + return result; +} + +static PyObject * +dec_mpd_qcopy_negate(PyObject *self, PyObject *dummy UNUSED) +{ + PyObject *result; + PyObject *context; + uint32_t status = 0; + + CURRENT_CONTEXT(context); + if ((result = dec_alloc()) == NULL) { + return NULL; + } + + mpd_qcopy_negate(MPD(result), MPD(self), &status); + if (dec_addstatus(context, status)) { + Py_DECREF(result); + return NULL; + } + + return result; +} + +Dec_UnaryFuncVA(mpd_qinvert) +Dec_UnaryFuncVA(mpd_qlogb) + +Dec_BinaryFuncVA_NO_CTX(mpd_compare_total) +Dec_BinaryFuncVA_NO_CTX(mpd_compare_total_mag) + +static PyObject * +dec_mpd_qcopy_sign(PyObject *v, PyObject *w) +{ + PyObject *result; + PyObject *a, *b; + PyObject *context; + uint32_t status = 0; + + CURRENT_CONTEXT(context); + CONVERT_BINOP_RAISE(&a, &b, v, w, context); + + if ((result = dec_alloc()) == 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; +} + +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_class(PyObject *self, PyObject *args) +{ + PyObject *context; + const char *cp; + + CURRENT_CONTEXT(context); + if (!PyArg_ParseTuple(args, "|O", &context)) { + return NULL; + } + CONTEXT_CHECK_VA(context); + + cp = mpd_class(MPD(self), CTX(context)); + return Py_BuildValue("s", cp); +} + +static PyObject * +dec_canonical(PyObject *self, PyObject *dummy UNUSED) +{ + Py_INCREF(self); + return self; +} + +static PyObject * +dec_copy(PyObject *self, PyObject *dummy UNUSED) +{ + Py_INCREF(self); + return self; +} + +static PyObject * +dec_mpd_qdivmod(PyObject *v, PyObject *args) +{ + PyObject *w, *context; + PyObject *a, *b; + PyObject *q, *r; + uint32_t status = 0; + PyObject *ret; + + CURRENT_CONTEXT(context); + if (!PyArg_ParseTuple(args, "O|O", &w, &context)) { + return NULL; + } + CONTEXT_CHECK_VA(context); + CONVERT_BINOP_RAISE(&a, &b, v, w, context); + + if ((q = dec_alloc()) == NULL) { + Py_DECREF(a); + Py_DECREF(b); + return NULL; + } + if ((r = dec_alloc()) == 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 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); + + if ((result = dec_alloc()) == 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; +} + +static PyObject * +dec_mpd_radix(PyObject *self UNUSED, PyObject *dummy UNUSED) +{ + PyObject *result; + + if ((result = dec_alloc()) == NULL) { + return NULL; + } + + _dec_settriple(result, MPD_POS, 10, 0); + return result; +} + +static PyObject * +dec_mpd_same_quantum(PyObject *v, PyObject *args) +{ + PyObject *result; + PyObject *w, *context; + PyObject *a, *b; + + CURRENT_CONTEXT(context); + if (!PyArg_ParseTuple(args, "O|O", &w, &context)) { + return NULL; + } + CONTEXT_CHECK_VA(context); + CONVERT_BINOP_RAISE(&a, &b, v, w, context); + + result = mpd_same_quantum(MPD(a), MPD(b)) ? + Dec_INCREF_TRUE : Dec_INCREF_FALSE; + Py_DECREF(a); + Py_DECREF(b); + + return result; +} + +static PyObject * +dec_mpd_sign(PyObject *self, PyObject *dummy UNUSED) +{ + return Py_BuildValue("i", mpd_arith_sign(MPD(self))); +} + +static PyObject * +dec_mpd_to_sci(PyObject *self, PyObject *args) +{ + PyObject *result; + PyObject *context; + char *s; + + CURRENT_CONTEXT(context); + if (!PyArg_ParseTuple(args, "|O", &context)) { + return NULL; + } + CONTEXT_CHECK_VA(context); + + s = mpd_to_sci(MPD(self), CtxCaps(context)); + if (s == NULL) { + PyErr_NoMemory(); + return NULL; + } + + result = PyUnicode_FromString(s); + mpd_free(s); + + return result; +} + +static PyObject * +dec_mpd_to_eng(PyObject *self, PyObject *args) +{ + PyObject *result; + PyObject *context; + char *s; + + CURRENT_CONTEXT(context); + if (!PyArg_ParseTuple(args, "|O", &context)) { + return NULL; + } + CONTEXT_CHECK_VA(context); + + s = mpd_to_eng(MPD(self), CtxCaps(context)); + if (s == NULL) { + PyErr_NoMemory(); + return NULL; + } + + result = PyUnicode_FromString(s); + mpd_free(s); + + return result; +} + +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) ? Dec_INCREF_TRUE : Dec_INCREF_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); +} + +/* 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); + if ((exp_hash = mpd_qnew()) == NULL) { + goto malloc_error; + } + if ((tmp = mpd_qnew()) == 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; /* GCOV_NOT_REACHED */ + if (dec_addstatus(context, status)) { /* GCOV_NOT_REACHED */ + result = -1; /* GCOV_NOT_REACHED */ + goto finish; /* GCOV_NOT_REACHED */ + } /* GCOV_NOT_REACHED */ + } + + +finish: + if (exp_hash) mpd_del(exp_hash); + if (tmp) mpd_del(tmp); + return result; + +malloc_error: + PyErr_NoMemory(); + result = -1; + goto finish; +} + +static PyObject * +dec_reduce(PyObject *self, PyObject *dummy UNUSED) +{ + PyObject *result, *str; + + if ((str = dec_str(self)) == NULL) { + return NULL; + } + + result = Py_BuildValue("O(O)", Py_TYPE(self), str); + Py_DECREF(str); + + return result; +} + +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; + + if ((result = dec_alloc()) == NULL) { + return NULL; + } + + _dec_settriple(result, MPD_POS, 0, 0); + return result; +} + +static PyObject * +dec_conjugate(PyObject *self, PyObject *dummy UNUSED) +{ + Py_INCREF(self); + return self; +} + +static PyObject * +dec_complex(PyObject *self, PyObject *dummy UNUSED) +{ + PyObject *f; + double x; + + if ((f = PyDec_AsFloat(self)) == NULL) { + return NULL; + } + + x = PyFloat_AsDouble(f); + Py_DECREF(f); + if (PyErr_Occurred()) { + return NULL; + } + + return PyComplex_FromDoubles(x, 0); +} + +static PyObject * +dec_trunc(PyObject *dec, PyObject *dummy UNUSED) +{ + PyObject *context; + + CURRENT_CONTEXT(context); + return dec_as_long(dec, context, MPD_ROUND_DOWN); +} + + +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 */ + { "abs", dec_mpd_qabs, METH_VARARGS, doc_abs }, + { "exp", dec_mpd_qexp, METH_VARARGS, doc_exp }, + { "invroot", dec_mpd_qinvroot, METH_VARARGS, doc_invroot }, + { "ln", dec_mpd_qln, METH_VARARGS, doc_ln }, + { "log10", dec_mpd_qlog10, METH_VARARGS, doc_log10 }, + { "minus", dec_mpd_qminus, METH_VARARGS, doc_minus }, + { "next_minus", dec_mpd_qnext_minus, METH_VARARGS, doc_next_minus }, + { "next_plus", dec_mpd_qnext_plus, METH_VARARGS, doc_next_plus }, + { "normalize", dec_mpd_qreduce, METH_VARARGS, doc_normalize }, /* alias for reduce */ + { "plus", dec_mpd_qplus, METH_VARARGS, doc_plus }, + { "reduce", dec_mpd_qreduce, METH_VARARGS, doc_reduce }, + { "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", dec_mpd_qsqrt, METH_VARARGS, doc_sqrt }, + + /* Binary arithmetic functions */ + { "add", dec_mpd_qadd, METH_VARARGS, doc_add }, + { "compare", dec_mpd_qcompare, METH_VARARGS, doc_compare }, + { "compare_signal", dec_mpd_qcompare_signal, METH_VARARGS, doc_compare_signal }, + { "div", dec_mpd_qdiv, METH_VARARGS, doc_div }, /* alias for divide */ + { "divide", dec_mpd_qdiv, METH_VARARGS, doc_divide }, + { "divide_int", dec_mpd_qdivint, METH_VARARGS, doc_divide_int }, + { "divint", dec_mpd_qdivint, METH_VARARGS, doc_divint }, /* alias for divide_int */ + { "divmod", dec_mpd_qdivmod, METH_VARARGS, doc_divmod }, + { "max", dec_mpd_qmax, METH_VARARGS, doc_max }, + { "max_mag", dec_mpd_qmax_mag, METH_VARARGS, doc_max_mag }, + { "min", dec_mpd_qmin, METH_VARARGS, doc_min }, + { "min_mag", dec_mpd_qmin_mag, METH_VARARGS, doc_min_mag }, + { "mul", dec_mpd_qmul, METH_VARARGS, doc_mul }, /* alias for multiply */ + { "multiply", dec_mpd_qmul, METH_VARARGS, doc_multiply }, + { "next_toward", dec_mpd_qnext_toward, METH_VARARGS, doc_next_toward }, + { "pow", dec_mpd_qpow, METH_VARARGS, doc_pow }, /* alias for power */ + { "power", dec_mpd_qpow, METH_VARARGS, doc_power }, + { "quantize", (PyCFunction)dec_mpd_qquantize, METH_VARARGS|METH_KEYWORDS, doc_quantize }, + { "rem", dec_mpd_qrem, METH_VARARGS, doc_rem }, /* alias for remainder */ + { "remainder", dec_mpd_qrem, METH_VARARGS, doc_remainder }, + { "remainder_near", dec_mpd_qrem_near, METH_VARARGS, doc_remainder_near }, + { "sub", dec_mpd_qsub, METH_VARARGS, doc_sub }, /* alias for subtract */ + { "subtract", dec_mpd_qsub, METH_VARARGS, doc_subtract }, + + /* Ternary arithmetic functions */ + { "fma", dec_mpd_qfma, METH_VARARGS, doc_fma }, + { "powmod", dec_mpd_qpowmod, METH_VARARGS, doc_powmod }, + + /* 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_integer", dec_mpd_isinteger, METH_NOARGS, doc_is_integer }, + { "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_special", dec_mpd_isspecial, METH_NOARGS, doc_is_special }, + { "is_zero", dec_mpd_iszero, METH_NOARGS, doc_is_zero }, + + /* Boolean functions, optional context arg */ + { "is_normal", dec_mpd_isnormal, METH_VARARGS, doc_is_normal }, + { "is_subnormal", dec_mpd_issubnormal, METH_VARARGS, 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 }, + { "copy_abs", dec_mpd_qcopy_abs, METH_NOARGS, doc_copy_abs }, + { "copy_negate", dec_mpd_qcopy_negate, METH_NOARGS, doc_copy_negate }, + { "radix", dec_mpd_radix, METH_NOARGS, doc_radix }, + { "sign", dec_mpd_sign, METH_NOARGS, doc_sign }, + + /* Unary functions, optional context arg */ + { "apply", PyDec_Apply, METH_VARARGS, doc_apply }, + { "logb", dec_mpd_qlogb, METH_VARARGS, doc_logb }, + { "logical_invert", dec_mpd_qinvert, METH_VARARGS, doc_logical_invert }, + { "number_class", dec_mpd_class, METH_VARARGS, doc_number_class }, + { "to_sci", dec_mpd_to_sci, METH_VARARGS, doc_to_sci }, /* alias for to_sci_string */ + { "to_sci_string", dec_mpd_to_sci, METH_VARARGS, doc_to_sci_string }, + { "to_eng", dec_mpd_to_eng, METH_VARARGS, doc_to_eng }, /* alias for to_eng_string */ + { "to_eng_string", dec_mpd_to_eng, METH_VARARGS, doc_to_eng_string }, + + /* Binary functions, optional context arg */ + { "compare_total", dec_mpd_compare_total, METH_VARARGS, doc_compare_total }, + { "compare_total_mag", dec_mpd_compare_total_mag, METH_VARARGS, doc_compare_total_mag }, + { "copy_sign", dec_mpd_qcopy_sign, METH_O, doc_copy_sign }, + { "logical_and", dec_mpd_qand, METH_VARARGS, doc_logical_and }, + { "logical_or", dec_mpd_qor, METH_VARARGS, doc_logical_or }, + { "logical_xor", dec_mpd_qxor, METH_VARARGS, doc_logical_xor }, + { "rotate", dec_mpd_qrotate, METH_VARARGS, doc_rotate }, + { "same_quantum", dec_mpd_same_quantum, METH_VARARGS, doc_same_quantum }, + { "scaleb", dec_mpd_qscaleb, METH_VARARGS, doc_scaleb }, + { "shift", dec_mpd_qshift, METH_VARARGS, doc_shift }, + + /* Miscellaneous */ + { "from_float", dec_from_float, METH_O|METH_CLASS, doc_from_float }, + { "as_tuple", PyDec_AsTuple, METH_NOARGS, doc_as_tuple }, + + /* Generic stuff */ + { "__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 }, + { "__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)) \ + ? Dec_INCREF_TRUE : Dec_INCREF_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)) ? Dec_INCREF_TRUE : Dec_INCREF_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; \ +} + +static PyObject * +ctx_copy_decimal(PyObject *context, PyObject *v) +{ + PyObject *result; + + CONVERT_OP_RAISE(&result, v, context); + return result; +} + +/* Arithmetic operations */ +DecCtx_UnaryFunc(mpd_qabs) +DecCtx_UnaryFunc(mpd_qexp) +DecCtx_UnaryFunc(mpd_qinvroot) +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) + +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_qpow) +DecCtx_BinaryFunc(mpd_qquantize) +DecCtx_BinaryFunc(mpd_qrem) +DecCtx_BinaryFunc(mpd_qrem_near) +DecCtx_BinaryFunc(mpd_qsub) + +DecCtx_TernaryFunc(mpd_qfma) +DecCtx_TernaryFunc(mpd_qpowmod) + +/* Miscellaneous */ +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_mpd_qcopy_abs(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; + } + + mpd_qcopy_abs(MPD(result), MPD(a), &status); + Py_DECREF(a); + if (dec_addstatus(context, status)) { + Py_DECREF(result); + return NULL; + } + + return result; +} + +/* apply() as a context method. */ +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_mpd_qcopy_negate(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; + } + + 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_qinvert) +DecCtx_UnaryFunc(mpd_qlogb) + +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); + + if ((result = dec_alloc()) == 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_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)) ? Dec_INCREF_TRUE : Dec_INCREF_FALSE; +} + +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_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 Py_BuildValue("s", cp); +} + +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); + + if ((q = dec_alloc()) == NULL) { + Py_DECREF(a); + Py_DECREF(b); + return NULL; + } + if ((r = dec_alloc()) == 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 PyObject * +ctx_mpd_to_sci(PyObject *context, PyObject *v) +{ + PyObject *result; + PyObject *a; + char *s; + + CONVERT_OP_RAISE(&a, v, context); + + s = mpd_to_sci(MPD(a), CtxCaps(context)); + Py_DECREF(a); + if (s == NULL) { + PyErr_NoMemory(); + return NULL; + } + + result = PyUnicode_FromString(s); + mpd_free(s); + + return result; +} + +static PyObject * +ctx_mpd_to_eng(PyObject *context, PyObject *v) +{ + PyObject *result; + PyObject *a; + char *s; + + CONVERT_OP_RAISE(&a, v, context); + + s = mpd_to_eng(MPD(a), CtxCaps(context)); + Py_DECREF(a); + if (s == NULL) { + PyErr_NoMemory(); + return NULL; + } + + result = PyUnicode_FromString(s); + mpd_free(s); + + return result; +} + +static PyObject * +ctx_mpd_radix(PyObject *context UNUSED, PyObject *dummy UNUSED) +{ + return dec_mpd_radix(context, dummy); +} + +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)) ? + Dec_INCREF_TRUE : Dec_INCREF_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 }, + { "invroot", ctx_mpd_qinvroot, METH_O, doc_ctx_invroot }, + { "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 }, /* alias for reduce */ + { "plus", ctx_mpd_qplus, METH_O, doc_ctx_plus }, + { "reduce", ctx_mpd_qreduce, METH_O, doc_ctx_reduce }, + { "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 }, + { "div", ctx_mpd_qdiv, METH_VARARGS, doc_ctx_div }, /* alias for divide */ + { "divide", ctx_mpd_qdiv, METH_VARARGS, doc_ctx_divide }, + { "divide_int", ctx_mpd_qdivint, METH_VARARGS, doc_ctx_divide_int }, + { "divint", ctx_mpd_qdivint, METH_VARARGS, doc_ctx_divint }, /* alias for 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 }, + { "mul", ctx_mpd_qmul, METH_VARARGS, doc_ctx_mul }, /* alias for multiply */ + { "multiply", ctx_mpd_qmul, METH_VARARGS, doc_ctx_multiply }, + { "next_toward", ctx_mpd_qnext_toward, METH_VARARGS, doc_ctx_next_toward }, + { "pow", ctx_mpd_qpow, METH_VARARGS, doc_ctx_pow }, /* alias for power */ + { "power", ctx_mpd_qpow, METH_VARARGS, doc_ctx_power }, + { "quantize", ctx_mpd_qquantize, METH_VARARGS, doc_ctx_quantize }, + { "rem", ctx_mpd_qrem, METH_VARARGS, doc_ctx_rem }, /* alias for remainder */ + { "remainder", ctx_mpd_qrem, METH_VARARGS, doc_ctx_remainder }, + { "remainder_near", ctx_mpd_qrem_near, METH_VARARGS, doc_ctx_remainder_near }, + { "sub", ctx_mpd_qsub, METH_VARARGS, doc_ctx_sub }, /* alias for subtract */ + { "subtract", ctx_mpd_qsub, METH_VARARGS, doc_ctx_subtract }, + + /* Ternary arithmetic functions */ + { "fma", ctx_mpd_qfma, METH_VARARGS, doc_ctx_fma }, + { "powmod", ctx_mpd_qpowmod, METH_VARARGS, doc_ctx_powmod }, + + /* 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 */ + { "apply", PyDecContext_Apply, METH_O, doc_ctx_apply }, + { "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", ctx_mpd_to_sci, METH_O, doc_ctx_to_sci }, /* alias for to_sci_string */ + { "to_sci_string", ctx_mpd_to_sci, METH_O, doc_ctx_to_sci_string }, + { "to_eng", ctx_mpd_to_eng, METH_O, doc_ctx_to_eng }, /* alias for to_eng_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 */ + { "setflags", PyDec_SetStatusFromList, METH_O, doc_ctx_setflags }, + { "settraps", PyDec_SetTrapsFromList, METH_O, doc_ctx_settraps }, + { "clear_flags", context_clear_flags, METH_NOARGS, doc_ctx_clear_flags }, + { "clear_traps", context_clear_traps, METH_NOARGS, doc_ctx_clear_traps }, + + /* Unsafe set functions with no 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 }, + + /* 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, /* 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 */ + 0 /* 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}, + { "IEEEContext", (PyCFunction)ieee_context, METH_O, doc_ieee_context}, + { 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 */ + {"DECIMAL32", MPD_DECIMAL32}, + {"DECIMAL64", MPD_DECIMAL64}, + {"DECIMAL128", MPD_DECIMAL128}, + {"IEEE_CONTEXT_MAX_BITS", MPD_IEEE_CONTEXT_MAX_BITS}, + {"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}, + {"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}, + {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 *obj = NULL; + PyObject *ret = NULL; + PyObject *str1 = NULL; + PyObject *str2 = NULL; + PyObject *str3 = NULL; + DecCondMap *cm; + struct ssize_constmap *ssize_cm; + struct int_constmap *int_cm; + + + /* 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; + PyDecSignalDict_Type.tp_base = &PyDict_Type; + PyDecContextManager_Type.tp_base = &PyBaseObject_Type; + + CHECK_INT(PyType_Ready(&PyDec_Type)); + CHECK_INT(PyType_Ready(&PyDecContext_Type)); + CHECK_INT(PyType_Ready(&PyDecSignalDict_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); + + + /* Imports from other modules */ + ASSIGN_PTR(_numbers, PyImport_ImportModule("numbers")); + ASSIGN_PTR(_Number, PyObject_GetAttrString(_numbers, "Number")); + ASSIGN_PTR(_Rational, PyObject_GetAttrString(_numbers, "Rational")); + ASSIGN_PTR(str1, Py_BuildValue("s", "register")); + ASSIGN_PTR(obj, Py_BuildValue("O", &PyDec_Type)); + ASSIGN_PTR(ret, PyObject_CallMethodObjArgs(_Number, str1, obj, NULL)); + Py_CLEAR(str1); + Py_CLEAR(obj); + Py_CLEAR(ret); + + ASSIGN_PTR(_collections, PyImport_ImportModule("collections")); + ASSIGN_PTR(str1, Py_BuildValue("s", "namedtuple")); + ASSIGN_PTR(str2, Py_BuildValue("s", "DecimalTuple")); + ASSIGN_PTR(str3, Py_BuildValue("s", "sign digits exponent")); + ASSIGN_PTR(DecimalTuple, PyObject_CallMethodObjArgs( + _collections, str1, str2, str3, NULL)); + Py_CLEAR(str1); + Py_CLEAR(str2); + Py_CLEAR(str3); + + + /* 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)); + + + /* Create top level exception */ + ASSIGN_PTR(DecimalException, PyErr_NewException( + "decimal.DecimalException", + PyExc_ArithmeticError, NULL)); + Py_INCREF(DecimalException); + CHECK_INT(PyModule_AddObject(m, "DecimalException", DecimalException)); + + /* Add exceptions that correspond to IEEE signals */ + for (cm = signal_map; cm->name != NULL; cm++) { + cm->dec_cond = PyErr_NewException((char *)cm->fqname, + DecimalException, NULL); + Py_INCREF(cm->dec_cond); + CHECK_INT(PyModule_AddObject(m, cm->name, cm->dec_cond)); + } + + /* + * Unfortunately, InvalidOperation is a signal that comprises + * several conditions, including InvalidOperation! Naming the + * signal IEEEInvalidOperation would prevent the confusion. + */ + cond_map[0].dec_cond = signal_map[0].dec_cond; + + /* Add remaining exceptions, inherit from InvalidOperation */ + for (cm = cond_map+1; cm->name != NULL; cm++) { + cm->dec_cond = PyErr_NewException((char *)cm->fqname, + signal_map[0].dec_cond, NULL); + Py_INCREF(cm->dec_cond); + CHECK_INT(PyModule_AddObject(m, cm->name, cm->dec_cond)); + } + + + /* 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)); + CHECK_INT(PyModule_AddIntConstant(m, "HAVE_THREADS", 0)); +#else + ASSIGN_PTR(tls_context_key, Py_BuildValue("s", "___DECIMAL_CTX__")); + CHECK_INT(PyModule_AddIntConstant(m, "HAVE_THREADS", 1)); +#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, Py_BuildValue(CONV_mpd_ssize_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)); + } + + + return m; + + +error: + 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(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 */ +} + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/basearith.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/basearith.c Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,582 @@ +/* + * Copyright (c) 2008-2010 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 +#include +#include +#include +#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. + */ +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 = 0; + mpd_size_t i; + + /* add v to u */ + 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_calloc(nplusm+1, sizeof *u)) == NULL) { + return -1; + } + } + if (n >= MPD_MINALLOC_MAX) { + if ((v = mpd_calloc(n+1, sizeof *v)) == NULL) { + mpd_free(u); + return -1; + } + } + + _mpd_shortmul(u, uconst, nplusm, d); + _mpd_shortmul(v, vconst, n, d); + + /* D2: loop */ + rhat = 0; + 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; +} + +/* Leftshift of src by shift digits; src may equal dest. */ +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) { + dest[n--] = h; + } + for (; m != MPD_SIZE_MAX; m--,n--) { + _mpd_divmod_pow10(&h, &l, src[m], MPD_RDIGITS-r); + dest[n] = ph * lprev + h; + lprev = l; + } + dest[q] = ph * lprev; + } + else { + while (--m != MPD_SIZE_MAX) { + dest[m+q] = src[m]; + } + } + + mpd_uint_zero(dest, q); +} + +/* Rightshift of src by shift digits; src may equal dest. */ +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); + } + h = hprev; + for (j=0,i=q+1; i 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. + */ +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 = 0; + mpd_size_t i; + + /* add v to u */ + 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; +} + + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/basearith.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/basearith.h Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2008-2010 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 +#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 + */ +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 = (lo & (1ULL<<63)) ? MPD_UINT_MAX : 0; + n_adj = lo + (n1_neg & MPD_RADIX); + + _mpd_mul_words(&h, &l, mprime_rdx, hi-n1_neg); + l = l + n_adj; + if (l < n_adj) h++; + t = h + hi; /* q1 */ + + t = MPD_UINT_MAX - t; + + _mpd_mul_words(&h, &l, t, MPD_RADIX); + l = l + lo; + if (l < lo) h++; + h += hi; + h -= MPD_RADIX; + + *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 b 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 1 or 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 words from data[len-1] to data[0] are MPD_RADIX-1 (all nines). + * Assumes that 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 */ + + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/bench.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/bench.c Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2008-2010 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 +#include + + +/* + * Example from: http://en.wikipedia.org/wiki/Mandelbrot_set + * + * Escape time algorithm for drawing the set: + * + * Point x0, y0 is deemed to be in the Mandelbrot set if the return + * value is maxiter. Lower return values indicate how quickly points + * escaped and can be used for coloring. + */ +int +color_point(mpd_t *x0, mpd_t *y0, int maxiter, mpd_context_t *ctx) +{ + mpd_t *x, *y, *sq_x, *sq_y; + mpd_t *two, *four, *c; + int i; + + x = mpd_new(ctx); + y = mpd_new(ctx); + mpd_set_u32(x, 0, ctx); + mpd_set_u32(y, 0, ctx); + + sq_x = mpd_new(ctx); + sq_y = mpd_new(ctx); + mpd_set_u32(sq_x, 0, ctx); + mpd_set_u32(sq_y, 0, ctx); + + two = mpd_new(ctx); + four = mpd_new(ctx); + mpd_set_u32(two, 2, ctx); + mpd_set_u32(four, 4, ctx); + + c = mpd_new(ctx); + mpd_set_u32(c, 0, ctx); + + for (i = 0; i < maxiter && mpd_cmp(c, four, ctx) <= 0; i++) { + + mpd_mul(y, x, y, ctx); + mpd_mul(y, y, two, ctx); + mpd_add(y, y, y0, ctx); + + mpd_sub(x, sq_x, sq_y, ctx); + mpd_add(x, x, x0, ctx); + + mpd_mul(sq_x, x, x, ctx); + mpd_mul(sq_y, y, y, ctx); + mpd_add(c, sq_x, sq_y, ctx); + } + + mpd_del(x); + mpd_del(y); + mpd_del(sq_x); + mpd_del(sq_y); + mpd_del(two); + mpd_del(four); + mpd_del(c); + + return i; +} + +int +main(int argc, char **argv) +{ + mpd_context_t ctx; + mpd_t *x0, *y0; + mpd_t *sqrt_2, *xstep, *ystep; + uint32_t prec = 19; + + int iter = 1000; + int points[40][80]; + int i, j; + clock_t start_clock, end_clock; + + + if (argc != 3) { + fprintf(stderr, "usage: ./bench prec iter\n"); + exit(1); + } + prec = strtoul(argv[1], NULL, 10); + iter = strtol(argv[2], NULL, 10); + + mpd_init(&ctx, prec); + /* no more MPD_MINALLOC changes after here */ + + sqrt_2 = mpd_new(&ctx); + xstep = mpd_new(&ctx); + ystep = mpd_new(&ctx); + x0 = mpd_new(&ctx); + y0 = mpd_new(&ctx); + + mpd_set_u32(sqrt_2, 2, &ctx); + mpd_sqrt(sqrt_2, sqrt_2, &ctx); + mpd_div_u32(xstep, sqrt_2, 40, &ctx); + mpd_div_u32(ystep, sqrt_2, 20, &ctx); + + start_clock = clock(); + mpd_copy(y0, sqrt_2, &ctx); + for (i = 0; i < 40; i++) { + mpd_copy(x0, sqrt_2, &ctx); + mpd_set_negative(x0); + for (j = 0; j < 80; j++) { + points[i][j] = color_point(x0, y0, iter, &ctx); + mpd_add(x0, x0, xstep, &ctx); + } + mpd_sub(y0, y0, ystep, &ctx); + } + end_clock = clock(); + +#ifdef BENCH_VERBOSE + for (i = 0; i < 40; i++) { + for (j = 0; j < 80; j++) { + if (points[i][j] == iter) { + putchar('*'); + } + else if (points[i][j] >= 10) { + putchar('+'); + } + else if (points[i][j] >= 5) { + putchar('.'); + } + else { + putchar(' '); + } + } + putchar('\n'); + } + putchar('\n'); +#endif + + printf("time: %f\n\n", (double)(end_clock-start_clock)/(double)CLOCKS_PER_SEC); + + mpd_del(x0); + mpd_del(y0); + mpd_del(sqrt_2); + mpd_del(xstep); + mpd_del(ystep); + + return 0; +} + + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/bits.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/bits.h Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2008-2010 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 + + +/* 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) +/* + * Returns the most significant bit position of n from 0 to 32 (64). + * Caller has to make sure that n is not 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; +} + +/* + * Returns the least significant bit position of n from 0 to 32 (64). + * Caller has to make sure that n is not 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. + * Caller has to make sure that a is not 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. + * Caller has to make sure that a is not 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 +/* + * Bit scan reverse. + * Caller has to make sure that a is not 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. + * Caller has to make sure that a is not 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 */ + + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/config.h.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/config.h.in Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,100 @@ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* Define if we can use x64 gcc inline assembler. */ +#undef HAVE_GCC_ASM_FOR_X64 + +/* Define if we can use x87 gcc inline assembler. */ +#undef HAVE_GCC_ASM_FOR_X87 + +/* Define if glibc has incorrect _FORTIFY_SOURCE wrappers for memmove and + bcopy. */ +#undef HAVE_GLIBC_MEMMOVE_BUG + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define if gcc has the ipa-pure-const bug. */ +#undef HAVE_IPA_PURE_CONST_BUG + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define if your compiler provides __uint128_t. */ +#undef HAVE_UINT128_T + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* The size of `size_t', as computed by sizeof. */ +#undef SIZEOF_SIZE_T + +/* The size of `__uint128_t', as computed by sizeof. */ +#undef SIZEOF___UINT128_T + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define for Solaris 2.5.1 so the uint32_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT32_T + +/* Define for Solaris 2.5.1 so the uint64_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT64_T + +/* Define to the type of a signed integer type of width exactly 32 bits if + such a type exists and the standard includes do not define it. */ +#undef int32_t + +/* Define to the type of a signed integer type of width exactly 64 bits if + such a type exists and the standard includes do not define it. */ +#undef int64_t + +/* Define to `unsigned int' if does not define. */ +#undef size_t + +/* Define to the type of an unsigned integer type of width exactly 32 bits if + such a type exists and the standard includes do not define it. */ +#undef uint32_t + +/* Define to the type of an unsigned integer type of width exactly 64 bits if + such a type exists and the standard includes do not define it. */ +#undef uint64_t diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/configure --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/configure Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,5500 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.68 for mpdecimal @RELEASE_VERSION@. +# +# Report bugs to . +# +# +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software +# Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + # We cannot yet assume a decent shell, so we have to provide a + # neutralization value for shells without unset; and this also + # works around shells that cannot unset nonexistent variables. + # Preserve -v and -x to the replacement shell. + BASH_ENV=/dev/null + ENV=/dev/null + (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV + export CONFIG_SHELL + case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; + esac + exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"} +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org and +$0: mpdecimal-bugs@bytereef.org about your system, +$0: including any error possibly output before this +$0: message. Then install a modern shell, or manually run +$0: the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='mpdecimal' +PACKAGE_TARNAME='mpdecimal' +PACKAGE_VERSION='@RELEASE_VERSION@' +PACKAGE_STRING='mpdecimal @RELEASE_VERSION@' +PACKAGE_BUGREPORT='mpdecimal-bugs@bytereef.org' +PACKAGE_URL='http://www.bytereef.org/mpdecimal/index.html' + +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='LTLIBOBJS +LIBOBJS +CONFIGURE_LDFLAGS +CONFIGURE_CFLAGS +MPD_DPREC +MPD_PREC +MPD_PUSE +MPD_PGEN +MPD_LDCOV +MPD_CCOV +MPD_OPT +MPD_CONFIG +MPD_WARN +MPD_HEADER +AR +LD +GMPPATH +GMPDEPS +EGREP +GREP +CPP +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +LIBSHARED +MACHINE +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +' + ac_precious_vars='build_alias +host_alias +target_alias +MACHINE +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used" >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures mpdecimal @RELEASE_VERSION@ to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/mpdecimal] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of mpdecimal @RELEASE_VERSION@:";; + esac + cat <<\_ACEOF + +Some influential environment variables: + MACHINE force configuration: x64, uint128, ansi64, ppro, ansi32, + ansi-legacy + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +mpdecimal home page: . +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +mpdecimal configure @RELEASE_VERSION@ +generated by GNU Autoconf 2.68 + +Copyright (C) 2010 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} +( $as_echo "## ------------------------------------------ ## +## Report this to mpdecimal-bugs@bytereef.org ## +## ------------------------------------------ ##" + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_mongrel + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_c_check_type LINENO TYPE VAR INCLUDES +# ------------------------------------------- +# Tests whether TYPE exists after having included INCLUDES, setting cache +# variable VAR accordingly. +ac_fn_c_check_type () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof ($2)) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof (($2))) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + eval "$3=yes" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_type + +# ac_fn_c_find_intX_t LINENO BITS VAR +# ----------------------------------- +# Finds a signed integer type with width BITS, setting cache variable VAR +# accordingly. +ac_fn_c_find_intX_t () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for int$2_t" >&5 +$as_echo_n "checking for int$2_t... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + # Order is important - never check a type that is potentially smaller + # than half of the expected target width. + for ac_type in int$2_t 'int' 'long int' \ + 'long long int' 'short int' 'signed char'; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default + enum { N = $2 / 2 - 1 }; +int +main () +{ +static int test_array [1 - 2 * !(0 < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1))]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default + enum { N = $2 / 2 - 1 }; +int +main () +{ +static int test_array [1 - 2 * !(($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1) + < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 2))]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + case $ac_type in #( + int$2_t) : + eval "$3=yes" ;; #( + *) : + eval "$3=\$ac_type" ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if eval test \"x\$"$3"\" = x"no"; then : + +else + break +fi + done +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_find_intX_t + +# ac_fn_c_find_uintX_t LINENO BITS VAR +# ------------------------------------ +# Finds an unsigned integer type with width BITS, setting cache variable VAR +# accordingly. +ac_fn_c_find_uintX_t () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5 +$as_echo_n "checking for uint$2_t... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + # Order is important - never check a type that is potentially smaller + # than half of the expected target width. + for ac_type in uint$2_t 'unsigned int' 'unsigned long int' \ + 'unsigned long long int' 'unsigned short int' 'unsigned char'; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + case $ac_type in #( + uint$2_t) : + eval "$3=yes" ;; #( + *) : + eval "$3=\$ac_type" ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if eval test \"x\$"$3"\" = x"no"; then : + +else + break +fi + done +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_find_uintX_t + +# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES +# -------------------------------------------- +# Tries to find the compile-time value of EXPR in a program that includes +# INCLUDES, setting VAR accordingly. Returns whether the value could be +# computed +ac_fn_c_compute_int () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) >= 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_lo=0 ac_mid=0 + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=$ac_mid; break +else + as_fn_arith $ac_mid + 1 && ac_lo=$as_val + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) < 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=-1 ac_mid=-1 + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) >= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_lo=$ac_mid; break +else + as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + ac_lo= ac_hi= +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=$ac_mid +else + as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in #(( +?*) eval "$3=\$ac_lo"; ac_retval=0 ;; +'') ac_retval=1 ;; +esac + else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +static long int longval () { return $2; } +static unsigned long int ulongval () { return $2; } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + return 1; + if (($2) < 0) + { + long int i = longval (); + if (i != ($2)) + return 1; + fprintf (f, "%ld", i); + } + else + { + unsigned long int i = ulongval (); + if (i != ($2)) + return 1; + fprintf (f, "%lu", i); + } + /* Do not output a trailing newline, as this causes \r\n confusion + on some platforms. */ + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + echo >>conftest.val; read $3 &5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by mpdecimal $as_me @RELEASE_VERSION@, which was +generated by GNU Autoconf 2.68. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_config_headers="$ac_config_headers config.h" + +ac_config_files="$ac_config_files Makefile tests/Makefile" + + + +LIBSHARED=libmpdec.so.$PACKAGE_VERSION + + +# Language and compiler: +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +saved_cflags=$CFLAGS +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +CFLAGS=$saved_cflags + +# System and machine type: +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking system as reported by uname -s" >&5 +$as_echo_n "checking system as reported by uname -s... " >&6; } +ac_sys_system=`uname -s` +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_sys_system" >&5 +$as_echo "$ac_sys_system" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking machine type as reported by uname -m" >&5 +$as_echo_n "checking machine type as reported by uname -m... " >&6; } +ac_sys_machine=`uname -m` +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_sys_machine" >&5 +$as_echo "$ac_sys_machine" >&6; } + +# Checks for header files: + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_header in inttypes.h stdint.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +# Type availability checks: +ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" +if test "x$ac_cv_type_size_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned int +_ACEOF + +fi + +ac_fn_c_find_intX_t "$LINENO" "32" "ac_cv_c_int32_t" +case $ac_cv_c_int32_t in #( + no|yes) ;; #( + *) + +cat >>confdefs.h <<_ACEOF +#define int32_t $ac_cv_c_int32_t +_ACEOF +;; +esac + +ac_fn_c_find_intX_t "$LINENO" "64" "ac_cv_c_int64_t" +case $ac_cv_c_int64_t in #( + no|yes) ;; #( + *) + +cat >>confdefs.h <<_ACEOF +#define int64_t $ac_cv_c_int64_t +_ACEOF +;; +esac + +ac_fn_c_find_uintX_t "$LINENO" "32" "ac_cv_c_uint32_t" +case $ac_cv_c_uint32_t in #( + no|yes) ;; #( + *) + +$as_echo "#define _UINT32_T 1" >>confdefs.h + + +cat >>confdefs.h <<_ACEOF +#define uint32_t $ac_cv_c_uint32_t +_ACEOF +;; + esac + +ac_fn_c_find_uintX_t "$LINENO" "64" "ac_cv_c_uint64_t" +case $ac_cv_c_uint64_t in #( + no|yes) ;; #( + *) + +$as_echo "#define _UINT64_T 1" >>confdefs.h + + +cat >>confdefs.h <<_ACEOF +#define uint64_t $ac_cv_c_uint64_t +_ACEOF +;; + esac + +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_UINT128_T 1" >>confdefs.h + +fi + + +# Sizes of various types: +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of size_t" >&5 +$as_echo_n "checking size of size_t... " >&6; } +if ${ac_cv_sizeof_size_t+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (size_t))" "ac_cv_sizeof_size_t" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_size_t" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (size_t) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_size_t=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_size_t" >&5 +$as_echo "$ac_cv_sizeof_size_t" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_SIZE_T $ac_cv_sizeof_size_t +_ACEOF + + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of __uint128_t" >&5 +$as_echo_n "checking size of __uint128_t... " >&6; } +if ${ac_cv_sizeof___uint128_t+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (__uint128_t))" "ac_cv_sizeof___uint128_t" "$ac_includes_default"; then : + +else + if test "$ac_cv_type___uint128_t" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (__uint128_t) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof___uint128_t=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof___uint128_t" >&5 +$as_echo "$ac_cv_sizeof___uint128_t" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF___UINT128_T $ac_cv_sizeof___uint128_t +_ACEOF + + + +# x64 with gcc asm: +{ $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 + +# x87 with gcc asm: +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for x87 gcc inline assembler" >&5 +$as_echo_n "checking for x87 gcc inline assembler... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + unsigned short cw; + __asm__ __volatile__ ("fnstcw %0" : "=m" (cw)); + __asm__ __volatile__ ("fldcw %0" : : "m" (cw)); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + have_gcc_asm_for_x87=yes +else + have_gcc_asm_for_x87=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_x87" >&5 +$as_echo "$have_gcc_asm_for_x87" >&6; } +if test "$have_gcc_asm_for_x87" = yes; then + +$as_echo "#define HAVE_GCC_ASM_FOR_X87 1" >>confdefs.h + +fi + +# gmp for extended tests: +GMPDEPS= +GMPPATH= +saved_ldflags="$LDFLAGS" +saved_cflags="$CFLAGS" +LDFLAGS=-lgmp +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for gmp" >&5 +$as_echo_n "checking for gmp... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include + +int +main () +{ + + mpz_t x; + mpz_init(x); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + have_gmp=yes +else + have_gmp=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test $have_gmp = no; then + LDFLAGS="-L/usr/local/lib -lgmp" + CFLAGS="-I/usr/local/include" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include + +int +main () +{ + + mpz_t x; + mpz_init(x); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + have_gmp=yes +else + have_gmp=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test $have_gmp = yes; then + GMPPATH=/usr/local + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_gmp" >&5 +$as_echo "$have_gmp" >&6; } +LDFLAGS="$saved_ldflags" +CFLAGS="$saved_cflags" + +if test $have_gmp = yes; then + GMPDEPS="mpd_mpz_add mpd_mpz_divmod mpd_mpz_mul mpd_mpz_sub" +fi + + + +# 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 +#include +#include +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 + +# suncc is dectected as cc: +case $ac_sys_system in +sun*|Sun*) + case $CC in + cc) + CC=suncc + ;; + esac +esac + +# Compiler dependent settings: +AR=ar +MPD_WARN="-Wall" +MPD_OPT="-O2 -fpic" +MPD_PGEN= +MPD_PUSE= +MPD_CCOV= +MPD_LDCOV= +case $CC in + *gcc*) + MPD_WARN="-Wall -W -Wno-unknown-pragmas" + MPD_OPT="-O2 -fpic -s" + MPD_PGEN="-fprofile-generate -fprofile-values" + MPD_PUSE="-fprofile-use -freorder-blocks" + MPD_CCOV="-O0 -g -fno-inline -fprofile-arcs -ftest-coverage -fpic" + MPD_LDCOV="-fprofile-arcs" + ;; + *icc*) + AR=xiar + MPD_WARN="-Wall -Wno-unknown-pragmas" + MPD_OPT="-O2 -fpic -s" + MPD_PGEN="-wd11505 -prof-gen" + MPD_PUSE="-wd11505 -prof-use" + ;; + *clang*) + MPD_WARN="-Wall -W -Wno-unknown-pragmas" + MPD_OPT="-O2 -fpic" + ;; + *suncc*) + MPD_WARN= + MPD_OPT="-O2 -fpic -s" + ;; + *ccomp*) + MPD_WARN= + MPD_CONFIG="-DCONFIG_32 -DANSI -DLEGACY_COMPILER" + MPD_OPT="-fstruct-passing -fstruct-assign" + LD="gcc" + LDFLAGS="-m32" + ;; +esac + +# Auto-detect machine dependent settings: +M64= +M32= +if test -n "$MACHINE"; then + M64="-m64 " + M32="-m32 " + case "$MACHINE" in + x64|uint128|ansi64|full_coverage|ppro|ansi32|ansi-legacy|universal) + : ;; + *) + echo "configure: error: invalid MACHINE variable: $MACHINE" + exit 1 ;; + esac +elif test $ac_cv_sizeof_size_t -eq 8; then + if test $have_gcc_asm_for_x64 = yes; then + MACHINE="x64" + elif test $ac_cv_sizeof__uint128_t -eq 8; then + MACHINE="uint128" + else + MACHINE="ansi64" + fi +else + # Macros for detecting uint64_t and int64_t are unreliable: + case $CC in + *ccomp*) + MACHINE="ansi-legacy" + ;; + *) + MACHINE="ansi32" + ;; + esac + if test $have_gcc_asm_for_x87 = yes; then + case $CC in + *gcc*|*clang*) # icc >= 11.0 works as well + case $ac_sys_system in + darwin*|Darwin*) + ;; + *) + MACHINE="ppro" + ;; + esac + ;; + esac + fi +fi + +# Set configuration variables: +MPD_PREC=9 +MPD_DPREC=18 +CONFIGURE_LDFLAGS= +case "$MACHINE" in + x64) + MPD_CONFIG=$M64"-DCONFIG_64 -DASM" + CONFIGURE_LDFLAGS=$M64 + MPD_PREC=19 + MPD_DPREC=38 + ;; + uint128) + MPD_CONFIG=$M64"-DCONFIG_64 -DANSI -DHAVE_UINT128_T" + CONFIGURE_LDFLAGS=$M64 + MPD_PREC=19 + MPD_DPREC=38 + ;; + ansi64) + MPD_CONFIG=$M64"-DCONFIG_64 -DANSI" + CONFIGURE_LDFLAGS=$M64 + MPD_PREC=19 + MPD_DPREC=38 + ;; + full_coverage) + # Formerly ansi64c32, for testing only! + MPD_CONFIG=$M64"-DTEST_COVERAGE -DCONFIG_32 -DANSI" + CONFIGURE_LDFLAGS=$M64 + ;; + ppro) + MPD_CONFIG=$M32"-DCONFIG_32 -DPPRO -DASM" + CONFIGURE_LDFLAGS=$M32 + # 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 + MPD_CONFIG="$MPD_CONFIG -fno-ipa-pure-const" + +$as_echo "#define HAVE_IPA_PURE_CONST_BUG 1" >>confdefs.h + + fi + ;; + esac + ;; + ansi32) + MPD_CONFIG=$M32"-DCONFIG_32 -DANSI" + CONFIGURE_LDFLAGS=$M32 + ;; + ansi-legacy) + MPD_CONFIG=$M32"-DCONFIG_32 -DANSI -DLEGACY_COMPILER" + CONFIGURE_LDFLAGS=$M32 + ;; + universal) + MPD_CONFIG="-DUNIVERSAL" +esac + + +# Substitute variables and generate output: +if test -z "$LD"; then + LD="$CC" +fi + + + + + + + + + + + + + + +if test -z "$CFLAGS"; then + CONFIGURE_CFLAGS="$MPD_WARN $MPD_CONFIG $MPD_OPT" +else + CONFIGURE_CFLAGS="$MPD_CONFIG -fpic $CFLAGS" +fi +if test "$have_glibc_memmove_bug" = yes; then + CONFIGURE_CFLAGS="$CONFIGURE_CFLAGS -U_FORTIFY_SOURCE" +fi + +if test -n "$LDFLAGS"; then + CONFIGURE_LDFLAGS="$LDFLAGS" +fi + + + + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by mpdecimal $as_me @RELEASE_VERSION@, which was +generated by GNU Autoconf 2.68. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to . +mpdecimal home page: ." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +mpdecimal config.status @RELEASE_VERSION@ +configured by $0, generated by GNU Autoconf 2.68, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2010 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "tests/Makefile") CONFIG_FILES="$CONFIG_FILES tests/Makefile" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi + ;; + + + esac + +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + + +GLIBC_MEMMOVE_BUG_WARN=" +***************************** WARNING ********************************* + +Detected glibc _FORTIFY_SOURCE/memmove bug. See: + + http://sourceware.org/ml/libc-alpha/2010-12/msg00009.html + +Enabling -U_FORTIFY_SOURCE workaround. If -D_FORTIFY_SOURCE is also +present in the command line, make sure that the order of the two +options is: + + ... -D_FORTIFY_SOURCE=2 ... -U_FORTIFY_SOURCE ... + +A better solution is to upgrade glibc or to report the bug to your +OS vendor. + +***************************** WARNING ********************************* +" +if test "$have_glibc_memmove_bug" = yes; then + echo "$GLIBC_MEMMOVE_BUG_WARN" +fi + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/configure.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/configure.in Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,384 @@ +dnl Some parts taken from Python's configure.in. + +AC_PREREQ([2.67]) +AC_INIT(mpdecimal, @RELEASE_VERSION@, mpdecimal-bugs@bytereef.org, mpdecimal, http://www.bytereef.org/mpdecimal/index.html) +AC_CONFIG_HEADER(config.h) +AC_CONFIG_FILES([Makefile tests/Makefile]) +AC_ARG_VAR(MACHINE, [force configuration: x64, uint128, ansi64, ppro, ansi32, ansi-legacy]) + +LIBSHARED=libmpdec.so.$PACKAGE_VERSION +AC_SUBST(LIBSHARED) + +# Language and compiler: +AC_LANG_C +saved_cflags=$CFLAGS +AC_PROG_CC +CFLAGS=$saved_cflags + +# System and machine type: +AC_MSG_CHECKING(system as reported by uname -s) +ac_sys_system=`uname -s` +AC_MSG_RESULT($ac_sys_system) + +AC_MSG_CHECKING(machine type as reported by uname -m) +ac_sys_machine=`uname -m` +AC_MSG_RESULT($ac_sys_machine) + +# Checks for header files: +AC_HEADER_STDC +AC_CHECK_HEADERS(inttypes.h stdint.h) + +# Type availability checks: +AC_TYPE_SIZE_T +AC_TYPE_INT32_T +AC_TYPE_INT64_T +AC_TYPE_UINT32_T +AC_TYPE_UINT64_T +AC_CHECK_TYPE(__uint128_t, AC_DEFINE(HAVE_UINT128_T, 1, + [Define if your compiler provides __uint128_t.]),,) + +# Sizes of various types: +AC_CHECK_SIZEOF(size_t, 4) +AC_CHECK_SIZEOF(__uint128_t, 8) + +# x64 with gcc asm: +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 + +# x87 with gcc asm: +AC_MSG_CHECKING(for x87 gcc inline assembler) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[ + unsigned short cw; + __asm__ __volatile__ ("fnstcw %0" : "=m" (cw)); + __asm__ __volatile__ ("fldcw %0" : : "m" (cw)); +]])],[have_gcc_asm_for_x87=yes],[have_gcc_asm_for_x87=no]) +AC_MSG_RESULT($have_gcc_asm_for_x87) +if test "$have_gcc_asm_for_x87" = yes; then + AC_DEFINE(HAVE_GCC_ASM_FOR_X87, 1, + [Define if we can use x87 gcc inline assembler.]) +fi + +# gmp for extended tests: +GMPDEPS= +GMPPATH= +saved_ldflags="$LDFLAGS" +saved_cflags="$CFLAGS" +LDFLAGS=-lgmp +AC_MSG_CHECKING(for gmp) +AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +#include +]], [[ + mpz_t x; + mpz_init(x); +]])], [have_gmp=yes], [have_gmp=no]) +if test $have_gmp = no; then + LDFLAGS="-L/usr/local/lib -lgmp" + CFLAGS="-I/usr/local/include" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + #include + ]], [[ + mpz_t x; + mpz_init(x); + ]])], [have_gmp=yes], [have_gmp=no]) + if test $have_gmp = yes; then + GMPPATH=/usr/local + fi +fi +AC_MSG_RESULT($have_gmp) +LDFLAGS="$saved_ldflags" +CFLAGS="$saved_cflags" + +if test $have_gmp = yes; then + GMPDEPS="mpd_mpz_add mpd_mpz_divmod mpd_mpz_mul mpd_mpz_sub" +fi +AC_SUBST(GMPDEPS) +AC_SUBST(GMPPATH) + +# 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 +#include +#include +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 + +# suncc is dectected as cc: +case $ac_sys_system in +sun*|Sun*) + case $CC in + cc) + CC=suncc + ;; + esac +esac + +# Compiler dependent settings: +AR=ar +MPD_WARN="-Wall" +MPD_OPT="-O2 -fpic" +MPD_PGEN= +MPD_PUSE= +MPD_CCOV= +MPD_LDCOV= +case $CC in + *gcc*) + MPD_WARN="-Wall -W -Wno-unknown-pragmas" + MPD_OPT="-O2 -fpic -s" + MPD_PGEN="-fprofile-generate -fprofile-values" + MPD_PUSE="-fprofile-use -freorder-blocks" + MPD_CCOV="-O0 -g -fno-inline -fprofile-arcs -ftest-coverage -fpic" + MPD_LDCOV="-fprofile-arcs" + ;; + *icc*) + AR=xiar + MPD_WARN="-Wall -Wno-unknown-pragmas" + MPD_OPT="-O2 -fpic -s" + MPD_PGEN="-wd11505 -prof-gen" + MPD_PUSE="-wd11505 -prof-use" + ;; + *clang*) + MPD_WARN="-Wall -W -Wno-unknown-pragmas" + MPD_OPT="-O2 -fpic" + ;; + *suncc*) + MPD_WARN= + MPD_OPT="-O2 -fpic -s" + ;; + *ccomp*) + MPD_WARN= + MPD_CONFIG="-DCONFIG_32 -DANSI -DLEGACY_COMPILER" + MPD_OPT="-fstruct-passing -fstruct-assign" + LD="gcc" + LDFLAGS="-m32" + ;; +esac + +# Auto-detect machine dependent settings: +M64= +M32= +if test -n "$MACHINE"; then + M64="-m64 " + M32="-m32 " + case "$MACHINE" in + x64|uint128|ansi64|full_coverage|ppro|ansi32|ansi-legacy|universal) + : ;; + *) + echo "configure: error: invalid MACHINE variable: $MACHINE" + exit 1 ;; + esac +elif test $ac_cv_sizeof_size_t -eq 8; then + if test $have_gcc_asm_for_x64 = yes; then + MACHINE="x64" + elif test $ac_cv_sizeof__uint128_t -eq 8; then + MACHINE="uint128" + else + MACHINE="ansi64" + fi +else + # Macros for detecting uint64_t and int64_t are unreliable: + case $CC in + *ccomp*) + MACHINE="ansi-legacy" + ;; + *) + MACHINE="ansi32" + ;; + esac + if test $have_gcc_asm_for_x87 = yes; then + case $CC in + *gcc*|*clang*) # icc >= 11.0 works as well + case $ac_sys_system in + darwin*|Darwin*) + ;; + *) + MACHINE="ppro" + ;; + esac + ;; + esac + fi +fi + +# Set configuration variables: +MPD_PREC=9 +MPD_DPREC=18 +CONFIGURE_LDFLAGS= +case "$MACHINE" in + x64) + MPD_CONFIG=$M64"-DCONFIG_64 -DASM" + CONFIGURE_LDFLAGS=$M64 + MPD_PREC=19 + MPD_DPREC=38 + ;; + uint128) + MPD_CONFIG=$M64"-DCONFIG_64 -DANSI -DHAVE_UINT128_T" + CONFIGURE_LDFLAGS=$M64 + MPD_PREC=19 + MPD_DPREC=38 + ;; + ansi64) + MPD_CONFIG=$M64"-DCONFIG_64 -DANSI" + CONFIGURE_LDFLAGS=$M64 + MPD_PREC=19 + MPD_DPREC=38 + ;; + full_coverage) + # Formerly ansi64c32, for testing only! + MPD_CONFIG=$M64"-DTEST_COVERAGE -DCONFIG_32 -DANSI" + CONFIGURE_LDFLAGS=$M64 + ;; + ppro) + MPD_CONFIG=$M32"-DCONFIG_32 -DPPRO -DASM" + CONFIGURE_LDFLAGS=$M32 + # 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 + MPD_CONFIG="$MPD_CONFIG -fno-ipa-pure-const" + AC_DEFINE(HAVE_IPA_PURE_CONST_BUG, 1, + [Define if gcc has the ipa-pure-const bug.]) + fi + ;; + esac + ;; + ansi32) + MPD_CONFIG=$M32"-DCONFIG_32 -DANSI" + CONFIGURE_LDFLAGS=$M32 + ;; + ansi-legacy) + MPD_CONFIG=$M32"-DCONFIG_32 -DANSI -DLEGACY_COMPILER" + CONFIGURE_LDFLAGS=$M32 + ;; + universal) + MPD_CONFIG="-DUNIVERSAL" +esac + + +# Substitute variables and generate output: +if test -z "$LD"; then + LD="$CC" +fi +AC_SUBST(LD) +AC_SUBST(AR) +AC_SUBST(MPD_HEADER) +AC_SUBST(MPD_WARN) +AC_SUBST(MPD_CONFIG) +AC_SUBST(MPD_OPT) + +AC_SUBST(MPD_CCOV) +AC_SUBST(MPD_LDCOV) +AC_SUBST(MPD_PGEN) +AC_SUBST(MPD_PUSE) +AC_SUBST(MPD_PREC) +AC_SUBST(MPD_DPREC) + +if test -z "$CFLAGS"; then + CONFIGURE_CFLAGS="$MPD_WARN $MPD_CONFIG $MPD_OPT" +else + CONFIGURE_CFLAGS="$MPD_CONFIG -fpic $CFLAGS" +fi +if test "$have_glibc_memmove_bug" = yes; then + CONFIGURE_CFLAGS="$CONFIGURE_CFLAGS -U_FORTIFY_SOURCE" +fi + +if test -n "$LDFLAGS"; then + CONFIGURE_LDFLAGS="$LDFLAGS" +fi + +AC_SUBST(CONFIGURE_CFLAGS) +AC_SUBST(CONFIGURE_LDFLAGS) + +AC_OUTPUT + +GLIBC_MEMMOVE_BUG_WARN=" +***************************** WARNING ********************************* + +Detected glibc _FORTIFY_SOURCE/memmove bug. See: + + http://sourceware.org/ml/libc-alpha/2010-12/msg00009.html + +Enabling -U_FORTIFY_SOURCE workaround. If -D_FORTIFY_SOURCE is also +present in the command line, make sure that the order of the two +options is: + + ... -D_FORTIFY_SOURCE=2 ... -U_FORTIFY_SOURCE ... + +A better solution is to upgrade glibc or to report the bug to your +OS vendor. + +***************************** WARNING ********************************* +" +if test "$have_glibc_memmove_bug" = yes; then + echo "$GLIBC_MEMMOVE_BUG_WARN" +fi + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/constants.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/constants.c Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2008-2010 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 +#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" +}; + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/constants.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/constants.h Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2008-2010 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 */ + + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/context.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/context.c Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2008-2010 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 +#include +#include + + +void +mpd_dflt_traphandler(mpd_context_t *ctx UNUSED) +{ + raise(SIGFPE); +} + +void (* mpd_traphandler)(mpd_context_t *) = mpd_dflt_traphandler; + + +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_maxcontext_plus(mpd_context_t *workctx, const mpd_context_t *ctx) +{ + workctx->prec = ctx->prec > MPD_MAX_PREC ? ctx->prec : MPD_MAX_PREC; + workctx->emax = ctx->emax > MPD_MAX_EMAX ? ctx->emax : MPD_MAX_EMAX; + workctx->emin = ctx->emin < MPD_MIN_EMIN ? ctx->emin : MPD_MIN_EMIN; + workctx->round=MPD_ROUND_HALF_EVEN; + workctx->traps=MPD_Traps; + workctx->status=0; + workctx->newtrap=0; + workctx->clamp=0; + workctx->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) +{ + int i; + + for (i = 0; i < MPD_ROUND_GUARD; i++) { + if (i == round) { + ctx->round = round; + return 1; + } + } + return 0; +} + +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); + } +} + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/convolute.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/convolute.c Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2008-2010 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 +#include "bits.h" +#include "constants.h" +#include "fnt.h" +#include "fourstep.h" +#include "numbertheory.h" +#include "sixstep.h" +#include "umodarith.h" +#include "convolute.h" + + +/* 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; +} + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/convolute.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/convolute.h Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2008-2010 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 + +#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 diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/crt.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/crt.c Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2008-2010 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 +#include +#include "numbertheory.h" +#include "umodarith.h" +#include "crt.h" + + +/* + * Functions for arithmetic on triple-word mpd_uint_t numbers. + */ + + +/* 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; + + w[0] = w[0] + v[0]; + carry = (w[0] < v[0]); + + w[1] = w[1] + v[1]; + if (w[1] < v[1]) w[2]++; + + w[1] = w[1] + carry; + if (w[1] < carry) w[2]++; + + w[2] += v[2]; +} + +/* 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/] + */ + +/* + * 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. + * + * Overflow analysis for 32 bit: + * + * carry[3] can hold cmax = 2**96-1. Let c_i denote the carry at the + * beginning of the ith iteration. Let zmax be the maximum z. + * + * cmax = 2**96-1 = 79228162514264337593543950335 + * zmax = (p1*p2*p3)-1 = 7711435583600944683209981953 + * + * c_0 = 0 + * c_1 = (c_0 + zmax) / 10**9 = 7711435583600944683 + * c_2 = (c_1 + zmax) / 10**9 = 7711435591312380266 + * c_3 = (c_2 + zmax) / 10**9 = 7711435591312380274 + * c_4 = (c_3 + zmax) / 10**9 = 7711435591312380274 + * (...) + * + * The carries do not increase, (c_i + zmax) cannot overflow. + * + * + * Overflow analysis for 64 bit: + * + * cmax = 2**192-1 = 6277101735386680763835789423207666416102355444464034512895 + * zmax = (p1*p2*p3)-1 = 6277101353934753858413533876806988331203900781075588186113 + * + * c_0 = 0 + * c_1 = (c_0 + zmax) / 10**19 = 627710135393475385841353387680698833120 + * c_2 = (c_1 + zmax) / 10**19 = 627710135393475385904124401220046371704 + * c_3 = (c_2 + zmax) / 10**19 = 627710135393475385904124401220046371710 + * c_4 = (c_3 + zmax) / 10**19 = 627710135393475385904124401220046371710 + * (...) + * + * The carries do not increase. (c_i + zmax) cannot overflow. + */ +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); +} + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/crt.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/crt.h Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2008-2010 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 + + +void crt3(mpd_uint_t *x1, mpd_uint_t *x2, mpd_uint_t *x3, mpd_size_t nmemb); + + +#endif diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/difradix2.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/difradix2.c Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2008-2010 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 +#include +#include "bits.h" +#include "numbertheory.h" +#include "umodarith.h" +#include "difradix2.h" + + +/* + * Generate bit reversed words and carry out the permutation. + * Algorithm due to Brent Lehmann, see Joerg Arndt, "Matters Computational", + * Chapter 1.13.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 { + if (r > x) { + t = a[x]; + a[x] = a[r]; + a[r] = t; + } + x += 1; + r ^= (n - (n >> (mpd_bsf(x)+1))); + } 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); + + 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); +} + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/difradix2.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/difradix2.h Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2008-2010 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 +#include "numbertheory.h" + + +void fnt_dif2(mpd_uint_t a[], mpd_size_t n, struct fnt_params *tparams); + + +#endif diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/docstrings.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/docstrings.h Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,939 @@ +/* + * Copyright (c) 2001-2010 Python Software Foundation. All Rights Reserved. + * Modified and extended by Stefan Krah. + */ + + +#ifndef DOCSTRINGS_H +#define DOCSTRINGS_H + + +#include "Python.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"); + +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"); + + +/******************************************************************************/ +/* 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_abs,"\n\ +abs() - Return the absolute value of the number.\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 ROUND_HALF_EVEN rounding mode is used. If the _allcr\n\ +field of the context is set to 1 (default), 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_special,"\n\ +is_special() - Return True if the argument is either NaN, sNaN or Infinity\n\ +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 ROUND_HALF_EVEN rounding mode is used. If the _allcr field of the\n\ +context is set to 1, the result is correctly rounded.\n\ +\n"); + +PyDoc_STRVAR(doc_log10,"\n\ +log10([context]) - Return the base ten logarithm of the operand.\n\ +The ROUND_HALF_EVEN rounding mode is used. If the _allcr field of the\n\ +context is set to 1, the result is 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\ +Normalize is an alias for reduce([context]).\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_reduce,"\n\ +reduce([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_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,"\n\ +to_eng([context]) - Convert a number to a string using engineering\n\ +notation. Alias for to_eng_string().\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"); + + +/******************************************************************************/ +/* Decimal Object, Additional Methods */ +/******************************************************************************/ + +PyDoc_STRVAR(doc_add,"\n\ +add(other[, context]) - Return the sum of self and other.\n\ +\n"); + +PyDoc_STRVAR(doc_apply,"\n\ +apply() - Apply the default context to the number.\n\ +\n"); + +PyDoc_STRVAR(doc_div,"\n\ +div(other[, context]) - Divide self by other.\n\ +Alias for divide(other[, context]).\n\ +\n"); + +PyDoc_STRVAR(doc_divide,"\n\ +divide(other[, context]) - Divide self by other.\n\ +\n"); + +PyDoc_STRVAR(doc_divide_int,"\n\ +divide_int(other[, context]) - Truncating division.\n\ +\n"); + +PyDoc_STRVAR(doc_divint,"\n\ +divint(other[, context]) - Truncating division.\n\ +Alias for divide_int(other[, context]).\n\ +\n"); + +PyDoc_STRVAR(doc_divmod,"\n\ +divmod(other[, context]) - Return quotient and remainder of the\n\ +division self / other.\n\ +\n"); + +PyDoc_STRVAR(doc_invroot,"\n\ +invroot([context]) - Return the reciprocal of the square root.\n\ +\n"); + +PyDoc_STRVAR(doc_is_integer,"\n\ +is_integer() - Return True if the argument is an integer and False otherwise.\n\ +\n"); + +PyDoc_STRVAR(doc_minus,"\n\ +minus() - Minus corresponds to the unary prefix minus operator in Python,\n\ +but applies the context precision and rounding.\n\ +\n"); + +PyDoc_STRVAR(doc_mul,"\n\ +mul(other[, context]) - Multiply self by other.\n\ +Alias for multiply(other[, context]).\n\ +\n"); + +PyDoc_STRVAR(doc_multiply,"\n\ +multiply(other[, context]) - Multiply self by other.\n\ +\n"); + +PyDoc_STRVAR(doc_plus,"\n\ +plus() - Plus corresponds to the unary prefix plus operator in Python,\n\ +but applies the context precision and rounding.\n\ +\n"); + +PyDoc_STRVAR(doc_pow,"\n\ +pow(other[, context]) - Raise self to the power of other.\n\ +Alias for power(other[, context]).\n\ +\n"); + +PyDoc_STRVAR(doc_power,"\n\ +power(other[, context]) - Raise self to the power of other.\n\ +\n"); + +PyDoc_STRVAR(doc_powmod,"\n\ +powmod(other, third[, context]) - Return (self ** other) % third.\n\ +\n"); + +PyDoc_STRVAR(doc_rem,"\n\ +rem(other[, context]) - Remainder of the division self / other.\n\ +Alias for remainder(other[, context]).\n\ +\n"); + +PyDoc_STRVAR(doc_remainder,"\n\ +remainder(other[, context]) - Remainder of the division self / other.\n\ +\n"); + +PyDoc_STRVAR(doc_sub,"\n\ +sub(other[, context]) - Subtract other from self.\n\ +Alias for subtract(other[, context]).\n\ +\n"); + +PyDoc_STRVAR(doc_subtract,"\n\ +subtract(other[, context]) - Subtract other from self.\n\ +\n"); + +PyDoc_STRVAR(doc_sign,"\n\ +sign() - Return -1 or 1, depending on the sign of self.\n\ +\n"); + +PyDoc_STRVAR(doc_to_sci,"\n\ +to_sci([context]) - Convert a number to a string using scientific notation.\n\ +Alias for to_sci_string().\n\ +\n"); + +PyDoc_STRVAR(doc_to_sci_string,"\n\ +to_sci_string([context]) - Convert a number to a string using\n\ +scientific notation.\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=[], _allcr=1)\n\ + >>>\n\ +\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_apply,"\n\ +apply(x) - Apply self to Decimal x.\n\ +\n"); + +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_div,"\n\ +div(x, y) - Return x divided by y. Alias for divide(x, y).\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_divint,"\n\ +divint(x, y) - Return x divided by y, truncated to an integer.\n\ +Alias for divide_int(x, y).\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_mul,"\n\ +mul(x, y) - Return the product of x and y. Alias for multiply(x, y).\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_pow,"\n\ +pow(x, y) - Compute x**y. Alias for power(x, y).\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.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_powmod,"\n\ +powmod(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\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_reduce,"\n\ +reduce(x) - Reduce x to its simplest form.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_rem,"\n\ +rem(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\ +Alias for remainder(x, y).\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_sub,"\n\ +sub(x, y) - Return the difference between x and y. Alias for subtract(x, y).\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,"\n\ +to_eng(x) - Convert a number to a string, using engineering notation.\n\ +Alias for to_eng_string(x).\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,"\n\ +to_sci(x) - Convert a number to a string using scientific notation.\n\ +Alias for to_sci_string(x).\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"); + +/******************************************************************************/ +/* Context Object, Additional Methods */ +/******************************************************************************/ + +PyDoc_STRVAR(doc_ctx_invroot,"\n\ +invroot(x) - Return the reciprocal of the square root of x.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_setflags,"\n\ +setflags(list) - Set the context flags from a list containing signals.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_settraps,"\n\ +settraps(list) - Set the context traps from a list containing signals.\n\ +\n"); + + +#endif + + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/fnt.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/fnt.c Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2008-2010 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 +#include +#include +#include "bits.h" +#include "difradix2.h" +#include "numbertheory.h" +#include "fnt.h" + + +/* 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; +} + + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/fnt.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/fnt.h Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2008-2010 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 + + +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 + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/fourstep.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/fourstep.c Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2008-2010 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 +#include "numbertheory.h" +#include "sixstep.h" +#include "transpose.h" +#include "umodarith.h" +#include "fourstep.h" + + +/* + * A variant of the four-step algorithm from: + * + * David H. Bailey: FFTs in External or Hierarchical Memory, Journal of + * Supercomputing, vol. 4, no. 1 (March 1990), p. 23-35. + * + * URL: http://crd.lbl.gov/~dhbailey/dhbpapers/ + */ + + +#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); + + + SETMODULUS(modnum); + _mpd_init_w3table(w3table, -1, modnum); + /* size three ntt on the columns */ + for (p0=a, p1=p0+C, p2=p0+2*C; p0= 48); + assert(n <= 3*MPD_MAXTRANSFORM_2N); + + +#if 0 + /* An unordered transform is sufficient for convolution. */ + if (ordered) { + transpose_3xpow2(a, C, R); + } +#endif + + /* transform rows */ + for (s = a; s < a+n; s += C) { + if (!inv_six_step_fnt(s, C, modnum)) { + return 0; + } + } + + + 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; + } + } + + + _mpd_init_w3table(w3table, 1, modnum); + /* size three ntt on the columns */ + for (p0=a, p1=p0+C, p2=p0+2*C; p0 + + +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 diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/io.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/io.c Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,1397 @@ +/* + * Copyright (c) 2008-2010 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 +#include +#include +#include +#include +#include +#include +#include +#include "bits.h" +#include "constants.h" +#include "memory.h" +#include "typearith.h" +#include "io.h" + + +/* + * 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'); + } + } +} + +/* + * Scan for at most one decimal point and at most one indicator. + * Remove unneeded zeros before and after the decimal point. + * The first relevant digit will be the start of the coefficient. + * A decimal point may occur before an indicator. + * Plus or minus may occur directly after the indicator. + * The rest of the characters must be decimal digits. + * + * Return start of the coefficient or NULL or error. The end of + * the string is stored in 'end'. + */ +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; + + /* [flags] */ + 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; + /* only digits for the diagnostic code */ + 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; + /* only digits for the diagnostic code */ + 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+1)+(mpd_ssize_t)fracdigits) { + dec->exp = MPD_SSIZE_MIN+1; + } + 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; + } + } + + _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 the exponent to a string */ +static inline char * +exp_to_string(char *s, mpd_ssize_t x) +{ + mpd_ssize_t q, d; + char sign = '+'; + int j; + + if (x < 0) { + sign = '-'; + x = -x; + } + *s++ = sign; + + j = mpd_exp_digits(x) - 1; + + for (; j != 0; --j) { + d = mpd_pow10[j]; + q = x / d; + x -= d * q; + *s++ = '0' + (char)q; + } + *s++ = '0' + (char)x; + + return s; +} + +/* print coefficient to string, len(dec) > 0 */ +static inline char * +coeff_to_string(char *s, const mpd_t *dec) +{ + mpd_uint_t x, q, d; + mpd_ssize_t i; + int j; + + /* most significant word */ + x = mpd_msword(dec); + j = mpd_word_digits(x) - 1; + + for (; j != 0; --j) { + d = mpd_pow10[j]; + q = x / d; + x -= d * q; + *s++ = '0' + (char)q; + } + *s++ = '0' + (char)x; + + /* remaining full words */ + for (i=dec->len-2; i >= 0; --i) { + x = dec->data[i]; + for (j=MPD_RDIGITS-1; j != 0; --j) { + d = mpd_pow10[j]; + q = x / d; + x -= d * q; + *s++ = '0' + (char)q; + } + *s++ = '0' + (char)x; + } + + return s; +} + +/* print coefficient to string, len(dec) > 0 */ +static inline char * +coeff_to_string_dot(char *s, char *dot, const mpd_t *dec) +{ + mpd_uint_t x, q, d; + mpd_ssize_t i; + int j; + + /* most significant word */ + x = mpd_msword(dec); + j = mpd_word_digits(x) - 1; + + for (; j >= 0; --j) { + if (s == dot) + *s++ = '.'; + d = mpd_pow10[j]; + q = x / d; + x -= d * q; + *s++ = '0' + (char)q; + } + + /* remaining full words */ + for (i=dec->len-2; i >= 0; --i) { + x = dec->data[i]; + for (j=MPD_RDIGITS-1; j >= 0; --j) { + if (s == dot) + *s++ = '.'; + d = mpd_pow10[j]; + q = x / d; + x -= d * q; + *s++ = '0' + (char)q; + } + } + + 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 + +/* + * Return the string representation of a decimal. Formatting is done according + * to 'flags'. A return value of NULL indicates MPD_Malloc_error. + * + * To allow formatting like [0e15, '.6e' -> 0.000000e-9], 'zeroexp' is used + * in combination with MPD_FMT_FIXED (see mpd_qformat for details). + */ +static char * +_mpd_to_string(const mpd_t *dec, int flags, mpd_ssize_t zeroexp) +{ + char *decstring = NULL, *cp = NULL; + mpd_ssize_t ldigits, dplace; + 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) { + return NULL; + } + + 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); + + /* + * ldigits: Digits to the left of the decimal point, as if the + * number was written without exponent notation. + * + * dplace: Position of the decimal point relative to the first + * member of the coefficient. + * + * 0.00000_.____._____000000. + * ^ ^ ^ ^ + * | | | | + * | | | `- dplace in the last position + * | | `- dplace in the middle of the coefficient + * | ` dplace = 1 (after the first coefficient digit) + * `- dplace is negative + */ + + ldigits = dec->digits + dec->exp; + dplace = 1; /* default for MPD_FMT_TOSCI, MPD_FMT_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) { + return NULL; + } + + + 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 + * MPD_FMT_FIXED with zeroexp: always + */ + if (ldigits != dplace || flags&MPD_FMT_EXP || zeroexp != MPD_SSIZE_MAX) { + /* space: expdigits+2 */ + mpd_ssize_t x = (zeroexp != MPD_SSIZE_MAX) ? zeroexp : ldigits-dplace; + *cp++ = (flags&MPD_FMT_UPPER) ? 'E' : 'e'; + cp = exp_to_string(cp, x); + } + + if (flags&MPD_FMT_PERCENT) { + *cp++ = '%'; + } + } + + assert(cp < decstring+mem); + *cp = '\0'; + return decstring; +} + +char * +mpd_to_sci(const mpd_t *dec, int fmt) +{ + int flags = MPD_FMT_TOSCI; + + flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER; + return _mpd_to_string(dec, flags, MPD_SSIZE_MAX); +} + +char * +mpd_to_eng(const mpd_t *dec, int fmt) +{ + int flags = MPD_FMT_TOENG; + + flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER; + return _mpd_to_string(dec, flags, MPD_SSIZE_MAX); +} + +/* Copy a single UTF-8 char to dest. */ +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_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; + spec->type = *cp++; + /* separator has already been specified */ + if (*spec->sep) return 0; + spec->type = (spec->type == 'N') ? 'G' : 'g'; + lc = localeconv(); + spec->dot = lc->decimal_point; + spec->sep = lc->thousands_sep; + spec->grouping = lc->grouping; + } + + /* 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 += 1; + 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 the decimal to dest, adding separators 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 min_width is reached. + */ +static void +_mpd_add_sep_dot(mpd_mbstr_t *dest, + const char *src, mpd_ssize_t n_src, /* integer part and length */ + const char *sign, const char *dot, const char *rest, + 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); + g = spec->grouping; + dest->cur = dest->nbytes; + dest->nbytes = dest->nchars = 0; + + /* rest <= MPD_MAX_PREC */ + _mbstr_copy_ascii(dest, rest, (mpd_ssize_t)strlen(rest)); + + if (dot) { + _mbstr_copy_char(dest, dot, (mpd_ssize_t)strlen(dot)); + } + + consume = *g; + while (1) { + 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) { + if (spec->align == 'z' && + dest->nchars + n_sign < spec->min_width) { + n_src = spec->min_width - (dest->nchars + n_sign); + consume = *g - consume; + pad = 1; + continue; + } + break; + } + + if (n_sep > 0) { + if (pad && n_src > 1) n_src -= 1; + _mbstr_copy_char(dest, spec->sep, n_sep); + } + + if (*g && *(g+1)) g++; + consume = *g; + } + + if (sign) { + _mbstr_copy_ascii(dest, sign, 1); + } + + if (dest->data) { + dest->data[dest->nbytes] = '\0'; + } +} + +/* Change decstring to locale-specific appearance. */ +static void +_mpd_apply_lconv(mpd_mbstr_t *result, char *decstring, mpd_spec_t *spec, + uint32_t *status) +{ + const char *sign = NULL, *intpart = NULL; + const char *dot = NULL, *rest = NULL; + const char *dp; + mpd_ssize_t n_int; + + assert(result->data == NULL); + + dp = decstring; + n_int = 0; + + if (!isdigit((uchar)*dp)) { + sign = dp++; + } + if (isdigit((uchar)*dp)) { + intpart = dp++; + while (isdigit((uchar)*dp)) { + dp++; + } + n_int = (mpd_ssize_t)(dp-intpart); + } + if (*dp == '.') { + if (*spec->dot == '\0') { + /* decimal point must be present */ + *status |= MPD_Invalid_operation; /* GCOV_NOT_REACHED */ + mpd_free(decstring); /* GCOV_NOT_REACHED */ + return; /* GCOV_NOT_REACHED */ + } + dp++; dot = spec->dot; + } + rest = dp; + + + if (!dot && !(intpart && *spec->sep && *spec->grouping)) { + result->data = decstring; + result->nbytes = result->nchars = (mpd_ssize_t)strlen(decstring); + return; + } + + /* Get the size of the new decimal string after inserting dot and + * separators. */ + _mpd_add_sep_dot(result, intpart, n_int, sign, dot, rest, spec); + + if ((result->data = mpd_alloc(result->nbytes+1, 1)) == NULL) { + *status |= MPD_Malloc_error; + mpd_free(decstring); + return; + } + + /* Perform actual writes. */ + _mpd_add_sep_dot(result, intpart, n_int, sign, dot, rest, spec); + + mpd_free(decstring); +} + +/* Add padding to the formatted string if necessary. */ +static void +_mpd_add_pad(mpd_mbstr_t *result, 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; + 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); + result->data = NULL; + return; + } + + if (spec->align == 'z') { + spec->align = '='; + } + + if (spec->align == '<') { + rpad = add_chars; + } + else if (spec->align == '>' || spec->align == '=') { + lpad = add_chars; + } + else { /* align == '^' */ + lpad = add_chars/2; + rpad = add_chars-lpad; + } + + len = result->nbytes; + if (spec->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 the string representation of an mpd_t, formatted according to 'spec'. + * 'spec' is modified. 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, mpd_spec_t *spec, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_context_t workctx; + 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 prec, fracdigits, exp; + mpd_ssize_t zeroexp = MPD_SSIZE_MAX; + mpd_mbstr_t result; + char *decstring; + int flags = 0; + + + if (spec->min_width > MPD_MAX_PREC) { + *status |= MPD_Invalid_operation; + return NULL; + } + + if (!mpd_qcopy(&tmp, dec, status)) { + return NULL; + } + + if (spec->type == '%') { + tmp.exp += 2; + spec->type = 'f'; + flags |= MPD_FMT_PERCENT; + } + + if (isupper((uchar)spec->type)) { + spec->type = tolower((uchar)spec->type); + flags |= MPD_FMT_UPPER; + } + if (spec->sign == ' ') { + flags |= MPD_FMT_SIGN_SPACE; + } + else if (spec->sign == '+') { + flags |= MPD_FMT_SIGN_PLUS; + } + + mpd_maxcontext_plus(&workctx, ctx); + workctx.round = ctx->round; + if (mpd_isspecial(&tmp)) { + /* no percent formatting */ + flags |= MPD_FMT_TOSCI; + if (spec->align == 'z') { + spec->fill[0] = ' '; + spec->fill[1] = '\0'; + spec->align = '>'; + } + } + else if (spec->type == 'g') { + /* spec->prec: significant digits */ + prec = (spec->prec < 0) ? dec->digits : spec->prec; + workctx.prec = (prec == 0) ? 1 : prec; + flags |= MPD_FMT_TOSCI; + mpd_qfinalize(&tmp, &workctx, &workctx.status); + } + else { + /* spec->prec: fraction digits */ + if (spec->type == 'e') { + fracdigits = (spec->prec < 0) ? tmp.digits-1 : spec->prec; + if (mpd_iszero(&tmp)) { + zeroexp = tmp.exp+fracdigits; + exp = -fracdigits; + flags |= MPD_FMT_FIXED; + } + else { + exp = tmp.exp + tmp.digits-(fracdigits+1); + flags |= MPD_FMT_EXP; + } + } + else { /* 'f' */ + fracdigits = spec->prec; + if (fracdigits < 0) { + fracdigits = (tmp.exp < 0) ? -tmp.exp : 0; + } + exp = -fracdigits; + fracdigits += (tmp.exp+tmp.digits); + fracdigits = (fracdigits < 0) ? 0 : fracdigits; + flags |= MPD_FMT_FIXED; + } + workctx.prec = fracdigits+1; + mpd_qrescale(&tmp, &tmp, exp, &workctx, &workctx.status); + if (tmp.digits > workctx.prec) { + mpd_qfinalize(&tmp, &workctx, &workctx.status); + } + } + + if ((decstring = _mpd_to_string(&tmp, flags, zeroexp)) == NULL) { + *status |= MPD_Malloc_error; + mpd_del(&tmp); + return NULL; + } + + result.data = decstring; + result.nbytes = result.nchars = 0; + if (!mpd_isspecial(&tmp) && *spec->dot != '\0') { + result.data = NULL; + _mpd_apply_lconv(&result, decstring, spec, status); + if (result.data == NULL) { + goto finish; + } + } + + if (spec->min_width) { + if (result.nbytes == 0) { + result.nbytes = result.nchars = + (mpd_ssize_t)strlen(result.data); + } + _mpd_add_pad(&result, spec, status); + } + + +finish: + mpd_del(&tmp); + return result.data; +} + +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<= 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<= 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<= 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 */ + } +} + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/io.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/io.h Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2008-2010 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 +#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 diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/memory.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/memory.c Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2008-2010 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 +#include +#include "typearith.h" +#include "memory.h" + + +mpd_ssize_t MPD_MINALLOC = MPD_MINALLOC_MIN; + +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; + +#if MPD_SIZE_MAX < SIZE_MAX + if (nmemb > MPD_SIZE_MAX || size > MPD_SIZE_MAX) { + return NULL; + } +#endif + req = mul_size_t((mpd_size_t)nmemb, (mpd_size_t)size); + if ((ptr = mpd_mallocfunc(req)) == 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) +{ + void *ptr; + mpd_size_t req; + + req = mul_size_t(nmemb, size); + if ((ptr = mpd_mallocfunc(req)) == NULL) { + return NULL; + } + + return ptr; +} + +/* calloc with overflow checking */ +void * +mpd_calloc(mpd_size_t nmemb, mpd_size_t size) +{ + void *ptr; + + if ((ptr = mpd_callocfunc(nmemb, size)) == NULL) { + return NULL; + } + + return ptr; +} + +/* 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; + + req = mul_size_t(nmemb, size); + if ((new = mpd_reallocfunc(ptr, req)) == 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) +{ + void *ptr; + mpd_size_t req; + + req = mul_size_t(nmemb, size); + req = add_size_t(req, struct_size); + if ((ptr = mpd_mallocfunc(req)) == NULL) { + return NULL; + } + + return ptr; +} + + +/* Allocate a new decimal with data-size 'size'. + * In case of an error the return value is NULL. + */ +mpd_t * +mpd_qnew_size(mpd_ssize_t size) +{ + mpd_t *result; + + size = (size < MPD_MINALLOC) ? MPD_MINALLOC : size; + + if ((result = mpd_alloc(1, sizeof *result)) == NULL) { + return NULL; + } + if ((result->data = mpd_alloc(size, sizeof *result->data)) == NULL) { + mpd_free(result); + return NULL; + } + + result->flags = 0; + result->exp = 0; + result->digits = 0; + result->len = 0; + result->alloc = size; + + return result; +} + +/* Allocate a new decimal with data-size 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; + + if ((result = mpd_qnew()) == NULL) { + mpd_addstatus_raise(ctx, MPD_Malloc_error); + } + return result; +} + +int +mpd_switch_to_dyn(mpd_t *result, mpd_ssize_t size, uint32_t *status) +{ + mpd_uint_t *p = result->data; + + if ((result->data = mpd_alloc(size, sizeof *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 = size; + mpd_set_dynamic_data(result); + return 1; +} + +int +mpd_switch_to_dyn_zero(mpd_t *result, mpd_ssize_t size, uint32_t *status) +{ + mpd_uint_t *p = result->data; + + if ((result->data = mpd_calloc(size, sizeof *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 = size; + mpd_set_dynamic_data(result); + + return 1; +} + +int +mpd_realloc_dyn(mpd_t *result, mpd_ssize_t size, uint32_t *status) +{ + uint8_t err = 0; + + result->data = mpd_realloc(result->data, size, sizeof *result->data, &err); + if (!err) { + result->alloc = size; + } + else if (size > 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; +} + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/memory.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/memory.h Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2008-2010 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 + + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/mpdecimal.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/mpdecimal.c Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,7925 @@ +/* + * Copyright (c) 2008-2010 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 +#include +#include +#include +#include +#include "basearith.h" +#include "bits.h" +#include "convolute.h" +#include "crt.h" +#include "errno.h" +#include "memory.h" +#include "typearith.h" +#include "umodarith.h" +#include "mptest.h" +#include "mptypes.h" + +#ifdef PPRO + #if defined(_MSC_VER) + #include + #pragma fenv_access(on) + #elif !defined(__OpenBSD__) && !defined(__NetBSD__) + /* C99 */ + #include + #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[17]) { + if (word < mpd_pow10[16]) { + return (word < mpd_pow10[15]) ? 15 : 16; + } + return 17; + } + + return (word < mpd_pow10[18]) ? 18 : 19; +} +#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); + } +} + +/* + * Update the memory size for the coefficient. Existing data up to size is + * left untouched. + * + * Error handling: When relloc fails, result->data will still be a valid pointer + * to the old memory area of size result->len. If the requested size is less than + * result->len, we can continue normally, so we treat the failure as a soft error. + * If the requested size is greater than the old area, MPD_Malloc_error is + * set and the result will be a NaN. + */ +ALWAYS_INLINE int +mpd_qresize(mpd_t *result, mpd_ssize_t size, 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 (size > result->alloc) { + return mpd_switch_to_dyn(result, size, status); + } + } + else if (size != result->alloc && size >= MPD_MINALLOC) { + return mpd_realloc_dyn(result, size, 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 size, 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 (size > result->alloc) { + return mpd_switch_to_dyn_zero(result, size, status); + } + } + else if (size != result->alloc && size >= MPD_MINALLOC) { + if (!mpd_realloc_dyn(result, size, status)) { + return 0; + } + } + + mpd_uint_zero(result->data, size); + + 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 size, mpd_context_t *ctx) +{ + uint32_t status = 0; + if (!mpd_qresize(result, size, &status)) { + mpd_addstatus_raise(ctx, status); + return 0; + } + return 1; +} + +int +mpd_resize_zero(mpd_t *result, mpd_ssize_t size, mpd_context_t *ctx) +{ + uint32_t status = 0; + if (!mpd_qresize_zero(result, size, &status)) { + mpd_addstatus_raise(ctx, status); + return 0; + } + return 1; +} + + +/******************************************************************************/ +/* Set attributes of a decimal */ +/******************************************************************************/ + +/* Set digits. result->len is assumed to be correct. */ +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)); +} + +/* Make a work context */ +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. */ +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)) { + 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) { + *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, + * so shift=emin-prec+1-exp > digits-prec, so digits-shift < prec. + * [ACL2 proof: checkexp-1] */ + 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; + } + } + } + } +} + +/* 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; +} + +/* Two word initial approximations for ln(10) */ +#ifdef CONFIG_64 +#if MPD_RDIGITS != 19 + #error "mpdecimal.c: MPD_RDIGITS must be 19." +#endif +static mpd_uint_t mpd_ln10_data[MPD_MINALLOC_MAX] = { + 179914546843642076, 2302585092994045684 +}; +static mpd_uint_t mpd_ln10_init[2] = { + 179914546843642076, 2302585092994045684 +}; +#else +#if MPD_RDIGITS != 9 + #error "mpdecimal.c: MPD_RDIGITS must be 9." +#endif +static mpd_uint_t mpd_ln10_data[MPD_MINALLOC_MAX] = {299404568, 230258509}; +static mpd_uint_t mpd_ln10_init[2] = {299404568, 230258509}; +#endif +/* mpd_ln10 is cached in order to speed up computations */ +mpd_t mpd_ln10 = {MPD_STATIC|MPD_STATIC_DATA, -(2*MPD_RDIGITS-1), + 2*MPD_RDIGITS, 2, MPD_MINALLOC_MAX, mpd_ln10_data}; + +static void +mpd_reset_ln10(void) +{ + if (mpd_isdynamic_data(&mpd_ln10)) { + mpd_free(mpd_ln10.data); + } + mpd_ln10.data = mpd_ln10_data; + mpd_ln10_data[0] = mpd_ln10_init[0]; + mpd_ln10_data[1] = mpd_ln10_init[1]; + mpd_ln10.flags = MPD_STATIC|MPD_STATIC_DATA; + mpd_ln10.exp = -(2*MPD_RDIGITS-1); + mpd_ln10.digits = 2*MPD_RDIGITS; + mpd_ln10.len = 2; + mpd_ln10.alloc = MPD_MINALLOC_MAX; +} + +/* + * Initializes or updates mpd_ln10. If mpd_ln10 is cached and has exactly the + * requested precision, the function returns. If the cached precision is greater + * than the requested precision, mpd_ln10 is shifted to the requested precision. + * + * The function can fail with MPD_Malloc_error. + */ +void +mpd_update_ln10(mpd_ssize_t maxprec, 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]; + int i; + + if (mpd_isspecial(&mpd_ln10)) { + mpd_reset_ln10(); + } + + if (mpd_ln10.digits > maxprec) { + /* shift to smaller cannot fail */ + mpd_qshiftr_inplace(&mpd_ln10, mpd_ln10.digits-maxprec); + mpd_ln10.exp = -(mpd_ln10.digits-1); + return; + } + else if (mpd_ln10.digits == maxprec) { + return; + } + + mpd_maxcontext(&maxcontext); + mpd_maxcontext(&varcontext); + varcontext.round = MPD_ROUND_TRUNC; + + i = ln_schedule_prec(klist, maxprec+2, mpd_ln10.digits); + for (; i >= 0; i--) { + varcontext.prec = 2*klist[i]+3; + mpd_ln10.flags ^= MPD_NEG; + _mpd_qexp(&tmp, &mpd_ln10, &varcontext, status); + mpd_ln10.flags ^= MPD_NEG; + mpd_qmul(&tmp, &static10, &tmp, &varcontext, status); + mpd_qsub(&tmp, &tmp, &one, &maxcontext, status); + mpd_qadd(&mpd_ln10, &mpd_ln10, &tmp, &maxcontext, status); + if (mpd_isspecial(&mpd_ln10)) { + break; + } + } + + mpd_del(&tmp); + varcontext.prec = maxprec; + varcontext.round = MPD_ROUND_HALF_EVEN; + mpd_qfinalize(&mpd_ln10, &varcontext, 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; + } + 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_update_ln10(maxprec+2, status); + mpd_qmul_ssize(&tmp, &mpd_ln10, 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_maxcontext(&workctx); + workctx.prec = ctx->prec + 3; + _mpd_qln(result, a, &workctx, status); + mpd_update_ln10(workctx.prec, status); + + workctx = *ctx; + workctx.round = MPD_ROUND_HALF_EVEN; + _mpd_qdiv(NO_IDEAL_EXP, result, result, &mpd_ln10, &workctx, status); +} + +/* 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); + + 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 = ONE_UM<>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 + cw = _control87(0, 0); + _control87((_RC_CHOP|_PC_64), (_MCW_RC|_MCW_PC)); +#else + cw = _mpd_get_control87(); + _mpd_set_control87(cw|0x780); +#endif + return cw; +} + +void +mpd_restore_fenv(unsigned int cw) +{ +#ifdef _MSC_VER + _control87(cw, (_MCW_RC|_MCW_PC)); +#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); + + 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; + } + 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 (mod->digits+mod->exp > ctx->prec) { + mpd_seterror(result, MPD_Invalid_operation, status); + 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); +} + +/* + * 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. + */ +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 (exp > MPD_MAX_EMAX || exp < MPD_MIN_ETINY) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + if (mpd_iszero(a)) { + _settriple(result, mpd_sign(a), 0, exp); + return; + } + + expdiff = a->exp - exp; + if (expdiff >= 0) { + shift = expdiff; + 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; + } +} + +/* Round to an integer according to 'action' and ctx->round. */ +enum {TO_INT_EXACT, TO_INT_SILENT, TO_INT_TRUNC, TO_INT_FLOOR, TO_INT_CEIL}; +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; + } + } + } + else if (action == TO_INT_FLOOR) { + if (rnd && mpd_isnegative(result)) { + _mpd_qsub(result, result, &one, ctx, status); + } + } + else if (action == TO_INT_CEIL) { + if (rnd && mpd_ispositive(result)) { + _mpd_qadd(result, result, &one, ctx, status); + } + } +} + +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) +{ + (void)_mpd_qround_to_integral(TO_INT_FLOOR, result, a, ctx, status); +} + +void +mpd_qceil(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + (void)_mpd_qround_to_integral(TO_INT_CEIL, result, a, ctx, 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 */ +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; + if (result == a) { + if ((v = mpd_qncopy(a)) == NULL) { /* GCOV_NOT_REACHED */ + mpd_seterror(result, MPD_Malloc_error, status); /* GCOV_NOT_REACHED */ + goto finish; /* GCOV_NOT_REACHED */ + } + } + + 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); + } + +finish: + mpd_del(&s); + mpd_del(&t); + if (v != &vtmp) mpd_del(v); + 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("_mpd_barrett_divmod: k > 2 in correcting loop"); /* GCOV_NOT_REACHED */ + abort(); /* 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); +} + + +/*********************************************************************/ +/* Testcases for Newton Division */ +/*********************************************************************/ + +static void +_mpd_qtest_newtondiv(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; + } + } + + + { + 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); +} + +static void +mpd_qtest_newtondiv(mpd_t *q, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + _mpd_qtest_newtondiv(SET_IDEAL_EXP, q, a, b, ctx, status); +} + +static void +_mpd_qtest_barrett_divmod(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; + } + } + + _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; /* GCOV_NOT_REACHED */ + goto nanresult; /* GCOV_NOT_REACHED */ + } + + 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; +} + +static void +mpd_qtest_newtondivint(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_qtest_barrett_divmod(q, &r, a, b, ctx, status); + mpd_del(&r); + mpd_qfinalize(q, ctx, status); +} + +static void +mpd_qtest_newtonrem(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_qtest_barrett_divmod(&q, r, a, b, ctx, status); + mpd_del(&q); + mpd_qfinalize(r, ctx, status); +} + +static void +mpd_qtest_newtondivmod(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_qtest_barrett_divmod(q, r, a, b, ctx, status); + mpd_qfinalize(q, ctx, status); + mpd_qfinalize(r, ctx, status); +} + +void +mpd_test_newtondiv(mpd_t *q, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qtest_newtondiv(q, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_test_newtondivint(mpd_t *q, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qtest_newtondivint(q, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_test_newtonrem(mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qtest_newtonrem(r, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_test_newtondivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qtest_newtondivmod(q, r, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/mpdecimal.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/mpdecimal.h Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,790 @@ +/* + * Copyright (c) 2008-2010 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 +#include +#include +#include +#include + +#ifdef _MSC_VER + #include "vccompat.h" + #ifndef UNUSED + #define UNUSED + #endif + #define EXTINLINE extern inline +#else + #ifdef HAVE_STDINT_H + #include + #endif + #ifdef HAVE_INTTYPES_H + #include + #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) */ +/* 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) */ +/* 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_maxcontext_plus(mpd_context_t *workctx, const 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; +extern mpd_t mpd_ln10; + + +/******************************************************************************/ +/* 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); +int mpd_parse_fmt_str(mpd_spec_t *spec, const char *fmt, int caps); +char * mpd_qformat_spec(const mpd_t *dec, 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_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_update_ln10(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 */ + + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/mpsignal.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/mpsignal.c Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,964 @@ +/* + * Copyright (c) 2008-2010 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" + + +char * +mpd_format(const mpd_t *dec, const char *fmt, mpd_context_t *ctx) +{ + char *ret; + uint32_t status = 0; + ret = mpd_qformat(dec, fmt, ctx, &status); + mpd_addstatus_raise(ctx, status); + return ret; +} + +void +mpd_import_u16(mpd_t *result, const uint16_t *srcdata, size_t srclen, + uint8_t srcsign, uint32_t base, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qimport_u16(result, srcdata, srclen, srcsign, base, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_import_u32(mpd_t *result, const uint32_t *srcdata, size_t srclen, + uint8_t srcsign, uint32_t base, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qimport_u32(result, srcdata, srclen, srcsign, base, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +size_t +mpd_export_u16(uint16_t *rdata, size_t rlen, uint32_t base, const mpd_t *src, + mpd_context_t *ctx) +{ + size_t n; + uint32_t status = 0; + n = mpd_qexport_u16(rdata, rlen, base, src, &status); + mpd_addstatus_raise(ctx, status); + return n; +} + +size_t +mpd_export_u32(uint32_t *rdata, size_t rlen, uint32_t base, const mpd_t *src, + mpd_context_t *ctx) +{ + size_t n; + uint32_t status = 0; + n = mpd_qexport_u32(rdata, rlen, base, src, &status); + mpd_addstatus_raise(ctx, status); + return n; +} + +void +mpd_finalize(mpd_t *result, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qfinalize(result, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +int +mpd_check_nan(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + if (mpd_qcheck_nan(result, a, ctx, &status)) { + mpd_addstatus_raise(ctx, status); + return 1; + } + return 0; +} + +int +mpd_check_nans(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + if (mpd_qcheck_nans(result, a, b, ctx, &status)) { + mpd_addstatus_raise(ctx, status); + return 1; + } + return 0; +} + +void +mpd_set_string(mpd_t *result, const char *s, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qset_string(result, s, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_maxcoeff(mpd_t *result, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qmaxcoeff(result, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +/* set static mpd from signed integer */ +void +mpd_sset_ssize(mpd_t *result, mpd_ssize_t a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qsset_ssize(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_sset_i32(mpd_t *result, int32_t a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qsset_i32(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +#ifdef CONFIG_64 +void +mpd_sset_i64(mpd_t *result, int64_t a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qsset_i64(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} +#endif + +/* set static mpd from unsigned integer */ +void +mpd_sset_uint(mpd_t *result, mpd_uint_t a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qsset_uint(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_sset_u32(mpd_t *result, uint32_t a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qsset_u32(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +#ifdef CONFIG_64 +void +mpd_sset_u64(mpd_t *result, uint64_t a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qsset_u64(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} +#endif + +/* set mpd from signed integer */ +void +mpd_set_ssize(mpd_t *result, mpd_ssize_t a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qset_ssize(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_set_i32(mpd_t *result, int32_t a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qset_i32(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +#ifndef LEGACY_COMPILER +void +mpd_set_i64(mpd_t *result, int64_t a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qset_i64(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} +#endif + +/* set mpd from unsigned integer */ +void +mpd_set_uint(mpd_t *result, mpd_uint_t a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qset_uint(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_set_u32(mpd_t *result, uint32_t a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qset_u32(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +#ifndef LEGACY_COMPILER +void +mpd_set_u64(mpd_t *result, uint64_t a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qset_u64(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} +#endif + +/* convert mpd to signed integer */ +mpd_ssize_t +mpd_get_ssize(const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_ssize_t ret; + + ret = mpd_qget_ssize(a, &status); + mpd_addstatus_raise(ctx, status); + return ret; +} + +#ifdef CONFIG_32 +int32_t +mpd_get_i32(const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + int32_t ret; + + ret = mpd_qget_i32(a, &status); + mpd_addstatus_raise(ctx, status); + return ret; +} +#else +int64_t +mpd_get_i64(const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + int64_t ret; + + ret = mpd_qget_i64(a, &status); + mpd_addstatus_raise(ctx, status); + return ret; +} +#endif + +mpd_uint_t +mpd_get_uint(const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_uint_t ret; + + ret = mpd_qget_uint(a, &status); + mpd_addstatus_raise(ctx, status); + return ret; +} + +mpd_uint_t +mpd_abs_uint(const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_uint_t ret; + + ret = mpd_qabs_uint(a, &status); + mpd_addstatus_raise(ctx, status); + return ret; +} + +#ifdef CONFIG_32 +uint32_t +mpd_get_u32(const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + uint32_t ret; + + ret = mpd_qget_u32(a, &status); + mpd_addstatus_raise(ctx, status); + return ret; +} +#else +uint64_t +mpd_get_u64(const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + uint64_t ret; + + ret = mpd_qget_u64(a, &status); + mpd_addstatus_raise(ctx, status); + return ret; +} +#endif + +void +mpd_and(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qand(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_copy(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + if (!mpd_qcopy(result, a, &status)) { + mpd_addstatus_raise(ctx, status); + } +} + +void +mpd_canonical(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) +{ + mpd_copy(result, a, ctx); +} + +void +mpd_copy_abs(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + if (!mpd_qcopy_abs(result, a, &status)) { + mpd_addstatus_raise(ctx, status); + } +} + +void +mpd_copy_negate(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + if (!mpd_qcopy_negate(result, a, &status)) { + mpd_addstatus_raise(ctx, status); + } +} + +void +mpd_copy_sign(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + if (!mpd_qcopy_sign(result, a, b, &status)) { + mpd_addstatus_raise(ctx, status); + } +} + +void +mpd_invert(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qinvert(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_logb(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qlogb(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_or(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qor(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_rotate(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qrotate(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_scaleb(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qscaleb(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_shiftl(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qshiftl(result, a, n, &status); + mpd_addstatus_raise(ctx, status); +} + +mpd_uint_t +mpd_shiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_uint_t rnd; + + rnd = mpd_qshiftr(result, a, n, &status); + mpd_addstatus_raise(ctx, status); + return rnd; +} + +void +mpd_shiftn(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qshiftn(result, a, n, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_shift(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qshift(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_xor(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qxor(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_abs(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qabs(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +int +mpd_cmp(const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + int c; + c = mpd_qcmp(a, b, &status); + mpd_addstatus_raise(ctx, status); + return c; +} + +int +mpd_compare(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + int c; + c = mpd_qcompare(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); + return c; +} + +int +mpd_compare_signal(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + int c; + c = mpd_qcompare_signal(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); + return c; +} + +void +mpd_add(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qadd(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_sub(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qsub(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_add_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qadd_ssize(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_add_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qadd_i32(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +#ifdef CONFIG_64 +void +mpd_add_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qadd_i64(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} +#endif + +void +mpd_add_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qadd_uint(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_add_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qadd_u32(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +#ifdef CONFIG_64 +void +mpd_add_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qadd_u64(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} +#endif + +void +mpd_sub_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qsub_ssize(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_sub_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qsub_i32(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +#ifdef CONFIG_64 +void +mpd_sub_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qsub_i64(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} +#endif + +void +mpd_sub_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qsub_uint(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_sub_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qsub_u32(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +#ifdef CONFIG_64 +void +mpd_sub_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qsub_u64(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} +#endif + +void +mpd_div(mpd_t *q, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qdiv(q, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_div_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qdiv_ssize(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_div_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qdiv_i32(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +#ifdef CONFIG_64 +void +mpd_div_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qdiv_i64(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} +#endif + +void +mpd_div_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qdiv_uint(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_div_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qdiv_u32(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +#ifdef CONFIG_64 +void +mpd_div_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qdiv_u64(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} +#endif + +void +mpd_divmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qdivmod(q, r, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_divint(mpd_t *q, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qdivint(q, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_exp(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qexp(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_fma(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c, + mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qfma(result, a, b, c, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_ln(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qln(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_log10(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qlog10(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_max(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qmax(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_max_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qmax_mag(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_min(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qmin(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_min_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qmin_mag(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_minus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qminus(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_mul(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qmul(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_mul_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qmul_ssize(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_mul_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qmul_i32(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +#ifdef CONFIG_64 +void +mpd_mul_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qmul_i64(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} +#endif + +void +mpd_mul_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qmul_uint(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_mul_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qmul_u32(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +#ifdef CONFIG_64 +void +mpd_mul_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qmul_u64(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} +#endif + +void +mpd_next_minus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qnext_minus(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_next_plus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qnext_plus(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_next_toward(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qnext_toward(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_plus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qplus(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_pow(mpd_t *result, const mpd_t *base, const mpd_t *exp, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qpow(result, base, exp, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_powmod(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_t *mod, + mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qpowmod(result, base, exp, mod, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_quantize(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qquantize(result, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_rescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qrescale(result, a, exp, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_reduce(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qreduce(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_rem(mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qrem(r, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_rem_near(mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qrem_near(r, a, b, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_round_to_intx(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qround_to_intx(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_round_to_int(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qround_to_int(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_trunc(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qtrunc(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_floor(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qfloor(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_ceil(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qceil(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_sqrt(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qsqrt(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + +void +mpd_invroot(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) +{ + uint32_t status = 0; + mpd_qinvroot(result, a, ctx, &status); + mpd_addstatus_raise(ctx, status); +} + + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/mptest.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/mptest.h Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2008-2010 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 MPTEST_H +#define MPTEST_H + + +#include "mpdecimal.h" + + +/* newton division undergoes the same rigorous tests as standard division */ +void mpd_test_newtondiv(mpd_t *q, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_test_newtondivint(mpd_t *q, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_test_newtonrem(mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); +void mpd_test_newtondivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); + +/* fenv */ +unsigned int mpd_set_fenv(void); +void mpd_restore_fenv(unsigned int); + +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 *_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 *_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); + + +#endif + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/mptypes.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/mptypes.h Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2008-2010 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 MPTYPES_H +#define MPTYPES_H + +#if defined(_MSC_VER) + #define PRIi64 "lld" + #define PRIu64 "llu" + #define PRIi32 "d" + #define PRIu32 "u" +#endif + +#if defined(CONFIG_64) + #if defined(_MSC_VER) || defined(__OpenBSD__) || defined(__APPLE__) + #define PRI_mpd_uint_t "llu" + #define PRI_mpd_size_t "llu" + #define PRI_mpd_ssize_t "lld" + #else + #define PRI_mpd_uint_t "lu" + #define PRI_mpd_size_t "lu" + #define PRI_mpd_ssize_t "ld" + #endif + #define PRI_time_t "ld" + #define CONV_mpd_ssize_t "L" + #define ONE_UM 1ULL +#elif defined(CONFIG_32) + #define PRI_mpd_uint_t "u" + #define PRI_mpd_size_t "u" + #define PRI_mpd_ssize_t "d" + #if defined (__OpenBSD__) + #undef PRI_mpd_size_t + #define PRI_mpd_size_t "lu" + #define PRI_time_t "d" + #elif defined(__FreeBSD__) + #if defined(__x86_64__) + #define PRI_time_t "ld" + #else + #define PRI_time_t "d" + #endif + #else + #define PRI_time_t "ld" + #endif + #if MPD_SSIZE_MAX != INT_MAX + #error "define CONV_mpd_ssize_t" + #endif + #define CONV_mpd_ssize_t "i" + #define ONE_UM 1UL +#else + #error "define CONFIG_64 or CONFIG_32" +#endif + +#endif + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/numbertheory.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/numbertheory.c Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2008-2010 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 +#include +#include "bits.h" +#include "umodarith.h" +#include "numbertheory.h" + + +/* transform kernel */ +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]; + p = umod; + xi = (p-1) / n; + + if (sign == -1) + return POWMOD(r, (p-1-xi)); + else + return POWMOD(r, xi); +} + +/* initialize transform parameters */ +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, imag, 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); + imag = _mpd_getkernel(4, -sign, modnum); + + tparams->modnum = modnum; + tparams->modulus = umod; + tparams->imag = imag; + tparams->kernel = kernel; + + 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); +} + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/numbertheory.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/numbertheory.h Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2008-2010 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 imag; + 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 + + diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/python/bench.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/python/bench.py Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,114 @@ +#!/usr/bin/env python + +# +# Copyright (C) 2001-2010 Python Software Foundation. All Rights Reserved. +# Modified and extended by Stefan Krah. +# + + +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) diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/python/deccheck.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/python/deccheck.py Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,1061 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2008-2010 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__', '__complex__', '__copy__', '__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' + ), + # 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',), + # Special: + 'special': ('context.__reduce_ex__', 'context.create_decimal_from_float') +} + +# Functions that require a restricted exponent range for reasonable runtimes. +UnaryRestricted = [ + '__int__', '__long__', '__trunc__', 'to_integral', 'to_integral_value', + 'to_integral_value' +] + +BinaryRestricted = ['__round__'] + + +# ====================================================================== +# 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, +} + +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, ctx=C.getcontext()): + """Initialization is from the C context""" + self.c = ctx + self.p = P.getcontext() + 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 signal == C.FloatOperation: + continue + 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) + 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.DecClamped or \ + context.c._flags & C.DecUnderflow: + # 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 un_resolve_ulp(self, t): + """Check if results of _decimal's exp, ln and log10 functions are + within the allowed ulp ranges. This function is only called if + context.c._allcr is 0.""" + # 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 + + # op might not be a Decimal for context methods. + op = P.Decimal(t.pop[0]) + exact = getattr(op, t.funcname)(context=self.maxctx) + + # _decimal's rounded result + rounded = P.Decimal(t.cresults[0]) + + self.ulpdiff += 1 + return self.check_ulpdiff(exact, rounded) + + 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 exp(self, t): + """Calls the resolve function if correct rounding is switched off.""" + if context.c._allcr: + return False + return self.un_resolve_ulp(t) + + def log10(self, t): + """Calls the resolve function if correct rounding is switched off.""" + if context.c._allcr: + return False + return self.un_resolve_ulp(t) + + def ln(self, t): + """Calls the resolve function if correct rounding is switched off.""" + if context.c._allcr: + return False + return self.un_resolve_ulp(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.""" + 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, 9999, itr): + fmt1 = rand_format(chr(random.randrange(32, 128))) + 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(prec, exprange): + c = Context() + c.Emax = random.randrange(1, exprange+1) + c.Emin = random.randrange(-exprange, 0) + c.prec = random.randrange(1, c.Emax+1) + c.clamp = random.randrange(2) + 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(prec, 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_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) diff -r 203407036e46 -r 49433f35a5f8 Modules/_decimal/python/failapi.diff --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/python/failapi.diff Sat Jun 18 13:21:59 2011 +0200 @@ -0,0 +1,1699 @@ +--- _decimal.c 2011-06-15 13:29:31.000000000 +0200 ++++ _decimal_failapi.c 2011-06-15 13:31:34.000000000 +0200 +@@ -138,6 +138,103 @@ + /* Top level Exception; inherits from ArithmeticError */ + static PyObject *DecimalException = NULL; + ++/* FailAPI */ ++static PyObject *FailAPIException = NULL; ++ ++static unsigned long counter = 0; ++static unsigned long apicalls = 0; ++static unsigned long failpoint = 0; ++static Py_complex _cfail = {-1, -1}; ++ ++#define FFUNC(ret, funcall) (\ ++ (++counter, ++apicalls == failpoint) \ ++ ? (PyErr_SetString(FailAPIException, "failapi"), ret) \ ++ : funcall) ++ ++#define FFUNC_NOEX(ret, funcall) (\ ++ (++counter, ++apicalls == failpoint) ? ret : funcall) ++ ++#define IFUNC(funcall) (\ ++ (++counter, ++apicalls == failpoint) \ ++ ? (PyErr_SetString(FailAPIException, "failapi"), -1) \ ++ : funcall) ++ ++#define PFUNC(funcall) (\ ++ (++counter, ++apicalls == failpoint) \ ++ ? (PyErr_SetString(FailAPIException, "failapi"), NULL) \ ++ : funcall) ++ ++#define PFUNC_NOEX(funcall) (\ ++ (++counter, ++apicalls == failpoint) ? NULL : funcall) ++ ++#define SFUNC(func, result, ...) do { \ ++ if (++counter, ++apicalls == failpoint) \ ++ mpd_seterror(result, MPD_Malloc_error, &status); \ ++ else \ ++ func(result, __VA_ARGS__); \ ++ } while (0) ++ ++static PyObject * ++get_failpoint(PyObject *self UNUSED, PyObject *dummy UNUSED) ++{ ++ return Py_BuildValue("k", failpoint); ++} ++ ++static PyObject * ++get_apicalls(PyObject *self UNUSED, PyObject *dummy UNUSED) ++{ ++ return Py_BuildValue("k", apicalls); ++} ++ ++static PyObject * ++set_failpoint(PyObject *self UNUSED, PyObject *value) ++{ ++ failpoint = PyLong_AsUnsignedLong(value); ++ if (PyErr_Occurred()) { ++ return NULL; /* GCOV_NOT_REACHED */ ++ } ++ Py_RETURN_NONE; ++} ++ ++static PyObject * ++set_apicalls(PyObject *self UNUSED, PyObject *value) ++{ ++ apicalls = PyLong_AsUnsignedLong(value); ++ if (PyErr_Occurred()) { ++ return NULL; /* GCOV_NOT_REACHED */ ++ } ++ Py_RETURN_NONE; ++} ++ ++void * ++mpd_malloc_fail(size_t size) ++{ ++ ++ if (++apicalls == failpoint) { ++ return NULL; ++ } ++ return PyMem_Malloc(size); ++} ++ ++void * ++mpd_calloc_fail(size_t nmemb, size_t size) ++{ ++ if (++apicalls == failpoint) { ++ return NULL; ++ } ++ return mpd_callocfunc_em(nmemb, size); ++} ++ ++void * ++mpd_realloc_fail(void *ptr, size_t size) ++{ ++ if (++apicalls == failpoint) { ++ return NULL; ++ } ++ return PyMem_Realloc(ptr, size); ++} ++/* End FailAPI */ ++ + /* Exceptions that correspond to IEEE signals; inherit from DecimalException */ + static DecCondMap signal_map[] = { + {"InvalidOperation", "decimal.InvalidOperation", MPD_IEEE_Invalid_operation, NULL}, +@@ -228,7 +325,7 @@ + static int + runtime_error_int(const char *mesg) + { +- PyErr_SetString(PyExc_RuntimeError, mesg); ++ PyErr_SetString(FailAPIException, mesg); + return -1; + } + #define INTERNAL_ERROR_INT(funcname) \ +@@ -237,7 +334,7 @@ + static PyObject * + runtime_error_ptr(const char *mesg) + { +- PyErr_SetString(PyExc_RuntimeError, mesg); ++ PyErr_SetString(FailAPIException, mesg); + return NULL; + } + #define INTERNAL_ERROR_PTR(funcname) \ +@@ -284,20 +381,20 @@ + PyObject *list; + DecCondMap *cm; + +- if ((list = PyList_New(0)) == NULL) { ++ if ((list = PFUNC(PyList_New(0))) == NULL) { + return NULL; + } + + for (cm = cond_map; cm->name != NULL; cm++) { + if (flags&cm->mpd_cond) { +- if (PyList_Append(list, cm->dec_cond) < 0) { ++ if (IFUNC(PyList_Append(list, cm->dec_cond)) < 0) { + goto error; + } + } + } + for (cm = signal_map+1; cm->name != NULL; cm++) { + if (flags&cm->mpd_cond) { +- if (PyList_Append(list, cm->dec_cond) < 0) { ++ if (IFUNC(PyList_Append(list, cm->dec_cond)) < 0) { + goto error; + } + } +@@ -316,13 +413,13 @@ + PyObject *list; + DecCondMap *cm; + +- if ((list = PyList_New(0)) == NULL) { ++ if ((list = PFUNC(PyList_New(0))) == NULL) { + return NULL; + } + + for (cm = signal_map; cm->name != NULL; cm++) { + if (flags&cm->mpd_cond) { +- if (PyList_Append(list, cm->dec_cond) < 0) { ++ if (IFUNC(PyList_Append(list, cm->dec_cond)) < 0) { + goto error; + } + } +@@ -376,7 +473,7 @@ + } + + for (cm = signal_map; cm->name != NULL; cm++) { +- if ((b = PyDict_GetItemWithError(val, cm->dec_cond)) == NULL) { ++ if ((b = PFUNC(PyDict_GetItemWithError(val, cm->dec_cond))) == NULL) { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, + "invalid signal dict."); +@@ -384,7 +481,7 @@ + return UINT32_MAX; + } + +- if ((x = PyObject_IsTrue(b)) < 0) { ++ if ((x = IFUNC(PyObject_IsTrue(b))) < 0) { + return UINT32_MAX; + } + if (x == 1) { +@@ -400,7 +497,7 @@ + { + long x; + +- x = PyLong_AsLong(v); ++ x = IFUNC(PyLong_AsLong(v)); + if (PyErr_Occurred()) { + return UINT32_MAX; + } +@@ -423,7 +520,7 @@ + return MPD_SSIZE_MAX; + } + +- x = _PyLong_AsMpdSsize(v); ++ x = IFUNC(_PyLong_AsMpdSsize(v)); + if (PyErr_Occurred()) { + return MPD_SSIZE_MAX; + } +@@ -436,6 +533,12 @@ + { + mpd_context_t *ctx = CTX(context); + ++ if (status&MPD_Malloc_error) { ++ /* Translate MemoryError to FailAPIException */ ++ PyErr_SetString(FailAPIException, "failapi"); ++ return 1; ++ } ++ + ctx->status |= status; + if (ctx->traps&status) { + PyObject *ex, *siglist; +@@ -464,7 +567,7 @@ + static int + signaldict_init(PyObject *self, PyObject *args, PyObject *kwds) + { +- if (PyDict_Type.tp_init(self, args, kwds) < 0) { ++ if (IFUNC(PyDict_Type.tp_init(self, args, kwds)) < 0) { + return -1; + } + +@@ -484,7 +587,7 @@ + + for (cm = signal_map; cm->name != NULL; cm++) { + b = (flags&cm->mpd_cond) ? Py_True : Py_False; +- if (PyDict_SetItem(self, cm->dec_cond, b) < 0) { ++ if (IFUNC(PyDict_SetItem(self, cm->dec_cond, b)) < 0) { + return -1; + } + } +@@ -500,7 +603,7 @@ + SdFlags(self) = 0; + + for (cm = signal_map; cm->name != NULL; cm++) { +- if (PyDict_SetItem(self, cm->dec_cond, Py_False) < 0) { ++ if (IFUNC(PyDict_SetItem(self, cm->dec_cond, Py_False)) < 0) { + return -1; + } + } +@@ -517,19 +620,19 @@ + return -1; + } + +- if ((x = PyObject_IsTrue(value)) < 0) { ++ if ((x = IFUNC(PyObject_IsTrue(value))) < 0) { + return -1; + } + if (x == 1) { + SdFlags(self) |= flag; +- if (PyDict_SetItem(self, key, Py_True) < 0) { ++ if (IFUNC(PyDict_SetItem(self, key, Py_True)) < 0) { + return -1; + } + return 0; + } + else { + SdFlags(self) &= ~flag; +- if (PyDict_SetItem(self, key, Py_False) < 0) { ++ if (IFUNC(PyDict_SetItem(self, key, Py_False)) < 0) { + return -1; + } + return 0; +@@ -542,7 +645,7 @@ + if (signaldict_update(self) < 0) { + return NULL; + } +- return PyObject_CallMethod((PyObject *)&PyDict_Type, name, "O", self); ++ return PFUNC(PyObject_CallMethod((PyObject *)&PyDict_Type, name, "O", self)); + } + + static PyObject * +@@ -558,7 +661,7 @@ + return NULL; + } + } +- return PyDict_Type.tp_richcompare(a, b, op); ++ return PFUNC(PyDict_Type.tp_richcompare(a, b, op)); + } + + static int +@@ -567,7 +670,7 @@ + if (signaldict_update(self) < 0) { + return -1; + } +- return PyDict_Contains(self, key); ++ return IFUNC(PyDict_Contains(self, key)); + } + + static PyObject * +@@ -576,25 +679,25 @@ + if (signaldict_update(self) < 0) { + return NULL; + } +- return PyDict_Copy(self); ++ return PFUNC(PyDict_Copy(self)); + } + + static PyObject * + signaldict_get(PyObject *self, PyObject *args) + { + PyObject *key = NULL, *failobj = NULL; +- if (!PyArg_ParseTuple(args, "O|O", &key, &failobj)) { ++ if (!FFUNC(0, PyArg_ParseTuple(args, "O|O", &key, &failobj))) { + return NULL; /* GCOV_NOT_REACHED (why?) */ + } + if (signaldict_update(self) < 0) { + return NULL; + } + if (failobj) { +- return PyObject_CallMethod((PyObject *)&PyDict_Type, "get", +- "OOO", self, key, failobj); ++ return PFUNC(PyObject_CallMethod((PyObject *)&PyDict_Type, "get", ++ "OOO", self, key, failobj)); + } +- return PyObject_CallMethod((PyObject *)&PyDict_Type, "get", +- "OO", self, key); ++ return PFUNC(PyObject_CallMethod((PyObject *)&PyDict_Type, "get", ++ "OO", self, key)); + } + + static PyObject * +@@ -604,8 +707,8 @@ + if (signaldict_update(self) < 0) { + return NULL; + } +- ret = PyDict_Contains(self, key); +- return ret < 0 ? NULL : PyBool_FromLong(ret); ++ ret = IFUNC(PyDict_Contains(self, key)); ++ return ret < 0 ? NULL : PFUNC(PyBool_FromLong(ret)); + } + + static PyObject * +@@ -614,7 +717,7 @@ + if (signaldict_update(self) < 0) { + return NULL; + } +- return PyDict_Items(self); ++ return PFUNC(PyDict_Items(self)); + } + + static PyObject * +@@ -623,7 +726,7 @@ + if (signaldict_update(self) < 0) { + return NULL; + } +- return PyDict_Type.tp_iter(self); ++ return PFUNC(PyDict_Type.tp_iter(self)); + } + + static PyObject * +@@ -632,7 +735,7 @@ + if (signaldict_update(self) < 0) { + return NULL; + } +- return PyDict_Keys(self); ++ return PFUNC(PyDict_Keys(self)); + } + + static Py_ssize_t +@@ -641,7 +744,7 @@ + if (signaldict_update(self) < 0) { + return -1; + } +- return PyDict_Type.tp_as_mapping->mp_length(self); ++ return IFUNC(PyDict_Type.tp_as_mapping->mp_length(self)); + } + + static int +@@ -659,7 +762,7 @@ + if (signaldict_update(self) < 0) { + return NULL; + } +- return PyDict_Type.tp_repr(self); ++ return PFUNC(PyDict_Type.tp_repr(self)); + } + + static PyObject * +@@ -685,7 +788,7 @@ + if (signaldict_update(self) < 0) { + return NULL; + } +- return PyDict_Type.tp_as_mapping->mp_subscript(self, key); ++ return PFUNC(PyDict_Type.tp_as_mapping->mp_subscript(self, key)); + } + + static PyObject * +@@ -694,7 +797,7 @@ + if (signaldict_update(self) < 0) { + return NULL; + } +- return PyDict_Values(self); ++ return PFUNC(PyDict_Values(self)); + } + + +@@ -781,7 +884,7 @@ + mpd_context_t *ctx; + + ctx = CTX(self); +- return Py_BuildValue(CONV_mpd_ssize_t, mpd_getprec(ctx)); ++ return PFUNC(Py_BuildValue(CONV_mpd_ssize_t, mpd_getprec(ctx))); + } + + static PyObject * +@@ -790,7 +893,7 @@ + mpd_context_t *ctx; + + ctx = CTX(self); +- return Py_BuildValue(CONV_mpd_ssize_t, mpd_getemax(ctx)); ++ return PFUNC(Py_BuildValue(CONV_mpd_ssize_t, mpd_getemax(ctx))); + } + + static PyObject * +@@ -799,7 +902,7 @@ + mpd_context_t *ctx; + + ctx = CTX(self); +- return Py_BuildValue(CONV_mpd_ssize_t, mpd_getemin(ctx)); ++ return PFUNC(Py_BuildValue(CONV_mpd_ssize_t, mpd_getemin(ctx))); + } + + static PyObject * +@@ -808,7 +911,7 @@ + mpd_context_t *ctx; + + ctx = CTX(self); +- return Py_BuildValue(CONV_mpd_ssize_t, mpd_etiny(ctx)); ++ return PFUNC(Py_BuildValue(CONV_mpd_ssize_t, mpd_etiny(ctx))); + } + + static PyObject * +@@ -817,7 +920,7 @@ + mpd_context_t *ctx; + + ctx = CTX(self); +- return Py_BuildValue(CONV_mpd_ssize_t, mpd_etop(ctx)); ++ return PFUNC(Py_BuildValue(CONV_mpd_ssize_t, mpd_etop(ctx))); + } + + static PyObject * +@@ -826,13 +929,13 @@ + mpd_context_t *ctx; + + ctx = CTX(self); +- return Py_BuildValue("i", mpd_getround(ctx)); ++ return PFUNC(Py_BuildValue("i", mpd_getround(ctx))); + } + + static PyObject * + context_getcapitals(PyObject *self, void *closure UNUSED) + { +- return Py_BuildValue("i", CtxCaps(self)); ++ return PFUNC(Py_BuildValue("i", CtxCaps(self))); + } + + static PyObject * +@@ -841,7 +944,7 @@ + mpd_context_t *ctx; + + ctx = CTX(self); +- return Py_BuildValue("i", mpd_gettraps(ctx)); ++ return PFUNC(Py_BuildValue("i", mpd_gettraps(ctx))); + } + + static PyObject * +@@ -850,7 +953,7 @@ + mpd_context_t *ctx; + + ctx = CTX(self); +- return Py_BuildValue("i", mpd_getstatus(ctx)); ++ return PFUNC(Py_BuildValue("i", mpd_getstatus(ctx))); + } + + static PyObject * +@@ -859,7 +962,7 @@ + mpd_context_t *ctx; + + ctx = CTX(self); +- return Py_BuildValue("i", mpd_getclamp(ctx)); ++ return PFUNC(Py_BuildValue("i", mpd_getclamp(ctx))); + } + + static PyObject * +@@ -868,7 +971,7 @@ + mpd_context_t *ctx; + + ctx = CTX(self); +- return Py_BuildValue("i", mpd_getcr(ctx)); ++ return PFUNC(Py_BuildValue("i", mpd_getcr(ctx))); + } + + static int +@@ -1028,7 +1131,7 @@ + } + + ctx = CTX(self); +- if (!mpd_qsettraps(ctx, flags)) { ++ if (!FFUNC_NOEX(0, mpd_qsettraps(ctx, flags))) { + INTERNAL_ERROR_INT("context_settraps"); + } + +@@ -1047,7 +1150,7 @@ + } + + ctx = CTX(self); +- if (!mpd_qsettraps(ctx, flags)) { ++ if (!FFUNC_NOEX(0, mpd_qsettraps(ctx, flags))) { + INTERNAL_ERROR_INT("context_settraps_list"); + } + +@@ -1066,7 +1169,7 @@ + } + + ctx = CTX(self); +- if (!mpd_qsettraps(ctx, flags)) { ++ if (!FFUNC_NOEX(0, mpd_qsettraps(ctx, flags))) { + INTERNAL_ERROR_INT("context_settraps_dict"); + } + +@@ -1085,7 +1188,7 @@ + } + + ctx = CTX(self); +- if (!mpd_qsetstatus(ctx, flags)) { ++ if (!FFUNC_NOEX(0, mpd_qsetstatus(ctx, flags))) { + INTERNAL_ERROR_INT("context_setstatus"); + } + +@@ -1104,7 +1207,7 @@ + } + + ctx = CTX(self); +- if (!mpd_qsetstatus(ctx, flags)) { ++ if (!FFUNC_NOEX(0, mpd_qsetstatus(ctx, flags))) { + INTERNAL_ERROR_INT("context_setstatus_list"); + } + +@@ -1123,7 +1226,7 @@ + } + + ctx = CTX(self); +- if (!mpd_qsetstatus(ctx, flags)) { ++ if (!FFUNC_NOEX(0, mpd_qsetstatus(ctx, flags))) { + INTERNAL_ERROR_INT("context_setstatus_dict"); + } + +@@ -1252,18 +1355,18 @@ + PyDecContextObject *self = NULL; + mpd_context_t *ctx; + +- self = PyObject_New(PyDecContextObject, &PyDecContext_Type); ++ self = PFUNC(PyObject_New(PyDecContextObject, &PyDecContext_Type)); + if (self == NULL) { + return NULL; + } + +- self->traps = PyObject_CallObject((PyObject *)&PyDecSignalDict_Type, NULL); ++ self->traps = PFUNC(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); ++ self->flags = PFUNC(PyObject_CallObject((PyObject *)&PyDecSignalDict_Type, NULL)); + if (self->flags == NULL) { + Py_DECREF(self); + return NULL; +@@ -1306,7 +1409,7 @@ + int i; + + if (PyLong_Check(v)) { +- x = PyLong_AsLong(v); ++ x = IFUNC(PyLong_AsLong(v)); + if (PyErr_Occurred()) { + return -1; + } +@@ -1345,13 +1448,13 @@ + if (default_context_template) { + t = *CTX(default_context_template); + } +- if (!PyArg_ParseTupleAndKeywords( ++ if (!FFUNC(0, PyArg_ParseTupleAndKeywords( + args, kwds, + "|" CONV_mpd_ssize_t "O" CONV_mpd_ssize_t CONV_mpd_ssize_t "ii" + "OOi", kwlist, + &t.prec, &rounding, &t.emin, &t.emax, &capitals, &t.clamp, + &status, &traps, &t.allcr +- )) { ++ ))) { + return -1; + } + if (rounding != NULL) { +@@ -1423,32 +1526,32 @@ + ctx = CTX(self); + + cp = s; mem = FD_CTX_LEN; +- n = snprintf(cp, mem, ++ n = FFUNC_NOEX(-1, snprintf(cp, mem, + "Context(prec=%"PRI_mpd_ssize_t", rounding=%s, " + "Emin=%"PRI_mpd_ssize_t", Emax=%"PRI_mpd_ssize_t", " + "capitals=%d, clamp=%d, flags=", + ctx->prec, mpd_round_string[ctx->round], + ctx->emin, ctx->emax, +- self->capitals, ctx->clamp); ++ self->capitals, ctx->clamp)); + if (n < 0 || n >= mem) goto error; + cp += n; mem -= n; + +- n = mpd_lsnprint_signals(cp, mem, ctx->status, dec_signal_string); ++ n = FFUNC_NOEX(-1, mpd_lsnprint_signals(cp, mem, ctx->status, dec_signal_string)); + if (n < 0 || n >= mem) goto error; + cp += n; mem -= n; + +- n = snprintf(cp, mem, ", traps="); ++ n = FFUNC_NOEX(-1, snprintf(cp, mem, ", traps=")); + if (n < 0 || n >= mem) goto error; + cp += n; mem -= n; + +- n = mpd_lsnprint_signals(cp, mem, ctx->traps, dec_signal_string); ++ n = FFUNC_NOEX(-1, mpd_lsnprint_signals(cp, mem, ctx->traps, dec_signal_string)); + if (n < 0 || n >= mem) goto error; + cp += n; mem -= n; + +- n = snprintf(cp, mem, ")"); ++ n = FFUNC_NOEX(-1, snprintf(cp, mem, ")")); + if (n < 0 || n >= mem) goto error; + +- return PyUnicode_FromString(s); ++ return PFUNC(PyUnicode_FromString(s)); + + error: + INTERNAL_ERROR_PTR("context_repr"); +@@ -1498,7 +1601,7 @@ + goto error; + } + +- context = PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL); ++ context = PFUNC(PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL)); + if (context == NULL) { + return NULL; + } +@@ -1519,7 +1622,7 @@ + { + PyObject *copy; + +- copy = PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL); ++ copy = PFUNC(PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL)); + if (copy == NULL) { + return NULL; + } +@@ -1549,13 +1652,13 @@ + return NULL; + } + +- ret = Py_BuildValue( ++ ret = PFUNC(Py_BuildValue( + "O(" CONV_mpd_ssize_t "s" CONV_mpd_ssize_t CONV_mpd_ssize_t + "iiOO)", + 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); +@@ -1694,14 +1797,14 @@ + PyObject *dict = NULL; + PyObject *tl_context = NULL; + +- dict = PyThreadState_GetDict(); ++ dict = PFUNC_NOEX(PyThreadState_GetDict()); + if (dict == NULL) { +- PyErr_SetString(PyExc_RuntimeError, ++ PyErr_SetString(FailAPIException, + "cannot get thread state."); + return NULL; + } + +- tl_context = PyDict_GetItemWithError(dict, tls_context_key); ++ tl_context = PFUNC(PyDict_GetItemWithError(dict, tls_context_key)); + if (tl_context != NULL) { + /* We already have a thread local context and + * return a borrowed reference. */ +@@ -1717,7 +1820,7 @@ + if (tl_context == NULL) { + return NULL; + } +- if (PyDict_SetItem(dict, tls_context_key, tl_context) < 0) { ++ if (IFUNC(PyDict_SetItem(dict, tls_context_key, tl_context)) < 0) { + Py_DECREF(tl_context); + return NULL; + } +@@ -1764,9 +1867,9 @@ + + CONTEXT_CHECK(v); + +- dict = PyThreadState_GetDict(); ++ dict = PFUNC_NOEX(PyThreadState_GetDict()); + if (dict == NULL) { +- PyErr_SetString(PyExc_RuntimeError, ++ PyErr_SetString(FailAPIException, + "cannot get thread state."); + return NULL; + } +@@ -1784,7 +1887,7 @@ + Py_INCREF(v); + } + +- if (PyDict_SetItem(dict, tls_context_key, v) < 0) { ++ if (IFUNC(PyDict_SetItem(dict, tls_context_key, v)) < 0) { + Py_DECREF(v); + return NULL; + } +@@ -1806,13 +1909,13 @@ + + CURRENT_CONTEXT(global); + local = global; +- if (!PyArg_ParseTuple(args, "|O", &local)) { ++ if (!FFUNC(0, PyArg_ParseTuple(args, "|O", &local))) { + return NULL; + } + CONTEXT_CHECK_VA(local); + +- self = PyObject_New(PyDecContextManagerObject, +- &PyDecContextManager_Type); ++ self = PFUNC(PyObject_New(PyDecContextManagerObject, ++ &PyDecContextManager_Type)); + if (self == NULL) { + return NULL; + } +@@ -1860,6 +1963,19 @@ + + ret = PyDec_SetCurrentContext(NULL, self->global); + if (ret == NULL) { ++ /* This particular failpoint leaves the global state ++ undefined (global context is not restored, but the ++ exception is swallowed by the test suite). So we ++ make sure that the proper exception has occcurred, ++ restore the context and reraise the esception. */ ++ assert(PyErr_Occurred() == FailAPIException); ++ PyErr_Clear(); ++ ++ ret = PyDec_SetCurrentContext(NULL, self->global); ++ PyErr_SetString(FailAPIException, "failapi"); ++ ++ assert(ret != NULL); ++ Py_DECREF(ret); + return NULL; + } + Py_DECREF(ret); +@@ -1917,10 +2033,10 @@ + PyObject *dec; + + if (type == &PyDec_Type) { +- dec = (PyObject *)PyObject_New(PyDecObject, &PyDec_Type); ++ dec = (PyObject *)PFUNC(PyObject_New(PyDecObject, &PyDec_Type)); + } + else { +- dec = type->tp_alloc(type, 0); ++ dec = PFUNC(type->tp_alloc(type, 0)); + } + if (dec == NULL) { + return NULL; +@@ -1929,7 +2045,7 @@ + MPD(dec) = mpd_qnew(); + if (MPD(dec) == NULL) { + Py_DECREF(dec); +- PyErr_NoMemory(); ++ PyErr_SetString(FailAPIException, "failapi"); + return NULL; + } + +@@ -2011,14 +2127,14 @@ + { + char *s; + +- s = PyMem_Malloc(PyUnicode_GET_SIZE(u)+1); ++ s = PFUNC_NOEX(PyMem_Malloc(PyUnicode_GET_SIZE(u)+1)); + if (s == NULL) { +- PyErr_NoMemory(); ++ PyErr_SetString(FailAPIException, "failapi"); + return NULL; + } +- if (PyUnicode_EncodeDecimal(PyUnicode_AS_UNICODE(u), ++ if (IFUNC(PyUnicode_EncodeDecimal(PyUnicode_AS_UNICODE(u), + PyUnicode_GET_SIZE(u), +- s, NULL)) { ++ s, NULL))) { + PyMem_Free(s); + return NULL; + } +@@ -2046,8 +2162,8 @@ + + if (s != x || t != y) { + n = t-s; +- if ((y = PyMem_Malloc(n+1)) == NULL) { +- PyErr_NoMemory(); ++ if ((y = PFUNC_NOEX(PyMem_Malloc(n+1))) == NULL) { ++ PyErr_SetString(FailAPIException, "failapi"); + return NULL; + } + strncpy(y, s, n); +@@ -2139,7 +2255,7 @@ + return NULL; + } + +- mpd_qset_ssize(MPD(dec), v, CTX(context), &status); ++ SFUNC(mpd_qset_ssize, MPD(dec), v, CTX(context), &status); + if (dec_addstatus(context, status)) { + Py_DECREF(dec); + return NULL; +@@ -2162,7 +2278,7 @@ + + mpd_maxcontext(&maxctx); + +- mpd_qset_ssize(MPD(dec), v, &maxctx, &status); ++ SFUNC(mpd_qset_ssize, MPD(dec), v, &maxctx, &status); + if (dec_addstatus(context, status)) { + Py_DECREF(dec); + return NULL; +@@ -2299,7 +2415,7 @@ + return NULL; + } + +- x = PyFloat_AsDouble(v); ++ x = IFUNC(PyFloat_AsDouble(v)); + if (x == -1.0 && PyErr_Occurred()) { + return NULL; + } +@@ -2322,13 +2438,13 @@ + } + + /* absolute value of the float */ +- tmp = PyObject_CallMethod(v, "__abs__", NULL); ++ tmp = PFUNC(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); ++ n_d = PFUNC(PyObject_CallMethod(tmp, "as_integer_ratio", NULL)); + Py_DECREF(tmp); + if (n_d == NULL) { + return NULL; +@@ -2336,7 +2452,7 @@ + n = PyTuple_GET_ITEM(n_d, 0); + d = PyTuple_GET_ITEM(n_d, 1); + +- tmp = PyObject_CallMethod(d, "bit_length", NULL); ++ tmp = PFUNC(PyObject_CallMethod(d, "bit_length", NULL)); + if (tmp == NULL) { + Py_DECREF(n_d); + return NULL; +@@ -2358,14 +2474,14 @@ + d1 = mpd_qnew(); + if (d1 == NULL) { + Py_DECREF(dec); +- PyErr_NoMemory(); ++ PyErr_SetString(FailAPIException, "failapi"); + return NULL; + } + d2 = mpd_qnew(); + if (d2 == NULL) { + mpd_del(d1); + Py_DECREF(dec); +- PyErr_NoMemory(); ++ PyErr_SetString(FailAPIException, "failapi"); + return NULL; + } + +@@ -2444,7 +2560,7 @@ + "sign must be an integer with the value 0 or 1."); + goto error; + } +- sign = PyLong_AsLong(tmp); ++ sign = IFUNC(PyLong_AsLong(tmp)); + if (PyErr_Occurred()) { + goto error; + } +@@ -2500,15 +2616,15 @@ + tsize = PyTuple_Size(digits); + /* [sign][coeffdigits+1][E][-][expdigits+1]['\0'] */ + mem = 1 + tsize + 3 + MPD_EXPDIGITS + 2; +- cp = decstring = PyMem_Malloc(mem); ++ cp = decstring = PFUNC_NOEX(PyMem_Malloc(mem)); + if (decstring == NULL) { +- PyErr_NoMemory(); ++ PyErr_SetString(FailAPIException, "failapi"); + goto error; + } + +- n = snprintf(cp, mem, "%s", sign_special); ++ n = FFUNC(-1, snprintf(cp, mem, "%s", sign_special)); + if (n < 0 || n >= mem) { +- PyErr_SetString(PyExc_RuntimeError, ++ PyErr_SetString(FailAPIException, + "internal error in dec_sequence_as_str."); + goto error; + } +@@ -2525,7 +2641,7 @@ + "coefficient must be a tuple of digits."); + goto error; + } +- l = PyLong_AsLong(tmp); ++ l = IFUNC(PyLong_AsLong(tmp)); + if (PyErr_Occurred()) { + goto error; + } +@@ -2541,9 +2657,9 @@ + if (sign_special[1] == '\0') { + /* not a special number */ + *cp++ = 'E'; +- n = snprintf(cp, MPD_EXPDIGITS+1, "%" PRI_mpd_ssize_t, exp); ++ n = FFUNC(-1, snprintf(cp, MPD_EXPDIGITS+1, "%" PRI_mpd_ssize_t, exp)); + if (n < 0 || n >= MPD_EXPDIGITS+1) { +- PyErr_SetString(PyExc_RuntimeError, ++ PyErr_SetString(FailAPIException, + "internal error in dec_sequence_as_str."); + goto error; + } +@@ -2684,13 +2800,13 @@ + return NULL; + } + +- mpd_qcopy(MPD(result), MPD(v), &status); ++ SFUNC(mpd_qcopy, MPD(result), MPD(v), &status); + if (dec_addstatus(context, status)) { + Py_DECREF(result); + return NULL; + } + +- mpd_qfinalize(MPD(result), CTX(context), &status); ++ SFUNC(mpd_qfinalize, MPD(result), CTX(context), &status); + if (dec_addstatus(context, status)) { + Py_DECREF(result); + return NULL; +@@ -2791,7 +2907,7 @@ + PyObject *context; + + CURRENT_CONTEXT(context); +- if (!PyArg_ParseTuple(args, "|OO", &v, &context)) { ++ if (!FFUNC(0, PyArg_ParseTuple(args, "|OO", &v, &context))) { + return NULL; + } + CONTEXT_CHECK_VA(context); +@@ -2804,7 +2920,7 @@ + { + PyObject *v = NULL; + +- if (!PyArg_ParseTuple(args, "|O", &v)) { ++ if (!FFUNC(0, PyArg_ParseTuple(args, "|O", &v))) { + return NULL; + } + +@@ -2927,7 +3043,7 @@ + mpd_t *vv; + + /* v is not special, r is a rational */ +- tmp = PyObject_GetAttrString(r, "denominator"); ++ tmp = PFUNC(PyObject_GetAttrString(r, "denominator")); + if (tmp == NULL) { + return NULL; + } +@@ -2940,7 +3056,7 @@ + vv = mpd_qncopy(MPD(v)); + if (vv == NULL) { + Py_DECREF(denom); +- PyErr_NoMemory(); ++ PyErr_SetString(FailAPIException, "failapi"); + return NULL; + } + result = dec_alloc(); +@@ -2980,7 +3096,7 @@ + { + PyObject *tmp, *num; + +- tmp = PyObject_GetAttrString(r, "numerator"); ++ tmp = PFUNC(PyObject_GetAttrString(r, "numerator")); + if (tmp == NULL) { + return NULL; + } +@@ -3021,12 +3137,12 @@ + } + } + else if (PyComplex_Check(w) && (op == Py_EQ || op == Py_NE)) { +- Py_complex c = PyComplex_AsCComplex(w); ++ Py_complex c = FFUNC(_cfail, PyComplex_AsCComplex(w)); + if (PyErr_Occurred()) { + *wcmp = NULL; + } + else if (c.imag == 0.0) { +- PyObject *tmp = PyFloat_FromDouble(c.real); ++ PyObject *tmp = PFUNC(PyFloat_FromDouble(c.real)); + if (tmp == NULL) { + *wcmp = NULL; + } +@@ -3085,11 +3201,11 @@ + CURRENT_CONTEXT(c); + res = mpd_to_sci(MPD(dec), CtxCaps(c)); + if (res == NULL) { +- PyErr_NoMemory(); ++ PyErr_SetString(FailAPIException, "failapi"); + return NULL; + } + +- s = PyUnicode_FromString(res); ++ s = PFUNC(PyUnicode_FromString(res)); + mpd_free(res); + + return s; +@@ -3110,16 +3226,18 @@ + CURRENT_CONTEXT(c); + cp = mpd_to_sci(MPD(dec), CtxCaps(c)); + if (cp == NULL) { +- PyErr_NoMemory(); ++ PyErr_SetString(FailAPIException, "failapi"); + return NULL; + } + declen = strlen(cp); + + err = 0; ++ mpd_reallocfunc = mpd_realloc_fail; + cp = mpd_realloc(cp, (mpd_size_t)(declen+dtaglen+3), sizeof *cp, &err); ++ mpd_reallocfunc = PyMem_Realloc; + if (err) { + mpd_free(cp); +- PyErr_NoMemory(); ++ PyErr_SetString(FailAPIException, "failapi"); + return NULL; + } + +@@ -3129,7 +3247,7 @@ + cp[declen+dtaglen+1] = ')'; + cp[declen+dtaglen+2] = '\0'; + +- s = PyUnicode_FromString(cp); ++ s = PFUNC(PyUnicode_FromString(cp)); + + mpd_free(cp); + return s; +@@ -3156,12 +3274,12 @@ + + + CURRENT_CONTEXT(context); +- if (!PyArg_ParseTuple(args, "O|O", &fmtarg, &override)) { ++ if (!FFUNC(0, PyArg_ParseTuple(args, "O|O", &fmtarg, &override))) { + return NULL; + } + + if (PyUnicode_Check(fmtarg)) { +- if ((fmt = PyUnicode_AsUTF8String(fmtarg)) == NULL) { ++ if ((fmt = PFUNC(PyUnicode_AsUTF8String(fmtarg))) == NULL) { + return NULL; + } + } +@@ -3184,19 +3302,19 @@ + goto finish; + } + if ((dot = PyDict_GetItemString(override, "decimal_point"))) { +- if ((dot = PyUnicode_AsUTF8String(dot)) == NULL) { ++ if ((dot = PFUNC(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) { ++ if ((sep = PFUNC(PyUnicode_AsUTF8String(sep))) == NULL) { + goto finish; + } + spec.sep = PyBytes_AS_STRING(sep); + } + if ((grouping = PyDict_GetItemString(override, "grouping"))) { +- if ((grouping = PyUnicode_AsUTF8String(grouping)) == NULL) { ++ if ((grouping = PFUNC(PyUnicode_AsUTF8String(grouping))) == NULL) { + goto finish; + } + spec.grouping = PyBytes_AS_STRING(grouping); +@@ -3205,17 +3323,17 @@ + else { + n = strlen(spec.dot); + if (n > 1 || (n == 1 && !isascii((uchar)spec.dot[0]))) { +- n = mbstowcs(buf, spec.dot, 2); ++ n = FFUNC_NOEX((size_t)-1, mbstowcs(buf, spec.dot, 2)); + if (n != 1) { +- PyErr_SetString(PyExc_ValueError, ++ PyErr_SetString(FailAPIException, + "invalid decimal point or unsupported " + "combination of LC_CTYPE and LC_NUMERIC."); + goto finish; + } +- if ((tmp = PyUnicode_FromWideChar(buf, n)) == NULL) { ++ if ((tmp = PFUNC(PyUnicode_FromWideChar(buf, n))) == NULL) { + goto finish; + } +- if ((dot = PyUnicode_AsUTF8String(tmp)) == NULL) { ++ if ((dot = PFUNC(PyUnicode_AsUTF8String(tmp))) == NULL) { + Py_DECREF(tmp); + goto finish; + } +@@ -3224,17 +3342,17 @@ + } + n = strlen(spec.sep); + if (n > 1 || (n == 1 && !isascii((uchar)spec.sep[0]))) { +- n = mbstowcs(buf, spec.sep, 2); ++ n = FFUNC_NOEX((size_t)-1, mbstowcs(buf, spec.sep, 2)); + if (n != 1) { +- PyErr_SetString(PyExc_ValueError, ++ PyErr_SetString(FailAPIException, + "invalid thousands separator or unsupported " + "combination of LC_CTYPE and LC_NUMERIC."); + goto finish; + } +- if ((tmp = PyUnicode_FromWideChar(buf, n)) == NULL) { ++ if ((tmp = PFUNC(PyUnicode_FromWideChar(buf, n))) == NULL) { + goto finish; + } +- if ((sep = PyUnicode_AsUTF8String(tmp)) == NULL) { ++ if ((sep = PFUNC(PyUnicode_AsUTF8String(tmp))) == NULL) { + Py_DECREF(tmp); + goto finish; + } +@@ -3249,7 +3367,7 @@ + dec_addstatus(context, status); + goto finish; + } +- result = PyUnicode_DecodeUTF8(decstring, strlen(decstring), NULL); ++ result = PFUNC(PyUnicode_DecodeUTF8(decstring, strlen(decstring), NULL)); + + + finish: +@@ -3286,24 +3404,24 @@ + } + + if ((x = mpd_qnew()) == NULL) { +- PyErr_NoMemory(); ++ PyErr_SetString(FailAPIException, "failapi"); + return NULL; + } + workctx = *CTX(context); + workctx.round = round; +- mpd_qround_to_int(x, MPD(dec), &workctx, &status); ++ SFUNC(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); ++ maxsize = FFUNC_NOEX((size_t)PY_SSIZE_T_MAX+1, mpd_sizeinbase(x, PyLong_BASE)); + if (maxsize > PY_SSIZE_T_MAX) { + mpd_del(x); +- PyErr_NoMemory(); ++ PyErr_SetString(FailAPIException, "failapi"); + return NULL; + } +- if ((pylong = _PyLong_New(maxsize)) == NULL) { ++ if ((pylong = PFUNC(_PyLong_New(maxsize))) == NULL) { + mpd_del(x); + return NULL; + } +@@ -3347,8 +3465,8 @@ + int round = -1; + + CURRENT_CONTEXT(context); +- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO", kwlist, +- &round, &context)) { ++ if (!FFUNC(0, PyArg_ParseTupleAndKeywords(args, kwds, "|iO", kwlist, ++ &round, &context))) { + return NULL; + } + CONTEXT_CHECK_VA(context); +@@ -3364,7 +3482,7 @@ + return NULL; + } + +- mpd_qround_to_int(MPD(result), MPD(dec), &workctx, &status); ++ SFUNC(mpd_qround_to_int, MPD(result), MPD(dec), &workctx, &status); + if (dec_addstatus(context, status)) { + Py_DECREF(result); + return NULL; +@@ -3384,8 +3502,8 @@ + int round = -1; + + CURRENT_CONTEXT(context); +- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO", kwlist, +- &round, &context)) { ++ if (!FFUNC(0, PyArg_ParseTupleAndKeywords(args, kwds, "|iO", kwlist, ++ &round, &context))) { + return NULL; + } + CONTEXT_CHECK_VA(context); +@@ -3435,7 +3553,7 @@ + + + CURRENT_CONTEXT(context); +- if (!PyArg_ParseTuple(args, "|O", &x)) { ++ if (!FFUNC(0, PyArg_ParseTuple(args, "|O", &x))) { + return NULL; + } + +@@ -3488,28 +3606,28 @@ + + + if ((x = mpd_qncopy(MPD(dec))) == NULL) { +- PyErr_NoMemory(); ++ PyErr_SetString(FailAPIException, "failapi"); + goto out; + } + +- sign = Py_BuildValue("i", mpd_sign(MPD(dec))); ++ sign = PFUNC(Py_BuildValue("i", mpd_sign(MPD(dec)))); + if (sign == NULL) goto out; + + if (mpd_isinfinite(x)) { +- if ((expt = Py_BuildValue("s", "F")) == NULL) { ++ if ((expt = PFUNC(Py_BuildValue("s", "F"))) == NULL) { + goto out; + } + /* decimal.py has non-compliant infinity payloads. */ +- if ((coeff = Py_BuildValue("(i)", 0)) == NULL) { ++ if (