diff -r 207408428242 -r 40917e4b51aa .hgeol --- a/.hgeol Tue Dec 13 15:53:47 2011 +0200 +++ b/.hgeol Thu Feb 23 16:43:15 2012 +0100 @@ -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 207408428242 -r 40917e4b51aa .hgignore --- a/.hgignore Tue Dec 13 15:53:47 2011 +0200 +++ b/.hgignore Thu Feb 23 16:43:15 2012 +0100 @@ -15,7 +15,7 @@ db_home platform$ pyconfig.h$ -python$ +^python$ python.exe$ reflog.txt$ tags$ diff -r 207408428242 -r 40917e4b51aa Doc/library/numeric.rst --- a/Doc/library/numeric.rst Tue Dec 13 15:53:47 2011 +0200 +++ b/Doc/library/numeric.rst Thu Feb 23 16:43:15 2012 +0100 @@ -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 207408428242 -r 40917e4b51aa Include/longintrepr.h --- a/Include/longintrepr.h Tue Dec 13 15:53:47 2011 +0200 +++ b/Include/longintrepr.h Thu Feb 23 16:43:15 2012 +0100 @@ -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 207408428242 -r 40917e4b51aa Lib/decimal.py --- a/Lib/decimal.py Tue Dec 13 15:53:47 2011 +0200 +++ b/Lib/decimal.py Thu Feb 23 16:43:15 2012 +0100 @@ -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 @@ -1906,11 +1912,12 @@ def _power_modulo(self, other, modulo, context=None): """Three argument version of __pow__""" - # if can't convert other and modulo to Decimal, raise - # TypeError; there's no point returning NotImplemented (no - # equivalent of __rpow__ for three argument pow) - other = _convert_other(other, raiseit=True) - modulo = _convert_other(modulo, raiseit=True) + other = _convert_other(other) + if other is NotImplemented: + return other + modulo = _convert_other(modulo) + if modulo is NotImplemented: + return modulo if context is None: context = getcontext() @@ -3832,11 +3839,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 +3864,78 @@ if traps is None: self.traps = dc.traps.copy() elif not isinstance(traps, dict): - self.traps = dict((s, int(s in traps)) for s in _signals) + self.traps = dict((s, int(s in traps)) for s in _signals + traps) else: self.traps = traps if flags is None: self.flags = dict.fromkeys(_signals, 0) elif not isinstance(flags, dict): - self.flags = dict((s, int(s in flags)) for s in _signals) + self.flags = dict((s, int(s in flags)) for s in _signals + flags) else: self.flags = flags + def _set_integer_check(self, name, value, vmin, vmax): + if not isinstance(value, int): + raise TypeError("%s must be an integer" % name) + if vmin == '-inf': + if value > vmax: + raise ValueError("%s must be in [%s, %d]. got: %s" % (name, vmin, vmax, value)) + elif vmax == 'inf': + if value < vmin: + raise ValueError("%s must be in [%d, %s]. got: %s" % (name, vmin, vmax, value)) + else: + if value < vmin or value > vmax: + raise ValueError("%s must be in [%d, %d]. got %s" % (name, vmin, vmax, value)) + return object.__setattr__(self, name, value) + + def _set_signal_dict(self, name, d): + if not isinstance(d, dict): + raise TypeError("%s must be a signal dict" % d) + for key in d: + if not key in _signals: + raise KeyError("%s is not a valid signal dict" % d) + for key in _signals: + if not key in d: + raise KeyError("%s is not a valid signal dict" % d) + return object.__setattr__(self, name, d) + + def __setattr__(self, name, value): + if name == 'prec': + return self._set_integer_check(name, value, 1, 'inf') + elif name == 'Emin': + return self._set_integer_check(name, value, '-inf', 0) + elif name == 'Emax': + return self._set_integer_check(name, value, 0, 'inf') + elif name == 'capitals': + return self._set_integer_check(name, value, 0, 1) + elif name == 'clamp': + return self._set_integer_check(name, value, 0, 1) + elif name == 'rounding': + if not value in _rounding_modes: + # raise TypeError even for strings to have consistency + # among various implementations. + raise TypeError("%s: invalid rounding mode" % value) + return object.__setattr__(self, name, value) + elif name == 'flags' or name == 'traps': + return self._set_signal_dict(name, value) + elif name == '_ignored_flags': + return object.__setattr__(self, name, value) + else: + raise AttributeError( + "'decimal.Context' object has no attribute '%s'" % name) + + def __delattr__(self, name): + raise AttributeError("%s cannot be deleted" % name) + + # Support for pickling, copy, and deepcopy + def __reduce__(self): + flags = [sig for sig, v in self.flags.items() if v] + traps = [sig for sig, v in self.traps.items() if v] + return (self.__class__, + (self.prec, self.rounding, self.Emin, self.Emax, + self.capitals, self.clamp, flags, traps)) + def __repr__(self): """Show the current context.""" s = [] @@ -3890,16 +3956,17 @@ def _shallow_copy(self): """Returns a shallow copy from self.""" - nc = Context(self.prec, self.rounding, self.traps, - self.flags, self.Emin, self.Emax, - self.capitals, self.clamp, self._ignored_flags) + nc = Context(self.prec, self.rounding, self.Emin, self.Emax, + self.capitals, self.clamp, self.flags, self.traps, + self._ignored_flags) return nc def copy(self): """Returns a deep copy from self.""" - nc = Context(self.prec, self.rounding, self.traps.copy(), - self.flags.copy(), self.Emin, self.Emax, - self.capitals, self.clamp, self._ignored_flags) + nc = Context(self.prec, self.rounding, self.Emin, self.Emax, + self.capitals, self.clamp, + self.flags.copy(), self.traps.copy(), + self._ignored_flags) return nc __copy__ = copy @@ -4062,6 +4129,8 @@ >>> ExtendedContext.canonical(Decimal('2.50')) Decimal('2.50') """ + if not isinstance(a, Decimal): + raise TypeError("canonical requires a Decimal as an argument.") return a.canonical(context=self) def compare(self, a, b): @@ -4372,6 +4441,8 @@ >>> ExtendedContext.is_canonical(Decimal('2.50')) True """ + if not isinstance(a, Decimal): + raise TypeError("is_canonical requires a Decimal as an argument.") return a.is_canonical() def is_finite(self, a): @@ -4964,7 +5035,7 @@ +Normal +Infinity - >>> c = Context(ExtendedContext) + >>> c = ExtendedContext.copy() >>> c.Emin = -999 >>> c.Emax = 999 >>> c.number_class(Decimal('Infinity')) @@ -6080,7 +6151,7 @@ # if format type is 'g' or 'G' then a precision of 0 makes little # sense; convert it to 1. Same if format type is unspecified. if format_dict['precision'] == 0: - if format_dict['type'] is None or format_dict['type'] in 'gG': + if format_dict['type'] is None or format_dict['type'] in 'gGn': format_dict['precision'] = 1 # determine thousands separator, grouping, and decimal separator, and @@ -6259,11 +6330,22 @@ # hash values to use for positive and negative infinities, and nans _PyHASH_INF = sys.hash_info.inf _PyHASH_NAN = sys.hash_info.nan -del sys # _PyHASH_10INV is the inverse of 10 modulo the prime _PyHASH_MODULUS _PyHASH_10INV = pow(10, _PyHASH_MODULUS - 2, _PyHASH_MODULUS) - +del sys + +try: + import _decimal +except ImportError: + pass +else: + s1 = set(dir()) + s2 = set(dir(_decimal)) + for name in s1 - s2: + del globals()[name] + del s1, s2, name + from _decimal import * if __name__ == '__main__': import doctest, decimal diff -r 207408428242 -r 40917e4b51aa Lib/test/support.py --- a/Lib/test/support.py Tue Dec 13 15:53:47 2011 +0200 +++ b/Lib/test/support.py Thu Feb 23 16:43:15 2012 +0100 @@ -1406,7 +1406,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 @@ -1421,7 +1421,7 @@ else: verbosity = None - f, t = doctest.testmod(module, verbose=verbosity) + f, t = doctest.testmod(module, verbose=verbosity, optionflags=optionflags) if f: raise TestFailed("%d of %d doctests failed" % (f, t)) if verbose: diff -r 207408428242 -r 40917e4b51aa Lib/test/test_decimal.py --- a/Lib/test/test_decimal.py Tue Dec 13 15:53:47 2011 +0200 +++ b/Lib/test/test_decimal.py Thu Feb 23 16:43:15 2012 +0100 @@ -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,7 +461,7 @@ self.context.traps[error] = 0 v = self.context.create_decimal(v) else: - v = Decimal(v, self.context) + v = self.read_unlimited(v, self.context) vals.append(v) ans = FixQuotes(ans) @@ -344,7 +473,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 +481,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 +502,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 +584,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 +615,35 @@ #leading and trailing whitespace permitted self.assertEqual(str(Decimal('1.3E4 \n')), '1.3E+4') self.assertEqual(str(Decimal(' -7.89')), '-7.89') + self.assertEqual(str(Decimal(" 3.45679 ")), '3.45679') + + # unicode whitespace + for lead in ["", ' ', '\u00a0', '\u205f']: + for trail in ["", ' ', '\u00a0', '\u205f']: + self.assertEqual(str(Decimal(lead + '9.311E+28' + trail)), + '9.311E+28') + + with localcontext() as c: + c.traps[InvalidOperation] = True + # Invalid string + self.assertRaises(InvalidOperation, Decimal, "xyz") + # Two arguments max + self.assertRaises(TypeError, Decimal, "1234", "x", "y") + + # space within the numeric part + self.assertRaises(InvalidOperation, Decimal, "1\u00a02\u00a03") + self.assertRaises(InvalidOperation, Decimal, "\u00a01\u00a02\u00a0") + + # unicode whitespace + self.assertRaises(InvalidOperation, Decimal, "\u00a0") + self.assertRaises(InvalidOperation, Decimal, "\u00a0\u00a0") + + # embedded NUL + self.assertRaises(InvalidOperation, Decimal, "12\u00003") + def test_explicit_from_tuples(self): + Decimal = self.decimal.Decimal #zero d = Decimal( (0, (0,), 0) ) @@ -477,6 +661,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 +679,65 @@ self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), '1') ) #bad coefficients + self.assertRaises(ValueError, Decimal, (1, "xyz", 2) ) self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, None, 1), 2) ) self.assertRaises(ValueError, Decimal, (1, (4, -3, 4, 9, 1), 2) ) self.assertRaises(ValueError, Decimal, (1, (4, 10, 4, 9, 1), 2) ) self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 'a', 1), 2) ) + def test_explicit_from_list(self): + Decimal = self.decimal.Decimal + + d = Decimal([0, [0], 0]) + self.assertEqual(str(d), '0') + + d = Decimal([1, [4, 3, 4, 9, 1, 3, 5, 3, 4], -25]) + self.assertEqual(str(d), '-4.34913534E-17') + + d = Decimal([1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25]) + self.assertEqual(str(d), '-4.34913534E-17') + + d = Decimal((1, [4, 3, 4, 9, 1, 3, 5, 3, 4], -25)) + self.assertEqual(str(d), '-4.34913534E-17') + def test_explicit_from_bool(self): + Decimal = self.decimal.Decimal + self.assertIs(bool(Decimal(0)), False) self.assertIs(bool(Decimal(1)), True) self.assertEqual(Decimal(False), Decimal(0)) self.assertEqual(Decimal(True), Decimal(1)) def test_explicit_from_Decimal(self): + Decimal = self.decimal.Decimal #positive d = Decimal(45) e = Decimal(d) self.assertEqual(str(e), '45') - self.assertNotEqual(id(d), id(e)) #very large positive d = Decimal(500000123) e = Decimal(d) self.assertEqual(str(e), '500000123') - self.assertNotEqual(id(d), id(e)) #negative d = Decimal(-45) e = Decimal(d) self.assertEqual(str(e), '-45') - self.assertNotEqual(id(d), id(e)) #zero d = Decimal(0) e = Decimal(d) self.assertEqual(str(e), '0') - self.assertNotEqual(id(d), id(e)) @requires_IEEE_754 def test_explicit_from_float(self): + 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 +758,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 +803,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 +880,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 +962,16 @@ self.assertEqual(eval('Decimal(10)' + sym + 'E()'), '10' + rop + 'str') - -class DecimalFormatTest(unittest.TestCase): +class CImplicitConstructionTest(ImplicitConstructionTest): + decimal = C +class PyImplicitConstructionTest(ImplicitConstructionTest): + decimal = P + +class FormatTest(unittest.TestCase): '''Unit tests for the format function.''' def test_formatting(self): + Decimal = self.decimal.Decimal + # triples giving a format, a Decimal, and the expected result test_values = [ ('e', '0E-15', '0e-15'), @@ -730,6 +1027,7 @@ ('g', '0E-7', '0e-7'), ('g', '-0E2', '-0e+2'), ('.0g', '3.14159265', '3'), # 0 sig fig -> 1 sig fig + ('.0n', '3.14159265', '3'), # same for 'n' ('.1g', '3.14159265', '3'), ('.2g', '3.14159265', '3.1'), ('.5g', '3.14159265', '3.1416'), @@ -814,56 +1112,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 +1204,33 @@ self.assertEqual(get_fmt(123456, crazy, '012n'), '00-01-2345-6') self.assertEqual(get_fmt(123456, crazy, '013n'), '000-01-2345-6') - -class DecimalArithmeticOperatorsTest(unittest.TestCase): + # wide char separator and decimal point + self.assertEqual(get_fmt(Decimal('-1.5'), dotsep_wide, '020n'), + '-0\u00b4000\u00b4000\u00b4000\u00b4001\u00bf5') + + def test_wide_char_separator_decimal_point(self): + # locale with wide char separator and decimal point + Decimal = self.decimal.Decimal + + try: + locale.setlocale(locale.LC_ALL, 'ps_AF') + except locale.Error: + return + + self.assertEqual(format(Decimal('100000000.123'), 'n'), + '100\u066c000\u066c000\u066b123') + locale.resetlocale() + +class CFormatTest(FormatTest): + decimal = C +class PyFormatTest(FormatTest): + decimal = P + +class ArithmeticOperatorsTest(unittest.TestCase): '''Unit tests for all arithmetic operators, binary and unary.''' def test_addition(self): + Decimal = self.decimal.Decimal d1 = Decimal('-11.1') d2 = Decimal('22.2') @@ -934,6 +1258,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 +1286,7 @@ self.assertEqual(d1, Decimal('-38.3')) def test_multiplication(self): + Decimal = self.decimal.Decimal d1 = Decimal('-5') d2 = Decimal('3') @@ -988,6 +1314,7 @@ self.assertEqual(d1, Decimal('-75')) def test_division(self): + Decimal = self.decimal.Decimal d1 = Decimal('-5') d2 = Decimal('2') @@ -1015,6 +1342,7 @@ self.assertEqual(d1, Decimal('-0.625')) def test_floor_division(self): + Decimal = self.decimal.Decimal d1 = Decimal('5') d2 = Decimal('2') @@ -1042,6 +1370,7 @@ self.assertEqual(d1, Decimal('1')) def test_powering(self): + Decimal = self.decimal.Decimal d1 = Decimal('5') d2 = Decimal('2') @@ -1069,6 +1398,7 @@ self.assertEqual(d1, Decimal('390625')) def test_module(self): + Decimal = self.decimal.Decimal d1 = Decimal('5') d2 = Decimal('2') @@ -1096,6 +1426,7 @@ self.assertEqual(d1, Decimal('1')) def test_floor_div_module(self): + Decimal = self.decimal.Decimal d1 = Decimal('5') d2 = Decimal('2') @@ -1122,6 +1453,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 +1467,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 +1515,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 +1705,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 +1723,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 +1795,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 +1819,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 +1863,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 +1903,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 +1912,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 +1920,7 @@ def test_tonum_methods(self): #Test float and int methods. + Decimal = self.decimal.Decimal d1 = Decimal('66') d2 = Decimal('15.32') @@ -1516,9 +2019,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 +2039,7 @@ self.assertEqual(d, eval(repr(d))) def test_as_tuple(self): + Decimal = self.decimal.Decimal #with zero d = Decimal(0) @@ -1550,7 +2053,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 +2073,1861 @@ 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) + + def test_named_parameters(self): + D = self.decimal.Decimal + Context = self.decimal.Context + localcontext = self.decimal.localcontext + InvalidOperation = self.decimal.InvalidOperation + Overflow = self.decimal.Overflow + + xc = Context() + xc.prec = 1 + xc.Emax = 1 + xc.Emin = -1 + + with localcontext() as c: + c.clear_flags() + + self.assertEqual(D(9, xc), 9) + self.assertEqual(D(9, context=xc), 9) + self.assertEqual(D(context=xc, value=9), 9) + self.assertEqual(D(context=xc), 0) + xc.clear_flags() + self.assertRaises(InvalidOperation, D, "xyz", context=xc) + self.assertTrue(xc.flags[InvalidOperation]) + self.assertFalse(c.flags[InvalidOperation]) + + xc.clear_flags() + self.assertEqual(D(2).exp(context=xc), 7) + self.assertRaises(Overflow, D(8).exp, context=xc) + self.assertTrue(xc.flags[Overflow]) + self.assertFalse(c.flags[Overflow]) + + xc.clear_flags() + self.assertEqual(D(2).ln(context=xc), D('0.7')) + self.assertRaises(InvalidOperation, D(-1).ln, context=xc) + self.assertTrue(xc.flags[InvalidOperation]) + self.assertFalse(c.flags[InvalidOperation]) + + self.assertEqual(D(0).log10(context=xc), D('-inf')) + self.assertEqual(D(-1).next_minus(context=xc), -2) + self.assertEqual(D(-1).next_plus(context=xc), D('-0.9')) + self.assertEqual(D("9.73").normalize(context=xc), D('1E+1')) + self.assertEqual(D("9999").to_integral(context=xc), 9999) + self.assertEqual(D("-2000").to_integral_exact(context=xc), -2000) + self.assertEqual(D("123").to_integral_value(context=xc), 123) + self.assertEqual(D("0.0625").sqrt(context=xc), D('0.2')) + + self.assertEqual(D("0.0625").compare(context=xc, other=3), -1) + xc.clear_flags() + self.assertRaises(InvalidOperation, + D("0").compare_signal, D('nan'), context=xc) + self.assertTrue(xc.flags[InvalidOperation]) + self.assertFalse(c.flags[InvalidOperation]) + self.assertEqual(D("0.01").max(D('0.0101'), context=xc), D('0.0')) + self.assertEqual(D("0.01").max(D('0.0101'), context=xc), D('0.0')) + self.assertEqual(D("0.2").max_mag(D('-0.3'), context=xc), + D('-0.3')) + self.assertEqual(D("0.02").min(D('-0.03'), context=xc), D('-0.0')) + self.assertEqual(D("0.02").min_mag(D('-0.03'), context=xc), + D('0.0')) + self.assertEqual(D("0.2").next_toward(D('-1'), context=xc), D('0.1')) + xc.clear_flags() + self.assertRaises(InvalidOperation, + D("0.2").quantize, D('1e10'), context=xc) + self.assertTrue(xc.flags[InvalidOperation]) + self.assertFalse(c.flags[InvalidOperation]) + self.assertEqual(D("9.99").remainder_near(D('1.5'), context=xc), + D('-0.5')) + + self.assertEqual(D("9.9").fma(third=D('0.9'), context=xc, other=7), + D('7E+1')) + + self.assertRaises(TypeError, D(1).is_canonical, context=xc) + self.assertRaises(TypeError, D(1).is_finite, context=xc) + self.assertRaises(TypeError, D(1).is_infinite, context=xc) + self.assertRaises(TypeError, D(1).is_nan, context=xc) + self.assertRaises(TypeError, D(1).is_qnan, context=xc) + self.assertRaises(TypeError, D(1).is_snan, context=xc) + self.assertRaises(TypeError, D(1).is_signed, context=xc) + self.assertRaises(TypeError, D(1).is_zero, context=xc) + + self.assertFalse(D("0.01").is_normal(context=xc)) + self.assertTrue(D("0.01").is_subnormal(context=xc)) + + self.assertRaises(TypeError, D(1).adjusted, context=xc) + self.assertRaises(TypeError, D(1).conjugate, context=xc) + self.assertRaises(TypeError, D(1).radix, context=xc) + + self.assertEqual(D(-111).logb(context=xc), 2) + self.assertEqual(D(0).logical_invert(context=xc), 1) + self.assertEqual(D('0.01').number_class(context=xc), '+Subnormal') + self.assertEqual(D('0.21').to_eng_string(context=xc), '0.21') + + self.assertEqual(D('11').logical_and(D('10'), context=xc), 0) + self.assertEqual(D('11').logical_or(D('10'), context=xc), 1) + self.assertEqual(D('01').logical_xor(D('10'), context=xc), 1) + self.assertEqual(D('23').rotate(1, context=xc), 3) + self.assertEqual(D('23').rotate(1, context=xc), 3) + xc.clear_flags() + self.assertRaises(Overflow, + D('23').scaleb, 1, context=xc) + self.assertTrue(xc.flags[Overflow]) + self.assertFalse(c.flags[Overflow]) + self.assertEqual(D('23').shift(-1, context=xc), 0) + + self.assertRaises(TypeError, D.from_float, 1.1, context=xc) + self.assertRaises(TypeError, D(0).as_tuple, context=xc) + + if (self.decimal == C): + self.assertRaises(TypeError, D(1).canonical, context=xc) + self.assertEqual(D("-1").copy_abs(context=xc), 1) + self.assertEqual(D("1").copy_negate(context=xc), -1) + else: + self.assertEqual(D(1).canonical(context=xc), 1) + self.assertRaises(TypeError, D("-1").copy_abs, context=xc) + self.assertRaises(TypeError, D("-1").copy_negate, context=xc) + +class CPythonAPItests(PythonAPItests): + decimal = C +class PyPythonAPItests(PythonAPItests): + decimal = P + +class ContextAPItests(unittest.TestCase): + + def test_pickle(self): + 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__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. + Context = self.decimal.Context + c = Context() + self.assertRaises(AttributeError, getattr, c, '_clamp') + + def test_abs(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.abs(Decimal(-1)) + self.assertEqual(c.abs(-1), d) + self.assertRaises(TypeError, c.abs, '-1') + + def test_add(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.add(Decimal(1), Decimal(1)) + self.assertEqual(c.add(1, 1), d) + self.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') + self.assertEqual(c.power(modulo=5, b=8, a=2), 1) + + def test_quantize(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.quantize(Decimal(1), Decimal(2)) + self.assertEqual(c.quantize(1, 2), d) + self.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)) + + def test_flag_comparisons(self): + Context = self.decimal.Context + Inexact = self.decimal.Inexact + Rounded = self.decimal.Rounded + + c = Context() + + # Valid SignalDict + self.assertNotEqual(c.flags, c.traps) + self.assertNotEqual(c.traps, c.flags) + + c.flags = c.traps + self.assertEqual(c.flags, c.traps) + self.assertEqual(c.traps, c.flags) + + c.flags[Rounded] = True + c.traps = c.flags + self.assertEqual(c.flags, c.traps) + self.assertEqual(c.traps, c.flags) + + d = {} + d.update(c.flags) + self.assertEqual(d, c.flags) + self.assertEqual(c.flags, d) + + d[Inexact] = True + self.assertNotEqual(d, c.flags) + self.assertNotEqual(c.flags, d) + + # Invalid SignalDict + d = {Inexact:False} + self.assertNotEqual(d, c.flags) + self.assertNotEqual(c.flags, d) + + d = ["xyz"] + self.assertNotEqual(d, c.flags) + self.assertNotEqual(c.flags, d) + +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(KeyError, setattr, c, 'flags', {}) + self.assertRaises(KeyError, setattr, c, 'traps', + {'InvalidOperation':0}) + + # Attributes cannot be deleted + for attr in ['prec', 'Emax', 'Emin', 'rounding', 'capitals', 'clamp', + 'flags', 'traps']: + self.assertRaises(AttributeError, c.__delattr__, attr) + + # Invalid attributes + self.assertRaises(TypeError, getattr, c, 9) + self.assertRaises(TypeError, setattr, c, 9) + + # Invalid values in constructor + self.assertRaises(TypeError, Context, rounding=999999) + self.assertRaises(TypeError, Context, rounding='xyz') + self.assertRaises(ValueError, Context, clamp=2) + self.assertRaises(ValueError, Context, capitals=-1) + self.assertRaises(KeyError, Context, flags=["P"]) + self.assertRaises(KeyError, Context, traps=["Q"]) + + # Type error in conversion + self.assertRaises(TypeError, Context, flags=(0,1)) + self.assertRaises(TypeError, Context, traps=(1,0)) + +class CContextInputValidation(ContextInputValidation): + decimal = C +class PyContextInputValidation(ContextInputValidation): + decimal = P + +class 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,715 +3999,833 @@ 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) + + @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.clear_traps() + context.traps[Inexact] = True + context.traps[FloatOperation] = True + self.assertEqual(context._traps, DecInexact|DecFloatOperation) + self.assertTrue(context.traps[FloatOperation]) + self.assertTrue(context.traps[Inexact]) + + 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_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(KeyError, c.flags.__setitem__, 801, 0) + self.assertRaises(KeyError, c.traps.__setitem__, 801, 0) + self.assertRaises(ValueError, c.flags.__delitem__, Overflow) + self.assertRaises(ValueError, c.traps.__delitem__, InvalidOperation) + self.assertRaises(TypeError, setattr, c, 'flags', ['x']) + self.assertRaises(TypeError, setattr, c,'traps', ['y']) + self.assertRaises(KeyError, setattr, c, 'flags', {0:1}) + self.assertRaises(KeyError, setattr, c, 'traps', {0:1}) + + # 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') + + # Invalid local context + self.assertRaises(TypeError, exec, 'with localcontext("xyz"): pass', + locals()) + + # setcontext + saved_context = getcontext() + self.assertRaises(TypeError, setcontext, "xyz") + setcontext(saved_context) + + 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) + + # 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_eng_string(), '99.9E+9') + + self.assertRaises(TypeError, pow, Decimal(1), 2, "3") + self.assertRaises(TypeError, Decimal(9).number_class, "x", "y") + self.assertRaises(TypeError, Decimal(9).same_quantum, 3, "x", "y") + + self.assertRaises( + TypeError, + Decimal("1.23456789").quantize, Decimal('1e-100000'), [] + ) + self.assertRaises( + TypeError, + Decimal("1.23456789").quantize, Decimal('1e-100000'), getcontext() + ) + self.assertRaises( + TypeError, + Decimal("1.23456789").quantize, Decimal('1e-100000'), 10 + ) + self.assertRaises( + TypeError, + Decimal("1.23456789").quantize, Decimal('1e-100000'), ROUND_UP, 1000 + ) + + with localcontext() as c: + c.clear_traps() + + # Invalid arguments + self.assertRaises(TypeError, c.copy_sign, Decimal(1), "x", "y") + self.assertRaises(TypeError, c.canonical, 200) + self.assertRaises(TypeError, c.is_canonical, 200) + self.assertRaises(TypeError, c.divmod, 9, 8, "x", "y") + self.assertRaises(TypeError, c.same_quantum, 9, 3, "x", "y") + + self.assertEqual(str(c.canonical(Decimal(200))), '200') + self.assertEqual(c.radix(), 10) + + c.traps[DivisionByZero] = True + self.assertRaises(DivisionByZero, Decimal(9).__divmod__, 0) + self.assertRaises(DivisionByZero, c.divmod, 9, 0) + self.assertTrue(c.flags[InvalidOperation]) + + c.clear_flags() + c.traps[InvalidOperation] = True + self.assertRaises(InvalidOperation, Decimal(9).__divmod__, 0) + self.assertRaises(InvalidOperation, c.divmod, 9, 0) + self.assertTrue(c.flags[DivisionByZero]) + + c.traps[InvalidOperation] = True + c.prec = 2 + self.assertRaises(InvalidOperation, pow, Decimal(1000), 1, 501) + + 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() - with self.assertRaises(AttributeError): - clamp_value = c._clamp - - 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) + 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) + + def test_invalid_override(self): + Decimal = C.Decimal + + try: + from locale import CHAR_MAX + except ImportError: + return + + def make_grouping(lst): + return ''.join([chr(x) for x in lst]) + + def get_fmt(x, override=None, fmt='n'): + return Decimal(x).__format__(fmt, override) + + invalid_grouping = { + 'decimal_point' : ',', + 'grouping' : make_grouping([255, 255, 0]), + 'thousands_sep' : ',' + } + invalid_dot = { + 'decimal_point' : 'xxxxx', + 'grouping' : make_grouping([3, 3, 0]), + 'thousands_sep' : ',' + } + invalid_sep = { + 'decimal_point' : '.', + 'grouping' : make_grouping([3, 3, 0]), + 'thousands_sep' : 'yyyyy' + } + + if CHAR_MAX == 127: # negative grouping in override + self.assertRaises(ValueError, get_fmt, 12345, + invalid_grouping, 'g') + + self.assertRaises(ValueError, get_fmt, 12345, invalid_dot, 'g') + self.assertRaises(ValueError, get_fmt, 12345, invalid_sep, 'g') + + +all_tests = [ + CExplicitConstructionTest, PyExplicitConstructionTest, + CImplicitConstructionTest, PyImplicitConstructionTest, + CFormatTest, PyFormatTest, + CArithmeticOperatorsTest, PyArithmeticOperatorsTest, + CThreadingTest, PyThreadingTest, + CUsabilityTest, PyUsabilityTest, + CPythonAPItests, PyPythonAPItests, + CContextAPItests, PyContextAPItests, + CContextWithStatement, PyContextWithStatement, + CContextFlags, PyContextFlags, + CSpecialContexts, PySpecialContexts, + CContextInputValidation, PyContextInputValidation, + 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. @@ -2366,27 +4834,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 @@ -2398,17 +4855,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 207408428242 -r 40917e4b51aa Lib/test/test_fractions.py --- a/Lib/test/test_fractions.py Tue Dec 13 15:53:47 2011 +0200 +++ b/Lib/test/test_fractions.py Thu Feb 23 16:43:15 2012 +0100 @@ -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 207408428242 -r 40917e4b51aa Lib/test/test_numeric_tower.py --- a/Lib/test/test_numeric_tower.py Tue Dec 13 15:53:47 2011 +0200 +++ b/Lib/test/test_numeric_tower.py Thu Feb 23 16:43:15 2012 +0100 @@ -150,7 +150,7 @@ # int, float, Fraction, Decimal test_values = [ float('-inf'), - D('-1e999999999'), + D('-1e425000000'), -1e308, F(-22, 7), -3.14, diff -r 207408428242 -r 40917e4b51aa Misc/valgrind-python.supp --- a/Misc/valgrind-python.supp Tue Dec 13 15:53:47 2011 +0200 +++ b/Misc/valgrind-python.supp Thu Feb 23 16:43:15 2012 +0100 @@ -413,3 +413,16 @@ } +# Additional suppressions for the unified decimal tests: +{ + test_decimal + Memcheck:Addr4 + fun:PyUnicodeUCS2_FSConverter +} + +{ + test_decimal2 + Memcheck:Addr4 + fun:PyUnicode_FSConverter +} + diff -r 207408428242 -r 40917e4b51aa Modules/_decimal/ISSUES.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/ISSUES.txt Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,30 @@ + + +Normal priority: +---------------- + +1) Separate library directory (?) + +2) Add comments. + + + +Low priority: +------------- + +1) Convert tabs (wait until commit). + +2) Pre-ANSI compilers require '#' in the first column (should be done + for the whole Python source tree if we support such compilers). (?) + +3) FETCH_CURRENT_CONTEXT vs. CURRENT_CONTEXT? + +4) Justify remaining uses of exit on overflow in bignum arith. Short + answer: with correct context values the coefficients never get big + enough for that to happen. + +5) Justify remaining uses of abort() in mpdecimal: These uses are + for debug purposes and can't be reached when the library is used + correctly. + + diff -r 207408428242 -r 40917e4b51aa Modules/_decimal/LIBTEST.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/LIBTEST.txt Thu Feb 23 16:43:15 2012 +0100 @@ -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 207408428242 -r 40917e4b51aa Modules/_decimal/Makefile.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/Makefile.in Thu Feb 23 16:43:15 2012 +0100 @@ -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 = $(CONFIGURE_CFLAGS) $(MPD_LDCOV) +endif +ifeq ($(MAKECMDGOALS), pycoverage) + CFLAGS = $(MPD_WARN) $(MPD_CONFIG) $(MPD_CCOV) + LDFLAGS = $(CONFIGURE_CFLAGS) $(MPD_LDCOV) +endif +ifeq ($(MAKECMDGOALS), coverage) + CFLAGS = $(MPD_WARN) $(MPD_CONFIG) $(MPD_CCOV) + LDFLAGS = $(CONFIGURE_CFLAGS) $(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 \ +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 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 + $(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 -f 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 207408428242 -r 40917e4b51aa Modules/_decimal/Makefile.vc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/Makefile.vc Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,243 @@ + +# ====================================================================== +# 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 \ +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 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 build rd /q /s build + -@if exist python\_decimal.pyd del python\_decimal.pyd + -@if exist python\_decimal_d.pyd del python\_decimal_d.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 + -@if exist python\decimaltestdata rd /q /s python\decimaltestdata + -@if exist python\__pycache__ rd /q /s python\__pycache__ + -@cd tests && nmake distclean + diff -r 207408428242 -r 40917e4b51aa Modules/_decimal/PYTEST.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/PYTEST.txt Thu Feb 23 16:43:15 2012 +0100 @@ -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 207408428242 -r 40917e4b51aa Modules/_decimal/_decimal.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/_decimal.c Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,5413 @@ +/* + * 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" + + +/* + * Type sizes with assertions in mpdecimal.h and pyport.h: + * sizeof(size_t) == sizeof(Py_ssize_t) + * sizeof(size_t) == sizeof(mpd_uint_t) == sizeof(mpd_ssize_t) + */ + +#ifdef TEST_COVERAGE + #undef Py_LOCAL_INLINE + #define Py_LOCAL_INLINE Py_LOCAL +#endif + +#define MPD_Float_operation MPD_Not_implemented + +#define BOUNDS_CHECK(x, MIN, MAX) x = (x < MIN || MAX < x) ? MAX : x + + +typedef struct { + PyObject_HEAD + mpd_t *dec; +} PyDecObject; + +typedef struct { + PyObject_HEAD + uint32_t *flags; +} PyDecSignalDictObject; + +typedef struct { + PyObject_HEAD + mpd_context_t ctx; + PyObject *traps; + PyObject *flags; + int capitals; +} PyDecContextObject; + +typedef struct { + PyObject_HEAD + PyObject *local; + PyObject *global; +} PyDecContextManagerObject; + + +#undef MPD +#undef CTX +static PyTypeObject PyDec_Type; +static PyTypeObject *PyDecSignalDict_Type; +static PyTypeObject PyDecContext_Type; +static PyTypeObject PyDecContextManager_Type; +#define PyDec_CheckExact(v) (Py_TYPE(v) == &PyDec_Type) +#define PyDec_Check(v) PyObject_TypeCheck(v, &PyDec_Type) +#define PyDecSignalDict_Check(v) (Py_TYPE(v) == PyDecSignalDict_Type) +#define PyDecContext_Check(v) (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) + + +Py_LOCAL_INLINE(PyObject *) +incr_true(void) +{ + Py_INCREF(Py_True); + return Py_True; +} + +Py_LOCAL_INLINE(PyObject *) +incr_false(void) +{ + Py_INCREF(Py_False); + return Py_False; +} + + +#ifdef WITHOUT_THREADS +/* Default module context */ +static PyObject *module_context = NULL; +#else +/* Key for thread state dictionary */ +static PyObject *tls_context_key = NULL; +#endif + +/* Template for creating new thread contexts, calling Context() without + * arguments and initializing the module_context on first access. */ +static PyObject *default_context_template = NULL; +/* Basic and extended context templates */ +static PyObject *basic_context_template = NULL; +static PyObject *extended_context_template = NULL; + + +/* Error codes for functions that return signals or conditions */ +#define DEC_INVALID_SIGNALS (MPD_Max_status+1U) +#define DEC_ERR_OCCURRED (DEC_INVALID_SIGNALS<<1) +#define DEC_ERRORS (DEC_INVALID_SIGNALS|DEC_ERR_OCCURRED) + +typedef struct { + const char *name; /* condition or signal name */ + const char *fqname; /* fully qualified name */ + uint32_t flag; /* libmpdec flag */ + PyObject *ex; /* corresponding exception */ +} DecCondMap; + +/* Top level Exception; inherits from ArithmeticError */ +static PyObject *DecimalException = NULL; + +/* Exceptions that correspond to IEEE signals; inherit from DecimalException */ +#define SIGNAL_MAP_LEN 9 +static DecCondMap signal_map[] = { + {"InvalidOperation", "decimal.InvalidOperation", MPD_IEEE_Invalid_operation, NULL}, + {"FloatOperation", "decimal.FloatOperation", MPD_Float_operation, NULL}, + {"DivisionByZero", "decimal.DivisionByZero", MPD_Division_by_zero, NULL}, + {"Overflow", "decimal.Overflow", MPD_Overflow, NULL}, + {"Underflow", "decimal.Underflow", MPD_Underflow, NULL}, + {"Subnormal", "decimal.Subnormal", MPD_Subnormal, NULL}, + {"Inexact", "decimal.Inexact", MPD_Inexact, NULL}, + {"Rounded", "decimal.Rounded", MPD_Rounded, NULL}, + {"Clamped", "decimal.Clamped", MPD_Clamped, NULL}, + {NULL} +}; + +/* Exceptions that inherit from InvalidOperation */ +static DecCondMap cond_map[] = { + {"InvalidOperation", "decimal.InvalidOperation", MPD_Invalid_operation, NULL}, + {"ConversionSyntax", "decimal.ConversionSyntax", MPD_Conversion_syntax, NULL}, + {"DivisionImpossible", "decimal.DivisionImpossible", MPD_Division_impossible, NULL}, + {"DivisionUndefined", "decimal.DivisionUndefined", MPD_Division_undefined, NULL}, + {"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->flag) { + return cm->ex; + } + } + + INTERNAL_ERROR_PTR("flags_as_exception"); /* GCOV_NOT_REACHED */ +} + +Py_LOCAL_INLINE(uint32_t) +exception_as_flag(PyObject *ex) +{ + DecCondMap *cm; + + for (cm = signal_map; cm->name != NULL; cm++) { + if (cm->ex == ex) { + return cm->flag; + } + } + + PyErr_SetString(PyExc_KeyError, invalid_signals_err); + return DEC_INVALID_SIGNALS; +} + +static PyObject * +flags_as_list(uint32_t flags) +{ + PyObject *list; + DecCondMap *cm; + + list = PyList_New(0); + if (list == NULL) { + return NULL; + } + + for (cm = cond_map; cm->name != NULL; cm++) { + if (flags&cm->flag) { + if (PyList_Append(list, cm->ex) < 0) { + goto error; + } + } + } + for (cm = signal_map+1; cm->name != NULL; cm++) { + if (flags&cm->flag) { + if (PyList_Append(list, cm->ex) < 0) { + goto error; + } + } + } + + return list; + +error: + Py_DECREF(list); + return NULL; +} + +static PyObject * +signals_as_list(uint32_t flags) +{ + PyObject *list; + DecCondMap *cm; + + list = PyList_New(0); + if (list == NULL) { + return NULL; + } + + for (cm = signal_map; cm->name != NULL; cm++) { + if (flags&cm->flag) { + if (PyList_Append(list, cm->ex) < 0) { + Py_DECREF(list); + return NULL; + } + } + } + + return list; +} + +static uint32_t +list_as_flags(PyObject *list) +{ + PyObject *item; + uint32_t flags, x; + Py_ssize_t n, j; + + assert(PyList_Check(list)); + + n = PyList_Size(list); + flags = 0; + for (j = 0; j < n; j++) { + item = PyList_GetItem(list, j); + x = exception_as_flag(item); + if (x & DEC_ERRORS) { + return x; + } + flags |= x; + } + + return flags; +} + +static PyObject * +flags_as_dict(uint32_t flags) +{ + DecCondMap *cm; + PyObject *dict; + + dict = PyDict_New(); + if (dict == NULL) { + return NULL; + } + + for (cm = signal_map; cm->name != NULL; cm++) { + PyObject *b = flags&cm->flag ? Py_True : Py_False; + if (PyDict_SetItem(dict, cm->ex, b) < 0) { + Py_DECREF(dict); + return NULL; + } + } + + return dict; +} + +static uint32_t +dict_as_flags(PyObject *val) +{ + PyObject *b; + DecCondMap *cm; + uint32_t flags = 0; + int x; + + if (!PyDict_Check(val)) { + PyErr_SetString(PyExc_TypeError, + "argument must be a signal dict"); + return DEC_INVALID_SIGNALS; + } + + for (cm = signal_map; cm->name != NULL; cm++) { + b = PyDict_GetItemWithError(val, cm->ex); + if (b == NULL) { + if (PyErr_Occurred()) { + return DEC_ERR_OCCURRED; + } + PyErr_SetString(PyExc_KeyError, + "invalid signal dict"); + return DEC_INVALID_SIGNALS; + } + + x = PyObject_IsTrue(b); + if (x < 0) { + return DEC_ERR_OCCURRED; + } + if (x == 1) { + flags |= cm->flag; + } + } + + return flags; +} + +static uint32_t +long_as_flags(PyObject *v) +{ + long x; + + x = PyLong_AsLong(v); + if (x == -1 && PyErr_Occurred()) { + return DEC_ERR_OCCURRED; + } + if (x < 0 || x > (long)MPD_Max_status) { + PyErr_SetString(PyExc_TypeError, invalid_flags_err); + return DEC_INVALID_SIGNALS; + } + + return x; +} + +static int +dec_addstatus(PyObject *context, uint32_t status) +{ + mpd_context_t *ctx = CTX(context); + + ctx->status |= status; + if (ctx->traps&status) { + PyObject *ex, *siglist; + + ex = flags_as_exception(ctx->traps&status); + if (ex == NULL) { + return 1; /* GCOV_NOT_REACHED */ + } + siglist = flags_as_list(ctx->traps&status); + if (siglist == NULL) { + return 1; + } + + PyErr_SetObject(ex, siglist); + Py_DECREF(siglist); + return 1; + } + return 0; +} + + +/******************************************************************************/ +/* SignalDict Object */ +/******************************************************************************/ + +/* The SignalDict is a MutableMapping that provides access to the + mpd_context_t flags, which reside in the context object. When a + new context is created, context.traps and context.flags are + initialized to new SignalDicts. Once a SignalDict is tied to + a context, it cannot be deleted. */ + +static int +signaldict_init(PyObject *self, PyObject *args UNUSED, PyObject *kwds UNUSED) +{ + SdFlagAddr(self) = NULL; + return 0; +} + +static Py_ssize_t +signaldict_len(PyObject *self UNUSED) +{ + return SIGNAL_MAP_LEN; +} + +static PyObject *SignalTuple; +static PyObject * +signaldict_iter(PyObject *self UNUSED) +{ + return PyTuple_Type.tp_iter(SignalTuple); +} + +static PyObject * +signaldict_getitem(PyObject *self, PyObject *key) +{ + uint32_t flag; + + flag = exception_as_flag(key); + if (flag & DEC_ERRORS) { + return NULL; + } + + return SdFlags(self)&flag ? incr_true() : incr_false(); +} + +static int +signaldict_setitem(PyObject *self, PyObject *key, PyObject *value) +{ + uint32_t flag; + int x; + + if (value == NULL) { + return value_error_int("signal keys cannot be deleted"); + } + + flag = exception_as_flag(key); + if (flag & DEC_ERRORS) { + return -1; + } + + x = PyObject_IsTrue(value); + if (x < 0) { + return -1; + } + + if (x == 1) { + SdFlags(self) |= flag; + } + else { + SdFlags(self) &= ~flag; + } + + return 0; +} + +static PyObject * +signaldict_repr(PyObject *self) +{ + DecCondMap *cm; + const char *n[SIGNAL_MAP_LEN]; /* name */ + const char *b[SIGNAL_MAP_LEN]; /* bool */ + int i; + + assert(SIGNAL_MAP_LEN == 9); + + for (cm=signal_map, i=0; cm->name != NULL; cm++, i++) { + n[i] = cm->fqname; + b[i] = SdFlags(self)&cm->flag ? "True" : "False"; + } + return PyUnicode_FromFormat( + "{:%s, :%s, :%s, " + ":%s, :%s, :%s, " + ":%s, :%s, :%s}", + n[0], b[0], n[1], b[1], n[2], b[2], + n[3], b[3], n[4], b[4], n[5], b[5], + n[6], b[6], n[7], b[7], n[8], b[8]); +} + +static PyObject * +signaldict_richcompare(PyObject *v, PyObject *w, int op) +{ + PyObject *res = Py_NotImplemented; + + assert(PyDecSignalDict_Check(v)); + + if (op == Py_EQ || op == Py_NE) { + if (PyDecSignalDict_Check(w)) { + res = (SdFlags(v)==SdFlags(w)) ^ (op==Py_NE) ? Py_True : Py_False; + } + else if (PyDict_Check(w)) { + uint32_t flags = dict_as_flags(w); + if (flags & DEC_ERRORS) { + if (flags & DEC_INVALID_SIGNALS) { + /* non-comparable: Py_NotImplemented */ + PyErr_Clear(); + } + else { + return NULL; + } + } + else { + res = (SdFlags(v)==flags) ^ (op==Py_NE) ? Py_True : Py_False; + } + } + } + + Py_INCREF(res); + return res; +} + +static PyObject * +signaldict_copy(PyObject *self) +{ + return flags_as_dict(SdFlags(self)); +} + + +static PyMappingMethods signaldict_as_mapping = { + (lenfunc)signaldict_len, /* mp_length */ + (binaryfunc)signaldict_getitem, /* mp_subscript */ + (objobjargproc)signaldict_setitem /* mp_ass_subscript */ +}; + +static PyMethodDef signaldict_methods[] = { + { "copy", (PyCFunction)signaldict_copy, METH_NOARGS, NULL}, + {NULL, NULL} +}; + + +static PyTypeObject PyDecSignalDictMixin_Type = +{ + PyVarObject_HEAD_INIT(0, 0) + "decimal.SignalDictMixin", /* tp_name */ + sizeof(PyDecSignalDictObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + (getattrfunc) 0, /* tp_getattr */ + (setattrfunc) 0, /* tp_setattr */ + 0, /* tp_reserved */ + (reprfunc) signaldict_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + &signaldict_as_mapping, /* tp_as_mapping */ + PyObject_HashNotImplemented, /* tp_hash */ + 0, /* tp_call */ + (reprfunc) 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + (setattrofunc) 0, /* tp_setattro */ + (PyBufferProcs *) 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE| + Py_TPFLAGS_HAVE_GC, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + signaldict_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)signaldict_iter, /* tp_iter */ + 0, /* tp_iternext */ + signaldict_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)signaldict_init, /* tp_init */ + 0, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ +}; + + +/******************************************************************************/ +/* Context Object, Part 1 */ +/******************************************************************************/ + +#define Dec_CONTEXT_GET_SSIZE(mem) \ +static PyObject * \ +context_get##mem(PyObject *self, void *closure UNUSED) \ +{ \ + return PyLong_FromSsize_t(mpd_get##mem(CTX(self))); \ +} + +#define Dec_CONTEXT_GET_ULONG(mem) \ +static PyObject * \ +context_get##mem(PyObject *self, void *closure UNUSED) \ +{ \ + return PyLong_FromUnsignedLong(mpd_get##mem(CTX(self))); \ +} + +Dec_CONTEXT_GET_SSIZE(prec) +Dec_CONTEXT_GET_SSIZE(emax) +Dec_CONTEXT_GET_SSIZE(emin) +Dec_CONTEXT_GET_SSIZE(round) +Dec_CONTEXT_GET_SSIZE(clamp) + +Dec_CONTEXT_GET_ULONG(traps) +Dec_CONTEXT_GET_ULONG(status) + +static PyObject * +context_getcapitals(PyObject *self, void *closure UNUSED) +{ + return PyLong_FromLong(CtxCaps(self)); +} + +static PyObject * +context_getallcr(PyObject *self, void *closure UNUSED) +{ + return PyLong_FromLong(mpd_getcr(CTX(self))); +} + +static PyObject * +context_getetiny(PyObject *self, PyObject *dummy UNUSED) +{ + return PyLong_FromSsize_t(mpd_etiny(CTX(self))); +} + +static PyObject * +context_getetop(PyObject *self, PyObject *dummy UNUSED) +{ + return PyLong_FromSsize_t(mpd_etop(CTX(self))); +} + +static int +context_setprec(PyObject *self, PyObject *value, void *closure UNUSED) +{ + mpd_context_t *ctx; + mpd_ssize_t x; + + x = PyLong_AsSsize_t(value); + if (x == -1 && PyErr_Occurred()) { + return -1; + } + + ctx = CTX(self); + if (!mpd_qsetprec(ctx, x)) { + return value_error_int( + "valid range for prec is [1, MAX_PREC]"); + } + + return 0; +} + +static int +context_setemin(PyObject *self, PyObject *value, void *closure UNUSED) +{ + mpd_context_t *ctx; + mpd_ssize_t x; + + x = PyLong_AsSsize_t(value); + if (x == -1 && PyErr_Occurred()) { + return -1; + } + + ctx = CTX(self); + if (!mpd_qsetemin(ctx, x)) { + return value_error_int( + "valid range for Emin is [MIN_EMIN, 0]"); + } + + return 0; +} + +static int +context_setemax(PyObject *self, PyObject *value, void *closure UNUSED) +{ + mpd_context_t *ctx; + mpd_ssize_t x; + + x = PyLong_AsSsize_t(value); + if (x == -1 && PyErr_Occurred()) { + return -1; + } + + ctx = CTX(self); + if (!mpd_qsetemax(ctx, x)) { + return value_error_int( + "valid range for Emax is [0, MAX_EMAX]"); + } + + return 0; +} + +static PyObject * +context_unsafe_setprec(PyObject *self, PyObject *value) +{ + mpd_context_t *ctx = CTX(self); + mpd_ssize_t x; + + x = PyLong_AsSsize_t(value); + if (x == -1 && PyErr_Occurred()) { + return NULL; + } + + ctx->prec = x; + Py_RETURN_NONE; +} + +static PyObject * +context_unsafe_setemin(PyObject *self, PyObject *value) +{ + mpd_context_t *ctx = CTX(self); + mpd_ssize_t x; + + x = PyLong_AsSsize_t(value); + if (x == -1 && PyErr_Occurred()) { + return NULL; + } + + ctx->emin = x; + Py_RETURN_NONE; +} + +static PyObject * +context_unsafe_setemax(PyObject *self, PyObject *value) +{ + mpd_context_t *ctx = CTX(self); + mpd_ssize_t x; + + x = PyLong_AsSsize_t(value); + if (x == -1 && PyErr_Occurred()) { + return NULL; + } + + 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 = PyLong_AsSsize_t(value); + if (x == -1 && PyErr_Occurred()) { + return -1; + } + BOUNDS_CHECK(x, INT_MIN, INT_MAX); + + ctx = CTX(self); + if (!mpd_qsetround(ctx, (int)x)) { + return type_error_int(invalid_rounding_err); + } + + return 0; +} + +static int +context_setcapitals(PyObject *self, PyObject *value, void *closure UNUSED) +{ + mpd_ssize_t x; + + x = PyLong_AsSsize_t(value); + if (x == -1 && PyErr_Occurred()) { + return -1; + } + + if (x != 0 && x != 1) { + return value_error_int( + "valid values for capitals are 0 or 1"); + } + CtxCaps(self) = (int)x; + + return 0; +} + +static int +context_settraps(PyObject *self, PyObject *value, void *closure UNUSED) +{ + mpd_context_t *ctx; + uint32_t flags; + + flags = long_as_flags(value); + if (flags & DEC_ERRORS) { + return -1; + } + + ctx = CTX(self); + if (!mpd_qsettraps(ctx, flags)) { + INTERNAL_ERROR_INT("context_settraps"); + } + + return 0; +} + +static int +context_settraps_list(PyObject *self, PyObject *value) +{ + mpd_context_t *ctx; + uint32_t flags; + + flags = list_as_flags(value); + if (flags & DEC_ERRORS) { + return -1; + } + + ctx = CTX(self); + if (!mpd_qsettraps(ctx, flags)) { + INTERNAL_ERROR_INT("context_settraps_list"); + } + + return 0; +} + +static int +context_settraps_dict(PyObject *self, PyObject *value) +{ + mpd_context_t *ctx; + uint32_t flags; + + if (PyDecSignalDict_Check(value)) { + flags = SdFlags(value); + } + else { + flags = dict_as_flags(value); + if (flags & DEC_ERRORS) { + return -1; + } + } + + ctx = CTX(self); + if (!mpd_qsettraps(ctx, flags)) { + INTERNAL_ERROR_INT("context_settraps_dict"); + } + + return 0; +} + +static int +context_setstatus(PyObject *self, PyObject *value, void *closure UNUSED) +{ + mpd_context_t *ctx; + uint32_t flags; + + flags = long_as_flags(value); + if (flags & DEC_ERRORS) { + return -1; + } + + ctx = CTX(self); + if (!mpd_qsetstatus(ctx, flags)) { + INTERNAL_ERROR_INT("context_setstatus"); + } + + return 0; +} + +static int +context_setstatus_list(PyObject *self, PyObject *value) +{ + mpd_context_t *ctx; + uint32_t flags; + + flags = list_as_flags(value); + if (flags & DEC_ERRORS) { + return -1; + } + + ctx = CTX(self); + if (!mpd_qsetstatus(ctx, flags)) { + INTERNAL_ERROR_INT("context_setstatus_list"); + } + + return 0; +} + +static int +context_setstatus_dict(PyObject *self, PyObject *value) +{ + mpd_context_t *ctx; + uint32_t flags; + + if (PyDecSignalDict_Check(value)) { + flags = SdFlags(value); + } + else { + flags = dict_as_flags(value); + if (flags & DEC_ERRORS) { + return -1; + } + } + + ctx = CTX(self); + if (!mpd_qsetstatus(ctx, flags)) { + INTERNAL_ERROR_INT("context_setstatus_dict"); + } + + return 0; +} + +static int +context_setclamp(PyObject *self, PyObject *value, void *closure UNUSED) +{ + mpd_context_t *ctx; + mpd_ssize_t x; + + x = PyLong_AsSsize_t(value); + if (x == -1 && PyErr_Occurred()) { + return -1; + } + BOUNDS_CHECK(x, INT_MIN, INT_MAX); + + ctx = CTX(self); + if (!mpd_qsetclamp(ctx, (int)x)) { + return value_error_int("valid values for clamp are 0 or 1"); + } + + return 0; +} + +static int +context_setallcr(PyObject *self, PyObject *value, void *closure UNUSED) +{ + mpd_context_t *ctx; + mpd_ssize_t x; + + x = PyLong_AsSsize_t(value); + if (x == -1 && PyErr_Occurred()) { + return -1; + } + BOUNDS_CHECK(x, INT_MIN, INT_MAX); + + ctx = CTX(self); + if (!mpd_qsetcr(ctx, (int)x)) { + return value_error_int("valid values for _allcr are 0 or 1"); + } + + return 0; +} + +static PyObject * +context_getattr(PyObject *self, PyObject *name) +{ + PyObject *retval; + + if (PyUnicode_Check(name)) { + if (PyUnicode_CompareWithASCIIString(name, "traps") == 0) { + retval = ((PyDecContextObject *)self)->traps; + Py_INCREF(retval); + return retval; + } + if (PyUnicode_CompareWithASCIIString(name, "flags") == 0) { + retval = ((PyDecContextObject *)self)->flags; + Py_INCREF(retval); + return retval; + } + } + + return PyObject_GenericGetAttr(self, name); +} + +static int +context_setattr(PyObject *self, PyObject *name, PyObject *value) +{ + if (value == NULL) { + PyErr_SetString(PyExc_AttributeError, + "context attributes cannot be deleted"); + return -1; + } + + if (PyUnicode_Check(name)) { + if (PyUnicode_CompareWithASCIIString(name, "traps") == 0) { + return context_settraps_dict(self, value); + } + if (PyUnicode_CompareWithASCIIString(name, "flags") == 0) { + return context_setstatus_dict(self, value); + } + } + + return PyObject_GenericSetAttr(self, name, value); +} + +static PyObject * +context_clear_traps(PyObject *self, PyObject *dummy UNUSED) +{ + CTX(self)->traps = 0; + Py_RETURN_NONE; +} + +static PyObject * +context_clear_flags(PyObject *self, PyObject *dummy UNUSED) +{ + CTX(self)->status = 0; + Py_RETURN_NONE; +} + +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 (x == -1 && PyErr_Occurred()) { + return -1; + } + BOUNDS_CHECK(x, 0, INT_MAX); + return (int)x; + } + else if (PyUnicode_Check(v)) { + for (i = 0; i < MPD_ROUND_GUARD; i++) { + s = mpd_round_string[i]; + if (PyUnicode_CompareWithASCIIString(v, s) == 0) { + return i; + } + } + } + + return type_error_int("invalid rounding mode"); +} + +static int +context_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = { + "prec", "rounding", "Emin", "Emax", "capitals", "clamp", + "flags", "traps", "_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, + "|nOnniiOOi", kwlist, + &t.prec, &rounding, &t.emin, &t.emax, &capitals, &t.clamp, + &status, &traps, &t.allcr + )) { + return -1; + } + if (rounding != NULL) { + t.round = getround(rounding); + if (t.round < 0) { + return -1; + } + } + + if (!mpd_qsetprec(ctx, t.prec) || + !mpd_qsetemin(ctx, t.emin) || + !mpd_qsetemax(ctx, t.emax) || + !mpd_qsetclamp(ctx, t.clamp) || + !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; +} + +static PyObject * +context_repr(PyDecContextObject *self) +{ + mpd_context_t *ctx; + char flags[MPD_MAX_SIGNAL_LIST]; + char traps[MPD_MAX_SIGNAL_LIST]; + int n, mem; + + assert(PyDecContext_Check(self)); + ctx = CTX(self); + + mem = MPD_MAX_SIGNAL_LIST; + n = mpd_lsnprint_signals(flags, mem, ctx->status, dec_signal_string); + if (n < 0 || n >= mem) { + INTERNAL_ERROR_PTR("context_repr"); + } + + n = mpd_lsnprint_signals(traps, mem, ctx->traps, dec_signal_string); + if (n < 0 || n >= mem) { + INTERNAL_ERROR_PTR("context_repr"); + } + + return PyUnicode_FromFormat( + "Context(prec=%zd, rounding=%s, Emin=%zd, Emax=%zd, " + "capitals=%d, clamp=%d, flags=%s, traps=%s)", + ctx->prec, mpd_round_string[ctx->round], ctx->emin, ctx->emax, + self->capitals, ctx->clamp, flags, traps); +} + +static void +init_basic_context(PyObject *v) +{ + mpd_context_t ctx = dflt_ctx; + + ctx.prec = 9; + ctx.traps |= (MPD_Underflow|MPD_Clamped); + ctx.round = MPD_ROUND_HALF_UP; + + *CTX(v) = ctx; + CtxCaps(v) = 1; +} + +static void +init_extended_context(PyObject *v) +{ + mpd_context_t ctx = dflt_ctx; + + ctx.prec = 9; + ctx.traps = 0; + + *CTX(v) = ctx; + CtxCaps(v) = 1; +} + +/* Factory function for creating IEEE interchange format contexts */ +static PyObject * +ieee_context(PyObject *dummy UNUSED, PyObject *v) +{ + PyObject *context; + mpd_ssize_t bits; + mpd_context_t ctx; + + bits = PyLong_AsSsize_t(v); + if (bits == -1 && PyErr_Occurred()) { + return NULL; + } + if (bits <= 0 || bits > INT_MAX) { + goto error; + } + if (mpd_ieee_context(&ctx, (int)bits) < 0) { + goto error; + } + + context = PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL); + if (context == NULL) { + return NULL; + } + *CTX(context) = ctx; + + return context; + +error: + PyErr_Format(PyExc_ValueError, + "argument must be a multiple of 32, with a maximum of %d", + MPD_IEEE_CONTEXT_MAX_BITS); + + return NULL; +} + +static PyObject * +context_copy(PyObject *self) +{ + PyObject *copy; + + copy = PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL); + if (copy == NULL) { + return NULL; + } + + *CTX(copy) = *CTX(self); + CTX(copy)->newtrap = 0; + CtxCaps(copy) = CtxCaps(self); + + return copy; +} + +static PyObject * +context_reduce(PyObject *self, PyObject *args UNUSED) +{ + PyObject *flags; + PyObject *traps; + PyObject *ret; + mpd_context_t *ctx; + + ctx = CTX(self); + + flags = signals_as_list(ctx->status); + if (flags == NULL) { + return NULL; + } + traps = signals_as_list(ctx->traps); + if (traps == NULL) { + Py_DECREF(flags); + return NULL; + } + + ret = Py_BuildValue( + "O(nsnniiOO)", + Py_TYPE(self), + ctx->prec, mpd_round_string[ctx->round], ctx->emin, ctx->emax, + CtxCaps(self), ctx->clamp, flags, traps + ); + + Py_DECREF(flags); + Py_DECREF(traps); + return ret; +} + + +static PyGetSetDef context_getsets [] = +{ + { "prec", (getter)context_getprec, (setter)context_setprec, NULL, NULL}, + { "Emax", (getter)context_getemax, (setter)context_setemax, NULL, NULL}, + { "Emin", (getter)context_getemin, (setter)context_setemin, NULL, NULL}, + { "rounding", (getter)context_getround, (setter)context_setround, NULL, NULL}, + { "capitals", (getter)context_getcapitals, (setter)context_setcapitals, NULL, NULL}, + { "clamp", (getter)context_getclamp, (setter)context_setclamp, NULL, NULL}, + { "_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) { + v = context_copy(v); + if (v == NULL) { + return NULL; + } + } + else { + Py_INCREF(v); + } + + Py_XDECREF(module_context); + module_context = v; + module_context_set = 1; + Py_RETURN_NONE; +} +#else +/* + * Thread local storage currently has a speed penalty of about 16%. + * All functions that map Python's arithmetic operators to mpdecimal + * functions have to look up the current context for each and every + * operation. + */ + +/* Return borrowed reference to thread local context. */ +static PyObject * +current_context(void) +{ + PyObject *dict = NULL; + PyObject *tl_context = NULL; + + dict = PyThreadState_GetDict(); + if (dict == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "cannot get thread state"); + return NULL; + } + + tl_context = PyDict_GetItemWithError(dict, tls_context_key); + if (tl_context != NULL) { + /* We already have a thread local context and + * return a borrowed reference. */ + CONTEXT_CHECK(tl_context); + return tl_context; + } + if (PyErr_Occurred()) { + return NULL; + } + + /* Otherwise, set up a new thread local context. */ + tl_context = context_copy(default_context_template); + if (tl_context == NULL) { + return NULL; + } + if (PyDict_SetItem(dict, tls_context_key, tl_context) < 0) { + Py_DECREF(tl_context); + return NULL; + } + Py_DECREF(tl_context); + + /* refcount is 1 */ + return tl_context; +} + +/* ctxobj := borrowed reference to the current context */ +#define CURRENT_CONTEXT(ctxobj) \ + ctxobj = current_context(); \ + if (ctxobj == NULL) { \ + return NULL; \ + } + +/* ctx := pointer to the mpd_context_t struct of the current context */ +#define CURRENT_CONTEXT_ADDR(ctx) { \ + PyObject *_c_t_x_o_b_j = current_context(); \ + if (_c_t_x_o_b_j == NULL) { \ + return NULL; \ + } \ + ctx = CTX(_c_t_x_o_b_j); \ +} + +/* Return current context, increment reference */ +static PyObject * +PyDec_GetCurrentContext(void) +{ + PyObject *context; + + context = current_context(); + if (context == NULL) { + return NULL; + } + + Py_INCREF(context); + return context; +} + +/* Set the thread local context to a new context, decrement old reference */ +static PyObject * +PyDec_SetCurrentContext(PyObject *self UNUSED, PyObject *v) +{ + PyObject *dict; + + CONTEXT_CHECK(v); + + dict = PyThreadState_GetDict(); + if (dict == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "cannot get thread state"); + return NULL; + } + + /* If the new context is one of the templates, make a copy. + * This is the current behavior of decimal.py. */ + if (v == default_context_template || + v == basic_context_template || + v == extended_context_template) { + v = context_copy(v); + if (v == NULL) { + return NULL; + } + } + else { + Py_INCREF(v); + } + + if (PyDict_SetItem(dict, tls_context_key, v) < 0) { + Py_DECREF(v); + return NULL; + } + + Py_DECREF(v); + Py_RETURN_NONE; +} +#endif + +/* Context manager object for the 'with' statement. The manager + * owns one reference to the global (outer) context and one + * to the local (inner) context. */ +static PyObject * +ctxmanager_new(PyTypeObject *type UNUSED, PyObject *args) +{ + PyDecContextManagerObject *self; + PyObject *local; + PyObject *global; + + CURRENT_CONTEXT(global); + local = global; + if (!PyArg_ParseTuple(args, "|O", &local)) { + return NULL; + } + CONTEXT_CHECK_VA(local); + + self = PyObject_New(PyDecContextManagerObject, + &PyDecContextManager_Type); + if (self == NULL) { + return NULL; + } + + self->local = context_copy(local); + if (self->local == NULL) { + self->global = NULL; + Py_DECREF(self); + return NULL; + } + self->global = global; + Py_INCREF(self->global); + + return (PyObject *)self; +} + +static void +ctxmanager_dealloc(PyDecContextManagerObject *self) +{ + Py_XDECREF(self->local); + Py_XDECREF(self->global); + PyObject_Del(self); +} + +static PyObject * +ctxmanager_set_local(PyDecContextManagerObject *self, PyObject *args UNUSED) +{ + PyObject *ret; + + ret = PyDec_SetCurrentContext(NULL, self->local); + if (ret == NULL) { + return NULL; + } + Py_DECREF(ret); + + Py_INCREF(self->local); + return self->local; +} + +static PyObject * +ctxmanager_restore_global(PyDecContextManagerObject *self, + PyObject *args UNUSED) +{ + PyObject *ret; + + ret = PyDec_SetCurrentContext(NULL, self->global); + if (ret == NULL) { + return NULL; + } + Py_DECREF(ret); + + Py_RETURN_NONE; +} + + +static PyMethodDef ctxmanager_methods[] = { + {"__enter__", (PyCFunction)ctxmanager_set_local, METH_NOARGS, NULL}, + {"__exit__", (PyCFunction)ctxmanager_restore_global, METH_VARARGS, NULL}, + {NULL, NULL} +}; + +static PyTypeObject PyDecContextManager_Type = +{ + PyVarObject_HEAD_INIT(NULL, 0) + "decimal.ContextManager", /* tp_name */ + sizeof(PyDecContextManagerObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor) ctxmanager_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + (getattrfunc) 0, /* tp_getattr */ + (setattrfunc) 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc) 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + (getattrofunc) PyObject_GenericGetAttr, /* tp_getattro */ + (setattrofunc) 0, /* tp_setattro */ + (PyBufferProcs *) 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + ctxmanager_methods, /* tp_methods */ +}; + + +/******************************************************************************/ +/* New Decimal Object */ +/******************************************************************************/ + +static PyObject * +PyDecType_New(PyTypeObject *type) +{ + PyObject *dec; + + if (type == &PyDec_Type) { + dec = (PyObject *)PyObject_New(PyDecObject, &PyDec_Type); + } + else { + dec = type->tp_alloc(type, 0); + } + if (dec == NULL) { + return NULL; + } + + MPD(dec) = mpd_qnew(); + if (MPD(dec) == NULL) { + Py_DECREF(dec); + PyErr_NoMemory(); + return NULL; + } + + return dec; +} +#define dec_alloc() PyDecType_New(&PyDec_Type) + +static void +dec_dealloc(PyObject *dec) +{ + if (MPD(dec)) { + mpd_del(MPD(dec)); + } + Py_TYPE(dec)->tp_free(dec); +} + + +/******************************************************************************/ +/* Conversions to Decimal */ +/******************************************************************************/ + +Py_LOCAL_INLINE(int) +is_space(enum PyUnicode_Kind kind, void *data, Py_ssize_t pos) +{ + Py_UCS4 ch = PyUnicode_READ(kind, data, pos); + return Py_UNICODE_ISSPACE(ch); +} + +/* Return the ASCII representation of a numeric Unicode string. The numeric + string may contain ascii characters in the range [1, 127], any Unicode + space and any unicode digit. If strip_ws is true, leading and trailing + whitespace is stripped. + + Return NULL if malloc fails and an empty string if invalid characters + are found. */ +static char * +numeric_as_ascii(const PyObject *u, int strip_ws) +{ + enum PyUnicode_Kind kind; + void *data; + Py_UCS4 ch; + char *res, *cp; + Py_ssize_t j, len; + int d; + + assert(PyUnicode_IS_READY(u)); + + kind = PyUnicode_KIND(u); + data = PyUnicode_DATA(u); + len = PyUnicode_GET_LENGTH(u); + + cp = res = PyMem_Malloc(len+1); + if (res == NULL) { + PyErr_NoMemory(); + return NULL; + } + + j = 0; + if (strip_ws) { + while (len > 0 && is_space(kind, data, len-1)) { + len--; + } + while (j < len && is_space(kind, data, j)) { + j++; + } + } + + for (; j < len; j++) { + ch = PyUnicode_READ(kind, data, j); + if (0 < ch && ch <= 127) { + *cp++ = ch; + continue; + } + if (Py_UNICODE_ISSPACE(ch)) { + *cp++ = ' '; + continue; + } + d = Py_UNICODE_TODECIMAL(ch); + if (d < 0) { + /* empty string triggers ConversionSyntax */ + *res = '\0'; + return res; + } + *cp++ = '0' + d; + } + *cp = '\0'; + return res; +} + +/* Return a new PyDecObject or a subtype from a C string. Use the context + during conversion. */ +static PyObject * +PyDecType_FromCString(PyTypeObject *type, const char *s, + PyObject *context) +{ + PyObject *dec; + uint32_t status = 0; + + dec = PyDecType_New(type); + if (dec == NULL) { + return NULL; + } + + mpd_qset_string(MPD(dec), s, CTX(context), &status); + if (dec_addstatus(context, status)) { + Py_DECREF(dec); + return NULL; + } + return dec; +} + +/* Return a new PyDecObject or a subtype from a C string. Attempt exact + conversion. If the operand cannot be converted exactly, set + InvalidOperation. */ +static PyObject * +PyDecType_FromCStringExact(PyTypeObject *type, const char *s, + PyObject *context) +{ + PyObject *dec; + uint32_t status = 0; + mpd_context_t maxctx; + + dec = PyDecType_New(type); + if (dec == NULL) { + return NULL; + } + + mpd_maxcontext(&maxctx); + + mpd_qset_string(MPD(dec), s, &maxctx, &status); + if (status & (MPD_Inexact|MPD_Rounded)) { + /* we want exact results */ + mpd_seterror(MPD(dec), MPD_Invalid_operation, &status); + } + status &= MPD_Errors; + if (dec_addstatus(context, status)) { + Py_DECREF(dec); + return NULL; + } + + return dec; +} + +/* Return a new PyDecObject or a subtype from a PyUnicodeObject. */ +static PyObject * +PyDecType_FromUnicode(PyTypeObject *type, const PyObject *u, + PyObject *context) +{ + PyObject *dec; + char *s; + + s = numeric_as_ascii(u, 0); + if (s == NULL) { + return NULL; + } + + dec = PyDecType_FromCString(type, s, context); + PyMem_Free(s); + return dec; +} + +/* Return a new PyDecObject or a subtype from a PyUnicodeObject. Attempt exact + * conversion. If the conversion is not exact, fail with InvalidOperation. + * Allow leading and trailing whitespace in the input operand. */ +static PyObject * +PyDecType_FromUnicodeExactWS(PyTypeObject *type, const PyObject *u, + PyObject *context) +{ + PyObject *dec; + char *s; + + s = numeric_as_ascii(u, 1); + if (s == NULL) { + return NULL; + } + + dec = PyDecType_FromCStringExact(type, s, context); + PyMem_Free(s); + return dec; +} + +/* Set PyDecObject from triple without any error checking. */ +Py_LOCAL_INLINE(void) +_dec_settriple(PyObject *dec, uint8_t sign, uint32_t v, mpd_ssize_t exp) +{ + +#ifdef CONFIG_64 + MPD(dec)->data[0] = v; + MPD(dec)->len = 1; +#else + uint32_t q, r; + q = v / MPD_RADIX; + r = v - q * MPD_RADIX; + MPD(dec)->data[1] = q; + MPD(dec)->data[0] = r; + MPD(dec)->len = q ? 2 : 1; +#endif + mpd_set_flags(MPD(dec), sign); + MPD(dec)->exp = exp; + mpd_setdigits(MPD(dec)); +} + +/* Return a new PyDecObject from an mpd_ssize_t. */ +static PyObject * +PyDecType_FromSsize(PyTypeObject *type, mpd_ssize_t v, PyObject *context) +{ + PyObject *dec; + uint32_t status = 0; + + dec = PyDecType_New(type); + if (dec == NULL) { + return NULL; + } + + mpd_qset_ssize(MPD(dec), v, CTX(context), &status); + if (dec_addstatus(context, status)) { + Py_DECREF(dec); + return NULL; + } + return dec; +} + +/* Return a new PyDecObject from an mpd_ssize_t. Conversion is exact. */ +static PyObject * +PyDecType_FromSsizeExact(PyTypeObject *type, mpd_ssize_t v, PyObject *context) +{ + PyObject *dec; + uint32_t status = 0; + mpd_context_t maxctx; + + dec = PyDecType_New(type); + if (dec == NULL) { + return NULL; + } + + mpd_maxcontext(&maxctx); + + mpd_qset_ssize(MPD(dec), v, &maxctx, &status); + if (dec_addstatus(context, status)) { + Py_DECREF(dec); + return NULL; + } + return dec; +} + +/* Convert from a PyLongObject. The context is not modified; flags set + during conversion are accumulated in the status parameter. */ +static PyObject * +dec_from_long(PyTypeObject *type, const PyObject *v, + const mpd_context_t *ctx, uint32_t *status) +{ + PyObject *dec; + PyLongObject *l = (PyLongObject *)v; + Py_ssize_t ob_size; + size_t len; + uint8_t sign; + + dec = PyDecType_New(type); + if (dec == NULL) { + return NULL; + } + + ob_size = Py_SIZE(l); + if (ob_size == 0) { + _dec_settriple(dec, MPD_POS, 0, 0); + return dec; + } + + if (ob_size < 0) { + len = -ob_size; + sign = MPD_NEG; + } + else { + len = ob_size; + sign = MPD_POS; + } + + if (len == 1) { + _dec_settriple(dec, sign, *l->ob_digit, 0); + mpd_qfinalize(MPD(dec), ctx, status); + return dec; + } + +#if PYLONG_BITS_IN_DIGIT == 30 + mpd_qimport_u32(MPD(dec), l->ob_digit, len, sign, PyLong_BASE, + ctx, status); +#elif PYLONG_BITS_IN_DIGIT == 15 + mpd_qimport_u16(MPD(dec), l->ob_digit, len, sign, PyLong_BASE, + ctx, status); +#else + #error "PYLONG_BITS_IN_DIGIT should be 15 or 30" +#endif + + return dec; +} + +/* Return a new PyDecObject from a PyLongObject. Use the context for + conversion. */ +static PyObject * +PyDecType_FromLong(PyTypeObject *type, const PyObject *pylong, + PyObject *context) +{ + PyObject *dec; + uint32_t status = 0; + + dec = dec_from_long(type, pylong, CTX(context), &status); + if (dec == NULL) { + return NULL; + } + + if (dec_addstatus(context, status)) { + Py_DECREF(dec); + return NULL; + } + + return dec; +} + +/* Return a new PyDecObject from a PyLongObject. Use a maximum context + for conversion. If the conversion is not exact, set InvalidOperation. */ +static PyObject * +PyDecType_FromLongExact(PyTypeObject *type, const PyObject *pylong, + PyObject *context) +{ + PyObject *dec; + uint32_t status = 0; + mpd_context_t maxctx; + + mpd_maxcontext(&maxctx); + dec = dec_from_long(type, pylong, &maxctx, &status); + if (dec == NULL) { + return NULL; + } + + if (status & (MPD_Inexact|MPD_Rounded)) { + /* we want exact results */ + mpd_seterror(MPD(dec), MPD_Invalid_operation, &status); + } + status &= MPD_Errors; + if (dec_addstatus(context, status)) { + Py_DECREF(dec); + return NULL; + } + + return dec; +} + +/* Return a PyDecObject or a subtype from a PyFloatObject. + Conversion is exact. */ +static PyObject * +PyDecType_FromFloatExact(PyTypeObject *type, PyObject *v, + PyObject *context) +{ + PyObject *dec, *tmp; + PyObject *n, *d, *n_d; + mpd_ssize_t k; + double x; + int sign; + mpd_t *d1, *d2; + uint32_t status = 0; + mpd_context_t maxctx; + + + assert(PyType_IsSubtype(type, &PyDec_Type)); + + if (PyLong_Check(v)) { + return PyDecType_FromLongExact(type, v, context); + } + if (!PyFloat_Check(v)) { + PyErr_SetString(PyExc_TypeError, + "argument must be int of float"); + return NULL; + } + + x = PyFloat_AsDouble(v); + if (x == -1.0 && PyErr_Occurred()) { + return NULL; + } + sign = (copysign(1.0, x) == 1.0) ? 0 : 1; + + if (Py_IS_NAN(x) || Py_IS_INFINITY(x)) { + dec = PyDecType_New(type); + if (dec == NULL) { + return NULL; + } + if (Py_IS_NAN(x)) { + /* decimal.py calls repr(float(+-nan)), + * which always gives a positive result. */ + mpd_setspecial(MPD(dec), MPD_POS, MPD_NAN); + } + else { + mpd_setspecial(MPD(dec), sign, MPD_INF); + } + return dec; + } + + /* absolute value of the float */ + tmp = PyObject_CallMethod(v, "__abs__", NULL); + if (tmp == NULL) { + return NULL; + } + + /* float as integer ratio: numerator/denominator */ + n_d = PyObject_CallMethod(tmp, "as_integer_ratio", NULL); + Py_DECREF(tmp); + if (n_d == NULL) { + return NULL; + } + n = PyTuple_GET_ITEM(n_d, 0); + d = PyTuple_GET_ITEM(n_d, 1); + + tmp = PyObject_CallMethod(d, "bit_length", NULL); + if (tmp == NULL) { + Py_DECREF(n_d); + return NULL; + } + k = PyLong_AsSsize_t(tmp); + Py_DECREF(tmp); + if (k == -1 && PyErr_Occurred()) { + Py_DECREF(n_d); + return NULL; + } + k--; + + dec = PyDecType_FromLongExact(type, n, context); + Py_DECREF(n_d); + if (dec == NULL) { + return NULL; + } + + d1 = mpd_qnew(); + if (d1 == NULL) { + Py_DECREF(dec); + PyErr_NoMemory(); + return NULL; + } + d2 = mpd_qnew(); + if (d2 == NULL) { + mpd_del(d1); + Py_DECREF(dec); + PyErr_NoMemory(); + return NULL; + } + + mpd_maxcontext(&maxctx); + mpd_qset_uint(d1, 5, &maxctx, &status); + mpd_qset_ssize(d2, k, &maxctx, &status); + mpd_qpow(d1, d1, d2, &maxctx, &status); + if (dec_addstatus(context, status)) { + mpd_del(d1); + mpd_del(d2); + Py_DECREF(dec); + return NULL; + } + + /* result = n * 5**k */ + mpd_qmul(MPD(dec), MPD(dec), d1, &maxctx, &status); + mpd_del(d1); + mpd_del(d2); + if (dec_addstatus(context, status)) { + Py_DECREF(dec); + return NULL; + } + /* result = +- n * 5**k * 10**-k */ + mpd_set_sign(MPD(dec), sign); + MPD(dec)->exp = -k; + + return dec; +} + +static PyObject * +PyDecType_FromFloat(PyTypeObject *type, PyObject *v, + PyObject *context) +{ + PyObject *dec; + uint32_t status = 0; + + dec = PyDecType_FromFloatExact(type, v, context); + if (dec == NULL) { + return NULL; + } + + mpd_qfinalize(MPD(dec), CTX(context), &status); + if (dec_addstatus(context, status)) { + Py_DECREF(dec); + return NULL; + } + + return dec; +} + +static PyObject * +sequence_as_tuple(PyObject *v, PyObject *ex, const char *mesg) +{ + if (PyTuple_Check(v)) { + Py_INCREF(v); + return v; + } + if (PyList_Check(v)) { + return PyList_AsTuple(v); + } + + PyErr_SetString(ex, mesg); + return NULL; +} + +/* Return a new C string representation of a DecimalTuple. */ +static char * +dectuple_as_str(PyObject *dectuple) +{ + PyObject *digits = NULL, *tmp; + char *decstring = NULL; + char sign_special[6]; + char *cp; + long sign, l; + mpd_ssize_t exp = 0; + Py_ssize_t i, mem, tsize; + int n; + + assert(PyTuple_Check(dectuple)); + + if (PyTuple_Size(dectuple) != 3) { + PyErr_SetString(PyExc_ValueError, + "argument must be a sequence of length 3"); + goto error; + } + + /* sign */ + tmp = PyTuple_GET_ITEM(dectuple, 0); + if (!PyLong_Check(tmp)) { + PyErr_SetString(PyExc_ValueError, + "sign must be an integer with the value 0 or 1"); + goto error; + } + sign = PyLong_AsLong(tmp); + if (sign == -1 && PyErr_Occurred()) { + goto error; + } + if (sign != 0 && sign != 1) { + PyErr_SetString(PyExc_ValueError, + "sign must be an integer with the value 0 or 1"); + goto error; + } + sign_special[0] = sign ? '-' : '+'; + sign_special[1] = '\0'; + + /* exponent or encoding for a special number */ + tmp = PyTuple_GET_ITEM(dectuple, 2); + if (PyUnicode_Check(tmp)) { + /* special */ + if (PyUnicode_CompareWithASCIIString(tmp, "F") == 0) { + strcat(sign_special, "Inf"); + } + else if (PyUnicode_CompareWithASCIIString(tmp, "n") == 0) { + strcat(sign_special, "NaN"); + } + else if (PyUnicode_CompareWithASCIIString(tmp, "N") == 0) { + strcat(sign_special, "sNaN"); + } + else { + PyErr_SetString(PyExc_ValueError, + "string argument in the third position " + "must be 'F', 'n' or 'N'"); + goto error; + } + } + else { + /* exponent */ + if (!PyLong_Check(tmp)) { + PyErr_SetString(PyExc_ValueError, + "exponent must be an integer"); + goto error; + } + exp = PyLong_AsSsize_t(tmp); + if (exp == -1 && PyErr_Occurred()) { + goto error; + } + } + + /* coefficient */ + digits = sequence_as_tuple(PyTuple_GET_ITEM(dectuple, 1), + PyExc_ValueError, "coefficient must be a tuple of digits"); + if (digits == NULL) { + goto error; + } + + tsize = PyTuple_Size(digits); + /* [sign][coeffdigits+1][E][-][expdigits+1]['\0'] */ + mem = 1 + tsize + 3 + MPD_EXPDIGITS + 2; + cp = decstring = PyMem_Malloc(mem); + if (decstring == NULL) { + PyErr_NoMemory(); + goto error; + } + + n = snprintf(cp, mem, "%s", sign_special); + if (n < 0 || n >= mem) { + PyErr_SetString(PyExc_RuntimeError, + "internal error in dec_sequence_as_str"); + goto error; + } + cp += n; + + if (tsize == 0 && sign_special[1] == '\0') { + /* empty tuple: zero coefficient, except for special numbers */ + *cp++ = '0'; + } + for (i = 0; i < tsize; i++) { + tmp = PyTuple_GET_ITEM(digits, i); + if (!PyLong_Check(tmp)) { + PyErr_SetString(PyExc_ValueError, + "coefficient must be a tuple of digits"); + goto error; + } + l = PyLong_AsLong(tmp); + if (l == -1 && PyErr_Occurred()) { + goto error; + } + if (l < 0 || l > 9) { + PyErr_SetString(PyExc_ValueError, + "coefficient must be a tuple of digits"); + goto error; + } + *cp++ = (char)l + '0'; + } + *cp = '\0'; + + if (sign_special[1] == '\0') { + /* not a special number */ + *cp++ = 'E'; + n = snprintf(cp, MPD_EXPDIGITS+1, "%" PRI_mpd_ssize_t, exp); + if (n < 0 || n >= MPD_EXPDIGITS+1) { + PyErr_SetString(PyExc_RuntimeError, + "internal error in dec_sequence_as_str"); + goto error; + } + } + + Py_XDECREF(digits); + return decstring; + + +error: + Py_XDECREF(digits); + if (decstring) PyMem_Free(decstring); + return NULL; +} + +/* Currently accepts tuples and lists. */ +static PyObject * +PyDecType_FromSequence(PyTypeObject *type, PyObject *v, + PyObject *context) +{ + PyObject *dectuple; + PyObject *dec; + char *s; + + dectuple = sequence_as_tuple(v, PyExc_TypeError, + "argument must be a tuple or list"); + if (dectuple == NULL) { + return NULL; + } + + s = dectuple_as_str(dectuple); + Py_DECREF(dectuple); + if (s == NULL) { + return NULL; + } + + dec = PyDecType_FromCString(type, s, context); + + PyMem_Free(s); + return dec; +} + +/* Currently accepts tuples and lists. */ +static PyObject * +PyDecType_FromSequenceExact(PyTypeObject *type, PyObject *v, + PyObject *context) +{ + PyObject *dectuple; + PyObject *dec; + char *s; + + dectuple = sequence_as_tuple(v, PyExc_TypeError, + "argument must be a tuple or list"); + if (dectuple == NULL) { + return NULL; + } + + s = dectuple_as_str(dectuple); + Py_DECREF(dectuple); + if (s == NULL) { + return NULL; + } + + dec = PyDecType_FromCStringExact(type, s, context); + + PyMem_Free(s); + return dec; +} + +#define PyDec_FromCString(str, context) \ + PyDecType_FromCString(&PyDec_Type, str, context) +#define PyDec_FromCStringExact(str, context) \ + PyDecType_FromCStringExact(&PyDec_Type, str, context) + +#define PyDec_FromUnicode(unicode, context) \ + PyDecType_FromUnicode(&PyDec_Type, unicode, context) +#define PyDec_FromUnicodeExact(unicode, context) \ + PyDecType_FromUnicodeExact(&PyDec_Type, unicode, context) +#define PyDec_FromUnicodeExactWS(unicode, context) \ + PyDecType_FromUnicodeExactWS(&PyDec_Type, unicode, context) + +#define PyDec_FromSsize(v, context) \ + PyDecType_FromSsize(&PyDec_Type, v, context) +#define PyDec_FromSsizeExact(v, context) \ + PyDecType_FromSsizeExact(&PyDec_Type, v, context) + +#define PyDec_FromLong(pylong, context) \ + PyDecType_FromLong(&PyDec_Type, pylong, context) +#define PyDec_FromLongExact(pylong, context) \ + PyDecType_FromLongExact(&PyDec_Type, pylong, context) + +#define PyDec_FromFloat(pyfloat, context) \ + PyDecType_FromFloat(&PyDec_Type, pyfloat, context) +#define PyDec_FromFloatExact(pyfloat, context) \ + PyDecType_FromFloatExact(&PyDec_Type, pyfloat, context) + +#define PyDec_FromSequence(sequence, context) \ + PyDecType_FromSequence(&PyDec_Type, sequence, context) +#define PyDec_FromSequenceExact(sequence, context) \ + PyDecType_FromSequenceExact(&PyDec_Type, sequence, context) + +/* class method */ +static PyObject * +dec_from_float(PyObject *dec, PyObject *pyfloat) +{ + PyObject *context; + + CURRENT_CONTEXT(context); + return PyDecType_FromFloatExact((PyTypeObject *)dec, pyfloat, context); +} + +/* create_decimal_from_float */ +static PyObject * +ctx_from_float(PyObject *context, PyObject *v) +{ + return PyDec_FromFloat(v, context); +} + +/* Apply the context to the input operand. Return a new PyDecObject. */ +static PyObject * +dec_apply(PyObject *v, PyObject *context) +{ + PyObject *result; + uint32_t status = 0; + + result = dec_alloc(); + if (result == NULL) { + return NULL; + } + + mpd_qcopy(MPD(result), MPD(v), &status); + if (dec_addstatus(context, status)) { + Py_DECREF(result); + return NULL; + } + + mpd_qfinalize(MPD(result), CTX(context), &status); + if (dec_addstatus(context, status)) { + Py_DECREF(result); + return NULL; + } + + return result; +} + +/* 'v' can have any type accepted by the Decimal constructor. Attempt + an exact conversion. If the result does not meet the restrictions + for an mpd_t, fail with InvalidOperation. */ +static PyObject * +PyDecType_FromObjectExact(PyTypeObject *type, PyObject *v, PyObject *context) +{ + if (v == NULL) { + return PyDecType_FromSsizeExact(type, 0, context); + } + else if (PyDec_Check(v)) { + Py_INCREF(v); + return v; + } + else if (PyUnicode_Check(v)) { + return PyDecType_FromUnicodeExactWS(type, v, context); + } + else if (PyLong_Check(v)) { + return PyDecType_FromLongExact(type, v, context); + } + else if (PyTuple_Check(v) || PyList_Check(v)) { + return PyDecType_FromSequenceExact(type, v, context); + } + else if (PyFloat_Check(v)) { + if (dec_addstatus(context, MPD_Float_operation)) { + return NULL; + } + return PyDecType_FromFloatExact(type, v, context); + } + else { + PyErr_Format(PyExc_TypeError, + "conversion from %s to Decimal is not supported", + v->ob_type->tp_name); + return NULL; + } +} + +/* The context is used during conversion. This function is the + equivalent of context.create_decimal(). */ +static PyObject * +PyDec_FromObject(PyObject *v, PyObject *context) +{ + if (v == NULL) { + return PyDec_FromSsize(0, context); + } + else if (PyDec_Check(v)) { + mpd_context_t *ctx = CTX(context); + if (mpd_isnan(MPD(v)) && + MPD(v)->digits > ctx->prec - ctx->clamp) { + /* Special case: too many NaN payload digits */ + PyObject *result; + if (dec_addstatus(context, MPD_Conversion_syntax)) { + return NULL; + } + result = dec_alloc(); + if (result == NULL) { + return NULL; + } + mpd_setspecial(MPD(result), MPD_POS, MPD_NAN); + return result; + } + return dec_apply(v, context); + } + else if (PyUnicode_Check(v)) { + return PyDec_FromUnicode(v, context); + } + else if (PyLong_Check(v)) { + return PyDec_FromLong(v, context); + } + else if (PyTuple_Check(v) || PyList_Check(v)) { + return PyDec_FromSequence(v, context); + } + else if (PyFloat_Check(v)) { + if (dec_addstatus(context, MPD_Float_operation)) { + return NULL; + } + return PyDec_FromFloat(v, context); + } + else { + PyErr_Format(PyExc_TypeError, + "conversion from %s to Decimal is not supported", + v->ob_type->tp_name); + return NULL; + } +} + +static PyObject * +dec_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"value", "context", NULL}; + PyObject *v = NULL; + PyObject *context; + + CURRENT_CONTEXT(context); + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, + &v, &context)) { + return NULL; + } + CONTEXT_CHECK_VA(context); + + return PyDecType_FromObjectExact(type, v, context); +} + +static PyObject * +ctx_create_decimal(PyObject *context, PyObject *args) +{ + PyObject *v = NULL; + + if (!PyArg_ParseTuple(args, "|O", &v)) { + return NULL; + } + + return PyDec_FromObject(v, context); +} + + +/******************************************************************************/ +/* Implicit conversions to Decimal */ +/******************************************************************************/ + +/* Try to convert PyObject v to a new PyDecObject conv. If the conversion + fails, set conv to NULL (exception is set). If the conversion is not + implemented, set conv to Py_NotImplemented. */ +#define NOT_IMPL 0 +#define TYPE_ERR 1 +Py_LOCAL_INLINE(int) +convert_op(int type_err, PyObject **conv, PyObject *v, PyObject *context) +{ + + if (PyDec_Check(v)) { + *conv = v; + Py_INCREF(v); + return 1; + } + if (PyLong_Check(v)) { + *conv = PyDec_FromLongExact(v, context); + if (*conv == NULL) { + return 0; + } + return 1; + } + + if (type_err) { + PyErr_Format(PyExc_TypeError, + "conversion from %s to Decimal is not supported", + v->ob_type->tp_name); + } + else { + Py_INCREF(Py_NotImplemented); + *conv = Py_NotImplemented; + } + return 0; +} + +/* Return NotImplemented for unsupported types. */ +#define CONVERT_OP(a, v, context) \ + if (!convert_op(NOT_IMPL, a, v, context)) { \ + return *(a); \ + } + +#define CONVERT_BINOP(a, b, v, w, context) \ + if (!convert_op(NOT_IMPL, a, v, context)) { \ + return *(a); \ + } \ + if (!convert_op(NOT_IMPL, b, w, context)) { \ + Py_DECREF(*(a)); \ + return *(b); \ + } + +#define CONVERT_TERNOP(a, b, c, v, w, x, context) \ + if (!convert_op(NOT_IMPL, a, v, context)) { \ + return *(a); \ + } \ + if (!convert_op(NOT_IMPL, b, w, context)) { \ + Py_DECREF(*(a)); \ + return *(b); \ + } \ + if (!convert_op(NOT_IMPL, c, x, context)) { \ + Py_DECREF(*(a)); \ + Py_DECREF(*(b)); \ + return *(c); \ + } + +/* Raise TypeError for unsupported types. */ +#define CONVERT_OP_RAISE(a, v, context) \ + if (!convert_op(TYPE_ERR, a, v, context)) { \ + return NULL; \ + } + +#define CONVERT_BINOP_RAISE(a, b, v, w, context) \ + if (!convert_op(TYPE_ERR, a, v, context)) { \ + return NULL; \ + } \ + if (!convert_op(TYPE_ERR, b, w, context)) { \ + Py_DECREF(*(a)); \ + return NULL; \ + } + +#define CONVERT_TERNOP_RAISE(a, b, c, v, w, x, context) \ + if (!convert_op(TYPE_ERR, a, v, context)) { \ + return NULL; \ + } \ + if (!convert_op(TYPE_ERR, b, w, context)) { \ + Py_DECREF(*(a)); \ + return NULL; \ + } \ + if (!convert_op(TYPE_ERR, c, x, context)) { \ + Py_DECREF(*(a)); \ + Py_DECREF(*(b)); \ + return NULL; \ + } + + +/******************************************************************************/ +/* Implicit conversions to Decimal for comparison */ +/******************************************************************************/ + +/* Convert rationals for comparison */ +static PyObject *Rational = NULL; +static PyObject * +multiply_by_denominator(PyObject *v, PyObject *r, PyObject *context) +{ + PyObject *result; + PyObject *tmp = NULL; + PyObject *denom = NULL; + uint32_t status = 0; + mpd_context_t maxctx; + mpd_ssize_t exp; + mpd_t *vv; + + /* v is not special, r is a rational */ + tmp = PyObject_GetAttrString(r, "denominator"); + if (tmp == NULL) { + return NULL; + } + denom = PyDec_FromLongExact(tmp, context); + Py_DECREF(tmp); + if (denom == NULL) { + return NULL; + } + + vv = mpd_qncopy(MPD(v)); + if (vv == NULL) { + Py_DECREF(denom); + PyErr_NoMemory(); + return NULL; + } + result = dec_alloc(); + if (result == NULL) { + Py_DECREF(denom); + mpd_del(vv); + return NULL; + } + + mpd_maxcontext(&maxctx); + /* Prevent Overflow in the following multiplication. The result of + the multiplication is only used in mpd_qcmp, which can handle + values that are technically out of bounds, like (for 32-bit) + 99999999999999999999...99999999e+425000000. */ + exp = vv->exp; + vv->exp = 0; + mpd_qmul(MPD(result), vv, MPD(denom), &maxctx, &status); + MPD(result)->exp = exp; + + Py_DECREF(denom); + mpd_del(vv); + /* If any status has been accumulated during the multiplication, + the result is invalid. This is very unlikely, since even the + 32-bit version supports 425000000 digits. */ + if (status) { + PyErr_SetString(PyExc_ValueError, + "exact conversion for comparison failed"); + Py_DECREF(result); + return NULL; + } + + return result; +} + +static PyObject * +numerator_as_decimal(PyObject *r, PyObject *context) +{ + PyObject *tmp, *num; + + tmp = PyObject_GetAttrString(r, "numerator"); + if (tmp == NULL) { + return NULL; + } + + num = PyDec_FromLongExact(tmp, context); + Py_DECREF(tmp); + return num; +} + +/* Convert v and w for comparison. v is a Decimal. If w is a Rational, both + v and w have to be transformed. Return 1 for success, with new references + to the converted objects in vcmp and wcmp. Return 0 for failure. In that + case wcmp is either NULL or Py_NotImplemented (new reference) and vcmp + is undefined. */ +static int +convert_op_cmp(PyObject **vcmp, PyObject **wcmp, PyObject *v, PyObject *w, + int op, PyObject *context) +{ + mpd_context_t *ctx = CTX(context); + + *vcmp = v; + + if (PyDec_Check(w)) { + Py_INCREF(w); + *wcmp = w; + } + else if (PyLong_Check(w)) { + *wcmp = PyDec_FromLongExact(w, context); + } + else if (PyFloat_Check(w)) { + if (op != Py_EQ && op != Py_NE && + dec_addstatus(context, MPD_Float_operation)) { + *wcmp = NULL; + } + else { + ctx->status |= MPD_Float_operation; + *wcmp = PyDec_FromFloatExact(w, context); + } + } + else if (PyComplex_Check(w) && (op == Py_EQ || op == Py_NE)) { + Py_complex c = PyComplex_AsCComplex(w); + if (c.real == -1.0 && PyErr_Occurred()) { + *wcmp = NULL; + } + else if (c.imag == 0.0) { + PyObject *tmp = PyFloat_FromDouble(c.real); + if (tmp == NULL) { + *wcmp = NULL; + } + else { + ctx->status |= MPD_Float_operation; + *wcmp = PyDec_FromFloatExact(tmp, context); + Py_DECREF(tmp); + } + } + else { + Py_INCREF(Py_NotImplemented); + *wcmp = Py_NotImplemented; + } + } + else if (PyObject_IsInstance(w, Rational)) { + *wcmp = numerator_as_decimal(w, context); + if (*wcmp && !mpd_isspecial(MPD(v))) { + *vcmp = multiply_by_denominator(v, w, context); + if (*vcmp == NULL) { + Py_CLEAR(*wcmp); + } + } + } + else { + Py_INCREF(Py_NotImplemented); + *wcmp = Py_NotImplemented; + } + + if (*wcmp == NULL || *wcmp == Py_NotImplemented) { + return 0; + } + if (*vcmp == v) { + Py_INCREF(v); + } + return 1; +} + +#define CONVERT_BINOP_CMP(vcmp, wcmp, v, w, op, ctx) \ + if (!convert_op_cmp(vcmp, wcmp, v, w, op, ctx)) { \ + return *(wcmp); \ + } \ + + +/******************************************************************************/ +/* Conversions from decimal */ +/******************************************************************************/ + +static PyObject * +unicode_fromascii(const char *s, Py_ssize_t size) +{ + PyObject *res; + + res = PyUnicode_New(size, 127); + if (res == NULL) { + return NULL; + } + + memcpy(PyUnicode_1BYTE_DATA(res), s, size); + return res; +} + +/* PyDecObject as a string. The default module context is only used for + the value of 'capitals'. */ +static PyObject * +dec_str(PyObject *dec) +{ + PyObject *res, *context; + mpd_ssize_t size; + char *cp; + + CURRENT_CONTEXT(context); + size = mpd_to_sci_size(&cp, MPD(dec), CtxCaps(context)); + if (size < 0) { + PyErr_NoMemory(); + return NULL; + } + + res = unicode_fromascii(cp, size); + mpd_free(cp); + return res; +} + +/* Representation of a PyDecObject. */ +static PyObject * +dec_repr(PyObject *dec) +{ + PyObject *res, *context; + char *cp; + + CURRENT_CONTEXT(context); + cp = mpd_to_sci(MPD(dec), CtxCaps(context)); + if (cp == NULL) { + PyErr_NoMemory(); + return NULL; + } + + res = PyUnicode_FromFormat("Decimal('%s')", cp); + mpd_free(cp); + return res; +} + +/* Convert decimal_point or thousands_sep, which may be multibyte or in + the range [128, 255], to a UTF8 string. */ +static PyObject * +dotsep_as_utf8(const char *s) +{ + PyObject *utf8; + PyObject *tmp; + wchar_t buf[2]; + size_t n; + + n = mbstowcs(buf, s, 2); + if (n != 1) { /* Issue #7442 */ + PyErr_SetString(PyExc_ValueError, + "invalid decimal point or unsupported " + "combination of LC_CTYPE and LC_NUMERIC"); + return NULL; + } + tmp = PyUnicode_FromWideChar(buf, n); + if (tmp == NULL) { + return NULL; + } + utf8 = PyUnicode_AsUTF8String(tmp); + Py_DECREF(tmp); + return utf8; +} + +/* Formatted representation of a PyDecObject. */ +static PyObject * +dec_format(PyObject *dec, PyObject *args) +{ + PyObject *result = NULL; + PyObject *override = NULL; + PyObject *dot = NULL; + PyObject *sep = NULL; + PyObject *grouping = NULL; + PyObject *fmt = NULL; + PyObject *fmtarg; + PyObject *context; + mpd_spec_t spec; + char *decstring= NULL; + uint32_t status = 0; + size_t n; + + + CURRENT_CONTEXT(context); + if (!PyArg_ParseTuple(args, "O|O", &fmtarg, &override)) { + return NULL; + } + + if (PyUnicode_Check(fmtarg)) { + fmt = PyUnicode_AsUTF8String(fmtarg); + if (fmt == NULL) { + return NULL; + } + } + else { + PyErr_SetString(PyExc_TypeError, + "format arg must be str"); + return NULL; + } + + if (!mpd_parse_fmt_str(&spec, PyBytes_AS_STRING(fmt), + CtxCaps(context))) { + PyErr_SetString(PyExc_ValueError, + "invalid format string"); + goto finish; + } + if (override) { + /* Values for decimal_point, thousands_sep and grouping can + be explicitly specified in the override dict. These values + take precedence over the values obtained from localeconv() + in mpd_parse_fmt_str(). The feature is not documented and + is only used in test_decimal. */ + if (!PyDict_Check(override)) { + PyErr_SetString(PyExc_TypeError, + "optional argument must be a dict"); + goto finish; + } + if ((dot = PyDict_GetItemString(override, "decimal_point"))) { + if ((dot = PyUnicode_AsUTF8String(dot)) == NULL) { + goto finish; + } + spec.dot = PyBytes_AS_STRING(dot); + } + if ((sep = PyDict_GetItemString(override, "thousands_sep"))) { + if ((sep = PyUnicode_AsUTF8String(sep)) == NULL) { + goto finish; + } + spec.sep = PyBytes_AS_STRING(sep); + } + if ((grouping = PyDict_GetItemString(override, "grouping"))) { + if ((grouping = PyUnicode_AsUTF8String(grouping)) == NULL) { + goto finish; + } + spec.grouping = PyBytes_AS_STRING(grouping); + } + if (mpd_validate_lconv(&spec) < 0) { + PyErr_SetString(PyExc_ValueError, + "invalid override dict"); + goto finish; + } + } + else { + n = strlen(spec.dot); + if (n > 1 || (n == 1 && !isascii((uchar)spec.dot[0]))) { + /* fix locale dependent non-ascii characters */ + dot = dotsep_as_utf8(spec.dot); + if (dot == NULL) { + goto finish; + } + spec.dot = PyBytes_AS_STRING(dot); + } + n = strlen(spec.sep); + if (n > 1 || (n == 1 && !isascii((uchar)spec.sep[0]))) { + /* fix locale dependent non-ascii characters */ + sep = dotsep_as_utf8(spec.sep); + if (sep == NULL) { + goto finish; + } + spec.sep = PyBytes_AS_STRING(sep); + } + } + + + decstring = mpd_qformat_spec(MPD(dec), &spec, CTX(context), &status); + if (decstring == NULL) { + dec_addstatus(context, status); + goto finish; + } + result = PyUnicode_DecodeUTF8(decstring, strlen(decstring), NULL); + + +finish: + Py_XDECREF(grouping); + Py_XDECREF(sep); + Py_XDECREF(dot); + Py_XDECREF(fmt); + if (decstring) mpd_free(decstring); + return result; +} + +/* Return a PyLongObject from a PyDecObject, using the specified rounding + * mode. The context precision is not observed. */ +static PyObject * +dec_as_long(PyObject *dec, PyObject *context, int round) +{ + PyLongObject *pylong; + size_t maxsize, n; + Py_ssize_t i; + mpd_t *x; + mpd_context_t workctx; + uint32_t status = 0; + + if (mpd_isspecial(MPD(dec))) { + if (mpd_isnan(MPD(dec))) { + PyErr_SetString(PyExc_ValueError, + "cannot convert NaN to integer"); + } + else { + PyErr_SetString(PyExc_OverflowError, + "cannot convert Infinity to integer"); + } + return NULL; + } + + x = mpd_qnew(); + if (x == NULL) { + PyErr_NoMemory(); + return NULL; + } + workctx = *CTX(context); + workctx.round = round; + mpd_qround_to_int(x, MPD(dec), &workctx, &status); + if (dec_addstatus(context, status)) { + mpd_del(x); + return NULL; + } + + maxsize = mpd_sizeinbase(x, PyLong_BASE); + if (maxsize > PY_SSIZE_T_MAX) { + mpd_del(x); + PyErr_NoMemory(); + return NULL; + } + pylong = _PyLong_New(maxsize); + if (pylong == NULL) { + mpd_del(x); + return NULL; + } + + status = 0; +#if PYLONG_BITS_IN_DIGIT == 30 + n = mpd_qexport_u32(pylong->ob_digit, maxsize, PyLong_BASE, x, &status); +#elif PYLONG_BITS_IN_DIGIT == 15 + n = mpd_qexport_u16(pylong->ob_digit, maxsize, PyLong_BASE, x, &status); +#else + #error "PYLONG_BITS_IN_DIGIT should be 15 or 30" +#endif + if (dec_addstatus(context, status)) { + Py_DECREF((PyObject *) pylong); + mpd_del(x); + return NULL; + } + + i = n; + while ((i > 0) && (pylong->ob_digit[i-1] == 0)) { + i--; + } + + Py_SIZE(pylong) = i; + if (mpd_isnegative(x) && !mpd_iszero(x)) { + Py_SIZE(pylong) = -i; + } + + mpd_del(x); + return (PyObject *) pylong; +} + +static PyObject * +PyDec_ToIntegralValue(PyObject *dec, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"rounding", "context", NULL}; + PyObject *result; + PyObject *context; + uint32_t status = 0; + mpd_context_t workctx; + int round = -1; + + CURRENT_CONTEXT(context); + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO", kwlist, + &round, &context)) { + return NULL; + } + CONTEXT_CHECK_VA(context); + + workctx = *CTX(context); + if (round >= 0) { + if (!mpd_qsetround(&workctx, round)) { + return type_error_ptr(invalid_rounding_err); + } + } + + result = dec_alloc(); + if (result == NULL) { + return NULL; + } + + mpd_qround_to_int(MPD(result), MPD(dec), &workctx, &status); + if (dec_addstatus(context, status)) { + Py_DECREF(result); + return NULL; + } + + return result; +} + +static PyObject * +PyDec_ToIntegralExact(PyObject *dec, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"rounding", "context", NULL}; + PyObject *result; + PyObject *context; + uint32_t status = 0; + mpd_context_t workctx; + int round = -1; + + CURRENT_CONTEXT(context); + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO", kwlist, + &round, &context)) { + return NULL; + } + CONTEXT_CHECK_VA(context); + + workctx = *CTX(context); + if (round >= 0) { + if (!mpd_qsetround(&workctx, round)) { + return type_error_ptr(invalid_rounding_err); + } + } + + result = dec_alloc(); + if (result == NULL) { + return NULL; + } + + mpd_qround_to_intx(MPD(result), MPD(dec), &workctx, &status); + if (dec_addstatus(context, status)) { + Py_DECREF(result); + return NULL; + } + + return result; +} + +static PyObject * +PyDec_AsFloat(PyObject *dec) +{ + PyObject *f, *s; + + s = dec_str(dec); + if (s == NULL) { + return NULL; + } + + f = PyFloat_FromString(s); + Py_DECREF(s); + + return f; +} + +static PyObject * +PyDec_Round(PyObject *dec, PyObject *args) +{ + PyObject *result; + PyObject *x = NULL; + uint32_t status = 0; + PyObject *context; + + + CURRENT_CONTEXT(context); + if (!PyArg_ParseTuple(args, "|O", &x)) { + return NULL; + } + + if (x) { + mpd_uint_t dq[1] = {1}; + mpd_t q = {MPD_STATIC|MPD_CONST_DATA,0,1,1,1,dq}; + mpd_ssize_t y; + + if (!PyLong_Check(x)) { + PyErr_SetString(PyExc_TypeError, + "optional arg must be an integer"); + return NULL; + } + + y = PyLong_AsSsize_t(x); + if (y == -1 && PyErr_Occurred()) { + return NULL; + } + result = dec_alloc(); + if (result == NULL) { + return NULL; + } + + q.exp = (y == MPD_SSIZE_MIN) ? MPD_SSIZE_MAX : -y; + mpd_qquantize(MPD(result), MPD(dec), &q, CTX(context), &status); + if (dec_addstatus(context, status)) { + Py_DECREF(result); + return NULL; + } + + return result; + } + else { + return dec_as_long(dec, context, MPD_ROUND_HALF_EVEN); + } +} + +PyObject *DecimalTuple = NULL; +/* Return the DecimalTuple representation of a PyDecObject. */ +static PyObject * +PyDec_AsTuple(PyObject *dec, PyObject *dummy UNUSED) +{ + PyObject *result = NULL; + PyObject *sign = NULL; + PyObject *coeff = NULL; + PyObject *expt = NULL; + PyObject *tmp = NULL; + mpd_t *x = NULL; + char *intstring = NULL; + Py_ssize_t intlen, i; + + + x = mpd_qncopy(MPD(dec)); + if (x == NULL) { + PyErr_NoMemory(); + goto out; + } + + sign = PyLong_FromUnsignedLong(mpd_sign(MPD(dec))); + if (sign == NULL) { + goto out; + } + + if (mpd_isinfinite(x)) { + expt = PyUnicode_FromString("F"); + if (expt == NULL) { + goto out; + } + /* decimal.py has non-compliant infinity payloads. */ + coeff = Py_BuildValue("(i)", 0); + if (coeff == NULL) { + goto out; + } + } + else { + if (mpd_isnan(x)) { + expt = PyUnicode_FromString(mpd_isqnan(x)?"n":"N"); + } + else { + expt = PyLong_FromSsize_t(MPD(dec)->exp); + } + if (expt == NULL) { + goto out; + } + + /* coefficient is defined */ + if (x->len > 0) { + + /* make an integer */ + x->exp = 0; + /* clear NaN and sign */ + mpd_clear_flags(x); + intstring = mpd_to_sci(x, 1); + if (intstring == NULL) { + PyErr_NoMemory(); + goto out; + } + + intlen = strlen(intstring); + coeff = PyTuple_New(intlen); + if (coeff == NULL) { + goto out; + } + + for (i = 0; i < intlen; i++) { + tmp = PyLong_FromLong(intstring[i]-'0'); + if (tmp == NULL) { + goto out; + } + PyTuple_SET_ITEM(coeff, i, tmp); + } + } + else { + coeff = PyTuple_New(0); + if (coeff == NULL) { + goto out; + } + } + } + + result = PyObject_CallFunctionObjArgs(DecimalTuple, + sign, coeff, expt, NULL); + +out: + if (x) mpd_del(x); + if (intstring) mpd_free(intstring); + Py_XDECREF(sign); + Py_XDECREF(coeff); + Py_XDECREF(expt); + return result; +} + + +/******************************************************************************/ +/* Macros for converting mpdecimal functions to Decimal methods */ +/******************************************************************************/ + +/* Unary number method that uses the default module context. */ +#define Dec_UnaryNumberMethod(MPDFUNC) \ +static PyObject * \ +nm_##MPDFUNC(PyObject *self) \ +{ \ + PyObject *result; \ + PyObject *context; \ + uint32_t status = 0; \ + \ + CURRENT_CONTEXT(context); \ + if ((result = dec_alloc()) == NULL) { \ + return NULL; \ + } \ + \ + MPDFUNC(MPD(result), MPD(self), CTX(context), &status); \ + if (dec_addstatus(context, status)) { \ + Py_DECREF(result); \ + return NULL; \ + } \ + \ + return result; \ +} + +/* Binary number method that uses default module context. */ +#define Dec_BinaryNumberMethod(MPDFUNC) \ +static PyObject * \ +nm_##MPDFUNC(PyObject *self, PyObject *other) \ +{ \ + PyObject *a, *b; \ + PyObject *result; \ + PyObject *context; \ + uint32_t status = 0; \ + \ + CURRENT_CONTEXT(context) ; \ + CONVERT_BINOP(&a, &b, self, other, context); \ + \ + if ((result = dec_alloc()) == NULL) { \ + Py_DECREF(a); \ + Py_DECREF(b); \ + return NULL; \ + } \ + \ + MPDFUNC(MPD(result), MPD(a), MPD(b), CTX(context), &status); \ + Py_DECREF(a); \ + Py_DECREF(b); \ + if (dec_addstatus(context, status)) { \ + Py_DECREF(result); \ + return NULL; \ + } \ + \ + return result; \ +} + +/* Boolean function without a context arg. */ +#define Dec_BoolFunc(MPDFUNC) \ +static PyObject * \ +dec_##MPDFUNC(PyObject *self, PyObject *dummy UNUSED) \ +{ \ + return MPDFUNC(MPD(self)) ? incr_true() : incr_false(); \ +} + +/* Boolean function with an optional context arg. */ +#define Dec_BoolFuncVA(MPDFUNC) \ +static PyObject * \ +dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \ +{ \ + static char *kwlist[] = {"context", NULL}; \ + PyObject *context; \ + \ + CURRENT_CONTEXT(context); \ + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, \ + &context)) { \ + return NULL; \ + } \ + CONTEXT_CHECK_VA(context); \ + \ + return MPDFUNC(MPD(self), CTX(context)) ? incr_true() : incr_false(); \ +} + +/* Unary function with an optional context arg. */ +#define Dec_UnaryFuncVA(MPDFUNC) \ +static PyObject * \ +dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \ +{ \ + static char *kwlist[] = {"context", NULL}; \ + PyObject *result; \ + PyObject *context; \ + uint32_t status = 0; \ + \ + CURRENT_CONTEXT(context); \ + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, \ + &context)) { \ + return NULL; \ + } \ + CONTEXT_CHECK_VA(context); \ + \ + if ((result = dec_alloc()) == NULL) { \ + return NULL; \ + } \ + \ + MPDFUNC(MPD(result), MPD(self), CTX(context), &status); \ + if (dec_addstatus(context, status)) { \ + Py_DECREF(result); \ + return NULL; \ + } \ + \ + return result; \ +} + +/* Unary function with an optional context arg. The actual MPDFUNC + only takes a status parameter. */ +#define Dec_UnaryFuncVA_NO_CTX(MPDFUNC) \ +static PyObject * \ +dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \ +{ \ + static char *kwlist[] = {"context", NULL}; \ + PyObject *result; \ + PyObject *context; \ + uint32_t status = 0; \ + \ + CURRENT_CONTEXT(context); \ + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, \ + &context)) { \ + return NULL; \ + } \ + CONTEXT_CHECK_VA(context); \ + \ + if ((result = dec_alloc()) == NULL) { \ + return NULL; \ + } \ + \ + MPDFUNC(MPD(result), MPD(self), &status); \ + if (dec_addstatus(context, status)) { \ + Py_DECREF(result); \ + return NULL; \ + } \ + \ + return result; \ +} + +/* Binary function with an optional context arg. */ +#define Dec_BinaryFuncVA(MPDFUNC) \ +static PyObject * \ +dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \ +{ \ + static char *kwlist[] = {"other", "context", NULL}; \ + PyObject *other, *context; \ + PyObject *a, *b; \ + PyObject *result; \ + uint32_t status = 0; \ + \ + CURRENT_CONTEXT(context); \ + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, \ + &other, &context)) { \ + return NULL; \ + } \ + CONTEXT_CHECK_VA(context); \ + CONVERT_BINOP_RAISE(&a, &b, self, other, context); \ + \ + if ((result = dec_alloc()) == NULL) { \ + Py_DECREF(a); \ + Py_DECREF(b); \ + return NULL; \ + } \ + \ + MPDFUNC(MPD(result), MPD(a), MPD(b), CTX(context), &status); \ + Py_DECREF(a); \ + Py_DECREF(b); \ + if (dec_addstatus(context, status)) { \ + Py_DECREF(result); \ + return NULL; \ + } \ + \ + return result; \ +} + +/* Binary function with an optional context arg. Actual MPDFUNC does + NOT take a context. The context is used to record InvalidOperation + if the second operand cannot be converted exactly. */ +#define Dec_BinaryFuncVA_NO_CTX(MPDFUNC) \ +static PyObject * \ +dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \ +{ \ + static char *kwlist[] = {"other", "context", NULL}; \ + PyObject *other, *context; \ + PyObject *a, *b; \ + PyObject *result; \ + \ + CURRENT_CONTEXT(context); \ + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, \ + &other, &context)) { \ + return NULL; \ + } \ + CONTEXT_CHECK_VA(context); \ + CONVERT_BINOP_RAISE(&a, &b, self, other, context); \ + \ + if ((result = dec_alloc()) == NULL) { \ + Py_DECREF(a); \ + Py_DECREF(b); \ + return NULL; \ + } \ + \ + MPDFUNC(MPD(result), MPD(a), MPD(b)); \ + Py_DECREF(a); \ + Py_DECREF(b); \ + \ + return result; \ +} + +/* Ternary function with an optional context arg. */ +#define Dec_TernaryFuncVA(MPDFUNC) \ +static PyObject * \ +dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \ +{ \ + static char *kwlist[] = {"other", "third", "context", NULL}; \ + PyObject *other, *third, *context; \ + PyObject *a, *b, *c; \ + PyObject *result; \ + uint32_t status = 0; \ + \ + CURRENT_CONTEXT(context); \ + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|O", kwlist, \ + &other, &third, &context)) { \ + return NULL; \ + } \ + CONTEXT_CHECK_VA(context); \ + CONVERT_TERNOP_RAISE(&a, &b, &c, self, other, third, context); \ + \ + if ((result = dec_alloc()) == NULL) { \ + Py_DECREF(a); \ + Py_DECREF(b); \ + Py_DECREF(c); \ + return NULL; \ + } \ + \ + MPDFUNC(MPD(result), MPD(a), MPD(b), MPD(c), CTX(context), &status); \ + Py_DECREF(a); \ + Py_DECREF(b); \ + Py_DECREF(c); \ + if (dec_addstatus(context, status)) { \ + Py_DECREF(result); \ + return NULL; \ + } \ + \ + return result; \ +} + + +/**********************************************/ +/* Number methods */ +/**********************************************/ + +Dec_UnaryNumberMethod(mpd_qminus) +Dec_UnaryNumberMethod(mpd_qplus) +Dec_UnaryNumberMethod(mpd_qabs) + +Dec_BinaryNumberMethod(mpd_qadd) +Dec_BinaryNumberMethod(mpd_qsub) +Dec_BinaryNumberMethod(mpd_qmul) +Dec_BinaryNumberMethod(mpd_qdiv) +Dec_BinaryNumberMethod(mpd_qrem) +Dec_BinaryNumberMethod(mpd_qdivint) + +static PyObject * +nm_dec_as_long(PyObject *dec) +{ + PyObject *context; + + CURRENT_CONTEXT(context); + return dec_as_long(dec, context, MPD_ROUND_DOWN); +} + +static int +nm_nonzero(PyDecObject *v) +{ + return !mpd_iszero(v->dec); +} + +static PyObject * +nm_mpd_qdivmod(PyObject *v, PyObject *w) +{ + PyObject *a, *b; + PyObject *q, *r; + PyObject *context; + uint32_t status = 0; + PyObject *ret; + + CURRENT_CONTEXT(context); + CONVERT_BINOP(&a, &b, v, w, context); + + q = dec_alloc(); + if (q == NULL) { + Py_DECREF(a); + Py_DECREF(b); + return NULL; + } + r = dec_alloc(); + if (r == NULL) { + Py_DECREF(a); + Py_DECREF(b); + Py_DECREF(q); + return NULL; + } + + mpd_qdivmod(MPD(q), MPD(r), MPD(a), MPD(b), CTX(context), &status); + Py_DECREF(a); + Py_DECREF(b); + if (dec_addstatus(context, status)) { + Py_DECREF(r); + Py_DECREF(q); + return NULL; + } + + ret = Py_BuildValue("(OO)", q, r); + Py_DECREF(r); + Py_DECREF(q); + return ret; +} + +static mpd_uint_t data_zero[1] = {0}; +static const mpd_t zero = {MPD_STATIC|MPD_CONST_DATA, 0, 1, 1, 1, data_zero}; + +static PyObject * +nm_mpd_qpow(PyObject *base, PyObject *exp, PyObject *mod) +{ + PyObject *a, *b, *c = NULL; + PyObject *result; + PyObject *context; + uint32_t status = 0; + + CURRENT_CONTEXT(context); + CONVERT_BINOP(&a, &b, base, exp, context); + + if (mod != Py_None) { + if (!convert_op(NOT_IMPL, &c, mod, context)) { + Py_DECREF(a); + Py_DECREF(b); + return c; + } + } + + result = dec_alloc(); + if (result == NULL) { + Py_DECREF(a); + Py_DECREF(b); + Py_XDECREF(c); + return NULL; + } + + if (c == NULL) { + mpd_qpow(MPD(result), MPD(a), MPD(b), + CTX(context), &status); + } + else { + mpd_qpowmod(MPD(result), MPD(a), MPD(b), MPD(c), + CTX(context), &status); + status = (status == MPD_Clamped) ? 0 : status; + /* remove ideal exponent for compatibility with decimal.py */ + mpd_qquantize(MPD(result), MPD(result), &zero, + CTX(context), &status); + Py_DECREF(c); + } + Py_DECREF(a); + Py_DECREF(b); + if (dec_addstatus(context, status)) { + Py_DECREF(result); + return NULL; + } + + return result; +} + + +/******************************************************************************/ +/* Decimal Methods */ +/******************************************************************************/ + +/* Unary arithmetic functions, optional context arg */ +Dec_UnaryFuncVA(mpd_qexp) +Dec_UnaryFuncVA(mpd_qln) +Dec_UnaryFuncVA(mpd_qlog10) +Dec_UnaryFuncVA(mpd_qnext_minus) +Dec_UnaryFuncVA(mpd_qnext_plus) +Dec_UnaryFuncVA(mpd_qreduce) +Dec_UnaryFuncVA(mpd_qsqrt) + +/* Binary arithmetic functions, optional context arg */ +Dec_BinaryFuncVA(mpd_qcompare) +Dec_BinaryFuncVA(mpd_qcompare_signal) +Dec_BinaryFuncVA(mpd_qmax) +Dec_BinaryFuncVA(mpd_qmax_mag) +Dec_BinaryFuncVA(mpd_qmin) +Dec_BinaryFuncVA(mpd_qmin_mag) +Dec_BinaryFuncVA(mpd_qnext_toward) +Dec_BinaryFuncVA(mpd_qrem_near) + +/* Ternary arithmetic functions, optional context arg */ +Dec_TernaryFuncVA(mpd_qfma) + +/* Boolean functions, no context arg */ +Dec_BoolFunc(mpd_iscanonical) +Dec_BoolFunc(mpd_isfinite) +Dec_BoolFunc(mpd_isinfinite) +Dec_BoolFunc(mpd_isnan) +Dec_BoolFunc(mpd_isqnan) +Dec_BoolFunc(mpd_issnan) +Dec_BoolFunc(mpd_issigned) +Dec_BoolFunc(mpd_iszero) + +/* Boolean functions, optional context arg */ +Dec_BoolFuncVA(mpd_isnormal) +Dec_BoolFuncVA(mpd_issubnormal) + +/* Unary functions, no context arg */ +static PyObject * +dec_mpd_adjexp(PyObject *self, PyObject *dummy UNUSED) +{ + mpd_ssize_t retval; + + if (mpd_isspecial(MPD(self))) { + retval = 0; + } + else { + retval = mpd_adjexp(MPD(self)); + } + + return PyLong_FromSsize_t(retval); +} + +static PyObject * +dec_canonical(PyObject *self, PyObject *dummy UNUSED) +{ + Py_INCREF(self); + return self; +} + +static PyObject * +dec_conjugate(PyObject *self, PyObject *dummy UNUSED) +{ + Py_INCREF(self); + return self; +} + +static PyObject * +dec_mpd_radix(PyObject *self UNUSED, PyObject *dummy UNUSED) +{ + PyObject *result; + + result = dec_alloc(); + if (result == NULL) { + return NULL; + } + + _dec_settriple(result, MPD_POS, 10, 0); + return result; +} + +/* Unary functions, optional context arg for conversion errors */ +Dec_UnaryFuncVA_NO_CTX(mpd_qcopy_abs) +Dec_UnaryFuncVA_NO_CTX(mpd_qcopy_negate) + +/* Unary functions, optional context arg */ +Dec_UnaryFuncVA(mpd_qinvert) +Dec_UnaryFuncVA(mpd_qlogb) + +static PyObject * +dec_mpd_class(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"context", NULL}; + PyObject *context; + const char *cp; + + CURRENT_CONTEXT(context); + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, + &context)) { + return NULL; + } + CONTEXT_CHECK_VA(context); + + cp = mpd_class(MPD(self), CTX(context)); + return PyUnicode_FromString(cp); +} + +static PyObject * +dec_mpd_to_eng(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"context", NULL}; + PyObject *result; + PyObject *context; + mpd_ssize_t size; + char *s; + + CURRENT_CONTEXT(context); + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, + &context)) { + return NULL; + } + CONTEXT_CHECK_VA(context); + + size = mpd_to_eng_size(&s, MPD(self), CtxCaps(context)); + if (size < 0) { + PyErr_NoMemory(); + return NULL; + } + + result = unicode_fromascii(s, size); + mpd_free(s); + + return result; +} + +/* Binary functions, optional context arg for conversion errors */ +Dec_BinaryFuncVA_NO_CTX(mpd_compare_total) +Dec_BinaryFuncVA_NO_CTX(mpd_compare_total_mag) + +static PyObject * +dec_mpd_qcopy_sign(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"other", "context", NULL}; + PyObject *other, *context; + PyObject *a, *b; + PyObject *result; + uint32_t status = 0; + + CURRENT_CONTEXT(context); + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, + &other, &context)) { + return NULL; + } + CONTEXT_CHECK_VA(context); + CONVERT_BINOP_RAISE(&a, &b, self, other, context); + + result = dec_alloc(); + if (result == NULL) { + Py_DECREF(a); + Py_DECREF(b); + return NULL; + } + + mpd_qcopy_sign(MPD(result), MPD(a), MPD(b), &status); + Py_DECREF(a); + Py_DECREF(b); + if (dec_addstatus(context, status)) { + Py_DECREF(result); + return NULL; + } + + return result; +} + +static PyObject * +dec_mpd_same_quantum(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"other", "context", NULL}; + PyObject *other, *context; + PyObject *a, *b; + PyObject *result; + + CURRENT_CONTEXT(context); + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, + &other, &context)) { + return NULL; + } + CONTEXT_CHECK_VA(context); + CONVERT_BINOP_RAISE(&a, &b, self, other, context); + + result = mpd_same_quantum(MPD(a), MPD(b)) ? incr_true() : incr_false(); + Py_DECREF(a); + Py_DECREF(b); + + return result; +} + +/* Binary functions, optional context arg */ +Dec_BinaryFuncVA(mpd_qand) +Dec_BinaryFuncVA(mpd_qor) +Dec_BinaryFuncVA(mpd_qxor) + +Dec_BinaryFuncVA(mpd_qrotate) +Dec_BinaryFuncVA(mpd_qscaleb) +Dec_BinaryFuncVA(mpd_qshift) + +static PyObject * +dec_mpd_qquantize(PyObject *v, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"exp", "rounding", "context", NULL}; + PyObject *w, *context; + PyObject *a, *b; + PyObject *result; + uint32_t status = 0; + mpd_context_t workctx; + int round = -1; + + CURRENT_CONTEXT(context); + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iO", kwlist, + &w, &round, &context)) { + return NULL; + } + CONTEXT_CHECK_VA(context); + + workctx = *CTX(context); + if (round >= 0) { + if (!mpd_qsetround(&workctx, round)) { + return type_error_ptr(invalid_rounding_err); + } + } + + CONVERT_BINOP_RAISE(&a, &b, v, w, context); + + result = dec_alloc(); + if (result == NULL) { + Py_DECREF(a); + Py_DECREF(b); + return NULL; + } + + mpd_qquantize(MPD(result), MPD(a), MPD(b), &workctx, &status); + Py_DECREF(a); + Py_DECREF(b); + if (dec_addstatus(context, status)) { + Py_DECREF(result); + return NULL; + } + + return result; +} + +/* Special methods */ +static PyObject * +dec_richcompare(PyObject *v, PyObject *w, int op) +{ + PyObject *a; + PyObject *b; + PyObject *context; + uint32_t status = 0; + int a_issnan, b_issnan; + int r; + + assert(PyDec_Check(v)); + + CURRENT_CONTEXT(context); + CONVERT_BINOP_CMP(&a, &b, v, w, op, context); + + a_issnan = mpd_issnan(MPD(a)); + b_issnan = mpd_issnan(MPD(b)); + + r = mpd_qcmp(MPD(a), MPD(b), &status); + Py_DECREF(a); + Py_DECREF(b); + if (r == INT_MAX) { + /* sNaNs or op={le,ge,lt,gt} always signal. */ + if (a_issnan || b_issnan || (op != Py_EQ && op != Py_NE)) { + if (dec_addstatus(context, status)) { + return NULL; + } + } + /* qNaN comparison with op={eq,ne} or comparison + * with InvalidOperation disabled. */ + return (op == Py_NE) ? incr_true() : incr_false(); + } + + switch (op) { + case Py_EQ: + r = (r == 0); + break; + case Py_NE: + r = (r != 0); + break; + case Py_LE: + r = (r <= 0); + break; + case Py_GE: + r = (r >= 0); + break; + case Py_LT: + r = (r == -1); + break; + case Py_GT: + r = (r == 1); + break; + } + + return PyBool_FromLong(r); +} + +/* __complex__ */ +static PyObject * +dec_complex(PyObject *self, PyObject *dummy UNUSED) +{ + PyObject *f; + double x; + + f = PyDec_AsFloat(self); + if (f == NULL) { + return NULL; + } + + x = PyFloat_AsDouble(f); + Py_DECREF(f); + if (x == -1.0 && PyErr_Occurred()) { + return NULL; + } + + return PyComplex_FromDoubles(x, 0); +} + +/* __copy__ and __deepcopy__ */ +static PyObject * +dec_copy(PyObject *self, PyObject *dummy UNUSED) +{ + Py_INCREF(self); + return self; +} + +/* Always uses the module context */ +static Py_hash_t +dec_hash(PyObject *v) +{ +#if defined(CONFIG_64) && _PyHASH_BITS == 61 + /* 2**61 - 1 */ + mpd_uint_t p_data[1] = {2305843009213693951ULL}; + mpd_t p = {MPD_POS|MPD_STATIC|MPD_CONST_DATA, 0, 19, 1, 1, p_data}; + /* Inverse of 10 modulo p */ + mpd_uint_t inv10_p_data[2] = {2075258708292324556ULL}; + mpd_t inv10_p = {MPD_POS|MPD_STATIC|MPD_CONST_DATA, + 0, 19, 1, 1, inv10_p_data}; +#elif defined(CONFIG_32) && _PyHASH_BITS == 31 + /* 2**31 - 1 */ + mpd_uint_t p_data[2] = {147483647UL, 2}; + mpd_t p = {MPD_POS|MPD_STATIC|MPD_CONST_DATA, 0, 10, 2, 2, p_data}; + /* Inverse of 10 modulo p */ + mpd_uint_t inv10_p_data[2] = {503238553UL, 1}; + mpd_t inv10_p = {MPD_POS|MPD_STATIC|MPD_CONST_DATA, + 0, 10, 2, 2, inv10_p_data}; +#else + #error "No valid combination of CONFIG_64, CONFIG_32 and _PyHASH_BITS" +#endif + const Py_hash_t py_hash_inf = 314159; + const Py_hash_t py_hash_nan = 0; + mpd_uint_t ten_data[1] = {10}; + mpd_t ten = {MPD_POS|MPD_STATIC|MPD_CONST_DATA, + 0, 2, 1, 1, ten_data}; + Py_hash_t result; + mpd_t *exp_hash = NULL; + mpd_t *tmp = NULL; + mpd_ssize_t exp; + uint32_t status = 0; + mpd_context_t maxctx; + PyObject *context; + + + context = current_context(); + if (context == NULL) { + return -1; + } + + if (mpd_isspecial(MPD(v))) { + if (mpd_issnan(MPD(v))) { + PyErr_SetString(PyExc_TypeError, + "Cannot hash a signaling NaN value"); + return -1; + } + else if (mpd_isnan(MPD(v))) { + return py_hash_nan; + } + else { + return py_hash_inf * mpd_arith_sign(MPD(v)); + } + } + + mpd_maxcontext(&maxctx); + exp_hash = mpd_qnew(); + if (exp_hash == NULL) { + goto malloc_error; + } + tmp = mpd_qnew(); + if (tmp == NULL) { + goto malloc_error; + } + + /* + * exp(v): exponent of v + * int(v): coefficient of v + */ + exp = MPD(v)->exp; + if (exp >= 0) { + /* 10**exp(v) % p */ + mpd_qsset_ssize(tmp, exp, &maxctx, &status); + mpd_qpowmod(exp_hash, &ten, tmp, &p, &maxctx, &status); + } + else { + /* inv10_p**(-exp(v)) % p */ + mpd_qsset_ssize(tmp, -exp, &maxctx, &status); + mpd_qpowmod(exp_hash, &inv10_p, tmp, &p, &maxctx, &status); + } + + /* hash = (int(v) * exp_hash) % p */ + if (!mpd_qcopy(tmp, MPD(v), &status)) { + goto malloc_error; + } + tmp->exp = 0; + mpd_set_positive(tmp); + mpd_qmul(tmp, tmp, exp_hash, &maxctx, &status); + mpd_qrem(tmp, tmp, &p, &maxctx, &status); + + result = mpd_qget_ssize(tmp, &status); + result = mpd_ispositive(MPD(v)) ? result : -result; + result = (result == -1) ? -2 : result; + + if (status != 0) { + status |= MPD_Invalid_operation; + if (dec_addstatus(context, status)) { + result = -1; + goto finish; + } + } + + +finish: + if (exp_hash) mpd_del(exp_hash); + if (tmp) mpd_del(tmp); + return result; + +malloc_error: + PyErr_NoMemory(); + result = -1; + goto finish; +} + +/* __reduce__ */ +static PyObject * +dec_reduce(PyObject *self, PyObject *dummy UNUSED) +{ + PyObject *result, *str; + + str = dec_str(self); + if (str == NULL) { + return NULL; + } + + result = Py_BuildValue("O(O)", Py_TYPE(self), str); + Py_DECREF(str); + + return result; +} + +/* __trunc__ */ +static PyObject * +dec_trunc(PyObject *dec, PyObject *dummy UNUSED) +{ + PyObject *context; + + CURRENT_CONTEXT(context); + return dec_as_long(dec, context, MPD_ROUND_DOWN); +} + +/* real and imag */ +static PyObject * +dec_real(PyObject *self, void *closure UNUSED) +{ + Py_INCREF(self); + return self; +} + +static PyObject * +dec_imag(PyObject *self UNUSED, void *closure UNUSED) +{ + PyObject *result; + + result = dec_alloc(); + if (result == NULL) { + return NULL; + } + + _dec_settriple(result, MPD_POS, 0, 0); + return result; +} + + +static PyGetSetDef dec_getsets [] = +{ + { "real", (getter)dec_real, NULL, NULL, NULL}, + { "imag", (getter)dec_imag, NULL, NULL, NULL}, + {NULL} +}; + +static PyNumberMethods dec_number_methods = +{ + (binaryfunc) nm_mpd_qadd, + (binaryfunc) nm_mpd_qsub, + (binaryfunc) nm_mpd_qmul, + (binaryfunc) nm_mpd_qrem, + (binaryfunc) nm_mpd_qdivmod, + (ternaryfunc) nm_mpd_qpow, + (unaryfunc) nm_mpd_qminus, + (unaryfunc) nm_mpd_qplus, + (unaryfunc) nm_mpd_qabs, + (inquiry) nm_nonzero, + (unaryfunc) 0, /* no bit-complement */ + (binaryfunc) 0, /* no shiftl */ + (binaryfunc) 0, /* no shiftr */ + (binaryfunc) 0, /* no bit-and */ + (binaryfunc) 0, /* no bit-xor */ + (binaryfunc) 0, /* no bit-ior */ + (unaryfunc) nm_dec_as_long, + 0, /* nb_reserved */ + (unaryfunc) PyDec_AsFloat, + 0, /* binaryfunc nb_inplace_add; */ + 0, /* binaryfunc nb_inplace_subtract; */ + 0, /* binaryfunc nb_inplace_multiply; */ + 0, /* binaryfunc nb_inplace_remainder; */ + 0, /* ternaryfunc nb_inplace_power; */ + 0, /* binaryfunc nb_inplace_lshift; */ + 0, /* binaryfunc nb_inplace_rshift; */ + 0, /* binaryfunc nb_inplace_and; */ + 0, /* binaryfunc nb_inplace_xor; */ + 0, /* binaryfunc nb_inplace_or; */ + (binaryfunc) nm_mpd_qdivint, /* binaryfunc nb_floor_divide; */ + (binaryfunc) nm_mpd_qdiv, /* binaryfunc nb_true_divide; */ + 0, /* binaryfunc nb_inplace_floor_divide; */ + 0, /* binaryfunc nb_inplace_true_divide; */ +}; + +static PyMethodDef dec_methods [] = +{ + /* Unary arithmetic functions, optional context arg */ + { "exp", (PyCFunction)dec_mpd_qexp, METH_VARARGS|METH_KEYWORDS, doc_exp }, + { "ln", (PyCFunction)dec_mpd_qln, METH_VARARGS|METH_KEYWORDS, doc_ln }, + { "log10", (PyCFunction)dec_mpd_qlog10, METH_VARARGS|METH_KEYWORDS, doc_log10 }, + { "next_minus", (PyCFunction)dec_mpd_qnext_minus, METH_VARARGS|METH_KEYWORDS, doc_next_minus }, + { "next_plus", (PyCFunction)dec_mpd_qnext_plus, METH_VARARGS|METH_KEYWORDS, doc_next_plus }, + { "normalize", (PyCFunction)dec_mpd_qreduce, METH_VARARGS|METH_KEYWORDS, doc_normalize }, + { "to_integral", (PyCFunction)PyDec_ToIntegralValue, METH_VARARGS|METH_KEYWORDS, doc_to_integral }, + { "to_integral_exact", (PyCFunction)PyDec_ToIntegralExact, METH_VARARGS|METH_KEYWORDS, doc_to_integral_exact }, + { "to_integral_value", (PyCFunction)PyDec_ToIntegralValue, METH_VARARGS|METH_KEYWORDS, doc_to_integral_value }, + { "sqrt", (PyCFunction)dec_mpd_qsqrt, METH_VARARGS|METH_KEYWORDS, doc_sqrt }, + + /* Binary arithmetic functions, optional context arg */ + { "compare", (PyCFunction)dec_mpd_qcompare, METH_VARARGS|METH_KEYWORDS, doc_compare }, + { "compare_signal", (PyCFunction)dec_mpd_qcompare_signal, METH_VARARGS|METH_KEYWORDS, doc_compare_signal }, + { "max", (PyCFunction)dec_mpd_qmax, METH_VARARGS|METH_KEYWORDS, doc_max }, + { "max_mag", (PyCFunction)dec_mpd_qmax_mag, METH_VARARGS|METH_KEYWORDS, doc_max_mag }, + { "min", (PyCFunction)dec_mpd_qmin, METH_VARARGS|METH_KEYWORDS, doc_min }, + { "min_mag", (PyCFunction)dec_mpd_qmin_mag, METH_VARARGS|METH_KEYWORDS, doc_min_mag }, + { "next_toward", (PyCFunction)dec_mpd_qnext_toward, METH_VARARGS|METH_KEYWORDS, doc_next_toward }, + { "quantize", (PyCFunction)dec_mpd_qquantize, METH_VARARGS|METH_KEYWORDS, doc_quantize }, + { "remainder_near", (PyCFunction)dec_mpd_qrem_near, METH_VARARGS|METH_KEYWORDS, doc_remainder_near }, + + /* Ternary arithmetic functions, optional context arg */ + { "fma", (PyCFunction)dec_mpd_qfma, METH_VARARGS|METH_KEYWORDS, doc_fma }, + + /* Boolean functions, no context arg */ + { "is_canonical", dec_mpd_iscanonical, METH_NOARGS, doc_is_canonical }, + { "is_finite", dec_mpd_isfinite, METH_NOARGS, doc_is_finite }, + { "is_infinite", dec_mpd_isinfinite, METH_NOARGS, doc_is_infinite }, + { "is_nan", dec_mpd_isnan, METH_NOARGS, doc_is_nan }, + { "is_qnan", dec_mpd_isqnan, METH_NOARGS, doc_is_qnan }, + { "is_snan", dec_mpd_issnan, METH_NOARGS, doc_is_snan }, + { "is_signed", dec_mpd_issigned, METH_NOARGS, doc_is_signed }, + { "is_zero", dec_mpd_iszero, METH_NOARGS, doc_is_zero }, + + /* Boolean functions, optional context arg */ + { "is_normal", (PyCFunction)dec_mpd_isnormal, METH_VARARGS|METH_KEYWORDS, doc_is_normal }, + { "is_subnormal", (PyCFunction)dec_mpd_issubnormal, METH_VARARGS|METH_KEYWORDS, doc_is_subnormal }, + + /* Unary functions, no context arg */ + { "adjusted", dec_mpd_adjexp, METH_NOARGS, doc_adjusted }, + { "canonical", dec_canonical, METH_NOARGS, doc_canonical }, + { "conjugate", dec_conjugate, METH_NOARGS, doc_conjugate }, + { "radix", dec_mpd_radix, METH_NOARGS, doc_radix }, + + /* Unary functions, optional context arg for conversion errors */ + { "copy_abs", (PyCFunction)dec_mpd_qcopy_abs, METH_VARARGS|METH_KEYWORDS, doc_copy_abs }, + { "copy_negate", (PyCFunction)dec_mpd_qcopy_negate, METH_VARARGS|METH_KEYWORDS, doc_copy_negate }, + + /* Unary functions, optional context arg */ + { "logb", (PyCFunction)dec_mpd_qlogb, METH_VARARGS|METH_KEYWORDS, doc_logb }, + { "logical_invert", (PyCFunction)dec_mpd_qinvert, METH_VARARGS|METH_KEYWORDS, doc_logical_invert }, + { "number_class", (PyCFunction)dec_mpd_class, METH_VARARGS|METH_KEYWORDS, doc_number_class }, + { "to_eng_string", (PyCFunction)dec_mpd_to_eng, METH_VARARGS|METH_KEYWORDS, doc_to_eng_string }, + + /* Binary functions, optional context arg for conversion errors */ + { "compare_total", (PyCFunction)dec_mpd_compare_total, METH_VARARGS|METH_KEYWORDS, doc_compare_total }, + { "compare_total_mag", (PyCFunction)dec_mpd_compare_total_mag, METH_VARARGS|METH_KEYWORDS, doc_compare_total_mag }, + { "copy_sign", (PyCFunction)dec_mpd_qcopy_sign, METH_VARARGS|METH_KEYWORDS, doc_copy_sign }, + { "same_quantum", (PyCFunction)dec_mpd_same_quantum, METH_VARARGS|METH_KEYWORDS, doc_same_quantum }, + + /* Binary functions, optional context arg */ + { "logical_and", (PyCFunction)dec_mpd_qand, METH_VARARGS|METH_KEYWORDS, doc_logical_and }, + { "logical_or", (PyCFunction)dec_mpd_qor, METH_VARARGS|METH_KEYWORDS, doc_logical_or }, + { "logical_xor", (PyCFunction)dec_mpd_qxor, METH_VARARGS|METH_KEYWORDS, doc_logical_xor }, + { "rotate", (PyCFunction)dec_mpd_qrotate, METH_VARARGS|METH_KEYWORDS, doc_rotate }, + { "scaleb", (PyCFunction)dec_mpd_qscaleb, METH_VARARGS|METH_KEYWORDS, doc_scaleb }, + { "shift", (PyCFunction)dec_mpd_qshift, METH_VARARGS|METH_KEYWORDS, doc_shift }, + + /* Miscellaneous */ + { "from_float", dec_from_float, METH_O|METH_CLASS, doc_from_float }, + { "as_tuple", PyDec_AsTuple, METH_NOARGS, doc_as_tuple }, + + /* Special methods */ + { "__copy__", dec_copy, METH_NOARGS, NULL }, + { "__deepcopy__", dec_copy, METH_O, NULL }, + { "__format__", dec_format, METH_VARARGS, NULL }, + { "__reduce__", dec_reduce, METH_NOARGS, NULL }, + { "__round__", PyDec_Round, METH_VARARGS, NULL }, + { "__trunc__", dec_trunc, METH_NOARGS, NULL }, + { "__complex__", dec_complex, METH_NOARGS, NULL }, + + { NULL, NULL, 1 } +}; + +static PyTypeObject PyDec_Type = +{ + PyVarObject_HEAD_INIT(NULL, 0) + "decimal.Decimal", /* tp_name */ + sizeof(PyDecObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor) dec_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + (getattrfunc) 0, /* tp_getattr */ + (setattrfunc) 0, /* tp_setattr */ + 0, /* tp_reserved */ + (reprfunc) dec_repr, /* tp_repr */ + &dec_number_methods, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc) dec_hash, /* tp_hash */ + 0, /* tp_call */ + (reprfunc) dec_str, /* tp_str */ + (getattrofunc) PyObject_GenericGetAttr, /* tp_getattro */ + (setattrofunc) 0, /* tp_setattro */ + (PyBufferProcs *) 0, /* tp_as_buffer */ + (Py_TPFLAGS_DEFAULT| + Py_TPFLAGS_BASETYPE), /* tp_flags */ + doc_decimal, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + dec_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + dec_methods, /* tp_methods */ + 0, /* tp_members */ + dec_getsets, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + dec_new, /* tp_new */ + PyObject_Del, /* tp_free */ +}; + + +/******************************************************************************/ +/* Context Object, Part 2 */ +/******************************************************************************/ + + +/************************************************************************/ +/* Macros for converting mpdecimal functions to Context methods */ +/************************************************************************/ + +/* Boolean context method. */ +#define DecCtx_BoolFunc(MPDFUNC) \ +static PyObject * \ +ctx_##MPDFUNC(PyObject *context, PyObject *v) \ +{ \ + PyObject *ret; \ + PyObject *a; \ + \ + CONVERT_OP_RAISE(&a, v, context); \ + \ + ret = MPDFUNC(MPD(a), CTX(context)) ? incr_true() : incr_false(); \ + Py_DECREF(a); \ + return ret; \ +} + +/* Boolean context method. MPDFUNC does NOT use a context. */ +#define DecCtx_BoolFunc_NO_CTX(MPDFUNC) \ +static PyObject * \ +ctx_##MPDFUNC(PyObject *context, PyObject *v) \ +{ \ + PyObject *ret; \ + PyObject *a; \ + \ + CONVERT_OP_RAISE(&a, v, context); \ + \ + ret = MPDFUNC(MPD(a)) ? incr_true() : incr_false(); \ + Py_DECREF(a); \ + return ret; \ +} + +/* Unary context method. */ +#define DecCtx_UnaryFunc(MPDFUNC) \ +static PyObject * \ +ctx_##MPDFUNC(PyObject *context, PyObject *v) \ +{ \ + PyObject *result, *a; \ + uint32_t status = 0; \ + \ + CONVERT_OP_RAISE(&a, v, context); \ + \ + if ((result = dec_alloc()) == NULL) { \ + Py_DECREF(a); \ + return NULL; \ + } \ + \ + MPDFUNC(MPD(result), MPD(a), CTX(context), &status); \ + Py_DECREF(a); \ + if (dec_addstatus(context, status)) { \ + Py_DECREF(result); \ + return NULL; \ + } \ + \ + return result; \ +} + +/* Binary context method. */ +#define DecCtx_BinaryFunc(MPDFUNC) \ +static PyObject * \ +ctx_##MPDFUNC(PyObject *context, PyObject *args) \ +{ \ + PyObject *v, *w; \ + PyObject *a, *b; \ + PyObject *result; \ + uint32_t status = 0; \ + \ + if (!PyArg_ParseTuple(args, "OO", &v, &w)) { \ + return NULL; \ + } \ + \ + CONVERT_BINOP_RAISE(&a, &b, v, w, context); \ + \ + if ((result = dec_alloc()) == NULL) { \ + Py_DECREF(a); \ + Py_DECREF(b); \ + return NULL; \ + } \ + \ + MPDFUNC(MPD(result), MPD(a), MPD(b), CTX(context), &status); \ + Py_DECREF(a); \ + Py_DECREF(b); \ + if (dec_addstatus(context, status)) { \ + Py_DECREF(result); \ + return NULL; \ + } \ + \ + return result; \ +} + +/* + * Binary context method. The context is only used for conversion. + * The actual MPDFUNC does NOT take a context arg. + */ +#define DecCtx_BinaryFunc_NO_CTX(MPDFUNC) \ +static PyObject * \ +ctx_##MPDFUNC(PyObject *context, PyObject *args) \ +{ \ + PyObject *v, *w; \ + PyObject *a, *b; \ + PyObject *result; \ + \ + if (!PyArg_ParseTuple(args, "OO", &v, &w)) { \ + return NULL; \ + } \ + \ + CONVERT_BINOP_RAISE(&a, &b, v, w, context); \ + \ + if ((result = dec_alloc()) == NULL) { \ + Py_DECREF(a); \ + Py_DECREF(b); \ + return NULL; \ + } \ + \ + MPDFUNC(MPD(result), MPD(a), MPD(b)); \ + Py_DECREF(a); \ + Py_DECREF(b); \ + \ + return result; \ +} + +/* Ternary context method. */ +#define DecCtx_TernaryFunc(MPDFUNC) \ +static PyObject * \ +ctx_##MPDFUNC(PyObject *context, PyObject *args) \ +{ \ + PyObject *v, *w, *x; \ + PyObject *a, *b, *c; \ + PyObject *result; \ + uint32_t status = 0; \ + \ + if (!PyArg_ParseTuple(args, "OOO", &v, &w, &x)) { \ + return NULL; \ + } \ + \ + CONVERT_TERNOP_RAISE(&a, &b, &c, v, w, x, context); \ + \ + if ((result = dec_alloc()) == NULL) { \ + Py_DECREF(a); \ + Py_DECREF(b); \ + Py_DECREF(c); \ + return NULL; \ + } \ + \ + MPDFUNC(MPD(result), MPD(a), MPD(b), MPD(c), CTX(context), &status); \ + Py_DECREF(a); \ + Py_DECREF(b); \ + Py_DECREF(c); \ + if (dec_addstatus(context, status)) { \ + Py_DECREF(result); \ + return NULL; \ + } \ + \ + return result; \ +} + + +/* Unary arithmetic functions */ +DecCtx_UnaryFunc(mpd_qabs) +DecCtx_UnaryFunc(mpd_qexp) +DecCtx_UnaryFunc(mpd_qln) +DecCtx_UnaryFunc(mpd_qlog10) +DecCtx_UnaryFunc(mpd_qminus) +DecCtx_UnaryFunc(mpd_qnext_minus) +DecCtx_UnaryFunc(mpd_qnext_plus) +DecCtx_UnaryFunc(mpd_qplus) +DecCtx_UnaryFunc(mpd_qreduce) +DecCtx_UnaryFunc(mpd_qround_to_int) +DecCtx_UnaryFunc(mpd_qround_to_intx) +DecCtx_UnaryFunc(mpd_qsqrt) + +/* Binary arithmetic functions */ +DecCtx_BinaryFunc(mpd_qadd) +DecCtx_BinaryFunc(mpd_qcompare) +DecCtx_BinaryFunc(mpd_qcompare_signal) +DecCtx_BinaryFunc(mpd_qdiv) +DecCtx_BinaryFunc(mpd_qdivint) +DecCtx_BinaryFunc(mpd_qmax) +DecCtx_BinaryFunc(mpd_qmax_mag) +DecCtx_BinaryFunc(mpd_qmin) +DecCtx_BinaryFunc(mpd_qmin_mag) +DecCtx_BinaryFunc(mpd_qmul) +DecCtx_BinaryFunc(mpd_qnext_toward) +DecCtx_BinaryFunc(mpd_qquantize) +DecCtx_BinaryFunc(mpd_qrem) +DecCtx_BinaryFunc(mpd_qrem_near) +DecCtx_BinaryFunc(mpd_qsub) + +static PyObject * +ctx_mpd_qdivmod(PyObject *context, PyObject *args) +{ + PyObject *v, *w; + PyObject *a, *b; + PyObject *q, *r; + uint32_t status = 0; + PyObject *ret; + + if (!PyArg_ParseTuple(args, "OO", &v, &w)) { + return NULL; + } + + CONVERT_BINOP_RAISE(&a, &b, v, w, context); + + q = dec_alloc(); + if (q == NULL) { + Py_DECREF(a); + Py_DECREF(b); + return NULL; + } + r = dec_alloc(); + if (r == NULL) { + Py_DECREF(a); + Py_DECREF(b); + Py_DECREF(q); + return NULL; + } + + mpd_qdivmod(MPD(q), MPD(r), MPD(a), MPD(b), CTX(context), &status); + Py_DECREF(a); + Py_DECREF(b); + if (dec_addstatus(context, status)) { + Py_DECREF(r); + Py_DECREF(q); + return NULL; + } + + ret = Py_BuildValue("(OO)", q, r); + Py_DECREF(r); + Py_DECREF(q); + return ret; +} + +/* Binary or ternary arithmetic functions */ +static PyObject * +ctx_mpd_qpow(PyObject *context, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"a", "b", "modulo", NULL}; + PyObject *base, *exp, *mod = NULL; + PyObject *a, *b, *c = NULL; + PyObject *result; + uint32_t status = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|O", kwlist, + &base, &exp, &mod)) { + return NULL; + } + + CONVERT_BINOP_RAISE(&a, &b, base, exp, context); + + if (mod != NULL) { + if (!convert_op(TYPE_ERR, &c, mod, context)) { + Py_DECREF(a); + Py_DECREF(b); + return c; + } + } + + result = dec_alloc(); + if (result == NULL) { + Py_DECREF(a); + Py_DECREF(b); + Py_XDECREF(c); + return NULL; + } + + if (c == NULL) { + mpd_qpow(MPD(result), MPD(a), MPD(b), + CTX(context), &status); + } + else { + mpd_qpowmod(MPD(result), MPD(a), MPD(b), MPD(c), + CTX(context), &status); + status = (status == MPD_Clamped) ? 0 : status; + /* remove ideal exponent for compatibility with decimal.py */ + mpd_qquantize(MPD(result), MPD(result), &zero, + CTX(context), &status); + Py_DECREF(c); + } + Py_DECREF(a); + Py_DECREF(b); + if (dec_addstatus(context, status)) { + Py_DECREF(result); + return NULL; + } + + return result; +} + +/* Ternary arithmetic functions */ +DecCtx_TernaryFunc(mpd_qfma) + +/* No argument */ +static PyObject * +ctx_mpd_radix(PyObject *context, PyObject *dummy) +{ + return dec_mpd_radix(context, dummy); +} + +/* Boolean functions: single decimal argument */ +DecCtx_BoolFunc(mpd_isnormal) +DecCtx_BoolFunc(mpd_issubnormal) +DecCtx_BoolFunc_NO_CTX(mpd_isfinite) +DecCtx_BoolFunc_NO_CTX(mpd_isinfinite) +DecCtx_BoolFunc_NO_CTX(mpd_isnan) +DecCtx_BoolFunc_NO_CTX(mpd_isqnan) +DecCtx_BoolFunc_NO_CTX(mpd_issigned) +DecCtx_BoolFunc_NO_CTX(mpd_issnan) +DecCtx_BoolFunc_NO_CTX(mpd_iszero) + +static PyObject * +ctx_iscanonical(PyObject *context UNUSED, PyObject *v) +{ + if (!PyDec_Check(v)) { + PyErr_SetString(PyExc_TypeError, + "argument must be a Decimal"); + return NULL; + } + + return mpd_iscanonical(MPD(v)) ? incr_true() : incr_false(); +} + +/* Functions with a single decimal argument */ +static PyObject * +PyDecContext_Apply(PyObject *context, PyObject *v) +{ + PyObject *result, *a; + + CONVERT_OP_RAISE(&a, v, context); + + result = dec_apply(a, context); + Py_DECREF(a); + return result; +} + +static PyObject * +ctx_canonical(PyObject *context UNUSED, PyObject *v) +{ + if (!PyDec_Check(v)) { + PyErr_SetString(PyExc_TypeError, + "argument must be a Decimal"); + return NULL; + } + + Py_INCREF(v); + return v; +} + +static PyObject * +ctx_mpd_qcopy_abs(PyObject *context, PyObject *v) +{ + PyObject *result, *a; + uint32_t status = 0; + + CONVERT_OP_RAISE(&a, v, context); + + result = dec_alloc(); + if (result == NULL) { + Py_DECREF(a); + return NULL; + } + + mpd_qcopy_abs(MPD(result), MPD(a), &status); + Py_DECREF(a); + if (dec_addstatus(context, status)) { + Py_DECREF(result); + return NULL; + } + + return result; +} + +static PyObject * +ctx_copy_decimal(PyObject *context, PyObject *v) +{ + PyObject *result; + + CONVERT_OP_RAISE(&result, v, context); + return result; +} + +static PyObject * +ctx_mpd_qcopy_negate(PyObject *context, PyObject *v) +{ + PyObject *result, *a; + uint32_t status = 0; + + CONVERT_OP_RAISE(&a, v, context); + + result = dec_alloc(); + if (result == NULL) { + Py_DECREF(a); + return NULL; + } + + mpd_qcopy_negate(MPD(result), MPD(a), &status); + Py_DECREF(a); + if (dec_addstatus(context, status)) { + Py_DECREF(result); + return NULL; + } + + return result; +} + +DecCtx_UnaryFunc(mpd_qlogb) +DecCtx_UnaryFunc(mpd_qinvert) + +static PyObject * +ctx_mpd_class(PyObject *context, PyObject *v) +{ + PyObject *a; + const char *cp; + + CONVERT_OP_RAISE(&a, v, context); + + cp = mpd_class(MPD(a), CTX(context)); + Py_DECREF(a); + + return PyUnicode_FromString(cp); +} + +static PyObject * +ctx_mpd_to_sci(PyObject *context, PyObject *v) +{ + PyObject *result; + PyObject *a; + mpd_ssize_t size; + char *s; + + CONVERT_OP_RAISE(&a, v, context); + + size = mpd_to_sci_size(&s, MPD(a), CtxCaps(context)); + Py_DECREF(a); + if (size < 0) { + PyErr_NoMemory(); + return NULL; + } + + result = unicode_fromascii(s, size); + mpd_free(s); + + return result; +} + +static PyObject * +ctx_mpd_to_eng(PyObject *context, PyObject *v) +{ + PyObject *result; + PyObject *a; + mpd_ssize_t size; + char *s; + + CONVERT_OP_RAISE(&a, v, context); + + size = mpd_to_eng_size(&s, MPD(a), CtxCaps(context)); + Py_DECREF(a); + if (size < 0) { + PyErr_NoMemory(); + return NULL; + } + + result = unicode_fromascii(s, size); + mpd_free(s); + + return result; +} + +/* Functions with two decimal arguments */ +DecCtx_BinaryFunc_NO_CTX(mpd_compare_total) +DecCtx_BinaryFunc_NO_CTX(mpd_compare_total_mag) + +static PyObject * +ctx_mpd_qcopy_sign(PyObject *context, PyObject *args) +{ + PyObject *v, *w; + PyObject *a, *b; + PyObject *result; + uint32_t status = 0; + + if (!PyArg_ParseTuple(args, "OO", &v, &w)) { + return NULL; + } + + CONVERT_BINOP_RAISE(&a, &b, v, w, context); + + result = dec_alloc(); + if (result == NULL) { + Py_DECREF(a); + Py_DECREF(b); + return NULL; + } + + mpd_qcopy_sign(MPD(result), MPD(a), MPD(b), &status); + Py_DECREF(a); + Py_DECREF(b); + if (dec_addstatus(context, status)) { + Py_DECREF(result); + return NULL; + } + + return result; +} + +DecCtx_BinaryFunc(mpd_qand) +DecCtx_BinaryFunc(mpd_qor) +DecCtx_BinaryFunc(mpd_qxor) + +DecCtx_BinaryFunc(mpd_qrotate) +DecCtx_BinaryFunc(mpd_qscaleb) +DecCtx_BinaryFunc(mpd_qshift) + +static PyObject * +ctx_mpd_same_quantum(PyObject *context, PyObject *args) +{ + PyObject *v, *w; + PyObject *a, *b; + PyObject *result; + + if (!PyArg_ParseTuple(args, "OO", &v, &w)) { + return NULL; + } + + CONVERT_BINOP_RAISE(&a, &b, v, w, context); + + result = mpd_same_quantum(MPD(a), MPD(b)) ? incr_true() : incr_false(); + Py_DECREF(a); + Py_DECREF(b); + + return result; +} + + +static PyMethodDef context_methods [] = +{ + /* Unary arithmetic functions */ + { "abs", ctx_mpd_qabs, METH_O, doc_ctx_abs }, + { "exp", ctx_mpd_qexp, METH_O, doc_ctx_exp }, + { "ln", ctx_mpd_qln, METH_O, doc_ctx_ln }, + { "log10", ctx_mpd_qlog10, METH_O, doc_ctx_log10 }, + { "minus", ctx_mpd_qminus, METH_O, doc_ctx_minus }, + { "next_minus", ctx_mpd_qnext_minus, METH_O, doc_ctx_next_minus }, + { "next_plus", ctx_mpd_qnext_plus, METH_O, doc_ctx_next_plus }, + { "normalize", ctx_mpd_qreduce, METH_O, doc_ctx_normalize }, + { "plus", ctx_mpd_qplus, METH_O, doc_ctx_plus }, + { "to_integral", ctx_mpd_qround_to_int, METH_O, doc_ctx_to_integral }, + { "to_integral_exact", ctx_mpd_qround_to_intx, METH_O, doc_ctx_to_integral_exact }, + { "to_integral_value", ctx_mpd_qround_to_int, METH_O, doc_ctx_to_integral_value }, + { "sqrt", ctx_mpd_qsqrt, METH_O, doc_ctx_sqrt }, + + /* Binary arithmetic functions */ + { "add", ctx_mpd_qadd, METH_VARARGS, doc_ctx_add }, + { "compare", ctx_mpd_qcompare, METH_VARARGS, doc_ctx_compare }, + { "compare_signal", ctx_mpd_qcompare_signal, METH_VARARGS, doc_ctx_compare_signal }, + { "divide", ctx_mpd_qdiv, METH_VARARGS, doc_ctx_divide }, + { "divide_int", ctx_mpd_qdivint, METH_VARARGS, doc_ctx_divide_int }, + { "divmod", ctx_mpd_qdivmod, METH_VARARGS, doc_ctx_divmod }, + { "max", ctx_mpd_qmax, METH_VARARGS, doc_ctx_max }, + { "max_mag", ctx_mpd_qmax_mag, METH_VARARGS, doc_ctx_max_mag }, + { "min", ctx_mpd_qmin, METH_VARARGS, doc_ctx_min }, + { "min_mag", ctx_mpd_qmin_mag, METH_VARARGS, doc_ctx_min_mag }, + { "multiply", ctx_mpd_qmul, METH_VARARGS, doc_ctx_multiply }, + { "next_toward", ctx_mpd_qnext_toward, METH_VARARGS, doc_ctx_next_toward }, + { "quantize", ctx_mpd_qquantize, METH_VARARGS, doc_ctx_quantize }, + { "remainder", ctx_mpd_qrem, METH_VARARGS, doc_ctx_remainder }, + { "remainder_near", ctx_mpd_qrem_near, METH_VARARGS, doc_ctx_remainder_near }, + { "subtract", ctx_mpd_qsub, METH_VARARGS, doc_ctx_subtract }, + + /* Binary or ternary arithmetic functions */ + { "power", (PyCFunction)ctx_mpd_qpow, METH_VARARGS|METH_KEYWORDS, doc_ctx_power }, + + /* Ternary arithmetic functions */ + { "fma", ctx_mpd_qfma, METH_VARARGS, doc_ctx_fma }, + + /* No argument */ + { "Etiny", context_getetiny, METH_NOARGS, doc_ctx_Etiny }, + { "Etop", context_getetop, METH_NOARGS, doc_ctx_Etop }, + { "radix", ctx_mpd_radix, METH_NOARGS, doc_ctx_radix }, + + /* Boolean functions */ + { "is_canonical", ctx_iscanonical, METH_O, doc_ctx_is_canonical }, + { "is_finite", ctx_mpd_isfinite, METH_O, doc_ctx_is_finite }, + { "is_infinite", ctx_mpd_isinfinite, METH_O, doc_ctx_is_infinite }, + { "is_nan", ctx_mpd_isnan, METH_O, doc_ctx_is_nan }, + { "is_normal", ctx_mpd_isnormal, METH_O, doc_ctx_is_normal }, + { "is_qnan", ctx_mpd_isqnan, METH_O, doc_ctx_is_qnan }, + { "is_signed", ctx_mpd_issigned, METH_O, doc_ctx_is_signed }, + { "is_snan", ctx_mpd_issnan, METH_O, doc_ctx_is_snan }, + { "is_subnormal", ctx_mpd_issubnormal, METH_O, doc_ctx_is_subnormal }, + { "is_zero", ctx_mpd_iszero, METH_O, doc_ctx_is_zero }, + + /* Functions with a single decimal argument */ + { "_apply", PyDecContext_Apply, METH_O, NULL }, /* alias for apply */ + { "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_string", ctx_mpd_to_sci, METH_O, doc_ctx_to_sci_string }, + { "to_eng_string", ctx_mpd_to_eng, METH_O, doc_ctx_to_eng_string }, + + /* Functions with two decimal arguments */ + { "compare_total", ctx_mpd_compare_total, METH_VARARGS, doc_ctx_compare_total }, + { "compare_total_mag", ctx_mpd_compare_total_mag, METH_VARARGS, doc_ctx_compare_total_mag }, + { "copy_sign", ctx_mpd_qcopy_sign, METH_VARARGS, doc_ctx_copy_sign }, + { "logical_and", ctx_mpd_qand, METH_VARARGS, doc_ctx_logical_and }, + { "logical_or", ctx_mpd_qor, METH_VARARGS, doc_ctx_logical_or }, + { "logical_xor", ctx_mpd_qxor, METH_VARARGS, doc_ctx_logical_xor }, + { "rotate", ctx_mpd_qrotate, METH_VARARGS, doc_ctx_rotate }, + { "same_quantum", ctx_mpd_same_quantum, METH_VARARGS, doc_ctx_same_quantum }, + { "scaleb", ctx_mpd_qscaleb, METH_VARARGS, doc_ctx_scaleb }, + { "shift", ctx_mpd_qshift, METH_VARARGS, doc_ctx_shift }, + + /* Set context values */ + { "clear_flags", context_clear_flags, METH_NOARGS, doc_ctx_clear_flags }, + { "clear_traps", context_clear_traps, METH_NOARGS, doc_ctx_clear_traps }, + + /* 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 *MutableMapping = NULL; + PyObject *obj = NULL; + DecCondMap *cm; + struct ssize_constmap *ssize_cm; + struct int_constmap *int_cm; + int i; + + + /* Init libmpdec */ + mpd_traphandler = dec_traphandler; + mpd_mallocfunc = PyMem_Malloc; + mpd_reallocfunc = PyMem_Realloc; + mpd_callocfunc = mpd_callocfunc_em; + mpd_free = PyMem_Free; + mpd_setminalloc(4); + + + /* Init types */ + PyDec_Type.tp_base = &PyBaseObject_Type; + PyDecContext_Type.tp_base = &PyBaseObject_Type; + PyDecContextManager_Type.tp_base = &PyBaseObject_Type; + PyDecSignalDictMixin_Type.tp_base = &PyBaseObject_Type; + + CHECK_INT(PyType_Ready(&PyDec_Type)); + CHECK_INT(PyType_Ready(&PyDecContext_Type)); + CHECK_INT(PyType_Ready(&PyDecSignalDictMixin_Type)); + CHECK_INT(PyType_Ready(&PyDecContextManager_Type)); + + ASSIGN_PTR(obj, PyUnicode_FromString("decimal")); + CHECK_INT(PyDict_SetItemString(PyDec_Type.tp_dict, "__module__", obj)); + CHECK_INT(PyDict_SetItemString(PyDecContext_Type.tp_dict, + "__module__", obj)); + Py_CLEAR(obj); + + + /* Numeric abstract base classes */ + ASSIGN_PTR(numbers, PyImport_ImportModule("numbers")); + ASSIGN_PTR(Number, PyObject_GetAttrString(numbers, "Number")); + /* Register Decimal with the Number abstract base class */ + ASSIGN_PTR(obj, PyObject_CallMethod(Number, "register", "(O)", + (PyObject *)&PyDec_Type)); + Py_CLEAR(obj); + /* Rational is a global variable used for fraction comparisons. */ + ASSIGN_PTR(Rational, PyObject_GetAttrString(numbers, "Rational")); + /* Done with numbers, Number */ + Py_CLEAR(numbers); + Py_CLEAR(Number); + + /* DecimalTuple */ + ASSIGN_PTR(collections, PyImport_ImportModule("collections")); + ASSIGN_PTR(DecimalTuple, PyObject_CallMethod(collections, + "namedtuple", "(ss)", "DecimalTuple", + "sign digits exponent")); + /* MutableMapping */ + ASSIGN_PTR(MutableMapping, PyObject_GetAttrString(collections, + "MutableMapping")); + /* Create SignalDict type */ + ASSIGN_PTR(PyDecSignalDict_Type, + (PyTypeObject *)PyObject_CallFunction( + (PyObject *)&PyType_Type, "s(OO){}", + "SignalDict", &PyDecSignalDictMixin_Type, + MutableMapping)); + + /* Done with collections, MutableMapping */ + Py_CLEAR(collections); + Py_CLEAR(MutableMapping); + + + /* Create the module */ + ASSIGN_PTR(m, PyModule_Create(&_decimal_module)); + + + /* Add types to the module */ + Py_INCREF(&PyDec_Type); + CHECK_INT(PyModule_AddObject(m, "Decimal", (PyObject *)&PyDec_Type)); + Py_INCREF(&PyDecContext_Type); + CHECK_INT(PyModule_AddObject(m, "Context", + (PyObject *)&PyDecContext_Type)); + + + /* Create top level exception */ + ASSIGN_PTR(DecimalException, PyErr_NewException( + "decimal.DecimalException", + PyExc_ArithmeticError, NULL)); + Py_INCREF(DecimalException); + CHECK_INT(PyModule_AddObject(m, "DecimalException", DecimalException)); + + /* Create signal tuple */ + ASSIGN_PTR(SignalTuple, PyTuple_New(SIGNAL_MAP_LEN)); + + /* Add exceptions that correspond to IEEE signals */ + for (cm=signal_map, i=0; cm->name != NULL; cm++, i++) { + ASSIGN_PTR(cm->ex, PyErr_NewException((char *)cm->fqname, + DecimalException, NULL)); + + /* add to module */ + Py_INCREF(cm->ex); + CHECK_INT(PyModule_AddObject(m, cm->name, cm->ex)); + + /* add to signal tuple */ + Py_INCREF(cm->ex); + PyTuple_SET_ITEM(SignalTuple, i, cm->ex); + } + + /* + * Unfortunately, InvalidOperation is a signal that comprises + * several conditions, including InvalidOperation! Naming the + * signal IEEEInvalidOperation would prevent the confusion. + */ + cond_map[0].ex = signal_map[0].ex; + + /* Add remaining exceptions, inherit from InvalidOperation */ + for (cm = cond_map+1; cm->name != NULL; cm++) { + ASSIGN_PTR(cm->ex, PyErr_NewException((char *)cm->fqname, + signal_map[0].ex, NULL)); + Py_INCREF(cm->ex); + CHECK_INT(PyModule_AddObject(m, cm->name, cm->ex)); + } + + + /* Init default context template first */ + ASSIGN_PTR(default_context_template, + PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL)); + Py_INCREF(default_context_template); + CHECK_INT(PyModule_AddObject(m, "DefaultContext", + default_context_template)); + +#ifdef WITHOUT_THREADS + /* Init module context */ + ASSIGN_PTR(module_context, + PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL)); + CHECK_INT(PyModule_AddIntConstant(m, "HAVE_THREADS", 0)); +#else + ASSIGN_PTR(tls_context_key, PyUnicode_FromString("___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, PyLong_FromSsize_t(ssize_cm->val)); + CHECK_INT(PyModule_AddObject(m, ssize_cm->name, obj)); + } + + /* Init int constants */ + for (int_cm = int_constants; int_cm->name != NULL; int_cm++) { + CHECK_INT(PyModule_AddIntConstant(m, int_cm->name, + int_cm->val)); + } + + + return m; + + +error: + Py_XDECREF(obj); /* GCOV_NOT_REACHED */ + Py_XDECREF(numbers); /* GCOV_NOT_REACHED */ + Py_XDECREF(Number); /* GCOV_NOT_REACHED */ + Py_XDECREF(Rational); /* GCOV_NOT_REACHED */ + Py_XDECREF(collections); /* GCOV_NOT_REACHED */ + Py_XDECREF(MutableMapping); /* GCOV_NOT_REACHED */ + Py_XDECREF(SignalTuple); /* GCOV_NOT_REACHED */ + Py_XDECREF(DecimalTuple); /* GCOV_NOT_REACHED */ +#ifdef WITHOUT_THREADS + Py_XDECREF(module_context); /* GCOV_NOT_REACHED */ +#else + Py_XDECREF(default_context_template); /* GCOV_NOT_REACHED */ + Py_XDECREF(tls_context_key); /* GCOV_NOT_REACHED */ +#endif + Py_XDECREF(basic_context_template); /* GCOV_NOT_REACHED */ + Py_XDECREF(extended_context_template); /* GCOV_NOT_REACHED */ + Py_XDECREF(m); /* GCOV_NOT_REACHED */ + + return NULL; /* GCOV_NOT_REACHED */ +} + + diff -r 207408428242 -r 40917e4b51aa Modules/_decimal/basearith.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/basearith.c Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,635 @@ +/* + * 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. Assumption: m > 0. + */ +mpd_uint_t +_mpd_shortadd(mpd_uint_t *w, mpd_size_t m, mpd_uint_t v) +{ + mpd_uint_t s; + mpd_uint_t carry; + mpd_size_t i; + + assert(m > 0); + + /* add v to w */ + s = w[0] + v; + carry = (s < v) | (s >= MPD_RADIX); + w[0] = carry ? s-MPD_RADIX : s; + + /* if there is a carry, propagate it */ + for (i = 1; carry && i < m; i++) { + s = w[i] + carry; + carry = (s == MPD_RADIX); + w[i] = carry ? 0 : s; + } + + return carry; +} + +/* Increment u. The calling function has to handle a possible carry. */ +mpd_uint_t +_mpd_baseincr(mpd_uint_t *u, mpd_size_t n) +{ + mpd_uint_t s; + mpd_uint_t carry = 1; + mpd_size_t i; + + assert(n > 0); + + /* if there is a carry, propagate it */ + for (i = 0; carry && i < n; i++) { + s = u[i] + carry; + carry = (s == MPD_RADIX); + u[i] = carry ? 0 : s; + } + + return carry; +} + +/* + * Knuth, TAOCP, Volume 2, 4.3.1: + * w := difference of u (len m) and v (len n). + * number in u >= number in v; + */ +void +_mpd_basesub(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v, + mpd_size_t m, mpd_size_t n) +{ + mpd_uint_t d; + mpd_uint_t borrow = 0; + mpd_size_t i; + + assert(m > 0 && n > 0); + + /* subtract n members of v from u */ + for (i = 0; i < n; i++) { + d = u[i] - (v[i] + borrow); + borrow = (u[i] < d); + w[i] = borrow ? d + MPD_RADIX : d; + } + /* if there is a borrow, propagate it */ + for (; borrow && i < m; i++) { + d = u[i] - borrow; + borrow = (u[i] == 0); + w[i] = borrow ? MPD_RADIX-1 : d; + } + /* copy the rest of u */ + for (; i < m; i++) { + w[i] = u[i]; + } +} + +/* + * Subtract the contents of u from w. w is larger than u. Borrows are + * propagated further, but eventually w can absorb the final borrow. + */ +void +_mpd_basesubfrom(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n) +{ + mpd_uint_t d; + mpd_uint_t borrow = 0; + mpd_size_t i; + + if (n == 0) return; + + /* subtract n members of u from w */ + for (i = 0; i < n; i++) { + d = w[i] - (u[i] + borrow); + borrow = (w[i] < d); + w[i] = borrow ? d + MPD_RADIX : d; + } + /* if there is a borrow, propagate it */ + for (; borrow; i++) { + d = w[i] - borrow; + borrow = (w[i] == 0); + w[i] = borrow ? MPD_RADIX-1 : d; + } +} + +/* w := product of u (len n) and v (single word) */ +void +_mpd_shortmul(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, mpd_uint_t v) +{ + mpd_uint_t hi, lo; + mpd_uint_t carry = 0; + mpd_size_t i; + + assert(n > 0); + + for (i=0; i < n; i++) { + + _mpd_mul_words(&hi, &lo, u[i], v); + lo = carry + lo; + if (lo < carry) hi++; + + _mpd_div_words_r(&carry, &w[i], hi, lo); + } + w[i] = carry; +} + +/* + * Knuth, TAOCP, Volume 2, 4.3.1: + * w := product of u (len m) and v (len n) + * w must be initialized to zero + */ +void +_mpd_basemul(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v, + mpd_size_t m, mpd_size_t n) +{ + mpd_uint_t hi, lo; + mpd_uint_t carry; + mpd_size_t i, j; + + assert(m > 0 && n > 0); + + for (j=0; j < n; j++) { + carry = 0; + for (i=0; i < m; i++) { + + _mpd_mul_words(&hi, &lo, u[i], v[j]); + lo = w[i+j] + lo; + if (lo < w[i+j]) hi++; + lo = carry + lo; + if (lo < carry) hi++; + + _mpd_div_words_r(&carry, &w[i+j], hi, lo); + } + w[j+m] = carry; + } +} + +/* + * Knuth, TAOCP Volume 2, 4.3.1, exercise 16: + * w := quotient of u (len n) divided by a single word v + */ +mpd_uint_t +_mpd_shortdiv(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, mpd_uint_t v) +{ + mpd_uint_t hi, lo; + mpd_uint_t rem = 0; + mpd_size_t i; + + assert(n > 0); + + for (i=n-1; i != MPD_SIZE_MAX; i--) { + + _mpd_mul_words(&hi, &lo, rem, MPD_RADIX); + lo = u[i] + lo; + if (lo < u[i]) hi++; + + _mpd_div_words(&w[i], &rem, hi, lo, v); + } + + return rem; +} + +/* + * Knuth, TAOCP Volume 2, 4.3.1: + * q, r := quotient and remainder of uconst (len nplusm) + * divided by vconst (len n) + * nplusm >= n + * + * If r is not NULL, r will contain the remainder. If r is NULL, the + * return value indicates if there is a remainder: 1 for true, 0 for + * false. A return value of -1 indicates an error. + */ +int +_mpd_basedivmod(mpd_uint_t *q, mpd_uint_t *r, + const mpd_uint_t *uconst, const mpd_uint_t *vconst, + mpd_size_t nplusm, mpd_size_t n) +{ + mpd_uint_t ustatic[MPD_MINALLOC_MAX]; + mpd_uint_t vstatic[MPD_MINALLOC_MAX]; + mpd_uint_t *u = ustatic; + mpd_uint_t *v = vstatic; + mpd_uint_t d, qhat, rhat, w2[2]; + mpd_uint_t hi, lo, x; + mpd_uint_t carry; + mpd_size_t i, j, m; + int retval = 0; + + assert(n > 1 && nplusm >= n); + m = sub_size_t(nplusm, n); + + /* D1: normalize */ + d = MPD_RADIX / (vconst[n-1] + 1); + + if (nplusm >= MPD_MINALLOC_MAX) { + if ((u = mpd_alloc(nplusm+1, sizeof *u)) == NULL) { + return -1; + } + } + if (n >= MPD_MINALLOC_MAX) { + if ((v = mpd_alloc(n+1, sizeof *v)) == NULL) { + mpd_free(u); + return -1; + } + } + + _mpd_shortmul(u, uconst, nplusm, d); + _mpd_shortmul(v, vconst, n, d); + + /* D2: loop */ + for (j=m; j != MPD_SIZE_MAX; j--) { + + /* D3: calculate qhat and rhat */ + rhat = _mpd_shortdiv(w2, u+j+n-1, 2, v[n-1]); + qhat = w2[1] * MPD_RADIX + w2[0]; + + while (1) { + if (qhat < MPD_RADIX) { + _mpd_singlemul(w2, qhat, v[n-2]); + if (w2[1] <= rhat) { + if (w2[1] != rhat || w2[0] <= u[j+n-2]) { + break; + } + } + } + qhat -= 1; + rhat += v[n-1]; + if (rhat < v[n-1] || rhat >= MPD_RADIX) { + break; + } + } + /* D4: multiply and subtract */ + carry = 0; + for (i=0; i <= n; i++) { + + _mpd_mul_words(&hi, &lo, qhat, v[i]); + + lo = carry + lo; + if (lo < carry) hi++; + + _mpd_div_words_r(&hi, &lo, hi, lo); + + x = u[i+j] - lo; + carry = (u[i+j] < x); + u[i+j] = carry ? x+MPD_RADIX : x; + carry += hi; + } + q[j] = qhat; + /* D5: test remainder */ + if (carry) { + q[j] -= 1; + /* D6: add back */ + (void)_mpd_baseadd(u+j, u+j, v, n+1, n); + } + } + + /* D8: unnormalize */ + if (r != NULL) { + _mpd_shortdiv(r, u, n, d); + /* we are not interested in the return value here */ + retval = 0; + } + else { + retval = !_mpd_isallzero(u, n); + } + + +if (u != ustatic) mpd_free(u); +if (v != vstatic) mpd_free(v); +return retval; +} + +/* + * Left shift of src by 'shift' digits; src may equal dest. + * + * dest := area of n mpd_uint_t with space for srcdigits+shift digits. + * src := coefficient with length m. + * + * The case splits in the function are non-obvious. The following + * equations might help: + * + * Let msdigits denote the number of digits in the most significant + * word of src. Then 1 <= msdigits <= rdigits. + * + * 1) shift = q * rdigits + r + * 2) srcdigits = qsrc * rdigits + msdigits + * 3) destdigits = shift + srcdigits + * = q * rdigits + r + qsrc * rdigits + msdigits + * = q * rdigits + (qsrc * rdigits + (r + msdigits)) + * + * The result has q zero words, followed by the coefficient that + * is left-shifted by r. The case r == 0 is trivial. For r > 0, it + * is important to keep in mind that we always read m source words, + * but write m+1 destination words if r + msdigits > rdigits, m words + * otherwise. + */ +void +_mpd_baseshiftl(mpd_uint_t *dest, mpd_uint_t *src, mpd_size_t n, mpd_size_t m, + mpd_size_t shift) +{ +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__) + /* spurious uninitialized warnings */ + mpd_uint_t l=l, lprev=lprev, h=h; +#else + mpd_uint_t l, lprev, h; +#endif + mpd_uint_t q, r; + mpd_uint_t ph; + + assert(m > 0 && n >= m); + + _mpd_div_word(&q, &r, (mpd_uint_t)shift, MPD_RDIGITS); + + if (r != 0) { + + ph = mpd_pow10[r]; + + --m; --n; + _mpd_divmod_pow10(&h, &lprev, src[m--], MPD_RDIGITS-r); + if (h != 0) { /* r + msdigits > rdigits <==> h != 0 */ + dest[n--] = h; + } + /* write m-1 shifted words */ + for (; m != MPD_SIZE_MAX; m--,n--) { + _mpd_divmod_pow10(&h, &l, src[m], MPD_RDIGITS-r); + dest[n] = ph * lprev + h; + lprev = l; + } + /* write least significant word */ + dest[q] = ph * lprev; + } + else { + while (--m != MPD_SIZE_MAX) { + dest[m+q] = src[m]; + } + } + + mpd_uint_zero(dest, q); +} + +/* + * Right shift of src by 'shift' digits; src may equal dest. + * Assumption: srcdigits-shift > 0. + * + * dest := area with space for srcdigits-shift digits. + * src := coefficient with length 'slen'. + * + * The case splits in the function rely on the following equations: + * + * Let msdigits denote the number of digits in the most significant + * word of src. Then 1 <= msdigits <= rdigits. + * + * 1) shift = q * rdigits + r + * 2) srcdigits = qsrc * rdigits + msdigits + * 3) destdigits = srcdigits - shift + * = qsrc * rdigits + msdigits - (q * rdigits + r) + * = (qsrc - q) * rdigits + msdigits - r + * + * Since destdigits > 0 and 1 <= msdigits <= rdigits: + * + * 4) qsrc >= q + * 5) qsrc == q ==> msdigits > r + * + * The result has slen-q words if msdigits > r, slen-q-1 words otherwise. + */ +mpd_uint_t +_mpd_baseshiftr(mpd_uint_t *dest, mpd_uint_t *src, mpd_size_t slen, + mpd_size_t shift) +{ +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__) + /* spurious uninitialized warnings */ + mpd_uint_t l=l, h=h, hprev=hprev; /* low, high, previous high */ +#else + mpd_uint_t l, h, hprev; /* low, high, previous high */ +#endif + mpd_uint_t rnd, rest; /* rounding digit, rest */ + mpd_uint_t q, r; + mpd_size_t i, j; + mpd_uint_t ph; + + assert(slen > 0); + + _mpd_div_word(&q, &r, (mpd_uint_t)shift, MPD_RDIGITS); + + rnd = rest = 0; + if (r != 0) { + + ph = mpd_pow10[MPD_RDIGITS-r]; + + _mpd_divmod_pow10(&hprev, &rest, src[q], r); + _mpd_divmod_pow10(&rnd, &rest, rest, r-1); + + if (rest == 0 && q > 0) { + rest = !_mpd_isallzero(src, q); + } + /* write slen-q-1 words */ + for (j=0,i=q+1; i 0) { + _mpd_divmod_pow10(&rnd, &rest, src[q-1], MPD_RDIGITS-1); + /* is there any non-zero digit below rnd? */ + if (rest == 0) rest = !_mpd_isallzero(src, q-1); + } + for (j = 0; j < slen-q; j++) { + dest[j] = src[q+j]; + } + } + + /* 0-4 ==> rnd+rest < 0.5 */ + /* 5 ==> rnd+rest == 0.5 */ + /* 6-9 ==> rnd+rest > 0.5 */ + return (rnd == 0 || rnd == 5) ? rnd + !!rest : rnd; +} + + +/*********************************************************************/ +/* Calculations in base b */ +/*********************************************************************/ + +/* + * Add v to w (len m). The calling function has to handle a possible + * final carry. Assumption: m > 0. + */ +mpd_uint_t +_mpd_shortadd_b(mpd_uint_t *w, mpd_size_t m, mpd_uint_t v, mpd_uint_t b) +{ + mpd_uint_t s; + mpd_uint_t carry; + mpd_size_t i; + + assert(m > 0); + + /* add v to w */ + s = w[0] + v; + carry = (s < v) | (s >= b); + w[0] = carry ? s-b : s; + + /* if there is a carry, propagate it */ + for (i = 1; carry && i < m; i++) { + s = w[i] + carry; + carry = (s == b); + w[i] = carry ? 0 : s; + } + + return carry; +} + +/* w := product of u (len n) and v (single word) */ +void +_mpd_shortmul_b(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, + mpd_uint_t v, mpd_uint_t b) +{ + mpd_uint_t hi, lo; + mpd_uint_t carry = 0; + mpd_size_t i; + + assert(n > 0); + + for (i=0; i < n; i++) { + + _mpd_mul_words(&hi, &lo, u[i], v); + lo = carry + lo; + if (lo < carry) hi++; + + _mpd_div_words(&carry, &w[i], hi, lo, b); + } + w[i] = carry; +} + +/* + * Knuth, TAOCP Volume 2, 4.3.1, exercise 16: + * w := quotient of u (len n) divided by a single word v + */ +mpd_uint_t +_mpd_shortdiv_b(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, + mpd_uint_t v, mpd_uint_t b) +{ + mpd_uint_t hi, lo; + mpd_uint_t rem = 0; + mpd_size_t i; + + assert(n > 0); + + for (i=n-1; i != MPD_SIZE_MAX; i--) { + + _mpd_mul_words(&hi, &lo, rem, b); + lo = u[i] + lo; + if (lo < u[i]) hi++; + + _mpd_div_words(&w[i], &rem, hi, lo, v); + } + + return rem; +} + + + diff -r 207408428242 -r 40917e4b51aa Modules/_decimal/basearith.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/basearith.h Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,213 @@ +/* + * 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 + * + * Variables from the paper and their translations (See section 8): + * + * N := 64 + * d := MPD_RADIX + * l := 64 + * m' := floor((2**(64+64) - 1)/MPD_RADIX) - 2**64 + * + * Since N-l == 0: + * + * dnorm := d + * n2 := hi + * n10 := lo + * + * ACL2 proof: mpd-div-words-r-correct + */ +static inline void +_mpd_div_words_r(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo) +{ + mpd_uint_t n_adj, h, l, t; + mpd_uint_t n1_neg; + + /* n1_neg = if lo >= 2**63 then MPD_UINT_MAX else 0 */ + n1_neg = (lo & (1ULL<<63)) ? MPD_UINT_MAX : 0; + /* n_adj = if lo >= 2**63 then lo+MPD_RADIX else lo */ + n_adj = lo + (n1_neg & MPD_RADIX); + + /* (h, l) = if lo >= 2**63 then m'*(hi+1) else m'*hi */ + _mpd_mul_words(&h, &l, mprime_rdx, hi-n1_neg); + l = l + n_adj; + if (l < n_adj) h++; + t = h + hi; + /* At this point t == qest, with q == qest or q == qest+1: + * 1) 0 <= 2**64*hi + lo - qest*MPD_RADIX < 2*MPD_RADIX + */ + + /* t = 2**64-1 - qest = 2**64 - (qest+1) */ + t = MPD_UINT_MAX - t; + + /* (h, l) = 2**64*MPD_RADIX - (qest+1)*MPD_RADIX */ + _mpd_mul_words(&h, &l, t, MPD_RADIX); + l = l + lo; + if (l < lo) h++; + h += hi; + h -= MPD_RADIX; + /* (h, l) = 2**64*hi + lo - (qest+1)*MPD_RADIX (mod 2**128) + * Case q == qest+1: + * a) h == 0, l == r + * b) q := h - t == qest+1 + * c) r := l + * Case q == qest: + * a) h == MPD_UINT_MAX, l == 2**64-(MPD_RADIX-r) + * b) q := h - t == qest + * c) r := l + MPD_RADIX = r + */ + + *q = (h - t); + *r = l + (MPD_RADIX & h); +} +#else +static inline void +_mpd_div_words_r(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo) +{ + _mpd_div_words(q, r, hi, lo, MPD_RADIX); +} +#endif + + +/* Multiply two single base MPD_RADIX words, store result in array w[2]. */ +static inline void +_mpd_singlemul(mpd_uint_t w[2], mpd_uint_t u, mpd_uint_t v) +{ + mpd_uint_t hi, lo; + + _mpd_mul_words(&hi, &lo, u, v); + _mpd_div_words_r(&w[1], &w[0], hi, lo); +} + +/* Multiply u (len 2) and v (len m, 1 <= m <= 2). */ +static inline void +_mpd_mul_2_le2(mpd_uint_t w[4], mpd_uint_t u[2], mpd_uint_t v[2], mpd_ssize_t m) +{ + mpd_uint_t hi, lo; + + _mpd_mul_words(&hi, &lo, u[0], v[0]); + _mpd_div_words_r(&w[1], &w[0], hi, lo); + + _mpd_mul_words(&hi, &lo, u[1], v[0]); + lo = w[1] + lo; + if (lo < w[1]) hi++; + _mpd_div_words_r(&w[2], &w[1], hi, lo); + if (m == 1) return; + + _mpd_mul_words(&hi, &lo, u[0], v[1]); + lo = w[1] + lo; + if (lo < w[1]) hi++; + _mpd_div_words_r(&w[3], &w[1], hi, lo); + + _mpd_mul_words(&hi, &lo, u[1], v[1]); + lo = w[2] + lo; + if (lo < w[2]) hi++; + lo = w[3] + lo; + if (lo < w[3]) hi++; + _mpd_div_words_r(&w[3], &w[2], hi, lo); +} + + +/* + * Test if all words from data[len-1] to data[0] are zero. If len is 0, nothing + * is tested and the coefficient is regarded as "all zero". + */ +static inline int +_mpd_isallzero(const mpd_uint_t *data, mpd_ssize_t len) +{ + while (--len >= 0) { + if (data[len] != 0) return 0; + } + return 1; +} + +/* + * Test if all full words from data[len-1] to data[0] are MPD_RADIX-1 + * (all nines). Return true if len == 0. + */ +static inline int +_mpd_isallnine(const mpd_uint_t *data, mpd_ssize_t len) +{ + while (--len >= 0) { + if (data[len] != MPD_RADIX-1) return 0; + } + return 1; +} + + +#endif /* BASEARITH_H */ + + + diff -r 207408428242 -r 40917e4b51aa Modules/_decimal/bench.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/bench.c Thu Feb 23 16:43:15 2012 +0100 @@ -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 207408428242 -r 40917e4b51aa Modules/_decimal/bits.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/bits.h Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,192 @@ +/* + * 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) +/* + * Return the most significant bit position of n from 0 to 31 (63). + * Assumptions: n != 0. + */ +static inline int +mpd_bsr(mpd_size_t n) +{ + int pos = 0; + mpd_size_t tmp; + +#ifdef CONFIG_64 + tmp = n >> 32; + if (tmp != 0) { n = tmp; pos += 32; } +#endif + tmp = n >> 16; + if (tmp != 0) { n = tmp; pos += 16; } + tmp = n >> 8; + if (tmp != 0) { n = tmp; pos += 8; } + tmp = n >> 4; + if (tmp != 0) { n = tmp; pos += 4; } + tmp = n >> 2; + if (tmp != 0) { n = tmp; pos += 2; } + tmp = n >> 1; + if (tmp != 0) { n = tmp; pos += 1; } + + return pos + (int)n - 1; +} + +/* + * Return the least significant bit position of n from 0 to 31 (63). + * Assumptions: n != 0. + */ +static inline int +mpd_bsf(mpd_size_t n) +{ + int pos; + +#ifdef CONFIG_64 + pos = 63; + if (n & 0x00000000FFFFFFFFULL) { pos -= 32; } else { n >>= 32; } + if (n & 0x000000000000FFFFULL) { pos -= 16; } else { n >>= 16; } + if (n & 0x00000000000000FFULL) { pos -= 8; } else { n >>= 8; } + if (n & 0x000000000000000FULL) { pos -= 4; } else { n >>= 4; } + if (n & 0x0000000000000003ULL) { pos -= 2; } else { n >>= 2; } + if (n & 0x0000000000000001ULL) { pos -= 1; } +#else + pos = 31; + if (n & 0x000000000000FFFFUL) { pos -= 16; } else { n >>= 16; } + if (n & 0x00000000000000FFUL) { pos -= 8; } else { n >>= 8; } + if (n & 0x000000000000000FUL) { pos -= 4; } else { n >>= 4; } + if (n & 0x0000000000000003UL) { pos -= 2; } else { n >>= 2; } + if (n & 0x0000000000000001UL) { pos -= 1; } +#endif + return pos; +} +/* END ANSI */ + +#elif defined(ASM) +/* + * Bit scan reverse. Assumptions: a != 0. + */ +static inline int +mpd_bsr(mpd_size_t a) +{ + mpd_size_t retval; + + __asm__ ( +#ifdef CONFIG_64 + "bsrq %1, %0\n\t" +#else + "bsr %1, %0\n\t" +#endif + :"=r" (retval) + :"r" (a) + :"cc" + ); + + return (int)retval; +} + +/* + * Bit scan forward. Assumptions: a != 0. + */ +static inline int +mpd_bsf(mpd_size_t a) +{ + mpd_size_t retval; + + __asm__ ( +#ifdef CONFIG_64 + "bsfq %1, %0\n\t" +#else + "bsf %1, %0\n\t" +#endif + :"=r" (retval) + :"r" (a) + :"cc" + ); + + return (int)retval; +} +/* END ASM */ + +#elif defined(MASM) +#include +/* + * Bit scan reverse. Assumptions: a != 0. + */ +static inline int __cdecl +mpd_bsr(mpd_size_t a) +{ + unsigned long retval; + +#ifdef CONFIG_64 + _BitScanReverse64(&retval, a); +#else + _BitScanReverse(&retval, a); +#endif + + return (int)retval; +} + +/* + * Bit scan forward. Assumptions: a != 0. + */ +static inline int __cdecl +mpd_bsf(mpd_size_t a) +{ + unsigned long retval; + +#ifdef CONFIG_64 + _BitScanForward64(&retval, a); +#else + _BitScanForward(&retval, a); +#endif + + return (int)retval; +} +/* END MASM (_MSC_VER) */ +#else + #error "missing preprocessor definitions" +#endif /* BSR/BSF */ + + +#endif /* BITS_H */ + + + diff -r 207408428242 -r 40917e4b51aa Modules/_decimal/config.h.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/config.h.in Thu Feb 23 16:43:15 2012 +0100 @@ -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 207408428242 -r 40917e4b51aa Modules/_decimal/configure --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/configure Thu Feb 23 16:43:15 2012 +0100 @@ -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 207408428242 -r 40917e4b51aa Modules/_decimal/configure.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/configure.in Thu Feb 23 16:43:15 2012 +0100 @@ -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 207408428242 -r 40917e4b51aa Modules/_decimal/constants.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/constants.c Thu Feb 23 16:43:15 2012 +0100 @@ -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 207408428242 -r 40917e4b51aa Modules/_decimal/constants.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/constants.h Thu Feb 23 16:43:15 2012 +0100 @@ -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 207408428242 -r 40917e4b51aa Modules/_decimal/context.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/context.c Thu Feb 23 16:43:15 2012 +0100 @@ -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; + + +/* Set guaranteed minimum number of coefficient words. The function may + be used once at program start. Setting MPD_MINALLOC to out-of-bounds + values is a catastrophic error, so in that case the function exits rather + than relying on the user to check a return value. */ +void +mpd_setminalloc(mpd_ssize_t n) +{ + static int minalloc_is_set = 0; + + if (minalloc_is_set) { + mpd_err_warn("mpd_setminalloc: ignoring request to set " + "MPD_MINALLOC a second time\n"); + return; + } + if (n < MPD_MINALLOC_MIN || n > MPD_MINALLOC_MAX) { + mpd_err_fatal("illegal value for MPD_MINALLOC"); /* GCOV_NOT_REACHED */ + } + MPD_MINALLOC = n; + minalloc_is_set = 1; +} + +void +mpd_init(mpd_context_t *ctx, mpd_ssize_t prec) +{ + mpd_ssize_t ideal_minalloc; + + mpd_defaultcontext(ctx); + + if (!mpd_qsetprec(ctx, prec)) { + mpd_addstatus_raise(ctx, MPD_Invalid_context); + return; + } + + ideal_minalloc = 2 * ((prec+MPD_RDIGITS-1) / MPD_RDIGITS); + if (ideal_minalloc < MPD_MINALLOC_MIN) ideal_minalloc = MPD_MINALLOC_MIN; + if (ideal_minalloc > MPD_MINALLOC_MAX) ideal_minalloc = MPD_MINALLOC_MAX; + + mpd_setminalloc(ideal_minalloc); +} + +void +mpd_maxcontext(mpd_context_t *ctx) +{ + ctx->prec=MPD_MAX_PREC; + ctx->emax=MPD_MAX_EMAX; + ctx->emin=MPD_MIN_EMIN; + ctx->round=MPD_ROUND_HALF_EVEN; + ctx->traps=MPD_Traps; + ctx->status=0; + ctx->newtrap=0; + ctx->clamp=0; + ctx->allcr=1; +} + +void +mpd_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) +{ + if (!(0 <= round && round < MPD_ROUND_GUARD)) { + return 0; + } + ctx->round = round; + return 1; +} + +int +mpd_qsettraps(mpd_context_t *ctx, uint32_t traps) +{ + if (traps > MPD_Max_status) { + return 0; + } + ctx->traps = traps; + return 1; +} + +int +mpd_qsetstatus(mpd_context_t *ctx, uint32_t flags) +{ + if (flags > MPD_Max_status) { + return 0; + } + ctx->status = flags; + return 1; +} + +int +mpd_qsetclamp(mpd_context_t *ctx, int c) +{ + if (c != 0 && c != 1) { + return 0; + } + ctx->clamp = c; + return 1; +} + +int +mpd_qsetcr(mpd_context_t *ctx, int c) +{ + if (c != 0 && c != 1) { + return 0; + } + ctx->allcr = c; + return 1; +} + + +void +mpd_addstatus_raise(mpd_context_t *ctx, uint32_t flags) +{ + ctx->status |= flags; + if (flags&ctx->traps) { + ctx->newtrap = (flags&ctx->traps); + mpd_traphandler(ctx); + } +} + + diff -r 207408428242 -r 40917e4b51aa Modules/_decimal/convolute.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/convolute.c Thu Feb 23 16:43:15 2012 +0100 @@ -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 "bits.h" +#include "constants.h" +#include "fnt.h" +#include "fourstep.h" +#include "numbertheory.h" +#include "sixstep.h" +#include "umodarith.h" +#include "convolute.h" + + +/* Bignum: Fast convolution using the Number Theoretic Transform. Used for + the multiplication of very large coefficients. */ + + +/* Convolute the data in c1 and c2. Result is in c1. */ +int +fnt_convolute(mpd_uint_t *c1, mpd_uint_t *c2, mpd_size_t n, int modnum) +{ + int (*fnt)(mpd_uint_t *, mpd_size_t, int); + int (*inv_fnt)(mpd_uint_t *, mpd_size_t, int); +#ifdef PPRO + double dmod; + uint32_t dinvmod[3]; +#endif + mpd_uint_t n_inv, umod; + mpd_size_t i; + + + SETMODULUS(modnum); + n_inv = POWMOD(n, (umod-2)); + + if (ispower2(n)) { + if (n > SIX_STEP_THRESHOLD) { + fnt = six_step_fnt; + inv_fnt = inv_six_step_fnt; + } + else { + fnt = std_fnt; + inv_fnt = std_inv_fnt; + } + } + else { + fnt = four_step_fnt; + inv_fnt = inv_four_step_fnt; + } + + if (!fnt(c1, n, modnum)) { + return 0; + } + if (!fnt(c2, n, modnum)) { + return 0; + } + for (i = 0; i < n-1; i += 2) { + mpd_uint_t x0 = c1[i]; + mpd_uint_t y0 = c2[i]; + mpd_uint_t x1 = c1[i+1]; + mpd_uint_t y1 = c2[i+1]; + MULMOD2(&x0, y0, &x1, y1); + c1[i] = x0; + c1[i+1] = x1; + } + + if (!inv_fnt(c1, n, modnum)) { + return 0; + } + for (i = 0; i < n-3; i += 4) { + mpd_uint_t x0 = c1[i]; + mpd_uint_t x1 = c1[i+1]; + mpd_uint_t x2 = c1[i+2]; + mpd_uint_t x3 = c1[i+3]; + MULMOD2C(&x0, &x1, n_inv); + MULMOD2C(&x2, &x3, n_inv); + c1[i] = x0; + c1[i+1] = x1; + c1[i+2] = x2; + c1[i+3] = x3; + } + + return 1; +} + +/* Autoconvolute the data in c1. Result is in c1. */ +int +fnt_autoconvolute(mpd_uint_t *c1, mpd_size_t n, int modnum) +{ + int (*fnt)(mpd_uint_t *, mpd_size_t, int); + int (*inv_fnt)(mpd_uint_t *, mpd_size_t, int); +#ifdef PPRO + double dmod; + uint32_t dinvmod[3]; +#endif + mpd_uint_t n_inv, umod; + mpd_size_t i; + + + SETMODULUS(modnum); + n_inv = POWMOD(n, (umod-2)); + + if (ispower2(n)) { + if (n > SIX_STEP_THRESHOLD) { + fnt = six_step_fnt; + inv_fnt = inv_six_step_fnt; + } + else { + fnt = std_fnt; + inv_fnt = std_inv_fnt; + } + } + else { + fnt = four_step_fnt; + inv_fnt = inv_four_step_fnt; + } + + if (!fnt(c1, n, modnum)) { + return 0; + } + for (i = 0; i < n-1; i += 2) { + mpd_uint_t x0 = c1[i]; + mpd_uint_t x1 = c1[i+1]; + MULMOD2(&x0, x0, &x1, x1); + c1[i] = x0; + c1[i+1] = x1; + } + + if (!inv_fnt(c1, n, modnum)) { + return 0; + } + for (i = 0; i < n-3; i += 4) { + mpd_uint_t x0 = c1[i]; + mpd_uint_t x1 = c1[i+1]; + mpd_uint_t x2 = c1[i+2]; + mpd_uint_t x3 = c1[i+3]; + MULMOD2C(&x0, &x1, n_inv); + MULMOD2C(&x2, &x3, n_inv); + c1[i] = x0; + c1[i+1] = x1; + c1[i+2] = x2; + c1[i+3] = x3; + } + + return 1; +} + + diff -r 207408428242 -r 40917e4b51aa Modules/_decimal/convolute.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/convolute.h Thu Feb 23 16:43:15 2012 +0100 @@ -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 207408428242 -r 40917e4b51aa Modules/_decimal/crt.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/crt.c Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,179 @@ +/* + * 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" + + +/* Bignum: Chinese Remainder Theorem, extends the maximum transform length. */ + + +/* Multiply P1P2 by v, store result in w. */ +static inline void +_crt_mulP1P2_3(mpd_uint_t w[3], mpd_uint_t v) +{ + mpd_uint_t hi1, hi2, lo; + + _mpd_mul_words(&hi1, &lo, LH_P1P2, v); + w[0] = lo; + + _mpd_mul_words(&hi2, &lo, UH_P1P2, v); + lo = hi1 + lo; + if (lo < hi1) hi2++; + + w[1] = lo; + w[2] = hi2; +} + +/* Add 3 words from v to w. The result is known to fit in w. */ +static inline void +_crt_add3(mpd_uint_t w[3], mpd_uint_t v[3]) +{ + mpd_uint_t carry; + mpd_uint_t s; + + s = w[0] + v[0]; + carry = (s < w[0]); + w[0] = s; + + s = w[1] + (v[1] + carry); + carry = (s < w[1]); + w[1] = s; + + w[2] = w[2] + (v[2] + carry); +} + +/* Divide 3 words in u by v, store result in w, return remainder. */ +static inline mpd_uint_t +_crt_div3(mpd_uint_t *w, const mpd_uint_t *u, mpd_uint_t v) +{ + mpd_uint_t r1 = u[2]; + mpd_uint_t r2; + + if (r1 < v) { + w[2] = 0; + } + else { + _mpd_div_word(&w[2], &r1, u[2], v); /* GCOV_NOT_REACHED */ + } + + _mpd_div_words(&w[1], &r2, r1, u[1], v); + _mpd_div_words(&w[0], &r1, r2, u[0], v); + + return r1; +} + + +/* + * Chinese Remainder Theorem: + * Algorithm from Joerg Arndt, "Matters Computational", + * Chapter 37.4.1 [http://www.jjj.de/fxt/] + * + * See also Knuth, TAOCP, Volume 2, 4.3.2, exercise 7. + */ + +/* + * CRT with carry: x1, x2, x3 contain numbers modulo p1, p2, p3. For each + * triple of members of the arrays, find the unique z modulo p1*p2*p3, with + * zmax = p1*p2*p3 - 1. + * + * In each iteration of the loop, split z into result[i] = z % MPD_RADIX + * and carry = z / MPD_RADIX. Let N be the size of carry[] and cmax the + * maximum carry. + * + * Limits for the 32-bit build: + * + * N = 2**96 + * cmax = 7711435591312380274 + * + * Limits for the 64 bit build: + * + * N = 2**192 + * cmax = 627710135393475385904124401220046371710 + * + * The following statements hold for both versions: + * + * 1) cmax + zmax < N, so the addition does not overflow. + * + * 2) (cmax + zmax) / MPD_RADIX == cmax. + * + * 3) If c <= cmax, then c_next = (c + zmax) / MPD_RADIX <= cmax. + */ +void +crt3(mpd_uint_t *x1, mpd_uint_t *x2, mpd_uint_t *x3, mpd_size_t rsize) +{ + mpd_uint_t p1 = mpd_moduli[P1]; + mpd_uint_t umod; +#ifdef PPRO + double dmod; + uint32_t dinvmod[3]; +#endif + mpd_uint_t a1, a2, a3; + mpd_uint_t s; + mpd_uint_t z[3], t[3]; + mpd_uint_t carry[3] = {0,0,0}; + mpd_uint_t hi, lo; + mpd_size_t i; + + for (i = 0; i < rsize; i++) { + + a1 = x1[i]; + a2 = x2[i]; + a3 = x3[i]; + + SETMODULUS(P2); + s = ext_submod(a2, a1, umod); + s = MULMOD(s, INV_P1_MOD_P2); + + _mpd_mul_words(&hi, &lo, s, p1); + lo = lo + a1; + if (lo < a1) hi++; + + SETMODULUS(P3); + s = dw_submod(a3, hi, lo, umod); + s = MULMOD(s, INV_P1P2_MOD_P3); + + z[0] = lo; + z[1] = hi; + z[2] = 0; + + _crt_mulP1P2_3(t, s); + _crt_add3(z, t); + _crt_add3(carry, z); + + x1[i] = _crt_div3(carry, carry, MPD_RADIX); + } + + assert(carry[0] == 0 && carry[1] == 0 && carry[2] == 0); +} + + diff -r 207408428242 -r 40917e4b51aa Modules/_decimal/crt.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/crt.h Thu Feb 23 16:43:15 2012 +0100 @@ -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 207408428242 -r 40917e4b51aa Modules/_decimal/difradix2.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/difradix2.c Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,173 @@ +/* + * 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" + + +/* Bignum: The actual transform routine (decimation in frequency). */ + + +/* + * Generate index pairs (x, bitreverse(x)) and carry out the permutation. + * n must be a power of two. + * Algorithm due to Brent/Lehmann, see Joerg Arndt, "Matters Computational", + * Chapter 1.14.4. [http://www.jjj.de/fxt/] + */ +static inline void +bitreverse_permute(mpd_uint_t a[], mpd_size_t n) +{ + mpd_size_t x = 0; + mpd_size_t r = 0; + mpd_uint_t t; + + do { /* Invariant: r = bitreverse(x) */ + if (r > x) { + t = a[x]; + a[x] = a[r]; + a[r] = t; + } + /* Flip trailing consecutive 1 bits and the first zero bit + * that absorbs a possible carry. */ + x += 1; + /* Mirror the operation on r: Flip n_trailing_zeros(x)+1 + high bits of r. */ + r ^= (n - (n >> (mpd_bsf(x)+1))); + /* The loop invariant is preserved. */ + } while (x < n); +} + + +/* Fast Number Theoretic Transform, decimation in frequency. */ +void +fnt_dif2(mpd_uint_t a[], mpd_size_t n, struct fnt_params *tparams) +{ + mpd_uint_t *wtable = tparams->wtable; + mpd_uint_t umod; +#ifdef PPRO + double dmod; + uint32_t dinvmod[3]; +#endif + mpd_uint_t u0, u1, v0, v1; + mpd_uint_t w, w0, w1, wstep; + mpd_size_t m, mhalf; + mpd_size_t j, r; + + + assert(ispower2(n)); + assert(n >= 4); + + SETMODULUS(tparams->modnum); + + /* m == n */ + mhalf = n / 2; + for (j = 0; j < mhalf; j += 2) { + + w0 = wtable[j]; + w1 = wtable[j+1]; + + u0 = a[j]; + v0 = a[j+mhalf]; + + u1 = a[j+1]; + v1 = a[j+1+mhalf]; + + a[j] = addmod(u0, v0, umod); + v0 = submod(u0, v0, umod); + + a[j+1] = addmod(u1, v1, umod); + v1 = submod(u1, v1, umod); + + MULMOD2(&v0, w0, &v1, w1); + + a[j+mhalf] = v0; + a[j+1+mhalf] = v1; + + } + + wstep = 2; + for (m = n/2; m >= 2; m>>=1, wstep<<=1) { + + mhalf = m / 2; + + /* j == 0 */ + for (r = 0; r < n; r += 2*m) { + + u0 = a[r]; + v0 = a[r+mhalf]; + + u1 = a[m+r]; + v1 = a[m+r+mhalf]; + + a[r] = addmod(u0, v0, umod); + v0 = submod(u0, v0, umod); + + a[m+r] = addmod(u1, v1, umod); + v1 = submod(u1, v1, umod); + + a[r+mhalf] = v0; + a[m+r+mhalf] = v1; + } + + for (j = 1; j < mhalf; j++) { + + w = wtable[j*wstep]; + + for (r = 0; r < n; r += 2*m) { + + u0 = a[r+j]; + v0 = a[r+j+mhalf]; + + u1 = a[m+r+j]; + v1 = a[m+r+j+mhalf]; + + a[r+j] = addmod(u0, v0, umod); + v0 = submod(u0, v0, umod); + + a[m+r+j] = addmod(u1, v1, umod); + v1 = submod(u1, v1, umod); + + MULMOD2C(&v0, &v1, w); + + a[r+j+mhalf] = v0; + a[m+r+j+mhalf] = v1; + } + + } + + } + + bitreverse_permute(a, n); +} + + diff -r 207408428242 -r 40917e4b51aa Modules/_decimal/difradix2.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/difradix2.h Thu Feb 23 16:43:15 2012 +0100 @@ -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 207408428242 -r 40917e4b51aa Modules/_decimal/docstrings.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/docstrings.h Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,747 @@ +/* + * Copyright (c) 2001-2010 Python Software Foundation. All Rights Reserved. + * Modified and extended by Stefan Krah. + */ + + +#ifndef DOCSTRINGS_H +#define DOCSTRINGS_H + + +#include "pymacro.h" + + +/******************************************************************************/ +/* Module */ +/******************************************************************************/ + + +PyDoc_STRVAR(doc__decimal, +"C decimal arithmetic module"); + +PyDoc_STRVAR(doc_getcontext,"\n\ +getcontext() - Get the current default context.\n\ +\n"); + +PyDoc_STRVAR(doc_setcontext,"\n\ +setcontext(c) - Set a new default context.\n\ +\n"); + +PyDoc_STRVAR(doc_localcontext,"\n\ +localcontext(c) - Return a context manager that will set the default context\n\ +to a copy of c on entry to the with-statement and restore the previous default\n\ +context when exiting the with-statement. If no context is specified, a copy of\n\ +the current default context is used.\n\ +\n"); + +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_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_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"); + +PyDoc_STRVAR(doc_number_class,"\n\ +number_class([context]) - Return a string describing the class of the operand.\n\ +The returned value is one of the following ten strings:\n\ +\n\ + * '-Infinity', indicating that the operand is negative infinity.\n\ + * '-Normal', indicating that the operand is a negative normal number.\n\ + * '-Subnormal', indicating that the operand is negative and subnormal.\n\ + * '-Zero', indicating that the operand is a negative zero.\n\ + * '+Zero', indicating that the operand is a positive zero.\n\ + * '+Subnormal', indicating that the operand is positive and subnormal.\n\ + * '+Normal', indicating that the operand is a positive normal number.\n\ + * '+Infinity', indicating that the operand is positive infinity.\n\ + * 'NaN', indicating that the operand is a quiet NaN (Not a Number).\n\ + * 'sNaN', indicating that the operand is a signaling NaN.\n\ +\n\ +\n"); + +PyDoc_STRVAR(doc_quantize,"\n\ +quantize(exp[, rounding[, context]]) - Return a value equal to the first\n\ +operand after rounding and having the exponent of the second operand.\n\ +\n\ + >>> Decimal('1.41421356').quantize(Decimal('1.000'))\n\ + Decimal('1.414')\n\ +\n\ +Unlike other operations, if the length of the coefficient after the quantize\n\ +operation would be greater than precision, then an InvalidOperation is signaled.\n\ +This guarantees that, unless there is an error condition, the quantized exponent\n\ +is always equal to that of the right-hand operand.\n\ +\n\ +Also unlike other operations, quantize never signals Underflow, even if the\n\ +result is subnormal and inexact.\n\ +\n\ +If the exponent of the second operand is larger than that of the first, then\n\ +rounding may be necessary. In this case, the rounding mode is determined by the\n\ +rounding argument if given, else by the given context argument; if neither\n\ +argument is given, the rounding mode of the current thread's context is used.\n\ +\n"); + +PyDoc_STRVAR(doc_radix,"\n\ +radix() - Return Decimal(10), the radix (base) in which the Decimal class does\n\ +all its arithmetic. Included for compatibility with the specification.\n\ +\n"); + +PyDoc_STRVAR(doc_remainder_near,"\n\ +remainder_near(other[, context]) - Compute the modulo as either a positive\n\ +or negative value depending on which is closest to zero. For instance,\n\ +Decimal(10).remainder_near(6) returns Decimal('-2'), which is closer to zero\n\ +than Decimal('4').\n\ +\n\ +If both are equally close, the one chosen will have the same sign as self.\n\ +\n"); + +PyDoc_STRVAR(doc_rotate,"\n\ +rotate(other[, context]) - Return the result of rotating the digits of the\n\ +first operand by an amount specified by the second operand. The second operand\n\ +must be an integer in the range -precision through precision. The absolute\n\ +value of the second operand gives the number of places to rotate. If the second\n\ +operand is positive then rotation is to the left; otherwise rotation is to the\n\ +right. The coefficient of the first operand is padded on the left with zeros to\n\ +length precision if necessary. The sign and exponent of the first operand are\n\ +unchanged.\n\ +\n"); + +PyDoc_STRVAR(doc_same_quantum,"\n\ +same_quantum(other[, context]) - Test whether self and other have the\n\ +same exponent or whether both are NaN.\n\ +\n"); + +PyDoc_STRVAR(doc_scaleb,"\n\ +scaleb(other[, context]) - Return the first operand with the exponent adjusted\n\ +the second. Equivalently, return the first operand multiplied by 10**other.\n\ +The second operand must be an integer.\n\ +\n"); + +PyDoc_STRVAR(doc_shift,"\n\ +shift(other[, context]) - Return the result of shifting the digits of\n\ +the first operand by an amount specified by the second operand. The second\n\ +operand must be an integer in the range -precision through precision. The\n\ +absolute value of the second operand gives the number of places to shift.\n\ +If the second operand is positive, then the shift is to the left; otherwise\n\ +the shift is to the right. Digits shifted into the coefficient are zeros.\n\ +The sign and exponent of the first operand are unchanged.\n\ +\n"); + +PyDoc_STRVAR(doc_sqrt,"\n\ +sqrt([context]) - Return the square root of the argument to full precision.\n\ +The result is correctly rounded using the ROUND_HALF_EVEN rounding mode.\n\ +\n"); + +PyDoc_STRVAR(doc_to_eng_string,"\n\ +to_eng_string([context]) - Convert to an engineering-type string.\n\ +Engineering notation has an exponent which is a multiple of 3, so\n\ +there are up to 3 digits left of the decimal place. For example,\n\ +Decimal('123E+1') is converted to Decimal('1.23E+3')\n\ +\n"); + +PyDoc_STRVAR(doc_to_integral,"\n\ +to_integral([rounding[, context]]) - Identical to the to_integral_value()\n\ +method. The to_integral name has been kept for compatibility with older\n\ +versions.\n\ +\n"); + +PyDoc_STRVAR(doc_to_integral_exact,"\n\ +to_integral_exact([rounding[, context]]) - Round to the nearest integer,\n\ +signaling Inexact or Rounded as appropriate if rounding occurs. The rounding\n\ +mode is determined by the rounding parameter if given, else by the given\n\ +context. If neither parameter is given, then the rounding mode of the current\n\ +default context is used.\n\ +\n"); + +PyDoc_STRVAR(doc_to_integral_value,"\n\ +to_integral_value([rounding[, context]]) - Round to the nearest integer without\n\ +signaling Inexact or Rounded. The rounding mode is determined by the rounding\n\ +parameter if given, else by the given context. If neither parameter is given,\n\ +then the rounding mode of the current default context is used.\n\ +\n"); + + +/******************************************************************************/ +/* Context Object and Methods */ +/******************************************************************************/ + +PyDoc_STRVAR(doc_context,"\n\ +The context affects almost all operations and controls rounding,\n\ +Over/Underflow, raising of exceptions and much more. A new context\n\ +can be constructed as follows:\n\ +\n\ + >>> c = Context(prec=28, Emin=-425000000, Emax=425000000,\n\ + ... rounding=ROUND_HALF_EVEN, capitals=1, clamp=1,\n\ + ... traps=[InvalidOperation, DivisionByZero, Overflow],\n\ + ... flags=[], _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_divide,"\n\ +divide(x, y) - Return x divided by y.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_divide_int,"\n\ +divide_int(x, y) - Return x divided by y, truncated to an integer.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_divmod,"\n\ +divmod(x, y) - Return quotient and remainder of the division x / y.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_exp,"\n\ +exp(x) - Return e ** x.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_fma,"\n\ +fma(x, y, z) - Return x multiplied by y, plus z.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_is_canonical,"\n\ +is_canonical(x) - Return True if x is canonical, False otherwise.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_is_finite,"\n\ +is_finite(x) - Return True if x is finite, False otherwise.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_is_infinite,"\n\ +is_infinite(x) - Return True if x is infinite, False otherwise.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_is_nan,"\n\ +is_nan(x) - Return True if x is a qNaN or sNaN, False otherwise.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_is_normal,"\n\ +is_normal(x) - Return True if x is a normal number, False otherwise.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_is_qnan,"\n\ +is_qnan(x) - Return True if x is a quiet NaN, False otherwise.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_is_signed,"\n\ +is_signed(x) - Return True if x is negative, False otherwise.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_is_snan,"\n\ +is_snan() - Return True if x is a signaling NaN, False otherwise.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_is_subnormal,"\n\ +is_subnormal(x) - Return True if x is subnormal, False otherwise.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_is_zero,"\n\ +is_zero(x) - Return True if x is a zero, False otherwise.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_ln,"\n\ +ln(x) - Return the natural (base e) logarithm of x.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_log10,"\n\ +log10(x) - Return the base 10 logarithm of x.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_logb,"\n\ +logb(x) - Return the exponent of the magnitude of the operand's MSD.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_logical_and,"\n\ +logical_and(x, y) - Digit-wise and of x and y.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_logical_invert,"\n\ +logical_invert(x) - Invert all digits of x.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_logical_or,"\n\ +logical_or(x, y) - Digit-wise or of x and y.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_logical_xor,"\n\ +logical_xor(x, y) - Digit-wise xor of x and y.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_max,"\n\ +max(x, y) - Compare the values numerically and return the maximum.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_max_mag,"\n\ +max_mag(x, y) - Compare the values numerically with their sign ignored.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_min,"\n\ +min(x, y) - Compare the values numerically and return the minimum.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_min_mag,"\n\ +min_mag(x, y) - Compare the values numerically with their sign ignored.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_minus,"\n\ +minus(x) - Minus corresponds to the unary prefix minus operator in Python,\n\ +but applies the context to the result.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_multiply,"\n\ +multiply(x, y) - Return the product of x and y.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_next_minus,"\n\ +next_minus(x) - Return the largest representable number smaller than x.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_next_plus,"\n\ +next_plus(x) - Return the smallest representable number larger than x.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_next_toward,"\n\ +next_toward(x) - Return the number closest to x, in the direction towards y.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_normalize,"\n\ +normalize(x) - Reduce x to its simplest form. Alias for reduce(x).\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_number_class,"\n\ +number_class(x) - Return an indication of the class of x.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_plus,"\n\ +plus(x) - Plus corresponds to the unary prefix plus operator in Python,\n\ +but applies the context to the result.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_power,"\n\ +power(x, y) - Compute x**y. If x is negative, then y must be integral.\n\ +The result will be inexact unless y is integral and the result is finite\n\ +and can be expressed exactly in 'precision' digits.\n\ +\n\ +power(x, y, m) - Compute (x**y) % m. The following restrictions hold:\n\ +\n\ + * all three arguments must be integral\n\ + * y must be nonnegative\n\ + * at least one of x or y must be nonzero\n\ + * m must be nonzero and less than 10**prec in absolute value\n\ +\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_quantize,"\n\ +quantize(x, y) - Return a value equal to x (rounded), having the exponent of y.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_radix,"\n\ +radix() - Return 10.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_remainder,"\n\ +remainder(x, y) - Return the remainder from integer division. The sign of\n\ +the result, if non-zero, is the same as that of the original dividend.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_remainder_near,"\n\ +remainder_near(x, y) - Return x - y * n, where n is the integer nearest the\n\ +exact value of x / y (if the result is 0 then its sign will be the sign of x).\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_rotate,"\n\ +rotate(x, y) - Return a copy of x, rotated by y places.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_same_quantum,"\n\ +same_quantum(x, y) - Return True if the two operands have the same exponent.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_scaleb,"\n\ +scaleb(x, y) - Return the first operand after adding the second value\n\ +to its exp.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_shift,"\n\ +shift(x, y) - Return a copy of x, shifted by y places.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_sqrt,"\n\ +sqrt(x) - Square root of a non-negative number to context precision.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_subtract,"\n\ +subtract(x, y) - Return the difference between x and y.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_to_eng_string,"\n\ +to_eng_string(x) - Convert a number to a string, using engineering notation.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_to_integral,"\n\ +to_integral(x) - Identical to to_integral_value(x).\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_to_integral_exact,"\n\ +to_integral_exact(x) - Round to an integer. Signal if the result is\n\ +rounded or inexact.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_to_integral_value,"\n\ +to_integral_value(x) - Round to an integer.\n\ +\n"); + +PyDoc_STRVAR(doc_ctx_to_sci_string,"\n\ +to_sci_string(x) - Convert a number to a string using scientific notation.\n\ +\n"); + + +#endif /* DOCSTRINGS_H */ + + + diff -r 207408428242 -r 40917e4b51aa Modules/_decimal/fnt.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/fnt.c Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,81 @@ +/* + * 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" + + +/* Bignum: Fast transform for medium-sized coefficients. */ + + +/* forward transform, sign = -1 */ +int +std_fnt(mpd_uint_t *a, mpd_size_t n, int modnum) +{ + struct fnt_params *tparams; + + assert(ispower2(n)); + assert(n >= 4); + assert(n <= 3*MPD_MAXTRANSFORM_2N); + + if ((tparams = _mpd_init_fnt_params(n, -1, modnum)) == NULL) { + return 0; + } + fnt_dif2(a, n, tparams); + + mpd_free(tparams); + return 1; +} + +/* reverse transform, sign = 1 */ +int +std_inv_fnt(mpd_uint_t *a, mpd_size_t n, int modnum) +{ + struct fnt_params *tparams; + + assert(ispower2(n)); + assert(n >= 4); + assert(n <= 3*MPD_MAXTRANSFORM_2N); + + if ((tparams = _mpd_init_fnt_params(n, 1, modnum)) == NULL) { + return 0; + } + fnt_dif2(a, n, tparams); + + mpd_free(tparams); + return 1; +} + + + diff -r 207408428242 -r 40917e4b51aa Modules/_decimal/fnt.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/fnt.h Thu Feb 23 16:43:15 2012 +0100 @@ -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 207408428242 -r 40917e4b51aa Modules/_decimal/fourstep.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/fourstep.c Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,255 @@ +/* + * 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" + + +/* Bignum: Cache efficient Matrix Fourier Transform for arrays of the + form 3 * 2**n (See literature/matrix-transform.txt). */ + + +#ifndef PPRO +static inline void +std_size3_ntt(mpd_uint_t *x1, mpd_uint_t *x2, mpd_uint_t *x3, + mpd_uint_t w3table[3], mpd_uint_t umod) +{ + mpd_uint_t r1, r2; + mpd_uint_t w; + mpd_uint_t s, tmp; + + + /* k = 0 -> w = 1 */ + s = *x1; + s = addmod(s, *x2, umod); + s = addmod(s, *x3, umod); + + r1 = s; + + /* k = 1 */ + s = *x1; + + w = w3table[1]; + tmp = MULMOD(*x2, w); + s = addmod(s, tmp, umod); + + w = w3table[2]; + tmp = MULMOD(*x3, w); + s = addmod(s, tmp, umod); + + r2 = s; + + /* k = 2 */ + s = *x1; + + w = w3table[2]; + tmp = MULMOD(*x2, w); + s = addmod(s, tmp, umod); + + w = w3table[1]; + tmp = MULMOD(*x3, w); + s = addmod(s, tmp, umod); + + *x3 = s; + *x2 = r2; + *x1 = r1; +} +#else /* PPRO */ +static inline void +ppro_size3_ntt(mpd_uint_t *x1, mpd_uint_t *x2, mpd_uint_t *x3, mpd_uint_t w3table[3], + mpd_uint_t umod, double *dmod, uint32_t dinvmod[3]) +{ + mpd_uint_t r1, r2; + mpd_uint_t w; + mpd_uint_t s, tmp; + + + /* k = 0 -> w = 1 */ + s = *x1; + s = addmod(s, *x2, umod); + s = addmod(s, *x3, umod); + + r1 = s; + + /* k = 1 */ + s = *x1; + + w = w3table[1]; + tmp = ppro_mulmod(*x2, w, dmod, dinvmod); + s = addmod(s, tmp, umod); + + w = w3table[2]; + tmp = ppro_mulmod(*x3, w, dmod, dinvmod); + s = addmod(s, tmp, umod); + + r2 = s; + + /* k = 2 */ + s = *x1; + + w = w3table[2]; + tmp = ppro_mulmod(*x2, w, dmod, dinvmod); + s = addmod(s, tmp, umod); + + w = w3table[1]; + tmp = ppro_mulmod(*x3, w, dmod, dinvmod); + s = addmod(s, tmp, umod); + + *x3 = s; + *x2 = r2; + *x1 = r1; +} +#endif + + +/* forward transform, sign = -1; transform length = 3 * 2**n */ +int +four_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum) +{ + mpd_size_t R = 3; /* number of rows */ + mpd_size_t C = n / 3; /* number of columns */ + mpd_uint_t w3table[3]; + mpd_uint_t kernel, w0, w1, wstep; + mpd_uint_t *s, *p0, *p1, *p2; + mpd_uint_t umod; +#ifdef PPRO + double dmod; + uint32_t dinvmod[3]; +#endif + mpd_size_t i, k; + + + assert(n >= 48); + assert(n <= 3*MPD_MAXTRANSFORM_2N); + + + /* Length R transform on the columns. */ + SETMODULUS(modnum); + _mpd_init_w3table(w3table, -1, modnum); + for (p0=a, p1=p0+C, p2=p0+2*C; p0= 48); + assert(n <= 3*MPD_MAXTRANSFORM_2N); + + +#if 0 /* An unordered transform is sufficient for convolution. */ + /* Transpose the matrix, producing an R*C matrix. */ + transpose_3xpow2(a, C, R); +#endif + + /* Length C transform on the rows. */ + for (s = a; s < a+n; s += C) { + if (!inv_six_step_fnt(s, C, modnum)) { + return 0; + } + } + + /* Multiply each matrix element (addressed by i*C+k) by r**(i*k). */ + SETMODULUS(modnum); + kernel = _mpd_getkernel(n, 1, modnum); + for (i = 1; i < R; i++) { + w0 = 1; + w1 = POWMOD(kernel, i); + wstep = MULMOD(w1, w1); + for (k = 0; k < C; k += 2) { + mpd_uint_t x0 = a[i*C+k]; + mpd_uint_t x1 = a[i*C+k+1]; + MULMOD2(&x0, w0, &x1, w1); + MULMOD2C(&w0, &w1, wstep); + a[i*C+k] = x0; + a[i*C+k+1] = x1; + } + } + + /* Length R transform on the columns. */ + _mpd_init_w3table(w3table, 1, modnum); + for (p0=a, p1=p0+C, p2=p0+2*C; p0 + + +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 207408428242 -r 40917e4b51aa Modules/_decimal/io.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/io.c Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,1575 @@ +/* + * 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" + + +/* This file contains functions for decimal <-> string conversions, including + PEP-3101 formatting for numeric types. */ + + +/* + * Work around the behavior of tolower() and strcasecmp() in certain + * locales. For example, in tr_TR.utf8: + * + * tolower((unsigned char)'I') == 'I' + * + * u is the exact uppercase version of l; n is strlen(l) or strlen(l)+1 + */ +static inline int +_mpd_strneq(const char *s, const char *l, const char *u, size_t n) +{ + while (--n != SIZE_MAX) { + if (*s != *l && *s != *u) { + return 0; + } + s++; u++; l++; + } + + return 1; +} + +static mpd_ssize_t +strtoexp(const char *s) +{ + char *end; + mpd_ssize_t retval; + + errno = 0; + retval = mpd_strtossize(s, &end, 10); + if (errno == 0 && !(*s != '\0' && *end == '\0')) + errno = EINVAL; + + return retval; +} + +/* + * Scan 'len' words. The most significant word contains 'r' digits, + * the remaining words are full words. Skip dpoint. The string 's' must + * consist of digits and an optional single decimal point at 'dpoint'. + */ +static void +string_to_coeff(mpd_uint_t *data, const char *s, const char *dpoint, int r, + size_t len) +{ + int j; + + if (r > 0) { + data[--len] = 0; + for (j = 0; j < r; j++, s++) { + if (s == dpoint) s++; + data[len] = 10 * data[len] + (*s - '0'); + } + } + + while (--len != SIZE_MAX) { + data[len] = 0; + for (j = 0; j < MPD_RDIGITS; j++, s++) { + if (s == dpoint) s++; + data[len] = 10 * data[len] + (*s - '0'); + } + } +} + +/* + * Partially verify a numeric string of the form: + * + * [cdigits][.][cdigits][eE][+-][edigits] + * + * If successful, return a pointer to the location of the first + * relevant coefficient digit. This digit is either non-zero or + * part of one of the following patterns: + * + * ["0\x00", "0.\x00", "0.E", "0.e", "0E", "0e"] + * + * The locations of a single optional dot or indicator are stored + * in 'dpoint' and 'exp'. + * + * The end of the string is stored in 'end'. If an indicator [eE] + * occurs without trailing [edigits], the condition is caught + * later by strtoexp(). + */ +static const char * +scan_dpoint_exp(const char *s, const char **dpoint, const char **exp, + const char **end) +{ + const char *coeff = NULL; + + *dpoint = NULL; + *exp = NULL; + for (; *s != '\0'; s++) { + switch (*s) { + case '.': + if (*dpoint != NULL || *exp != NULL) + return NULL; + *dpoint = s; + break; + case 'E': case 'e': + if (*exp != NULL) + return NULL; + *exp = s; + if (*(s+1) == '+' || *(s+1) == '-') + s++; + break; + default: + if (!isdigit((uchar)*s)) + return NULL; + if (coeff == NULL && *exp == NULL) { + if (*s == '0') { + if (!isdigit((uchar)*(s+1))) + if (!(*(s+1) == '.' && + isdigit((uchar)*(s+2)))) + coeff = s; + } + else { + coeff = s; + } + } + break; + + } + } + + *end = s; + return coeff; +} + +/* scan the payload of a NaN */ +static const char * +scan_payload(const char *s, const char **end) +{ + const char *coeff; + + while (*s == '0') + s++; + coeff = s; + + while (isdigit((uchar)*s)) + s++; + *end = s; + + return (*s == '\0') ? coeff : NULL; +} + +/* convert a character string to a decimal */ +void +mpd_qset_string(mpd_t *dec, const char *s, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_ssize_t q, r, len; + const char *coeff, *end; + const char *dpoint = NULL, *exp = NULL; + size_t digits; + uint8_t sign = MPD_POS; + + mpd_set_flags(dec, 0); + dec->len = 0; + dec->exp = 0; + + /* sign */ + if (*s == '+') { + s++; + } + else if (*s == '-') { + mpd_set_negative(dec); + sign = MPD_NEG; + s++; + } + + if (_mpd_strneq(s, "nan", "NAN", 3)) { /* NaN */ + s += 3; + mpd_setspecial(dec, sign, MPD_NAN); + if (*s == '\0') + return; + /* validate payload: digits only */ + if ((coeff = scan_payload(s, &end)) == NULL) + goto conversion_error; + /* payload consists entirely of zeros */ + if (*coeff == '\0') + return; + digits = end - coeff; + /* prec >= 1, clamp is 0 or 1 */ + if (digits > (size_t)(ctx->prec-ctx->clamp)) + goto conversion_error; + } /* sNaN */ + else if (_mpd_strneq(s, "snan", "SNAN", 4)) { + s += 4; + mpd_setspecial(dec, sign, MPD_SNAN); + if (*s == '\0') + return; + /* validate payload: digits only */ + if ((coeff = scan_payload(s, &end)) == NULL) + goto conversion_error; + /* payload consists entirely of zeros */ + if (*coeff == '\0') + return; + digits = end - coeff; + if (digits > (size_t)(ctx->prec-ctx->clamp)) + goto conversion_error; + } + else if (_mpd_strneq(s, "inf", "INF", 3)) { + s += 3; + if (*s == '\0' || _mpd_strneq(s, "inity", "INITY", 6)) { + /* numeric-value: infinity */ + mpd_setspecial(dec, sign, MPD_INF); + return; + } + goto conversion_error; + } + else { + /* scan for start of coefficient, decimal point, indicator, end */ + if ((coeff = scan_dpoint_exp(s, &dpoint, &exp, &end)) == NULL) + goto conversion_error; + + /* numeric-value: [exponent-part] */ + if (exp) { + /* exponent-part */ + end = exp; exp++; + dec->exp = strtoexp(exp); + if (errno) { + if (!(errno == ERANGE && + (dec->exp == MPD_SSIZE_MAX || + dec->exp == MPD_SSIZE_MIN))) + goto conversion_error; + } + } + + digits = end - coeff; + if (dpoint) { + size_t fracdigits = end-dpoint-1; + if (dpoint > coeff) digits--; + + if (fracdigits > MPD_MAX_PREC) { + goto conversion_error; + } + if (dec->exp < MPD_SSIZE_MIN+(mpd_ssize_t)fracdigits) { + dec->exp = MPD_SSIZE_MIN; + } + else { + dec->exp -= (mpd_ssize_t)fracdigits; + } + } + if (digits > MPD_MAX_PREC) { + goto conversion_error; + } + if (dec->exp > MPD_EXP_INF) { + dec->exp = MPD_EXP_INF; + } + if (dec->exp == MPD_SSIZE_MIN) { + dec->exp = MPD_SSIZE_MIN+1; + } + } + + _mpd_idiv_word(&q, &r, (mpd_ssize_t)digits, MPD_RDIGITS); + + len = (r == 0) ? q : q+1; + if (len == 0) { + goto conversion_error; /* GCOV_NOT_REACHED */ + } + if (!mpd_qresize(dec, len, status)) { + mpd_seterror(dec, MPD_Malloc_error, status); + return; + } + dec->len = len; + + string_to_coeff(dec->data, coeff, dpoint, (int)r, len); + + mpd_setdigits(dec); + mpd_qfinalize(dec, ctx, status); + return; + +conversion_error: + /* standard wants a positive NaN */ + mpd_seterror(dec, MPD_Conversion_syntax, status); +} + +/* Print word x with n decimal digits to string s. dot is either NULL + or the location of a decimal point. */ +#define EXTRACT_DIGIT(s, x, d, dot) \ + if (s == dot) *s++ = '.'; *s++ = '0' + (char)(x / d); x %= d +static inline char * +word_to_string(char *s, mpd_uint_t x, int n, char *dot) +{ + switch(n) { +#ifdef CONFIG_64 + case 20: EXTRACT_DIGIT(s, x, 10000000000000000000ULL, dot); /* GCOV_NOT_REACHED */ + case 19: EXTRACT_DIGIT(s, x, 1000000000000000000ULL, dot); + case 18: EXTRACT_DIGIT(s, x, 100000000000000000ULL, dot); + case 17: EXTRACT_DIGIT(s, x, 10000000000000000ULL, dot); + case 16: EXTRACT_DIGIT(s, x, 1000000000000000ULL, dot); + case 15: EXTRACT_DIGIT(s, x, 100000000000000ULL, dot); + case 14: EXTRACT_DIGIT(s, x, 10000000000000ULL, dot); + case 13: EXTRACT_DIGIT(s, x, 1000000000000ULL, dot); + case 12: EXTRACT_DIGIT(s, x, 100000000000ULL, dot); + case 11: EXTRACT_DIGIT(s, x, 10000000000ULL, dot); +#endif + case 10: EXTRACT_DIGIT(s, x, 1000000000UL, dot); + case 9: EXTRACT_DIGIT(s, x, 100000000UL, dot); + case 8: EXTRACT_DIGIT(s, x, 10000000UL, dot); + case 7: EXTRACT_DIGIT(s, x, 1000000UL, dot); + case 6: EXTRACT_DIGIT(s, x, 100000UL, dot); + case 5: EXTRACT_DIGIT(s, x, 10000UL, dot); + case 4: EXTRACT_DIGIT(s, x, 1000UL, dot); + case 3: EXTRACT_DIGIT(s, x, 100UL, dot); + case 2: EXTRACT_DIGIT(s, x, 10UL, dot); + default: if (s == dot) *s++ = '.'; *s++ = '0' + (char)x; + } + + *s = '\0'; + return s; +} + +/* Print exponent x to string s. Undefined for MPD_SSIZE_MIN. */ +static inline char * +exp_to_string(char *s, mpd_ssize_t x) +{ + char sign = '+'; + + if (x < 0) { + sign = '-'; + x = -x; + } + *s++ = sign; + + return word_to_string(s, x, mpd_word_digits(x), NULL); +} + +/* Print the coefficient of dec to string s. len(dec) > 0. */ +static inline char * +coeff_to_string(char *s, const mpd_t *dec) +{ + mpd_uint_t x; + mpd_ssize_t i; + + /* most significant word */ + x = mpd_msword(dec); + s = word_to_string(s, x, mpd_word_digits(x), NULL); + + /* remaining full words */ + for (i=dec->len-2; i >= 0; --i) { + x = dec->data[i]; + s = word_to_string(s, x, MPD_RDIGITS, NULL); + } + + return s; +} + +/* Print the coefficient of dec to string s. len(dec) > 0. dot is either + NULL or a pointer to the location of a decimal point. */ +static inline char * +coeff_to_string_dot(char *s, char *dot, const mpd_t *dec) +{ + mpd_uint_t x; + mpd_ssize_t i; + + /* most significant word */ + x = mpd_msword(dec); + s = word_to_string(s, x, mpd_word_digits(x), dot); + + /* remaining full words */ + for (i=dec->len-2; i >= 0; --i) { + x = dec->data[i]; + s = word_to_string(s, x, MPD_RDIGITS, dot); + } + + return s; +} + +/* Format type */ +#define MPD_FMT_LOWER 0x00000000 +#define MPD_FMT_UPPER 0x00000001 +#define MPD_FMT_TOSCI 0x00000002 +#define MPD_FMT_TOENG 0x00000004 +#define MPD_FMT_EXP 0x00000008 +#define MPD_FMT_FIXED 0x00000010 +#define MPD_FMT_PERCENT 0x00000020 +#define MPD_FMT_SIGN_SPACE 0x00000040 +#define MPD_FMT_SIGN_PLUS 0x00000080 + +/* Default place of the decimal point for MPD_FMT_TOSCI, MPD_FMT_EXP */ +#define MPD_DEFAULT_DOTPLACE 1 + +/* + * Set *result to the string representation of a decimal. Return the length + * of *result, not including the terminating '\0' character. + * + * Formatting is done according to 'flags'. A return value of -1 with *result + * set to NULL indicates MPD_Malloc_error. + * + * 'dplace' is the default place of the decimal point. It is always set to + * MPD_DEFAULT_DOTPLACE except for zeros in combination with MPD_FMT_EXP. + */ +static mpd_ssize_t +_mpd_to_string(char **result, const mpd_t *dec, int flags, mpd_ssize_t dplace) +{ + char *decstring = NULL, *cp = NULL; + mpd_ssize_t ldigits; + mpd_ssize_t mem = 0, k; + + if (mpd_isspecial(dec)) { + + mem = sizeof "-Infinity"; + if (mpd_isnan(dec) && dec->len > 0) { + /* diagnostic code */ + mem += dec->digits; + } + cp = decstring = mpd_alloc(mem, sizeof *decstring); + if (cp == NULL) { + *result = NULL; + return -1; + } + + if (mpd_isnegative(dec)) { + *cp++ = '-'; + } + else if (flags&MPD_FMT_SIGN_SPACE) { + *cp++ = ' '; + } + else if (flags&MPD_FMT_SIGN_PLUS) { + *cp++ = '+'; + } + + if (mpd_isnan(dec)) { + if (mpd_isqnan(dec)) { + strcpy(cp, "NaN"); + cp += 3; + } + else { + strcpy(cp, "sNaN"); + cp += 4; + } + if (dec->len > 0) { /* diagnostic code */ + cp = coeff_to_string(cp, dec); + } + } + else if (mpd_isinfinite(dec)) { + strcpy(cp, "Infinity"); + cp += 8; + } + else { /* debug */ + abort(); /* GCOV_NOT_REACHED */ + } + } + else { + assert(dec->len > 0); + + /* + * For easier manipulation of the decimal point's location + * and the exponent that is finally printed, the number is + * rescaled to a virtual representation with exp = 0. Here + * ldigits denotes the number of decimal digits to the left + * of the decimal point and remains constant once initialized. + * + * dplace is the location of the decimal point relative to + * the start of the coefficient. Note that 3) always holds + * when dplace is shifted. + * + * 1) ldigits := dec->digits - dec->exp + * 2) dplace := ldigits (initially) + * 3) exp := ldigits - dplace (initially exp = 0) + * + * 0.00000_.____._____000000. + * ^ ^ ^ ^ + * | | | | + * | | | `- dplace >= digits + * | | `- dplace in the middle of the coefficient + * | ` dplace = 1 (after the first coefficient digit) + * `- dplace <= 0 + */ + + ldigits = dec->digits + dec->exp; + + if (flags&MPD_FMT_EXP) { + ; + } + else if (flags&MPD_FMT_FIXED || (dec->exp <= 0 && ldigits > -6)) { + /* MPD_FMT_FIXED: always use fixed point notation. + * MPD_FMT_TOSCI, MPD_FMT_TOENG: for a certain range, + * override exponent notation. */ + dplace = ldigits; + } + else if (flags&MPD_FMT_TOENG) { + if (mpd_iszero(dec)) { + /* If the exponent is divisible by three, + * dplace = 1. Otherwise, move dplace one + * or two places to the left. */ + dplace = -1 + mod_mpd_ssize_t(dec->exp+2, 3); + } + else { /* ldigits-1 is the adjusted exponent, which + * should be divisible by three. If not, move + * dplace one or two places to the right. */ + dplace += mod_mpd_ssize_t(ldigits-1, 3); + } + } + + /* + * Basic space requirements: + * + * [-][.][coeffdigits][E][-][expdigits+1][%]['\0'] + * + * If the decimal point lies outside of the coefficient digits, + * space is adjusted accordingly. + */ + if (dplace <= 0) { + mem = -dplace + dec->digits + 2; + } + else if (dplace >= dec->digits) { + mem = dplace; + } + else { + mem = dec->digits; + } + mem += (MPD_EXPDIGITS+1+6); + + cp = decstring = mpd_alloc(mem, sizeof *decstring); + if (cp == NULL) { + *result = NULL; + return -1; + } + + + if (mpd_isnegative(dec)) { + *cp++ = '-'; + } + else if (flags&MPD_FMT_SIGN_SPACE) { + *cp++ = ' '; + } + else if (flags&MPD_FMT_SIGN_PLUS) { + *cp++ = '+'; + } + + if (dplace <= 0) { + /* space: -dplace+dec->digits+2 */ + *cp++ = '0'; + *cp++ = '.'; + for (k = 0; k < -dplace; k++) { + *cp++ = '0'; + } + cp = coeff_to_string(cp, dec); + } + else if (dplace >= dec->digits) { + /* space: dplace */ + cp = coeff_to_string(cp, dec); + for (k = 0; k < dplace-dec->digits; k++) { + *cp++ = '0'; + } + } + else { + /* space: dec->digits+1 */ + cp = coeff_to_string_dot(cp, cp+dplace, dec); + } + + /* + * Conditions for printing an exponent: + * + * MPD_FMT_TOSCI, MPD_FMT_TOENG: only if ldigits != dplace + * MPD_FMT_FIXED: never (ldigits == dplace) + * MPD_FMT_EXP: always + */ + if (ldigits != dplace || flags&MPD_FMT_EXP) { + /* space: expdigits+2 */ + *cp++ = (flags&MPD_FMT_UPPER) ? 'E' : 'e'; + cp = exp_to_string(cp, ldigits-dplace); + } + + if (flags&MPD_FMT_PERCENT) { + *cp++ = '%'; + } + } + + assert(cp < decstring+mem); + assert(cp-decstring < MPD_SSIZE_MAX); + + *cp = '\0'; + *result = decstring; + return (mpd_ssize_t)(cp-decstring); +} + +char * +mpd_to_sci(const mpd_t *dec, int fmt) +{ + char *res; + int flags = MPD_FMT_TOSCI; + + flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER; + (void)_mpd_to_string(&res, dec, flags, MPD_DEFAULT_DOTPLACE); + return res; +} + +char * +mpd_to_eng(const mpd_t *dec, int fmt) +{ + char *res; + int flags = MPD_FMT_TOENG; + + flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER; + (void)_mpd_to_string(&res, dec, flags, MPD_DEFAULT_DOTPLACE); + return res; +} + +mpd_ssize_t +mpd_to_sci_size(char **res, const mpd_t *dec, int fmt) +{ + int flags = MPD_FMT_TOSCI; + + flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER; + return _mpd_to_string(res, dec, flags, MPD_DEFAULT_DOTPLACE); +} + +mpd_ssize_t +mpd_to_eng_size(char **res, const mpd_t *dec, int fmt) +{ + int flags = MPD_FMT_TOENG; + + flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER; + return _mpd_to_string(res, dec, flags, MPD_DEFAULT_DOTPLACE); +} + +/* Copy a single UTF-8 char to dest. See: The Unicode Standard, version 5.2, + chapter 3.9: Well-formed UTF-8 byte sequences. */ +static int +_mpd_copy_utf8(char dest[5], const char *s) +{ + const uchar *cp = (const uchar *)s; + uchar lb, ub; + int count, i; + + + if (*cp == 0) { + /* empty string */ + dest[0] = '\0'; + return 0; + } + else if (*cp <= 0x7f) { + /* ascii */ + dest[0] = *cp; + dest[1] = '\0'; + return 1; + } + else if (0xc2 <= *cp && *cp <= 0xdf) { + lb = 0x80; ub = 0xbf; + count = 2; + } + else if (*cp == 0xe0) { + lb = 0xa0; ub = 0xbf; + count = 3; + } + else if (*cp <= 0xec) { + lb = 0x80; ub = 0xbf; + count = 3; + } + else if (*cp == 0xed) { + lb = 0x80; ub = 0x9f; + count = 3; + } + else if (*cp <= 0xef) { + lb = 0x80; ub = 0xbf; + count = 3; + } + else if (*cp == 0xf0) { + lb = 0x90; ub = 0xbf; + count = 4; + } + else if (*cp <= 0xf3) { + lb = 0x80; ub = 0xbf; + count = 4; + } + else if (*cp == 0xf4) { + lb = 0x80; ub = 0x8f; + count = 4; + } + else { + /* invalid */ + goto error; + } + + dest[0] = *cp++; + if (*cp < lb || ub < *cp) { + goto error; + } + dest[1] = *cp++; + for (i = 2; i < count; i++) { + if (*cp < 0x80 || 0xbf < *cp) { + goto error; + } + dest[i] = *cp++; + } + dest[i] = '\0'; + + return count; + +error: + dest[0] = '\0'; + return -1; +} + +int +mpd_validate_lconv(mpd_spec_t *spec) +{ + size_t n; +#if CHAR_MAX == SCHAR_MAX + const char *cp = spec->grouping; + while (*cp != '\0') { + if (*cp++ < 0) { + return -1; + } + } +#endif + n = strlen(spec->dot); + if (n == 0 || n > 4) { + return -1; + } + if (strlen(spec->sep) > 4) { + return -1; + } + + return 0; +} + +int +mpd_parse_fmt_str(mpd_spec_t *spec, const char *fmt, int caps) +{ + char *cp = (char *)fmt; + int have_align = 0, n; + + /* defaults */ + spec->min_width = 0; + spec->prec = -1; + spec->type = caps ? 'G' : 'g'; + spec->align = '>'; + spec->sign = '-'; + spec->dot = ""; + spec->sep = ""; + spec->grouping = ""; + + + /* presume that the first character is a UTF-8 fill character */ + if ((n = _mpd_copy_utf8(spec->fill, cp)) < 0) { + return 0; + } + + /* alignment directive, prefixed by a fill character */ + if (*cp && (*(cp+n) == '<' || *(cp+n) == '>' || + *(cp+n) == '=' || *(cp+n) == '^')) { + cp += n; + spec->align = *cp++; + have_align = 1; + } /* alignment directive */ + else { + /* default fill character */ + spec->fill[0] = ' '; + spec->fill[1] = '\0'; + if (*cp == '<' || *cp == '>' || + *cp == '=' || *cp == '^') { + spec->align = *cp++; + have_align = 1; + } + } + + /* sign formatting */ + if (*cp == '+' || *cp == '-' || *cp == ' ') { + spec->sign = *cp++; + } + + /* zero padding */ + if (*cp == '0') { + /* zero padding implies alignment, which should not be + * specified twice. */ + if (have_align) { + return 0; + } + spec->align = 'z'; + spec->fill[0] = *cp++; + spec->fill[1] = '\0'; + } + + /* minimum width */ + if (isdigit((uchar)*cp)) { + if (*cp == '0') { + return 0; + } + errno = 0; + spec->min_width = mpd_strtossize(cp, &cp, 10); + if (errno == ERANGE || errno == EINVAL) { + return 0; + } + } + + /* thousands separator */ + if (*cp == ',') { + spec->dot = "."; + spec->sep = ","; + spec->grouping = "\003\003"; + cp++; + } + + /* fraction digits or significant digits */ + if (*cp == '.') { + cp++; + if (!isdigit((uchar)*cp)) { + return 0; + } + errno = 0; + spec->prec = mpd_strtossize(cp, &cp, 10); + if (errno == ERANGE || errno == EINVAL) { + return 0; + } + } + + /* type */ + if (*cp == 'E' || *cp == 'e' || *cp == 'F' || *cp == 'f' || + *cp == 'G' || *cp == 'g' || *cp == '%') { + spec->type = *cp++; + } + else if (*cp == 'N' || *cp == 'n') { + /* locale specific conversion */ + struct lconv *lc; + /* separator has already been specified */ + if (*spec->sep) { + return 0; + } + spec->type = *cp++; + spec->type = (spec->type == 'N') ? 'G' : 'g'; + lc = localeconv(); + spec->dot = lc->decimal_point; + spec->sep = lc->thousands_sep; + spec->grouping = lc->grouping; + if (mpd_validate_lconv(spec) < 0) { + return 0; + } + } + + /* check correctness */ + if (*cp != '\0') { + return 0; + } + + return 1; +} + +/* + * The following functions assume that spec->min_width <= MPD_MAX_PREC, which + * is made sure in mpd_qformat_spec. Then, even with a spec that inserts a + * four-byte separator after each digit, nbytes in the following struct + * cannot overflow. + */ + +/* Multibyte string */ +typedef struct { + mpd_ssize_t nbytes; /* length in bytes */ + mpd_ssize_t nchars; /* length in chars */ + mpd_ssize_t cur; /* current write index */ + char *data; +} mpd_mbstr_t; + +static inline void +_mpd_bcopy(char *dest, const char *src, mpd_ssize_t n) +{ + while (--n >= 0) { + dest[n] = src[n]; + } +} + +static inline void +_mbstr_copy_char(mpd_mbstr_t *dest, const char *src, mpd_ssize_t n) +{ + dest->nbytes += n; + dest->nchars += (n > 0 ? 1 : 0); + dest->cur -= n; + + if (dest->data != NULL) { + _mpd_bcopy(dest->data+dest->cur, src, n); + } +} + +static inline void +_mbstr_copy_ascii(mpd_mbstr_t *dest, const char *src, mpd_ssize_t n) +{ + dest->nbytes += n; + dest->nchars += n; + dest->cur -= n; + + if (dest->data != NULL) { + _mpd_bcopy(dest->data+dest->cur, src, n); + } +} + +static inline void +_mbstr_copy_pad(mpd_mbstr_t *dest, mpd_ssize_t n) +{ + dest->nbytes += n; + dest->nchars += n; + dest->cur -= n; + + if (dest->data != NULL) { + char *cp = dest->data + dest->cur; + while (--n >= 0) { + cp[n] = '0'; + } + } +} + +/* + * Copy a numeric string to dest->data, adding separators in the integer + * part according to spec->grouping. If leading zero padding is enabled + * and the result is smaller than spec->min_width, continue adding zeros + * and separators until the minimum width is reached. + * + * The final length of dest->data is stored in dest->nbytes. The number + * of UTF-8 characters is stored in dest->nchars. + * + * First run (dest->data == NULL): determine the length of the result + * string and store it in dest->nbytes. + * + * Second run (write to dest->data): data is written in chunks and in + * reverse order, starting with the rest of the numeric string. + */ +static void +_mpd_add_sep_dot(mpd_mbstr_t *dest, + const char *sign, /* location of optional sign */ + const char *src, mpd_ssize_t n_src, /* integer part and length */ + const char *dot, /* location of optional decimal point */ + const char *rest, mpd_ssize_t n_rest, /* remaining part and length */ + const mpd_spec_t *spec) +{ + mpd_ssize_t n_sep, n_sign, consume; + const char *g; + int pad = 0; + + n_sign = sign ? 1 : 0; + n_sep = (mpd_ssize_t)strlen(spec->sep); + /* Initial write index: set to location of '\0' in the output string. + * Irrelevant for the first run. */ + dest->cur = dest->nbytes; + dest->nbytes = dest->nchars = 0; + + _mbstr_copy_ascii(dest, rest, n_rest); + + if (dot) { + _mbstr_copy_char(dest, dot, (mpd_ssize_t)strlen(dot)); + } + + g = spec->grouping; + consume = *g; + while (1) { + /* If the group length is 0 or CHAR_MAX or greater than the + * number of source bytes, consume all remaining bytes. */ + if (*g == 0 || *g == CHAR_MAX || consume > n_src) { + consume = n_src; + } + n_src -= consume; + if (pad) { + _mbstr_copy_pad(dest, consume); + } + else { + _mbstr_copy_ascii(dest, src+n_src, consume); + } + + if (n_src == 0) { + /* Either the real source of intpart digits or the virtual + * source of padding zeros is exhausted. */ + if (spec->align == 'z' && + dest->nchars + n_sign < spec->min_width) { + /* Zero padding is set and length < min_width: + * Generate n_src additional characters. */ + n_src = spec->min_width - (dest->nchars + n_sign); + /* Next iteration: + * case *g == 0 || *g == CHAR_MAX: + * consume all padding characters + * case consume < g*: + * fill remainder of current group + * case consume == g* + * copying is a no-op */ + consume = *g - consume; + /* Switch on virtual source of zeros. */ + pad = 1; + continue; + } + break; + } + + if (n_sep > 0) { + /* If padding is switched on, separators are counted + * as padding characters. This rule does not apply if + * the separator would be the first character of the + * result string. */ + if (pad && n_src > 1) n_src -= 1; + _mbstr_copy_char(dest, spec->sep, n_sep); + } + + /* If non-NUL, use the next value for grouping. */ + if (*g && *(g+1)) g++; + consume = *g; + } + + if (sign) { + _mbstr_copy_ascii(dest, sign, 1); + } + + if (dest->data) { + dest->data[dest->nbytes] = '\0'; + } +} + +/* + * Convert a numeric-string to its locale-specific appearance. + * The string must have one of these forms: + * + * 1) [sign] digits [exponent-part] + * 2) [sign] digits '.' [digits] [exponent-part] + * + * Not allowed, since _mpd_to_string() never returns this form: + * + * 3) [sign] '.' digits [exponent-part] + * + * Input: result->data := original numeric string (ASCII) + * result->bytes := strlen(result->data) + * result->nchars := strlen(result->data) + * + * Output: result->data := modified or original string + * result->bytes := strlen(result->data) + * result->nchars := number of characters (possibly UTF-8) + */ +static int +_mpd_apply_lconv(mpd_mbstr_t *result, const mpd_spec_t *spec, uint32_t *status) +{ + const char *sign = NULL, *intpart = NULL, *dot = NULL; + const char *rest, *dp; + char *decstring; + mpd_ssize_t n_int, n_rest; + + /* original numeric string */ + dp = result->data; + + /* sign */ + if (*dp == '+' || *dp == '-' || *dp == ' ') { + sign = dp++; + } + /* integer part */ + assert(isdigit((uchar)*dp)); + intpart = dp++; + while (isdigit((uchar)*dp)) { + dp++; + } + n_int = (mpd_ssize_t)(dp-intpart); + /* decimal point */ + if (*dp == '.') { + dp++; dot = spec->dot; + } + /* rest */ + rest = dp; + n_rest = result->nbytes - (mpd_ssize_t)(dp-result->data); + + if (dot == NULL && (*spec->sep == '\0' || *spec->grouping == '\0')) { + /* _mpd_add_sep_dot() would not change anything */ + return 1; + } + + /* Determine the size of the new decimal string after inserting the + * decimal point, optional separators and optional padding. */ + decstring = result->data; + result->data = NULL; + _mpd_add_sep_dot(result, sign, intpart, n_int, dot, + rest, n_rest, spec); + + result->data = mpd_alloc(result->nbytes+1, 1); + if (result->data == NULL) { + *status |= MPD_Malloc_error; + mpd_free(decstring); + return 0; + } + + /* Perform actual writes. */ + _mpd_add_sep_dot(result, sign, intpart, n_int, dot, + rest, n_rest, spec); + + mpd_free(decstring); + return 1; +} + +/* Add padding to the formatted string if necessary. */ +static int +_mpd_add_pad(mpd_mbstr_t *result, const mpd_spec_t *spec, uint32_t *status) +{ + if (result->nchars < spec->min_width) { + mpd_ssize_t add_chars, add_bytes; + size_t lpad = 0, rpad = 0; + size_t n_fill, len, i, j; + char align = spec->align; + uint8_t err = 0; + char *cp; + + n_fill = strlen(spec->fill); + add_chars = (spec->min_width - result->nchars); + /* max value: MPD_MAX_PREC * 4 */ + add_bytes = add_chars * (mpd_ssize_t)n_fill; + + cp = result->data = mpd_realloc(result->data, + result->nbytes+add_bytes+1, + sizeof *result->data, &err); + if (err) { + *status |= MPD_Malloc_error; + mpd_free(result->data); + return 0; + } + + if (align == 'z') { + align = '='; + } + + if (align == '<') { + rpad = add_chars; + } + else if (align == '>' || align == '=') { + lpad = add_chars; + } + else { /* align == '^' */ + lpad = add_chars/2; + rpad = add_chars-lpad; + } + + len = result->nbytes; + if (align == '=' && (*cp == '-' || *cp == '+' || *cp == ' ')) { + /* leave sign in the leading position */ + cp++; len--; + } + + memmove(cp+n_fill*lpad, cp, len); + for (i = 0; i < lpad; i++) { + for (j = 0; j < n_fill; j++) { + cp[i*n_fill+j] = spec->fill[j]; + } + } + cp += (n_fill*lpad + len); + for (i = 0; i < rpad; i++) { + for (j = 0; j < n_fill; j++) { + cp[i*n_fill+j] = spec->fill[j]; + } + } + + result->nbytes += add_bytes; + result->nchars += add_chars; + result->data[result->nbytes] = '\0'; + } + + return 1; +} + +/* Round a number to prec digits. The adjusted exponent stays the same + or increases by one if rounding up crosses a power of ten boundary. + If result->digits would exceed MPD_MAX_PREC+1, MPD_Invalid_operation + is set and the result is NaN. */ +static inline void +_mpd_round(mpd_t *result, const mpd_t *a, mpd_ssize_t prec, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_ssize_t exp = a->exp + a->digits - prec; + + if (prec <= 0) { + mpd_seterror(result, MPD_Invalid_operation, status); /* GCOV_NOT_REACHED */ + return; /* GCOV_NOT_REACHED */ + } + if (mpd_isspecial(a) || mpd_iszero(a)) { + mpd_qcopy(result, a, status); /* GCOV_NOT_REACHED */ + return; /* GCOV_NOT_REACHED */ + } + + mpd_qrescale_fmt(result, a, exp, ctx, status); + if (result->digits > prec) { + mpd_qrescale_fmt(result, result, exp+1, ctx, status); + } +} + +/* + * Return the string representation of an mpd_t, formatted according to 'spec'. + * The format specification is assumed to be valid. Memory errors are indicated + * as usual. This function is quiet. + */ +char * +mpd_qformat_spec(const mpd_t *dec, const mpd_spec_t *spec, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_uint_t dt[MPD_MINALLOC_MAX]; + mpd_t tmp = {MPD_STATIC|MPD_STATIC_DATA,0,0,0,MPD_MINALLOC_MAX,dt}; + mpd_ssize_t dplace = MPD_DEFAULT_DOTPLACE; + mpd_mbstr_t result; + mpd_spec_t stackspec; + char type = spec->type; + int flags = 0; + + + if (spec->min_width > MPD_MAX_PREC) { + *status |= MPD_Invalid_operation; + return NULL; + } + + if (isupper((uchar)type)) { + type = tolower((uchar)type); + flags |= MPD_FMT_UPPER; + } + if (spec->sign == ' ') { + flags |= MPD_FMT_SIGN_SPACE; + } + else if (spec->sign == '+') { + flags |= MPD_FMT_SIGN_PLUS; + } + + if (mpd_isspecial(dec)) { + if (spec->align == 'z') { + stackspec = *spec; + stackspec.fill[0] = ' '; + stackspec.fill[1] = '\0'; + stackspec.align = '>'; + spec = &stackspec; + } + } + else { + uint32_t workstatus = 0; + mpd_ssize_t prec; + + switch (type) { + case 'g': flags |= MPD_FMT_TOSCI; break; + case 'e': flags |= MPD_FMT_EXP; break; + case '%': flags |= MPD_FMT_PERCENT; + if (!mpd_qcopy(&tmp, dec, status)) { + return NULL; + } + tmp.exp += 2; + dec = &tmp; + type = 'f'; /* fall through */ + case 'f': flags |= MPD_FMT_FIXED; break; + default: abort(); /* debug: GCOV_NOT_REACHED */ + } + + if (spec->prec >= 0) { + if (spec->prec > MPD_MAX_PREC) { + *status |= MPD_Invalid_operation; + goto error; + } + + switch (type) { + case 'g': + prec = (spec->prec == 0) ? 1 : spec->prec; + if (dec->digits > prec) { + _mpd_round(&tmp, dec, prec, ctx, + &workstatus); + dec = &tmp; + } + break; + case 'e': + if (mpd_iszero(dec)) { + dplace = 1-spec->prec; + } + else { + _mpd_round(&tmp, dec, spec->prec+1, ctx, + &workstatus); + dec = &tmp; + } + break; + case 'f': + mpd_qrescale(&tmp, dec, -spec->prec, ctx, + &workstatus); + dec = &tmp; + break; + } + } + + if (type == 'f') { + if (mpd_iszero(dec) && dec->exp > 0) { + mpd_qrescale(&tmp, dec, 0, ctx, &workstatus); + dec = &tmp; + } + } + + if (workstatus&MPD_Errors) { + *status |= (workstatus&MPD_Errors); + goto error; + } + } + + /* + * At this point, for all scaled or non-scaled decimals: + * 1) 1 <= digits <= MAX_PREC+1 + * 2) adjexp(scaled) = adjexp(orig) [+1] + * 3) case 'g': MIN_ETINY <= exp <= MAX_EMAX+1 + * case 'e': MIN_ETINY-MAX_PREC <= exp <= MAX_EMAX+1 + * case 'f': MIN_ETINY <= exp <= MAX_EMAX+1 + * 4) max memory alloc in _mpd_to_string: + * case 'g': MAX_PREC+36 + * case 'e': MAX_PREC+36 + * case 'f': 2*MPD_MAX_PREC+30 + */ + result.nbytes = _mpd_to_string(&result.data, dec, flags, dplace); + result.nchars = result.nbytes; + if (result.nbytes < 0) { + *status |= MPD_Malloc_error; + goto error; + } + + if (*spec->dot != '\0' && !mpd_isspecial(dec)) { + if (result.nchars > MPD_MAX_PREC+36) { + /* Since a group length of one is not explicitly + * disallowed, ensure that it is always possible to + * insert a four byte separator after each digit. */ + *status |= MPD_Invalid_operation; + mpd_free(result.data); + goto error; + } + if (!_mpd_apply_lconv(&result, spec, status)) { + goto error; + } + } + + if (spec->min_width) { + if (!_mpd_add_pad(&result, spec, status)) { + goto error; + } + } + + mpd_del(&tmp); + return result.data; + +error: + mpd_del(&tmp); + return NULL; +} + +char * +mpd_qformat(const mpd_t *dec, const char *fmt, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_spec_t spec; + + if (!mpd_parse_fmt_str(&spec, fmt, 1)) { + *status |= MPD_Invalid_operation; + return NULL; + } + + return mpd_qformat_spec(dec, &spec, ctx, status); +} + +/* + * The specification has a *condition* called Invalid_operation and an + * IEEE *signal* called Invalid_operation. The former corresponds to + * MPD_Invalid_operation, the latter to MPD_IEEE_Invalid_operation. + * MPD_IEEE_Invalid_operation comprises the following conditions: + * + * [MPD_Conversion_syntax, MPD_Division_impossible, MPD_Division_undefined, + * MPD_Fpu_error, MPD_Invalid_context, MPD_Invalid_operation, + * MPD_Malloc_error] + * + * In the following functions, 'flag' denotes the condition, 'signal' + * denotes the IEEE signal. + */ + +static const char *mpd_flag_string[MPD_NUM_FLAGS] = { + "Clamped", + "Conversion_syntax", + "Division_by_zero", + "Division_impossible", + "Division_undefined", + "Fpu_error", + "Inexact", + "Invalid_context", + "Invalid_operation", + "Malloc_error", + "Not_implemented", + "Overflow", + "Rounded", + "Subnormal", + "Underflow", +}; + +static const char *mpd_signal_string[MPD_NUM_FLAGS] = { + "Clamped", + "IEEE_Invalid_operation", + "Division_by_zero", + "IEEE_Invalid_operation", + "IEEE_Invalid_operation", + "IEEE_Invalid_operation", + "Inexact", + "IEEE_Invalid_operation", + "IEEE_Invalid_operation", + "IEEE_Invalid_operation", + "Not_implemented", + "Overflow", + "Rounded", + "Subnormal", + "Underflow", +}; + +/* print conditions to buffer, separated by spaces */ +int +mpd_snprint_flags(char *dest, int nmemb, uint32_t flags) +{ + char *cp; + int n, j; + + assert(nmemb >= MPD_MAX_FLAG_STRING); + + *dest = '\0'; cp = dest; + for (j = 0; j < MPD_NUM_FLAGS; j++) { + if (flags & (1U<= 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 207408428242 -r 40917e4b51aa Modules/_decimal/io.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/io.h Thu Feb 23 16:43:15 2012 +0100 @@ -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 207408428242 -r 40917e4b51aa Modules/_decimal/literature/README.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/literature/README.txt Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,51 @@ + + +This document contains links to the literature used in the process of +creating the library. The list is probably not complete. + + +Mike Cowlishaw: General Decimal Arithmetic Specification +http://speleotrove.com/decimal/decarith.html + + +Jean-Michel Muller: On the definition of ulp (x) +lara.inist.fr/bitstream/2332/518/1/LIP-RR2005-09.pdf + + +T. E. Hull, A. Abrham: Properly rounded variable precision square root +http://portal.acm.org/citation.cfm?id=214413 + + +T. E. Hull, A. Abrham: Variable precision exponential function +http://portal.acm.org/citation.cfm?id=6498 + + +Roman E. Maeder: Storage allocation for the Karatsuba integer multiplication +algorithm. http://www.springerlink.com/content/w15058mj6v59t565/ + + +J. M. Pollard: The fast Fourier transform in a finite field +http://www.ams.org/journals/mcom/1971-25-114/S0025-5718-1971-0301966-0/home.html + + +David H. Bailey: FFTs in External or Hierarchical Memory +http://crd.lbl.gov/~dhbailey/dhbpapers/ + + +W. Morven Gentleman: Matrix Multiplication and Fast Fourier Transforms +http://www.alcatel-lucent.com/bstj/vol47-1968/articles/bstj47-6-1099.pdf + + +Mikko Tommila: Apfloat documentation +http://www.apfloat.org/apfloat/2.41/apfloat.pdf + + +Joerg Arndt: "Matters Computational" +http://www.jjj.de/fxt/ + + +Karl Hasselstrom: Fast Division of Large Integers +www.treskal.com/kalle/exjobb/original-report.pdf + + + diff -r 207408428242 -r 40917e4b51aa Modules/_decimal/literature/bignum.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/literature/bignum.txt Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,83 @@ + + +Bignum support (Fast Number Theoretic Transform or FNT): +======================================================== + +Bignum arithmetic in libmpdec uses the scheme for fast convolution +of integer sequences from: + +J. M. Pollard: The fast Fourier transform in a finite field +http://www.ams.org/journals/mcom/1971-25-114/S0025-5718-1971-0301966-0/home.html + + +The transform in a finite field can be used for convolution in the same +way as the Fourier Transform. The main advantages of the Number Theoretic +Transform are that it is both exact and very memory efficient. + + +Convolution in pseudo-code: +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + fnt_convolute(a, b): + x = fnt(a) # forward transform of a + y = fnt(b) # forward transform of b + z = pairwise multiply x[i] and y[i] + result = inv_fnt(z) # backward transform of z. + + +Extending the maximum transform length (Chinese Remainder Theorem): +------------------------------------------------------------------- + +The maximum transform length is quite limited when using a single +prime field. However, it is possible to use multiple primes and +recover the result using the Chinese Remainder Theorem. + + +Multiplication in pseudo-code: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + _mpd_fntmul(u, v): + c1 = fnt_convolute(u, v, P1) # convolute modulo prime1 + c2 = fnt_convolute(u, v, P2) # convolute modulo prime2 + c3 = fnt_convolute(u, v, P3) # convolute modulo prime3 + result = crt3(c1, c2, c3) # Chinese Remainder Theorem + + +Optimized transform functions: +------------------------------ + +There are three different fnt() functions: + + std_fnt: "standard" decimation in frequency transform for array lengths + of 2**n. Performs well up to 1024 words. + + sixstep: Cache-friendly algorithm for array lengths of 2**n. Outperforms + std_fnt for large arrays. + + fourstep: Algorithm for array lengths of 3 * 2**n. Also cache friendly + in large parts. + + +List of bignum-only files: +-------------------------- + +Functions from these files are only used in _mpd_fntmul(). + + umodarith.h -> fast low level routines for unsigned modular arithmetic + numbertheory.c -> routines for setting up the FNT + difradix2.c -> decimation in frequency transform, used as the + "base case" by the following three files: + + fnt.c -> standard transform for smaller arrays + sixstep.c -> transform large arrays of length 2**n + fourstep.c -> transform arrays of length 3 * 2**n + + convolute.c -> do the actual fast convolution, using one of + the three transform functions. + transpose.c -> transpositions needed for the sixstep algorithm. + crt.c -> Chinese Remainder Theorem: use information from three + transforms modulo three different primes to get the + final result. + + + diff -r 207408428242 -r 40917e4b51aa Modules/_decimal/literature/fnt.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/literature/fnt.py Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,212 @@ +# +# 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. +# + + +###################################################################### +# This file lists and checks some of the constants and limits used # +# in libmpdec's Number Theoretic Transform. At the end of the file # +# there is an example function for the plain DFT transform. # +###################################################################### + + +# +# Number theoretic transforms are done in subfields of F(p). P[i] +# are the primes, D[i] = P[i] - 1 are highly composite and w[i] +# are the respective primitive roots of F(p). +# +# The strategy is to convolute two coefficients modulo all three +# primes, then use the Chinese Remainder Theorem on the three +# result arrays to recover the result in the usual base RADIX +# form. +# + +# ====================================================================== +# Primitive roots +# ====================================================================== + +# +# Verify primitive roots: +# +# For a prime field, r is a primitive root if and only if for all prime +# factors f of p-1, r**((p-1)/f) =/= 1 (mod p). +# +def prod(F, E): + """Check that the factorization of P-1 is correct. F is the list of + factors of P-1, E lists the number of occurrences of each factor.""" + x = 1 + for y, z in zip(F, E): + x *= y**z + return x + +def is_primitive_root(r, p, factors, exponents): + """Check if r is a primitive root of F(p).""" + if p != prod(factors, exponents) + 1: + return False + for f in factors: + q, control = divmod(p-1, f) + if control != 0: + return False + if pow(r, q, p) == 1: + return False + return True + + +# ================================================================= +# Constants and limits for the 64-bit version +# ================================================================= + +RADIX = 10**19 + +# Primes P1, P2 and P3: +P = [2**64-2**32+1, 2**64-2**34+1, 2**64-2**40+1] + +# P-1, highly composite. The transform length d is variable and +# must divide D = P-1. Since all D are divisible by 3 * 2**32, +# transform lengths can be 2**n or 3 * 2**n (where n <= 32). +D = [2**32 * 3 * (5 * 17 * 257 * 65537), + 2**34 * 3**2 * (7 * 11 * 31 * 151 * 331), + 2**40 * 3**2 * (5 * 7 * 13 * 17 * 241)] + +# Prime factors of P-1 and their exponents: +F = [(2,3,5,17,257,65537), (2,3,7,11,31,151,331), (2,3,5,7,13,17,241)] +E = [(32,1,1,1,1,1), (34,2,1,1,1,1,1), (40,2,1,1,1,1,1)] + +# Maximum transform length for 2**n. Above that only 3 * 2**31 +# or 3 * 2**32 are possible. +MPD_MAXTRANSFORM_2N = 2**32 + + +# Limits in the terminology of Pollard's paper: +m2 = (MPD_MAXTRANSFORM_2N * 3) // 2 # Maximum length of the smaller array. +M1 = M2 = RADIX-1 # Maximum value per single word. +L = m2 * M1 * M2 +P[0] * P[1] * P[2] > 2 * L + + +# Primitive roots of F(P1), F(P2) and F(P3): +w = [7, 10, 19] + +# The primitive roots are correct: +for i in range(3): + if not is_primitive_root(w[i], P[i], F[i], E[i]): + print("FAIL") + + +# ================================================================= +# Constants and limits for the 32-bit version +# ================================================================= + +RADIX = 10**9 + +# Primes P1, P2 and P3: +P = [2113929217, 2013265921, 1811939329] + +# P-1, highly composite. All D = P-1 are divisible by 3 * 2**25, +# allowing for transform lengths up to 3 * 2**25 words. +D = [2**25 * 3**2 * 7, + 2**27 * 3 * 5, + 2**26 * 3**3] + +# Prime factors of P-1 and their exponents: +F = [(2,3,7), (2,3,5), (2,3)] +E = [(25,2,1), (27,1,1), (26,3)] + +# Maximum transform length for 2**n. Above that only 3 * 2**24 or +# 3 * 2**25 are possible. +MPD_MAXTRANSFORM_2N = 2**25 + + +# Limits in the terminology of Pollard's paper: +m2 = (MPD_MAXTRANSFORM_2N * 3) // 2 # Maximum length of the smaller array. +M1 = M2 = RADIX-1 # Maximum value per single word. +L = m2 * M1 * M2 +P[0] * P[1] * P[2] > 2 * L + + +# Primitive roots of F(P1), F(P2) and F(P3): +w = [5, 31, 13] + +# The primitive roots are correct: +for i in range(3): + if not is_primitive_root(w[i], P[i], F[i], E[i]): + print("FAIL") + + +# ====================================================================== +# Example transform using a single prime +# ====================================================================== + +def ntt(lst, dir): + """Perform a transform on the elements of lst. len(lst) must + be 2**n or 3 * 2**n, where n <= 25. This is the slow DFT.""" + p = 2113929217 # prime + d = len(lst) # transform length + d_prime = pow(d, (p-2), p) # inverse of d + xi = (p-1)//d + w = 5 # primitive root of F(p) + r = pow(w, xi, p) # primitive root of the subfield + r_prime = pow(w, (p-1-xi), p) # inverse of r + if dir == 1: # forward transform + a = lst # input array + A = [0] * d # transformed values + for i in range(d): + s = 0 + for j in range(d): + s += a[j] * pow(r, i*j, p) + A[i] = s % p + return A + elif dir == -1: # backward transform + A = lst # input array + a = [0] * d # transformed values + for j in range(d): + s = 0 + for i in range(d): + s += A[i] * pow(r_prime, i*j, p) + a[j] = (d_prime * s) % p + return a + +def ntt_convolute(a, b): + """convolute arrays a and b.""" + assert(len(a) == len(b)) + x = ntt(a, 1) + y = ntt(b, 1) + for i in range(len(a)): + y[i] = y[i] * x[i] + r = ntt(y, -1) + return r + + +# Example: Two arrays representing 21 and 81 in little-endian: +a = [1, 2, 0, 0] +b = [1, 8, 0, 0] + +assert(ntt_convolute(a, b) == [1, 10, 16, 0]) +assert(21 * 81 == (1*10**0 + 10*10**1 + 16*10**2 + 0*10**3)) + + + + diff -r 207408428242 -r 40917e4b51aa Modules/_decimal/literature/matrix-transform.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/literature/matrix-transform.txt Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,256 @@ + + +(* Copyright (c) 2011 Stefan Krah. All rights reserved. *) + + +The Matrix Fourier Transform: +============================= + +In libmpdec, the Matrix Fourier Transform [1] is called four-step transform +after a variant that appears in [2]. The algorithm requires that the input +array can be viewed as an R*C matrix. + +All operations are done modulo p. For readability, the proofs drop all +instances of (mod p). + + +Algorithm four-step (forward transform): +---------------------------------------- + + a := input array + d := len(a) = R * C + p := prime + w := primitive root of unity of the prime field + r := w**((p-1)/d) + A := output array + + 1) Apply a length R FNT to each column. + + 2) Multiply each matrix element (addressed by j*C+m) by r**(j*m). + + 3) Apply a length C FNT to each row. + + 4) Transpose the matrix. + + +Proof (forward transform): +-------------------------- + + The algorithm can be derived starting from the regular definition of + the finite-field transform of length d: + + d-1 + ,---- + \ + A[k] = | a[l] * r**(k * l) + / + `---- + l = 0 + + + The sum can be rearranged into the sum of the sums of columns: + + C-1 R-1 + ,---- ,---- + \ \ + = | | a[i * C + j] * r**(k * (i * C + j)) + / / + `---- `---- + j = 0 i = 0 + + + Extracting a constant from the inner sum: + + C-1 R-1 + ,---- ,---- + \ \ + = | r**k*j * | a[i * C + j] * r**(k * i * C) + / / + `---- `---- + j = 0 i = 0 + + + Without any loss of generality, let k = n * R + m, + where n < C and m < R: + + C-1 R-1 + ,---- ,---- + \ \ + A[n*R+m] = | r**(R*n*j) * r**(m*j) * | a[i*C+j] * r**(R*C*n*i) * r**(C*m*i) + / / + `---- `---- + j = 0 i = 0 + + + Since r = w ** ((p-1) / (R*C)): + + a) r**(R*C*n*i) = w**((p-1)*n*i) = 1 + + b) r**(C*m*i) = w**((p-1) / R) ** (m*i) = r_R ** (m*i) + + c) r**(R*n*j) = w**((p-1) / C) ** (n*j) = r_C ** (n*j) + + r_R := root of the subfield of length R. + r_C := root of the subfield of length C. + + + C-1 R-1 + ,---- ,---- + \ \ + A[n*R+m] = | r_C**(n*j) * [ r**(m*j) * | a[i*C+j] * r_R**(m*i) ] + / ^ / + `---- | `---- 1) transform the columns + j = 0 | i = 0 + ^ | + | `-- 2) multiply + | + `-- 3) transform the rows + + + Note that the entire RHS is a function of n and m and that the results + for each pair (n, m) are stored in Fortran order. + + Let the term in square brackets be f(m, j). Step 1) and 2) precalculate + the term for all (m, j). After that, the original matrix is now a lookup + table with the mth element in the jth column at location m * C + j. + + Let the complete RHS be g(m, n). Step 3) does an in-place transform of + length n on all rows. After that, the original matrix is now a lookup + table with the mth element in the nth column at location m * C + n. + + But each (m, n) pair should be written to location n * R + m. Therefore, + step 4) transposes the result of step 3). + + + +Algorithm four-step (inverse transform): +---------------------------------------- + + A := input array + d := len(A) = R * C + p := prime + d' := d**(p-2) # inverse of d + w := primitive root of unity of the prime field + r := w**((p-1)/d) # root of the subfield + r' := w**((p-1) - (p-1)/d) # inverse of r + a := output array + + 0) View the matrix as a C*R matrix. + + 1) Transpose the matrix, producing an R*C matrix. + + 2) Apply a length C FNT to each row. + + 3) Multiply each matrix element (addressed by i*C+n) by r**(i*n). + + 4) Apply a length R FNT to each column. + + +Proof (inverse transform): +-------------------------- + + The algorithm can be derived starting from the regular definition of + the finite-field inverse transform of length d: + + d-1 + ,---- + \ + a[k] = d' * | A[l] * r' ** (k * l) + / + `---- + l = 0 + + + The sum can be rearranged into the sum of the sums of columns. Note + that at this stage we still have a C*R matrix, so C denotes the number + of rows: + + R-1 C-1 + ,---- ,---- + \ \ + = d' * | | a[j * R + i] * r' ** (k * (j * R + i)) + / / + `---- `---- + i = 0 j = 0 + + + Extracting a constant from the inner sum: + + R-1 C-1 + ,---- ,---- + \ \ + = d' * | r' ** (k*i) * | a[j * R + i] * r' ** (k * j * R) + / / + `---- `---- + i = 0 j = 0 + + + Without any loss of generality, let k = m * C + n, + where m < R and n < C: + + R-1 C-1 + ,---- ,---- + \ \ + A[m*C+n] = d' * | r' ** (C*m*i) * r' ** (n*i) * | a[j*R+i] * r' ** (R*C*m*j) * r' ** (R*n*j) + / / + `---- `---- + i = 0 j = 0 + + + Since r' = w**((p-1) - (p-1)/d) and d = R*C: + + a) r' ** (R*C*m*j) = w**((p-1)*R*C*m*j - (p-1)*m*j) = 1 + + b) r' ** (C*m*i) = w**((p-1)*C - (p-1)/R) ** (m*i) = r_R' ** (m*i) + + c) r' ** (R*n*j) = r_C' ** (n*j) + + d) d' = d**(p-2) = (R*C) ** (p-2) = R**(p-2) * C**(p-2) = R' * C' + + r_R' := inverse of the root of the subfield of length R. + r_C' := inverse of the root of the subfield of length C. + R' := inverse of R + C' := inverse of C + + + R-1 C-1 + ,---- ,---- 2) transform the rows of a^T + \ \ + A[m*C+n] = R' * | r_R' ** (m*i) * [ r' ** (n*i) * C' * | a[j*R+i] * r_C' ** (n*j) ] + / ^ / ^ + `---- | `---- | + i = 0 | j = 0 | + ^ | `-- 1) Transpose input matrix + | `-- 3) multiply to address elements by + | i * C + j + `-- 3) transform the columns + + + + Note that the entire RHS is a function of m and n and that the results + for each pair (m, n) are stored in C order. + + Let the term in square brackets be f(n, i). Without step 1), the sum + would perform a length C transform on the columns of the input matrix. + This is a) inefficient and b) the results are needed in C order, so + step 1) exchanges rows and columns. + + Step 2) and 3) precalculate f(n, i) for all (n, i). After that, the + original matrix is now a lookup table with the ith element in the nth + column at location i * C + n. + + Let the complete RHS be g(m, n). Step 4) does an in-place transform of + length m on all columns. After that, the original matrix is now a lookup + table with the mth element in the nth column at location m * C + n, + which means that all A[k] = A[m * C + n] are in the correct order. + + +-- + + [1] Joerg Arndt: "Matters Computational" + http://www.jjj.de/fxt/ + [2] David H. Bailey: FFTs in External or Hierarchical Memory + http://crd.lbl.gov/~dhbailey/dhbpapers/ + + + diff -r 207408428242 -r 40917e4b51aa Modules/_decimal/literature/mulmod-64.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/literature/mulmod-64.txt Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,127 @@ + + +(* Copyright (c) 2011 Stefan Krah. All rights reserved. *) + + +========================================================================== + Calculate (a * b) % p using special primes +========================================================================== + +A description of the algorithm can be found in the apfloat manual by +Tommila [1]. + + +Definitions: +------------ + +In the whole document, "==" stands for "is congruent with". + +Result of a * b in terms of high/low words: + + (1) hi * 2**64 + lo = a * b + +Special primes: + + (2) p = 2**64 - z + 1, where z = 2**n + +Single step modular reduction: + + (3) R(hi, lo) = hi * z - hi + lo + + +Strategy: +--------- + + a) Set (hi, lo) to the result of a * b. + + b) Set (hi', lo') to the result of R(hi, lo). + + c) Repeat step b) until 0 <= hi' * 2**64 + lo' < 2*p. + + d) If the result is less than p, return lo'. Otherwise return lo' - p. + + +The reduction step b) preserves congruence: +------------------------------------------- + + hi * 2**64 + lo == hi * z - hi + lo (mod p) + + Proof: + ~~~~~~ + + hi * 2**64 + lo = (2**64 - z + 1) * hi + z * hi - hi + lo + + = p * hi + z * hi - hi + lo + + == z * hi - hi + lo (mod p) + + +Maximum numbers of step b): +--------------------------- + +# To avoid unneccessary formalism, define: + +def R(hi, lo, z): + return divmod(hi * z - hi + lo, 2**64) + +# For simplicity, assume hi=2**64-1, lo=2**64-1 after the +# initial multiplication a * b. This is of course impossible +# but certainly covers all cases. + +# Then, for p1: +hi=2**64-1; lo=2**64-1; z=2**32 +p1 = 2**64 - z + 1 + +hi, lo = R(hi, lo, z) # First reduction +hi, lo = R(hi, lo, z) # Second reduction +hi * 2**64 + lo < 2 * p1 # True + +# For p2: +hi=2**64-1; lo=2**64-1; z=2**34 +p2 = 2**64 - z + 1 + +hi, lo = R(hi, lo, z) # First reduction +hi, lo = R(hi, lo, z) # Second reduction +hi, lo = R(hi, lo, z) # Third reduction +hi * 2**64 + lo < 2 * p2 # True + +# For p3: +hi=2**64-1; lo=2**64-1; z=2**40 +p3 = 2**64 - z + 1 + +hi, lo = R(hi, lo, z) # First reduction +hi, lo = R(hi, lo, z) # Second reduction +hi, lo = R(hi, lo, z) # Third reduction +hi * 2**64 + lo < 2 * p3 # True + + +Step d) preserves congruence and yields a result < p: +----------------------------------------------------- + + Case hi = 0: + + Case lo < p: trivial. + + Case lo >= p: + + lo == lo - p (mod p) # result is congruent + + p <= lo < 2*p -> 0 <= lo - p < p # result is in the correct range + + Case hi = 1: + + p < 2**64 /\ 2**64 + lo < 2*p -> lo < p # lo is always less than p + + 2**64 + lo == 2**64 + (lo - p) (mod p) # result is congruent + + = lo - p # exactly the same value as the previous RHS + # in uint64_t arithmetic. + + p < 2**64 + lo < 2*p -> 0 < 2**64 + (lo - p) < p # correct range + + + +[1] http://www.apfloat.org/apfloat/2.40/apfloat.pdf + + + diff -r 207408428242 -r 40917e4b51aa Modules/_decimal/literature/mulmod-ppro.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/literature/mulmod-ppro.txt Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,269 @@ + + +(* Copyright (c) 2011 Stefan Krah. All rights reserved. *) + + +======================================================================== + Calculate (a * b) % p using the 80-bit x87 FPU +======================================================================== + +A description of the algorithm can be found in the apfloat manual by +Tommila [1]. + +The proof follows an argument made by Granlund/Montgomery in [2]. + + +Definitions and assumptions: +---------------------------- + +The 80-bit extended precision format uses 64 bits for the significand: + + (1) F = 64 + +The modulus is prime and less than 2**31: + + (2) 2 <= p < 2**31 + +The factors are less than p: + + (3) 0 <= a < p + (4) 0 <= b < p + +The product a * b is less than 2**62 and is thus exact in 64 bits: + + (5) n = a * b + +The product can be represented in terms of quotient and remainder: + + (6) n = q * p + r + +Using (3), (4) and the fact that p is prime, the remainder is always +greater than zero: + + (7) 0 <= q < p /\ 1 <= r < p + + +Strategy: +--------- + +Precalculate the 80-bit long double inverse of p, with a maximum +relative error of 2**(1-F): + + (8) pinv = (long double)1.0 / p + +Calculate an estimate for q = floor(n/p). The multiplication has another +maximum relative error of 2**(1-F): + + (9) qest = n * pinv + +If we can show that q < qest < q+1, then trunc(qest) = q. It is then +easy to recover the remainder r. The complete algorithm is: + + a) Set the control word to 64-bit precision and truncation mode. + + b) n = a * b # Calculate exact product. + + c) qest = n * pinv # Calculate estimate for the quotient. + + d) q = (qest+2**63)-2**63 # Truncate qest to the exact quotient. + + f) r = n - q * p # Calculate remainder. + + +Proof for q < qest < q+1: +------------------------- + +Using the cumulative error, the error bounds for qest are: + + n n * (1 + 2**(1-F))**2 + (9) --------------------- <= qest <= --------------------- + p * (1 + 2**(1-F))**2 p + + + Lemma 1: + -------- + n q * p + r + (10) q < --------------------- = --------------------- + p * (1 + 2**(1-F))**2 p * (1 + 2**(1-F))**2 + + + Proof: + ~~~~~~ + + (I) q * p * (1 + 2**(1-F))**2 < q * p + r + + (II) q * p * 2**(2-F) + q * p * 2**(2-2*F) < r + + Using (1) and (7), it is sufficient to show that: + + (III) q * p * 2**(-62) + q * p * 2**(-126) < 1 <= r + + (III) can easily be verified by substituting the largest possible + values p = 2**31-1 and q = 2**31-2. + + The critical cases occur when r = 1, n = m * p + 1. These cases + can be exhaustively verified with a test program. + + + Lemma 2: + -------- + + n * (1 + 2**(1-F))**2 (q * p + r) * (1 + 2**(1-F))**2 + (11) --------------------- = ------------------------------- < q + 1 + p p + + Proof: + ~~~~~~ + + (I) (q * p + r) + (q * p + r) * 2**(2-F) + (q * p + r) * 2**(2-2*F) < q * p + p + + (II) (q * p + r) * 2**(2-F) + (q * p + r) * 2**(2-2*F) < p - r + + Using (1) and (7), it is sufficient to show that: + + (III) (q * p + r) * 2**(-62) + (q * p + r) * 2**(-126) < 1 <= p - r + + (III) can easily be verified by substituting the largest possible + values p = 2**31-1, q = 2**31-2 and r = 2**31-2. + + The critical cases occur when r = (p - 1), n = m * p - 1. These cases + can be exhaustively verified with a test program. + + +[1] http://www.apfloat.org/apfloat/2.40/apfloat.pdf +[2] http://gmplib.org/~tege/divcnst-pldi94.pdf + [Section 7: "Use of floating point"] + + + +(* Coq proof for (10) and (11) *) + +Require Import ZArith. +Require Import QArith. +Require Import Qpower. +Require Import Qabs. +Require Import Psatz. + +Open Scope Q_scope. + + +Ltac qreduce T := + rewrite <- (Qred_correct (T)); simpl (Qred (T)). + +Theorem Qlt_move_right : + forall x y z:Q, x + z < y <-> x < y - z. +Proof. + intros. + split. + intros. + psatzl Q. + intros. + psatzl Q. +Qed. + +Theorem Qlt_mult_by_z : + forall x y z:Q, 0 < z -> (x < y <-> x * z < y * z). +Proof. + intros. + split. + intros. + apply Qmult_lt_compat_r. trivial. trivial. + intros. + rewrite <- (Qdiv_mult_l x z). rewrite <- (Qdiv_mult_l y z). + apply Qmult_lt_compat_r. + apply Qlt_shift_inv_l. + trivial. psatzl Q. trivial. psatzl Q. psatzl Q. +Qed. + +Theorem Qle_mult_quad : + forall (a b c d:Q), + 0 <= a -> a <= c -> + 0 <= b -> b <= d -> + a * b <= c * d. + intros. + psatz Q. +Qed. + + +Theorem q_lt_qest: + forall (p q r:Q), + (0 < p) -> (p <= (2#1)^31 - 1) -> + (0 <= q) -> (q <= p - 1) -> + (1 <= r) -> (r <= p - 1) -> + q < (q * p + r) / (p * (1 + (2#1)^(-63))^2). +Proof. + intros. + rewrite Qlt_mult_by_z with (z := (p * (1 + (2#1)^(-63))^2)). + + unfold Qdiv. + rewrite <- Qmult_assoc. + rewrite (Qmult_comm (/ (p * (1 + (2 # 1) ^ (-63)) ^ 2)) (p * (1 + (2 # 1) ^ (-63)) ^ 2)). + rewrite Qmult_inv_r. + rewrite Qmult_1_r. + + assert (q * (p * (1 + (2 # 1) ^ (-63)) ^ 2) == q * p + (q * p) * ((2 # 1) ^ (-62) + (2 # 1) ^ (-126))). + qreduce ((1 + (2 # 1) ^ (-63)) ^ 2). + qreduce ((2 # 1) ^ (-62) + (2 # 1) ^ (-126)). + ring_simplify. + reflexivity. + rewrite H5. + + rewrite Qplus_comm. + rewrite Qlt_move_right. + ring_simplify (q * p + r - q * p). + qreduce ((2 # 1) ^ (-62) + (2 # 1) ^ (-126)). + + apply Qlt_le_trans with (y := 1). + rewrite Qlt_mult_by_z with (z := 85070591730234615865843651857942052864 # 18446744073709551617). + ring_simplify. + + apply Qle_lt_trans with (y := ((2 # 1) ^ 31 - (2#1)) * ((2 # 1) ^ 31 - 1)). + apply Qle_mult_quad. + assumption. psatzl Q. psatzl Q. psatzl Q. psatzl Q. psatzl Q. assumption. psatzl Q. psatzl Q. +Qed. + +Theorem qest_lt_qplus1: + forall (p q r:Q), + (0 < p) -> (p <= (2#1)^31 - 1) -> + (0 <= q) -> (q <= p - 1) -> + (1 <= r) -> (r <= p - 1) -> + ((q * p + r) * (1 + (2#1)^(-63))^2) / p < q + 1. +Proof. + intros. + rewrite Qlt_mult_by_z with (z := p). + + unfold Qdiv. + rewrite <- Qmult_assoc. + rewrite (Qmult_comm (/ p) p). + rewrite Qmult_inv_r. + rewrite Qmult_1_r. + + assert ((q * p + r) * (1 + (2 # 1) ^ (-63)) ^ 2 == q * p + r + (q * p + r) * ((2 # 1) ^ (-62) + (2 # 1) ^ (-126))). + qreduce ((1 + (2 # 1) ^ (-63)) ^ 2). + qreduce ((2 # 1) ^ (-62) + (2 # 1) ^ (-126)). + ring_simplify. reflexivity. + rewrite H5. + + rewrite <- Qplus_assoc. rewrite <- Qplus_comm. rewrite Qlt_move_right. + ring_simplify ((q + 1) * p - q * p). + + rewrite <- Qplus_comm. rewrite Qlt_move_right. + + apply Qlt_le_trans with (y := 1). + qreduce ((2 # 1) ^ (-62) + (2 # 1) ^ (-126)). + + rewrite Qlt_mult_by_z with (z := 85070591730234615865843651857942052864 # 18446744073709551617). + ring_simplify. + + ring_simplify in H0. + apply Qle_lt_trans with (y := (2147483646 # 1) * (2147483647 # 1) + (2147483646 # 1)). + + apply Qplus_le_compat. + apply Qle_mult_quad. + assumption. psatzl Q. auto with qarith. assumption. psatzl Q. + auto with qarith. auto with qarith. + psatzl Q. psatzl Q. assumption. +Qed. + + + diff -r 207408428242 -r 40917e4b51aa Modules/_decimal/literature/six-step.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/literature/six-step.txt Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,63 @@ + + +(* Copyright (c) 2011 Stefan Krah. All rights reserved. *) + + +The Six Step Transform: +======================= + +In libmpdec, the six-step transform is the Matrix Fourier Transform (See +matrix-transform.txt) in disguise. It is called six-step transform after +a variant that appears in [1]. The algorithm requires that the input +array can be viewed as an R*C matrix. + + +Algorithm six-step (forward transform): +--------------------------------------- + + 1a) Transpose the matrix. + + 1b) Apply a length R FNT to each row. + + 1c) Transpose the matrix. + + 2) Multiply each matrix element (addressed by j*C+m) by r**(j*m). + + 3) Apply a length C FNT to each row. + + 4) Transpose the matrix. + +Note that steps 1a) - 1c) are exactly equivalent to step 1) of the Matrix +Fourier Transform. For large R, it is faster to transpose twice and do +a transform on the rows than to perform a column transpose directly. + + + +Algorithm six-step (inverse transform): +--------------------------------------- + + 0) View the matrix as a C*R matrix. + + 1) Transpose the matrix, producing an R*C matrix. + + 2) Apply a length C FNT to each row. + + 3) Multiply each matrix element (addressed by i*C+n) by r**(i*n). + + 4a) Transpose the matrix. + + 4b) Apply a length R FNT to each row. + + 4c) Transpose the matrix. + +Again, steps 4a) - 4c) are equivalent to step 4) of the Matrix Fourier +Transform. + + + +-- + + [1] David H. Bailey: FFTs in External or Hierarchical Memory + http://crd.lbl.gov/~dhbailey/dhbpapers/ + + diff -r 207408428242 -r 40917e4b51aa Modules/_decimal/literature/umodarith.lisp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/literature/umodarith.lisp Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,692 @@ +; +; 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. +; + + +(in-package "ACL2") + +(include-book "arithmetic/top-with-meta" :dir :system) +(include-book "arithmetic-2/floor-mod/floor-mod" :dir :system) + + +;; ===================================================================== +;; Proofs for several functions in umodarith.h +;; ===================================================================== + + + +;; ===================================================================== +;; Helper theorems +;; ===================================================================== + +(defthm elim-mod-m= s m) (mod (- s m) base) s))) + s)) + +(defthmd addmod-correct + (implies (and (< 0 m) (< m base) + (< a m) (<= b m) + (natp m) (natp base) + (natp a) (natp b)) + (equal (addmod a b m base) + (mod (+ a b) m))) + :hints (("Goal" :cases ((<= base (+ a b)))) + ("Subgoal 2.1'" :use ((:instance elim-mod-m= a m) (- a m) a)) + (b (if (>= b m) (- b m) b)) + (d (mod (- a b) base)) + (d (if (< a b) (mod (+ d m) base) d))) + d)) + +; a < 2*m, b < 2*m +(defun ext-submod-2 (a b m base) + (let* ((a (mod a m)) + (b (mod b m)) + (d (mod (- a b) base)) + (d (if (< a b) (mod (+ d m) base) d))) + d)) + +(defthmd ext-submod-ext-submod-2-equal + (implies (and (< 0 m) (< m base) + (< a (* 2 m)) (< b (* 2 m)) + (natp m) (natp base) + (natp a) (natp b)) + (equal (ext-submod a b m base) + (ext-submod-2 a b m base)))) + +(defthmd ext-submod-2-correct + (implies (and (< 0 m) (< m base) + (< a (* 2 m)) (< b (* 2 m)) + (natp m) (natp base) + (natp a) (natp b)) + (equal (ext-submod-2 a b m base) + (mod (- a b) m)))) + + +;; ========================================================================= +;; dw-reduce is correct +;; ========================================================================= + +(defun dw-reduce (hi lo m base) + (let* ((r1 (mod hi m)) + (r2 (mod (+ (* r1 base) lo) m))) + r2)) + +(defthmd dw-reduce-correct + (implies (and (< 0 m) (< m base) + (< hi base) (< lo base) + (natp m) (natp base) + (natp hi) (natp lo)) + (equal (dw-reduce hi lo m base) + (mod (+ (* hi base) lo) m)))) + +(defthmd <=-multiply-both-sides-by-z + (implies (and (rationalp x) (rationalp y) + (< 0 z) (rationalp z)) + (equal (<= x y) + (<= (* z x) (* z y))))) + +(defthmd dw-reduce-aux1 + (implies (and (< 0 m) (< m base) + (natp m) (natp base) + (< lo base) (natp lo) + (< x m) (natp x)) + (< (+ lo (* base x)) (* base m))) + :hints (("Goal" :cases ((<= (+ x 1) m))) + ("Subgoal 1''" :cases ((<= (* base (+ x 1)) (* base m)))) + ("subgoal 1.2" :use ((:instance <=-multiply-both-sides-by-z + (x (+ 1 x)) + (y m) + (z base)))))) + +(defthm dw-reduce-aux2 + (implies (and (< x (* base m)) + (< 0 m) (< m base) + (natp m) (natp base) (natp x)) + (< (floor x m) base))) + +;; This is the necessary condition for using _mpd_div_words(). +(defthmd dw-reduce-second-quotient-fits-in-single-word + (implies (and (< 0 m) (< m base) + (< hi base) (< lo base) + (natp m) (natp base) + (natp hi) (natp lo) + (equal r1 (mod hi m))) + (< (floor (+ (* r1 base) lo) m) + base)) + :hints (("Goal" :cases ((< r1 m))) + ("Subgoal 1''" :cases ((< (+ lo (* base (mod hi m))) (* base m)))) + ("Subgoal 1.2" :use ((:instance dw-reduce-aux1 + (x (mod hi m))))))) + + +;; ========================================================================= +;; dw-submod is correct +;; ========================================================================= + +(defun dw-submod (a hi lo m base) + (let* ((r (dw-reduce hi lo m base)) + (d (mod (- a r) base)) + (d (if (< a r) (mod (+ d m) base) d))) + d)) + +(defthmd dw-submod-aux1 + (implies (and (natp a) (< 0 m) (natp m) + (natp x) (equal r (mod x m))) + (equal (mod (- a x) m) + (mod (- a r) m)))) + +(defthmd dw-submod-correct + (implies (and (< 0 m) (< m base) + (natp a) (< a m) + (< hi base) (< lo base) + (natp m) (natp base) + (natp hi) (natp lo)) + (equal (dw-submod a hi lo m base) + (mod (- a (+ (* base hi) lo)) m))) + :hints (("Goal" :in-theory (disable dw-reduce) + :use ((:instance dw-submod-aux1 + (x (+ lo (* base hi))) + (r (dw-reduce hi lo m base))) + (:instance dw-reduce-correct))))) + + +;; ========================================================================= +;; ANSI C arithmetic for uint64_t +;; ========================================================================= + +(defun add (a b) + (mod (+ a b) + (expt 2 64))) + +(defun sub (a b) + (mod (- a b) + (expt 2 64))) + +(defun << (w n) + (mod (* w (expt 2 n)) + (expt 2 64))) + +(defun >> (w n) + (floor w (expt 2 n))) + +;; join upper and lower half of a double word, yielding a 128 bit number +(defun join (hi lo) + (+ (* (expt 2 64) hi) lo)) + + +;; ============================================================================= +;; Fast modular reduction +;; ============================================================================= + +;; These are the three primes used in the Number Theoretic Transform. +;; A fast modular reduction scheme exists for all of them. +(defmacro p1 () + (+ (expt 2 64) (- (expt 2 32)) 1)) + +(defmacro p2 () + (+ (expt 2 64) (- (expt 2 34)) 1)) + +(defmacro p3 () + (+ (expt 2 64) (- (expt 2 40)) 1)) + + +;; reduce the double word number hi*2**64 + lo (mod p1) +(defun simple-mod-reduce-p1 (hi lo) + (+ (* (expt 2 32) hi) (- hi) lo)) + +;; reduce the double word number hi*2**64 + lo (mod p2) +(defun simple-mod-reduce-p2 (hi lo) + (+ (* (expt 2 34) hi) (- hi) lo)) + +;; reduce the double word number hi*2**64 + lo (mod p3) +(defun simple-mod-reduce-p3 (hi lo) + (+ (* (expt 2 40) hi) (- hi) lo)) + + +; ---------------------------------------------------------- +; The modular reductions given above are correct +; ---------------------------------------------------------- + +(defthmd congruence-p1-aux + (equal (* (expt 2 64) hi) + (+ (* (p1) hi) + (* (expt 2 32) hi) + (- hi)))) + +(defthmd congruence-p2-aux + (equal (* (expt 2 64) hi) + (+ (* (p2) hi) + (* (expt 2 34) hi) + (- hi)))) + +(defthmd congruence-p3-aux + (equal (* (expt 2 64) hi) + (+ (* (p3) hi) + (* (expt 2 40) hi) + (- hi)))) + +(defthmd mod-augment + (implies (and (rationalp x) + (rationalp y) + (rationalp m)) + (equal (mod (+ x y) m) + (mod (+ x (mod y m)) m)))) + +(defthmd simple-mod-reduce-p1-congruent + (implies (and (integerp hi) + (integerp lo)) + (equal (mod (simple-mod-reduce-p1 hi lo) (p1)) + (mod (join hi lo) (p1)))) + :hints (("Goal''" :use ((:instance congruence-p1-aux) + (:instance mod-augment + (m (p1)) + (x (+ (- hi) lo (* (expt 2 32) hi))) + (y (* (p1) hi))))))) + +(defthmd simple-mod-reduce-p2-congruent + (implies (and (integerp hi) + (integerp lo)) + (equal (mod (simple-mod-reduce-p2 hi lo) (p2)) + (mod (join hi lo) (p2)))) + :hints (("Goal''" :use ((:instance congruence-p2-aux) + (:instance mod-augment + (m (p2)) + (x (+ (- hi) lo (* (expt 2 34) hi))) + (y (* (p2) hi))))))) + +(defthmd simple-mod-reduce-p3-congruent + (implies (and (integerp hi) + (integerp lo)) + (equal (mod (simple-mod-reduce-p3 hi lo) (p3)) + (mod (join hi lo) (p3)))) + :hints (("Goal''" :use ((:instance congruence-p3-aux) + (:instance mod-augment + (m (p3)) + (x (+ (- hi) lo (* (expt 2 40) hi))) + (y (* (p3) hi))))))) + + +; --------------------------------------------------------------------- +; We need a number less than 2*p, so that we can use the trick from +; elim-mod-m> hi 32)) + (x (sub lo x)) + (hi (if (> x lo) (+ hi -1) hi)) + (y (<< y 32)) + (lo (add y x)) + (hi (if (< lo y) (+ hi 1) hi))) + (+ (* hi (expt 2 64)) lo))) + +(defun mod-reduce-p2 (hi lo) + (let* ((y hi) + (x y) + (hi (>> hi 30)) + (x (sub lo x)) + (hi (if (> x lo) (+ hi -1) hi)) + (y (<< y 34)) + (lo (add y x)) + (hi (if (< lo y) (+ hi 1) hi))) + (+ (* hi (expt 2 64)) lo))) + +(defun mod-reduce-p3 (hi lo) + (let* ((y hi) + (x y) + (hi (>> hi 24)) + (x (sub lo x)) + (hi (if (> x lo) (+ hi -1) hi)) + (y (<< y 40)) + (lo (add y x)) + (hi (if (< lo y) (+ hi 1) hi))) + (+ (* hi (expt 2 64)) lo))) + + +; ------------------------------------------------------------------------- +; The compiler friendly versions are equal to the simple versions +; ------------------------------------------------------------------------- + +(defthm mod-reduce-aux1 + (implies (and (<= 0 a) (natp a) (natp m) + (< (- m) b) (<= b 0) + (integerp b) + (< (mod (+ b a) m) + (mod a m))) + (equal (mod (+ b a) m) + (+ b (mod a m)))) + :hints (("Subgoal 2" :use ((:instance modaux-1b + (x (+ a b))))))) + +(defthm mod-reduce-aux2 + (implies (and (<= 0 a) (natp a) (natp m) + (< b m) (natp b) + (< (mod (+ b a) m) + (mod a m))) + (equal (+ m (mod (+ b a) m)) + (+ b (mod a m))))) + + +(defthm mod-reduce-aux3 + (implies (and (< 0 a) (natp a) (natp m) + (< (- m) b) (< b 0) + (integerp b) + (<= (mod a m) + (mod (+ b a) m))) + (equal (+ (- m) (mod (+ b a) m)) + (+ b (mod a m)))) + :hints (("Subgoal 1.2'" :use ((:instance modaux-1b + (x b)))) + ("Subgoal 1''" :use ((:instance modaux-2d + (x I)))))) + + +(defthm mod-reduce-aux4 + (implies (and (< 0 a) (natp a) (natp m) + (< b m) (natp b) + (<= (mod a m) + (mod (+ b a) m))) + (equal (mod (+ b a) m) + (+ b (mod a m))))) + + +(defthm mod-reduce-p1==simple-mod-reduce-p1 + (implies (and (< hi (expt 2 64)) + (< lo (expt 2 64)) + (natp hi) (natp lo)) + (equal (mod-reduce-p1 hi lo) + (simple-mod-reduce-p1 hi lo))) + :hints (("Goal" :in-theory (disable expt) + :cases ((< 0 hi))) + ("Subgoal 1.2.2'" :use ((:instance mod-reduce-aux1 + (m (expt 2 64)) + (b (+ (- HI) LO)) + (a (* (expt 2 32) hi))))) + ("Subgoal 1.2.1'" :use ((:instance mod-reduce-aux3 + (m (expt 2 64)) + (b (+ (- HI) LO)) + (a (* (expt 2 32) hi))))) + ("Subgoal 1.1.2'" :use ((:instance mod-reduce-aux2 + (m (expt 2 64)) + (b (+ (- HI) LO)) + (a (* (expt 2 32) hi))))) + ("Subgoal 1.1.1'" :use ((:instance mod-reduce-aux4 + (m (expt 2 64)) + (b (+ (- HI) LO)) + (a (* (expt 2 32) hi))))))) + + +(defthm mod-reduce-p2==simple-mod-reduce-p2 + (implies (and (< hi (expt 2 64)) + (< lo (expt 2 64)) + (natp hi) (natp lo)) + (equal (mod-reduce-p2 hi lo) + (simple-mod-reduce-p2 hi lo))) + :hints (("Goal" :cases ((< 0 hi))) + ("Subgoal 1.2.2'" :use ((:instance mod-reduce-aux1 + (m (expt 2 64)) + (b (+ (- HI) LO)) + (a (* (expt 2 34) hi))))) + ("Subgoal 1.2.1'" :use ((:instance mod-reduce-aux3 + (m (expt 2 64)) + (b (+ (- HI) LO)) + (a (* (expt 2 34) hi))))) + ("Subgoal 1.1.2'" :use ((:instance mod-reduce-aux2 + (m (expt 2 64)) + (b (+ (- HI) LO)) + (a (* (expt 2 34) hi))))) + ("Subgoal 1.1.1'" :use ((:instance mod-reduce-aux4 + (m (expt 2 64)) + (b (+ (- HI) LO)) + (a (* (expt 2 34) hi))))))) + + +(defthm mod-reduce-p3==simple-mod-reduce-p3 + (implies (and (< hi (expt 2 64)) + (< lo (expt 2 64)) + (natp hi) (natp lo)) + (equal (mod-reduce-p3 hi lo) + (simple-mod-reduce-p3 hi lo))) + :hints (("Goal" :cases ((< 0 hi))) + ("Subgoal 1.2.2'" :use ((:instance mod-reduce-aux1 + (m (expt 2 64)) + (b (+ (- HI) LO)) + (a (* (expt 2 40) hi))))) + ("Subgoal 1.2.1'" :use ((:instance mod-reduce-aux3 + (m (expt 2 64)) + (b (+ (- HI) LO)) + (a (* (expt 2 40) hi))))) + ("Subgoal 1.1.2'" :use ((:instance mod-reduce-aux2 + (m (expt 2 64)) + (b (+ (- HI) LO)) + (a (* (expt 2 40) hi))))) + ("Subgoal 1.1.1'" :use ((:instance mod-reduce-aux4 + (m (expt 2 64)) + (b (+ (- HI) LO)) + (a (* (expt 2 40) hi))))))) + + + diff -r 207408428242 -r 40917e4b51aa Modules/_decimal/memory.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/memory.c Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,270 @@ +/* + * 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" + + +/* Guaranteed minimum allocation for a coefficient. May be changed once + at program start using mpd_setminalloc(). */ +mpd_ssize_t MPD_MINALLOC = MPD_MINALLOC_MIN; + +/* Custom allocation and free functions */ +void *(* mpd_mallocfunc)(size_t size) = malloc; +void *(* mpd_reallocfunc)(void *ptr, size_t size) = realloc; +void *(* mpd_callocfunc)(size_t nmemb, size_t size) = calloc; +void (* mpd_free)(void *ptr) = free; + + +/* emulate calloc if it is not available */ +void * +mpd_callocfunc_em(size_t nmemb, size_t size) +{ + void *ptr; + size_t req; + mpd_size_t overflow; + +#if MPD_SIZE_MAX < SIZE_MAX + /* full_coverage test only */ + if (nmemb > MPD_SIZE_MAX || size > MPD_SIZE_MAX) { + return NULL; + } +#endif + + req = mul_size_t_overflow((mpd_size_t)nmemb, (mpd_size_t)size, + &overflow); + if (overflow) { + return NULL; + } + + ptr = mpd_mallocfunc(req); + if (ptr == NULL) { + return NULL; + } + /* used on uint32_t or uint64_t */ + memset(ptr, 0, req); + + return ptr; +} + + +/* malloc with overflow checking */ +void * +mpd_alloc(mpd_size_t nmemb, mpd_size_t size) +{ + mpd_size_t req, overflow; + + req = mul_size_t_overflow(nmemb, size, &overflow); + if (overflow) { + return NULL; + } + + return mpd_mallocfunc(req); +} + +/* calloc with overflow checking */ +void * +mpd_calloc(mpd_size_t nmemb, mpd_size_t size) +{ + mpd_size_t overflow; + + (void)mul_size_t_overflow(nmemb, size, &overflow); + if (overflow) { + return NULL; + } + + return mpd_callocfunc(nmemb, size); +} + +/* realloc with overflow checking */ +void * +mpd_realloc(void *ptr, mpd_size_t nmemb, mpd_size_t size, uint8_t *err) +{ + void *new; + mpd_size_t req, overflow; + + req = mul_size_t_overflow(nmemb, size, &overflow); + if (overflow) { + *err = 1; + return ptr; + } + + new = mpd_reallocfunc(ptr, req); + if (new == NULL) { + *err = 1; + return ptr; + } + + return new; +} + +/* struct hack malloc with overflow checking */ +void * +mpd_sh_alloc(mpd_size_t struct_size, mpd_size_t nmemb, mpd_size_t size) +{ + mpd_size_t req, overflow; + + req = mul_size_t_overflow(nmemb, size, &overflow); + if (overflow) { + return NULL; + } + + req = add_size_t_overflow(req, struct_size, &overflow); + if (overflow) { + return NULL; + } + + return mpd_mallocfunc(req); +} + + +/* Allocate a new decimal with a coefficient of length 'nwords'. In case + of an error the return value is NULL. */ +mpd_t * +mpd_qnew_size(mpd_ssize_t nwords) +{ + mpd_t *result; + + nwords = (nwords < MPD_MINALLOC) ? MPD_MINALLOC : nwords; + + result = mpd_alloc(1, sizeof *result); + if (result == NULL) { + return NULL; + } + + result->data = mpd_alloc(nwords, sizeof *result->data); + if (result->data == NULL) { + mpd_free(result); + return NULL; + } + + result->flags = 0; + result->exp = 0; + result->digits = 0; + result->len = 0; + result->alloc = nwords; + + return result; +} + +/* Allocate a new decimal with a coefficient of length MPD_MINALLOC. + In case of an error the return value is NULL. */ +mpd_t * +mpd_qnew(void) +{ + return mpd_qnew_size(MPD_MINALLOC); +} + +/* Allocate new decimal. Caller can check for NULL or MPD_Malloc_error. + Raises on error. */ +mpd_t * +mpd_new(mpd_context_t *ctx) +{ + mpd_t *result; + + result = mpd_qnew(); + if (result == NULL) { + mpd_addstatus_raise(ctx, MPD_Malloc_error); + } + return result; +} + +/* Convert a static coefficient to a larger dynamic one with length 'nwords'. + Existing data is copied. */ +int +mpd_switch_to_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status) +{ + mpd_uint_t *p = result->data; + + assert(nwords >= result->alloc); + + result->data = mpd_alloc(nwords, sizeof *result->data); + if (result->data == NULL) { + result->data = p; + mpd_set_qnan(result); + mpd_set_positive(result); + result->exp = result->digits = result->len = 0; + *status |= MPD_Malloc_error; + return 0; + } + + memcpy(result->data, p, result->len * (sizeof *result->data)); + result->alloc = nwords; + mpd_set_dynamic_data(result); + return 1; +} + +/* Convert a static coefficient to a dynamic one with length 'nwords'. + Existing data is zeroed. */ +int +mpd_switch_to_dyn_zero(mpd_t *result, mpd_ssize_t nwords, uint32_t *status) +{ + mpd_uint_t *p = result->data; + + result->data = mpd_calloc(nwords, sizeof *result->data); + if (result->data == NULL) { + result->data = p; + mpd_set_qnan(result); + mpd_set_positive(result); + result->exp = result->digits = result->len = 0; + *status |= MPD_Malloc_error; + return 0; + } + + result->alloc = nwords; + mpd_set_dynamic_data(result); + + return 1; +} + +/* Resize a dynamic coefficient to length 'nwords'. If resize to smaller + fails, the previous memory area is reused without returning an error. */ +int +mpd_realloc_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status) +{ + uint8_t err = 0; + + result->data = mpd_realloc(result->data, nwords, sizeof *result->data, &err); + if (!err) { + result->alloc = nwords; + } + else if (nwords > result->alloc) { + mpd_set_qnan(result); + mpd_set_positive(result); + result->exp = result->digits = result->len = 0; + *status |= MPD_Malloc_error; + return 0; + } + + return 1; +} + + diff -r 207408428242 -r 40917e4b51aa Modules/_decimal/memory.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/memory.h Thu Feb 23 16:43:15 2012 +0100 @@ -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 207408428242 -r 40917e4b51aa Modules/_decimal/mpdecimal.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/mpdecimal.c Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,7969 @@ +/* + * 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" + +#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[18]) { + if (word < mpd_pow10[16]) { + return (word < mpd_pow10[15]) ? 15 : 16; + } + return (word < mpd_pow10[17]) ? 17 : 18; + } + + return (word < mpd_pow10[19]) ? 19 : 20; +} +#else +ALWAYS_INLINE int +mpd_word_digits(mpd_uint_t word) +{ + if (word < mpd_pow10[4]) { + if (word < mpd_pow10[2]) { + return (word < mpd_pow10[1]) ? 1 : 2; + } + return (word < mpd_pow10[3]) ? 3 : 4; + } + if (word < mpd_pow10[6]) { + return (word < mpd_pow10[5]) ? 5 : 6; + } + if (word < mpd_pow10[8]) { + return (word < mpd_pow10[7]) ? 7 : 8; + } + + return (word < mpd_pow10[9]) ? 9 : 10; +} +#endif + + +/* Adjusted exponent */ +ALWAYS_INLINE mpd_ssize_t +mpd_adjexp(const mpd_t *dec) +{ + return (dec->exp + dec->digits) - 1; +} + +/* Etiny */ +ALWAYS_INLINE mpd_ssize_t +mpd_etiny(const mpd_context_t *ctx) +{ + return ctx->emin - (ctx->prec - 1); +} + +/* Etop: used for folding down in IEEE clamping */ +ALWAYS_INLINE mpd_ssize_t +mpd_etop(const mpd_context_t *ctx) +{ + return ctx->emax - (ctx->prec - 1); +} + +/* Most significant word */ +ALWAYS_INLINE mpd_uint_t +mpd_msword(const mpd_t *dec) +{ + assert(dec->len > 0); + return dec->data[dec->len-1]; +} + +/* Most significant digit of a word */ +inline mpd_uint_t +mpd_msd(mpd_uint_t word) +{ + int n; + + n = mpd_word_digits(word); + return word / mpd_pow10[n-1]; +} + +/* Least significant digit of a word */ +ALWAYS_INLINE mpd_uint_t +mpd_lsd(mpd_uint_t word) +{ + return word % 10; +} + +/* Coefficient size needed to store 'digits' */ +ALWAYS_INLINE mpd_ssize_t +mpd_digits_to_size(mpd_ssize_t digits) +{ + mpd_ssize_t q, r; + + _mpd_idiv_word(&q, &r, digits, MPD_RDIGITS); + return (r == 0) ? q : q+1; +} + +/* Number of digits in the exponent. Not defined for MPD_SSIZE_MIN. */ +inline int +mpd_exp_digits(mpd_ssize_t exp) +{ + exp = (exp < 0) ? -exp : exp; + return mpd_word_digits(exp); +} + +/* Canonical */ +ALWAYS_INLINE int +mpd_iscanonical(const mpd_t *dec UNUSED) +{ + return 1; +} + +/* Finite */ +ALWAYS_INLINE int +mpd_isfinite(const mpd_t *dec) +{ + return !(dec->flags & MPD_SPECIAL); +} + +/* Infinite */ +ALWAYS_INLINE int +mpd_isinfinite(const mpd_t *dec) +{ + return dec->flags & MPD_INF; +} + +/* NaN */ +ALWAYS_INLINE int +mpd_isnan(const mpd_t *dec) +{ + return dec->flags & (MPD_NAN|MPD_SNAN); +} + +/* Negative */ +ALWAYS_INLINE int +mpd_isnegative(const mpd_t *dec) +{ + return dec->flags & MPD_NEG; +} + +/* Positive */ +ALWAYS_INLINE int +mpd_ispositive(const mpd_t *dec) +{ + return !(dec->flags & MPD_NEG); +} + +/* qNaN */ +ALWAYS_INLINE int +mpd_isqnan(const mpd_t *dec) +{ + return dec->flags & MPD_NAN; +} + +/* Signed */ +ALWAYS_INLINE int +mpd_issigned(const mpd_t *dec) +{ + return dec->flags & MPD_NEG; +} + +/* sNaN */ +ALWAYS_INLINE int +mpd_issnan(const mpd_t *dec) +{ + return dec->flags & MPD_SNAN; +} + +/* Special */ +ALWAYS_INLINE int +mpd_isspecial(const mpd_t *dec) +{ + return dec->flags & MPD_SPECIAL; +} + +/* Zero */ +ALWAYS_INLINE int +mpd_iszero(const mpd_t *dec) +{ + return !mpd_isspecial(dec) && mpd_msword(dec) == 0; +} + +/* Test for zero when specials have been ruled out already */ +ALWAYS_INLINE int +mpd_iszerocoeff(const mpd_t *dec) +{ + return mpd_msword(dec) == 0; +} + +/* Normal */ +inline int +mpd_isnormal(const mpd_t *dec, const mpd_context_t *ctx) +{ + if (mpd_isspecial(dec)) return 0; + if (mpd_iszerocoeff(dec)) return 0; + + return mpd_adjexp(dec) >= ctx->emin; +} + +/* Subnormal */ +inline int +mpd_issubnormal(const mpd_t *dec, const mpd_context_t *ctx) +{ + if (mpd_isspecial(dec)) return 0; + if (mpd_iszerocoeff(dec)) return 0; + + return mpd_adjexp(dec) < ctx->emin; +} + +/* Odd word */ +ALWAYS_INLINE int +mpd_isoddword(mpd_uint_t word) +{ + return word & 1; +} + +/* Odd coefficient */ +ALWAYS_INLINE int +mpd_isoddcoeff(const mpd_t *dec) +{ + return mpd_isoddword(dec->data[0]); +} + +/* 0 if dec is positive, 1 if dec is negative */ +ALWAYS_INLINE uint8_t +mpd_sign(const mpd_t *dec) +{ + return dec->flags & MPD_NEG; +} + +/* 1 if dec is positive, -1 if dec is negative */ +ALWAYS_INLINE int +mpd_arith_sign(const mpd_t *dec) +{ + return 1 - 2 * mpd_isnegative(dec); +} + +/* Radix */ +ALWAYS_INLINE long +mpd_radix(void) +{ + return 10; +} + +/* Dynamic decimal */ +ALWAYS_INLINE int +mpd_isdynamic(mpd_t *dec) +{ + return !(dec->flags & MPD_STATIC); +} + +/* Static decimal */ +ALWAYS_INLINE int +mpd_isstatic(mpd_t *dec) +{ + return dec->flags & MPD_STATIC; +} + +/* Data of decimal is dynamic */ +ALWAYS_INLINE int +mpd_isdynamic_data(mpd_t *dec) +{ + return !(dec->flags & MPD_DATAFLAGS); +} + +/* Data of decimal is static */ +ALWAYS_INLINE int +mpd_isstatic_data(mpd_t *dec) +{ + return dec->flags & MPD_STATIC_DATA; +} + +/* Data of decimal is shared */ +ALWAYS_INLINE int +mpd_isshared_data(mpd_t *dec) +{ + return dec->flags & MPD_SHARED_DATA; +} + +/* Data of decimal is const */ +ALWAYS_INLINE int +mpd_isconst_data(mpd_t *dec) +{ + return dec->flags & MPD_CONST_DATA; +} + + +/******************************************************************************/ +/* Inline memory handling */ +/******************************************************************************/ + +/* Fill destination with zeros */ +ALWAYS_INLINE void +mpd_uint_zero(mpd_uint_t *dest, mpd_size_t len) +{ + mpd_size_t i; + + for (i = 0; i < len; i++) { + dest[i] = 0; + } +} + +/* Free a decimal */ +ALWAYS_INLINE void +mpd_del(mpd_t *dec) +{ + if (mpd_isdynamic_data(dec)) { + mpd_free(dec->data); + } + if (mpd_isdynamic(dec)) { + mpd_free(dec); + } +} + +/* + * 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. If the operand + is modified, it will be zero, special or (sub)normal with a coefficient + that fits into the current context precision. */ +static inline void +_mpd_check_exp(mpd_t *dec, const mpd_context_t *ctx, uint32_t *status) +{ + mpd_ssize_t adjexp, etiny, shift; + int rnd; + + adjexp = mpd_adjexp(dec); + if (adjexp > ctx->emax) { + + if (mpd_iszerocoeff(dec)) { + dec->exp = ctx->emax; + if (ctx->clamp) { + dec->exp -= (ctx->prec-1); + } + mpd_zerocoeff(dec); + *status |= MPD_Clamped; + return; + } + + switch (ctx->round) { + case MPD_ROUND_HALF_UP: case MPD_ROUND_HALF_EVEN: + case MPD_ROUND_HALF_DOWN: case MPD_ROUND_UP: + case MPD_ROUND_TRUNC: + mpd_setspecial(dec, mpd_sign(dec), MPD_INF); + break; + case MPD_ROUND_DOWN: case MPD_ROUND_05UP: + mpd_qmaxcoeff(dec, ctx, status); + dec->exp = ctx->emax - ctx->prec + 1; + break; + case MPD_ROUND_CEILING: + if (mpd_isnegative(dec)) { + mpd_qmaxcoeff(dec, ctx, status); + dec->exp = ctx->emax - ctx->prec + 1; + } + else { + mpd_setspecial(dec, MPD_POS, MPD_INF); + } + break; + case MPD_ROUND_FLOOR: + if (mpd_ispositive(dec)) { + mpd_qmaxcoeff(dec, ctx, status); + dec->exp = ctx->emax - ctx->prec + 1; + } + else { + mpd_setspecial(dec, MPD_NEG, MPD_INF); + } + break; + default: /* debug */ + abort(); /* GCOV_NOT_REACHED */ + } + + *status |= MPD_Overflow|MPD_Inexact|MPD_Rounded; + + } /* fold down */ + else if (ctx->clamp && dec->exp > mpd_etop(ctx)) { + /* At this point adjexp=exp+digits-1 <= emax and exp > etop=emax-prec+1: + * (1) shift = exp -emax+prec-1 > 0 + * (2) digits+shift = exp+digits-1 - emax + prec <= prec */ + shift = dec->exp - mpd_etop(ctx); + if (!mpd_qshiftl(dec, dec, shift, status)) { + return; + } + dec->exp -= shift; + *status |= MPD_Clamped; + if (!mpd_iszerocoeff(dec) && adjexp < ctx->emin) { + /* Underflow is impossible, since exp < etiny=emin-prec+1 + * and exp > etop=emax-prec+1 would imply emax < emin. */ + *status |= MPD_Subnormal; + } + } + else if (adjexp < ctx->emin) { + + etiny = mpd_etiny(ctx); + + if (mpd_iszerocoeff(dec)) { + if (dec->exp < etiny) { + dec->exp = etiny; + mpd_zerocoeff(dec); + *status |= MPD_Clamped; + } + return; + } + + *status |= MPD_Subnormal; + if (dec->exp < etiny) { + /* At this point adjexp=exp+digits-1 < emin and exp < etiny=emin-prec+1: + * (1) shift = emin-prec+1 - exp > 0 + * (2) digits-shift = exp+digits-1 - emin + prec < prec */ + shift = etiny - dec->exp; + rnd = (int)mpd_qshiftr_inplace(dec, shift); + dec->exp = etiny; + /* We always have a spare digit in case of an increment. */ + _mpd_apply_round_excess(dec, rnd, ctx, status); + *status |= MPD_Rounded; + if (rnd) { + *status |= (MPD_Inexact|MPD_Underflow); + if (mpd_iszerocoeff(dec)) { + mpd_zerocoeff(dec); + *status |= MPD_Clamped; + } + } + } + /* Case exp >= etiny=emin-prec+1: + * (1) adjexp=exp+digits-1 < emin + * (2) digits < emin-exp+1 <= prec */ + } +} + +/* Transcendental functions do not always set Underflow reliably, + * since they only use as much precision as is necessary for correct + * rounding. If a result like 1.0000000000e-101 is finalized, there + * is no rounding digit that would trigger Underflow. But we can + * assume Inexact, so a short check suffices. */ +static inline void +mpd_check_underflow(mpd_t *dec, const mpd_context_t *ctx, uint32_t *status) +{ + if (mpd_adjexp(dec) < ctx->emin && !mpd_iszero(dec) && + dec->exp < mpd_etiny(ctx)) { + *status |= MPD_Underflow; + } +} + +/* Check if a normal number must be rounded after the exponent has been checked. */ +static inline void +_mpd_check_round(mpd_t *dec, const mpd_context_t *ctx, uint32_t *status) +{ + mpd_uint_t rnd; + mpd_ssize_t shift; + + /* must handle specials: _mpd_check_exp() can produce infinities or NaNs */ + if (mpd_isspecial(dec)) { + return; + } + + if (dec->digits > ctx->prec) { + shift = dec->digits - ctx->prec; + rnd = mpd_qshiftr_inplace(dec, shift); + dec->exp += shift; + _mpd_apply_round(dec, rnd, ctx, status); + *status |= MPD_Rounded; + if (rnd) { + *status |= MPD_Inexact; + } + } +} + +/* Finalize all operations. */ +void +mpd_qfinalize(mpd_t *result, const mpd_context_t *ctx, uint32_t *status) +{ + if (mpd_isspecial(result)) { + if (mpd_isnan(result)) { + _mpd_fix_nan(result, ctx); + } + return; + } + + _mpd_check_exp(result, ctx, status); + _mpd_check_round(result, ctx, status); +} + + +/******************************************************************************/ +/* Copying */ +/******************************************************************************/ + +/* Internal function: Copy a decimal, share data with src: USE WITH CARE! */ +static inline void +_mpd_copy_shared(mpd_t *dest, const mpd_t *src) +{ + dest->flags = src->flags; + dest->exp = src->exp; + dest->digits = src->digits; + dest->len = src->len; + dest->alloc = src->alloc; + dest->data = src->data; + + mpd_set_shared_data(dest); +} + +/* + * Copy a decimal. In case of an error, status is set to MPD_Malloc_error. + */ +int +mpd_qcopy(mpd_t *result, const mpd_t *a, uint32_t *status) +{ + if (result == a) return 1; + + if (!mpd_qresize(result, a->len, status)) { + return 0; + } + + mpd_copy_flags(result, a); + result->exp = a->exp; + result->digits = a->digits; + result->len = a->len; + memcpy(result->data, a->data, a->len * (sizeof *result->data)); + + return 1; +} + +/* + * Copy to a decimal with a static buffer. The caller has to make sure that + * the buffer is big enough. Cannot fail. + */ +static void +mpd_qcopy_static(mpd_t *result, const mpd_t *a) +{ + if (result == a) return; + + memcpy(result->data, a->data, a->len * (sizeof *result->data)); + + mpd_copy_flags(result, a); + result->exp = a->exp; + result->digits = a->digits; + result->len = a->len; +} + +/* + * Return a newly allocated copy of the operand. In case of an error, + * status is set to MPD_Malloc_error and the return value is NULL. + */ +mpd_t * +mpd_qncopy(const mpd_t *a) +{ + mpd_t *result; + + if ((result = mpd_qnew_size(a->len)) == NULL) { + return NULL; + } + memcpy(result->data, a->data, a->len * (sizeof *result->data)); + mpd_copy_flags(result, a); + result->exp = a->exp; + result->digits = a->digits; + result->len = a->len; + + return result; +} + +/* + * Copy a decimal and set the sign to positive. In case of an error, the + * status is set to MPD_Malloc_error. + */ +int +mpd_qcopy_abs(mpd_t *result, const mpd_t *a, uint32_t *status) +{ + if (!mpd_qcopy(result, a, status)) { + return 0; + } + mpd_set_positive(result); + return 1; +} + +/* + * Copy a decimal and negate the sign. In case of an error, the + * status is set to MPD_Malloc_error. + */ +int +mpd_qcopy_negate(mpd_t *result, const mpd_t *a, uint32_t *status) +{ + if (!mpd_qcopy(result, a, status)) { + return 0; + } + _mpd_negate(result); + return 1; +} + +/* + * Copy a decimal, setting the sign of the first operand to the sign of the + * second operand. In case of an error, the status is set to MPD_Malloc_error. + */ +int +mpd_qcopy_sign(mpd_t *result, const mpd_t *a, const mpd_t *b, uint32_t *status) +{ + uint8_t sign_b = mpd_sign(b); /* result may equal b! */ + + if (!mpd_qcopy(result, a, status)) { + return 0; + } + mpd_set_sign(result, sign_b); + return 1; +} + + +/******************************************************************************/ +/* Comparisons */ +/******************************************************************************/ + +/* + * For all functions that compare two operands and return an int the usual + * convention applies to the return value: + * + * -1 if op1 < op2 + * 0 if op1 == op2 + * 1 if op1 > op2 + * + * INT_MAX for error + */ + + +/* Convenience macro. If a and b are not equal, return from the calling + * function with the correct comparison value. */ +#define CMP_EQUAL_OR_RETURN(a, b) \ + if (a != b) { \ + if (a < b) { \ + return -1; \ + } \ + return 1; \ + } + +/* + * Compare the data of big and small. This function does the equivalent + * of first shifting small to the left and then comparing the data of + * big and small, except that no allocation for the left shift is needed. + */ +static int +_mpd_basecmp(mpd_uint_t *big, mpd_uint_t *small, mpd_size_t n, mpd_size_t m, + mpd_size_t shift) +{ +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__) + /* spurious uninitialized warnings */ + mpd_uint_t l=l, lprev=lprev, h=h; +#else + mpd_uint_t l, lprev, h; +#endif + mpd_uint_t q, r; + mpd_uint_t ph, x; + + assert(m > 0 && n >= m && shift > 0); + + _mpd_div_word(&q, &r, (mpd_uint_t)shift, MPD_RDIGITS); + + if (r != 0) { + + ph = mpd_pow10[r]; + + --m; --n; + _mpd_divmod_pow10(&h, &lprev, small[m--], MPD_RDIGITS-r); + if (h != 0) { + CMP_EQUAL_OR_RETURN(big[n], h) + --n; + } + for (; m != MPD_SIZE_MAX; m--,n--) { + _mpd_divmod_pow10(&h, &l, small[m], MPD_RDIGITS-r); + x = ph * lprev + h; + CMP_EQUAL_OR_RETURN(big[n], x) + lprev = l; + } + x = ph * lprev; + CMP_EQUAL_OR_RETURN(big[q], x) + } + else { + while (--m != MPD_SIZE_MAX) { + CMP_EQUAL_OR_RETURN(big[m+q], small[m]) + } + } + + return !_mpd_isallzero(big, q); +} + +/* Compare two decimals with the same adjusted exponent. */ +static int +_mpd_cmp_same_adjexp(const mpd_t *a, const mpd_t *b) +{ + mpd_ssize_t shift, i; + + if (a->exp != b->exp) { + /* Cannot wrap: a->exp + a->digits = b->exp + b->digits, so + * a->exp - b->exp = b->digits - a->digits. */ + shift = a->exp - b->exp; + if (shift > 0) { + return -1 * _mpd_basecmp(b->data, a->data, b->len, a->len, shift); + } + else { + return _mpd_basecmp(a->data, b->data, a->len, b->len, -shift); + } + } + + /* + * At this point adjexp(a) == adjexp(b) and a->exp == b->exp, + * so a->digits == b->digits, therefore a->len == b->len. + */ + for (i = a->len-1; i >= 0; --i) { + CMP_EQUAL_OR_RETURN(a->data[i], b->data[i]) + } + + return 0; +} + +/* Compare two numerical values. */ +static int +_mpd_cmp(const mpd_t *a, const mpd_t *b) +{ + mpd_ssize_t adjexp_a, adjexp_b; + + /* equal pointers */ + if (a == b) { + return 0; + } + + /* infinities */ + if (mpd_isinfinite(a)) { + if (mpd_isinfinite(b)) { + return mpd_isnegative(b) - mpd_isnegative(a); + } + return mpd_arith_sign(a); + } + if (mpd_isinfinite(b)) { + return -mpd_arith_sign(b); + } + + /* zeros */ + if (mpd_iszerocoeff(a)) { + if (mpd_iszerocoeff(b)) { + return 0; + } + return -mpd_arith_sign(b); + } + if (mpd_iszerocoeff(b)) { + return mpd_arith_sign(a); + } + + /* different signs */ + if (mpd_sign(a) != mpd_sign(b)) { + return mpd_sign(b) - mpd_sign(a); + } + + /* different adjusted exponents */ + adjexp_a = mpd_adjexp(a); + adjexp_b = mpd_adjexp(b); + if (adjexp_a != adjexp_b) { + if (adjexp_a < adjexp_b) { + return -1 * mpd_arith_sign(a); + } + return mpd_arith_sign(a); + } + + /* same adjusted exponents */ + return _mpd_cmp_same_adjexp(a, b) * mpd_arith_sign(a); +} + +/* Compare the absolutes of two numerical values. */ +static int +_mpd_cmp_abs(const mpd_t *a, const mpd_t *b) +{ + mpd_ssize_t adjexp_a, adjexp_b; + + /* equal pointers */ + if (a == b) { + return 0; + } + + /* infinities */ + if (mpd_isinfinite(a)) { + if (mpd_isinfinite(b)) { + return 0; + } + return 1; + } + if (mpd_isinfinite(b)) { + return -1; + } + + /* zeros */ + if (mpd_iszerocoeff(a)) { + if (mpd_iszerocoeff(b)) { + return 0; + } + return -1; + } + if (mpd_iszerocoeff(b)) { + return 1; + } + + /* different adjusted exponents */ + adjexp_a = mpd_adjexp(a); + adjexp_b = mpd_adjexp(b); + if (adjexp_a != adjexp_b) { + if (adjexp_a < adjexp_b) { + return -1; + } + return 1; + } + + /* same adjusted exponents */ + return _mpd_cmp_same_adjexp(a, b); +} + +/* Compare two values and return an integer result. */ +int +mpd_qcmp(const mpd_t *a, const mpd_t *b, uint32_t *status) +{ + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_isnan(a) || mpd_isnan(b)) { + *status |= MPD_Invalid_operation; + return INT_MAX; + } + } + + return _mpd_cmp(a, b); +} + +/* + * Compare a and b, convert the the usual integer result to a decimal and + * store it in 'result'. For convenience, the integer result of the comparison + * is returned. Comparisons involving NaNs return NaN/INT_MAX. + */ +int +mpd_qcompare(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + int c; + + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(result, a, b, ctx, status)) { + return INT_MAX; + } + } + + c = _mpd_cmp(a, b); + _settriple(result, (c < 0), (c != 0), 0); + return c; +} + +/* Same as mpd_compare(), but signal for all NaNs, i.e. also for quiet NaNs. */ +int +mpd_qcompare_signal(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + int c; + + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(result, a, b, ctx, status)) { + *status |= MPD_Invalid_operation; + return INT_MAX; + } + } + + c = _mpd_cmp(a, b); + _settriple(result, (c < 0), (c != 0), 0); + return c; +} + +/* Compare the operands using a total order. */ +int +mpd_cmp_total(const mpd_t *a, const mpd_t *b) +{ + mpd_t aa, bb; + int nan_a, nan_b; + int c; + + if (mpd_sign(a) != mpd_sign(b)) { + return mpd_sign(b) - mpd_sign(a); + } + + + if (mpd_isnan(a)) { + c = 1; + if (mpd_isnan(b)) { + nan_a = (mpd_isqnan(a)) ? 1 : 0; + nan_b = (mpd_isqnan(b)) ? 1 : 0; + if (nan_b == nan_a) { + if (a->len > 0 && b->len > 0) { + _mpd_copy_shared(&aa, a); + _mpd_copy_shared(&bb, b); + aa.exp = bb.exp = 0; + /* compare payload */ + c = _mpd_cmp_abs(&aa, &bb); + } + else { + c = (a->len > 0) - (b->len > 0); + } + } + else { + c = nan_a - nan_b; + } + } + } + else if (mpd_isnan(b)) { + c = -1; + } + else { + c = _mpd_cmp_abs(a, b); + if (c == 0 && a->exp != b->exp) { + c = (a->exp < b->exp) ? -1 : 1; + } + } + + return c * mpd_arith_sign(a); +} + +/* + * Compare a and b according to a total order, convert the usual integer result + * to a decimal and store it in 'result'. For convenience, the integer result + * of the comparison is returned. + */ +int +mpd_compare_total(mpd_t *result, const mpd_t *a, const mpd_t *b) +{ + int c; + + c = mpd_cmp_total(a, b); + _settriple(result, (c < 0), (c != 0), 0); + return c; +} + +/* Compare the magnitude of the operands using a total order. */ +int +mpd_cmp_total_mag(const mpd_t *a, const mpd_t *b) +{ + mpd_t aa, bb; + + _mpd_copy_shared(&aa, a); + _mpd_copy_shared(&bb, b); + + mpd_set_positive(&aa); + mpd_set_positive(&bb); + + return mpd_cmp_total(&aa, &bb); +} + +/* + * Compare the magnitude of a and b according to a total order, convert the + * the usual integer result to a decimal and store it in 'result'. + * For convenience, the integer result of the comparison is returned. + */ +int +mpd_compare_total_mag(mpd_t *result, const mpd_t *a, const mpd_t *b) +{ + int c; + + c = mpd_cmp_total_mag(a, b); + _settriple(result, (c < 0), (c != 0), 0); + return c; +} + +/* Determine an ordering for operands that are numerically equal. */ +static inline int +_mpd_cmp_numequal(const mpd_t *a, const mpd_t *b) +{ + int sign_a, sign_b; + int c; + + sign_a = mpd_sign(a); + sign_b = mpd_sign(b); + if (sign_a != sign_b) { + c = sign_b - sign_a; + } + else { + c = (a->exp < b->exp) ? -1 : 1; + c *= mpd_arith_sign(a); + } + + return c; +} + + +/******************************************************************************/ +/* Shifting the coefficient */ +/******************************************************************************/ + +/* + * Shift the coefficient of the operand to the left, no check for specials. + * Both operands may be the same pointer. If the result length has to be + * increased, mpd_qresize() might fail with MPD_Malloc_error. + */ +int +mpd_qshiftl(mpd_t *result, const mpd_t *a, mpd_ssize_t n, uint32_t *status) +{ + mpd_ssize_t size; + + assert(n >= 0); + + if (mpd_iszerocoeff(a) || n == 0) { + return mpd_qcopy(result, a, status); + } + + size = mpd_digits_to_size(a->digits+n); + if (!mpd_qresize(result, size, status)) { + return 0; /* result is NaN */ + } + + _mpd_baseshiftl(result->data, a->data, size, a->len, n); + + mpd_copy_flags(result, a); + result->len = size; + result->exp = a->exp; + result->digits = a->digits+n; + + return 1; +} + +/* Determine the rounding indicator if all digits of the coefficient are shifted + * out of the picture. */ +static mpd_uint_t +_mpd_get_rnd(const mpd_uint_t *data, mpd_ssize_t len, int use_msd) +{ + mpd_uint_t rnd = 0, rest = 0, word; + + word = data[len-1]; + /* special treatment for the most significant digit if shift == digits */ + if (use_msd) { + _mpd_divmod_pow10(&rnd, &rest, word, mpd_word_digits(word)-1); + if (len > 1 && rest == 0) { + rest = !_mpd_isallzero(data, len-1); + } + } + else { + rest = !_mpd_isallzero(data, len); + } + + return (rnd == 0 || rnd == 5) ? rnd + !!rest : rnd; +} + +/* + * Same as mpd_qshiftr(), but 'result' is a static array. It is the + * caller's responsibility to make sure that the array is big enough. + * The function cannot fail. + */ +mpd_uint_t +mpd_qsshiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n) +{ + mpd_uint_t rnd; + mpd_ssize_t size; + + assert(n >= 0); + + if (mpd_iszerocoeff(a) || n == 0) { + mpd_qcopy_static(result, a); + return 0; + } + + if (n >= a->digits) { + rnd = _mpd_get_rnd(a->data, a->len, (n==a->digits)); + mpd_zerocoeff(result); + result->digits = 1; + size = 1; + } + else { + result->digits = a->digits-n; + size = mpd_digits_to_size(result->digits); + rnd = _mpd_baseshiftr(result->data, a->data, a->len, n); + } + + mpd_copy_flags(result, a); + result->exp = a->exp; + result->len = size; + + return rnd; +} + +/* + * Inplace shift of the coefficient to the right, no check for specials. + * Returns the rounding indicator for mpd_rnd_incr(). + * The function cannot fail. + */ +mpd_uint_t +mpd_qshiftr_inplace(mpd_t *result, mpd_ssize_t n) +{ + uint32_t dummy; + mpd_uint_t rnd; + mpd_ssize_t size; + + assert(n >= 0); + + if (mpd_iszerocoeff(result) || n == 0) { + return 0; + } + + if (n >= result->digits) { + rnd = _mpd_get_rnd(result->data, result->len, (n==result->digits)); + mpd_zerocoeff(result); + result->digits = 1; + size = 1; + } + else { + rnd = _mpd_baseshiftr(result->data, result->data, result->len, n); + result->digits -= n; + size = mpd_digits_to_size(result->digits); + /* reducing the size cannot fail */ + mpd_qresize(result, size, &dummy); + } + + result->len = size; + + return rnd; +} + +/* + * Shift the coefficient of the operand to the right, no check for specials. + * Both operands may be the same pointer. Returns the rounding indicator to + * be used by mpd_rnd_incr(). If the result length has to be increased, + * mpd_qcopy() or mpd_qresize() might fail with MPD_Malloc_error. In those + * cases, MPD_UINT_MAX is returned. + */ +mpd_uint_t +mpd_qshiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n, uint32_t *status) +{ + mpd_uint_t rnd; + mpd_ssize_t size; + + assert(n >= 0); + + if (mpd_iszerocoeff(a) || n == 0) { + if (!mpd_qcopy(result, a, status)) { + return MPD_UINT_MAX; + } + return 0; + } + + if (n >= a->digits) { + rnd = _mpd_get_rnd(a->data, a->len, (n==a->digits)); + mpd_zerocoeff(result); + result->digits = 1; + size = 1; + } + else { + result->digits = a->digits-n; + size = mpd_digits_to_size(result->digits); + if (result == a) { + rnd = _mpd_baseshiftr(result->data, a->data, a->len, n); + /* reducing the size cannot fail */ + mpd_qresize(result, size, status); + } + else { + if (!mpd_qresize(result, size, status)) { + return MPD_UINT_MAX; + } + rnd = _mpd_baseshiftr(result->data, a->data, a->len, n); + } + } + + mpd_copy_flags(result, a); + result->exp = a->exp; + result->len = size; + + return rnd; +} + + +/******************************************************************************/ +/* Miscellaneous operations */ +/******************************************************************************/ + +/* Logical And */ +void +mpd_qand(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + const mpd_t *big = a, *small = b; + mpd_uint_t x, y, z, xbit, ybit; + int k, mswdigits; + mpd_ssize_t i; + + if (mpd_isspecial(a) || mpd_isspecial(b) || + mpd_isnegative(a) || mpd_isnegative(b) || + a->exp != 0 || b->exp != 0) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + if (b->digits > a->digits) { + big = b; + small = a; + } + if (!mpd_qresize(result, big->len, status)) { + return; + } + + + /* full words */ + for (i = 0; i < small->len-1; i++) { + x = small->data[i]; + y = big->data[i]; + z = 0; + for (k = 0; k < MPD_RDIGITS; k++) { + xbit = x % 10; + x /= 10; + ybit = y % 10; + y /= 10; + if (xbit > 1 || ybit > 1) { + goto invalid_operation; + } + z += (xbit&ybit) ? mpd_pow10[k] : 0; + } + result->data[i] = z; + } + /* most significant word of small */ + x = small->data[i]; + y = big->data[i]; + z = 0; + mswdigits = mpd_word_digits(x); + for (k = 0; k < mswdigits; k++) { + xbit = x % 10; + x /= 10; + ybit = y % 10; + y /= 10; + if (xbit > 1 || ybit > 1) { + goto invalid_operation; + } + z += (xbit&ybit) ? mpd_pow10[k] : 0; + } + result->data[i++] = z; + + /* scan the rest of y for digit > 1 */ + for (; k < MPD_RDIGITS; k++) { + ybit = y % 10; + y /= 10; + if (ybit > 1) { + goto invalid_operation; + } + } + /* scan the rest of big for digit > 1 */ + for (; i < big->len; i++) { + y = big->data[i]; + for (k = 0; k < MPD_RDIGITS; k++) { + ybit = y % 10; + y /= 10; + if (ybit > 1) { + goto invalid_operation; + } + } + } + + mpd_clear_flags(result); + result->exp = 0; + result->len = _mpd_real_size(result->data, small->len); + mpd_qresize(result, result->len, status); + mpd_setdigits(result); + _mpd_cap(result, ctx); + return; + +invalid_operation: + mpd_seterror(result, MPD_Invalid_operation, status); +} + +/* Class of an operand. Returns a pointer to the constant name. */ +const char * +mpd_class(const mpd_t *a, const mpd_context_t *ctx) +{ + if (mpd_isnan(a)) { + if (mpd_isqnan(a)) + return "NaN"; + else + return "sNaN"; + } + else if (mpd_ispositive(a)) { + if (mpd_isinfinite(a)) + return "+Infinity"; + else if (mpd_iszero(a)) + return "+Zero"; + else if (mpd_isnormal(a, ctx)) + return "+Normal"; + else + return "+Subnormal"; + } + else { + if (mpd_isinfinite(a)) + return "-Infinity"; + else if (mpd_iszero(a)) + return "-Zero"; + else if (mpd_isnormal(a, ctx)) + return "-Normal"; + else + return "-Subnormal"; + } +} + +/* Logical Xor */ +void +mpd_qinvert(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_uint_t x, z, xbit; + mpd_ssize_t i, digits, len; + mpd_ssize_t q, r; + int k; + + if (mpd_isspecial(a) || mpd_isnegative(a) || a->exp != 0) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + digits = (a->digits < ctx->prec) ? ctx->prec : a->digits; + _mpd_idiv_word(&q, &r, digits, MPD_RDIGITS); + len = (r == 0) ? q : q+1; + if (!mpd_qresize(result, len, status)) { + return; + } + + for (i = 0; i < len; i++) { + x = (i < a->len) ? a->data[i] : 0; + z = 0; + for (k = 0; k < MPD_RDIGITS; k++) { + xbit = x % 10; + x /= 10; + if (xbit > 1) { + goto invalid_operation; + } + z += !xbit ? mpd_pow10[k] : 0; + } + result->data[i] = z; + } + + mpd_clear_flags(result); + result->exp = 0; + result->len = _mpd_real_size(result->data, len); + mpd_qresize(result, result->len, status); + mpd_setdigits(result); + _mpd_cap(result, ctx); + return; + +invalid_operation: + mpd_seterror(result, MPD_Invalid_operation, status); +} + +/* Exponent of the magnitude of the most significant digit of the operand. */ +void +mpd_qlogb(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + if (mpd_isspecial(a)) { + if (mpd_qcheck_nan(result, a, ctx, status)) { + return; + } + mpd_setspecial(result, MPD_POS, MPD_INF); + } + else if (mpd_iszerocoeff(a)) { + mpd_setspecial(result, MPD_NEG, MPD_INF); + *status |= MPD_Division_by_zero; + } + else { + mpd_qset_ssize(result, mpd_adjexp(a), ctx, status); + } +} + +/* Logical Or */ +void +mpd_qor(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + const mpd_t *big = a, *small = b; + mpd_uint_t x, y, z, xbit, ybit; + int k, mswdigits; + mpd_ssize_t i; + + if (mpd_isspecial(a) || mpd_isspecial(b) || + mpd_isnegative(a) || mpd_isnegative(b) || + a->exp != 0 || b->exp != 0) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + if (b->digits > a->digits) { + big = b; + small = a; + } + if (!mpd_qresize(result, big->len, status)) { + return; + } + + + /* full words */ + for (i = 0; i < small->len-1; i++) { + x = small->data[i]; + y = big->data[i]; + z = 0; + for (k = 0; k < MPD_RDIGITS; k++) { + xbit = x % 10; + x /= 10; + ybit = y % 10; + y /= 10; + if (xbit > 1 || ybit > 1) { + goto invalid_operation; + } + z += (xbit|ybit) ? mpd_pow10[k] : 0; + } + result->data[i] = z; + } + /* most significant word of small */ + x = small->data[i]; + y = big->data[i]; + z = 0; + mswdigits = mpd_word_digits(x); + for (k = 0; k < mswdigits; k++) { + xbit = x % 10; + x /= 10; + ybit = y % 10; + y /= 10; + if (xbit > 1 || ybit > 1) { + goto invalid_operation; + } + z += (xbit|ybit) ? mpd_pow10[k] : 0; + } + + /* scan and copy the rest of y for digit > 1 */ + for (; k < MPD_RDIGITS; k++) { + ybit = y % 10; + y /= 10; + if (ybit > 1) { + goto invalid_operation; + } + z += ybit*mpd_pow10[k]; + } + result->data[i++] = z; + /* scan and copy the rest of big for digit > 1 */ + for (; i < big->len; i++) { + y = big->data[i]; + for (k = 0; k < MPD_RDIGITS; k++) { + ybit = y % 10; + y /= 10; + if (ybit > 1) { + goto invalid_operation; + } + } + result->data[i] = big->data[i]; + } + + mpd_clear_flags(result); + result->exp = 0; + result->len = _mpd_real_size(result->data, big->len); + mpd_qresize(result, result->len, status); + mpd_setdigits(result); + _mpd_cap(result, ctx); + return; + +invalid_operation: + mpd_seterror(result, MPD_Invalid_operation, status); +} + +/* + * Rotate the coefficient of a by b->data digits. b must be an integer with + * exponent 0. + */ +void +mpd_qrotate(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + uint32_t workstatus = 0; + MPD_NEW_STATIC(tmp,0,0,0,0); + MPD_NEW_STATIC(big,0,0,0,0); + MPD_NEW_STATIC(small,0,0,0,0); + mpd_ssize_t n, lshift, rshift; + + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(result, a, b, ctx, status)) { + return; + } + } + if (b->exp != 0 || mpd_isinfinite(b)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + n = mpd_qget_ssize(b, &workstatus); + if (workstatus&MPD_Invalid_operation) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + if (n > ctx->prec || n < -ctx->prec) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + if (mpd_isinfinite(a)) { + mpd_qcopy(result, a, status); + return; + } + + if (n >= 0) { + lshift = n; + rshift = ctx->prec-n; + } + else { + lshift = ctx->prec+n; + rshift = -n; + } + + if (a->digits > ctx->prec) { + if (!mpd_qcopy(&tmp, a, status)) { + mpd_seterror(result, MPD_Malloc_error, status); + goto finish; + } + _mpd_cap(&tmp, ctx); + a = &tmp; + } + + if (!mpd_qshiftl(&big, a, lshift, status)) { + mpd_seterror(result, MPD_Malloc_error, status); + goto finish; + } + _mpd_cap(&big, ctx); + + if (mpd_qshiftr(&small, a, rshift, status) == MPD_UINT_MAX) { + mpd_seterror(result, MPD_Malloc_error, status); + goto finish; + } + _mpd_qadd(result, &big, &small, ctx, status); + + +finish: + mpd_del(&tmp); + mpd_del(&big); + mpd_del(&small); +} + +/* + * b must be an integer with exponent 0 and in the range +-2*(emax + prec). + * XXX: In my opinion +-(2*emax + prec) would be more sensible. + * The result is a with the value of b added to its exponent. + */ +void +mpd_qscaleb(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + uint32_t workstatus = 0; + mpd_uint_t n, maxjump; +#ifndef LEGACY_COMPILER + int64_t exp; +#else + mpd_uint_t x; + int x_sign, n_sign; + mpd_ssize_t exp; +#endif + + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(result, a, b, ctx, status)) { + return; + } + } + if (b->exp != 0 || mpd_isinfinite(b)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + n = mpd_qabs_uint(b, &workstatus); + /* the spec demands this */ + maxjump = 2 * (mpd_uint_t)(ctx->emax + ctx->prec); + + if (n > maxjump || workstatus&MPD_Invalid_operation) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + if (mpd_isinfinite(a)) { + mpd_qcopy(result, a, status); + return; + } + +#ifndef LEGACY_COMPILER + exp = a->exp + (int64_t)n * mpd_arith_sign(b); + exp = (exp > MPD_EXP_INF) ? MPD_EXP_INF : exp; + exp = (exp < MPD_EXP_CLAMP) ? MPD_EXP_CLAMP : exp; +#else + x = (a->exp < 0) ? -a->exp : a->exp; + x_sign = (a->exp < 0) ? 1 : 0; + n_sign = mpd_isnegative(b) ? 1 : 0; + + if (x_sign == n_sign) { + x = x + n; + if (x < n) x = MPD_UINT_MAX; + } + else { + x_sign = (x >= n) ? x_sign : n_sign; + x = (x >= n) ? x - n : n - x; + } + if (!x_sign && x > MPD_EXP_INF) x = MPD_EXP_INF; + if (x_sign && x > -MPD_EXP_CLAMP) x = -MPD_EXP_CLAMP; + exp = x_sign ? -((mpd_ssize_t)x) : (mpd_ssize_t)x; +#endif + + mpd_qcopy(result, a, status); + result->exp = (mpd_ssize_t)exp; + + mpd_qfinalize(result, ctx, status); +} + +/* + * Shift the coefficient by n digits, positive n is a left shift. In the case + * of a left shift, the result is decapitated to fit the context precision. If + * you don't want that, use mpd_shiftl(). + */ +void +mpd_qshiftn(mpd_t *result, const mpd_t *a, mpd_ssize_t n, const mpd_context_t *ctx, + uint32_t *status) +{ + if (mpd_isspecial(a)) { + if (mpd_qcheck_nan(result, a, ctx, status)) { + return; + } + mpd_qcopy(result, a, status); + return; + } + + if (n >= 0 && n <= ctx->prec) { + mpd_qshiftl(result, a, n, status); + _mpd_cap(result, ctx); + } + else if (n < 0 && n >= -ctx->prec) { + if (!mpd_qcopy(result, a, status)) { + return; + } + _mpd_cap(result, ctx); + mpd_qshiftr_inplace(result, -n); + } + else { + mpd_seterror(result, MPD_Invalid_operation, status); + } +} + +/* + * Same as mpd_shiftn(), but the shift is specified by the decimal b, which + * must be an integer with a zero exponent. Infinities remain infinities. + */ +void +mpd_qshift(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, + uint32_t *status) +{ + uint32_t workstatus = 0; + mpd_ssize_t n; + + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(result, a, b, ctx, status)) { + return; + } + } + if (b->exp != 0 || mpd_isinfinite(b)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + n = mpd_qget_ssize(b, &workstatus); + if (workstatus&MPD_Invalid_operation) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + if (n > ctx->prec || n < -ctx->prec) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + if (mpd_isinfinite(a)) { + mpd_qcopy(result, a, status); + return; + } + + if (n >= 0) { + mpd_qshiftl(result, a, n, status); + _mpd_cap(result, ctx); + } + else { + if (!mpd_qcopy(result, a, status)) { + return; + } + _mpd_cap(result, ctx); + mpd_qshiftr_inplace(result, -n); + } +} + +/* Logical Xor */ +void +mpd_qxor(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + const mpd_t *big = a, *small = b; + mpd_uint_t x, y, z, xbit, ybit; + int k, mswdigits; + mpd_ssize_t i; + + if (mpd_isspecial(a) || mpd_isspecial(b) || + mpd_isnegative(a) || mpd_isnegative(b) || + a->exp != 0 || b->exp != 0) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + if (b->digits > a->digits) { + big = b; + small = a; + } + if (!mpd_qresize(result, big->len, status)) { + return; + } + + + /* full words */ + for (i = 0; i < small->len-1; i++) { + x = small->data[i]; + y = big->data[i]; + z = 0; + for (k = 0; k < MPD_RDIGITS; k++) { + xbit = x % 10; + x /= 10; + ybit = y % 10; + y /= 10; + if (xbit > 1 || ybit > 1) { + goto invalid_operation; + } + z += (xbit^ybit) ? mpd_pow10[k] : 0; + } + result->data[i] = z; + } + /* most significant word of small */ + x = small->data[i]; + y = big->data[i]; + z = 0; + mswdigits = mpd_word_digits(x); + for (k = 0; k < mswdigits; k++) { + xbit = x % 10; + x /= 10; + ybit = y % 10; + y /= 10; + if (xbit > 1 || ybit > 1) { + goto invalid_operation; + } + z += (xbit^ybit) ? mpd_pow10[k] : 0; + } + + /* scan and copy the rest of y for digit > 1 */ + for (; k < MPD_RDIGITS; k++) { + ybit = y % 10; + y /= 10; + if (ybit > 1) { + goto invalid_operation; + } + z += ybit*mpd_pow10[k]; + } + result->data[i++] = z; + /* scan and copy the rest of big for digit > 1 */ + for (; i < big->len; i++) { + y = big->data[i]; + for (k = 0; k < MPD_RDIGITS; k++) { + ybit = y % 10; + y /= 10; + if (ybit > 1) { + goto invalid_operation; + } + } + result->data[i] = big->data[i]; + } + + mpd_clear_flags(result); + result->exp = 0; + result->len = _mpd_real_size(result->data, big->len); + mpd_qresize(result, result->len, status); + mpd_setdigits(result); + _mpd_cap(result, ctx); + return; + +invalid_operation: + mpd_seterror(result, MPD_Invalid_operation, status); +} + + +/******************************************************************************/ +/* Arithmetic operations */ +/******************************************************************************/ + +/* + * The absolute value of a. If a is negative, the result is the same + * as the result of the minus operation. Otherwise, the result is the + * result of the plus operation. + */ +void +mpd_qabs(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + if (mpd_isspecial(a)) { + if (mpd_qcheck_nan(result, a, ctx, status)) { + return; + } + } + + if (mpd_isnegative(a)) { + mpd_qminus(result, a, ctx, status); + } + else { + mpd_qplus(result, a, ctx, status); + } + + mpd_qfinalize(result, ctx, status); +} + +static inline void +_mpd_ptrswap(mpd_t **a, mpd_t **b) +{ + mpd_t *t = *a; + *a = *b; + *b = t; +} + +/* Add or subtract infinities. */ +static void +_mpd_qaddsub_inf(mpd_t *result, const mpd_t *a, const mpd_t *b, uint8_t sign_b, + uint32_t *status) +{ + if (mpd_isinfinite(a)) { + if (mpd_sign(a) != sign_b && mpd_isinfinite(b)) { + mpd_seterror(result, MPD_Invalid_operation, status); + } + else { + mpd_setspecial(result, mpd_sign(a), MPD_INF); + } + return; + } + assert(mpd_isinfinite(b)); + mpd_setspecial(result, sign_b, MPD_INF); +} + +/* Add or subtract non-special numbers. */ +static void +_mpd_qaddsub(mpd_t *result, const mpd_t *a, const mpd_t *b, uint8_t sign_b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_t *big, *small; + MPD_NEW_STATIC(big_aligned,0,0,0,0); + MPD_NEW_CONST(tiny,0,0,0,1,1,1); + mpd_uint_t carry; + mpd_ssize_t newsize, shift; + mpd_ssize_t exp, i; + int swap = 0; + + + /* compare exponents */ + big = (mpd_t *)a; small = (mpd_t *)b; + if (big->exp != small->exp) { + if (small->exp > big->exp) { + _mpd_ptrswap(&big, &small); + swap++; + } + if (!mpd_iszerocoeff(big)) { + /* Test for adjexp(small) + big->digits < adjexp(big), if big-digits > prec + * Test for adjexp(small) + prec + 1 < adjexp(big), if big-digits <= prec + * If true, the magnitudes of the numbers are so far apart that one can as + * well add or subtract 1*10**big->exp. */ + exp = big->exp - 1; + exp += (big->digits > ctx->prec) ? 0 : big->digits-ctx->prec-1; + if (mpd_adjexp(small) < exp) { + mpd_copy_flags(&tiny, small); + tiny.exp = exp; + tiny.digits = 1; + tiny.len = 1; + tiny.data[0] = mpd_iszerocoeff(small) ? 0 : 1; + small = &tiny; + } + /* this cannot wrap: the difference is positive and <= maxprec+1 */ + shift = big->exp - small->exp; + if (!mpd_qshiftl(&big_aligned, big, shift, status)) { + mpd_seterror(result, MPD_Malloc_error, status); + goto finish; + } + big = &big_aligned; + } + } + result->exp = small->exp; + + + /* compare length of coefficients */ + if (big->len < small->len) { + _mpd_ptrswap(&big, &small); + swap++; + } + + newsize = big->len; + if (!mpd_qresize(result, newsize, status)) { + goto finish; + } + + if (mpd_sign(a) == sign_b) { + + carry = _mpd_baseadd(result->data, big->data, small->data, + big->len, small->len); + + if (carry) { + newsize = big->len + 1; + if (!mpd_qresize(result, newsize, status)) { + goto finish; + } + result->data[newsize-1] = carry; + } + + result->len = newsize; + mpd_set_flags(result, sign_b); + } + else { + if (big->len == small->len) { + for (i=big->len-1; i >= 0; --i) { + if (big->data[i] != small->data[i]) { + if (big->data[i] < small->data[i]) { + _mpd_ptrswap(&big, &small); + swap++; + } + break; + } + } + } + + _mpd_basesub(result->data, big->data, small->data, + big->len, small->len); + newsize = _mpd_real_size(result->data, big->len); + /* resize to smaller cannot fail */ + (void)mpd_qresize(result, newsize, status); + + result->len = newsize; + sign_b = (swap & 1) ? sign_b : mpd_sign(a); + mpd_set_flags(result, sign_b); + + if (mpd_iszerocoeff(result)) { + mpd_set_positive(result); + if (ctx->round == MPD_ROUND_FLOOR) { + mpd_set_negative(result); + } + } + } + + mpd_setdigits(result); + +finish: + mpd_del(&big_aligned); +} + +/* Add a and b. No specials, no finalizing. */ +static void +_mpd_qadd(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + _mpd_qaddsub(result, a, b, mpd_sign(b), ctx, status); +} + +/* Subtract b from a. No specials, no finalizing. */ +static void +_mpd_qsub(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + _mpd_qaddsub(result, a, b, !mpd_sign(b), ctx, status); +} + +/* Add a and b. */ +void +mpd_qadd(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(result, a, b, ctx, status)) { + return; + } + _mpd_qaddsub_inf(result, a, b, mpd_sign(b), status); + return; + } + + _mpd_qaddsub(result, a, b, mpd_sign(b), ctx, status); + mpd_qfinalize(result, ctx, status); +} + +/* Subtract b from a. */ +void +mpd_qsub(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(result, a, b, ctx, status)) { + return; + } + _mpd_qaddsub_inf(result, a, b, !mpd_sign(b), status); + return; + } + + _mpd_qaddsub(result, a, b, !mpd_sign(b), ctx, status); + mpd_qfinalize(result, ctx, status); +} + +/* Add decimal and mpd_ssize_t. */ +void +mpd_qadd_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t maxcontext; + MPD_NEW_STATIC(bb,0,0,0,0); + + mpd_maxcontext(&maxcontext); + mpd_qsset_ssize(&bb, b, &maxcontext, status); + mpd_qadd(result, a, &bb, ctx, status); + mpd_del(&bb); +} + +/* Add decimal and mpd_uint_t. */ +void +mpd_qadd_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t maxcontext; + MPD_NEW_STATIC(bb,0,0,0,0); + + mpd_maxcontext(&maxcontext); + mpd_qsset_uint(&bb, b, &maxcontext, status); + mpd_qadd(result, a, &bb, ctx, status); + mpd_del(&bb); +} + +/* Subtract mpd_ssize_t from decimal. */ +void +mpd_qsub_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t maxcontext; + MPD_NEW_STATIC(bb,0,0,0,0); + + mpd_maxcontext(&maxcontext); + mpd_qsset_ssize(&bb, b, &maxcontext, status); + mpd_qsub(result, a, &bb, ctx, status); + mpd_del(&bb); +} + +/* Subtract mpd_uint_t from decimal. */ +void +mpd_qsub_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t maxcontext; + MPD_NEW_STATIC(bb,0,0,0,0); + + mpd_maxcontext(&maxcontext); + mpd_qsset_uint(&bb, b, &maxcontext, status); + mpd_qsub(result, a, &bb, ctx, status); + mpd_del(&bb); +} + +/* Add decimal and int32_t. */ +void +mpd_qadd_i32(mpd_t *result, const mpd_t *a, int32_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qadd_ssize(result, a, b, ctx, status); +} + +/* Add decimal and uint32_t. */ +void +mpd_qadd_u32(mpd_t *result, const mpd_t *a, uint32_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qadd_uint(result, a, b, ctx, status); +} + +#ifdef CONFIG_64 +/* Add decimal and int64_t. */ +void +mpd_qadd_i64(mpd_t *result, const mpd_t *a, int64_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qadd_ssize(result, a, b, ctx, status); +} + +/* Add decimal and uint64_t. */ +void +mpd_qadd_u64(mpd_t *result, const mpd_t *a, uint64_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qadd_uint(result, a, b, ctx, status); +} +#endif + +/* Subtract int32_t from decimal. */ +void +mpd_qsub_i32(mpd_t *result, const mpd_t *a, int32_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qsub_ssize(result, a, b, ctx, status); +} + +/* Subtract uint32_t from decimal. */ +void +mpd_qsub_u32(mpd_t *result, const mpd_t *a, uint32_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qsub_uint(result, a, b, ctx, status); +} + +#ifdef CONFIG_64 +/* Subtract int64_t from decimal. */ +void +mpd_qsub_i64(mpd_t *result, const mpd_t *a, int64_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qsub_ssize(result, a, b, ctx, status); +} + +/* Subtract uint64_t from decimal. */ +void +mpd_qsub_u64(mpd_t *result, const mpd_t *a, uint64_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qsub_uint(result, a, b, ctx, status); +} +#endif + + +/* Divide infinities. */ +static void +_mpd_qdiv_inf(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + if (mpd_isinfinite(a)) { + if (mpd_isinfinite(b)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + mpd_setspecial(result, mpd_sign(a)^mpd_sign(b), MPD_INF); + return; + } + assert(mpd_isinfinite(b)); + _settriple(result, mpd_sign(a)^mpd_sign(b), 0, mpd_etiny(ctx)); + *status |= MPD_Clamped; +} + +enum {NO_IDEAL_EXP, SET_IDEAL_EXP}; +/* Divide a by b. */ +static void +_mpd_qdiv(int action, mpd_t *q, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + MPD_NEW_STATIC(aligned,0,0,0,0); + mpd_uint_t ld; + mpd_ssize_t shift, exp, tz; + mpd_ssize_t newsize; + mpd_ssize_t ideal_exp; + mpd_uint_t rem; + uint8_t sign_a = mpd_sign(a); + uint8_t sign_b = mpd_sign(b); + + + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(q, a, b, ctx, status)) { + return; + } + _mpd_qdiv_inf(q, a, b, ctx, status); + return; + } + if (mpd_iszerocoeff(b)) { + if (mpd_iszerocoeff(a)) { + mpd_seterror(q, MPD_Division_undefined, status); + } + else { + mpd_setspecial(q, sign_a^sign_b, MPD_INF); + *status |= MPD_Division_by_zero; + } + return; + } + if (mpd_iszerocoeff(a)) { + exp = a->exp - b->exp; + _settriple(q, sign_a^sign_b, 0, exp); + mpd_qfinalize(q, ctx, status); + return; + } + + shift = (b->digits - a->digits) + ctx->prec + 1; + ideal_exp = a->exp - b->exp; + exp = ideal_exp - shift; + if (shift > 0) { + if (!mpd_qshiftl(&aligned, a, shift, status)) { + mpd_seterror(q, MPD_Malloc_error, status); + goto finish; + } + a = &aligned; + } + else if (shift < 0) { + shift = -shift; + if (!mpd_qshiftl(&aligned, b, shift, status)) { + mpd_seterror(q, MPD_Malloc_error, status); + goto finish; + } + b = &aligned; + } + + + newsize = a->len - b->len + 1; + if ((q != b && q != a) || (q == b && newsize > b->len)) { + if (!mpd_qresize(q, newsize, status)) { + mpd_seterror(q, MPD_Malloc_error, status); + goto finish; + } + } + + + if (b->len == 1) { + rem = _mpd_shortdiv(q->data, a->data, a->len, b->data[0]); + } + else if (a->len < 2*MPD_NEWTONDIV_CUTOFF && + b->len < MPD_NEWTONDIV_CUTOFF) { + int ret = _mpd_basedivmod(q->data, NULL, a->data, b->data, + a->len, b->len); + if (ret < 0) { + mpd_seterror(q, MPD_Malloc_error, status); + goto finish; + } + rem = ret; + } + else { + MPD_NEW_STATIC(r,0,0,0,0); + _mpd_qbarrett_divmod(q, &r, a, b, status); + if (mpd_isspecial(q) || mpd_isspecial(&r)) { + mpd_del(&r); + goto finish; + } + rem = !mpd_iszerocoeff(&r); + mpd_del(&r); + newsize = q->len; + } + + newsize = _mpd_real_size(q->data, newsize); + /* resize to smaller cannot fail */ + mpd_qresize(q, newsize, status); + q->len = newsize; + mpd_setdigits(q); + + shift = ideal_exp - exp; + if (rem) { + ld = mpd_lsd(q->data[0]); + if (ld == 0 || ld == 5) { + q->data[0] += 1; + } + } + else if (action == SET_IDEAL_EXP && shift > 0) { + tz = mpd_trail_zeros(q); + shift = (tz > shift) ? shift : tz; + mpd_qshiftr_inplace(q, shift); + exp += shift; + } + + mpd_set_flags(q, sign_a^sign_b); + q->exp = exp; + + +finish: + mpd_del(&aligned); + mpd_qfinalize(q, ctx, status); +} + +/* Divide a by b. */ +void +mpd_qdiv(mpd_t *q, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + _mpd_qdiv(SET_IDEAL_EXP, q, a, b, ctx, status); +} + +/* Internal function. */ +static void +_mpd_qdivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + MPD_NEW_STATIC(aligned,0,0,0,0); + mpd_ssize_t qsize, rsize; + mpd_ssize_t ideal_exp, expdiff, shift; + uint8_t sign_a = mpd_sign(a); + uint8_t sign_ab = mpd_sign(a)^mpd_sign(b); + + + ideal_exp = (a->exp > b->exp) ? b->exp : a->exp; + if (mpd_iszerocoeff(a)) { + if (!mpd_qcopy(r, a, status)) { + goto nanresult; /* GCOV_NOT_REACHED */ + } + r->exp = ideal_exp; + _settriple(q, sign_ab, 0, 0); + return; + } + + expdiff = mpd_adjexp(a) - mpd_adjexp(b); + if (expdiff < 0) { + if (a->exp > b->exp) { + /* positive and less than b->digits - a->digits */ + shift = a->exp - b->exp; + if (!mpd_qshiftl(r, a, shift, status)) { + goto nanresult; + } + r->exp = ideal_exp; + } + else { + if (!mpd_qcopy(r, a, status)) { + goto nanresult; + } + } + _settriple(q, sign_ab, 0, 0); + return; + } + if (expdiff > ctx->prec) { + *status |= MPD_Division_impossible; + goto nanresult; + } + + + /* + * At this point we have: + * (1) 0 <= a->exp + a->digits - b->exp - b->digits <= prec + * (2) a->exp - b->exp >= b->digits - a->digits + * (3) a->exp - b->exp <= prec + b->digits - a->digits + */ + if (a->exp != b->exp) { + shift = a->exp - b->exp; + if (shift > 0) { + /* by (3), after the shift a->digits <= prec + b->digits */ + if (!mpd_qshiftl(&aligned, a, shift, status)) { + goto nanresult; + } + a = &aligned; + } + else { + shift = -shift; + /* by (2), after the shift b->digits <= a->digits */ + if (!mpd_qshiftl(&aligned, b, shift, status)) { + goto nanresult; + } + b = &aligned; + } + } + + + qsize = a->len - b->len + 1; + if (!(q == a && qsize < a->len) && !(q == b && qsize < b->len)) { + if (!mpd_qresize(q, qsize, status)) { + goto nanresult; + } + } + + rsize = b->len; + if (!(r == a && rsize < a->len)) { + if (!mpd_qresize(r, rsize, status)) { + goto nanresult; + } + } + + if (b->len == 1) { + if (a->len == 1) { + _mpd_div_word(&q->data[0], &r->data[0], a->data[0], b->data[0]); + } + else { + r->data[0] = _mpd_shortdiv(q->data, a->data, a->len, b->data[0]); + } + } + else if (a->len < 2*MPD_NEWTONDIV_CUTOFF && + b->len < MPD_NEWTONDIV_CUTOFF) { + int ret; + ret = _mpd_basedivmod(q->data, r->data, a->data, b->data, + a->len, b->len); + if (ret == -1) { + *status |= MPD_Malloc_error; + goto nanresult; + } + } + else { + _mpd_qbarrett_divmod(q, r, a, b, status); + if (mpd_isspecial(q) || mpd_isspecial(r)) { + goto nanresult; + } + if (mpd_isinfinite(q) || q->digits > ctx->prec) { + *status |= MPD_Division_impossible; + goto nanresult; + } + qsize = q->len; + rsize = r->len; + } + + qsize = _mpd_real_size(q->data, qsize); + /* resize to smaller cannot fail */ + mpd_qresize(q, qsize, status); + q->len = qsize; + mpd_setdigits(q); + mpd_set_flags(q, sign_ab); + q->exp = 0; + if (q->digits > ctx->prec) { + *status |= MPD_Division_impossible; + goto nanresult; + } + + rsize = _mpd_real_size(r->data, rsize); + /* resize to smaller cannot fail */ + mpd_qresize(r, rsize, status); + r->len = rsize; + mpd_setdigits(r); + mpd_set_flags(r, sign_a); + r->exp = ideal_exp; + +out: + mpd_del(&aligned); + return; + +nanresult: + mpd_setspecial(q, MPD_POS, MPD_NAN); + mpd_setspecial(r, MPD_POS, MPD_NAN); + goto out; +} + +/* Integer division with remainder. */ +void +mpd_qdivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + uint8_t sign = mpd_sign(a)^mpd_sign(b); + + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(q, a, b, ctx, status)) { + mpd_qcopy(r, q, status); + return; + } + if (mpd_isinfinite(a)) { + if (mpd_isinfinite(b)) { + mpd_setspecial(q, MPD_POS, MPD_NAN); + } + else { + mpd_setspecial(q, sign, MPD_INF); + } + mpd_setspecial(r, MPD_POS, MPD_NAN); + *status |= MPD_Invalid_operation; + return; + } + if (mpd_isinfinite(b)) { + if (!mpd_qcopy(r, a, status)) { + mpd_seterror(q, MPD_Malloc_error, status); + return; + } + mpd_qfinalize(r, ctx, status); + _settriple(q, sign, 0, 0); + return; + } + /* debug */ + abort(); /* GCOV_NOT_REACHED */ + } + if (mpd_iszerocoeff(b)) { + if (mpd_iszerocoeff(a)) { + mpd_setspecial(q, MPD_POS, MPD_NAN); + mpd_setspecial(r, MPD_POS, MPD_NAN); + *status |= MPD_Division_undefined; + } + else { + mpd_setspecial(q, sign, MPD_INF); + mpd_setspecial(r, MPD_POS, MPD_NAN); + *status |= (MPD_Division_by_zero|MPD_Invalid_operation); + } + return; + } + + _mpd_qdivmod(q, r, a, b, ctx, status); + mpd_qfinalize(q, ctx, status); + mpd_qfinalize(r, ctx, status); +} + +void +mpd_qdivint(mpd_t *q, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + MPD_NEW_STATIC(r,0,0,0,0); + uint8_t sign = mpd_sign(a)^mpd_sign(b); + + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(q, a, b, ctx, status)) { + return; + } + if (mpd_isinfinite(a) && mpd_isinfinite(b)) { + mpd_seterror(q, MPD_Invalid_operation, status); + return; + } + if (mpd_isinfinite(a)) { + mpd_setspecial(q, sign, MPD_INF); + return; + } + if (mpd_isinfinite(b)) { + _settriple(q, sign, 0, 0); + return; + } + /* debug */ + abort(); /* GCOV_NOT_REACHED */ + } + if (mpd_iszerocoeff(b)) { + if (mpd_iszerocoeff(a)) { + mpd_seterror(q, MPD_Division_undefined, status); + } + else { + mpd_setspecial(q, sign, MPD_INF); + *status |= MPD_Division_by_zero; + } + return; + } + + + _mpd_qdivmod(q, &r, a, b, ctx, status); + mpd_del(&r); + mpd_qfinalize(q, ctx, status); +} + +/* Divide decimal by mpd_ssize_t. */ +void +mpd_qdiv_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t maxcontext; + MPD_NEW_STATIC(bb,0,0,0,0); + + mpd_maxcontext(&maxcontext); + mpd_qsset_ssize(&bb, b, &maxcontext, status); + mpd_qdiv(result, a, &bb, ctx, status); + mpd_del(&bb); +} + +/* Divide decimal by mpd_uint_t. */ +void +mpd_qdiv_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t maxcontext; + MPD_NEW_STATIC(bb,0,0,0,0); + + mpd_maxcontext(&maxcontext); + mpd_qsset_uint(&bb, b, &maxcontext, status); + mpd_qdiv(result, a, &bb, ctx, status); + mpd_del(&bb); +} + +/* Divide decimal by int32_t. */ +void +mpd_qdiv_i32(mpd_t *result, const mpd_t *a, int32_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qdiv_ssize(result, a, b, ctx, status); +} + +/* Divide decimal by uint32_t. */ +void +mpd_qdiv_u32(mpd_t *result, const mpd_t *a, uint32_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qdiv_uint(result, a, b, ctx, status); +} + +#ifdef CONFIG_64 +/* Divide decimal by int64_t. */ +void +mpd_qdiv_i64(mpd_t *result, const mpd_t *a, int64_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qdiv_ssize(result, a, b, ctx, status); +} + +/* Divide decimal by uint64_t. */ +void +mpd_qdiv_u64(mpd_t *result, const mpd_t *a, uint64_t b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_qdiv_uint(result, a, b, ctx, status); +} +#endif + +#if defined(_MSC_VER) + /* conversion from 'double' to 'mpd_ssize_t', possible loss of data */ + #pragma warning(disable:4244) +#endif +/* + * Get the number of iterations for the Horner scheme in _mpd_qexp(). + */ +static inline mpd_ssize_t +_mpd_get_exp_iterations(const mpd_t *a, mpd_ssize_t prec) +{ + mpd_uint_t dummy; + mpd_uint_t msdigits; + double f; + + /* 9 is MPD_RDIGITS for 32 bit platforms */ + _mpd_get_msdigits(&dummy, &msdigits, a, 9); + f = ((double)msdigits + 1) / mpd_pow10[mpd_word_digits(msdigits)]; + +#ifdef CONFIG_64 + #ifdef USE_80BIT_LONG_DOUBLE + return ceill((1.435*(long double)prec - 1.182) + / log10l((long double)prec/f)); + #else + /* prec > floor((1ULL<<53) / 1.435) */ + if (prec > 6276793905742851LL) { + return MPD_SSIZE_MAX; + } + return ceil((1.435*(double)prec - 1.182) / log10((double)prec/f)); + #endif +#else /* CONFIG_32 */ + return ceil((1.435*(double)prec - 1.182) / log10((double)prec/f)); + #if defined(_MSC_VER) + #pragma warning(default:4244) + #endif +#endif +} + +/* + * Internal function, specials have been dealt with. + * + * The algorithm is from Hull&Abrham, Variable Precision Exponential Function, + * ACM Transactions on Mathematical Software, Vol. 12, No. 2, June 1986. + * + * Main differences: + * + * - The number of iterations for the Horner scheme is calculated using the + * C log10() function. + * + * - The analysis for early abortion has been adapted for the mpd_t + * ranges. + */ +static void +_mpd_qexp(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_context_t workctx; + MPD_NEW_STATIC(tmp,0,0,0,0); + MPD_NEW_STATIC(sum,0,0,0,0); + MPD_NEW_CONST(word,0,0,0,1,1,1); + mpd_ssize_t j, n, t; + + assert(!mpd_isspecial(a)); + + /* + * We are calculating e^x = e^(r*10^t) = (e^r)^(10^t), where r < 1 and t >= 0. + * + * If t > 0, we have: + * + * (1) 0.1 <= r < 1, so e^r >= e^0.1. Overflow in the final power operation + * will occur when (e^0.1)^(10^t) > 10^(emax+1). If we consider MAX_EMAX, + * this will happen for t > 10 (32 bit) or (t > 19) (64 bit). + * + * (2) -1 < r <= -0.1, so e^r > e^-1. Underflow in the final power operation + * will occur when (e^-1)^(10^t) < 10^(etiny-1). If we consider MIN_ETINY, + * this will also happen for t > 10 (32 bit) or (t > 19) (64 bit). + */ +#if defined(CONFIG_64) + #define MPD_EXP_MAX_T 19 +#elif defined(CONFIG_32) + #define MPD_EXP_MAX_T 10 +#endif + t = a->digits + a->exp; + t = (t > 0) ? t : 0; + if (t > MPD_EXP_MAX_T) { + if (mpd_ispositive(a)) { + mpd_setspecial(result, MPD_POS, MPD_INF); + *status |= MPD_Overflow|MPD_Inexact|MPD_Rounded; + } + else { + _settriple(result, MPD_POS, 0, mpd_etiny(ctx)); + *status |= (MPD_Inexact|MPD_Rounded|MPD_Subnormal| + MPD_Underflow|MPD_Clamped); + } + return; + } + + mpd_maxcontext(&workctx); + workctx.prec = ctx->prec + t + 2; + workctx.prec = (workctx.prec < 9) ? 9 : workctx.prec; + workctx.round = MPD_ROUND_HALF_EVEN; + + if ((n = _mpd_get_exp_iterations(a, workctx.prec)) == MPD_SSIZE_MAX) { + mpd_seterror(result, MPD_Invalid_operation, status); /* GCOV_UNLIKELY */ + goto finish; /* GCOV_UNLIKELY */ + } + + if (!mpd_qcopy(result, a, status)) { + goto finish; + } + result->exp -= t; + + _settriple(&sum, MPD_POS, 1, 0); + + for (j = n-1; j >= 1; j--) { + word.data[0] = j; + mpd_setdigits(&word); + mpd_qdiv(&tmp, result, &word, &workctx, &workctx.status); + mpd_qmul(&sum, &sum, &tmp, &workctx, &workctx.status); + mpd_qadd(&sum, &sum, &one, &workctx, &workctx.status); + } + +#ifdef CONFIG_64 + _mpd_qpow_uint(result, &sum, mpd_pow10[t], MPD_POS, &workctx, status); +#else + if (t <= MPD_MAX_POW10) { + _mpd_qpow_uint(result, &sum, mpd_pow10[t], MPD_POS, &workctx, status); + } + else { + t -= MPD_MAX_POW10; + _mpd_qpow_uint(&tmp, &sum, mpd_pow10[MPD_MAX_POW10], MPD_POS, + &workctx, status); + _mpd_qpow_uint(result, &tmp, mpd_pow10[t], MPD_POS, &workctx, status); + } +#endif + + +finish: + mpd_del(&tmp); + mpd_del(&sum); + *status |= (workctx.status&MPD_Errors); + *status |= (MPD_Inexact|MPD_Rounded); +} + +/* exp(a) */ +void +mpd_qexp(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_context_t workctx; + + if (mpd_isspecial(a)) { + if (mpd_qcheck_nan(result, a, ctx, status)) { + return; + } + if (mpd_isnegative(a)) { + _settriple(result, MPD_POS, 0, 0); + } + else { + mpd_setspecial(result, MPD_POS, MPD_INF); + } + return; + } + if (mpd_iszerocoeff(a)) { + _settriple(result, MPD_POS, 1, 0); + return; + } + + workctx = *ctx; + workctx.round = MPD_ROUND_HALF_EVEN; + + if (ctx->allcr) { + MPD_NEW_STATIC(t1, 0,0,0,0); + MPD_NEW_STATIC(t2, 0,0,0,0); + MPD_NEW_STATIC(ulp, 0,0,0,0); + MPD_NEW_STATIC(aa, 0,0,0,0); + mpd_ssize_t prec; + + if (result == a) { + if (!mpd_qcopy(&aa, a, status)) { + mpd_seterror(result, MPD_Malloc_error, status); + return; + } + a = &aa; + } + + workctx.clamp = 0; + prec = ctx->prec + 3; + while (1) { + workctx.prec = prec; + _mpd_qexp(result, a, &workctx, status); + _ssettriple(&ulp, MPD_POS, 1, + result->exp + result->digits-workctx.prec-1); + + workctx.prec = ctx->prec; + mpd_qadd(&t1, result, &ulp, &workctx, &workctx.status); + mpd_qsub(&t2, result, &ulp, &workctx, &workctx.status); + if (mpd_isspecial(result) || mpd_iszerocoeff(result) || + mpd_qcmp(&t1, &t2, status) == 0) { + workctx.clamp = ctx->clamp; + mpd_check_underflow(result, &workctx, status); + mpd_qfinalize(result, &workctx, status); + break; + } + prec += MPD_RDIGITS; + } + mpd_del(&t1); + mpd_del(&t2); + mpd_del(&ulp); + mpd_del(&aa); + } + else { + _mpd_qexp(result, a, &workctx, status); + mpd_check_underflow(result, &workctx, status); + mpd_qfinalize(result, &workctx, status); + } +} + +/* Fused multiply-add: (a * b) + c, with a single final rounding. */ +void +mpd_qfma(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c, + const mpd_context_t *ctx, uint32_t *status) +{ + uint32_t workstatus = 0; + mpd_t *cc = (mpd_t *)c; + + if (result == c) { + if ((cc = mpd_qncopy(c)) == NULL) { + mpd_seterror(result, MPD_Malloc_error, status); + return; + } + } + + _mpd_qmul(result, a, b, ctx, &workstatus); + if (!(workstatus&MPD_Invalid_operation)) { + mpd_qadd(result, result, cc, ctx, &workstatus); + } + + if (cc != c) mpd_del(cc); + *status |= workstatus; +} + +static inline int +ln_schedule_prec(mpd_ssize_t klist[MPD_MAX_PREC_LOG2], mpd_ssize_t maxprec, + mpd_ssize_t initprec) +{ + mpd_ssize_t k; + int i; + + assert(maxprec >= 2 && initprec >= 2); + if (maxprec <= initprec) return -1; + + i = 0; k = maxprec; + do { + k = (k+2) / 2; + klist[i++] = k; + } while (k > initprec); + + return i-1; +} + +/* 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 = ((mpd_size_t)1)<>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; + } + if (mod->digits+mod->exp > ctx->prec) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + sign = (mpd_isnegative(base)) && (mpd_isodd(exp)); + if (mpd_iszerocoeff(exp)) { + if (mpd_iszerocoeff(base)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + r = (_mpd_cmp_abs(mod, &one)==0) ? 0 : 1; + _settriple(result, sign, r, 0); + return; + } + if (mpd_isnegative(exp)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + if (mpd_iszerocoeff(base)) { + _settriple(result, sign, 0, 0); + return; + } + + if (!mpd_qcopy(&tmod, mod, status)) { + goto mpd_errors; + } + mpd_set_positive(&tmod); + + mpd_maxcontext(&maxcontext); + + mpd_qround_to_int(&tbase, base, &maxcontext, status); + mpd_qround_to_int(&texp, exp, &maxcontext, status); + mpd_qround_to_int(&tmod, &tmod, &maxcontext, status); + + tbase_exp = tbase.exp; + tbase.exp = 0; + texp_exp = texp.exp; + texp.exp = 0; + + /* base = (base.int % modulo * pow(10, base.exp, modulo)) % modulo */ + mpd_qrem(&tbase, &tbase, &tmod, &maxcontext, status); + _settriple(result, MPD_POS, 1, tbase_exp); + mpd_qrem(result, result, &tmod, &maxcontext, status); + mpd_qmul(&tbase, &tbase, result, &maxcontext, status); + mpd_qrem(&tbase, &tbase, &tmod, &maxcontext, status); + if (mpd_isspecial(&tbase) || + mpd_isspecial(&texp) || + mpd_isspecial(&tmod)) { + goto mpd_errors; + } + + for (i = 0; i < texp_exp; i++) { + _mpd_qpowmod_uint(&tmp, &tbase, 10, &tmod, status); + t = tmp; + tmp = tbase; + tbase = t; + } + if (mpd_isspecial(&tbase)) { + goto mpd_errors; /* GCOV_UNLIKELY */ + } + + /* resize to smaller cannot fail */ + mpd_qcopy(result, &one, status); + while (mpd_isfinite(&texp) && !mpd_iszero(&texp)) { + if (mpd_isodd(&texp)) { + mpd_qmul(result, result, &tbase, &maxcontext, status); + mpd_qrem(result, result, &tmod, &maxcontext, status); + } + mpd_qmul(&tbase, &tbase, &tbase, &maxcontext, status); + mpd_qrem(&tbase, &tbase, &tmod, &maxcontext, status); + mpd_qdivint(&texp, &texp, &two, &maxcontext, status); + } + if (mpd_isspecial(&texp) || mpd_isspecial(&tbase) || + mpd_isspecial(&tmod) || mpd_isspecial(result)) { + /* MPD_Malloc_error */ + goto mpd_errors; + } + else { + mpd_set_sign(result, sign); + } + +out: + mpd_del(&tbase); + mpd_del(&texp); + mpd_del(&tmod); + mpd_del(&tmp); + mpd_qfinalize(result, ctx, status); + return; + +mpd_errors: + mpd_setspecial(result, MPD_POS, MPD_NAN); + goto out; +} + +void +mpd_qquantize(mpd_t *result, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + uint32_t workstatus = 0; + mpd_ssize_t b_exp = b->exp; + mpd_ssize_t expdiff, shift; + mpd_uint_t rnd; + + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(result, a, b, ctx, status)) { + return; + } + if (mpd_isinfinite(a) && mpd_isinfinite(b)) { + mpd_qcopy(result, a, status); + return; + } + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + if (b->exp > ctx->emax || b->exp < mpd_etiny(ctx)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + if (mpd_iszero(a)) { + _settriple(result, mpd_sign(a), 0, b->exp); + mpd_qfinalize(result, ctx, status); + return; + } + + + expdiff = a->exp - b->exp; + if (a->digits + expdiff > ctx->prec) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + if (expdiff >= 0) { + shift = expdiff; + if (!mpd_qshiftl(result, a, shift, status)) { + return; + } + result->exp = b_exp; + } + else { + /* At this point expdiff < 0 and a->digits+expdiff <= prec, + * so the shift before an increment will fit in prec. */ + shift = -expdiff; + rnd = mpd_qshiftr(result, a, shift, status); + if (rnd == MPD_UINT_MAX) { + return; + } + result->exp = b_exp; + if (!_mpd_apply_round_fit(result, rnd, ctx, status)) { + return; + } + workstatus |= MPD_Rounded; + if (rnd) { + workstatus |= MPD_Inexact; + } + } + + if (mpd_adjexp(result) > ctx->emax || + mpd_adjexp(result) < mpd_etiny(ctx)) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + *status |= workstatus; + mpd_qfinalize(result, ctx, status); +} + +void +mpd_qreduce(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, + uint32_t *status) +{ + mpd_ssize_t shift, maxexp, maxshift; + uint8_t sign_a = mpd_sign(a); + + if (mpd_isspecial(a)) { + if (mpd_qcheck_nan(result, a, ctx, status)) { + return; + } + mpd_qcopy(result, a, status); + return; + } + + if (!mpd_qcopy(result, a, status)) { + return; + } + mpd_qfinalize(result, ctx, status); + if (mpd_isspecial(result)) { + return; + } + if (mpd_iszero(result)) { + _settriple(result, sign_a, 0, 0); + return; + } + + shift = mpd_trail_zeros(result); + maxexp = (ctx->clamp) ? mpd_etop(ctx) : ctx->emax; + /* After the finalizing above result->exp <= maxexp. */ + maxshift = maxexp - result->exp; + shift = (shift > maxshift) ? maxshift : shift; + + mpd_qshiftr_inplace(result, shift); + result->exp += shift; +} + +void +mpd_qrem(mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, + uint32_t *status) +{ + MPD_NEW_STATIC(q,0,0,0,0); + + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(r, a, b, ctx, status)) { + return; + } + if (mpd_isinfinite(a)) { + mpd_seterror(r, MPD_Invalid_operation, status); + return; + } + if (mpd_isinfinite(b)) { + mpd_qcopy(r, a, status); + mpd_qfinalize(r, ctx, status); + return; + } + /* debug */ + abort(); /* GCOV_NOT_REACHED */ + } + if (mpd_iszerocoeff(b)) { + if (mpd_iszerocoeff(a)) { + mpd_seterror(r, MPD_Division_undefined, status); + } + else { + mpd_seterror(r, MPD_Invalid_operation, status); + } + return; + } + + _mpd_qdivmod(&q, r, a, b, ctx, status); + mpd_del(&q); + mpd_qfinalize(r, ctx, status); +} + +void +mpd_qrem_near(mpd_t *r, const mpd_t *a, const mpd_t *b, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_context_t workctx; + MPD_NEW_STATIC(btmp,0,0,0,0); + MPD_NEW_STATIC(q,0,0,0,0); + mpd_ssize_t expdiff, floordigits; + int cmp, isodd, allnine; + + if (mpd_isspecial(a) || mpd_isspecial(b)) { + if (mpd_qcheck_nans(r, a, b, ctx, status)) { + return; + } + if (mpd_isinfinite(a)) { + mpd_seterror(r, MPD_Invalid_operation, status); + return; + } + if (mpd_isinfinite(b)) { + mpd_qcopy(r, a, status); + mpd_qfinalize(r, ctx, status); + return; + } + /* debug */ + abort(); /* GCOV_NOT_REACHED */ + } + if (mpd_iszerocoeff(b)) { + if (mpd_iszerocoeff(a)) { + mpd_seterror(r, MPD_Division_undefined, status); + } + else { + mpd_seterror(r, MPD_Invalid_operation, status); + } + return; + } + + if (r == b) { + if (!mpd_qcopy(&btmp, b, status)) { + mpd_seterror(r, MPD_Malloc_error, status); + return; + } + b = &btmp; + } + + workctx = *ctx; + workctx.prec = a->digits; + workctx.prec = (workctx.prec > ctx->prec) ? workctx.prec : ctx->prec; + + _mpd_qdivmod(&q, r, a, b, &workctx, status); + if (mpd_isnan(&q) || mpd_isnan(r) || q.digits > ctx->prec) { + mpd_seterror(r, MPD_Division_impossible, status); + goto finish; + } + if (mpd_iszerocoeff(r)) { + goto finish; + } + + /* Deal with cases like rmnx078: + * remaindernear 999999999.5 1 -> NaN Division_impossible */ + expdiff = mpd_adjexp(b) - mpd_adjexp(r); + if (-1 <= expdiff && expdiff <= 1) { + + mpd_qtrunc(&q, &q, &workctx, &workctx.status); + allnine = mpd_coeff_isallnine(&q); + floordigits = q.digits; + isodd = mpd_isodd(&q); + + mpd_maxcontext(&workctx); + if (mpd_sign(a) == mpd_sign(b)) { + _mpd_qsub(&q, r, b, &workctx, &workctx.status); + if (workctx.status&MPD_Errors) { + mpd_seterror(r, workctx.status&MPD_Errors, status); + goto finish; + } + } + else { + _mpd_qadd(&q, r, b, &workctx, &workctx.status); + if (workctx.status&MPD_Errors) { + mpd_seterror(r, workctx.status&MPD_Errors, status); + goto finish; + } + } + + cmp = mpd_cmp_total_mag(&q, r); + if (cmp < 0 || (cmp == 0 && isodd)) { + if (allnine && floordigits == ctx->prec) { + mpd_seterror(r, MPD_Division_impossible, status); + goto finish; + } + mpd_qcopy(r, &q, status); + *status &= ~MPD_Rounded; + } + } + + +finish: + mpd_del(&btmp); + mpd_del(&q); + mpd_qfinalize(r, ctx, status); +} + +ALWAYS_INLINE void +_mpd_qrescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, + const mpd_context_t *ctx, uint32_t *status) +{ + mpd_ssize_t expdiff, shift; + mpd_uint_t rnd; + + if (mpd_isspecial(a)) { + mpd_qcopy(result, a, status); + return; + } + + if (mpd_iszero(a)) { + _settriple(result, mpd_sign(a), 0, exp); + return; + } + + expdiff = a->exp - exp; + if (expdiff >= 0) { + shift = expdiff; + if (a->digits + shift > MPD_MAX_PREC+1) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + if (!mpd_qshiftl(result, a, shift, status)) { + return; + } + result->exp = exp; + } + else { + shift = -expdiff; + rnd = mpd_qshiftr(result, a, shift, status); + if (rnd == MPD_UINT_MAX) { + return; + } + result->exp = exp; + _mpd_apply_round_excess(result, rnd, ctx, status); + *status |= MPD_Rounded; + if (rnd) { + *status |= MPD_Inexact; + } + } + + if (mpd_issubnormal(result, ctx)) { + *status |= MPD_Subnormal; + } +} + +/* + * Rescale a number so that it has exponent 'exp'. Does not regard context + * precision, emax, emin, but uses the rounding mode. Special numbers are + * quietly copied. Restrictions: + * + * MPD_MIN_ETINY <= exp <= MPD_MAX_EMAX+1 + * result->digits <= MPD_MAX_PREC+1 + */ +void +mpd_qrescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, + const mpd_context_t *ctx, uint32_t *status) +{ + if (exp > MPD_MAX_EMAX+1 || exp < MPD_MIN_ETINY) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + _mpd_qrescale(result, a, exp, ctx, status); +} + +/* + * Same as mpd_qrescale, but with relaxed restrictions. The result of this + * function should only be used for formatting a number and never as input + * for other operations. + * + * MPD_MIN_ETINY-MPD_MAX_PREC <= exp <= MPD_MAX_EMAX+1 + * result->digits <= MPD_MAX_PREC+1 + */ +void +mpd_qrescale_fmt(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, + const mpd_context_t *ctx, uint32_t *status) +{ + if (exp > MPD_MAX_EMAX+1 || exp < MPD_MIN_ETINY-MPD_MAX_PREC) { + mpd_seterror(result, MPD_Invalid_operation, status); + return; + } + + _mpd_qrescale(result, a, exp, ctx, status); +} + +/* Round to an integer according to 'action' and ctx->round. */ +enum {TO_INT_EXACT, TO_INT_SILENT, TO_INT_TRUNC, 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 207408428242 -r 40917e4b51aa Modules/_decimal/mpdecimal.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/mpdecimal.h Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,802 @@ +/* + * 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) */ + +/* conversion specifiers */ +#define PRI_mpd_uint_t PRIu64 +#define PRI_mpd_ssize_t PRIi64 +/* END CONFIG_64 */ + + +/* BEGIN CONFIG_32 */ +#elif defined(CONFIG_32) +/* types for modular and base arithmetic */ +#define MPD_UINT_MAX UINT32_MAX +#define MPD_BITS_PER_UINT 32 +typedef uint32_t mpd_uint_t; /* unsigned mod type */ + +#ifndef LEGACY_COMPILER +#define MPD_UUINT_MAX UINT64_MAX +typedef uint64_t mpd_uuint_t; /* double width unsigned mod type */ +#endif + +#define MPD_SIZE_MAX SIZE_MAX +typedef size_t mpd_size_t; /* unsigned size type */ + +/* type for dec->len, dec->exp, ctx->prec */ +#define MPD_SSIZE_MAX INT32_MAX +#define MPD_SSIZE_MIN INT32_MIN +typedef int32_t mpd_ssize_t; +#define _mpd_strtossize strtol + +/* decimal arithmetic */ +#define MPD_RADIX 1000000000UL /* 10**9 */ +#define MPD_RDIGITS 9 +#define MPD_MAX_POW10 9 +#define MPD_EXPDIGITS 10 /* MPD_EXPDIGITS <= MPD_RDIGITS+1 */ + +#define MPD_MAXTRANSFORM_2N 33554432UL /* 2**25 */ +#define MPD_MAX_PREC 425000000L +#define MPD_MAX_PREC_LOG2 32 +#define MPD_ELIMIT 425000001L +#define MPD_MAX_EMAX 425000000L /* ELIMIT-1 */ +#define MPD_MIN_EMIN (-425000000L) /* -EMAX */ +#define MPD_MIN_ETINY (MPD_MIN_EMIN-(MPD_MAX_PREC-1)) +#define MPD_EXP_INF 1000000001L /* allows for emax=999999999 in the tests */ +#define MPD_EXP_CLAMP (-2000000001L) /* allows for emin=-999999999 in the tests */ +#define MPD_MAXIMPORT 94444445L /* ceil((2*MPD_MAX_PREC)/MPD_RDIGITS) */ + +/* conversion specifiers */ +#define PRI_mpd_uint_t PRIu32 +#define PRI_mpd_ssize_t PRIi32 +/* END CONFIG_32 */ + +#else + #error "define CONFIG_64 or CONFIG_32" +#endif +/* END CONFIG */ + + +#if MPD_SIZE_MAX != MPD_UINT_MAX + #error "unsupported platform: need mpd_size_t == mpd_uint_t" +#endif + + +/******************************************************************************/ +/* Context */ +/******************************************************************************/ + +enum { + MPD_ROUND_UP, /* round away from 0 */ + MPD_ROUND_DOWN, /* round toward 0 (truncate) */ + MPD_ROUND_CEILING, /* round toward +infinity */ + MPD_ROUND_FLOOR, /* round toward -infinity */ + MPD_ROUND_HALF_UP, /* 0.5 is rounded up */ + MPD_ROUND_HALF_DOWN, /* 0.5 is rounded down */ + MPD_ROUND_HALF_EVEN, /* 0.5 is rounded to even */ + MPD_ROUND_05UP, /* round zero or five away from 0 */ + MPD_ROUND_TRUNC, /* truncate, but set infinity */ + MPD_ROUND_GUARD +}; + +enum { MPD_CLAMP_DEFAULT, MPD_CLAMP_IEEE_754, MPD_CLAMP_GUARD }; + +extern const char *mpd_round_string[MPD_ROUND_GUARD]; +extern const char *mpd_clamp_string[MPD_CLAMP_GUARD]; + + +typedef struct { + mpd_ssize_t prec; /* precision */ + mpd_ssize_t emax; /* max positive exp */ + mpd_ssize_t emin; /* min negative exp */ + uint32_t traps; /* status events that should be trapped */ + uint32_t status; /* status flags */ + uint32_t newtrap; /* set by mpd_addstatus_raise() */ + int round; /* rounding mode */ + int clamp; /* clamp mode */ + int allcr; /* all functions correctly rounded */ +} mpd_context_t; + + +/* Status flags */ +#define MPD_Clamped 0x00000001U +#define MPD_Conversion_syntax 0x00000002U +#define MPD_Division_by_zero 0x00000004U +#define MPD_Division_impossible 0x00000008U +#define MPD_Division_undefined 0x00000010U +#define MPD_Fpu_error 0x00000020U +#define MPD_Inexact 0x00000040U +#define MPD_Invalid_context 0x00000080U +#define MPD_Invalid_operation 0x00000100U +#define MPD_Malloc_error 0x00000200U +#define MPD_Not_implemented 0x00000400U +#define MPD_Overflow 0x00000800U +#define MPD_Rounded 0x00001000U +#define MPD_Subnormal 0x00002000U +#define MPD_Underflow 0x00004000U +#define MPD_Max_status (0x00008000U-1U) + +/* Conditions that result in an IEEE 754 exception */ +#define MPD_IEEE_Invalid_operation (MPD_Conversion_syntax | \ + MPD_Division_impossible | \ + MPD_Division_undefined | \ + MPD_Fpu_error | \ + MPD_Invalid_context | \ + MPD_Invalid_operation | \ + MPD_Malloc_error) \ + +/* Errors that require the result of an operation to be set to NaN */ +#define MPD_Errors (MPD_IEEE_Invalid_operation | \ + MPD_Division_by_zero) + +/* Default traps */ +#define MPD_Traps (MPD_IEEE_Invalid_operation | \ + MPD_Division_by_zero | \ + MPD_Overflow | \ + MPD_Underflow) + +/* Official name */ +#define MPD_Insufficient_storage MPD_Malloc_error + +/* IEEE 754 interchange format contexts */ +#define MPD_IEEE_CONTEXT_MAX_BITS 512 /* 16*(log2(MPD_MAX_EMAX / 3)-3) */ +#define MPD_DECIMAL32 32 +#define MPD_DECIMAL64 64 +#define MPD_DECIMAL128 128 + + +#define MPD_MINALLOC_MIN 2 +#define MPD_MINALLOC_MAX 64 +extern mpd_ssize_t MPD_MINALLOC; +extern void (* mpd_traphandler)(mpd_context_t *); +void mpd_dflt_traphandler(mpd_context_t *); + +void mpd_setminalloc(mpd_ssize_t n); +void mpd_init(mpd_context_t *ctx, mpd_ssize_t prec); + +void mpd_maxcontext(mpd_context_t *ctx); +void mpd_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); +mpd_ssize_t mpd_to_sci_size(char **res, const mpd_t *dec, int fmt); +mpd_ssize_t mpd_to_eng_size(char **res, const mpd_t *dec, int fmt); +int mpd_validate_lconv(mpd_spec_t *spec); +int mpd_parse_fmt_str(mpd_spec_t *spec, const char *fmt, int caps); +char * mpd_qformat_spec(const mpd_t *dec, const mpd_spec_t *spec, const mpd_context_t *ctx, uint32_t *status); +char *mpd_qformat(const mpd_t *dec, const char *fmt, const mpd_context_t *ctx, uint32_t *status); + +#define MPD_NUM_FLAGS 15 +#define MPD_MAX_FLAG_STRING 208 +#define MPD_MAX_FLAG_LIST (MPD_MAX_FLAG_STRING+18) +#define MPD_MAX_SIGNAL_LIST 121 +int mpd_snprint_flags(char *dest, int nmemb, uint32_t flags); +int mpd_lsnprint_flags(char *dest, int nmemb, uint32_t flags, const char *flag_string[]); +int mpd_lsnprint_signals(char *dest, int nmemb, uint32_t flags, const char *signal_string[]); + +/* output to a file */ +void mpd_fprint(FILE *file, const mpd_t *dec); +void mpd_print(const mpd_t *dec); + +/* assignment from a string */ +void mpd_qset_string(mpd_t *dec, const char *s, const mpd_context_t *ctx, uint32_t *status); + +/* set to NaN with error flags */ +void mpd_seterror(mpd_t *result, uint32_t flags, uint32_t *status); +/* set a special with sign and type */ +void mpd_setspecial(mpd_t *dec, uint8_t sign, uint8_t type); +/* set coefficient to zero or all nines */ +void mpd_zerocoeff(mpd_t *result); +void mpd_qmaxcoeff(mpd_t *result, const mpd_context_t *ctx, uint32_t *status); + +/* quietly assign a C integer type to an mpd_t */ +void mpd_qset_ssize(mpd_t *result, mpd_ssize_t a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qset_i32(mpd_t *result, int32_t a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qset_uint(mpd_t *result, mpd_uint_t a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qset_u32(mpd_t *result, uint32_t a, const mpd_context_t *ctx, uint32_t *status); +#ifndef LEGACY_COMPILER +void mpd_qset_i64(mpd_t *result, int64_t a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qset_u64(mpd_t *result, uint64_t a, const mpd_context_t *ctx, uint32_t *status); +#endif + +/* quietly assign a C integer type to an mpd_t with a static coefficient */ +void mpd_qsset_ssize(mpd_t *result, mpd_ssize_t a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qsset_i32(mpd_t *result, int32_t a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qsset_uint(mpd_t *result, mpd_uint_t a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qsset_u32(mpd_t *result, uint32_t a, const mpd_context_t *ctx, uint32_t *status); + +/* quietly get a C integer type from an mpd_t */ +mpd_ssize_t mpd_qget_ssize(const mpd_t *dec, uint32_t *status); +mpd_uint_t mpd_qget_uint(const mpd_t *dec, uint32_t *status); +mpd_uint_t mpd_qabs_uint(const mpd_t *dec, uint32_t *status); + + +/* quiet functions */ +int mpd_qcheck_nan(mpd_t *nanresult, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +int mpd_qcheck_nans(mpd_t *nanresult, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qfinalize(mpd_t *result, const mpd_context_t *ctx, uint32_t *status); + +const char * mpd_class(const mpd_t *a, const mpd_context_t *ctx); + +int mpd_qcopy(mpd_t *result, const mpd_t *a, uint32_t *status); +mpd_t *mpd_qncopy(const mpd_t *a); +int mpd_qcopy_abs(mpd_t *result, const mpd_t *a, uint32_t *status); +int mpd_qcopy_negate(mpd_t *result, const mpd_t *a, uint32_t *status); +int mpd_qcopy_sign(mpd_t *result, const mpd_t *a, const mpd_t *b, uint32_t *status); + +void mpd_qand(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qinvert(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qlogb(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qor(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qscaleb(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qxor(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +int mpd_same_quantum(const mpd_t *a, const mpd_t *b); + +void mpd_qrotate(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +int mpd_qshiftl(mpd_t *result, const mpd_t *a, mpd_ssize_t n, uint32_t *status); +mpd_uint_t mpd_qshiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n, uint32_t *status); +mpd_uint_t mpd_qshiftr_inplace(mpd_t *result, mpd_ssize_t n); +void mpd_qshift(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qshiftn(mpd_t *result, const mpd_t *a, mpd_ssize_t n, const mpd_context_t *ctx, uint32_t *status); + +int mpd_qcmp(const mpd_t *a, const mpd_t *b, uint32_t *status); +int mpd_qcompare(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +int mpd_qcompare_signal(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +int mpd_cmp_total(const mpd_t *a, const mpd_t *b); +int mpd_cmp_total_mag(const mpd_t *a, const mpd_t *b); +int mpd_compare_total(mpd_t *result, const mpd_t *a, const mpd_t *b); +int mpd_compare_total_mag(mpd_t *result, const mpd_t *a, const mpd_t *b); + +void mpd_qround_to_intx(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qround_to_int(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qtrunc(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qfloor(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qceil(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); + +void mpd_qabs(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qmax(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qmax_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qmin(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qmin_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qminus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qplus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qnext_minus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qnext_plus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qnext_toward(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qquantize(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qrescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, const mpd_context_t *ctx, uint32_t *status); +void mpd_qrescale_fmt(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, const mpd_context_t *ctx, uint32_t *status); +void mpd_qreduce(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_qadd(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qadd_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qadd_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qadd_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qadd_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qsub(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qsub_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qsub_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qsub_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qsub_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qmul(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qmul_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qmul_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qmul_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qmul_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qfma(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c, const mpd_context_t *ctx, uint32_t *status); +void mpd_qdiv(mpd_t *q, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qdiv_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qdiv_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qdiv_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qdiv_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qdivint(mpd_t *q, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qrem(mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qrem_near(mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qdivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); +void mpd_qpow(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_context_t *ctx, uint32_t *status); +void mpd_qpowmod(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_t *mod, const mpd_context_t *ctx, uint32_t *status); +void mpd_qexp(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); +void mpd_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 207408428242 -r 40917e4b51aa Modules/_decimal/mpsignal.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/mpsignal.c Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,967 @@ +/* + * 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" + + +/* Signaling wrappers for the quiet functions in mpdecimal.c. */ + + +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 207408428242 -r 40917e4b51aa Modules/_decimal/mptest.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/mptest.h Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,86 @@ +/* + * 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" +#include "vccompat.h" + + +/* This header file is required for various tests. */ + + +#define PRI_mpd_size_t "zu" + +#if defined(CONFIG_64) + #if defined(_MSC_VER) + #undef PRI_mpd_size_t + #define PRI_mpd_size_t PRIu64 + #endif + #define PRI_time_t "ld" +#elif defined(CONFIG_32) + #if defined(_MSC_VER) + #undef PRI_mpd_size_t + #define PRI_mpd_size_t PRIu32 + #endif + #if defined (__OpenBSD__) + #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 +#else + #error "define CONFIG_64 or CONFIG_32" +#endif + + +/* 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 207408428242 -r 40917e4b51aa Modules/_decimal/numbertheory.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/numbertheory.c Thu Feb 23 16:43:15 2012 +0100 @@ -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 +#include "bits.h" +#include "umodarith.h" +#include "numbertheory.h" + + +/* Bignum: Initialize the Number Theoretic Transform. */ + + +/* + * Return the nth root of unity in F(p). This corresponds to e**((2*pi*i)/n) + * in the Fourier transform. We have w**n == 1 (mod p). + * n := transform length. + * sign := -1 for forward transform, 1 for backward transform. + * modnum := one of {P1, P2, P3}. + */ +mpd_uint_t +_mpd_getkernel(mpd_uint_t n, int sign, int modnum) +{ + mpd_uint_t umod, p, r, xi; +#ifdef PPRO + double dmod; + uint32_t dinvmod[3]; +#endif + + SETMODULUS(modnum); + r = mpd_roots[modnum]; /* primitive root of F(p) */ + p = umod; + xi = (p-1) / n; + + if (sign == -1) + return POWMOD(r, (p-1-xi)); + else + return POWMOD(r, xi); +} + +/* + * Initialize and return transform parameters. + * n := transform length. + * sign := -1 for forward transform, 1 for backward transform. + * modnum := one of {P1, P2, P3}. + */ +struct fnt_params * +_mpd_init_fnt_params(mpd_size_t n, int sign, int modnum) +{ + struct fnt_params *tparams; + mpd_uint_t umod; +#ifdef PPRO + double dmod; + uint32_t dinvmod[3]; +#endif + mpd_uint_t kernel, w; + mpd_uint_t i; + mpd_size_t nhalf; + + assert(ispower2(n)); + assert(sign == -1 || sign == 1); + assert(P1 <= modnum && modnum <= P3); + + nhalf = n/2; + tparams = mpd_sh_alloc(sizeof *tparams, nhalf, sizeof (mpd_uint_t)); + if (tparams == NULL) { + return NULL; + } + + SETMODULUS(modnum); + kernel = _mpd_getkernel(n, sign, modnum); + + tparams->modnum = modnum; + tparams->modulus = umod; + tparams->kernel = kernel; + + /* wtable[] := w**0, w**1, ..., w**(nhalf-1) */ + w = 1; + for (i = 0; i < nhalf; i++) { + tparams->wtable[i] = w; + w = MULMOD(w, kernel); + } + + return tparams; +} + +/* Initialize wtable of size three. */ +void +_mpd_init_w3table(mpd_uint_t w3table[3], int sign, int modnum) +{ + mpd_uint_t umod; +#ifdef PPRO + double dmod; + uint32_t dinvmod[3]; +#endif + mpd_uint_t kernel; + + SETMODULUS(modnum); + kernel = _mpd_getkernel(3, sign, modnum); + + w3table[0] = 1; + w3table[1] = kernel; + w3table[2] = POWMOD(kernel, 2); +} + + diff -r 207408428242 -r 40917e4b51aa Modules/_decimal/numbertheory.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/numbertheory.h Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,71 @@ +/* + * 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 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 207408428242 -r 40917e4b51aa Modules/_decimal/python/bench.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/python/bench.py Thu Feb 23 16:43:15 2012 +0100 @@ -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 207408428242 -r 40917e4b51aa Modules/_decimal/python/deccheck.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_decimal/python/deccheck.py Thu Feb 23 16:43:15 2012 +0100 @@ -0,0 +1,1100 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2008-2010 Stefan Krah. All r