17db96d56Sopenharmony_ci#
27db96d56Sopenharmony_ci# Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
37db96d56Sopenharmony_ci#
47db96d56Sopenharmony_ci# Redistribution and use in source and binary forms, with or without
57db96d56Sopenharmony_ci# modification, are permitted provided that the following conditions
67db96d56Sopenharmony_ci# are met:
77db96d56Sopenharmony_ci#
87db96d56Sopenharmony_ci# 1. Redistributions of source code must retain the above copyright
97db96d56Sopenharmony_ci#    notice, this list of conditions and the following disclaimer.
107db96d56Sopenharmony_ci#
117db96d56Sopenharmony_ci# 2. Redistributions in binary form must reproduce the above copyright
127db96d56Sopenharmony_ci#    notice, this list of conditions and the following disclaimer in the
137db96d56Sopenharmony_ci#    documentation and/or other materials provided with the distribution.
147db96d56Sopenharmony_ci#
157db96d56Sopenharmony_ci# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
167db96d56Sopenharmony_ci# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
177db96d56Sopenharmony_ci# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
187db96d56Sopenharmony_ci# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
197db96d56Sopenharmony_ci# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
207db96d56Sopenharmony_ci# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
217db96d56Sopenharmony_ci# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
227db96d56Sopenharmony_ci# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
237db96d56Sopenharmony_ci# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
247db96d56Sopenharmony_ci# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
257db96d56Sopenharmony_ci# SUCH DAMAGE.
267db96d56Sopenharmony_ci#
277db96d56Sopenharmony_ci
287db96d56Sopenharmony_ci#
297db96d56Sopenharmony_ci# Usage: python deccheck.py [--short|--medium|--long|--all]
307db96d56Sopenharmony_ci#
317db96d56Sopenharmony_ci
327db96d56Sopenharmony_ci
337db96d56Sopenharmony_ciimport random
347db96d56Sopenharmony_ciimport time
357db96d56Sopenharmony_ci
367db96d56Sopenharmony_ciRANDSEED = int(time.time())
377db96d56Sopenharmony_cirandom.seed(RANDSEED)
387db96d56Sopenharmony_ci
397db96d56Sopenharmony_ciimport sys
407db96d56Sopenharmony_ciimport os
417db96d56Sopenharmony_cifrom copy import copy
427db96d56Sopenharmony_cifrom collections import defaultdict
437db96d56Sopenharmony_ci
447db96d56Sopenharmony_ciimport argparse
457db96d56Sopenharmony_ciimport subprocess
467db96d56Sopenharmony_cifrom subprocess import PIPE, STDOUT
477db96d56Sopenharmony_cifrom queue import Queue, Empty
487db96d56Sopenharmony_cifrom threading import Thread, Event, Lock
497db96d56Sopenharmony_ci
507db96d56Sopenharmony_cifrom test.support.import_helper import import_fresh_module
517db96d56Sopenharmony_cifrom randdec import randfloat, all_unary, all_binary, all_ternary
527db96d56Sopenharmony_cifrom randdec import unary_optarg, binary_optarg, ternary_optarg
537db96d56Sopenharmony_cifrom formathelper import rand_format, rand_locale
547db96d56Sopenharmony_cifrom _pydecimal import _dec_from_triple
557db96d56Sopenharmony_ci
567db96d56Sopenharmony_ciC = import_fresh_module('decimal', fresh=['_decimal'])
577db96d56Sopenharmony_ciP = import_fresh_module('decimal', blocked=['_decimal'])
587db96d56Sopenharmony_ciEXIT_STATUS = 0
597db96d56Sopenharmony_ci
607db96d56Sopenharmony_ci
617db96d56Sopenharmony_ci# Contains all categories of Decimal methods.
627db96d56Sopenharmony_ciFunctions = {
637db96d56Sopenharmony_ci    # Plain unary:
647db96d56Sopenharmony_ci    'unary': (
657db96d56Sopenharmony_ci        '__abs__', '__bool__', '__ceil__', '__complex__', '__copy__',
667db96d56Sopenharmony_ci        '__floor__', '__float__', '__hash__', '__int__', '__neg__',
677db96d56Sopenharmony_ci        '__pos__', '__reduce__', '__repr__', '__str__', '__trunc__',
687db96d56Sopenharmony_ci        'adjusted', 'as_integer_ratio', 'as_tuple', 'canonical', 'conjugate',
697db96d56Sopenharmony_ci        'copy_abs', 'copy_negate', 'is_canonical', 'is_finite', 'is_infinite',
707db96d56Sopenharmony_ci        'is_nan', 'is_qnan', 'is_signed', 'is_snan', 'is_zero', 'radix'
717db96d56Sopenharmony_ci    ),
727db96d56Sopenharmony_ci    # Unary with optional context:
737db96d56Sopenharmony_ci    'unary_ctx': (
747db96d56Sopenharmony_ci        'exp', 'is_normal', 'is_subnormal', 'ln', 'log10', 'logb',
757db96d56Sopenharmony_ci        'logical_invert', 'next_minus', 'next_plus', 'normalize',
767db96d56Sopenharmony_ci        'number_class', 'sqrt', 'to_eng_string'
777db96d56Sopenharmony_ci    ),
787db96d56Sopenharmony_ci    # Unary with optional rounding mode and context:
797db96d56Sopenharmony_ci    'unary_rnd_ctx': ('to_integral', 'to_integral_exact', 'to_integral_value'),
807db96d56Sopenharmony_ci    # Plain binary:
817db96d56Sopenharmony_ci    'binary': (
827db96d56Sopenharmony_ci        '__add__', '__divmod__', '__eq__', '__floordiv__', '__ge__', '__gt__',
837db96d56Sopenharmony_ci        '__le__', '__lt__', '__mod__', '__mul__', '__ne__', '__pow__',
847db96d56Sopenharmony_ci        '__radd__', '__rdivmod__', '__rfloordiv__', '__rmod__', '__rmul__',
857db96d56Sopenharmony_ci        '__rpow__', '__rsub__', '__rtruediv__', '__sub__', '__truediv__',
867db96d56Sopenharmony_ci        'compare_total', 'compare_total_mag', 'copy_sign', 'quantize',
877db96d56Sopenharmony_ci        'same_quantum'
887db96d56Sopenharmony_ci    ),
897db96d56Sopenharmony_ci    # Binary with optional context:
907db96d56Sopenharmony_ci    'binary_ctx': (
917db96d56Sopenharmony_ci        'compare', 'compare_signal', 'logical_and', 'logical_or', 'logical_xor',
927db96d56Sopenharmony_ci        'max', 'max_mag', 'min', 'min_mag', 'next_toward', 'remainder_near',
937db96d56Sopenharmony_ci        'rotate', 'scaleb', 'shift'
947db96d56Sopenharmony_ci    ),
957db96d56Sopenharmony_ci    # Plain ternary:
967db96d56Sopenharmony_ci    'ternary': ('__pow__',),
977db96d56Sopenharmony_ci    # Ternary with optional context:
987db96d56Sopenharmony_ci    'ternary_ctx': ('fma',),
997db96d56Sopenharmony_ci    # Special:
1007db96d56Sopenharmony_ci    'special': ('__format__', '__reduce_ex__', '__round__', 'from_float',
1017db96d56Sopenharmony_ci                'quantize'),
1027db96d56Sopenharmony_ci    # Properties:
1037db96d56Sopenharmony_ci    'property': ('real', 'imag')
1047db96d56Sopenharmony_ci}
1057db96d56Sopenharmony_ci
1067db96d56Sopenharmony_ci# Contains all categories of Context methods. The n-ary classification
1077db96d56Sopenharmony_ci# applies to the number of Decimal arguments.
1087db96d56Sopenharmony_ciContextFunctions = {
1097db96d56Sopenharmony_ci    # Plain nullary:
1107db96d56Sopenharmony_ci    'nullary': ('context.__hash__', 'context.__reduce__', 'context.radix'),
1117db96d56Sopenharmony_ci    # Plain unary:
1127db96d56Sopenharmony_ci    'unary': ('context.abs', 'context.canonical', 'context.copy_abs',
1137db96d56Sopenharmony_ci              'context.copy_decimal', 'context.copy_negate',
1147db96d56Sopenharmony_ci              'context.create_decimal', 'context.exp', 'context.is_canonical',
1157db96d56Sopenharmony_ci              'context.is_finite', 'context.is_infinite', 'context.is_nan',
1167db96d56Sopenharmony_ci              'context.is_normal', 'context.is_qnan', 'context.is_signed',
1177db96d56Sopenharmony_ci              'context.is_snan', 'context.is_subnormal', 'context.is_zero',
1187db96d56Sopenharmony_ci              'context.ln', 'context.log10', 'context.logb',
1197db96d56Sopenharmony_ci              'context.logical_invert', 'context.minus', 'context.next_minus',
1207db96d56Sopenharmony_ci              'context.next_plus', 'context.normalize', 'context.number_class',
1217db96d56Sopenharmony_ci              'context.plus', 'context.sqrt', 'context.to_eng_string',
1227db96d56Sopenharmony_ci              'context.to_integral', 'context.to_integral_exact',
1237db96d56Sopenharmony_ci              'context.to_integral_value', 'context.to_sci_string'
1247db96d56Sopenharmony_ci    ),
1257db96d56Sopenharmony_ci    # Plain binary:
1267db96d56Sopenharmony_ci    'binary': ('context.add', 'context.compare', 'context.compare_signal',
1277db96d56Sopenharmony_ci               'context.compare_total', 'context.compare_total_mag',
1287db96d56Sopenharmony_ci               'context.copy_sign', 'context.divide', 'context.divide_int',
1297db96d56Sopenharmony_ci               'context.divmod', 'context.logical_and', 'context.logical_or',
1307db96d56Sopenharmony_ci               'context.logical_xor', 'context.max', 'context.max_mag',
1317db96d56Sopenharmony_ci               'context.min', 'context.min_mag', 'context.multiply',
1327db96d56Sopenharmony_ci               'context.next_toward', 'context.power', 'context.quantize',
1337db96d56Sopenharmony_ci               'context.remainder', 'context.remainder_near', 'context.rotate',
1347db96d56Sopenharmony_ci               'context.same_quantum', 'context.scaleb', 'context.shift',
1357db96d56Sopenharmony_ci               'context.subtract'
1367db96d56Sopenharmony_ci    ),
1377db96d56Sopenharmony_ci    # Plain ternary:
1387db96d56Sopenharmony_ci    'ternary': ('context.fma', 'context.power'),
1397db96d56Sopenharmony_ci    # Special:
1407db96d56Sopenharmony_ci    'special': ('context.__reduce_ex__', 'context.create_decimal_from_float')
1417db96d56Sopenharmony_ci}
1427db96d56Sopenharmony_ci
1437db96d56Sopenharmony_ci# Functions that set no context flags but whose result can differ depending
1447db96d56Sopenharmony_ci# on prec, Emin and Emax.
1457db96d56Sopenharmony_ciMaxContextSkip = ['is_normal', 'is_subnormal', 'logical_invert', 'next_minus',
1467db96d56Sopenharmony_ci                  'next_plus', 'number_class', 'logical_and', 'logical_or',
1477db96d56Sopenharmony_ci                  'logical_xor', 'next_toward', 'rotate', 'shift']
1487db96d56Sopenharmony_ci
1497db96d56Sopenharmony_ci# Functions that require a restricted exponent range for reasonable runtimes.
1507db96d56Sopenharmony_ciUnaryRestricted = [
1517db96d56Sopenharmony_ci  '__ceil__', '__floor__', '__int__', '__trunc__',
1527db96d56Sopenharmony_ci  'as_integer_ratio', 'to_integral', 'to_integral_value'
1537db96d56Sopenharmony_ci]
1547db96d56Sopenharmony_ci
1557db96d56Sopenharmony_ciBinaryRestricted = ['__round__']
1567db96d56Sopenharmony_ci
1577db96d56Sopenharmony_ciTernaryRestricted = ['__pow__', 'context.power']
1587db96d56Sopenharmony_ci
1597db96d56Sopenharmony_ci
1607db96d56Sopenharmony_ci# ======================================================================
1617db96d56Sopenharmony_ci#                            Unified Context
1627db96d56Sopenharmony_ci# ======================================================================
1637db96d56Sopenharmony_ci
1647db96d56Sopenharmony_ci# Translate symbols.
1657db96d56Sopenharmony_ciCondMap = {
1667db96d56Sopenharmony_ci        C.Clamped:             P.Clamped,
1677db96d56Sopenharmony_ci        C.ConversionSyntax:    P.ConversionSyntax,
1687db96d56Sopenharmony_ci        C.DivisionByZero:      P.DivisionByZero,
1697db96d56Sopenharmony_ci        C.DivisionImpossible:  P.InvalidOperation,
1707db96d56Sopenharmony_ci        C.DivisionUndefined:   P.DivisionUndefined,
1717db96d56Sopenharmony_ci        C.Inexact:             P.Inexact,
1727db96d56Sopenharmony_ci        C.InvalidContext:      P.InvalidContext,
1737db96d56Sopenharmony_ci        C.InvalidOperation:    P.InvalidOperation,
1747db96d56Sopenharmony_ci        C.Overflow:            P.Overflow,
1757db96d56Sopenharmony_ci        C.Rounded:             P.Rounded,
1767db96d56Sopenharmony_ci        C.Subnormal:           P.Subnormal,
1777db96d56Sopenharmony_ci        C.Underflow:           P.Underflow,
1787db96d56Sopenharmony_ci        C.FloatOperation:      P.FloatOperation,
1797db96d56Sopenharmony_ci}
1807db96d56Sopenharmony_ci
1817db96d56Sopenharmony_ciRoundModes = [C.ROUND_UP, C.ROUND_DOWN, C.ROUND_CEILING, C.ROUND_FLOOR,
1827db96d56Sopenharmony_ci              C.ROUND_HALF_UP, C.ROUND_HALF_DOWN, C.ROUND_HALF_EVEN,
1837db96d56Sopenharmony_ci              C.ROUND_05UP]
1847db96d56Sopenharmony_ci
1857db96d56Sopenharmony_ci
1867db96d56Sopenharmony_ciclass Context(object):
1877db96d56Sopenharmony_ci    """Provides a convenient way of syncing the C and P contexts"""
1887db96d56Sopenharmony_ci
1897db96d56Sopenharmony_ci    __slots__ = ['c', 'p']
1907db96d56Sopenharmony_ci
1917db96d56Sopenharmony_ci    def __init__(self, c_ctx=None, p_ctx=None):
1927db96d56Sopenharmony_ci        """Initialization is from the C context"""
1937db96d56Sopenharmony_ci        self.c = C.getcontext() if c_ctx is None else c_ctx
1947db96d56Sopenharmony_ci        self.p = P.getcontext() if p_ctx is None else p_ctx
1957db96d56Sopenharmony_ci        self.p.prec = self.c.prec
1967db96d56Sopenharmony_ci        self.p.Emin = self.c.Emin
1977db96d56Sopenharmony_ci        self.p.Emax = self.c.Emax
1987db96d56Sopenharmony_ci        self.p.rounding = self.c.rounding
1997db96d56Sopenharmony_ci        self.p.capitals = self.c.capitals
2007db96d56Sopenharmony_ci        self.settraps([sig for sig in self.c.traps if self.c.traps[sig]])
2017db96d56Sopenharmony_ci        self.setstatus([sig for sig in self.c.flags if self.c.flags[sig]])
2027db96d56Sopenharmony_ci        self.p.clamp = self.c.clamp
2037db96d56Sopenharmony_ci
2047db96d56Sopenharmony_ci    def __str__(self):
2057db96d56Sopenharmony_ci        return str(self.c) + '\n' + str(self.p)
2067db96d56Sopenharmony_ci
2077db96d56Sopenharmony_ci    def getprec(self):
2087db96d56Sopenharmony_ci        assert(self.c.prec == self.p.prec)
2097db96d56Sopenharmony_ci        return self.c.prec
2107db96d56Sopenharmony_ci
2117db96d56Sopenharmony_ci    def setprec(self, val):
2127db96d56Sopenharmony_ci        self.c.prec = val
2137db96d56Sopenharmony_ci        self.p.prec = val
2147db96d56Sopenharmony_ci
2157db96d56Sopenharmony_ci    def getemin(self):
2167db96d56Sopenharmony_ci        assert(self.c.Emin == self.p.Emin)
2177db96d56Sopenharmony_ci        return self.c.Emin
2187db96d56Sopenharmony_ci
2197db96d56Sopenharmony_ci    def setemin(self, val):
2207db96d56Sopenharmony_ci        self.c.Emin = val
2217db96d56Sopenharmony_ci        self.p.Emin = val
2227db96d56Sopenharmony_ci
2237db96d56Sopenharmony_ci    def getemax(self):
2247db96d56Sopenharmony_ci        assert(self.c.Emax == self.p.Emax)
2257db96d56Sopenharmony_ci        return self.c.Emax
2267db96d56Sopenharmony_ci
2277db96d56Sopenharmony_ci    def setemax(self, val):
2287db96d56Sopenharmony_ci        self.c.Emax = val
2297db96d56Sopenharmony_ci        self.p.Emax = val
2307db96d56Sopenharmony_ci
2317db96d56Sopenharmony_ci    def getround(self):
2327db96d56Sopenharmony_ci        assert(self.c.rounding == self.p.rounding)
2337db96d56Sopenharmony_ci        return self.c.rounding
2347db96d56Sopenharmony_ci
2357db96d56Sopenharmony_ci    def setround(self, val):
2367db96d56Sopenharmony_ci        self.c.rounding = val
2377db96d56Sopenharmony_ci        self.p.rounding = val
2387db96d56Sopenharmony_ci
2397db96d56Sopenharmony_ci    def getcapitals(self):
2407db96d56Sopenharmony_ci        assert(self.c.capitals == self.p.capitals)
2417db96d56Sopenharmony_ci        return self.c.capitals
2427db96d56Sopenharmony_ci
2437db96d56Sopenharmony_ci    def setcapitals(self, val):
2447db96d56Sopenharmony_ci        self.c.capitals = val
2457db96d56Sopenharmony_ci        self.p.capitals = val
2467db96d56Sopenharmony_ci
2477db96d56Sopenharmony_ci    def getclamp(self):
2487db96d56Sopenharmony_ci        assert(self.c.clamp == self.p.clamp)
2497db96d56Sopenharmony_ci        return self.c.clamp
2507db96d56Sopenharmony_ci
2517db96d56Sopenharmony_ci    def setclamp(self, val):
2527db96d56Sopenharmony_ci        self.c.clamp = val
2537db96d56Sopenharmony_ci        self.p.clamp = val
2547db96d56Sopenharmony_ci
2557db96d56Sopenharmony_ci    prec = property(getprec, setprec)
2567db96d56Sopenharmony_ci    Emin = property(getemin, setemin)
2577db96d56Sopenharmony_ci    Emax = property(getemax, setemax)
2587db96d56Sopenharmony_ci    rounding = property(getround, setround)
2597db96d56Sopenharmony_ci    clamp = property(getclamp, setclamp)
2607db96d56Sopenharmony_ci    capitals = property(getcapitals, setcapitals)
2617db96d56Sopenharmony_ci
2627db96d56Sopenharmony_ci    def clear_traps(self):
2637db96d56Sopenharmony_ci        self.c.clear_traps()
2647db96d56Sopenharmony_ci        for trap in self.p.traps:
2657db96d56Sopenharmony_ci            self.p.traps[trap] = False
2667db96d56Sopenharmony_ci
2677db96d56Sopenharmony_ci    def clear_status(self):
2687db96d56Sopenharmony_ci        self.c.clear_flags()
2697db96d56Sopenharmony_ci        self.p.clear_flags()
2707db96d56Sopenharmony_ci
2717db96d56Sopenharmony_ci    def settraps(self, lst):
2727db96d56Sopenharmony_ci        """lst: C signal list"""
2737db96d56Sopenharmony_ci        self.clear_traps()
2747db96d56Sopenharmony_ci        for signal in lst:
2757db96d56Sopenharmony_ci            self.c.traps[signal] = True
2767db96d56Sopenharmony_ci            self.p.traps[CondMap[signal]] = True
2777db96d56Sopenharmony_ci
2787db96d56Sopenharmony_ci    def setstatus(self, lst):
2797db96d56Sopenharmony_ci        """lst: C signal list"""
2807db96d56Sopenharmony_ci        self.clear_status()
2817db96d56Sopenharmony_ci        for signal in lst:
2827db96d56Sopenharmony_ci            self.c.flags[signal] = True
2837db96d56Sopenharmony_ci            self.p.flags[CondMap[signal]] = True
2847db96d56Sopenharmony_ci
2857db96d56Sopenharmony_ci    def assert_eq_status(self):
2867db96d56Sopenharmony_ci        """assert equality of C and P status"""
2877db96d56Sopenharmony_ci        for signal in self.c.flags:
2887db96d56Sopenharmony_ci            if self.c.flags[signal] == (not self.p.flags[CondMap[signal]]):
2897db96d56Sopenharmony_ci                return False
2907db96d56Sopenharmony_ci        return True
2917db96d56Sopenharmony_ci
2927db96d56Sopenharmony_ci
2937db96d56Sopenharmony_ci# We don't want exceptions so that we can compare the status flags.
2947db96d56Sopenharmony_cicontext = Context()
2957db96d56Sopenharmony_cicontext.Emin = C.MIN_EMIN
2967db96d56Sopenharmony_cicontext.Emax = C.MAX_EMAX
2977db96d56Sopenharmony_cicontext.clear_traps()
2987db96d56Sopenharmony_ci
2997db96d56Sopenharmony_ci# When creating decimals, _decimal is ultimately limited by the maximum
3007db96d56Sopenharmony_ci# context values. We emulate this restriction for decimal.py.
3017db96d56Sopenharmony_cimaxcontext = P.Context(
3027db96d56Sopenharmony_ci    prec=C.MAX_PREC,
3037db96d56Sopenharmony_ci    Emin=C.MIN_EMIN,
3047db96d56Sopenharmony_ci    Emax=C.MAX_EMAX,
3057db96d56Sopenharmony_ci    rounding=P.ROUND_HALF_UP,
3067db96d56Sopenharmony_ci    capitals=1
3077db96d56Sopenharmony_ci)
3087db96d56Sopenharmony_cimaxcontext.clamp = 0
3097db96d56Sopenharmony_ci
3107db96d56Sopenharmony_cidef RestrictedDecimal(value):
3117db96d56Sopenharmony_ci    maxcontext.traps = copy(context.p.traps)
3127db96d56Sopenharmony_ci    maxcontext.clear_flags()
3137db96d56Sopenharmony_ci    if isinstance(value, str):
3147db96d56Sopenharmony_ci        value = value.strip()
3157db96d56Sopenharmony_ci    dec = maxcontext.create_decimal(value)
3167db96d56Sopenharmony_ci    if maxcontext.flags[P.Inexact] or \
3177db96d56Sopenharmony_ci       maxcontext.flags[P.Rounded] or \
3187db96d56Sopenharmony_ci       maxcontext.flags[P.Clamped] or \
3197db96d56Sopenharmony_ci       maxcontext.flags[P.InvalidOperation]:
3207db96d56Sopenharmony_ci        return context.p._raise_error(P.InvalidOperation)
3217db96d56Sopenharmony_ci    if maxcontext.flags[P.FloatOperation]:
3227db96d56Sopenharmony_ci        context.p.flags[P.FloatOperation] = True
3237db96d56Sopenharmony_ci    return dec
3247db96d56Sopenharmony_ci
3257db96d56Sopenharmony_ci
3267db96d56Sopenharmony_ci# ======================================================================
3277db96d56Sopenharmony_ci#      TestSet: Organize data and events during a single test case
3287db96d56Sopenharmony_ci# ======================================================================
3297db96d56Sopenharmony_ci
3307db96d56Sopenharmony_ciclass RestrictedList(list):
3317db96d56Sopenharmony_ci    """List that can only be modified by appending items."""
3327db96d56Sopenharmony_ci    def __getattribute__(self, name):
3337db96d56Sopenharmony_ci        if name != 'append':
3347db96d56Sopenharmony_ci            raise AttributeError("unsupported operation")
3357db96d56Sopenharmony_ci        return list.__getattribute__(self, name)
3367db96d56Sopenharmony_ci    def unsupported(self, *_):
3377db96d56Sopenharmony_ci        raise AttributeError("unsupported operation")
3387db96d56Sopenharmony_ci    __add__ = __delattr__ = __delitem__ = __iadd__ = __imul__ = unsupported
3397db96d56Sopenharmony_ci    __mul__ = __reversed__ = __rmul__ = __setattr__ = __setitem__ = unsupported
3407db96d56Sopenharmony_ci
3417db96d56Sopenharmony_ciclass TestSet(object):
3427db96d56Sopenharmony_ci    """A TestSet contains the original input operands, converted operands,
3437db96d56Sopenharmony_ci       Python exceptions that occurred either during conversion or during
3447db96d56Sopenharmony_ci       execution of the actual function, and the final results.
3457db96d56Sopenharmony_ci
3467db96d56Sopenharmony_ci       For safety, most attributes are lists that only support the append
3477db96d56Sopenharmony_ci       operation.
3487db96d56Sopenharmony_ci
3497db96d56Sopenharmony_ci       If a function name is prefixed with 'context.', the corresponding
3507db96d56Sopenharmony_ci       context method is called.
3517db96d56Sopenharmony_ci    """
3527db96d56Sopenharmony_ci    def __init__(self, funcname, operands):
3537db96d56Sopenharmony_ci        if funcname.startswith("context."):
3547db96d56Sopenharmony_ci            self.funcname = funcname.replace("context.", "")
3557db96d56Sopenharmony_ci            self.contextfunc = True
3567db96d56Sopenharmony_ci        else:
3577db96d56Sopenharmony_ci            self.funcname = funcname
3587db96d56Sopenharmony_ci            self.contextfunc = False
3597db96d56Sopenharmony_ci        self.op = operands               # raw operand tuple
3607db96d56Sopenharmony_ci        self.context = context           # context used for the operation
3617db96d56Sopenharmony_ci        self.cop = RestrictedList()      # converted C.Decimal operands
3627db96d56Sopenharmony_ci        self.cex = RestrictedList()      # Python exceptions for C.Decimal
3637db96d56Sopenharmony_ci        self.cresults = RestrictedList() # C.Decimal results
3647db96d56Sopenharmony_ci        self.pop = RestrictedList()      # converted P.Decimal operands
3657db96d56Sopenharmony_ci        self.pex = RestrictedList()      # Python exceptions for P.Decimal
3667db96d56Sopenharmony_ci        self.presults = RestrictedList() # P.Decimal results
3677db96d56Sopenharmony_ci
3687db96d56Sopenharmony_ci        # If the above results are exact, unrounded and not clamped, repeat
3697db96d56Sopenharmony_ci        # the operation with a maxcontext to ensure that huge intermediate
3707db96d56Sopenharmony_ci        # values do not cause a MemoryError.
3717db96d56Sopenharmony_ci        self.with_maxcontext = False
3727db96d56Sopenharmony_ci        self.maxcontext = context.c.copy()
3737db96d56Sopenharmony_ci        self.maxcontext.prec = C.MAX_PREC
3747db96d56Sopenharmony_ci        self.maxcontext.Emax = C.MAX_EMAX
3757db96d56Sopenharmony_ci        self.maxcontext.Emin = C.MIN_EMIN
3767db96d56Sopenharmony_ci        self.maxcontext.clear_flags()
3777db96d56Sopenharmony_ci
3787db96d56Sopenharmony_ci        self.maxop = RestrictedList()       # converted C.Decimal operands
3797db96d56Sopenharmony_ci        self.maxex = RestrictedList()       # Python exceptions for C.Decimal
3807db96d56Sopenharmony_ci        self.maxresults = RestrictedList()  # C.Decimal results
3817db96d56Sopenharmony_ci
3827db96d56Sopenharmony_ci
3837db96d56Sopenharmony_ci# ======================================================================
3847db96d56Sopenharmony_ci#                SkipHandler: skip known discrepancies
3857db96d56Sopenharmony_ci# ======================================================================
3867db96d56Sopenharmony_ci
3877db96d56Sopenharmony_ciclass SkipHandler:
3887db96d56Sopenharmony_ci    """Handle known discrepancies between decimal.py and _decimal.so.
3897db96d56Sopenharmony_ci       These are either ULP differences in the power function or
3907db96d56Sopenharmony_ci       extremely minor issues."""
3917db96d56Sopenharmony_ci
3927db96d56Sopenharmony_ci    def __init__(self):
3937db96d56Sopenharmony_ci        self.ulpdiff = 0
3947db96d56Sopenharmony_ci        self.powmod_zeros = 0
3957db96d56Sopenharmony_ci        self.maxctx = P.Context(Emax=10**18, Emin=-10**18)
3967db96d56Sopenharmony_ci
3977db96d56Sopenharmony_ci    def default(self, t):
3987db96d56Sopenharmony_ci        return False
3997db96d56Sopenharmony_ci    __ge__ =  __gt__ = __le__ = __lt__ = __ne__ = __eq__ = default
4007db96d56Sopenharmony_ci    __reduce__ = __format__ = __repr__ = __str__ = default
4017db96d56Sopenharmony_ci
4027db96d56Sopenharmony_ci    def harrison_ulp(self, dec):
4037db96d56Sopenharmony_ci        """ftp://ftp.inria.fr/INRIA/publication/publi-pdf/RR/RR-5504.pdf"""
4047db96d56Sopenharmony_ci        a = dec.next_plus()
4057db96d56Sopenharmony_ci        b = dec.next_minus()
4067db96d56Sopenharmony_ci        return abs(a - b)
4077db96d56Sopenharmony_ci
4087db96d56Sopenharmony_ci    def standard_ulp(self, dec, prec):
4097db96d56Sopenharmony_ci        return _dec_from_triple(0, '1', dec._exp+len(dec._int)-prec)
4107db96d56Sopenharmony_ci
4117db96d56Sopenharmony_ci    def rounding_direction(self, x, mode):
4127db96d56Sopenharmony_ci        """Determine the effective direction of the rounding when
4137db96d56Sopenharmony_ci           the exact result x is rounded according to mode.
4147db96d56Sopenharmony_ci           Return -1 for downwards, 0 for undirected, 1 for upwards,
4157db96d56Sopenharmony_ci           2 for ROUND_05UP."""
4167db96d56Sopenharmony_ci        cmp = 1 if x.compare_total(P.Decimal("+0")) >= 0 else -1
4177db96d56Sopenharmony_ci
4187db96d56Sopenharmony_ci        if mode in (P.ROUND_HALF_EVEN, P.ROUND_HALF_UP, P.ROUND_HALF_DOWN):
4197db96d56Sopenharmony_ci            return 0
4207db96d56Sopenharmony_ci        elif mode == P.ROUND_CEILING:
4217db96d56Sopenharmony_ci            return 1
4227db96d56Sopenharmony_ci        elif mode == P.ROUND_FLOOR:
4237db96d56Sopenharmony_ci            return -1
4247db96d56Sopenharmony_ci        elif mode == P.ROUND_UP:
4257db96d56Sopenharmony_ci            return cmp
4267db96d56Sopenharmony_ci        elif mode == P.ROUND_DOWN:
4277db96d56Sopenharmony_ci            return -cmp
4287db96d56Sopenharmony_ci        elif mode == P.ROUND_05UP:
4297db96d56Sopenharmony_ci            return 2
4307db96d56Sopenharmony_ci        else:
4317db96d56Sopenharmony_ci            raise ValueError("Unexpected rounding mode: %s" % mode)
4327db96d56Sopenharmony_ci
4337db96d56Sopenharmony_ci    def check_ulpdiff(self, exact, rounded):
4347db96d56Sopenharmony_ci        # current precision
4357db96d56Sopenharmony_ci        p = context.p.prec
4367db96d56Sopenharmony_ci
4377db96d56Sopenharmony_ci        # Convert infinities to the largest representable number + 1.
4387db96d56Sopenharmony_ci        x = exact
4397db96d56Sopenharmony_ci        if exact.is_infinite():
4407db96d56Sopenharmony_ci            x = _dec_from_triple(exact._sign, '10', context.p.Emax)
4417db96d56Sopenharmony_ci        y = rounded
4427db96d56Sopenharmony_ci        if rounded.is_infinite():
4437db96d56Sopenharmony_ci            y = _dec_from_triple(rounded._sign, '10', context.p.Emax)
4447db96d56Sopenharmony_ci
4457db96d56Sopenharmony_ci        # err = (rounded - exact) / ulp(rounded)
4467db96d56Sopenharmony_ci        self.maxctx.prec = p * 2
4477db96d56Sopenharmony_ci        t = self.maxctx.subtract(y, x)
4487db96d56Sopenharmony_ci        if context.c.flags[C.Clamped] or \
4497db96d56Sopenharmony_ci           context.c.flags[C.Underflow]:
4507db96d56Sopenharmony_ci            # The standard ulp does not work in Underflow territory.
4517db96d56Sopenharmony_ci            ulp = self.harrison_ulp(y)
4527db96d56Sopenharmony_ci        else:
4537db96d56Sopenharmony_ci            ulp = self.standard_ulp(y, p)
4547db96d56Sopenharmony_ci        # Error in ulps.
4557db96d56Sopenharmony_ci        err = self.maxctx.divide(t, ulp)
4567db96d56Sopenharmony_ci
4577db96d56Sopenharmony_ci        dir = self.rounding_direction(x, context.p.rounding)
4587db96d56Sopenharmony_ci        if dir == 0:
4597db96d56Sopenharmony_ci            if P.Decimal("-0.6") < err < P.Decimal("0.6"):
4607db96d56Sopenharmony_ci                return True
4617db96d56Sopenharmony_ci        elif dir == 1: # directed, upwards
4627db96d56Sopenharmony_ci            if P.Decimal("-0.1") < err < P.Decimal("1.1"):
4637db96d56Sopenharmony_ci                return True
4647db96d56Sopenharmony_ci        elif dir == -1: # directed, downwards
4657db96d56Sopenharmony_ci            if P.Decimal("-1.1") < err < P.Decimal("0.1"):
4667db96d56Sopenharmony_ci                return True
4677db96d56Sopenharmony_ci        else: # ROUND_05UP
4687db96d56Sopenharmony_ci            if P.Decimal("-1.1") < err < P.Decimal("1.1"):
4697db96d56Sopenharmony_ci                return True
4707db96d56Sopenharmony_ci
4717db96d56Sopenharmony_ci        print("ulp: %s  error: %s  exact: %s  c_rounded: %s"
4727db96d56Sopenharmony_ci              % (ulp, err, exact, rounded))
4737db96d56Sopenharmony_ci        return False
4747db96d56Sopenharmony_ci
4757db96d56Sopenharmony_ci    def bin_resolve_ulp(self, t):
4767db96d56Sopenharmony_ci        """Check if results of _decimal's power function are within the
4777db96d56Sopenharmony_ci           allowed ulp ranges."""
4787db96d56Sopenharmony_ci        # NaNs are beyond repair.
4797db96d56Sopenharmony_ci        if t.rc.is_nan() or t.rp.is_nan():
4807db96d56Sopenharmony_ci            return False
4817db96d56Sopenharmony_ci
4827db96d56Sopenharmony_ci        # "exact" result, double precision, half_even
4837db96d56Sopenharmony_ci        self.maxctx.prec = context.p.prec * 2
4847db96d56Sopenharmony_ci
4857db96d56Sopenharmony_ci        op1, op2 = t.pop[0], t.pop[1]
4867db96d56Sopenharmony_ci        if t.contextfunc:
4877db96d56Sopenharmony_ci            exact = getattr(self.maxctx, t.funcname)(op1, op2)
4887db96d56Sopenharmony_ci        else:
4897db96d56Sopenharmony_ci            exact = getattr(op1, t.funcname)(op2, context=self.maxctx)
4907db96d56Sopenharmony_ci
4917db96d56Sopenharmony_ci        # _decimal's rounded result
4927db96d56Sopenharmony_ci        rounded = P.Decimal(t.cresults[0])
4937db96d56Sopenharmony_ci
4947db96d56Sopenharmony_ci        self.ulpdiff += 1
4957db96d56Sopenharmony_ci        return self.check_ulpdiff(exact, rounded)
4967db96d56Sopenharmony_ci
4977db96d56Sopenharmony_ci    ############################ Correct rounding #############################
4987db96d56Sopenharmony_ci    def resolve_underflow(self, t):
4997db96d56Sopenharmony_ci        """In extremely rare cases where the infinite precision result is just
5007db96d56Sopenharmony_ci           below etiny, cdecimal does not set Subnormal/Underflow. Example:
5017db96d56Sopenharmony_ci
5027db96d56Sopenharmony_ci           setcontext(Context(prec=21, rounding=ROUND_UP, Emin=-55, Emax=85))
5037db96d56Sopenharmony_ci           Decimal("1.00000000000000000000000000000000000000000000000"
5047db96d56Sopenharmony_ci                   "0000000100000000000000000000000000000000000000000"
5057db96d56Sopenharmony_ci                   "0000000000000025").ln()
5067db96d56Sopenharmony_ci        """
5077db96d56Sopenharmony_ci        if t.cresults != t.presults:
5087db96d56Sopenharmony_ci            return False # Results must be identical.
5097db96d56Sopenharmony_ci        if context.c.flags[C.Rounded] and \
5107db96d56Sopenharmony_ci           context.c.flags[C.Inexact] and \
5117db96d56Sopenharmony_ci           context.p.flags[P.Rounded] and \
5127db96d56Sopenharmony_ci           context.p.flags[P.Inexact]:
5137db96d56Sopenharmony_ci            return True # Subnormal/Underflow may be missing.
5147db96d56Sopenharmony_ci        return False
5157db96d56Sopenharmony_ci
5167db96d56Sopenharmony_ci    def exp(self, t):
5177db96d56Sopenharmony_ci        """Resolve Underflow or ULP difference."""
5187db96d56Sopenharmony_ci        return self.resolve_underflow(t)
5197db96d56Sopenharmony_ci
5207db96d56Sopenharmony_ci    def log10(self, t):
5217db96d56Sopenharmony_ci        """Resolve Underflow or ULP difference."""
5227db96d56Sopenharmony_ci        return self.resolve_underflow(t)
5237db96d56Sopenharmony_ci
5247db96d56Sopenharmony_ci    def ln(self, t):
5257db96d56Sopenharmony_ci        """Resolve Underflow or ULP difference."""
5267db96d56Sopenharmony_ci        return self.resolve_underflow(t)
5277db96d56Sopenharmony_ci
5287db96d56Sopenharmony_ci    def __pow__(self, t):
5297db96d56Sopenharmony_ci        """Always calls the resolve function. C.Decimal does not have correct
5307db96d56Sopenharmony_ci           rounding for the power function."""
5317db96d56Sopenharmony_ci        if context.c.flags[C.Rounded] and \
5327db96d56Sopenharmony_ci           context.c.flags[C.Inexact] and \
5337db96d56Sopenharmony_ci           context.p.flags[P.Rounded] and \
5347db96d56Sopenharmony_ci           context.p.flags[P.Inexact]:
5357db96d56Sopenharmony_ci            return self.bin_resolve_ulp(t)
5367db96d56Sopenharmony_ci        else:
5377db96d56Sopenharmony_ci            return False
5387db96d56Sopenharmony_ci    power = __rpow__ = __pow__
5397db96d56Sopenharmony_ci
5407db96d56Sopenharmony_ci    ############################## Technicalities #############################
5417db96d56Sopenharmony_ci    def __float__(self, t):
5427db96d56Sopenharmony_ci        """NaN comparison in the verify() function obviously gives an
5437db96d56Sopenharmony_ci           incorrect answer:  nan == nan -> False"""
5447db96d56Sopenharmony_ci        if t.cop[0].is_nan() and t.pop[0].is_nan():
5457db96d56Sopenharmony_ci            return True
5467db96d56Sopenharmony_ci        return False
5477db96d56Sopenharmony_ci    __complex__ = __float__
5487db96d56Sopenharmony_ci
5497db96d56Sopenharmony_ci    def __radd__(self, t):
5507db96d56Sopenharmony_ci        """decimal.py gives precedence to the first NaN; this is
5517db96d56Sopenharmony_ci           not important, as __radd__ will not be called for
5527db96d56Sopenharmony_ci           two decimal arguments."""
5537db96d56Sopenharmony_ci        if t.rc.is_nan() and t.rp.is_nan():
5547db96d56Sopenharmony_ci            return True
5557db96d56Sopenharmony_ci        return False
5567db96d56Sopenharmony_ci    __rmul__ = __radd__
5577db96d56Sopenharmony_ci
5587db96d56Sopenharmony_ci    ################################ Various ##################################
5597db96d56Sopenharmony_ci    def __round__(self, t):
5607db96d56Sopenharmony_ci        """Exception: Decimal('1').__round__(-100000000000000000000000000)
5617db96d56Sopenharmony_ci           Should it really be InvalidOperation?"""
5627db96d56Sopenharmony_ci        if t.rc is None and t.rp.is_nan():
5637db96d56Sopenharmony_ci            return True
5647db96d56Sopenharmony_ci        return False
5657db96d56Sopenharmony_ci
5667db96d56Sopenharmony_cishandler = SkipHandler()
5677db96d56Sopenharmony_cidef skip_error(t):
5687db96d56Sopenharmony_ci    return getattr(shandler, t.funcname, shandler.default)(t)
5697db96d56Sopenharmony_ci
5707db96d56Sopenharmony_ci
5717db96d56Sopenharmony_ci# ======================================================================
5727db96d56Sopenharmony_ci#                      Handling verification errors
5737db96d56Sopenharmony_ci# ======================================================================
5747db96d56Sopenharmony_ci
5757db96d56Sopenharmony_ciclass VerifyError(Exception):
5767db96d56Sopenharmony_ci    """Verification failed."""
5777db96d56Sopenharmony_ci    pass
5787db96d56Sopenharmony_ci
5797db96d56Sopenharmony_cidef function_as_string(t):
5807db96d56Sopenharmony_ci    if t.contextfunc:
5817db96d56Sopenharmony_ci        cargs = t.cop
5827db96d56Sopenharmony_ci        pargs = t.pop
5837db96d56Sopenharmony_ci        maxargs = t.maxop
5847db96d56Sopenharmony_ci        cfunc = "c_func: %s(" % t.funcname
5857db96d56Sopenharmony_ci        pfunc = "p_func: %s(" % t.funcname
5867db96d56Sopenharmony_ci        maxfunc = "max_func: %s(" % t.funcname
5877db96d56Sopenharmony_ci    else:
5887db96d56Sopenharmony_ci        cself, cargs = t.cop[0], t.cop[1:]
5897db96d56Sopenharmony_ci        pself, pargs = t.pop[0], t.pop[1:]
5907db96d56Sopenharmony_ci        maxself, maxargs = t.maxop[0], t.maxop[1:]
5917db96d56Sopenharmony_ci        cfunc = "c_func: %s.%s(" % (repr(cself), t.funcname)
5927db96d56Sopenharmony_ci        pfunc = "p_func: %s.%s(" % (repr(pself), t.funcname)
5937db96d56Sopenharmony_ci        maxfunc = "max_func: %s.%s(" % (repr(maxself), t.funcname)
5947db96d56Sopenharmony_ci
5957db96d56Sopenharmony_ci    err = cfunc
5967db96d56Sopenharmony_ci    for arg in cargs:
5977db96d56Sopenharmony_ci        err += "%s, " % repr(arg)
5987db96d56Sopenharmony_ci    err = err.rstrip(", ")
5997db96d56Sopenharmony_ci    err += ")\n"
6007db96d56Sopenharmony_ci
6017db96d56Sopenharmony_ci    err += pfunc
6027db96d56Sopenharmony_ci    for arg in pargs:
6037db96d56Sopenharmony_ci        err += "%s, " % repr(arg)
6047db96d56Sopenharmony_ci    err = err.rstrip(", ")
6057db96d56Sopenharmony_ci    err += ")"
6067db96d56Sopenharmony_ci
6077db96d56Sopenharmony_ci    if t.with_maxcontext:
6087db96d56Sopenharmony_ci        err += "\n"
6097db96d56Sopenharmony_ci        err += maxfunc
6107db96d56Sopenharmony_ci        for arg in maxargs:
6117db96d56Sopenharmony_ci            err += "%s, " % repr(arg)
6127db96d56Sopenharmony_ci        err = err.rstrip(", ")
6137db96d56Sopenharmony_ci        err += ")"
6147db96d56Sopenharmony_ci
6157db96d56Sopenharmony_ci    return err
6167db96d56Sopenharmony_ci
6177db96d56Sopenharmony_cidef raise_error(t):
6187db96d56Sopenharmony_ci    global EXIT_STATUS
6197db96d56Sopenharmony_ci
6207db96d56Sopenharmony_ci    if skip_error(t):
6217db96d56Sopenharmony_ci        return
6227db96d56Sopenharmony_ci    EXIT_STATUS = 1
6237db96d56Sopenharmony_ci
6247db96d56Sopenharmony_ci    err = "Error in %s:\n\n" % t.funcname
6257db96d56Sopenharmony_ci    err += "input operands: %s\n\n" % (t.op,)
6267db96d56Sopenharmony_ci    err += function_as_string(t)
6277db96d56Sopenharmony_ci
6287db96d56Sopenharmony_ci    err += "\n\nc_result: %s\np_result: %s\n" % (t.cresults, t.presults)
6297db96d56Sopenharmony_ci    if t.with_maxcontext:
6307db96d56Sopenharmony_ci        err += "max_result: %s\n\n" % (t.maxresults)
6317db96d56Sopenharmony_ci    else:
6327db96d56Sopenharmony_ci        err += "\n"
6337db96d56Sopenharmony_ci
6347db96d56Sopenharmony_ci    err += "c_exceptions: %s\np_exceptions: %s\n" % (t.cex, t.pex)
6357db96d56Sopenharmony_ci    if t.with_maxcontext:
6367db96d56Sopenharmony_ci        err += "max_exceptions: %s\n\n" % t.maxex
6377db96d56Sopenharmony_ci    else:
6387db96d56Sopenharmony_ci        err += "\n"
6397db96d56Sopenharmony_ci
6407db96d56Sopenharmony_ci    err += "%s\n" % str(t.context)
6417db96d56Sopenharmony_ci    if t.with_maxcontext:
6427db96d56Sopenharmony_ci        err += "%s\n" % str(t.maxcontext)
6437db96d56Sopenharmony_ci    else:
6447db96d56Sopenharmony_ci        err += "\n"
6457db96d56Sopenharmony_ci
6467db96d56Sopenharmony_ci    raise VerifyError(err)
6477db96d56Sopenharmony_ci
6487db96d56Sopenharmony_ci
6497db96d56Sopenharmony_ci# ======================================================================
6507db96d56Sopenharmony_ci#                        Main testing functions
6517db96d56Sopenharmony_ci#
6527db96d56Sopenharmony_ci#  The procedure is always (t is the TestSet):
6537db96d56Sopenharmony_ci#
6547db96d56Sopenharmony_ci#   convert(t) -> Initialize the TestSet as necessary.
6557db96d56Sopenharmony_ci#
6567db96d56Sopenharmony_ci#                 Return 0 for early abortion (e.g. if a TypeError
6577db96d56Sopenharmony_ci#                 occurs during conversion, there is nothing to test).
6587db96d56Sopenharmony_ci#
6597db96d56Sopenharmony_ci#                 Return 1 for continuing with the test case.
6607db96d56Sopenharmony_ci#
6617db96d56Sopenharmony_ci#   callfuncs(t) -> Call the relevant function for each implementation
6627db96d56Sopenharmony_ci#                   and record the results in the TestSet.
6637db96d56Sopenharmony_ci#
6647db96d56Sopenharmony_ci#   verify(t) -> Verify the results. If verification fails, details
6657db96d56Sopenharmony_ci#                are printed to stdout.
6667db96d56Sopenharmony_ci# ======================================================================
6677db96d56Sopenharmony_ci
6687db96d56Sopenharmony_cidef all_nan(a):
6697db96d56Sopenharmony_ci    if isinstance(a, C.Decimal):
6707db96d56Sopenharmony_ci        return a.is_nan()
6717db96d56Sopenharmony_ci    elif isinstance(a, tuple):
6727db96d56Sopenharmony_ci        return all(all_nan(v) for v in a)
6737db96d56Sopenharmony_ci    return False
6747db96d56Sopenharmony_ci
6757db96d56Sopenharmony_cidef convert(t, convstr=True):
6767db96d56Sopenharmony_ci    """ t is the testset. At this stage the testset contains a tuple of
6777db96d56Sopenharmony_ci        operands t.op of various types. For decimal methods the first
6787db96d56Sopenharmony_ci        operand (self) is always converted to Decimal. If 'convstr' is
6797db96d56Sopenharmony_ci        true, string operands are converted as well.
6807db96d56Sopenharmony_ci
6817db96d56Sopenharmony_ci        Context operands are of type deccheck.Context, rounding mode
6827db96d56Sopenharmony_ci        operands are given as a tuple (C.rounding, P.rounding).
6837db96d56Sopenharmony_ci
6847db96d56Sopenharmony_ci        Other types (float, int, etc.) are left unchanged.
6857db96d56Sopenharmony_ci    """
6867db96d56Sopenharmony_ci    for i, op in enumerate(t.op):
6877db96d56Sopenharmony_ci
6887db96d56Sopenharmony_ci        context.clear_status()
6897db96d56Sopenharmony_ci        t.maxcontext.clear_flags()
6907db96d56Sopenharmony_ci
6917db96d56Sopenharmony_ci        if op in RoundModes:
6927db96d56Sopenharmony_ci            t.cop.append(op)
6937db96d56Sopenharmony_ci            t.pop.append(op)
6947db96d56Sopenharmony_ci            t.maxop.append(op)
6957db96d56Sopenharmony_ci
6967db96d56Sopenharmony_ci        elif not t.contextfunc and i == 0 or \
6977db96d56Sopenharmony_ci             convstr and isinstance(op, str):
6987db96d56Sopenharmony_ci            try:
6997db96d56Sopenharmony_ci                c = C.Decimal(op)
7007db96d56Sopenharmony_ci                cex = None
7017db96d56Sopenharmony_ci            except (TypeError, ValueError, OverflowError) as e:
7027db96d56Sopenharmony_ci                c = None
7037db96d56Sopenharmony_ci                cex = e.__class__
7047db96d56Sopenharmony_ci
7057db96d56Sopenharmony_ci            try:
7067db96d56Sopenharmony_ci                p = RestrictedDecimal(op)
7077db96d56Sopenharmony_ci                pex = None
7087db96d56Sopenharmony_ci            except (TypeError, ValueError, OverflowError) as e:
7097db96d56Sopenharmony_ci                p = None
7107db96d56Sopenharmony_ci                pex = e.__class__
7117db96d56Sopenharmony_ci
7127db96d56Sopenharmony_ci            try:
7137db96d56Sopenharmony_ci                C.setcontext(t.maxcontext)
7147db96d56Sopenharmony_ci                maxop = C.Decimal(op)
7157db96d56Sopenharmony_ci                maxex = None
7167db96d56Sopenharmony_ci            except (TypeError, ValueError, OverflowError) as e:
7177db96d56Sopenharmony_ci                maxop = None
7187db96d56Sopenharmony_ci                maxex = e.__class__
7197db96d56Sopenharmony_ci            finally:
7207db96d56Sopenharmony_ci                C.setcontext(context.c)
7217db96d56Sopenharmony_ci
7227db96d56Sopenharmony_ci            t.cop.append(c)
7237db96d56Sopenharmony_ci            t.cex.append(cex)
7247db96d56Sopenharmony_ci
7257db96d56Sopenharmony_ci            t.pop.append(p)
7267db96d56Sopenharmony_ci            t.pex.append(pex)
7277db96d56Sopenharmony_ci
7287db96d56Sopenharmony_ci            t.maxop.append(maxop)
7297db96d56Sopenharmony_ci            t.maxex.append(maxex)
7307db96d56Sopenharmony_ci
7317db96d56Sopenharmony_ci            if cex is pex:
7327db96d56Sopenharmony_ci                if str(c) != str(p) or not context.assert_eq_status():
7337db96d56Sopenharmony_ci                    raise_error(t)
7347db96d56Sopenharmony_ci                if cex and pex:
7357db96d56Sopenharmony_ci                    # nothing to test
7367db96d56Sopenharmony_ci                    return 0
7377db96d56Sopenharmony_ci            else:
7387db96d56Sopenharmony_ci                raise_error(t)
7397db96d56Sopenharmony_ci
7407db96d56Sopenharmony_ci            # The exceptions in the maxcontext operation can legitimately
7417db96d56Sopenharmony_ci            # differ, only test that maxex implies cex:
7427db96d56Sopenharmony_ci            if maxex is not None and cex is not maxex:
7437db96d56Sopenharmony_ci                raise_error(t)
7447db96d56Sopenharmony_ci
7457db96d56Sopenharmony_ci        elif isinstance(op, Context):
7467db96d56Sopenharmony_ci            t.context = op
7477db96d56Sopenharmony_ci            t.cop.append(op.c)
7487db96d56Sopenharmony_ci            t.pop.append(op.p)
7497db96d56Sopenharmony_ci            t.maxop.append(t.maxcontext)
7507db96d56Sopenharmony_ci
7517db96d56Sopenharmony_ci        else:
7527db96d56Sopenharmony_ci            t.cop.append(op)
7537db96d56Sopenharmony_ci            t.pop.append(op)
7547db96d56Sopenharmony_ci            t.maxop.append(op)
7557db96d56Sopenharmony_ci
7567db96d56Sopenharmony_ci    return 1
7577db96d56Sopenharmony_ci
7587db96d56Sopenharmony_cidef callfuncs(t):
7597db96d56Sopenharmony_ci    """ t is the testset. At this stage the testset contains operand lists
7607db96d56Sopenharmony_ci        t.cop and t.pop for the C and Python versions of decimal.
7617db96d56Sopenharmony_ci        For Decimal methods, the first operands are of type C.Decimal and
7627db96d56Sopenharmony_ci        P.Decimal respectively. The remaining operands can have various types.
7637db96d56Sopenharmony_ci        For Context methods, all operands can have any type.
7647db96d56Sopenharmony_ci
7657db96d56Sopenharmony_ci        t.rc and t.rp are the results of the operation.
7667db96d56Sopenharmony_ci    """
7677db96d56Sopenharmony_ci    context.clear_status()
7687db96d56Sopenharmony_ci    t.maxcontext.clear_flags()
7697db96d56Sopenharmony_ci
7707db96d56Sopenharmony_ci    try:
7717db96d56Sopenharmony_ci        if t.contextfunc:
7727db96d56Sopenharmony_ci            cargs = t.cop
7737db96d56Sopenharmony_ci            t.rc = getattr(context.c, t.funcname)(*cargs)
7747db96d56Sopenharmony_ci        else:
7757db96d56Sopenharmony_ci            cself = t.cop[0]
7767db96d56Sopenharmony_ci            cargs = t.cop[1:]
7777db96d56Sopenharmony_ci            t.rc = getattr(cself, t.funcname)(*cargs)
7787db96d56Sopenharmony_ci        t.cex.append(None)
7797db96d56Sopenharmony_ci    except (TypeError, ValueError, OverflowError, MemoryError) as e:
7807db96d56Sopenharmony_ci        t.rc = None
7817db96d56Sopenharmony_ci        t.cex.append(e.__class__)
7827db96d56Sopenharmony_ci
7837db96d56Sopenharmony_ci    try:
7847db96d56Sopenharmony_ci        if t.contextfunc:
7857db96d56Sopenharmony_ci            pargs = t.pop
7867db96d56Sopenharmony_ci            t.rp = getattr(context.p, t.funcname)(*pargs)
7877db96d56Sopenharmony_ci        else:
7887db96d56Sopenharmony_ci            pself = t.pop[0]
7897db96d56Sopenharmony_ci            pargs = t.pop[1:]
7907db96d56Sopenharmony_ci            t.rp = getattr(pself, t.funcname)(*pargs)
7917db96d56Sopenharmony_ci        t.pex.append(None)
7927db96d56Sopenharmony_ci    except (TypeError, ValueError, OverflowError, MemoryError) as e:
7937db96d56Sopenharmony_ci        t.rp = None
7947db96d56Sopenharmony_ci        t.pex.append(e.__class__)
7957db96d56Sopenharmony_ci
7967db96d56Sopenharmony_ci    # If the above results are exact, unrounded, normal etc., repeat the
7977db96d56Sopenharmony_ci    # operation with a maxcontext to ensure that huge intermediate values
7987db96d56Sopenharmony_ci    # do not cause a MemoryError.
7997db96d56Sopenharmony_ci    if (t.funcname not in MaxContextSkip and
8007db96d56Sopenharmony_ci        not context.c.flags[C.InvalidOperation] and
8017db96d56Sopenharmony_ci        not context.c.flags[C.Inexact] and
8027db96d56Sopenharmony_ci        not context.c.flags[C.Rounded] and
8037db96d56Sopenharmony_ci        not context.c.flags[C.Subnormal] and
8047db96d56Sopenharmony_ci        not context.c.flags[C.Clamped] and
8057db96d56Sopenharmony_ci        not context.clamp and # results are padded to context.prec if context.clamp==1.
8067db96d56Sopenharmony_ci        not any(isinstance(v, C.Context) for v in t.cop)): # another context is used.
8077db96d56Sopenharmony_ci        t.with_maxcontext = True
8087db96d56Sopenharmony_ci        try:
8097db96d56Sopenharmony_ci            if t.contextfunc:
8107db96d56Sopenharmony_ci                maxargs = t.maxop
8117db96d56Sopenharmony_ci                t.rmax = getattr(t.maxcontext, t.funcname)(*maxargs)
8127db96d56Sopenharmony_ci            else:
8137db96d56Sopenharmony_ci                maxself = t.maxop[0]
8147db96d56Sopenharmony_ci                maxargs = t.maxop[1:]
8157db96d56Sopenharmony_ci                try:
8167db96d56Sopenharmony_ci                    C.setcontext(t.maxcontext)
8177db96d56Sopenharmony_ci                    t.rmax = getattr(maxself, t.funcname)(*maxargs)
8187db96d56Sopenharmony_ci                finally:
8197db96d56Sopenharmony_ci                    C.setcontext(context.c)
8207db96d56Sopenharmony_ci            t.maxex.append(None)
8217db96d56Sopenharmony_ci        except (TypeError, ValueError, OverflowError, MemoryError) as e:
8227db96d56Sopenharmony_ci            t.rmax = None
8237db96d56Sopenharmony_ci            t.maxex.append(e.__class__)
8247db96d56Sopenharmony_ci
8257db96d56Sopenharmony_cidef verify(t, stat):
8267db96d56Sopenharmony_ci    """ t is the testset. At this stage the testset contains the following
8277db96d56Sopenharmony_ci        tuples:
8287db96d56Sopenharmony_ci
8297db96d56Sopenharmony_ci            t.op: original operands
8307db96d56Sopenharmony_ci            t.cop: C.Decimal operands (see convert for details)
8317db96d56Sopenharmony_ci            t.pop: P.Decimal operands (see convert for details)
8327db96d56Sopenharmony_ci            t.rc: C result
8337db96d56Sopenharmony_ci            t.rp: Python result
8347db96d56Sopenharmony_ci
8357db96d56Sopenharmony_ci        t.rc and t.rp can have various types.
8367db96d56Sopenharmony_ci    """
8377db96d56Sopenharmony_ci    t.cresults.append(str(t.rc))
8387db96d56Sopenharmony_ci    t.presults.append(str(t.rp))
8397db96d56Sopenharmony_ci    if t.with_maxcontext:
8407db96d56Sopenharmony_ci        t.maxresults.append(str(t.rmax))
8417db96d56Sopenharmony_ci
8427db96d56Sopenharmony_ci    if isinstance(t.rc, C.Decimal) and isinstance(t.rp, P.Decimal):
8437db96d56Sopenharmony_ci        # General case: both results are Decimals.
8447db96d56Sopenharmony_ci        t.cresults.append(t.rc.to_eng_string())
8457db96d56Sopenharmony_ci        t.cresults.append(t.rc.as_tuple())
8467db96d56Sopenharmony_ci        t.cresults.append(str(t.rc.imag))
8477db96d56Sopenharmony_ci        t.cresults.append(str(t.rc.real))
8487db96d56Sopenharmony_ci        t.presults.append(t.rp.to_eng_string())
8497db96d56Sopenharmony_ci        t.presults.append(t.rp.as_tuple())
8507db96d56Sopenharmony_ci        t.presults.append(str(t.rp.imag))
8517db96d56Sopenharmony_ci        t.presults.append(str(t.rp.real))
8527db96d56Sopenharmony_ci
8537db96d56Sopenharmony_ci        if t.with_maxcontext and isinstance(t.rmax, C.Decimal):
8547db96d56Sopenharmony_ci            t.maxresults.append(t.rmax.to_eng_string())
8557db96d56Sopenharmony_ci            t.maxresults.append(t.rmax.as_tuple())
8567db96d56Sopenharmony_ci            t.maxresults.append(str(t.rmax.imag))
8577db96d56Sopenharmony_ci            t.maxresults.append(str(t.rmax.real))
8587db96d56Sopenharmony_ci
8597db96d56Sopenharmony_ci        nc = t.rc.number_class().lstrip('+-s')
8607db96d56Sopenharmony_ci        stat[nc] += 1
8617db96d56Sopenharmony_ci    else:
8627db96d56Sopenharmony_ci        # Results from e.g. __divmod__ can only be compared as strings.
8637db96d56Sopenharmony_ci        if not isinstance(t.rc, tuple) and not isinstance(t.rp, tuple):
8647db96d56Sopenharmony_ci            if t.rc != t.rp:
8657db96d56Sopenharmony_ci                raise_error(t)
8667db96d56Sopenharmony_ci            if t.with_maxcontext and not isinstance(t.rmax, tuple):
8677db96d56Sopenharmony_ci                if t.rmax != t.rc:
8687db96d56Sopenharmony_ci                    raise_error(t)
8697db96d56Sopenharmony_ci        stat[type(t.rc).__name__] += 1
8707db96d56Sopenharmony_ci
8717db96d56Sopenharmony_ci    # The return value lists must be equal.
8727db96d56Sopenharmony_ci    if t.cresults != t.presults:
8737db96d56Sopenharmony_ci        raise_error(t)
8747db96d56Sopenharmony_ci    # The Python exception lists (TypeError, etc.) must be equal.
8757db96d56Sopenharmony_ci    if t.cex != t.pex:
8767db96d56Sopenharmony_ci        raise_error(t)
8777db96d56Sopenharmony_ci    # The context flags must be equal.
8787db96d56Sopenharmony_ci    if not t.context.assert_eq_status():
8797db96d56Sopenharmony_ci        raise_error(t)
8807db96d56Sopenharmony_ci
8817db96d56Sopenharmony_ci    if t.with_maxcontext:
8827db96d56Sopenharmony_ci        # NaN payloads etc. depend on precision and clamp.
8837db96d56Sopenharmony_ci        if all_nan(t.rc) and all_nan(t.rmax):
8847db96d56Sopenharmony_ci            return
8857db96d56Sopenharmony_ci        # The return value lists must be equal.
8867db96d56Sopenharmony_ci        if t.maxresults != t.cresults:
8877db96d56Sopenharmony_ci            raise_error(t)
8887db96d56Sopenharmony_ci        # The Python exception lists (TypeError, etc.) must be equal.
8897db96d56Sopenharmony_ci        if t.maxex != t.cex:
8907db96d56Sopenharmony_ci            raise_error(t)
8917db96d56Sopenharmony_ci        # The context flags must be equal.
8927db96d56Sopenharmony_ci        if t.maxcontext.flags != t.context.c.flags:
8937db96d56Sopenharmony_ci            raise_error(t)
8947db96d56Sopenharmony_ci
8957db96d56Sopenharmony_ci
8967db96d56Sopenharmony_ci# ======================================================================
8977db96d56Sopenharmony_ci#                           Main test loops
8987db96d56Sopenharmony_ci#
8997db96d56Sopenharmony_ci#  test_method(method, testspecs, testfunc) ->
9007db96d56Sopenharmony_ci#
9017db96d56Sopenharmony_ci#     Loop through various context settings. The degree of
9027db96d56Sopenharmony_ci#     thoroughness is determined by 'testspec'. For each
9037db96d56Sopenharmony_ci#     setting, call 'testfunc'. Generally, 'testfunc' itself
9047db96d56Sopenharmony_ci#     a loop, iterating through many test cases generated
9057db96d56Sopenharmony_ci#     by the functions in randdec.py.
9067db96d56Sopenharmony_ci#
9077db96d56Sopenharmony_ci#  test_n-ary(method, prec, exp_range, restricted_range, itr, stat) ->
9087db96d56Sopenharmony_ci#
9097db96d56Sopenharmony_ci#     'test_unary', 'test_binary' and 'test_ternary' are the
9107db96d56Sopenharmony_ci#     main test functions passed to 'test_method'. They deal
9117db96d56Sopenharmony_ci#     with the regular cases. The thoroughness of testing is
9127db96d56Sopenharmony_ci#     determined by 'itr'.
9137db96d56Sopenharmony_ci#
9147db96d56Sopenharmony_ci#     'prec', 'exp_range' and 'restricted_range' are passed
9157db96d56Sopenharmony_ci#     to the test-generating functions and limit the generated
9167db96d56Sopenharmony_ci#     values. In some cases, for reasonable run times a
9177db96d56Sopenharmony_ci#     maximum exponent of 9999 is required.
9187db96d56Sopenharmony_ci#
9197db96d56Sopenharmony_ci#     The 'stat' parameter is passed down to the 'verify'
9207db96d56Sopenharmony_ci#     function, which records statistics for the result values.
9217db96d56Sopenharmony_ci# ======================================================================
9227db96d56Sopenharmony_ci
9237db96d56Sopenharmony_cidef log(fmt, args=None):
9247db96d56Sopenharmony_ci    if args:
9257db96d56Sopenharmony_ci        sys.stdout.write(''.join((fmt, '\n')) % args)
9267db96d56Sopenharmony_ci    else:
9277db96d56Sopenharmony_ci        sys.stdout.write(''.join((str(fmt), '\n')))
9287db96d56Sopenharmony_ci    sys.stdout.flush()
9297db96d56Sopenharmony_ci
9307db96d56Sopenharmony_cidef test_method(method, testspecs, testfunc):
9317db96d56Sopenharmony_ci    """Iterate a test function through many context settings."""
9327db96d56Sopenharmony_ci    log("testing %s ...", method)
9337db96d56Sopenharmony_ci    stat = defaultdict(int)
9347db96d56Sopenharmony_ci    for spec in testspecs:
9357db96d56Sopenharmony_ci        if 'samples' in spec:
9367db96d56Sopenharmony_ci            spec['prec'] = sorted(random.sample(range(1, 101),
9377db96d56Sopenharmony_ci                                  spec['samples']))
9387db96d56Sopenharmony_ci        for prec in spec['prec']:
9397db96d56Sopenharmony_ci            context.prec = prec
9407db96d56Sopenharmony_ci            for expts in spec['expts']:
9417db96d56Sopenharmony_ci                emin, emax = expts
9427db96d56Sopenharmony_ci                if emin == 'rand':
9437db96d56Sopenharmony_ci                    context.Emin = random.randrange(-1000, 0)
9447db96d56Sopenharmony_ci                    context.Emax = random.randrange(prec, 1000)
9457db96d56Sopenharmony_ci                else:
9467db96d56Sopenharmony_ci                    context.Emin, context.Emax = emin, emax
9477db96d56Sopenharmony_ci                if prec > context.Emax: continue
9487db96d56Sopenharmony_ci                log("    prec: %d  emin: %d  emax: %d",
9497db96d56Sopenharmony_ci                    (context.prec, context.Emin, context.Emax))
9507db96d56Sopenharmony_ci                restr_range = 9999 if context.Emax > 9999 else context.Emax+99
9517db96d56Sopenharmony_ci                for rounding in RoundModes:
9527db96d56Sopenharmony_ci                    context.rounding = rounding
9537db96d56Sopenharmony_ci                    context.capitals = random.randrange(2)
9547db96d56Sopenharmony_ci                    if spec['clamp'] == 'rand':
9557db96d56Sopenharmony_ci                        context.clamp = random.randrange(2)
9567db96d56Sopenharmony_ci                    else:
9577db96d56Sopenharmony_ci                        context.clamp = spec['clamp']
9587db96d56Sopenharmony_ci                    exprange = context.c.Emax
9597db96d56Sopenharmony_ci                    testfunc(method, prec, exprange, restr_range,
9607db96d56Sopenharmony_ci                             spec['iter'], stat)
9617db96d56Sopenharmony_ci    log("    result types: %s" % sorted([t for t in stat.items()]))
9627db96d56Sopenharmony_ci
9637db96d56Sopenharmony_cidef test_unary(method, prec, exp_range, restricted_range, itr, stat):
9647db96d56Sopenharmony_ci    """Iterate a unary function through many test cases."""
9657db96d56Sopenharmony_ci    if method in UnaryRestricted:
9667db96d56Sopenharmony_ci        exp_range = restricted_range
9677db96d56Sopenharmony_ci    for op in all_unary(prec, exp_range, itr):
9687db96d56Sopenharmony_ci        t = TestSet(method, op)
9697db96d56Sopenharmony_ci        try:
9707db96d56Sopenharmony_ci            if not convert(t):
9717db96d56Sopenharmony_ci                continue
9727db96d56Sopenharmony_ci            callfuncs(t)
9737db96d56Sopenharmony_ci            verify(t, stat)
9747db96d56Sopenharmony_ci        except VerifyError as err:
9757db96d56Sopenharmony_ci            log(err)
9767db96d56Sopenharmony_ci
9777db96d56Sopenharmony_ci    if not method.startswith('__'):
9787db96d56Sopenharmony_ci        for op in unary_optarg(prec, exp_range, itr):
9797db96d56Sopenharmony_ci            t = TestSet(method, op)
9807db96d56Sopenharmony_ci            try:
9817db96d56Sopenharmony_ci                if not convert(t):
9827db96d56Sopenharmony_ci                    continue
9837db96d56Sopenharmony_ci                callfuncs(t)
9847db96d56Sopenharmony_ci                verify(t, stat)
9857db96d56Sopenharmony_ci            except VerifyError as err:
9867db96d56Sopenharmony_ci                log(err)
9877db96d56Sopenharmony_ci
9887db96d56Sopenharmony_cidef test_binary(method, prec, exp_range, restricted_range, itr, stat):
9897db96d56Sopenharmony_ci    """Iterate a binary function through many test cases."""
9907db96d56Sopenharmony_ci    if method in BinaryRestricted:
9917db96d56Sopenharmony_ci        exp_range = restricted_range
9927db96d56Sopenharmony_ci    for op in all_binary(prec, exp_range, itr):
9937db96d56Sopenharmony_ci        t = TestSet(method, op)
9947db96d56Sopenharmony_ci        try:
9957db96d56Sopenharmony_ci            if not convert(t):
9967db96d56Sopenharmony_ci                continue
9977db96d56Sopenharmony_ci            callfuncs(t)
9987db96d56Sopenharmony_ci            verify(t, stat)
9997db96d56Sopenharmony_ci        except VerifyError as err:
10007db96d56Sopenharmony_ci            log(err)
10017db96d56Sopenharmony_ci
10027db96d56Sopenharmony_ci    if not method.startswith('__'):
10037db96d56Sopenharmony_ci        for op in binary_optarg(prec, exp_range, itr):
10047db96d56Sopenharmony_ci            t = TestSet(method, op)
10057db96d56Sopenharmony_ci            try:
10067db96d56Sopenharmony_ci                if not convert(t):
10077db96d56Sopenharmony_ci                    continue
10087db96d56Sopenharmony_ci                callfuncs(t)
10097db96d56Sopenharmony_ci                verify(t, stat)
10107db96d56Sopenharmony_ci            except VerifyError as err:
10117db96d56Sopenharmony_ci                log(err)
10127db96d56Sopenharmony_ci
10137db96d56Sopenharmony_cidef test_ternary(method, prec, exp_range, restricted_range, itr, stat):
10147db96d56Sopenharmony_ci    """Iterate a ternary function through many test cases."""
10157db96d56Sopenharmony_ci    if method in TernaryRestricted:
10167db96d56Sopenharmony_ci        exp_range = restricted_range
10177db96d56Sopenharmony_ci    for op in all_ternary(prec, exp_range, itr):
10187db96d56Sopenharmony_ci        t = TestSet(method, op)
10197db96d56Sopenharmony_ci        try:
10207db96d56Sopenharmony_ci            if not convert(t):
10217db96d56Sopenharmony_ci                continue
10227db96d56Sopenharmony_ci            callfuncs(t)
10237db96d56Sopenharmony_ci            verify(t, stat)
10247db96d56Sopenharmony_ci        except VerifyError as err:
10257db96d56Sopenharmony_ci            log(err)
10267db96d56Sopenharmony_ci
10277db96d56Sopenharmony_ci    if not method.startswith('__'):
10287db96d56Sopenharmony_ci        for op in ternary_optarg(prec, exp_range, itr):
10297db96d56Sopenharmony_ci            t = TestSet(method, op)
10307db96d56Sopenharmony_ci            try:
10317db96d56Sopenharmony_ci                if not convert(t):
10327db96d56Sopenharmony_ci                    continue
10337db96d56Sopenharmony_ci                callfuncs(t)
10347db96d56Sopenharmony_ci                verify(t, stat)
10357db96d56Sopenharmony_ci            except VerifyError as err:
10367db96d56Sopenharmony_ci                log(err)
10377db96d56Sopenharmony_ci
10387db96d56Sopenharmony_cidef test_format(method, prec, exp_range, restricted_range, itr, stat):
10397db96d56Sopenharmony_ci    """Iterate the __format__ method through many test cases."""
10407db96d56Sopenharmony_ci    for op in all_unary(prec, exp_range, itr):
10417db96d56Sopenharmony_ci        fmt1 = rand_format(chr(random.randrange(0, 128)), 'EeGgn')
10427db96d56Sopenharmony_ci        fmt2 = rand_locale()
10437db96d56Sopenharmony_ci        for fmt in (fmt1, fmt2):
10447db96d56Sopenharmony_ci            fmtop = (op[0], fmt)
10457db96d56Sopenharmony_ci            t = TestSet(method, fmtop)
10467db96d56Sopenharmony_ci            try:
10477db96d56Sopenharmony_ci                if not convert(t, convstr=False):
10487db96d56Sopenharmony_ci                    continue
10497db96d56Sopenharmony_ci                callfuncs(t)
10507db96d56Sopenharmony_ci                verify(t, stat)
10517db96d56Sopenharmony_ci            except VerifyError as err:
10527db96d56Sopenharmony_ci                log(err)
10537db96d56Sopenharmony_ci    for op in all_unary(prec, 9999, itr):
10547db96d56Sopenharmony_ci        fmt1 = rand_format(chr(random.randrange(0, 128)), 'Ff%')
10557db96d56Sopenharmony_ci        fmt2 = rand_locale()
10567db96d56Sopenharmony_ci        for fmt in (fmt1, fmt2):
10577db96d56Sopenharmony_ci            fmtop = (op[0], fmt)
10587db96d56Sopenharmony_ci            t = TestSet(method, fmtop)
10597db96d56Sopenharmony_ci            try:
10607db96d56Sopenharmony_ci                if not convert(t, convstr=False):
10617db96d56Sopenharmony_ci                    continue
10627db96d56Sopenharmony_ci                callfuncs(t)
10637db96d56Sopenharmony_ci                verify(t, stat)
10647db96d56Sopenharmony_ci            except VerifyError as err:
10657db96d56Sopenharmony_ci                log(err)
10667db96d56Sopenharmony_ci
10677db96d56Sopenharmony_cidef test_round(method, prec, exprange, restricted_range, itr, stat):
10687db96d56Sopenharmony_ci    """Iterate the __round__ method through many test cases."""
10697db96d56Sopenharmony_ci    for op in all_unary(prec, 9999, itr):
10707db96d56Sopenharmony_ci        n = random.randrange(10)
10717db96d56Sopenharmony_ci        roundop = (op[0], n)
10727db96d56Sopenharmony_ci        t = TestSet(method, roundop)
10737db96d56Sopenharmony_ci        try:
10747db96d56Sopenharmony_ci            if not convert(t):
10757db96d56Sopenharmony_ci                continue
10767db96d56Sopenharmony_ci            callfuncs(t)
10777db96d56Sopenharmony_ci            verify(t, stat)
10787db96d56Sopenharmony_ci        except VerifyError as err:
10797db96d56Sopenharmony_ci            log(err)
10807db96d56Sopenharmony_ci
10817db96d56Sopenharmony_cidef test_from_float(method, prec, exprange, restricted_range, itr, stat):
10827db96d56Sopenharmony_ci    """Iterate the __float__ method through many test cases."""
10837db96d56Sopenharmony_ci    for rounding in RoundModes:
10847db96d56Sopenharmony_ci        context.rounding = rounding
10857db96d56Sopenharmony_ci        for i in range(1000):
10867db96d56Sopenharmony_ci            f = randfloat()
10877db96d56Sopenharmony_ci            op = (f,) if method.startswith("context.") else ("sNaN", f)
10887db96d56Sopenharmony_ci            t = TestSet(method, op)
10897db96d56Sopenharmony_ci            try:
10907db96d56Sopenharmony_ci                if not convert(t):
10917db96d56Sopenharmony_ci                    continue
10927db96d56Sopenharmony_ci                callfuncs(t)
10937db96d56Sopenharmony_ci                verify(t, stat)
10947db96d56Sopenharmony_ci            except VerifyError as err:
10957db96d56Sopenharmony_ci                log(err)
10967db96d56Sopenharmony_ci
10977db96d56Sopenharmony_cidef randcontext(exprange):
10987db96d56Sopenharmony_ci    c = Context(C.Context(), P.Context())
10997db96d56Sopenharmony_ci    c.Emax = random.randrange(1, exprange+1)
11007db96d56Sopenharmony_ci    c.Emin = random.randrange(-exprange, 0)
11017db96d56Sopenharmony_ci    maxprec = 100 if c.Emax >= 100 else c.Emax
11027db96d56Sopenharmony_ci    c.prec = random.randrange(1, maxprec+1)
11037db96d56Sopenharmony_ci    c.clamp = random.randrange(2)
11047db96d56Sopenharmony_ci    c.clear_traps()
11057db96d56Sopenharmony_ci    return c
11067db96d56Sopenharmony_ci
11077db96d56Sopenharmony_cidef test_quantize_api(method, prec, exprange, restricted_range, itr, stat):
11087db96d56Sopenharmony_ci    """Iterate the 'quantize' method through many test cases, using
11097db96d56Sopenharmony_ci       the optional arguments."""
11107db96d56Sopenharmony_ci    for op in all_binary(prec, restricted_range, itr):
11117db96d56Sopenharmony_ci        for rounding in RoundModes:
11127db96d56Sopenharmony_ci            c = randcontext(exprange)
11137db96d56Sopenharmony_ci            quantizeop = (op[0], op[1], rounding, c)
11147db96d56Sopenharmony_ci            t = TestSet(method, quantizeop)
11157db96d56Sopenharmony_ci            try:
11167db96d56Sopenharmony_ci                if not convert(t):
11177db96d56Sopenharmony_ci                    continue
11187db96d56Sopenharmony_ci                callfuncs(t)
11197db96d56Sopenharmony_ci                verify(t, stat)
11207db96d56Sopenharmony_ci            except VerifyError as err:
11217db96d56Sopenharmony_ci                log(err)
11227db96d56Sopenharmony_ci
11237db96d56Sopenharmony_ci
11247db96d56Sopenharmony_cidef check_untested(funcdict, c_cls, p_cls):
11257db96d56Sopenharmony_ci    """Determine untested, C-only and Python-only attributes.
11267db96d56Sopenharmony_ci       Uncomment print lines for debugging."""
11277db96d56Sopenharmony_ci    c_attr = set(dir(c_cls))
11287db96d56Sopenharmony_ci    p_attr = set(dir(p_cls))
11297db96d56Sopenharmony_ci    intersect = c_attr & p_attr
11307db96d56Sopenharmony_ci
11317db96d56Sopenharmony_ci    funcdict['c_only'] = tuple(sorted(c_attr-intersect))
11327db96d56Sopenharmony_ci    funcdict['p_only'] = tuple(sorted(p_attr-intersect))
11337db96d56Sopenharmony_ci
11347db96d56Sopenharmony_ci    tested = set()
11357db96d56Sopenharmony_ci    for lst in funcdict.values():
11367db96d56Sopenharmony_ci        for v in lst:
11377db96d56Sopenharmony_ci            v = v.replace("context.", "") if c_cls == C.Context else v
11387db96d56Sopenharmony_ci            tested.add(v)
11397db96d56Sopenharmony_ci
11407db96d56Sopenharmony_ci    funcdict['untested'] = tuple(sorted(intersect-tested))
11417db96d56Sopenharmony_ci
11427db96d56Sopenharmony_ci    # for key in ('untested', 'c_only', 'p_only'):
11437db96d56Sopenharmony_ci    #     s = 'Context' if c_cls == C.Context else 'Decimal'
11447db96d56Sopenharmony_ci    #     print("\n%s %s:\n%s" % (s, key, funcdict[key]))
11457db96d56Sopenharmony_ci
11467db96d56Sopenharmony_ci
11477db96d56Sopenharmony_ciif __name__ == '__main__':
11487db96d56Sopenharmony_ci
11497db96d56Sopenharmony_ci    parser = argparse.ArgumentParser(prog="deccheck.py")
11507db96d56Sopenharmony_ci
11517db96d56Sopenharmony_ci    group = parser.add_mutually_exclusive_group()
11527db96d56Sopenharmony_ci    group.add_argument('--short', dest='time', action="store_const", const='short', default='short', help="short test (default)")
11537db96d56Sopenharmony_ci    group.add_argument('--medium', dest='time', action="store_const", const='medium', default='short', help="medium test (reasonable run time)")
11547db96d56Sopenharmony_ci    group.add_argument('--long', dest='time', action="store_const", const='long', default='short', help="long test (long run time)")
11557db96d56Sopenharmony_ci    group.add_argument('--all', dest='time', action="store_const", const='all', default='short', help="all tests (excessive run time)")
11567db96d56Sopenharmony_ci
11577db96d56Sopenharmony_ci    group = parser.add_mutually_exclusive_group()
11587db96d56Sopenharmony_ci    group.add_argument('--single', dest='single', nargs=1, default=False, metavar="TEST", help="run a single test")
11597db96d56Sopenharmony_ci    group.add_argument('--multicore', dest='multicore', action="store_true", default=False, help="use all available cores")
11607db96d56Sopenharmony_ci
11617db96d56Sopenharmony_ci    args = parser.parse_args()
11627db96d56Sopenharmony_ci    assert args.single is False or args.multicore is False
11637db96d56Sopenharmony_ci    if args.single:
11647db96d56Sopenharmony_ci        args.single = args.single[0]
11657db96d56Sopenharmony_ci
11667db96d56Sopenharmony_ci
11677db96d56Sopenharmony_ci    # Set up the testspecs list. A testspec is simply a dictionary
11687db96d56Sopenharmony_ci    # that determines the amount of different contexts that 'test_method'
11697db96d56Sopenharmony_ci    # will generate.
11707db96d56Sopenharmony_ci    base_expts = [(C.MIN_EMIN, C.MAX_EMAX)]
11717db96d56Sopenharmony_ci    if C.MAX_EMAX == 999999999999999999:
11727db96d56Sopenharmony_ci        base_expts.append((-999999999, 999999999))
11737db96d56Sopenharmony_ci
11747db96d56Sopenharmony_ci    # Basic contexts.
11757db96d56Sopenharmony_ci    base = {
11767db96d56Sopenharmony_ci        'expts': base_expts,
11777db96d56Sopenharmony_ci        'prec': [],
11787db96d56Sopenharmony_ci        'clamp': 'rand',
11797db96d56Sopenharmony_ci        'iter': None,
11807db96d56Sopenharmony_ci        'samples': None,
11817db96d56Sopenharmony_ci    }
11827db96d56Sopenharmony_ci    # Contexts with small values for prec, emin, emax.
11837db96d56Sopenharmony_ci    small = {
11847db96d56Sopenharmony_ci        'prec': [1, 2, 3, 4, 5],
11857db96d56Sopenharmony_ci        'expts': [(-1, 1), (-2, 2), (-3, 3), (-4, 4), (-5, 5)],
11867db96d56Sopenharmony_ci        'clamp': 'rand',
11877db96d56Sopenharmony_ci        'iter': None
11887db96d56Sopenharmony_ci    }
11897db96d56Sopenharmony_ci    # IEEE interchange format.
11907db96d56Sopenharmony_ci    ieee = [
11917db96d56Sopenharmony_ci        # DECIMAL32
11927db96d56Sopenharmony_ci        {'prec': [7], 'expts': [(-95, 96)], 'clamp': 1, 'iter': None},
11937db96d56Sopenharmony_ci        # DECIMAL64
11947db96d56Sopenharmony_ci        {'prec': [16], 'expts': [(-383, 384)], 'clamp': 1, 'iter': None},
11957db96d56Sopenharmony_ci        # DECIMAL128
11967db96d56Sopenharmony_ci        {'prec': [34], 'expts': [(-6143, 6144)], 'clamp': 1, 'iter': None}
11977db96d56Sopenharmony_ci    ]
11987db96d56Sopenharmony_ci
11997db96d56Sopenharmony_ci    if args.time == 'medium':
12007db96d56Sopenharmony_ci        base['expts'].append(('rand', 'rand'))
12017db96d56Sopenharmony_ci        # 5 random precisions
12027db96d56Sopenharmony_ci        base['samples'] = 5
12037db96d56Sopenharmony_ci        testspecs = [small] + ieee + [base]
12047db96d56Sopenharmony_ci    elif args.time == 'long':
12057db96d56Sopenharmony_ci        base['expts'].append(('rand', 'rand'))
12067db96d56Sopenharmony_ci        # 10 random precisions
12077db96d56Sopenharmony_ci        base['samples'] = 10
12087db96d56Sopenharmony_ci        testspecs = [small] + ieee + [base]
12097db96d56Sopenharmony_ci    elif args.time == 'all':
12107db96d56Sopenharmony_ci        base['expts'].append(('rand', 'rand'))
12117db96d56Sopenharmony_ci        # All precisions in [1, 100]
12127db96d56Sopenharmony_ci        base['samples'] = 100
12137db96d56Sopenharmony_ci        testspecs = [small] + ieee + [base]
12147db96d56Sopenharmony_ci    else: # --short
12157db96d56Sopenharmony_ci        rand_ieee = random.choice(ieee)
12167db96d56Sopenharmony_ci        base['iter'] = small['iter'] = rand_ieee['iter'] = 1
12177db96d56Sopenharmony_ci        # 1 random precision and exponent pair
12187db96d56Sopenharmony_ci        base['samples'] = 1
12197db96d56Sopenharmony_ci        base['expts'] = [random.choice(base_expts)]
12207db96d56Sopenharmony_ci        # 1 random precision and exponent pair
12217db96d56Sopenharmony_ci        prec = random.randrange(1, 6)
12227db96d56Sopenharmony_ci        small['prec'] = [prec]
12237db96d56Sopenharmony_ci        small['expts'] = [(-prec, prec)]
12247db96d56Sopenharmony_ci        testspecs = [small, rand_ieee, base]
12257db96d56Sopenharmony_ci
12267db96d56Sopenharmony_ci
12277db96d56Sopenharmony_ci    check_untested(Functions, C.Decimal, P.Decimal)
12287db96d56Sopenharmony_ci    check_untested(ContextFunctions, C.Context, P.Context)
12297db96d56Sopenharmony_ci
12307db96d56Sopenharmony_ci
12317db96d56Sopenharmony_ci    if args.multicore:
12327db96d56Sopenharmony_ci        q = Queue()
12337db96d56Sopenharmony_ci    elif args.single:
12347db96d56Sopenharmony_ci        log("Random seed: %d", RANDSEED)
12357db96d56Sopenharmony_ci    else:
12367db96d56Sopenharmony_ci        log("\n\nRandom seed: %d\n\n", RANDSEED)
12377db96d56Sopenharmony_ci
12387db96d56Sopenharmony_ci
12397db96d56Sopenharmony_ci    FOUND_METHOD = False
12407db96d56Sopenharmony_ci    def do_single(method, f):
12417db96d56Sopenharmony_ci        global FOUND_METHOD
12427db96d56Sopenharmony_ci        if args.multicore:
12437db96d56Sopenharmony_ci            q.put(method)
12447db96d56Sopenharmony_ci        elif not args.single or args.single == method:
12457db96d56Sopenharmony_ci            FOUND_METHOD = True
12467db96d56Sopenharmony_ci            f()
12477db96d56Sopenharmony_ci
12487db96d56Sopenharmony_ci    # Decimal methods:
12497db96d56Sopenharmony_ci    for method in Functions['unary'] + Functions['unary_ctx'] + \
12507db96d56Sopenharmony_ci                  Functions['unary_rnd_ctx']:
12517db96d56Sopenharmony_ci        do_single(method, lambda: test_method(method, testspecs, test_unary))
12527db96d56Sopenharmony_ci
12537db96d56Sopenharmony_ci    for method in Functions['binary'] + Functions['binary_ctx']:
12547db96d56Sopenharmony_ci        do_single(method, lambda: test_method(method, testspecs, test_binary))
12557db96d56Sopenharmony_ci
12567db96d56Sopenharmony_ci    for method in Functions['ternary'] + Functions['ternary_ctx']:
12577db96d56Sopenharmony_ci        name = '__powmod__' if method == '__pow__' else method
12587db96d56Sopenharmony_ci        do_single(name, lambda: test_method(method, testspecs, test_ternary))
12597db96d56Sopenharmony_ci
12607db96d56Sopenharmony_ci    do_single('__format__', lambda: test_method('__format__', testspecs, test_format))
12617db96d56Sopenharmony_ci    do_single('__round__', lambda: test_method('__round__', testspecs, test_round))
12627db96d56Sopenharmony_ci    do_single('from_float', lambda: test_method('from_float', testspecs, test_from_float))
12637db96d56Sopenharmony_ci    do_single('quantize_api', lambda: test_method('quantize', testspecs, test_quantize_api))
12647db96d56Sopenharmony_ci
12657db96d56Sopenharmony_ci    # Context methods:
12667db96d56Sopenharmony_ci    for method in ContextFunctions['unary']:
12677db96d56Sopenharmony_ci        do_single(method, lambda: test_method(method, testspecs, test_unary))
12687db96d56Sopenharmony_ci
12697db96d56Sopenharmony_ci    for method in ContextFunctions['binary']:
12707db96d56Sopenharmony_ci        do_single(method, lambda: test_method(method, testspecs, test_binary))
12717db96d56Sopenharmony_ci
12727db96d56Sopenharmony_ci    for method in ContextFunctions['ternary']:
12737db96d56Sopenharmony_ci        name = 'context.powmod' if method == 'context.power' else method
12747db96d56Sopenharmony_ci        do_single(name, lambda: test_method(method, testspecs, test_ternary))
12757db96d56Sopenharmony_ci
12767db96d56Sopenharmony_ci    do_single('context.create_decimal_from_float',
12777db96d56Sopenharmony_ci              lambda: test_method('context.create_decimal_from_float',
12787db96d56Sopenharmony_ci                                   testspecs, test_from_float))
12797db96d56Sopenharmony_ci
12807db96d56Sopenharmony_ci    if args.multicore:
12817db96d56Sopenharmony_ci        error = Event()
12827db96d56Sopenharmony_ci        write_lock = Lock()
12837db96d56Sopenharmony_ci
12847db96d56Sopenharmony_ci        def write_output(out, returncode):
12857db96d56Sopenharmony_ci            if returncode != 0:
12867db96d56Sopenharmony_ci                error.set()
12877db96d56Sopenharmony_ci
12887db96d56Sopenharmony_ci            with write_lock:
12897db96d56Sopenharmony_ci                sys.stdout.buffer.write(out + b"\n")
12907db96d56Sopenharmony_ci                sys.stdout.buffer.flush()
12917db96d56Sopenharmony_ci
12927db96d56Sopenharmony_ci        def tfunc():
12937db96d56Sopenharmony_ci            while not error.is_set():
12947db96d56Sopenharmony_ci                try:
12957db96d56Sopenharmony_ci                    test = q.get(block=False, timeout=-1)
12967db96d56Sopenharmony_ci                except Empty:
12977db96d56Sopenharmony_ci                    return
12987db96d56Sopenharmony_ci
12997db96d56Sopenharmony_ci                cmd = [sys.executable, "deccheck.py", "--%s" % args.time, "--single", test]
13007db96d56Sopenharmony_ci                p = subprocess.Popen(cmd, stdout=PIPE, stderr=STDOUT)
13017db96d56Sopenharmony_ci                out, _ = p.communicate()
13027db96d56Sopenharmony_ci                write_output(out, p.returncode)
13037db96d56Sopenharmony_ci
13047db96d56Sopenharmony_ci        N = os.cpu_count()
13057db96d56Sopenharmony_ci        t = N * [None]
13067db96d56Sopenharmony_ci
13077db96d56Sopenharmony_ci        for i in range(N):
13087db96d56Sopenharmony_ci            t[i] = Thread(target=tfunc)
13097db96d56Sopenharmony_ci            t[i].start()
13107db96d56Sopenharmony_ci
13117db96d56Sopenharmony_ci        for i in range(N):
13127db96d56Sopenharmony_ci            t[i].join()
13137db96d56Sopenharmony_ci
13147db96d56Sopenharmony_ci        sys.exit(1 if error.is_set() else 0)
13157db96d56Sopenharmony_ci
13167db96d56Sopenharmony_ci    elif args.single:
13177db96d56Sopenharmony_ci        if not FOUND_METHOD:
13187db96d56Sopenharmony_ci            log("\nerror: cannot find method \"%s\"" % args.single)
13197db96d56Sopenharmony_ci            EXIT_STATUS = 1
13207db96d56Sopenharmony_ci        sys.exit(EXIT_STATUS)
13217db96d56Sopenharmony_ci    else:
13227db96d56Sopenharmony_ci        sys.exit(EXIT_STATUS)
1323