Decimal module and test cases were updated to their state of
the art.  It now complies latest specification and tests.

The only difference of this version with the one in the trunk
is that a small subset that hash tests were removed, because
they rely on modifications to core hash() function (see
issue 1182 for further details).
diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py
index 841ea6f..46a448b 100644
--- a/Lib/test/test_decimal.py
+++ b/Lib/test/test_decimal.py
@@ -65,9 +65,7 @@
 # Slower, since it runs some things several times.
 EXTENDEDERRORTEST = False
 
-
 #Map the test cases' error names to the actual errors
-
 ErrorNames = {'clamped' : Clamped,
               'conversion_syntax' : InvalidOperation,
               'division_by_zero' : DivisionByZero,
@@ -92,20 +90,88 @@
                 'half_down' : ROUND_HALF_DOWN,
                 'half_even' : ROUND_HALF_EVEN,
                 'half_up' : ROUND_HALF_UP,
-                'up' : ROUND_UP}
+                'up' : ROUND_UP,
+                '05up' : ROUND_05UP}
 
 # Name adapter to be able to change the Decimal and Context
 # interface without changing the test files from Cowlishaw
-nameAdapter = {'toeng':'to_eng_string',
-               'tosci':'to_sci_string',
-               'samequantum':'same_quantum',
-               'tointegral':'to_integral',
-               'remaindernear':'remainder_near',
-               'divideint':'divide_int',
-               'squareroot':'sqrt',
+nameAdapter = {'and':'logical_and',
                'apply':'_apply',
+               'class':'number_class',
+               'comparesig':'compare_signal',
+               'comparetotal':'compare_total',
+               'comparetotmag':'compare_total_mag',
+               'copy':'copy_decimal',
+               'copyabs':'copy_abs',
+               'copynegate':'copy_negate',
+               'copysign':'copy_sign',
+               'divideint':'divide_int',
+               'invert':'logical_invert',
+               'iscanonical':'is_canonical',
+               'isfinite':'is_finite',
+               'isinfinite':'is_infinite',
+               'isnan':'is_nan',
+               'isnormal':'is_normal',
+               'isqnan':'is_qnan',
+               'issigned':'is_signed',
+               'issnan':'is_snan',
+               'issubnormal':'is_subnormal',
+               'iszero':'is_zero',
+               'maxmag':'max_mag',
+               'minmag':'min_mag',
+               'nextminus':'next_minus',
+               'nextplus':'next_plus',
+               'nexttoward':'next_toward',
+               'or':'logical_or',
+               'reduce':'normalize',
+               'remaindernear':'remainder_near',
+               'samequantum':'same_quantum',
+               'squareroot':'sqrt',
+               'toeng':'to_eng_string',
+               'tointegral':'to_integral_value',
+               'tointegralx':'to_integral_exact',
+               'tosci':'to_sci_string',
+               'xor':'logical_xor',
               }
 
+# The following functions return True/False rather than a Decimal instance
+
+LOGICAL_FUNCTIONS = (
+    'is_canonical',
+    'is_finite',
+    'is_infinite',
+    'is_nan',
+    'is_normal',
+    'is_qnan',
+    'is_signed',
+    'is_snan',
+    'is_subnormal',
+    'is_zero',
+    'same_quantum',
+    )
+
+# For some operations (currently exp, ln, log10, power), the decNumber
+# reference implementation imposes additional restrictions on the
+# context and operands.  These restrictions are not part of the
+# specification; however, the effect of these restrictions does show
+# up in some of the testcases.  We skip testcases that violate these
+# restrictions, since Decimal behaves differently from decNumber for
+# these testcases so these testcases would otherwise fail.
+
+decNumberRestricted = ('power', 'ln', 'log10', 'exp')
+DEC_MAX_MATH = 999999
+def outside_decNumber_bounds(v, context):
+    if (context.prec > DEC_MAX_MATH or
+        context.Emax > DEC_MAX_MATH or
+        -context.Emin > DEC_MAX_MATH):
+        return True
+    if not v._is_special and v and (
+        len(v._int) > DEC_MAX_MATH or
+        v.adjusted() > DEC_MAX_MATH or
+        v.adjusted() < 1-2*DEC_MAX_MATH):
+        return True
+    return False
+
 class DecimalTest(unittest.TestCase):
     """Class which tests the Decimal class against the test cases.
 
@@ -142,10 +208,6 @@
             #print line
             try:
                 t = self.eval_line(line)
-            except InvalidOperation:
-                print 'Error in test cases:'
-                print line
-                continue
             except DecimalException, exception:
                 #Exception raised where there shoudn't have been one.
                 self.fail('Exception "'+exception.__class__.__name__ + '" raised on line '+line)
@@ -194,7 +256,8 @@
             Sides = s.split('->')
             L = Sides[0].strip().split()
             id = L[0]
-#            print id,
+            if DEBUG:
+                print "Test ", id,
             funct = L[1].lower()
             valstemp = L[2:]
             L = Sides[1].strip().split()
@@ -246,11 +309,27 @@
                         self.context.traps[error] = 0
                 v = self.context.create_decimal(v)
             else:
-                v = Decimal(v)
+                v = Decimal(v, self.context)
             vals.append(v)
 
         ans = FixQuotes(ans)
 
+        # skip tests that are related to bounds imposed in the decNumber
+        # reference implementation
+        if fname in decNumberRestricted:
+            if fname == 'power':
+                if not (vals[1]._isinteger() and
+                        -1999999997 <= vals[1] <= 999999999):
+                    if outside_decNumber_bounds(vals[0], self.context) or \
+                            outside_decNumber_bounds(vals[1], self.context):
+                        #print "Skipping test %s" % s
+                        return
+            else:
+                if outside_decNumber_bounds(vals[0], self.context):
+                    #print "Skipping test %s" % s
+                    return
+
+
         if EXTENDEDERRORTEST and fname not in ('to_sci_string', 'to_eng_string'):
             for error in theirexceptions:
                 self.context.traps[error] = 1
@@ -264,9 +343,11 @@
                 else:
                     self.fail("Did not raise %s in %s" % (error, s))
                 self.context.traps[error] = 0
+        if DEBUG:
+            print "--", self.context
         try:
             result = str(funct(*vals))
-            if fname == 'same_quantum':
+            if fname in LOGICAL_FUNCTIONS:
                 result = str(int(eval(result))) # 'True', 'False' -> '1', '0'
         except Signals, error:
             self.fail("Raised %s in %s" % (error, s))
@@ -283,8 +364,7 @@
         self.assertEqual(result, ans,
                          'Incorrect answer for ' + s + ' -- got ' + result)
         self.assertEqual(myexceptions, theirexceptions,
-                         'Incorrect flags set in ' + s + ' -- got ' \
-                         + str(myexceptions))
+              'Incorrect flags set in ' + s + ' -- got ' + str(myexceptions))
         return
 
     def getexceptions(self):
@@ -301,17 +381,6 @@
     def change_clamp(self, clamp):
         self.context._clamp = clamp
 
-# Dynamically build custom test definition for each file in the test
-# directory and add the definitions to the DecimalTest class.  This
-# procedure insures that new files do not get skipped.
-for filename in os.listdir(directory):
-    if '.decTest' not in filename:
-        continue
-    head, tail = filename.split('.')
-    tester = lambda self, f=filename: self.eval_file(directory + f)
-    setattr(DecimalTest, 'test_' + head, tester)
-    del filename, head, tail, tester
-
 
 
 # The following classes test the behaviour of Decimal according to PEP 327
@@ -383,13 +452,19 @@
 
         #bad sign
         self.assertRaises(ValueError, Decimal, (8, (4, 3, 4, 9, 1), 2) )
+        self.assertRaises(ValueError, Decimal, (0., (4, 3, 4, 9, 1), 2) )
+        self.assertRaises(ValueError, Decimal, (Decimal(1), (4, 3, 4, 9, 1), 2))
 
         #bad exp
         self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 'wrong!') )
+        self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 0.) )
+        self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), '1') )
 
         #bad coefficients
         self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, None, 1), 2) )
         self.assertRaises(ValueError, Decimal, (1, (4, -3, 4, 9, 1), 2) )
+        self.assertRaises(ValueError, Decimal, (1, (4, 10, 4, 9, 1), 2) )
+        self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 'a', 1), 2) )
 
     def test_explicit_from_Decimal(self):
 
@@ -803,7 +878,7 @@
         th2.start()
 
         self.finish1.wait()
-        self.finish1.wait()
+        self.finish2.wait()
         return
 
     if threading is None:
@@ -853,6 +928,10 @@
         a.sort()
         self.assertEqual(a, b)
 
+        # with None
+        self.assertFalse(Decimal(1) < None)
+        self.assertTrue(Decimal(1) > None)
+
     def test_copy_and_deepcopy_methods(self):
         d = Decimal('43.24')
         c = copy.copy(d)
@@ -863,12 +942,54 @@
     def test_hash_method(self):
         #just that it's hashable
         hash(Decimal(23))
+
+        test_values = [Decimal(sign*(2**m + n))
+                       for m in [0, 14, 15, 16, 17, 30, 31,
+                                 32, 33, 62, 63, 64, 65, 66]
+                       for n in range(-10, 10)
+                       for sign in [-1, 1]]
+        test_values.extend([
+                Decimal("-0"), # zeros
+                Decimal("0.00"),
+                Decimal("-0.000"),
+                Decimal("0E10"),
+                Decimal("-0E12"),
+                Decimal("10.0"), # negative exponent
+                Decimal("-23.00000"),
+                Decimal("1230E100"), # positive exponent
+                Decimal("-4.5678E50"),
+                # a value for which hash(n) != hash(n % (2**64-1))
+                # in Python pre-2.6
+                Decimal(2**64 + 2**32 - 1),
+                ])
+
+        # check that hash(d) == hash(int(d)) for integral values
+        for value in test_values:
+            self.assertEqual(hash(value), hash(int(value)))
+
         #the same hash that to an int
         self.assertEqual(hash(Decimal(23)), hash(23))
         self.assertRaises(TypeError, hash, Decimal('NaN'))
         self.assert_(hash(Decimal('Inf')))
         self.assert_(hash(Decimal('-Inf')))
 
+        # check that the value of the hash doesn't depend on the
+        # current context (issue #1757)
+        c = getcontext()
+        old_precision = c.prec
+        x = Decimal("123456789.1")
+
+        c.prec = 6
+        h1 = hash(x)
+        c.prec = 10
+        h2 = hash(x)
+        c.prec = 16
+        h3 = hash(x)
+
+        self.assertEqual(h1, h2)
+        self.assertEqual(h1, h3)
+        c.prec = old_precision
+
     def test_min_and_max_methods(self):
 
         d1 = Decimal('15.32')
@@ -955,13 +1076,35 @@
         d = Decimal("Infinity")
         self.assertEqual(d.as_tuple(), (0, (0,), 'F') )
 
+        #leading zeros in coefficient should be stripped
+        d = Decimal( (0, (0, 0, 4, 0, 5, 3, 4), -2) )
+        self.assertEqual(d.as_tuple(), (0, (4, 0, 5, 3, 4), -2) )
+        d = Decimal( (1, (0, 0, 0), 37) )
+        self.assertEqual(d.as_tuple(), (1, (0,), 37))
+        d = Decimal( (1, (), 37) )
+        self.assertEqual(d.as_tuple(), (1, (0,), 37))
+
+        #leading zeros in NaN diagnostic info should be stripped
+        d = Decimal( (0, (0, 0, 4, 0, 5, 3, 4), 'n') )
+        self.assertEqual(d.as_tuple(), (0, (4, 0, 5, 3, 4), 'n') )
+        d = Decimal( (1, (0, 0, 0), 'N') )
+        self.assertEqual(d.as_tuple(), (1, (), 'N') )
+        d = Decimal( (1, (), 'n') )
+        self.assertEqual(d.as_tuple(), (1, (), 'n') )
+
+        #coefficient in infinity should be ignored
+        d = Decimal( (0, (4, 5, 3, 4), 'F') )
+        self.assertEqual(d.as_tuple(), (0, (0,), 'F'))
+        d = Decimal( (1, (0, 2, 7, 1), 'F') )
+        self.assertEqual(d.as_tuple(), (1, (0,), 'F'))
+
     def test_immutability_operations(self):
         # Do operations and check that it didn't change change internal objects.
 
         d1 = Decimal('-25e55')
         b1 = Decimal('-25e55')
-        d2 = Decimal('33e-33')
-        b2 = Decimal('33e-33')
+        d2 = Decimal('33e+33')
+        b2 = Decimal('33e+33')
 
         def checkSameDec(operation, useOther=False):
             if useOther:
@@ -1025,6 +1168,21 @@
         checkSameDec("to_eng_string")
         checkSameDec("to_integral")
 
+    def test_subclassing(self):
+        # Different behaviours when subclassing Decimal
+
+        class MyDecimal(Decimal):
+            pass
+
+        d1 = MyDecimal(1)
+        d2 = MyDecimal(2)
+        d = d1 + d2
+        self.assertTrue(type(d) is Decimal)
+
+        d = d1.max(d2)
+        self.assertTrue(type(d) is Decimal)
+
+
 class DecimalPythonAPItests(unittest.TestCase):
 
     def test_pickle(self):
@@ -1091,7 +1249,59 @@
         self.assert_(new_ctx is not set_ctx, 'did not copy the context')
         self.assert_(set_ctx is enter_ctx, '__enter__ returned wrong context')
 
-def test_main(arith=False, verbose=None):
+class ContextFlags(unittest.TestCase):
+    def test_flags_irrelevant(self):
+        # check that the result (numeric result + flags raised) of an
+        # arithmetic operation doesn't depend on the current flags
+
+        context = Context(prec=9, Emin = -999999999, Emax = 999999999,
+                    rounding=ROUND_HALF_EVEN, traps=[], flags=[])
+
+        # operations that raise various flags, in the form (function, arglist)
+        operations = [
+            (context._apply, [Decimal("100E-1000000009")]),
+            (context.sqrt, [Decimal(2)]),
+            (context.add, [Decimal("1.23456789"), Decimal("9.87654321")]),
+            (context.multiply, [Decimal("1.23456789"), Decimal("9.87654321")]),
+            (context.subtract, [Decimal("1.23456789"), Decimal("9.87654321")]),
+            ]
+
+        # try various flags individually, then a whole lot at once
+        flagsets = [[Inexact], [Rounded], [Underflow], [Clamped], [Subnormal],
+                    [Inexact, Rounded, Underflow, Clamped, Subnormal]]
+
+        for fn, args in operations:
+            # find answer and flags raised using a clean context
+            context.clear_flags()
+            ans = fn(*args)
+            flags = [k for k, v in context.flags.items() if v]
+
+            for extra_flags in flagsets:
+                # set flags, before calling operation
+                context.clear_flags()
+                for flag in extra_flags:
+                    context._raise_error(flag)
+                new_ans = fn(*args)
+
+                # flags that we expect to be set after the operation
+                expected_flags = list(flags)
+                for flag in extra_flags:
+                    if flag not in expected_flags:
+                        expected_flags.append(flag)
+                expected_flags.sort()
+
+                # flags we actually got
+                new_flags = [k for k,v in context.flags.items() if v]
+                new_flags.sort()
+
+                self.assertEqual(ans, new_ans,
+                                 "operation produces different answers depending on flags set: " +
+                                 "expected %s, got %s." % (ans, new_ans))
+                self.assertEqual(new_flags, expected_flags,
+                                  "operation raises different flags depending on flags set: " +
+                                  "expected %s, got %s" % (expected_flags, new_flags))
+
+def test_main(arith=False, verbose=None, todo_tests=None, debug=None):
     """ Execute the tests.
 
     Runs all arithmetic tests if arith is True or if the "decimal" resource
@@ -1099,35 +1309,58 @@
     """
 
     init()
-    global TEST_ALL
+    global TEST_ALL, DEBUG
     TEST_ALL = arith or is_resource_enabled('decimal')
+    DEBUG = debug
 
-    test_classes = [
-        DecimalExplicitConstructionTest,
-        DecimalImplicitConstructionTest,
-        DecimalArithmeticOperatorsTest,
-        DecimalUseOfContextTest,
-        DecimalUsabilityTest,
-        DecimalPythonAPItests,
-        ContextAPItests,
-        DecimalTest,
-        WithStatementTest,
-    ]
+    if todo_tests is None:
+        test_classes = [
+            DecimalExplicitConstructionTest,
+            DecimalImplicitConstructionTest,
+            DecimalArithmeticOperatorsTest,
+            DecimalUseOfContextTest,
+            DecimalUsabilityTest,
+            DecimalPythonAPItests,
+            ContextAPItests,
+            DecimalTest,
+            WithStatementTest,
+            ContextFlags
+        ]
+    else:
+        test_classes = [DecimalTest]
+
+    # Dynamically build custom test definition for each file in the test
+    # directory and add the definitions to the DecimalTest class.  This
+    # procedure insures that new files do not get skipped.
+    for filename in os.listdir(directory):
+        if '.decTest' not in filename or filename.startswith("."):
+            continue
+        head, tail = filename.split('.')
+        if todo_tests is not None and head not in todo_tests:
+            continue
+        tester = lambda self, f=filename: self.eval_file(directory + f)
+        setattr(DecimalTest, 'test_' + head, tester)
+        del filename, head, tail, tester
+
 
     try:
         run_unittest(*test_classes)
-        import decimal as DecimalModule
-        run_doctest(DecimalModule, verbose)
+        if todo_tests is None:
+            import decimal as DecimalModule
+            run_doctest(DecimalModule, verbose)
     finally:
         setcontext(ORIGINAL_CONTEXT)
 
 if __name__ == '__main__':
-    # Calling with no arguments runs all tests.
-    # Calling with "Skip" will skip over 90% of the arithmetic tests.
-    if len(sys.argv) == 1:
-        test_main(arith=True, verbose=True)
-    elif len(sys.argv) == 2:
-        arith = sys.argv[1].lower() != 'skip'
-        test_main(arith=arith, verbose=True)
+    import optparse
+    p = optparse.OptionParser("test_decimal.py [--debug] [{--skip | test1 [test2 [...]]}]")
+    p.add_option('--debug', '-d', action='store_true', help='shows the test number and context before each test')
+    p.add_option('--skip',  '-s', action='store_true', help='skip over 90% of the arithmetic tests')
+    (opt, args) = p.parse_args()
+
+    if opt.skip:
+        test_main(arith=False, verbose=True)
+    elif args:
+        test_main(arith=True, verbose=True, todo_tests=args, debug=opt.debug)
     else:
-        raise ValueError("test called with wrong arguments, use test_Decimal [Skip]")
+        test_main(arith=True, verbose=True)