[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