[Python-checkins] r77344 - in python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests: README.txt bench.py checkleak.py ctx-deccheck.py deccheck.py formathelper.py genlocale.py genrandformat.py genrandlocale.py randdec.py
stefan.krah
python-checkins at python.org
Thu Jan 7 13:23:08 CET 2010
Author: stefan.krah
Date: Thu Jan 7 13:23:07 2010
New Revision: 77344
Log:
Initial import of Lib/test/decimal_extended_tests
Added:
python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/
python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/README.txt (contents, props changed)
python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/bench.py (contents, props changed)
python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/checkleak.py (contents, props changed)
python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/ctx-deccheck.py (contents, props changed)
python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/deccheck.py (contents, props changed)
python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/formathelper.py (contents, props changed)
python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/genlocale.py (contents, props changed)
python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/genrandformat.py (contents, props changed)
python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/genrandlocale.py (contents, props changed)
python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/randdec.py (contents, props changed)
Added: python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/README.txt
==============================================================================
--- (empty file)
+++ python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/README.txt Thu Jan 7 13:23:07 2010
@@ -0,0 +1,14 @@
+
+
+Tests against decimal.py:
+
+
+../../../python deccheck.py
+../../../python ctx-deccheck.py
+
+
+You should not see errors apart from an occasional "Cannot convert byte to string",
+see: http://bugs.python.org/issue7442
+
+
+
Added: python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/bench.py
==============================================================================
--- (empty file)
+++ python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/bench.py Thu Jan 7 13:23:07 2010
@@ -0,0 +1,145 @@
+#!/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
+import decimal
+import cdecimal
+from gmpy import mpf, mpz, set_minprec
+
+
+Decimal = decimal.Decimal
+Cdecimal = cdecimal.Decimal
+
+
+# Pi function from the decimal.py documentation
+def pi_native(prec=None):
+ """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_decimal(prec):
+ """Decimal"""
+ decimal.getcontext().prec = prec
+ lasts, t, s, n, na, d, da = Decimal(0), Decimal(3), Decimal(3), Decimal(1), Decimal(0), Decimal(0), Decimal(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.Cdecimal"""
+ cdecimal.getcontext().prec = prec
+ lasts, t, s, n, na, d, da = Cdecimal(0), Cdecimal(3), Cdecimal(3), Cdecimal(1), Cdecimal(0), Cdecimal(0), Cdecimal(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_mpf(prec):
+ """gmp.mpf"""
+ set_minprec(prec)
+ lasts, t, s, n, na, d, da = mpf(0), mpf(3), mpf(3), mpf(1), mpf(0), mpf(0), mpf(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)
+
+
+d = {
+ "native": pi_native,
+ "cdecimal": pi_Cdecimal,
+ "gmpy": pi_mpf,
+ "decimal": pi_decimal,
+}
+
+
+def prec_to_bits(prec):
+ return ceil(prec * log(10) / log(2))
+
+
+
+print("\n# ======================================================================")
+print("# Calculating pi, 10000 iterations")
+print("# ======================================================================\n")
+
+for prec in [9, 19, 38, 100]:
+ print("\nPrecision: %d decimal digits\n" % prec)
+ for name in ["native", "cdecimal", "gmpy", "decimal"]:
+ if prec > 19 and (name == "native" or name == "decimal"):
+ continue
+ p = prec
+ if name == "gmpy":
+ p = int(prec_to_bits(prec))
+ func = d[name]
+ start = time.time()
+ for i in range(10000):
+ x = func(p)
+ print("%s:" % name)
+ print("result: %s" % str(x))
+ print("time: %fs\n" % (time.time()-start))
+
+
+print("\n# ======================================================================")
+print("# Factorial")
+print("# ======================================================================\n")
+
+cdecimal.getcontext().prec = cdecimal.MAX_PREC
+
+for n in [100000, 1000000, 10000000, 100000000]:
+
+ print("n = %d\n" % n)
+
+ start_calc = time.time()
+ x = factorial(Cdecimal(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))
+
+ start_calc = time.time()
+ y = factorial(mpz(n), 0)
+ end_calc = time.time()
+ start_conv = time.time()
+ sy = str(y)
+ end_conv = time.time()
+
+ print("gmpy:")
+ print("calculation time: %fs" % (end_calc-start_calc))
+ print("conversion time: %fs\n\n" % (end_conv-start_conv))
+
+ assert(sx == sy)
Added: python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/checkleak.py
==============================================================================
--- (empty file)
+++ python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/checkleak.py Thu Jan 7 13:23:07 2010
@@ -0,0 +1,21 @@
+#
+# Valgrind claims a possible leak indicating that the signal-dicts
+# of the context are not freed properly. The tests below do not
+# show any leaks.
+#
+
+
+from cdecimal import *
+
+
+for i in xrange(10000000):
+ c = Context()
+ c.prec = 9
+ setcontext(c)
+
+
+for i in xrange(10000000):
+ c = Context()
+ d = getcontext().copy()
+ del(c)
+ del(d)
Added: python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/ctx-deccheck.py
==============================================================================
--- (empty file)
+++ python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/ctx-deccheck.py Thu Jan 7 13:23:07 2010
@@ -0,0 +1,929 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2008-2010 Stefan Krah. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+#
+
+# Usage: python ctx-deccheck.py [--short|--medium|--long|--all]
+
+
+import cdecimal, decimal
+import sys, inspect
+from copy import copy
+
+
+py_minor = sys.version_info[1]
+py_micro = sys.version_info[2]
+
+
+# Translate symbols.
+deccond = {
+ cdecimal.Clamped: decimal.Clamped,
+ cdecimal.ConversionSyntax: decimal.ConversionSyntax,
+ cdecimal.DivisionByZero: decimal.DivisionByZero,
+ cdecimal.DivisionImpossible: decimal.InvalidOperation,
+ cdecimal.DivisionUndefined: decimal.DivisionUndefined,
+ cdecimal.Inexact: decimal.Inexact,
+ cdecimal.InvalidContext: decimal.InvalidContext,
+ cdecimal.InvalidOperation: decimal.InvalidOperation,
+ cdecimal.Overflow: decimal.Overflow,
+ cdecimal.Rounded: decimal.Rounded,
+ cdecimal.Subnormal: decimal.Subnormal,
+ cdecimal.Underflow: decimal.Underflow,
+}
+
+mpdcond = {
+ decimal.Clamped: cdecimal.Clamped,
+ decimal.ConversionSyntax: cdecimal.ConversionSyntax,
+ decimal.DivisionByZero: cdecimal.DivisionByZero,
+ decimal.InvalidOperation: cdecimal.DivisionImpossible,
+ decimal.DivisionUndefined: cdecimal.DivisionUndefined,
+ decimal.Inexact: cdecimal.Inexact,
+ decimal.InvalidContext: cdecimal.InvalidContext,
+ decimal.InvalidOperation: cdecimal.InvalidOperation,
+ decimal.Overflow: cdecimal.Overflow,
+ decimal.Rounded: cdecimal.Rounded,
+ decimal.Subnormal: cdecimal.Subnormal,
+ decimal.Underflow: cdecimal.Underflow
+}
+
+decround = {
+ cdecimal.ROUND_UP: decimal.ROUND_UP,
+ cdecimal.ROUND_DOWN: decimal.ROUND_DOWN,
+ cdecimal.ROUND_CEILING: decimal.ROUND_CEILING,
+ cdecimal.ROUND_FLOOR: decimal.ROUND_FLOOR,
+ cdecimal.ROUND_HALF_UP: decimal.ROUND_HALF_UP,
+ cdecimal.ROUND_HALF_DOWN: decimal.ROUND_HALF_DOWN,
+ cdecimal.ROUND_HALF_EVEN: decimal.ROUND_HALF_EVEN,
+ cdecimal.ROUND_05UP: decimal.ROUND_05UP
+}
+
+
+class Context(object):
+ """Provides a convenient way of syncing the cdecimal and decimal contexts"""
+
+ __slots__ = ['f', 'd']
+
+ def __init__(self, mpdctx=cdecimal.getcontext()):
+ """Initialization is from the cdecimal context"""
+ self.f = mpdctx
+ self.d = decimal.getcontext()
+ self.d.prec = self.f.prec
+ self.d.Emin = self.f.Emin
+ self.d.Emax = self.f.Emax
+ self.d.rounding = decround[self.f.rounding]
+ self.d.capitals = self.f.capitals
+ self.settraps([sig for sig in self.f.traps if self.f.traps[sig]])
+ self.setstatus([sig for sig in self.f.flags if self.f.flags[sig]])
+ self.d._clamp = self.f._clamp
+
+ def getprec(self):
+ assert(self.f.prec == self.d.prec)
+ return self.f.prec
+
+ def setprec(self, val):
+ self.f.prec = val
+ self.d.prec = val
+
+ def getemin(self):
+ assert(self.f.Emin == self.d.Emin)
+ return self.f.Emin
+
+ def setemin(self, val):
+ self.f.Emin = val
+ self.d.Emin = val
+
+ def getemax(self):
+ assert(self.f.Emax == self.d.Emax)
+ return self.f.Emax
+
+ def setemax(self, val):
+ self.f.Emax = val
+ self.d.Emax = val
+
+ def getround(self):
+ assert(self.f.rounding == self.d.rounding)
+ return self.f.rounding
+
+ def setround(self, val):
+ self.f.rounding = val
+ self.d.rounding = decround[val]
+
+ def getcapitals(self):
+ assert(self.f.capitals == self.d.capitals)
+ return self.f.capitals
+
+ def setcapitals(self, val):
+ self.f.capitals = val
+ self.d.capitals = val
+
+ def getclamp(self):
+ assert(self.f._clamp == self.d._clamp)
+ return self.f._clamp
+
+ def setclamp(self, val):
+ self.f._clamp = val
+ self.d._clamp = val
+
+ prec = property(getprec, setprec)
+ emin = property(getemin, setemin)
+ emax = property(getemax, setemax)
+ round = property(getround, setround)
+ clamp = property(getclamp, setclamp)
+ capitals = property(getcapitals, setcapitals)
+
+ def clear_traps(self):
+ self.f.clear_traps()
+ for trap in self.d.traps:
+ self.d.traps[trap] = False
+
+ def clear_status(self):
+ self.f.clear_flags()
+ self.d.clear_flags()
+
+ def settraps(self, lst): # cdecimal signal list
+ self.clear_traps()
+ for signal in lst:
+ self.f.traps[signal] = True
+ self.d.traps[deccond[signal]] = True
+
+ def setstatus(self, lst): # cdecimal signal list
+ self.clear_status()
+ for signal in lst:
+ self.f.flags[signal] = True
+ self.d.flags[deccond[signal]] = True
+
+ def assert_eq_status(self):
+ """assert equality of cdecimal and decimal status"""
+ for signal in self.f.flags:
+ if self.f.flags[signal] == (not self.d.flags[deccond[signal]]):
+ return False
+ return True
+
+
+# We don't want exceptions so that we can compare the status flags.
+context = Context()
+context.clear_traps()
+
+
+_exc_fmt = "\
+cdecimal_sci: %s\n\
+decimal_sci: %s\n\
+cdecimal_eng: %s\n\
+decimal_eng: %s\n"
+
+_exc_fmt_tuple = "\
+cdecimal_tuple: %s\n\
+decimal_tuple: %s\n"
+
+_exc_fmt_obj = "\
+cdecimal: %s\n\
+decimal: %s\n\n"
+
+class CdecException(ArithmeticError):
+ def __init__(self, result, funcname, operands):
+ self.errstring = "Error in %s(%s" % (funcname, operands[0])
+ for op in operands[1:]:
+ self.errstring += ", %s" % op
+ self.errstring += "):\n\n"
+ if isinstance(result, cdec):
+ self.errstring += _exc_fmt % (str(result.mpd),
+ str(result.dec),
+ result.mpd.to_eng(),
+ result.dec.to_eng_string())
+ mpd_tuple = result.mpd.as_tuple()
+ dec_tuple = result.dec.as_tuple()
+ if mpd_tuple != dec_tuple:
+ self.errstring += _exc_fmt_tuple % (str(mpd_tuple),
+ str(dec_tuple))
+ else:
+ self.errstring += _exc_fmt_obj % (str(result[0]), str(result[1]))
+ self.errstring += "%s\n%s\n\n" % (str(context.f), str(context.d))
+ def __str__(self):
+ return self.errstring
+
+
+class dHandlerCdec:
+ """For cdec return values:
+
+ Handle known disagreements between decimal.py and cdecimal.so.
+ This is just a temporary measure against cluttered output.
+ Detection is crude and possibly unreliable."""
+
+ def __init__(self):
+ self.logb_round_if_gt_prec = 0
+ self.ulpdiff = 0
+ self.powmod_zeros = 0
+ self.total_mag_nan = 0
+ self.quantize_status = 0
+ self.max_status = 0
+
+ def default(self, result, operands):
+ return False
+
+ def ulp(self, dec):
+ """Harrison ULP: ftp://ftp.inria.fr/INRIA/publication/publi-pdf/RR/RR-5504.pdf"""
+ a = dec.next_plus()
+ b = dec.next_minus()
+ return abs(a - b)
+
+ def bin_resolve_ulp(self, result, funcname, operands):
+ """Results of cdecimal's power function are currently not always
+ correctly rounded. Check if the cdecimal result differs by less
+ than 1 ULP from the correctly rounded decimal.py result."""
+ mpdstr = str(result.mpd)
+ mpdresult = decimal.Decimal(mpdstr)
+ decresult = result.dec
+ deculp = self.ulp(decresult)
+ op1 = operands[0].dec
+ op2 = operands[1].dec
+ tmpctx = context.d.copy()
+ tmpctx.prec *= 2
+ # result, recalculated at double precision
+ dpresult = getattr(op1, funcname)(op2, context=tmpctx)
+ mpddiff = abs(dpresult - mpdresult)
+ if mpddiff >= deculp:
+ return False # not simply a disagreement, but wrong
+ decdiff = abs(dpresult - decresult)
+ if decdiff >= deculp:
+ return False # not simply a disagreement, but wrong
+ self.ulpdiff += 1
+ return True
+
+ def __pow__(self, result, operands):
+ """See DIFFERENCES.txt"""
+ if operands[2] is not None: # three argument __pow__
+ if (operands[0].mpd.is_zero() != operands[1].mpd.is_zero()):
+ if (result.mpd == 0 or result.mpd == 1) and result.dec.is_nan():
+ if not context.f.flags[cdecimal.InvalidOperation] and \
+ context.d.flags[decimal.InvalidOperation]:
+ self.powmod_zeros += 1
+ return True
+ elif context.f.flags[cdecimal.Rounded] and \
+ context.f.flags[cdecimal.Inexact] and \
+ context.d.flags[decimal.Rounded] and \
+ context.d.flags[decimal.Inexact]:
+ return self.bin_resolve_ulp(result, "__pow__", operands)
+ else:
+ return False
+
+ power = __pow__
+
+ if py_minor < 2:
+ def rotate(self, result, operands):
+ """truncate excess digits before the operation"""
+ if len(operands[0].dec._int) > context.f.prec:
+ return True
+ return False
+ shift = rotate
+
+ def compare_total_mag(self, result, operands):
+ if operands[0].mpd.is_nan() and operands[1].mpd.is_nan() and \
+ abs(result.mpd) == 1 and abs(result.dec) == 1:
+ self.total_mag_nan += 1
+ return True
+ return False
+ compare_total = compare_total_mag
+
+ def logb(self, result, operands):
+ if context.f.flags[cdecimal.Rounded] and \
+ (not context.d.flags[decimal.Rounded]):
+ self.logb_round_if_gt_prec += 1
+ return True
+ return False
+
+
+class dHandlerObj():
+ """For non-decimal return values:
+
+ Handle known disagreements between decimal.py and cdecimal.so.
+ Currently there are none."""
+
+ def __init__(self):
+ pass
+
+ def default(self, result, operands):
+ return False
+
+
+dhandler_cdec = dHandlerCdec()
+def cdec_known_disagreement(result, funcname, operands):
+ return getattr(dhandler_cdec, funcname, dhandler_cdec.default)(result, operands)
+
+#dhandler_obj = dHandlerObj()
+#def obj_known_disagreement(result, funcname, operands):
+# return getattr(dhandler_obj, funcname, dhandler_obj.default)(result, operands)
+
+
+
+def verify(result, funcname, operands):
+ """Verifies that after operation 'funcname' with operand(s) 'operands'
+ result[0] and result[1] as well as the context flags have the same
+ values."""
+ if result[0] != result[1] or not context.assert_eq_status():
+ #if obj_known_disagreement(result, funcname, operands):
+ # return # skip known disagreements
+ raise CdecException(result, funcname, operands)
+
+
+class cdec(object):
+ """Joins cdecimal.so and decimal.py for redundant calculations with error
+ checking. Always calls the context methods of cdecimal and decimal. This
+ is not very clean, but an easy way of adapting deccheck.py for testing
+ context methods."""
+
+ __slots__ = ['mpd', 'dec']
+
+ def __new__(cls, value=None):
+ self = object.__new__(cls)
+ self.mpd = None
+ self.dec = None
+ if value is not None:
+ context.clear_status()
+ if isinstance(value, float):
+ self.mpd = context.f.create_decimal_from_float(value)
+ self.dec = context.d.create_decimal_from_float(value)
+ else:
+ self.mpd = context.f.create_decimal(value)
+ self.dec = context.d.create_decimal(value)
+ self.verify('__xnew__', (value,))
+ return self
+
+ def verify(self, funcname, operands):
+ """Verifies that after operation 'funcname' with operand(s) 'operands'
+ self.mpd and self.dec as well as the context flags have the same
+ values."""
+ mpdstr = str(self.mpd)
+ decstr = str(self.dec)
+ mpdstr_eng = self.mpd.to_eng_string()
+ decstr_eng = self.dec.to_eng_string()
+ if mpdstr != decstr or mpdstr_eng != decstr_eng or \
+ not context.assert_eq_status():
+ if cdec_known_disagreement(self, funcname, operands):
+ return # skip known disagreements
+ raise CdecException(self, funcname, operands)
+
+ def unaryfunc(self, funcname):
+ "unary function returning a cdec, uses the context methods"
+ context.clear_status()
+ c = cdec()
+ c.mpd = getattr(context.f, funcname)(self.mpd)
+ c.dec = getattr(context.d, funcname)(self.dec)
+ c.verify(funcname, (self,))
+ return c
+
+ def obj_unaryfunc(self, funcname):
+ "unary function returning an object other than a cdec"
+ context.clear_status()
+ r_mpd = getattr(context.f, funcname)(self.mpd)
+ r_dec = getattr(context.d, funcname)(self.dec)
+ verify((r_mpd, r_dec), funcname, (self,))
+ return r_mpd
+
+ def binaryfunc(self, other, funcname):
+ "binary function returning a cdec, uses the context methods"
+ context.clear_status()
+ c = cdec()
+ other_mpd = other_dec = other
+ if isinstance(other, cdec):
+ other_mpd = other.mpd
+ other_dec = other.dec
+ c.mpd = getattr(context.f, funcname)(self.mpd, other_mpd)
+ c.dec = getattr(context.d, funcname)(self.dec, other_dec)
+ c.verify(funcname, (self, other))
+ return c
+
+ def obj_binaryfunc(self, other, funcname):
+ "binary function returning an object other than a cdec"
+ context.clear_status()
+ other_mpd = other_dec = other
+ if isinstance(other, cdec):
+ other_mpd = other.mpd
+ other_dec = other.dec
+ r_mpd = getattr(context.f, funcname)(self.mpd, other_mpd)
+ r_dec = getattr(context.d, funcname)(self.dec, other_dec)
+ verify((r_mpd, r_dec), funcname, (self, other))
+ return r_mpd
+
+ def ternaryfunc(self, other, third, funcname):
+ "ternary function returning a cdec, uses the context methods"
+ context.clear_status()
+ c = cdec()
+ other_mpd = other_dec = other
+ if isinstance(other, cdec):
+ other_mpd = other.mpd
+ other_dec = other.dec
+ third_mpd = third_dec = third
+ if isinstance(third, cdec):
+ third_mpd = third.mpd
+ third_dec = third.dec
+ if funcname == 'power':
+ if (third is not None):
+ c.mpd = getattr(context.f, 'powmod')(self.mpd, other_mpd, third_mpd)
+ else:
+ c.mpd = getattr(context.f, 'pow')(self.mpd, other_mpd)
+ else:
+ c.mpd = getattr(context.f, funcname)(self.mpd, other_mpd, third_mpd)
+ c.dec = getattr(context.d, funcname)(self.dec, other_dec, third_dec)
+ c.verify(funcname, (self, other, third))
+ return c
+
+ def __repr__(self):
+ self.obj_unaryfunc('__repr__')
+ return "cdec('" + str(self.mpd) + "')"
+
+ def __str__(self):
+ self.obj_unaryfunc('__str__')
+ return str(self.mpd)
+
+ def abs(self):
+ return self.unaryfunc('abs')
+
+ def add(self, other):
+ return self.binaryfunc(other, 'add')
+
+ def compare(self, other):
+ return self.binaryfunc(other, 'compare')
+
+ def compare_signal(self, other):
+ return self.binaryfunc(other, 'compare_signal')
+
+ def compare_total(self, other):
+ return self.binaryfunc(other, 'compare_total')
+
+ def compare_total_mag(self, other):
+ return self.binaryfunc(other, 'compare_total_mag')
+
+ def copy_abs(self):
+ return self.unaryfunc('copy_abs')
+
+ def copy_decimal(self):
+ return self.unaryfunc('copy_decimal')
+
+ def copy_negate(self):
+ return self.unaryfunc('copy_negate')
+
+ def copy_sign(self, other):
+ return self.binaryfunc(other, 'copy_sign')
+
+ def create_decimal(self):
+ return self.unaryfunc('create_decimal')
+
+ def divide(self, other):
+ return self.binaryfunc(other, 'divide')
+
+ def divide_int(self, other):
+ return self.binaryfunc(other, 'divide_int')
+
+ def divmod(self, other):
+ context.clear_status()
+ q = cdec()
+ r = cdec()
+ other_mpd = other_dec = other
+ if isinstance(other, cdec):
+ other_mpd = other.mpd
+ other_dec = other.dec
+ q.mpd, r.mpd = context.f.divmod(self.mpd, other_mpd)
+ q.dec, r.dec = context.d.divmod(self.dec, other_dec)
+ q.verify('divmod', (self, other))
+ r.verify('divmod', (self, other))
+ return (q, r)
+
+ def exp(self):
+ return self.unaryfunc('exp')
+
+ def fma(self, other, third):
+ return self.ternaryfunc(other, third, 'fma')
+
+ # imag
+ # invroot
+
+ def is_canonical(self):
+ return self.obj_unaryfunc('is_canonical')
+
+ def is_finite(self):
+ return self.obj_unaryfunc('is_finite')
+
+ def is_infinite(self):
+ return self.obj_unaryfunc('is_infinite')
+
+ def is_nan(self):
+ return self.obj_unaryfunc('is_nan')
+
+ def is_normal(self):
+ return self.obj_unaryfunc('is_normal')
+
+ def is_qnan(self):
+ return self.obj_unaryfunc('is_qnan')
+
+ def is_signed(self):
+ return self.obj_unaryfunc('is_signed')
+
+ def is_snan(self):
+ return self.obj_unaryfunc('is_snan')
+
+ def is_subnormal(self):
+ return self.obj_unaryfunc('is_subnormal')
+
+ def is_zero(self):
+ return self.obj_unaryfunc('is_zero')
+
+ def ln(self):
+ return self.unaryfunc('ln')
+
+ def log10(self):
+ return self.unaryfunc('log10')
+
+ def logb(self):
+ return self.unaryfunc('logb')
+
+ def logical_and(self, other):
+ return self.binaryfunc(other, 'logical_and')
+
+ def logical_invert(self):
+ return self.unaryfunc('logical_invert')
+
+ def logical_or(self, other):
+ return self.binaryfunc(other, 'logical_or')
+
+ def logical_xor(self, other):
+ return self.binaryfunc(other, 'logical_xor')
+
+ def max(self, other):
+ return self.binaryfunc(other, 'max')
+
+ def max_mag(self, other):
+ return self.binaryfunc(other, 'max_mag')
+
+ def min(self, other):
+ return self.binaryfunc(other, 'min_mag')
+
+ def min_mag(self, other):
+ return self.binaryfunc(other, 'min_mag')
+
+ def minus(self):
+ return self.unaryfunc('minus')
+
+ def multiply(self, other):
+ return self.binaryfunc(other, 'multiply')
+
+ def next_minus(self):
+ return self.unaryfunc('next_minus')
+
+ def next_plus(self):
+ return self.unaryfunc('next_plus')
+
+ def next_toward(self, other):
+ return self.binaryfunc(other, 'next_toward')
+
+ def normalize(self):
+ return self.unaryfunc('normalize')
+
+ def number_class(self):
+ return self.obj_unaryfunc('number_class')
+
+ def plus(self):
+ return self.unaryfunc('plus')
+
+ def power(self, other, mod=None):
+ return self.ternaryfunc(other, mod, 'power')
+
+ # powmod: same as __pow__ or power with three arguments
+
+ def quantize(self, other):
+ return self.binaryfunc(other, 'quantize')
+
+ # real
+ # reduce: same as normalize
+
+ def remainder(self, other):
+ return self.binaryfunc(other, 'remainder')
+
+ def remainder_near(self, other):
+ return self.binaryfunc(other, 'remainder_near')
+
+ def rotate(self, other):
+ return self.binaryfunc(other, 'rotate')
+
+ def same_quantum(self, other):
+ return self.obj_binaryfunc(other, 'same_quantum')
+
+ def scaleb(self, other):
+ return self.binaryfunc(other, 'scaleb')
+
+ def shift(self, other):
+ return self.binaryfunc(other, 'shift')
+
+ # sign
+
+ def sqrt(self):
+ return self.unaryfunc('sqrt')
+
+ def subtract(self, other):
+ return self.binaryfunc(other, 'subtract')
+
+ def to_eng_string(self):
+ return self.obj_unaryfunc('to_eng_string')
+
+ def to_integral(self):
+ return self.unaryfunc('to_integral')
+
+ def to_integral_exact(self):
+ return self.unaryfunc('to_integral_exact')
+
+ def to_integral_value(self):
+ return self.unaryfunc('to_integral_value')
+
+ def to_sci_string(self):
+ return self.obj_unaryfunc('to_sci_string')
+
+
+def log(fmt, args=None):
+ if args:
+ sys.stdout.write(''.join((fmt, '\n')) % args)
+ else:
+ sys.stdout.write(''.join((str(fmt), '\n')))
+ sys.stdout.flush()
+
+def test_unary(method, prec_lst, iter):
+ log("testing %s ...", method)
+ for prec in prec_lst:
+ log(" prec: %d", prec)
+ context.prec = prec
+ for round in sorted(decround):
+ context.round = round
+ rprec = 10**prec
+ exprange = cdecimal.MAX_EMAX
+ if method in ['__int__', '__long__', '__trunc__', 'to_integral', \
+ 'to_integral_value', 'to_integral_value']:
+ exprange = 9999
+ for a in un_close_to_pow10(prec, exprange, iter):
+ try:
+ x = cdec(a)
+ getattr(x, method)()
+ except CdecException as err:
+ log(err)
+ for a in un_close_numbers(prec, exprange, -exprange, iter):
+ try:
+ x = cdec(a)
+ getattr(x, method)()
+ except CdecException as err:
+ log(err)
+ for a in un_incr_digits_tuple(prec, exprange, iter):
+ try:
+ x = cdec(a)
+ getattr(x, method)()
+ except CdecException as err:
+ log(err)
+ for i in range(1000):
+ try:
+ s = randdec(prec, exprange)
+ x = cdec(s)
+ getattr(x, method)()
+ except CdecException as err:
+ log(err)
+ except OverflowError:
+ pass
+ try:
+ s = randtuple(prec, exprange)
+ x = cdec(s)
+ getattr(x, method)()
+ except CdecException as err:
+ log(err)
+ except OverflowError:
+ pass
+
+def test_un_logical(method, prec_lst, iter):
+ log("testing %s ...", method)
+ for prec in prec_lst:
+ log(" prec: %d", prec)
+ context.prec = prec
+ for round in sorted(decround):
+ context.round = round
+ for a in logical_un_incr_digits(prec, iter):
+ try:
+ x = cdec(a)
+ getattr(x, method)()
+ except CdecException as err:
+ log(err)
+ for i in range(1000):
+ try:
+ s = randdec(prec, 999999)
+ x = cdec(s)
+ getattr(x, method)()
+ except CdecException as err:
+ log(err)
+ except OverflowError:
+ pass
+
+def test_binary(method, prec_lst, iter):
+ log("testing %s ...", method)
+ for prec in prec_lst:
+ log(" prec: %d", prec)
+ context.prec = prec
+ for round in sorted(decround):
+ context.round = round
+ exprange = cdecimal.MAX_EMAX
+ if method in ['__pow__', '__rpow__', 'power']:
+ exprange = 99999
+ for a, b in bin_close_to_pow10(prec, exprange, iter):
+ try:
+ x = cdec(a)
+ y = cdec(b)
+ getattr(x, method)(y)
+ except CdecException as err:
+ log(err)
+ for a, b in bin_close_numbers(prec, exprange, -exprange, iter):
+ try:
+ x = cdec(a)
+ y = cdec(b)
+ getattr(x, method)(y)
+ except CdecException as err:
+ log(err)
+ for a, b in bin_incr_digits(prec, exprange, iter):
+ try:
+ x = cdec(a)
+ y = cdec(b)
+ getattr(x, method)(y)
+ except CdecException as err:
+ log(err)
+ for i in range(1000):
+ s1 = randdec(prec, exprange)
+ s2 = randdec(prec, exprange)
+ try:
+ x = cdec(s1)
+ y = cdec(s2)
+ getattr(x, method)(y)
+ except CdecException as err:
+ log(err)
+
+def test_bin_logical(method, prec_lst, iter):
+ log("testing %s ...", method)
+ for prec in prec_lst:
+ log(" prec: %d", prec)
+ context.prec = prec
+ for round in sorted(decround):
+ context.round = round
+ for a, b in logical_bin_incr_digits(prec, iter):
+ try:
+ x = cdec(a)
+ y = cdec(b)
+ getattr(x, method)(y)
+ except CdecException as err:
+ log(err)
+ for i in range(1000):
+ s1 = randdec(prec, 999999)
+ s2 = randdec(prec, 999999)
+ try:
+ x = cdec(s1)
+ y = cdec(s2)
+ getattr(x, method)(y)
+ except CdecException as err:
+ log(err)
+
+def test_ternary(method, prec_lst, iter):
+ log("testing %s ...", method)
+ for prec in prec_lst:
+ log(" prec: %d", prec)
+ context.prec = prec
+ for round in sorted(decround):
+ context.round = round
+ exprange = cdecimal.MAX_EMAX
+ if method in ['__pow__', 'power']:
+ exprange = 99999
+ for a, b, c in tern_close_numbers(prec, exprange, -exprange, iter):
+ try:
+ x = cdec(a)
+ y = cdec(b)
+ z = cdec(c)
+ getattr(x, method)(y, z)
+ except CdecException as err:
+ log(err)
+ for a, b, c in tern_incr_digits(prec, exprange, iter):
+ try:
+ x = cdec(a)
+ y = cdec(b)
+ z = cdec(c)
+ getattr(x, method)(y, z)
+ except CdecException as err:
+ log(err)
+ for i in range(1000):
+ s1 = randdec(prec, 2*exprange)
+ s2 = randdec(prec, 2*exprange)
+ s3 = randdec(prec, 2*exprange)
+ try:
+ x = cdec(s1)
+ y = cdec(s2)
+ z = cdec(s3)
+ getattr(x, method)(y, z)
+ except CdecException as err:
+ log(err)
+
+def test_from_float(prec_lst):
+ log("testing create_decimal_from_float ...")
+ for prec in prec_lst:
+ log(" prec: %d", prec)
+ context.prec = prec
+ for round in sorted(decround):
+ context.round = round
+ exprange = 384
+ for i in range(1000):
+ intpart = str(random.randrange(100000000000000000000000000000000000000))
+ fracpart = str(random.randrange(100000000000000000000000000000000000000))
+ exp = str(random.randrange(-384, 384))
+ fstring = intpart + '.' + fracpart + 'e' + exp
+ f = float(fstring)
+ try:
+ c = cdec(f)
+ except CdecException as err:
+ log(err)
+
+
+if __name__ == '__main__':
+
+ from randdec import *
+ import time
+ import sys
+
+
+ samples = 1
+ iter = 1
+
+ if '--short' in sys.argv:
+ samples = 1
+ iter = 1
+ elif '--medium' in sys.argv:
+ samples = 1
+ iter = None
+ elif '--long' in sys.argv:
+ samples = 5
+ iter = None
+ elif '--all' in sys.argv:
+ samples = 100
+ iter = None
+
+ all_context_methods = set(dir(cdecimal.getcontext()) + dir(decimal.getcontext()))
+ all_cdec_methods = [m for m in dir(cdec) if m in all_context_methods]
+ untested_methods = [m for m in all_context_methods if not (m in all_cdec_methods)]
+
+ unary_methods = []
+ binary_methods = []
+ ternary_methods = []
+ for m in all_cdec_methods:
+ try:
+ l = len(inspect.getargspec(getattr(cdec, m))[0])
+ except TypeError:
+ continue
+ if l == 1:
+ unary_methods.append(m)
+ elif l == 2:
+ binary_methods.append(m)
+ elif l == 3:
+ ternary_methods.append(m)
+ else:
+ raise ValueError((m, l))
+
+ unary_methods.remove('__repr__')
+ unary_methods.remove('__str__')
+ binary_methods.remove('__new__')
+ untested_methods.append('__repr__')
+ untested_methods.append('__str__')
+ untested_methods.append('__new__')
+ untested_methods.remove('create_decimal_from_float')
+
+ binary_methods.append('power')
+
+ untested_methods.sort()
+ unary_methods.sort()
+ binary_methods.sort()
+ ternary_methods.sort()
+
+
+ x = int(time.time())
+ random.seed(x)
+ log("\nRandom seed: %d\n\n", x)
+ log("Skipping tests: \n\n%s\n", untested_methods)
+
+
+ for method in unary_methods:
+ prec_lst = sorted(random.sample(range(1, 101), samples))
+ test_unary(method, prec_lst, iter)
+
+ for method in binary_methods:
+ prec_lst = sorted(random.sample(range(1, 101), samples))
+ test_binary(method, prec_lst, iter)
+
+ for method in ternary_methods:
+ prec_lst = sorted(random.sample(range(1, 101), samples))
+ test_ternary(method, prec_lst, iter)
+
+ prec_lst = sorted(random.sample(range(1, 101), samples))
+ test_un_logical('logical_invert', prec_lst, iter)
+
+ for method in ['logical_and', 'logical_or', 'logical_xor']:
+ prec_lst = sorted(random.sample(range(1, 101), samples))
+ test_bin_logical(method, prec_lst, iter)
+
+ prec_lst = sorted(random.sample(range(1, 101), samples))
+ test_from_float(prec_lst)
Added: python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/deccheck.py
==============================================================================
--- (empty file)
+++ python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/deccheck.py Thu Jan 7 13:23:07 2010
@@ -0,0 +1,1295 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2008-2010 Stefan Krah. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+#
+
+#
+# The cdec (for "check decimal") class checks cdecimal.so against decimal.py.
+# A cdec object consists of a cdecimal.Decimal and a decimal.Decimal. Every
+# operation is carried out on both types. If the results don't match, an
+# exception is raised.
+#
+# Usage: python deccheck.py [--short|--medium|--long|--all]
+#
+
+
+import cdecimal, decimal
+import sys, inspect
+import platform
+from copy import copy
+
+
+py_minor = sys.version_info[1]
+py_micro = sys.version_info[2]
+
+
+# Translate symbols.
+deccond = {
+ cdecimal.Clamped: decimal.Clamped,
+ cdecimal.ConversionSyntax: decimal.ConversionSyntax,
+ cdecimal.DivisionByZero: decimal.DivisionByZero,
+ cdecimal.DivisionImpossible: decimal.InvalidOperation,
+ cdecimal.DivisionUndefined: decimal.DivisionUndefined,
+ cdecimal.Inexact: decimal.Inexact,
+ cdecimal.InvalidContext: decimal.InvalidContext,
+ cdecimal.InvalidOperation: decimal.InvalidOperation,
+ cdecimal.Overflow: decimal.Overflow,
+ cdecimal.Rounded: decimal.Rounded,
+ cdecimal.Subnormal: decimal.Subnormal,
+ cdecimal.Underflow: decimal.Underflow,
+}
+
+mpdcond = {
+ decimal.Clamped: cdecimal.Clamped,
+ decimal.ConversionSyntax: cdecimal.ConversionSyntax,
+ decimal.DivisionByZero: cdecimal.DivisionByZero,
+ decimal.InvalidOperation: cdecimal.DivisionImpossible,
+ decimal.DivisionUndefined: cdecimal.DivisionUndefined,
+ decimal.Inexact: cdecimal.Inexact,
+ decimal.InvalidContext: cdecimal.InvalidContext,
+ decimal.InvalidOperation: cdecimal.InvalidOperation,
+ decimal.Overflow: cdecimal.Overflow,
+ decimal.Rounded: cdecimal.Rounded,
+ decimal.Subnormal: cdecimal.Subnormal,
+ decimal.Underflow: cdecimal.Underflow
+}
+
+decround = {
+ cdecimal.ROUND_UP: decimal.ROUND_UP,
+ cdecimal.ROUND_DOWN: decimal.ROUND_DOWN,
+ cdecimal.ROUND_CEILING: decimal.ROUND_CEILING,
+ cdecimal.ROUND_FLOOR: decimal.ROUND_FLOOR,
+ cdecimal.ROUND_HALF_UP: decimal.ROUND_HALF_UP,
+ cdecimal.ROUND_HALF_DOWN: decimal.ROUND_HALF_DOWN,
+ cdecimal.ROUND_HALF_EVEN: decimal.ROUND_HALF_EVEN,
+ cdecimal.ROUND_05UP: decimal.ROUND_05UP
+}
+
+
+class Context(object):
+ """Provides a convenient way of syncing the cdecimal and decimal contexts"""
+
+ __slots__ = ['f', 'd']
+
+ def __init__(self, mpdctx=cdecimal.getcontext()):
+ """Initialization is from the cdecimal context"""
+ self.f = mpdctx
+ self.d = decimal.getcontext()
+ self.d.prec = self.f.prec
+ self.d.Emin = self.f.Emin
+ self.d.Emax = self.f.Emax
+ self.d.rounding = decround[self.f.rounding]
+ self.d.capitals = self.f.capitals
+ self.settraps([sig for sig in self.f.traps if self.f.traps[sig]])
+ self.setstatus([sig for sig in self.f.flags if self.f.flags[sig]])
+ self.d._clamp = self.f._clamp
+
+ def getprec(self):
+ assert(self.f.prec == self.d.prec)
+ return self.f.prec
+
+ def setprec(self, val):
+ self.f.prec = val
+ self.d.prec = val
+
+ def getemin(self):
+ assert(self.f.Emin == self.d.Emin)
+ return self.f.Emin
+
+ def setemin(self, val):
+ self.f.Emin = val
+ self.d.Emin = val
+
+ def getemax(self):
+ assert(self.f.Emax == self.d.Emax)
+ return self.f.Emax
+
+ def setemax(self, val):
+ self.f.Emax = val
+ self.d.Emax = val
+
+ def getround(self):
+ assert(self.f.rounding == self.d.rounding)
+ return self.f.rounding
+
+ def setround(self, val):
+ self.f.rounding = val
+ self.d.rounding = decround[val]
+
+ def getcapitals(self):
+ assert(self.f.capitals == self.d.capitals)
+ return self.f.capitals
+
+ def setcapitals(self, val):
+ self.f.capitals = val
+ self.d.capitals = val
+
+ def getclamp(self):
+ assert(self.f._clamp == self.d._clamp)
+ return self.f._clamp
+
+ def setclamp(self, val):
+ self.f._clamp = val
+ self.d._clamp = val
+
+ prec = property(getprec, setprec)
+ emin = property(getemin, setemin)
+ emax = property(getemax, setemax)
+ round = property(getround, setround)
+ clamp = property(getclamp, setclamp)
+ capitals = property(getcapitals, setcapitals)
+
+ def clear_traps(self):
+ self.f.clear_traps()
+ for trap in self.d.traps:
+ self.d.traps[trap] = False
+
+ def clear_status(self):
+ self.f.clear_flags()
+ self.d.clear_flags()
+
+ def settraps(self, lst): # cdecimal signal list
+ self.clear_traps()
+ for signal in lst:
+ self.f.traps[signal] = True
+ self.d.traps[deccond[signal]] = True
+
+ def setstatus(self, lst): # cdecimal signal list
+ self.clear_status()
+ for signal in lst:
+ self.f.flags[signal] = True
+ self.d.flags[deccond[signal]] = True
+
+ def assert_eq_status(self):
+ """assert equality of cdecimal and decimal status"""
+ for signal in self.f.flags:
+ if self.f.flags[signal] == (not self.d.flags[deccond[signal]]):
+ return False
+ return True
+
+
+# We don't want exceptions so that we can compare the status flags.
+context = Context()
+context.clear_traps()
+
+# When creating decimals, cdecimal is ultimately limited by the maximum
+# context values. We emulate this restriction for decimal.py.
+maxcontext = decimal.Context(
+ prec=cdecimal.MAX_PREC,
+ Emin=cdecimal.MIN_EMIN,
+ Emax=cdecimal.MAX_EMAX,
+ rounding=decimal.ROUND_HALF_UP,
+ capitals=1
+)
+maxcontext._clamp = 0
+
+def decimal_new(value):
+ maxcontext.traps = copy(context.d.traps)
+ maxcontext.clear_flags()
+ dec = maxcontext.create_decimal(value)
+ if maxcontext.flags[decimal.Inexact] or \
+ maxcontext.flags[decimal.Rounded]:
+ dec = decimal.Decimal("NaN")
+ context.d.flags[decimal.InvalidOperation] = True
+ return dec
+
+
+_exc_fmt = "\
+cdecimal_sci: %s\n\
+decimal_sci: %s\n\
+cdecimal_eng: %s\n\
+decimal_eng: %s\n"
+
+_exc_fmt_tuple = "\
+cdecimal_tuple: %s\n\
+decimal_tuple: %s\n"
+
+_exc_fmt_obj = "\
+cdecimal: %s\n\
+decimal: %s\n\n"
+
+class CdecException(ArithmeticError):
+ def __init__(self, result, funcname, operands):
+ self.errstring = "Error in %s(%s" % (funcname, operands[0])
+ for op in operands[1:]:
+ self.errstring += ", %s" % op
+ self.errstring += "):\n\n"
+ if isinstance(result, cdec):
+ self.errstring += _exc_fmt % (str(result.mpd),
+ str(result.dec),
+ result.mpd.to_eng(),
+ result.dec.to_eng_string())
+ mpd_tuple = result.mpd.as_tuple()
+ dec_tuple = result.dec.as_tuple()
+ if mpd_tuple != dec_tuple:
+ self.errstring += _exc_fmt_tuple % (str(mpd_tuple),
+ str(dec_tuple))
+ else:
+ self.errstring += _exc_fmt_obj % (str(result[0]), str(result[1]))
+ self.errstring += "%s\n%s\n\n" % (str(context.f), str(context.d))
+ def __str__(self):
+ return self.errstring
+
+
+class dHandlerCdec:
+ """For cdec return values:
+
+ Handle known disagreements between decimal.py and cdecimal.so.
+ This is just a temporary measure against cluttered output.
+ Detection is crude and possibly unreliable."""
+
+ def __init__(self):
+ self.logb_round_if_gt_prec = 0
+ self.ulpdiff = 0
+ self.powmod_zeros = 0
+ self.total_mag_nan = 0
+ self.quantize_status = 0
+ self.max_status = 0
+
+ def default(self, result, operands):
+ return False
+
+ def ulp(self, dec):
+ """Harrison ULP: ftp://ftp.inria.fr/INRIA/publication/publi-pdf/RR/RR-5504.pdf"""
+ a = dec.next_plus()
+ b = dec.next_minus()
+ return abs(a - b)
+
+ def bin_resolve_ulp(self, result, funcname, operands):
+ """Results of cdecimal's power function are currently not always
+ correctly rounded. Check if the cdecimal result differs by less
+ than 1 ULP from the correctly rounded decimal.py result."""
+ mpdstr = str(result.mpd)
+ mpdresult = decimal.Decimal(mpdstr)
+ decresult = result.dec
+ deculp = self.ulp(decresult)
+ op1 = operands[0].dec
+ op2 = operands[1].dec
+ tmpctx = context.d.copy()
+ tmpctx.prec *= 2
+ # result, recalculated at double precision
+ dpresult = getattr(op1, funcname)(op2, context=tmpctx)
+ mpddiff = abs(dpresult - mpdresult)
+ if mpddiff >= deculp:
+ return False # not simply a disagreement, but wrong
+ decdiff = abs(dpresult - decresult)
+ if decdiff >= deculp:
+ return False # not simply a disagreement, but wrong
+ self.ulpdiff += 1
+ return True
+
+ def __pow__(self, result, operands):
+ """See DIFFERENCES.txt"""
+ if operands[2] is not None: # three argument __pow__
+ if (operands[0].mpd.is_zero() != operands[1].mpd.is_zero()):
+ if (result.mpd == 0 or result.mpd == 1) and result.dec.is_nan():
+ if (not context.f.flags[cdecimal.InvalidOperation]) and \
+ context.d.flags[decimal.InvalidOperation]:
+ self.powmod_zeros += 1
+ return True
+ elif context.f.flags[cdecimal.Rounded] and \
+ context.f.flags[cdecimal.Inexact] and \
+ context.d.flags[decimal.Rounded] and \
+ context.d.flags[decimal.Inexact]:
+ return self.bin_resolve_ulp(result, "__pow__", operands)
+ else:
+ return False
+ power = __pow__
+
+ def __radd__(self, result, operands):
+ """decimal.py gives preference to the first nan"""
+ if operands[0].mpd.is_nan() and operands[1].mpd.is_nan() and \
+ result.mpd.is_nan() and result.dec.is_nan():
+ return True
+ return False
+ __rmul__ = __radd__
+
+ if py_minor <= 1:
+ def rotate(self, result, operands):
+ """truncate excess digits before the operation"""
+ if len(operands[0].dec._int) > context.f.prec:
+ return True
+ return False
+ shift = rotate
+
+ def compare_total_mag(self, result, operands):
+ if operands[0].mpd.is_nan() and operands[1].mpd.is_nan() and \
+ abs(result.mpd) == 1 and abs(result.dec) == 1:
+ self.total_mag_nan += 1
+ return True
+ return False
+ compare_total = compare_total_mag
+
+ def logb(self, result, operands):
+ if context.f.flags[cdecimal.Rounded] and \
+ (not context.d.flags[decimal.Rounded]):
+ self.logb_round_if_gt_prec += 1
+ return True
+ return False
+
+
+class dHandlerObj():
+ """For non-decimal return values:
+
+ Handle known disagreements between decimal.py and cdecimal.so.
+ Currently there are none."""
+
+ def __init__(self):
+ pass
+
+ def default(self, result, operands):
+ return False
+
+
+dhandler_cdec = dHandlerCdec()
+def cdec_known_disagreement(result, funcname, operands):
+ return getattr(dhandler_cdec, funcname, dhandler_cdec.default)(result, operands)
+
+#dhandler_obj = dHandlerObj()
+#def obj_known_disagreement(result, funcname, operands):
+# return getattr(dhandler_obj, funcname, dhandler_obj.default)(result, operands)
+
+
+
+def verify(result, funcname, operands):
+ """Verifies that after operation 'funcname' with operand(s) 'operands'
+ result[0] and result[1] as well as the context flags have the same
+ values."""
+ if result[0] != result[1] or not context.assert_eq_status():
+ #if obj_known_disagreement(result, funcname, operands):
+ # return # skip known disagreements
+ raise CdecException(result, funcname, operands)
+
+
+class cdec(object):
+ """Joins cdecimal.so and decimal.py for redundant calculations
+ with error checking."""
+
+ __slots__ = ['mpd', 'dec']
+
+ def __new__(cls, value=None):
+ self = object.__new__(cls)
+ self.mpd = None
+ self.dec = None
+ if value is not None:
+ context.clear_status()
+ if isinstance(value, float):
+ self.mpd = cdecimal.Decimal.from_float(value)
+ self.dec = decimal.Decimal.from_float(value)
+ else:
+ self.mpd = cdecimal.Decimal(value)
+ self.dec = decimal_new(value)
+ self.verify('__xnew__', (value,))
+ return self
+
+ def verify(self, funcname, operands):
+ """Verifies that after operation 'funcname' with operand(s) 'operands'
+ self.mpd and self.dec as well as the context flags have the same
+ values."""
+ mpdstr = str(self.mpd)
+ decstr = str(self.dec)
+ mpdstr_eng = self.mpd.to_eng_string()
+ decstr_eng = self.dec.to_eng_string()
+ mpd_tuple = self.mpd.as_tuple()
+ dec_tuple = self.dec.as_tuple()
+ if mpd_tuple != dec_tuple: # XXX
+ if mpd_tuple[2] == 'F' and dec_tuple[2] == 'F' and \
+ mpd_tuple[1] == () and dec_tuple[1] == (0,):
+ return
+ if mpdstr != decstr or mpdstr_eng != decstr_eng or mpd_tuple != dec_tuple \
+ or not context.assert_eq_status():
+ if cdec_known_disagreement(self, funcname, operands):
+ return # skip known disagreements
+ raise CdecException(self, funcname, operands)
+
+ def unaryfunc(self, funcname):
+ "unary function returning a cdec"
+ context.clear_status()
+ c = cdec()
+ c.mpd = getattr(self.mpd, funcname)()
+ c.dec = getattr(self.dec, funcname)()
+ c.verify(funcname, (self,))
+ return c
+
+ def unaryfunc_ctx(self, funcname):
+ "unary function returning a cdec, uses the context methods of decimal.py"
+ context.clear_status()
+ c = cdec()
+ c.mpd = getattr(self.mpd, funcname)()
+ c.dec = getattr(context.d, funcname)(self.dec)
+ c.verify(funcname, (self,))
+ return c
+
+ def obj_unaryfunc(self, funcname):
+ "unary function returning a cdec"
+ context.clear_status()
+ r_mpd = getattr(self.mpd, funcname)()
+ r_dec = getattr(self.dec, funcname)()
+ verify((r_mpd, r_dec), funcname, (self,))
+ return r_mpd
+
+ def binaryfunc(self, other, funcname):
+ "binary function returning a cdec"
+ context.clear_status()
+ c = cdec()
+ other_mpd = other_dec = other
+ if isinstance(other, cdec):
+ other_mpd = other.mpd
+ other_dec = other.dec
+ c.mpd = getattr(self.mpd, funcname)(other_mpd)
+ c.dec = getattr(self.dec, funcname)(other_dec)
+ c.verify(funcname, (self, other))
+ return c
+
+ def binaryfunc_ctx(self, other, funcname):
+ "binary function returning a cdec, uses the context methods of decimal.py"
+ context.clear_status()
+ c = cdec()
+ other_mpd = other_dec = other
+ if isinstance(other, cdec):
+ other_mpd = other.mpd
+ other_dec = other.dec
+ c.mpd = getattr(self.mpd, funcname)(other_mpd)
+ c.dec = getattr(context.d, funcname)(self.dec, other_dec)
+ c.verify(funcname, (self, other))
+ return c
+
+ def obj_binaryfunc(self, other, funcname):
+ "binary function returning an object other than a cdec"
+ context.clear_status()
+ other_mpd = other_dec = other
+ if isinstance(other, cdec):
+ other_mpd = other.mpd
+ other_dec = other.dec
+ r_mpd = getattr(self.mpd, funcname)(other_mpd)
+ r_dec = getattr(self.dec, funcname)(other_dec)
+ verify((r_mpd, r_dec), funcname, (self, other))
+ return r_mpd
+
+ def ternaryfunc(self, other, third, funcname):
+ "ternary function returning a cdec"
+ context.clear_status()
+ c = cdec()
+ other_mpd = other_dec = other
+ if isinstance(other, cdec):
+ other_mpd = other.mpd
+ other_dec = other.dec
+ third_mpd = third_dec = third
+ if isinstance(third, cdec):
+ third_mpd = third.mpd
+ third_dec = third.dec
+ c.mpd = getattr(self.mpd, funcname)(other_mpd, third_mpd)
+ c.dec = getattr(self.dec, funcname)(other_dec, third_dec)
+ c.verify(funcname, (self, other, third))
+ return c
+
+ def __abs__(self):
+ return self.unaryfunc('__abs__')
+
+ def __add__(self, other):
+ return self.binaryfunc(other, '__add__')
+
+ def __bool__(self):
+ return self.obj_unaryfunc('__bool__')
+
+ def __copy__(self):
+ return self.unaryfunc('__copy__')
+
+ def __deepcopy__(self, memo=None):
+ context.clear_status()
+ c = cdec()
+ c.mpd = self.mpd.__deepcopy__(memo)
+ c.dec = self.dec.__deepcopy__(memo)
+ c.verify('__deepcopy__', (self,))
+ return c
+
+ def __div__(self, other):
+ return self.binaryfunc(other, '__div__')
+
+ def __divmod__(self, other):
+ context.clear_status()
+ q = cdec()
+ r = cdec()
+ other_mpd = other_dec = other
+ if isinstance(other, cdec):
+ other_mpd = other.mpd
+ other_dec = other.dec
+ q.mpd, r.mpd = self.mpd.__divmod__(other_mpd)
+ q.dec, r.dec = self.dec.__divmod__(other_dec, context.d)
+ q.verify('__divmod__', (self, other))
+ r.verify('__divmod__', (self, other))
+ return (q, r)
+
+ def __eq__(self, other):
+ return self.obj_binaryfunc(other, '__eq__')
+
+ def __float__(self):
+ if (self.mpd.is_nan() and self.dec.is_nan()):
+ return float("NaN")
+ try:
+ return self.obj_unaryfunc('__float__')
+ except ValueError:
+ return None
+
+ def __floordiv__(self, other):
+ return self.binaryfunc(other, '__floordiv__')
+
+ def __ge__(self, other):
+ return self.obj_binaryfunc(other, '__ge__')
+
+ def __gt__(self, other):
+ return self.obj_binaryfunc(other, '__gt__')
+
+ def __hash__(self):
+ global PY25_HASH_HAVE_WARNED
+ if self.mpd.is_nan():
+ return cdec(0) # for testing
+ raise TypeError('Cannot hash a NaN value.')
+ ret = None
+ try: # Python 2.5 can use exorbitant amounts of memory
+ ret = self.obj_unaryfunc('__hash__')
+ except MemoryError:
+ if not PY25_HASH_HAVE_WARNED:
+ sys.stderr.write("Out of memory while hashing %s: upgrade to Python 2.6\n"
+ % str(self.mpd))
+ PY25_HASH_HAVE_WARNED = 1
+ return ret
+
+ def __int__(self):
+ # ValueError or OverflowError
+ if self.mpd.is_special():
+ return (None, None)
+ return self.obj_unaryfunc('__int__')
+
+ def __le__(self, other):
+ return self.obj_binaryfunc(other, '__le__')
+
+ def __long__(self):
+ # ValueError or OverflowError
+ if self.mpd.is_special():
+ return (None, None)
+ return self.obj_unaryfunc('__long__')
+
+ def __lt__(self, other):
+ return self.obj_binaryfunc(other, '__lt__')
+
+ def __mod__(self, other):
+ return self.binaryfunc(other, '__mod__')
+
+ def __mul__(self, other):
+ return self.binaryfunc(other, '__mul__')
+
+ def __ne__(self, other):
+ return self.obj_binaryfunc(other, '__ne__')
+
+ def __neg__(self):
+ return self.unaryfunc('__neg__')
+
+ def __nonzero__(self):
+ return self.obj_unaryfunc('__nonzero__')
+
+ def __pos__(self):
+ return self.unaryfunc('__pos__')
+
+ def __pow__(self, other, mod=None):
+ return self.ternaryfunc(other, mod, '__pow__')
+
+ def __radd__(self, other):
+ return self.binaryfunc(other, '__radd__')
+
+ def __rdiv__(self, other):
+ return self.binaryfunc(other, '__rdiv__')
+
+ def __rdivmod__(self, other):
+ context.clear_status()
+ q = cdec()
+ r = cdec()
+ other_mpd = other_dec = other
+ if isinstance(other, cdec):
+ other_mpd = other.mpd
+ other_dec = other.dec
+ q.mpd, r.mpd = self.mpd.__rdivmod__(other_mpd)
+ q.dec, r.dec = self.dec.__rdivmod__(other_dec, context.d)
+ q.verify('__rdivmod__', (self, other))
+ r.verify('__rdivmod__', (self, other))
+ return (q, r)
+
+ # __reduce__
+
+ def __repr__(self):
+ self.obj_unaryfunc('__repr__')
+ return "cdec('" + str(self.mpd) + "')"
+
+ def __rfloordiv__(self, other):
+ return self.binaryfunc(other, '__rfloordiv__')
+
+ def __rmod__(self, other):
+ return self.binaryfunc(other, '__rmod__')
+
+ def __rmul__(self, other):
+ return self.binaryfunc(other, '__rmul__')
+
+ def __rsub__(self, other):
+ return self.binaryfunc(other, '__rsub__')
+
+ def __rtruediv__(self, other):
+ return self.binaryfunc(other, '__rtruediv__')
+
+ def __rpow__(self, other):
+ return other.__pow__(self)
+
+ def __str__(self):
+ self.obj_unaryfunc('__str__')
+ return str(self.mpd)
+
+ def __sub__(self, other):
+ return self.binaryfunc(other, '__sub__')
+
+ def __truediv__(self, other):
+ return self.binaryfunc(other, '__truediv__')
+
+ def __trunc__(self):
+ # ValueError or OverflowError
+ if self.mpd.is_special():
+ return (None, None)
+ return self.obj_unaryfunc('__trunc__')
+
+ def _apply(self):
+ return self.unaryfunc('_apply')
+
+ def abs(self):
+ return self.unaryfunc_ctx('abs')
+
+ def add(self, other):
+ return self.binaryfunc_ctx(other, 'add')
+
+ def adjusted(self):
+ return self.obj_unaryfunc('adjusted')
+
+ def canonical(self):
+ return self.unaryfunc('canonical')
+
+ def compare(self, other):
+ return self.binaryfunc(other, 'compare')
+
+ def compare_signal(self, other):
+ return self.binaryfunc(other, 'compare_signal')
+
+ def compare_total(self, other):
+ return self.binaryfunc(other, 'compare_total')
+
+ def compare_total_mag(self, other):
+ return self.binaryfunc(other, 'compare_total_mag')
+
+ def copy_abs(self):
+ return self.unaryfunc('copy_abs')
+
+ def copy_negate(self):
+ return self.unaryfunc('copy_negate')
+
+ def copy_sign(self, other):
+ return self.binaryfunc(other, 'copy_sign')
+
+ def divide(self, other):
+ return self.binaryfunc_ctx(other, 'divide')
+
+ def divide_int(self, other):
+ return self.binaryfunc_ctx(other, 'divide_int')
+
+ def divmod(self, other):
+ context.clear_status()
+ q = cdec()
+ r = cdec()
+ other_mpd = other_dec = other
+ if isinstance(other, cdec):
+ other_mpd = other.mpd
+ other_dec = other.dec
+ q.mpd, r.mpd = self.mpd.divmod(other_mpd)
+ q.dec, r.dec = context.d.divmod(self.dec, other_dec)
+ q.verify('divmod', (self, other))
+ r.verify('divmod', (self, other))
+ return (q, r)
+
+ def exp(self):
+ return self.unaryfunc('exp')
+
+ def fma(self, other, third):
+ return self.ternaryfunc(other, third, 'fma')
+
+ # imag
+ # invroot
+
+ def is_canonical(self):
+ return self.obj_unaryfunc('is_canonical')
+
+ def is_finite(self):
+ return self.obj_unaryfunc('is_finite')
+
+ def is_infinite(self):
+ return self.obj_unaryfunc('is_infinite')
+
+ def is_nan(self):
+ return self.obj_unaryfunc('is_nan')
+
+ def is_normal(self):
+ return self.obj_unaryfunc('is_normal')
+
+ def is_qnan(self):
+ return self.obj_unaryfunc('is_qnan')
+
+ def is_signed(self):
+ return self.obj_unaryfunc('is_signed')
+
+ def is_snan(self):
+ return self.obj_unaryfunc('is_snan')
+
+ def is_subnormal(self):
+ return self.obj_unaryfunc('is_subnormal')
+
+ def is_zero(self):
+ return self.obj_unaryfunc('is_zero')
+
+ def ln(self):
+ return self.unaryfunc('ln')
+
+ def log10(self):
+ return self.unaryfunc('log10')
+
+ def logb(self):
+ return self.unaryfunc('logb')
+
+ def logical_and(self, other):
+ return self.binaryfunc(other, 'logical_and')
+
+ def logical_invert(self):
+ return self.unaryfunc('logical_invert')
+
+ def logical_or(self, other):
+ return self.binaryfunc(other, 'logical_or')
+
+ def logical_xor(self, other):
+ return self.binaryfunc(other, 'logical_xor')
+
+ def max(self, other):
+ return self.binaryfunc(other, 'max')
+
+ def max_mag(self, other):
+ return self.binaryfunc(other, 'max_mag')
+
+ def min(self, other):
+ return self.binaryfunc(other, 'min_mag')
+
+ def min_mag(self, other):
+ return self.binaryfunc(other, 'min_mag')
+
+ def minus(self):
+ return self.unaryfunc_ctx('minus')
+
+ def multiply(self, other):
+ return self.binaryfunc_ctx(other, 'multiply')
+
+ def next_minus(self):
+ return self.unaryfunc('next_minus')
+
+ def next_plus(self):
+ return self.unaryfunc('next_plus')
+
+ def next_toward(self, other):
+ return self.binaryfunc(other, 'next_toward')
+
+ def normalize(self):
+ return self.unaryfunc('normalize')
+
+ def number_class(self):
+ return self.obj_unaryfunc('number_class')
+
+ def plus(self):
+ return self.unaryfunc_ctx('plus')
+
+ def power(self, other, third=None):
+ "ternary function returning a cdec, uses the context methods of decimal.py"
+ context.clear_status()
+ c = cdec()
+ other_mpd = other_dec = other
+ if isinstance(other, cdec):
+ other_mpd = other.mpd
+ other_dec = other.dec
+ third_mpd = third_dec = third
+ if isinstance(third, cdec):
+ third_mpd = third.mpd
+ third_dec = third.dec
+ if (third is not None):
+ c.mpd = getattr(self.mpd, 'powmod')(other_mpd, third_mpd)
+ else:
+ c.mpd = getattr(self.mpd, 'pow')(other_mpd)
+ c.dec = getattr(context.d, 'power')(self.dec, other_dec, third_dec)
+ c.verify('power', (self, other, third))
+ return c
+
+ # powmod: same as __pow__ or power with three arguments
+
+ def quantize(self, other):
+ return self.binaryfunc(other, 'quantize')
+
+ def radix(self):
+ return self.obj_unaryfunc('radix')
+
+ # real
+ # reduce: same as normalize
+
+ def remainder(self, other):
+ return self.binaryfunc_ctx(other, 'remainder')
+
+ def remainder_near(self, other):
+ return self.binaryfunc(other, 'remainder_near')
+
+ def rotate(self, other):
+ return self.binaryfunc(other, 'rotate')
+
+ def same_quantum(self, other):
+ return self.obj_binaryfunc(other, 'same_quantum')
+
+ def scaleb(self, other):
+ return self.binaryfunc(other, 'scaleb')
+
+ def shift(self, other):
+ return self.binaryfunc(other, 'shift')
+
+ # sign
+
+ def sqrt(self):
+ return self.unaryfunc('sqrt')
+
+ def subtract(self, other):
+ return self.binaryfunc_ctx(other, 'subtract')
+
+ def to_eng_string(self):
+ return self.obj_unaryfunc('to_eng_string')
+
+ def to_integral(self):
+ return self.unaryfunc('to_integral')
+
+ def to_integral_exact(self):
+ return self.unaryfunc('to_integral_exact')
+
+ def to_integral_value(self):
+ return self.unaryfunc('to_integral_value')
+
+ def to_sci_string(self):
+ context.clear_status()
+ # cdecimal's Decimal has a 'to_sci_string' method
+ # that honours the default context.
+ r_mpd = self.mpd.to_sci_string()
+ r_dec = context.d.to_sci_string(self.dec)
+ verify((r_mpd, r_dec), 'to_sci_string', (self,))
+ return r_mpd
+
+
+def log(fmt, args=None):
+ if args:
+ sys.stdout.write(''.join((fmt, '\n')) % args)
+ else:
+ sys.stdout.write(''.join((str(fmt), '\n')))
+ sys.stdout.flush()
+
+def test_unary(method, prec_lst, iter):
+ log("testing %s ...", method)
+ for prec in prec_lst:
+ log(" prec: %d", prec)
+ context.prec = prec
+ for round in sorted(decround):
+ context.round = round
+ rprec = 10**prec
+ exprange = cdecimal.MAX_EMAX
+ if method in ['__int__', '__long__', '__trunc__', 'to_integral', \
+ 'to_integral_value', 'to_integral_value']:
+ exprange = 9999
+ for a in un_close_to_pow10(prec, exprange, iter):
+ try:
+ x = cdec(a)
+ getattr(x, method)()
+ except CdecException as err:
+ log(err)
+ for a in un_close_numbers(prec, exprange, -exprange, iter):
+ try:
+ x = cdec(a)
+ getattr(x, method)()
+ except CdecException as err:
+ log(err)
+ for a in un_incr_digits_tuple(prec, exprange, iter):
+ try:
+ x = cdec(a)
+ getattr(x, method)()
+ except CdecException as err:
+ log(err)
+ for i in range(1000):
+ try:
+ s = randdec(prec, exprange)
+ x = cdec(s)
+ getattr(x, method)()
+ except CdecException as err:
+ log(err)
+ except OverflowError:
+ pass
+ try:
+ s = randtuple(prec, exprange)
+ x = cdec(s)
+ getattr(x, method)()
+ except CdecException as err:
+ log(err)
+ except OverflowError:
+ pass
+
+def test_un_logical(method, prec_lst, iter):
+ log("testing %s ...", method)
+ for prec in prec_lst:
+ log(" prec: %d", prec)
+ context.prec = prec
+ for round in sorted(decround):
+ context.round = round
+ for a in logical_un_incr_digits(prec, iter):
+ try:
+ x = cdec(a)
+ getattr(x, method)()
+ except CdecException as err:
+ log(err)
+ for i in range(1000):
+ try:
+ s = randdec(prec, 999999)
+ x = cdec(s)
+ getattr(x, method)()
+ except CdecException as err:
+ log(err)
+ except OverflowError:
+ pass
+
+def test_binary(method, prec_lst, iter):
+ log("testing %s ...", method)
+ for prec in prec_lst:
+ log(" prec: %d", prec)
+ context.prec = prec
+ for round in sorted(decround):
+ context.round = round
+ exprange = cdecimal.MAX_EMAX
+ if method in ['__pow__', '__rpow__', 'power']:
+ exprange = 99999
+ for a, b in bin_close_to_pow10(prec, exprange, iter):
+ try:
+ x = cdec(a)
+ y = cdec(b)
+ getattr(x, method)(y)
+ except CdecException as err:
+ log(err)
+ for a, b in bin_close_numbers(prec, exprange, -exprange, iter):
+ try:
+ x = cdec(a)
+ y = cdec(b)
+ getattr(x, method)(y)
+ except CdecException as err:
+ log(err)
+ for a, b in bin_incr_digits(prec, exprange, iter):
+ try:
+ x = cdec(a)
+ y = cdec(b)
+ getattr(x, method)(y)
+ except CdecException as err:
+ log(err)
+ for i in range(1000):
+ s1 = randdec(prec, exprange)
+ s2 = randdec(prec, exprange)
+ try:
+ x = cdec(s1)
+ y = cdec(s2)
+ getattr(x, method)(y)
+ except CdecException as err:
+ log(err)
+
+def test_bin_logical(method, prec_lst, iter):
+ log("testing %s ...", method)
+ for prec in prec_lst:
+ log(" prec: %d", prec)
+ context.prec = prec
+ for round in sorted(decround):
+ context.round = round
+ for a, b in logical_bin_incr_digits(prec, iter):
+ try:
+ x = cdec(a)
+ y = cdec(b)
+ getattr(x, method)(y)
+ except CdecException as err:
+ log(err)
+ for i in range(1000):
+ s1 = randdec(prec, 999999)
+ s2 = randdec(prec, 999999)
+ try:
+ x = cdec(s1)
+ y = cdec(s2)
+ getattr(x, method)(y)
+ except CdecException as err:
+ log(err)
+
+def test_ternary(method, prec_lst, iter):
+ log("testing %s ...", method)
+ for prec in prec_lst:
+ log(" prec: %d", prec)
+ context.prec = prec
+ for round in sorted(decround):
+ context.round = round
+ exprange = cdecimal.MAX_EMAX
+ if method in ['__pow__', 'power']:
+ exprange = 99999
+ for a, b, c in tern_close_numbers(prec, exprange, -exprange, iter):
+ try:
+ x = cdec(a)
+ y = cdec(b)
+ z = cdec(c)
+ getattr(x, method)(y, z)
+ except CdecException as err:
+ log(err)
+ for a, b, c in tern_incr_digits(prec, exprange, iter):
+ try:
+ x = cdec(a)
+ y = cdec(b)
+ z = cdec(c)
+ getattr(x, method)(y, z)
+ except CdecException as err:
+ log(err)
+ for i in range(1000):
+ s1 = randdec(prec, 2*exprange)
+ s2 = randdec(prec, 2*exprange)
+ s3 = randdec(prec, 2*exprange)
+ try:
+ x = cdec(s1)
+ y = cdec(s2)
+ z = cdec(s3)
+ getattr(x, method)(y, z)
+ except CdecException as err:
+ log(err)
+
+def test_format(prec_lst, iter):
+ log("testing format")
+ for prec in prec_lst:
+ log(" prec: %d", prec)
+ context.prec = prec
+ for round in sorted(decround):
+ context.round = round
+ for a in un_incr_digits_tuple(prec, 9999, iter):
+ try:
+ fmt = rand_format(chr(random.randrange(32, 128)))
+ x = format(context.f.create_decimal(a), fmt)
+ y = format(context.d.create_decimal(a), fmt)
+ except Exception as err:
+ print(err, fmt)
+ continue
+ if x != y:
+ print(context.f)
+ print(context.d)
+ print("\n%s %s" % (a, fmt))
+ print("%s %s\n" % (x, y))
+ for i in range(1000):
+ try:
+ a = randdec(99, 9999)
+ fmt = rand_format(chr(random.randrange(32, 128)))
+ x = format(context.f.create_decimal(a), fmt)
+ y = format(context.d.create_decimal(a), fmt)
+ except Exception as err:
+ print(err, fmt)
+ continue
+ if x != y:
+ print(context.f)
+ print(context.d)
+ print("\n%s %s" % (a, fmt))
+ print("%s %s\n" % (x, y))
+
+def test_locale(prec_lst, iter):
+ import array
+ log("testing locale")
+ for prec in prec_lst:
+ log(" prec: %d", prec)
+ context.prec = prec
+ for round in sorted(decround):
+ context.round = round
+ for a in un_incr_digits_tuple(prec, 9999, iter):
+ try:
+ fmt = rand_locale()
+ x = format(context.f.create_decimal(a), fmt)
+ y = format(context.d.create_decimal(a), fmt)
+ except Exception as err:
+ print(err, fmt)
+ continue
+ if x != y:
+ print(context.f)
+ print(context.d)
+ print(locale.setlocale(locale.LC_NUMERIC))
+ print("%s %s" % (a, fmt))
+ print(list(array.array('u', x)))
+ print(list(array.array('u', y)))
+ for i in range(1000):
+ try:
+ a = randdec(99, 9999)
+ fmt = rand_locale()
+ x = format(context.f.create_decimal(a), fmt)
+ y = format(context.d.create_decimal(a), fmt)
+ except Exception as err:
+ print(err, fmt)
+ continue
+ if x != y:
+ print(context.f)
+ print(context.d)
+ print(locale.setlocale(locale.LC_NUMERIC))
+ print("%s %s" % (a, fmt))
+ print(list(array.array('u', x)))
+ print(list(array.array('u', y)))
+
+def test_round(prec_lst, iter):
+ log("testing round")
+ for prec in prec_lst:
+ log(" prec: %d", prec)
+ context.prec = 99
+ for round in sorted(decround):
+ context.round = round
+ for a in un_incr_digits_tuple(prec, 9999, 1):
+ try:
+ n = random.randrange(10)
+ x = (context.f.create_decimal(a)).__round__(n)
+ y = (context.d.create_decimal(a)).__round__(n)
+ except Exception as err:
+ print(err)
+ continue
+ if str(x) != str(y):
+ print(context.f)
+ print(context.d)
+ print("\n%s %s" % (a, n))
+ print("%s %s\n" % (x, y))
+ exit(1)
+ for i in range(1000):
+ try:
+ a = randdec(99, 9999)
+ n = random.randrange(10)
+ x = context.f.create_decimal(a).__round__(n)
+ y = context.d.create_decimal(a).__round__(n)
+ except Exception as err:
+ print(err)
+ continue
+ if str(x) != str(y):
+ print(context.f)
+ print(context.d)
+ print("\n%s %s" % (a, n))
+ print("%s %s\n" % (x, y))
+
+def test_from_float(prec_lst):
+ log("testing from_float ...")
+ for prec in prec_lst:
+ log(" prec: %d", prec)
+ context.prec = prec
+ for round in sorted(decround):
+ context.round = round
+ exprange = 384
+ for i in range(1000):
+ intpart = str(random.randrange(100000000000000000000000000000000000000))
+ fracpart = str(random.randrange(100000000000000000000000000000000000000))
+ exp = str(random.randrange(-384, 384))
+ fstring = intpart + '.' + fracpart + 'e' + exp
+ f = float(fstring)
+ try:
+ c = cdec(f)
+ except CdecException as err:
+ log(err)
+
+
+if __name__ == '__main__':
+
+ from randdec import *
+ import time
+ import sys
+
+
+ samples = 1
+ iter = 1
+
+ if '--short' in sys.argv:
+ samples = 1
+ iter = 1
+ elif '--medium' in sys.argv:
+ samples = 1
+ iter = None
+ elif '--long' in sys.argv:
+ samples = 5
+ iter = None
+ elif '--all' in sys.argv:
+ samples = 100
+ iter = None
+
+ all_decimal_methods = set(dir(cdecimal.Decimal) + dir(decimal.Decimal))
+ all_cdec_methods = [m for m in dir(cdec) if m in all_decimal_methods]
+ untested_methods = [m for m in all_decimal_methods if not (m in all_cdec_methods)]
+
+ unary_methods = []
+ binary_methods = []
+ ternary_methods = []
+ for m in all_cdec_methods:
+ try:
+ l = len(inspect.getargspec(getattr(cdec, m))[0])
+ except TypeError:
+ continue
+ if l == 1:
+ unary_methods.append(m)
+ elif l == 2:
+ binary_methods.append(m)
+ elif l == 3:
+ ternary_methods.append(m)
+ else:
+ raise ValueError((m, l))
+
+ unary_methods.append('__deepcopy__')
+ binary_methods.remove('__deepcopy__')
+ binary_methods.remove('__new__')
+ binary_methods.append('power')
+ untested_methods.remove('from_float')
+ if py_minor < 6:
+ unary_methods.remove('__trunc__')
+ for elem in ['__ge__', '__gt__', '__le__', '__lt__']:
+ binary_methods.remove(elem)
+
+ untested_methods.sort()
+ unary_methods.sort()
+ binary_methods.sort()
+ ternary_methods.sort()
+
+
+ x = int(time.time())
+ random.seed(x)
+ log("\nRandom seed: %d\n\n", x)
+ log("Skipping tests: \n\n%s\n", untested_methods)
+
+
+ for method in unary_methods:
+ prec_lst = sorted(random.sample(range(1, 101), samples))
+ test_unary(method, prec_lst, iter)
+
+ for method in binary_methods:
+ prec_lst = sorted(random.sample(range(1, 101), samples))
+ test_binary(method, prec_lst, iter)
+
+ for method in ternary_methods:
+ prec_lst = sorted(random.sample(range(1, 101), samples))
+ test_ternary(method, prec_lst, iter)
+
+ prec_lst = sorted(random.sample(range(1, 101), samples))
+ test_un_logical('logical_invert', prec_lst, iter)
+
+ for method in ['logical_and', 'logical_or', 'logical_xor']:
+ prec_lst = sorted(random.sample(range(1, 101), samples))
+ test_bin_logical(method, prec_lst, iter)
+
+ if py_minor >= 2:
+ # Some tests will fail with 3.1, since alignment has been changed
+ # in decimal.py 3.2.
+ from genlocale import *
+ prec_lst = sorted(random.sample(range(1, 101), samples))
+ test_format(prec_lst, iter)
+ test_locale(prec_lst, iter)
+ test_round(prec_lst, iter)
+ test_from_float(prec_lst)
Added: python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/formathelper.py
==============================================================================
--- (empty file)
+++ python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/formathelper.py Thu Jan 7 13:23:07 2010
@@ -0,0 +1,193 @@
+#
+# Copyright (c) 2008-2010 Stefan Krah. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+#
+
+
+import sys, locale, random
+import platform, subprocess
+from randdec import *
+from decimal import *
+
+
+windows_lang_strings = [
+ "chinese", "chinese-simplified", "chinese-traditional", "czech", "danish",
+ "dutch", "belgian", "english", "australian", "canadian", "english-nz",
+ "english-uk", "english-us", "finnish", "french", "french-belgian",
+ "french-canadian", "french-swiss", "german", "german-austrian",
+ "german-swiss", "greek", "hungarian", "icelandic", "italian", "italian-swiss",
+ "japanese", "korean", "norwegian", "norwegian-bokmal", "norwegian-nynorsk",
+ "polish", "portuguese", "portuguese-brazil", "russian", "slovak", "spanish",
+ "spanish-mexican", "spanish-modern", "swedish", "turkish",
+]
+
+preferred_encoding = {
+ 'cs_CZ': 'ISO8859-2',
+ 'cs_CZ.iso88592': 'ISO8859-2',
+ 'czech': 'ISO8859-2',
+ 'eesti': 'ISO8859-1',
+ 'estonian': 'ISO8859-1',
+ 'et_EE': 'ISO8859-15',
+ 'et_EE.ISO-8859-15': 'ISO8859-15',
+ 'et_EE.iso885915': 'ISO8859-15',
+ 'et_EE.iso88591': 'ISO8859-1',
+ 'fi_FI.iso88591': 'ISO8859-1',
+ 'fi_FI': 'ISO8859-15',
+ 'fi_FI at euro': 'ISO8859-15',
+ 'fi_FI.iso885915 at euro': 'ISO8859-15',
+ 'finnish': 'ISO8859-1',
+ 'lv_LV': 'ISO8859-13',
+ 'lv_LV.iso885913': 'ISO8859-13',
+ 'nb_NO': 'ISO8859-1',
+ 'nb_NO.iso88591': 'ISO8859-1',
+ 'bokmal': 'ISO8859-1',
+ 'nn_NO': 'ISO8859-1',
+ 'nn_NO.iso88591': 'ISO8859-1',
+ 'no_NO': 'ISO8859-1',
+ 'norwegian': 'ISO8859-1',
+ 'nynorsk': 'ISO8859-1',
+ 'ru_RU': 'ISO8859-5',
+ 'ru_RU.iso88595': 'ISO8859-5',
+ 'russian': 'ISO8859-5',
+ 'ru_RU.KOI8-R': 'KOI8-R',
+ 'ru_RU.koi8r': 'KOI8-R',
+ 'ru_RU.CP1251': 'CP1251',
+ 'ru_RU.cp1251': 'CP1251',
+ 'sk_SK': 'ISO8859-2',
+ 'sk_SK.iso88592': 'ISO8859-2',
+ 'slovak': 'ISO8859-2',
+ 'sv_FI': 'ISO8859-1',
+ 'sv_FI.iso88591': 'ISO8859-1',
+ 'sv_FI at euro': 'ISO8859-15',
+ 'sv_FI.iso885915 at euro': 'ISO8859-15',
+ 'uk_UA': 'KOI8-U',
+ 'uk_UA.koi8u': 'KOI8-U'
+}
+
+integers = [
+ "",
+ "1",
+ "12",
+ "123",
+ "1234",
+ "12345",
+ "123456",
+ "1234567",
+ "12345678",
+ "123456789",
+ "1234567890",
+ "12345678901",
+ "123456789012",
+ "1234567890123",
+ "12345678901234",
+ "123456789012345",
+ "1234567890123456",
+ "12345678901234567",
+ "123456789012345678",
+ "1234567890123456789",
+ "12345678901234567890",
+ "123456789012345678901",
+ "1234567890123456789012",
+]
+
+numbers = [
+ "0", "-0", "+0",
+ "0.0", "-0.0", "+0.0",
+ "0e0", "-0e0", "+0e0",
+ ".0", "-.0",
+ ".1", "-.1",
+ "1.1", "-1.1",
+ "1e1", "-1e1"
+]
+
+py_major = sys.version_info[0]
+
+if platform.system() == 'Windows':
+ locale_list = windows_lang_strings
+else:
+ try:
+ # On Ubuntu, `locale -a` gives the wrong case for some locales,
+ # so we get the correct names directly:
+ f = open("/var/lib/locales/supported.d/local")
+ locale_list = [loc.split()[0] for loc in f.readlines()]
+ except:
+ locale_list = subprocess.Popen(["locale", "-a"],
+ stdout=subprocess.PIPE).communicate()[0]
+ if py_major == 3:
+ locale_list = locale_list.decode()
+ locale_list = locale_list.split('\n')
+
+
+if py_major < 3:
+ # issue7327 (min_width and multibyte separators): wont_fix
+ save_loc = locale.setlocale(locale.LC_NUMERIC)
+ for loc in locale_list[:]:
+ try:
+ locale.setlocale(locale.LC_NUMERIC, loc)
+ except locale.Error:
+ locale_list.remove(loc)
+ continue
+ d = locale.localeconv()
+ if len(d['thousands_sep']) > 1 or len(d['decimal_point']) > 1:
+ locale_list.remove(loc)
+ locale.setlocale(locale.LC_NUMERIC, save_loc)
+
+try:
+ locale_list.remove('')
+ # Debian
+ locale_list.remove(b'bokm\xc3\xa5l'.decode('utf-8'))
+ locale_list.remove(b'fran\xc3\xa7ais'.decode('utf-8'))
+except:
+ pass
+
+if platform.system() == 'FreeBSD':
+ # http://www.freebsd.org/cgi/query-pr.cgi?pr=142173
+ for loc in ['it_CH.ISO8859-1', 'it_CH.ISO8859-15', 'it_CH.UTF-8', 'it_IT.ISO8859-1',
+ 'it_IT.ISO8859-15', 'it_IT.UTF-8', 'sl_SI.ISO8859-2', 'sl_SI.UTF-8']:
+ try:
+ locale_list.remove(loc)
+ except:
+ pass
+
+
+def get_preferred_encoding():
+ loc = locale.setlocale(locale.LC_CTYPE)
+ if loc in preferred_encoding:
+ return preferred_encoding[loc]
+ else:
+ return locale.getpreferredencoding()
+
+if py_major < 3:
+ def printit(testno, s, fmt, encoding=None):
+ if not encoding:
+ encoding = get_preferred_encoding()
+ try:
+ result = format(Decimal(s), fmt)
+ if isinstance(fmt, unicode):
+ fmt = repr(fmt.encode(encoding))[1:-1]
+ if isinstance(result, unicode):
+ result = repr(result.encode(encoding))[1:-1]
+ if "'" in result:
+ sys.stdout.write("xfmt%d format %s '%s' -> \"%s\"\n"
+ % (testno, s, fmt, result))
+ else:
+ sys.stdout.write("xfmt%d format %s '%s' -> '%s'\n"
+ % (testno, s, fmt, result))
+ except Exception as err:
+ sys.stderr.write("%s %s %s\n" % (err, s, fmt))
+else:
+ def printit(testno, s, fmt, encoding=None):
+ if not encoding:
+ encoding = get_preferred_encoding()
+ try:
+ result = format(Decimal(s), fmt)
+ fmt = str(fmt.encode(encoding))[2:-1]
+ result = str(result.encode(encoding))[2:-1]
+ if "'" in result:
+ sys.stdout.write("xfmt%d format %s '%s' -> \"%s\"\n"
+ % (testno, s, fmt, result))
+ else:
+ sys.stdout.write("xfmt%d format %s '%s' -> '%s'\n"
+ % (testno, s, fmt, result))
+ except Exception as err:
+ sys.stderr.write("%s %s %s\n" % (err, s, fmt))
Added: python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/genlocale.py
==============================================================================
--- (empty file)
+++ python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/genlocale.py Thu Jan 7 13:23:07 2010
@@ -0,0 +1,172 @@
+#
+# Copyright (c) 2008-2010 Stefan Krah. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+#
+
+
+#
+# Very extensive test that comes close to brute force testing
+# all format string combinations containing either a thousands
+# separator or the 'n' specifier.
+#
+# Usage: python3.2 genlocale.py | ../runtest -
+#
+
+
+from formathelper import *
+
+
+# Generate random format strings, no 'n' specifier
+# [[fill]align][sign][#][0][width][.precision][type]
+def _gen_format_sep():
+ for align in ('', '<', '>', '=', '^'):
+ for fill in ('', 'x'):
+ if align == '': fill = ''
+ for sign in ('', '+', '-', ' '):
+ for zeropad in ('', '0'):
+ if align != '': zeropad = ''
+ for width in ['']+[str(y) for y in range(1, 15)]+['101']:
+ for prec in ['']+['.'+str(y) for y in range(15)]:
+ # for type in ('', 'E', 'e', 'G', 'g', 'F', 'f', '%'):
+ type = random.choice(('', 'E', 'e', 'G', 'g', 'F', 'f', '%'))
+ yield ''.join((fill, align, sign, zeropad, width, ',', prec, type))
+
+
+# Generate random format strings with 'n' specifier
+# [[fill]align][sign][#][0][width][.precision][type]
+def _gen_format_locale():
+ for align in ('', '<', '>', '=', '^'):
+ for fill in ('', 'x'):
+ if align == '': fill = ''
+ for sign in ('', '+', '-', ' '):
+ for zeropad in ('', '0'):
+ if align != '': zeropad = ''
+ for width in ['']+[str(y) for y in range(1, 20)]+['101']:
+ for prec in ['']+['.'+str(y) for y in range(1, 20)]:
+ yield ''.join((fill, align, sign, zeropad, width, prec, 'n'))
+
+
+# Generate random format strings with a unicode fill character
+# [[fill]align][sign][#][0][width][.precision][type]
+def randf(fill):
+ active = sorted(random.sample(range(5), random.randrange(6)))
+ s = ''
+ s += str(fill)
+ s += random.choice('<>=^')
+ have_align = 1
+ for elem in active:
+ if elem == 0: # sign
+ s += random.choice('+- ')
+ elif elem == 1: # width
+ s += str(random.randrange(1, 100))
+ elif elem == 2: # thousands separator
+ s += ','
+ elif elem == 3: # prec
+ s += '.'
+ # decimal.py does not support prec=0
+ s += str(random.randrange(1, 100))
+ elif elem == 4:
+ if 2 in active: c = 'EeGgFf%'
+ else: c = 'EeGgFfn%'
+ s += random.choice(c)
+ return s
+
+# Generate random format strings with random locale setting
+# [[fill]align][sign][#][0][width][.precision][type]
+def rand_locale():
+ try:
+ loc = random.choice(locale_list)
+ locale.setlocale(locale.LC_ALL, loc)
+ except locale.Error as err:
+ pass
+ active = sorted(random.sample(range(5), random.randrange(6)))
+ s = ''
+ have_align = 0
+ for elem in active:
+ if elem == 0: # fill+align
+ s += chr(random.randrange(32, 128))
+ s += random.choice('<>=^')
+ have_align = 1
+ elif elem == 1: # sign
+ s += random.choice('+- ')
+ elif elem == 2 and not have_align: # zeropad
+ s += '0'
+ elif elem == 3: # width
+ s += str(random.randrange(1, 100))
+ elif elem == 4: # prec
+ s += '.'
+ # decimal.py does not support prec=0
+ s += str(random.randrange(1, 100))
+ s += 'n'
+ return s
+
+
+if __name__ == '__main__':
+
+ testno = 0
+ print("rounding: half_even")
+
+ if not unicode_chars:
+ unicode_chars = gen_unicode_chars()
+
+ # unicode fill character test
+ for x in range(10):
+ for fill in unicode_chars:
+ intpart = fracpart = ''
+ while (not intpart) and (not fracpart):
+ intpart = random.choice(integers)
+ fracpart = random.choice(integers)
+ s = ''.join((random.choice(('', '-')), intpart, '.', fracpart))
+ fmt = randf(fill)
+ testno += 1
+ printit(testno, s, fmt, 'utf-8')
+
+ # thousands separator test
+ for fmt in _gen_format_sep():
+ for s in un_incr_digits(15, 384, 30):
+ testno += 1
+ for sign in ('', '-'):
+ for intpart in integers:
+ for fracpart in integers:
+ if (not intpart) and (not fracpart):
+ continue
+ s = ''.join((sign, intpart, '.', fracpart))
+ testno += 1
+ printit(testno, s, fmt)
+ for s in numbers:
+ testno += 1
+ printit(testno, s, fmt)
+ for x in range(100):
+ s = randdec(20, 425)
+ testno += 1
+ printit(testno, s, fmt)
+ for x in range(100):
+ s = randint(20, 425)
+ testno += 1
+ printit(testno, s, fmt)
+
+ # locale test
+ for loc in locale_list:
+ try:
+ locale.setlocale(locale.LC_ALL, loc)
+ except locale.Error as err:
+ sys.stderr.write("%s: %s\n" % (loc, err))
+ continue
+ print("locale: %s" % loc)
+ for fmt in _gen_format_locale():
+ for sign in ('', '-'):
+ intpart = fracpart = ''
+ while (not intpart) and (not fracpart):
+ intpart = random.choice(integers)
+ fracpart = random.choice(integers)
+ s = ''.join((sign, intpart, '.', fracpart))
+ testno += 1
+ printit(testno, s, fmt)
+ for s in random.sample(numbers, 3):
+ testno += 1
+ printit(testno, s, fmt)
+ getcontext().prec = 300
+ for x in range(10):
+ s = randdec(20, 425000000)
+ testno += 1
+ printit(testno, s, fmt)
Added: python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/genrandformat.py
==============================================================================
--- (empty file)
+++ python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/genrandformat.py Thu Jan 7 13:23:07 2010
@@ -0,0 +1,33 @@
+#
+# Copyright (c) 2008-2010 Stefan Krah. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+#
+
+
+#
+# Test formatting using random format strings. This must be run
+# in a UFT-8 terminal.
+#
+# Usage: python3.2 genrandformat.py | ../runtest -
+#
+
+
+from formathelper import *
+print("rounding: half_even")
+
+
+testno = 0
+for x in range(1000):
+ for sign in ('', '-'):
+ intpart = fracpart = ''
+ while (not intpart) and (not fracpart):
+ intpart = random.choice(integers)
+ fracpart = random.choice(integers)
+ s = ''.join((sign, intpart, '.', fracpart))
+ fmt = rand_format(rand_unicode())
+ testno += 1
+ printit(testno, s, fmt, 'utf-8')
+ for s in un_incr_digits(15, 384, 30):
+ fmt = rand_format(rand_unicode())
+ testno += 1
+ printit(testno, s, fmt, 'utf-8')
Added: python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/genrandlocale.py
==============================================================================
--- (empty file)
+++ python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/genrandlocale.py Thu Jan 7 13:23:07 2010
@@ -0,0 +1,33 @@
+#
+# For each possible locale setting, test formatting using random
+# format strings.
+#
+# Usage: python3.2 genrandlocale.py | ../runtest -
+#
+
+
+from formathelper import *
+print("rounding: half_even")
+
+
+testno = 0
+for loc in locale_list:
+ try:
+ locale.setlocale(locale.LC_ALL, loc)
+ except locale.Error as err:
+ sys.stderr.write("%s: %s\n" % (loc, err))
+ continue
+ print("locale: %s" % loc)
+ for sign in ('', '-'):
+ intpart = fracpart = ''
+ while (not intpart) and (not fracpart):
+ intpart = random.choice(integers)
+ fracpart = random.choice(integers)
+ s = ''.join((sign, intpart, '.', fracpart))
+ fmt = rand_format('x')
+ testno += 1
+ printit(testno, s, fmt)
+ for s in un_incr_digits(15, 384, 30):
+ fmt = rand_format('x')
+ testno += 1
+ printit(testno, s, fmt)
Added: python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/randdec.py
==============================================================================
--- (empty file)
+++ python/branches/py3k-cdecimal/Lib/test/decimal_extended_tests/randdec.py Thu Jan 7 13:23:07 2010
@@ -0,0 +1,466 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2008-2010 Stefan Krah. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+#
+
+
+#
+# Grammar from http://speleotrove.com/decimal/daconvs.html
+#
+# sign ::= '+' | '-'
+# digit ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' |
+# '8' | '9'
+# indicator ::= 'e' | 'E'
+# digits ::= digit [digit]...
+# decimal-part ::= digits '.' [digits] | ['.'] digits
+# exponent-part ::= indicator [sign] digits
+# infinity ::= 'Infinity' | 'Inf'
+# nan ::= 'NaN' [digits] | 'sNaN' [digits]
+# numeric-value ::= decimal-part [exponent-part] | infinity
+# numeric-string ::= [sign] numeric-value | [sign] nan
+#
+
+
+import random, sys
+
+
+def sign():
+ if random.randrange(2):
+ if random.randrange(2): return '+'
+ return ''
+ return '-'
+
+def indicator():
+ return "eE"[random.randrange(2)]
+
+def digits(maxprec):
+ if maxprec == 0: return ''
+ return str(random.randrange(10**maxprec))
+
+def dot():
+ if random.randrange(2): return '.'
+ return ''
+
+def decimal_part(maxprec):
+ if random.randrange(100) > 60: # integers
+ return digits(maxprec)
+ if random.randrange(2):
+ intlen = random.randrange(1, maxprec+1)
+ fraclen = maxprec-intlen
+ intpart = digits(intlen)
+ fracpart = digits(fraclen)
+ return ''.join((intpart, '.', fracpart))
+ else:
+ return ''.join((dot(), digits(maxprec)))
+
+def expdigits(maxexp):
+ return str(random.randrange(maxexp))
+
+def exponent_part(maxexp):
+ return ''.join((indicator(), sign(), expdigits(maxexp)))
+
+def infinity():
+ if random.randrange(2): return 'Infinity'
+ return 'Inf'
+
+def nan():
+ d = ''
+ if random.randrange(2):
+ d = digits(random.randrange(99));
+ if random.randrange(2):
+ return ''.join(('NaN', d))
+ else:
+ return ''.join(('sNaN', d))
+
+def numeric_value(maxprec, maxexp):
+ if random.randrange(100) > 90:
+ return infinity()
+ exp_part = ''
+ if random.randrange(100) > 60:
+ exp_part = exponent_part(maxexp)
+ return ''.join((decimal_part(maxprec), exp_part))
+
+def numeric_string(maxprec, maxexp):
+ if random.randrange(100) > 95:
+ return ''.join((sign(), nan()))
+ else:
+ return ''.join((sign(), numeric_value(maxprec, maxexp)))
+
+def randdec(maxprec, maxexp):
+ return numeric_string(maxprec, maxexp)
+
+def randint(maxprec, maxexp):
+ return digits(maxprec)
+
+def rand_adjexp(maxprec, maxadjexp):
+ d = digits(maxprec)
+ maxexp = maxadjexp-len(d)+1
+ if maxexp == 0: maxexp = 1
+ exp = str(random.randrange(maxexp-2*(abs(maxexp)), maxexp))
+ return ''.join((sign(), d, 'E', exp))
+
+
+def ndigits(n):
+ if n < 1: return 0
+ return random.randrange(10**(n-1), 10**n)
+
+def randtuple(maxprec, maxexp):
+ n = random.randrange(100)
+ sign = (0,1)[random.randrange(1)]
+ coeff = ndigits(maxprec)
+ if n >= 95:
+ coeff = ()
+ exp = 'F'
+ elif n >= 85:
+ coeff = tuple(map(int, str(ndigits(maxprec))))
+ exp = "nN"[random.randrange(1)]
+ else:
+ coeff = tuple(map(int, str(ndigits(maxprec))))
+ exp = random.randrange(-maxexp, maxexp)
+ return (sign, coeff, exp)
+
+
+def from_triple(sign, coeff, exp):
+ return ''.join((str(sign*coeff), indicator(), str(exp)))
+
+
+# Close to 10**n
+def un_close_to_pow10(prec, maxexp, iter=None):
+ if iter is None:
+ lst = range(prec+30)
+ else:
+ lst = random.sample(range(prec+30), iter)
+ nines = [10**n - 1 for n in lst]
+ pow10 = [10**n for n in lst]
+ for coeff in nines:
+ yield coeff
+ yield -coeff
+ yield from_triple(1, coeff, random.randrange(2*maxexp))
+ yield from_triple(-1, coeff, random.randrange(2*maxexp))
+ for coeff in pow10:
+ yield coeff
+ yield -coeff
+
+# Close to 10**n
+def bin_close_to_pow10(prec, maxexp, iter=None):
+ if iter is None:
+ lst = range(prec+30)
+ else:
+ lst = random.sample(range(prec+30), iter)
+ nines = [10**n - 1 for n in lst]
+ pow10 = [10**n for n in lst]
+ for coeff in nines:
+ yield coeff, 1
+ yield -coeff, -1
+ yield 1, coeff
+ yield -1, -coeff
+ yield from_triple(1, coeff, random.randrange(2*maxexp)), 1
+ yield from_triple(-1, coeff, random.randrange(2*maxexp)), -1
+ yield 1, from_triple(1, coeff, -random.randrange(2*maxexp))
+ yield -1, from_triple(-1, coeff, -random.randrange(2*maxexp))
+ for coeff in pow10:
+ yield coeff, -1
+ yield -coeff, 1
+ yield 1, -coeff
+ yield -coeff, 1
+
+# Close to 1:
+def close_to_one_greater(prec, emax, emin):
+ rprec = 10**prec
+ return ''.join(("1.", '0'*random.randrange(prec), str(random.randrange(rprec))))
+
+def close_to_one_less(prec, emax, emin):
+ rprec = 10**prec
+ return ''.join(("0.9", '9'*random.randrange(prec), str(random.randrange(rprec))))
+
+# Close to 0:
+def close_to_zero_greater(prec, emax, emin):
+ rprec = 10**prec
+ return ''.join(("0.", '0'*random.randrange(prec), str(random.randrange(rprec))))
+
+def close_to_zero_less(prec, emax, emin):
+ rprec = 10**prec
+ return ''.join(("-0.", '0'*random.randrange(prec), str(random.randrange(rprec))))
+
+# Close to emax:
+def close_to_emax_less(prec, emax, emin):
+ rprec = 10**prec
+ return ''.join(("9.", '9'*random.randrange(prec), str(random.randrange(rprec)), "E", str(emax)))
+
+def close_to_emax_greater(prec, emax, emin):
+ rprec = 10**prec
+ return ''.join(("1.", '0'*random.randrange(prec), str(random.randrange(rprec)), "E", str(emax+1)))
+
+# Close to emin:
+def close_to_emin_greater(prec, emax, emin):
+ rprec = 10**prec
+ return ''.join(("1.", '0'*random.randrange(prec), str(random.randrange(rprec)), "E", str(emin)))
+
+def close_to_emin_less(prec, emax, emin):
+ rprec = 10**prec
+ return ''.join(("9.", '9'*random.randrange(prec), str(random.randrange(rprec)), "E", str(emin-1)))
+
+# Close to etiny:
+def close_to_etiny_greater(prec, emax, emin):
+ rprec = 10**prec
+ etiny = emin - (prec - 1)
+ return ''.join(("1.", '0'*random.randrange(prec), str(random.randrange(rprec)), "E", str(etiny)))
+
+def close_to_etiny_less(prec, emax, emin):
+ rprec = 10**prec
+ etiny = emin - (prec - 1)
+ return ''.join(("9.", '9'*random.randrange(prec), str(random.randrange(rprec)), "E", str(etiny-1)))
+
+
+def close_to_min_etiny_greater(prec, max_prec, min_emin):
+ rprec = 10**prec
+ etiny = min_emin - (max_prec - 1)
+ return ''.join(("1.", '0'*random.randrange(prec), str(random.randrange(rprec)), "E", str(etiny)))
+
+def close_to_min_etiny_less(prec, max_prec, min_emin):
+ rprec = 10**prec
+ etiny = min_emin - (max_prec - 1)
+ return ''.join(("9.", '9'*random.randrange(prec), str(random.randrange(rprec)), "E", str(etiny-1)))
+
+
+close_funcs = [
+ close_to_one_greater, close_to_one_less, close_to_zero_greater, close_to_zero_less,
+ close_to_emax_less, close_to_emax_greater, close_to_emin_greater, close_to_emin_less,
+ close_to_etiny_greater, close_to_etiny_less, close_to_min_etiny_greater, close_to_min_etiny_less
+]
+
+
+def un_close_numbers(prec, emax, emin, iter=None):
+ if iter is None:
+ iter = 1000
+ for i in range(iter):
+ for func in close_funcs:
+ yield func(prec, emax, emin)
+
+def bin_close_numbers(prec, emax, emin, iter=None):
+ if iter is None:
+ iter = 1000
+ for i in range(iter):
+ for func1 in close_funcs:
+ for func2 in close_funcs:
+ yield func1(prec, emax, emin), func2(prec, emax, emin)
+ for func in close_funcs:
+ yield randdec(prec, emax), func(prec, emax, emin)
+ yield func(prec, emax, emin), randdec(prec, emax)
+
+def tern_close_numbers(prec, emax, emin, iter):
+ if iter is None:
+ iter = 1000
+ for i in range(iter):
+ for func1 in close_funcs:
+ for func2 in close_funcs:
+ for func3 in close_funcs:
+ yield func1(prec, emax, emin), func2(prec, emax, emin), func3(prec, emax, emin)
+ for func in close_funcs:
+ yield randdec(prec, emax), func(prec, emax, emin), func(prec, emax, emin)
+ yield func(prec, emax, emin), randdec(prec, emax), func(prec, emax, emin)
+ yield func(prec, emax, emin), func(prec, emax, emin), randdec(prec, emax)
+ for func in close_funcs:
+ yield randdec(prec, emax), randdec(prec, emax), func(prec, emax, emin)
+ yield randdec(prec, emax), func(prec, emax, emin), randdec(prec, emax)
+ yield func(prec, emax, emin), randdec(prec, emax), randdec(prec, emax)
+
+
+# If iter == None, test all digit lengths up to prec + 30
+def un_incr_digits(prec, maxexp, iter):
+ if iter is None:
+ lst = range(prec+30)
+ else:
+ lst = random.sample(range(prec+30), iter)
+ for m in lst:
+ yield from_triple(1, ndigits(m), 0)
+ yield from_triple(-1, ndigits(m), 0)
+ yield from_triple(1, ndigits(m), random.randrange(maxexp))
+ yield from_triple(-1, ndigits(m), random.randrange(maxexp))
+
+# If iter == None, test all digit lengths up to prec + 30
+# Also output decimals im tuple form.
+def un_incr_digits_tuple(prec, maxexp, iter):
+ if iter is None:
+ lst = range(prec+30)
+ else:
+ lst = random.sample(range(prec+30), iter)
+ for m in lst:
+ yield from_triple(1, ndigits(m), 0)
+ yield from_triple(-1, ndigits(m), 0)
+ yield from_triple(1, ndigits(m), random.randrange(maxexp))
+ yield from_triple(-1, ndigits(m), random.randrange(maxexp))
+ # test from tuple
+ yield (0, tuple(map(int, str(ndigits(m)))), 0)
+ yield (1, tuple(map(int, str(ndigits(m)))), 0)
+ yield (0, tuple(map(int, str(ndigits(m)))), random.randrange(maxexp))
+ yield (1, tuple(map(int, str(ndigits(m)))), random.randrange(maxexp))
+
+# If iter == None, test all combinations of digit lengths up to prec + 30
+def bin_incr_digits(prec, maxexp, iter):
+ if iter is None:
+ lst1 = range(prec+30)
+ lst2 = range(prec+30)
+ else:
+ lst1 = random.sample(range(prec+30), iter)
+ lst2 = random.sample(range(prec+30), iter)
+ for m in lst1:
+ self = from_triple(1, ndigits(m), 0)
+ yield self, self
+ self = from_triple(-1, ndigits(m), 0)
+ yield self, self
+ self = from_triple(1, ndigits(m), random.randrange(maxexp))
+ yield self, self
+ self = from_triple(-1, ndigits(m), random.randrange(maxexp))
+ yield self, self
+ for m in lst1:
+ for n in lst2:
+ self = from_triple(1, ndigits(m), 0)
+ other = from_triple(1, ndigits(n), 0)
+ yield self, other
+ self = from_triple(-1, ndigits(m), 0)
+ other = from_triple(1, ndigits(n), 0)
+ yield self, other
+ self = from_triple(1, ndigits(m), 0)
+ other = from_triple(-1, ndigits(n), 0)
+ yield self, other
+ self = from_triple(-1, ndigits(m), 0)
+ other = from_triple(-1, ndigits(n), 0)
+ yield self, other
+ self = from_triple(1, ndigits(m), random.randrange(maxexp))
+ other = from_triple(1, ndigits(n), random.randrange(maxexp))
+ yield self, other
+ self = from_triple(-1, ndigits(m), random.randrange(maxexp))
+ other = from_triple(1, ndigits(n), random.randrange(maxexp))
+ yield self, other
+ self = from_triple(1, ndigits(m), random.randrange(maxexp))
+ other = from_triple(-1, ndigits(n), random.randrange(maxexp))
+ yield self, other
+ self = from_triple(-1, ndigits(m), random.randrange(maxexp))
+ other = from_triple(-1, ndigits(n), random.randrange(maxexp))
+ yield self, other
+
+
+def randsign():
+ return (1, -1)[random.randrange(2)]
+
+# If iter == None, test all combinations of digit lengths up to prec + 30
+def tern_incr_digits(prec, maxexp, iter):
+ if iter is None:
+ lst1 = range(prec+30)
+ lst2 = range(prec+30)
+ lst3 = range(prec+30)
+ else:
+ lst1 = random.sample(range(prec+30), iter)
+ lst2 = random.sample(range(prec+30), iter)
+ lst3 = random.sample(range(prec+30), iter)
+ for m in lst1:
+ for n in lst2:
+ for p in lst3:
+ self = from_triple(randsign(), ndigits(m), 0)
+ other = from_triple(randsign(), ndigits(n), 0)
+ third = from_triple(randsign(), ndigits(p), 0)
+ yield self, other, third
+
+
+# Tests for the 'logical' fucntions
+def bindigits(prec):
+ z = 0
+ for i in range(prec):
+ z += random.randrange(2) * 10**i
+ return z
+
+def logical_un_incr_digits(prec, iter):
+ if iter is None:
+ lst = range(prec+30)
+ else:
+ lst = random.sample(range(prec+30), iter)
+ for m in lst:
+ self = from_triple(1, bindigits(m), 0)
+ yield self
+
+def logical_bin_incr_digits(prec, iter):
+ if iter is None:
+ lst1 = range(prec+30)
+ lst2 = range(prec+30)
+ else:
+ lst1 = random.sample(range(prec+30), iter)
+ lst2 = random.sample(range(prec+30), iter)
+ for m in lst1:
+ self = from_triple(1, bindigits(m), 0)
+ yield self, self
+ for m in lst1:
+ for n in lst2:
+ self = from_triple(1, bindigits(m), 0)
+ other = from_triple(1, bindigits(n), 0)
+ yield self, other
+
+
+py_major = sys.version_info[0]
+if py_major == 2:
+ def touni(c): return unichr(c)
+else:
+ def touni(c): return chr(c)
+
+# Generate list of all unicode characters that are accepted
+# as fill characters by decimal.py.
+def gen_unicode_chars():
+ from decimal import Decimal
+ sys.stderr.write("\ngenerating unicode chars ... ")
+ r = []
+ for c in range(32, 0x110001):
+ try:
+ x = touni(c)
+ try:
+ x.encode('utf-8').decode()
+ format(Decimal(0), x + '<19g')
+ r.append(x)
+ except:
+ pass
+ except ValueError:
+ pass
+ r.remove(touni(ord("'")))
+ r.remove(touni(ord('"')))
+ r.remove(touni(ord('\\')))
+ sys.stderr.write("DONE\n\n")
+ return r
+
+unicode_chars = []
+def rand_unicode():
+ global unicode_chars
+ if not unicode_chars:
+ unicode_chars = gen_unicode_chars()
+ return random.choice(unicode_chars)
+
+
+# Generate random format strings
+# [[fill]align][sign][#][0][width][.precision][type]
+import string
+def rand_format(fill):
+ active = sorted(random.sample(range(7), random.randrange(8)))
+ have_align = 0
+ s = ''
+ for elem in active:
+ if elem == 0: # fill+align
+ s += fill
+ s += random.choice('<>=^')
+ have_align = 1
+ elif elem == 1: # sign
+ s += random.choice('+- ')
+ elif elem == 2 and not have_align: # zeropad
+ s += '0'
+ elif elem == 3: # width
+ s += str(random.randrange(1, 100))
+ elif elem == 4: # thousands separator
+ s += ','
+ elif elem == 5: # prec
+ s += '.'
+ # decimal.py does not support prec=0
+ s += str(random.randrange(1, 100))
+ elif elem == 6:
+ if 4 in active: c = 'EeGgFf%'
+ else: c = 'EeGgFfn%'
+ s += random.choice(c)
+ return s
More information about the Python-checkins
mailing list