xref: /aosp_15_r20/external/mbedtls/scripts/mbedtls_dev/bignum_common.py (revision 62c56f9862f102b96d72393aff6076c951fb8148)
1*62c56f98SSadaf Ebrahimi"""Common features for bignum in test generation framework."""
2*62c56f98SSadaf Ebrahimi# Copyright The Mbed TLS Contributors
3*62c56f98SSadaf Ebrahimi# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
4*62c56f98SSadaf Ebrahimi#
5*62c56f98SSadaf Ebrahimi
6*62c56f98SSadaf Ebrahimifrom abc import abstractmethod
7*62c56f98SSadaf Ebrahimiimport enum
8*62c56f98SSadaf Ebrahimifrom typing import Iterator, List, Tuple, TypeVar, Any
9*62c56f98SSadaf Ebrahimifrom copy import deepcopy
10*62c56f98SSadaf Ebrahimifrom itertools import chain
11*62c56f98SSadaf Ebrahimifrom math import ceil
12*62c56f98SSadaf Ebrahimi
13*62c56f98SSadaf Ebrahimifrom . import test_case
14*62c56f98SSadaf Ebrahimifrom . import test_data_generation
15*62c56f98SSadaf Ebrahimifrom .bignum_data import INPUTS_DEFAULT, MODULI_DEFAULT
16*62c56f98SSadaf Ebrahimi
17*62c56f98SSadaf EbrahimiT = TypeVar('T') #pylint: disable=invalid-name
18*62c56f98SSadaf Ebrahimi
19*62c56f98SSadaf Ebrahimidef invmod(a: int, n: int) -> int:
20*62c56f98SSadaf Ebrahimi    """Return inverse of a to modulo n.
21*62c56f98SSadaf Ebrahimi
22*62c56f98SSadaf Ebrahimi    Equivalent to pow(a, -1, n) in Python 3.8+. Implementation is equivalent
23*62c56f98SSadaf Ebrahimi    to long_invmod() in CPython.
24*62c56f98SSadaf Ebrahimi    """
25*62c56f98SSadaf Ebrahimi    b, c = 1, 0
26*62c56f98SSadaf Ebrahimi    while n:
27*62c56f98SSadaf Ebrahimi        q, r = divmod(a, n)
28*62c56f98SSadaf Ebrahimi        a, b, c, n = n, c, b - q*c, r
29*62c56f98SSadaf Ebrahimi    # at this point a is the gcd of the original inputs
30*62c56f98SSadaf Ebrahimi    if a == 1:
31*62c56f98SSadaf Ebrahimi        return b
32*62c56f98SSadaf Ebrahimi    raise ValueError("Not invertible")
33*62c56f98SSadaf Ebrahimi
34*62c56f98SSadaf Ebrahimidef invmod_positive(a: int, n: int) -> int:
35*62c56f98SSadaf Ebrahimi    """Return a non-negative inverse of a to modulo n."""
36*62c56f98SSadaf Ebrahimi    inv = invmod(a, n)
37*62c56f98SSadaf Ebrahimi    return inv if inv >= 0 else inv + n
38*62c56f98SSadaf Ebrahimi
39*62c56f98SSadaf Ebrahimidef hex_to_int(val: str) -> int:
40*62c56f98SSadaf Ebrahimi    """Implement the syntax accepted by mbedtls_test_read_mpi().
41*62c56f98SSadaf Ebrahimi
42*62c56f98SSadaf Ebrahimi    This is a superset of what is accepted by mbedtls_test_read_mpi_core().
43*62c56f98SSadaf Ebrahimi    """
44*62c56f98SSadaf Ebrahimi    if val in ['', '-']:
45*62c56f98SSadaf Ebrahimi        return 0
46*62c56f98SSadaf Ebrahimi    return int(val, 16)
47*62c56f98SSadaf Ebrahimi
48*62c56f98SSadaf Ebrahimidef quote_str(val: str) -> str:
49*62c56f98SSadaf Ebrahimi    return "\"{}\"".format(val)
50*62c56f98SSadaf Ebrahimi
51*62c56f98SSadaf Ebrahimidef bound_mpi(val: int, bits_in_limb: int) -> int:
52*62c56f98SSadaf Ebrahimi    """First number exceeding number of limbs needed for given input value."""
53*62c56f98SSadaf Ebrahimi    return bound_mpi_limbs(limbs_mpi(val, bits_in_limb), bits_in_limb)
54*62c56f98SSadaf Ebrahimi
55*62c56f98SSadaf Ebrahimidef bound_mpi_limbs(limbs: int, bits_in_limb: int) -> int:
56*62c56f98SSadaf Ebrahimi    """First number exceeding maximum of given number of limbs."""
57*62c56f98SSadaf Ebrahimi    bits = bits_in_limb * limbs
58*62c56f98SSadaf Ebrahimi    return 1 << bits
59*62c56f98SSadaf Ebrahimi
60*62c56f98SSadaf Ebrahimidef limbs_mpi(val: int, bits_in_limb: int) -> int:
61*62c56f98SSadaf Ebrahimi    """Return the number of limbs required to store value."""
62*62c56f98SSadaf Ebrahimi    bit_length = max(val.bit_length(), 1)
63*62c56f98SSadaf Ebrahimi    return (bit_length + bits_in_limb - 1) // bits_in_limb
64*62c56f98SSadaf Ebrahimi
65*62c56f98SSadaf Ebrahimidef combination_pairs(values: List[T]) -> List[Tuple[T, T]]:
66*62c56f98SSadaf Ebrahimi    """Return all pair combinations from input values."""
67*62c56f98SSadaf Ebrahimi    return [(x, y) for x in values for y in values]
68*62c56f98SSadaf Ebrahimi
69*62c56f98SSadaf Ebrahimidef bits_to_limbs(bits: int, bits_in_limb: int) -> int:
70*62c56f98SSadaf Ebrahimi    """ Return the appropriate ammount of limbs needed to store
71*62c56f98SSadaf Ebrahimi        a number contained in input bits"""
72*62c56f98SSadaf Ebrahimi    return ceil(bits / bits_in_limb)
73*62c56f98SSadaf Ebrahimi
74*62c56f98SSadaf Ebrahimidef hex_digits_for_limb(limbs: int, bits_in_limb: int) -> int:
75*62c56f98SSadaf Ebrahimi    """ Return the hex digits need for a number of limbs. """
76*62c56f98SSadaf Ebrahimi    return 2 * ((limbs * bits_in_limb) // 8)
77*62c56f98SSadaf Ebrahimi
78*62c56f98SSadaf Ebrahimidef hex_digits_max_int(val: str, bits_in_limb: int) -> int:
79*62c56f98SSadaf Ebrahimi    """ Return the first number exceeding maximum  the limb space
80*62c56f98SSadaf Ebrahimi    required to store the input hex-string value. This method
81*62c56f98SSadaf Ebrahimi    weights on the input str_len rather than numerical value
82*62c56f98SSadaf Ebrahimi    and works with zero-padded inputs"""
83*62c56f98SSadaf Ebrahimi    n = ((1 << (len(val) * 4)) - 1)
84*62c56f98SSadaf Ebrahimi    l = limbs_mpi(n, bits_in_limb)
85*62c56f98SSadaf Ebrahimi    return bound_mpi_limbs(l, bits_in_limb)
86*62c56f98SSadaf Ebrahimi
87*62c56f98SSadaf Ebrahimidef zfill_match(reference: str, target: str) -> str:
88*62c56f98SSadaf Ebrahimi    """ Zero pad target hex-string to match the limb size of
89*62c56f98SSadaf Ebrahimi    the reference input """
90*62c56f98SSadaf Ebrahimi    lt = len(target)
91*62c56f98SSadaf Ebrahimi    lr = len(reference)
92*62c56f98SSadaf Ebrahimi    target_len = lr if lt < lr else lt
93*62c56f98SSadaf Ebrahimi    return "{:x}".format(int(target, 16)).zfill(target_len)
94*62c56f98SSadaf Ebrahimi
95*62c56f98SSadaf Ebrahimiclass OperationCommon(test_data_generation.BaseTest):
96*62c56f98SSadaf Ebrahimi    """Common features for bignum binary operations.
97*62c56f98SSadaf Ebrahimi
98*62c56f98SSadaf Ebrahimi    This adds functionality common in binary operation tests.
99*62c56f98SSadaf Ebrahimi
100*62c56f98SSadaf Ebrahimi    Attributes:
101*62c56f98SSadaf Ebrahimi        symbol: Symbol to use for the operation in case description.
102*62c56f98SSadaf Ebrahimi        input_values: List of values to use as test case inputs. These are
103*62c56f98SSadaf Ebrahimi            combined to produce pairs of values.
104*62c56f98SSadaf Ebrahimi        input_cases: List of tuples containing pairs of test case inputs. This
105*62c56f98SSadaf Ebrahimi            can be used to implement specific pairs of inputs.
106*62c56f98SSadaf Ebrahimi        unique_combinations_only: Boolean to select if test case combinations
107*62c56f98SSadaf Ebrahimi            must be unique. If True, only A,B or B,A would be included as a test
108*62c56f98SSadaf Ebrahimi            case. If False, both A,B and B,A would be included.
109*62c56f98SSadaf Ebrahimi        input_style: Controls the way how test data is passed to the functions
110*62c56f98SSadaf Ebrahimi            in the generated test cases. "variable" passes them as they are
111*62c56f98SSadaf Ebrahimi            defined in the python source. "arch_split" pads the values with
112*62c56f98SSadaf Ebrahimi            zeroes depending on the architecture/limb size. If this is set,
113*62c56f98SSadaf Ebrahimi            test cases are generated for all architectures.
114*62c56f98SSadaf Ebrahimi        arity: the number of operands for the operation. Currently supported
115*62c56f98SSadaf Ebrahimi            values are 1 and 2.
116*62c56f98SSadaf Ebrahimi    """
117*62c56f98SSadaf Ebrahimi    symbol = ""
118*62c56f98SSadaf Ebrahimi    input_values = INPUTS_DEFAULT # type: List[str]
119*62c56f98SSadaf Ebrahimi    input_cases = [] # type: List[Any]
120*62c56f98SSadaf Ebrahimi    dependencies = [] # type: List[Any]
121*62c56f98SSadaf Ebrahimi    unique_combinations_only = False
122*62c56f98SSadaf Ebrahimi    input_styles = ["variable", "fixed", "arch_split"] # type: List[str]
123*62c56f98SSadaf Ebrahimi    input_style = "variable" # type: str
124*62c56f98SSadaf Ebrahimi    limb_sizes = [32, 64] # type: List[int]
125*62c56f98SSadaf Ebrahimi    arities = [1, 2]
126*62c56f98SSadaf Ebrahimi    arity = 2
127*62c56f98SSadaf Ebrahimi    suffix = False   # for arity = 1, symbol can be prefix (default) or suffix
128*62c56f98SSadaf Ebrahimi
129*62c56f98SSadaf Ebrahimi    def __init__(self, val_a: str, val_b: str = "0", bits_in_limb: int = 32) -> None:
130*62c56f98SSadaf Ebrahimi        self.val_a = val_a
131*62c56f98SSadaf Ebrahimi        self.val_b = val_b
132*62c56f98SSadaf Ebrahimi        # Setting the int versions here as opposed to making them @properties
133*62c56f98SSadaf Ebrahimi        # provides earlier/more robust input validation.
134*62c56f98SSadaf Ebrahimi        self.int_a = hex_to_int(val_a)
135*62c56f98SSadaf Ebrahimi        self.int_b = hex_to_int(val_b)
136*62c56f98SSadaf Ebrahimi        self.dependencies = deepcopy(self.dependencies)
137*62c56f98SSadaf Ebrahimi        if bits_in_limb not in self.limb_sizes:
138*62c56f98SSadaf Ebrahimi            raise ValueError("Invalid number of bits in limb!")
139*62c56f98SSadaf Ebrahimi        if self.input_style == "arch_split":
140*62c56f98SSadaf Ebrahimi            self.dependencies.append("MBEDTLS_HAVE_INT{:d}".format(bits_in_limb))
141*62c56f98SSadaf Ebrahimi        self.bits_in_limb = bits_in_limb
142*62c56f98SSadaf Ebrahimi
143*62c56f98SSadaf Ebrahimi    @property
144*62c56f98SSadaf Ebrahimi    def boundary(self) -> int:
145*62c56f98SSadaf Ebrahimi        if self.arity == 1:
146*62c56f98SSadaf Ebrahimi            return self.int_a
147*62c56f98SSadaf Ebrahimi        elif self.arity == 2:
148*62c56f98SSadaf Ebrahimi            return max(self.int_a, self.int_b)
149*62c56f98SSadaf Ebrahimi        raise ValueError("Unsupported number of operands!")
150*62c56f98SSadaf Ebrahimi
151*62c56f98SSadaf Ebrahimi    @property
152*62c56f98SSadaf Ebrahimi    def limb_boundary(self) -> int:
153*62c56f98SSadaf Ebrahimi        return bound_mpi(self.boundary, self.bits_in_limb)
154*62c56f98SSadaf Ebrahimi
155*62c56f98SSadaf Ebrahimi    @property
156*62c56f98SSadaf Ebrahimi    def limbs(self) -> int:
157*62c56f98SSadaf Ebrahimi        return limbs_mpi(self.boundary, self.bits_in_limb)
158*62c56f98SSadaf Ebrahimi
159*62c56f98SSadaf Ebrahimi    @property
160*62c56f98SSadaf Ebrahimi    def hex_digits(self) -> int:
161*62c56f98SSadaf Ebrahimi        return hex_digits_for_limb(self.limbs, self.bits_in_limb)
162*62c56f98SSadaf Ebrahimi
163*62c56f98SSadaf Ebrahimi    def format_arg(self, val: str) -> str:
164*62c56f98SSadaf Ebrahimi        if self.input_style not in self.input_styles:
165*62c56f98SSadaf Ebrahimi            raise ValueError("Unknown input style!")
166*62c56f98SSadaf Ebrahimi        if self.input_style == "variable":
167*62c56f98SSadaf Ebrahimi            return val
168*62c56f98SSadaf Ebrahimi        else:
169*62c56f98SSadaf Ebrahimi            return val.zfill(self.hex_digits)
170*62c56f98SSadaf Ebrahimi
171*62c56f98SSadaf Ebrahimi    def format_result(self, res: int) -> str:
172*62c56f98SSadaf Ebrahimi        res_str = '{:x}'.format(res)
173*62c56f98SSadaf Ebrahimi        return quote_str(self.format_arg(res_str))
174*62c56f98SSadaf Ebrahimi
175*62c56f98SSadaf Ebrahimi    @property
176*62c56f98SSadaf Ebrahimi    def arg_a(self) -> str:
177*62c56f98SSadaf Ebrahimi        return self.format_arg(self.val_a)
178*62c56f98SSadaf Ebrahimi
179*62c56f98SSadaf Ebrahimi    @property
180*62c56f98SSadaf Ebrahimi    def arg_b(self) -> str:
181*62c56f98SSadaf Ebrahimi        if self.arity == 1:
182*62c56f98SSadaf Ebrahimi            raise AttributeError("Operation is unary and doesn't have arg_b!")
183*62c56f98SSadaf Ebrahimi        return self.format_arg(self.val_b)
184*62c56f98SSadaf Ebrahimi
185*62c56f98SSadaf Ebrahimi    def arguments(self) -> List[str]:
186*62c56f98SSadaf Ebrahimi        args = [quote_str(self.arg_a)]
187*62c56f98SSadaf Ebrahimi        if self.arity == 2:
188*62c56f98SSadaf Ebrahimi            args.append(quote_str(self.arg_b))
189*62c56f98SSadaf Ebrahimi        return args + self.result()
190*62c56f98SSadaf Ebrahimi
191*62c56f98SSadaf Ebrahimi    def description(self) -> str:
192*62c56f98SSadaf Ebrahimi        """Generate a description for the test case.
193*62c56f98SSadaf Ebrahimi
194*62c56f98SSadaf Ebrahimi        If not set, case_description uses the form A `symbol` B, where symbol
195*62c56f98SSadaf Ebrahimi        is used to represent the operation. Descriptions of each value are
196*62c56f98SSadaf Ebrahimi        generated to provide some context to the test case.
197*62c56f98SSadaf Ebrahimi        """
198*62c56f98SSadaf Ebrahimi        if not self.case_description:
199*62c56f98SSadaf Ebrahimi            if self.arity == 1:
200*62c56f98SSadaf Ebrahimi                format_string = "{1:x} {0}" if self.suffix else "{0} {1:x}"
201*62c56f98SSadaf Ebrahimi                self.case_description = format_string.format(
202*62c56f98SSadaf Ebrahimi                    self.symbol, self.int_a
203*62c56f98SSadaf Ebrahimi                )
204*62c56f98SSadaf Ebrahimi            elif self.arity == 2:
205*62c56f98SSadaf Ebrahimi                self.case_description = "{:x} {} {:x}".format(
206*62c56f98SSadaf Ebrahimi                    self.int_a, self.symbol, self.int_b
207*62c56f98SSadaf Ebrahimi                )
208*62c56f98SSadaf Ebrahimi        return super().description()
209*62c56f98SSadaf Ebrahimi
210*62c56f98SSadaf Ebrahimi    @property
211*62c56f98SSadaf Ebrahimi    def is_valid(self) -> bool:
212*62c56f98SSadaf Ebrahimi        return True
213*62c56f98SSadaf Ebrahimi
214*62c56f98SSadaf Ebrahimi    @abstractmethod
215*62c56f98SSadaf Ebrahimi    def result(self) -> List[str]:
216*62c56f98SSadaf Ebrahimi        """Get the result of the operation.
217*62c56f98SSadaf Ebrahimi
218*62c56f98SSadaf Ebrahimi        This could be calculated during initialization and stored as `_result`
219*62c56f98SSadaf Ebrahimi        and then returned, or calculated when the method is called.
220*62c56f98SSadaf Ebrahimi        """
221*62c56f98SSadaf Ebrahimi        raise NotImplementedError
222*62c56f98SSadaf Ebrahimi
223*62c56f98SSadaf Ebrahimi    @classmethod
224*62c56f98SSadaf Ebrahimi    def get_value_pairs(cls) -> Iterator[Tuple[str, str]]:
225*62c56f98SSadaf Ebrahimi        """Generator to yield pairs of inputs.
226*62c56f98SSadaf Ebrahimi
227*62c56f98SSadaf Ebrahimi        Combinations are first generated from all input values, and then
228*62c56f98SSadaf Ebrahimi        specific cases provided.
229*62c56f98SSadaf Ebrahimi        """
230*62c56f98SSadaf Ebrahimi        if cls.arity == 1:
231*62c56f98SSadaf Ebrahimi            yield from ((a, "0") for a in cls.input_values)
232*62c56f98SSadaf Ebrahimi        elif cls.arity == 2:
233*62c56f98SSadaf Ebrahimi            if cls.unique_combinations_only:
234*62c56f98SSadaf Ebrahimi                yield from combination_pairs(cls.input_values)
235*62c56f98SSadaf Ebrahimi            else:
236*62c56f98SSadaf Ebrahimi                yield from (
237*62c56f98SSadaf Ebrahimi                    (a, b)
238*62c56f98SSadaf Ebrahimi                    for a in cls.input_values
239*62c56f98SSadaf Ebrahimi                    for b in cls.input_values
240*62c56f98SSadaf Ebrahimi                )
241*62c56f98SSadaf Ebrahimi        else:
242*62c56f98SSadaf Ebrahimi            raise ValueError("Unsupported number of operands!")
243*62c56f98SSadaf Ebrahimi
244*62c56f98SSadaf Ebrahimi    @classmethod
245*62c56f98SSadaf Ebrahimi    def generate_function_tests(cls) -> Iterator[test_case.TestCase]:
246*62c56f98SSadaf Ebrahimi        if cls.input_style not in cls.input_styles:
247*62c56f98SSadaf Ebrahimi            raise ValueError("Unknown input style!")
248*62c56f98SSadaf Ebrahimi        if cls.arity not in cls.arities:
249*62c56f98SSadaf Ebrahimi            raise ValueError("Unsupported number of operands!")
250*62c56f98SSadaf Ebrahimi        if cls.input_style == "arch_split":
251*62c56f98SSadaf Ebrahimi            test_objects = (cls(a, b, bits_in_limb=bil)
252*62c56f98SSadaf Ebrahimi                            for a, b in cls.get_value_pairs()
253*62c56f98SSadaf Ebrahimi                            for bil in cls.limb_sizes)
254*62c56f98SSadaf Ebrahimi            special_cases = (cls(*args, bits_in_limb=bil) # type: ignore
255*62c56f98SSadaf Ebrahimi                             for args in cls.input_cases
256*62c56f98SSadaf Ebrahimi                             for bil in cls.limb_sizes)
257*62c56f98SSadaf Ebrahimi        else:
258*62c56f98SSadaf Ebrahimi            test_objects = (cls(a, b)
259*62c56f98SSadaf Ebrahimi                            for a, b in cls.get_value_pairs())
260*62c56f98SSadaf Ebrahimi            special_cases = (cls(*args) for args in cls.input_cases)
261*62c56f98SSadaf Ebrahimi        yield from (valid_test_object.create_test_case()
262*62c56f98SSadaf Ebrahimi                    for valid_test_object in filter(
263*62c56f98SSadaf Ebrahimi                        lambda test_object: test_object.is_valid,
264*62c56f98SSadaf Ebrahimi                        chain(test_objects, special_cases)
265*62c56f98SSadaf Ebrahimi                        )
266*62c56f98SSadaf Ebrahimi                    )
267*62c56f98SSadaf Ebrahimi
268*62c56f98SSadaf Ebrahimi
269*62c56f98SSadaf Ebrahimiclass ModulusRepresentation(enum.Enum):
270*62c56f98SSadaf Ebrahimi    """Representation selector of a modulus."""
271*62c56f98SSadaf Ebrahimi    # Numerical values aligned with the type mbedtls_mpi_mod_rep_selector
272*62c56f98SSadaf Ebrahimi    INVALID = 0
273*62c56f98SSadaf Ebrahimi    MONTGOMERY = 2
274*62c56f98SSadaf Ebrahimi    OPT_RED = 3
275*62c56f98SSadaf Ebrahimi
276*62c56f98SSadaf Ebrahimi    def symbol(self) -> str:
277*62c56f98SSadaf Ebrahimi        """The C symbol for this representation selector."""
278*62c56f98SSadaf Ebrahimi        return 'MBEDTLS_MPI_MOD_REP_' + self.name
279*62c56f98SSadaf Ebrahimi
280*62c56f98SSadaf Ebrahimi    @classmethod
281*62c56f98SSadaf Ebrahimi    def supported_representations(cls) -> List['ModulusRepresentation']:
282*62c56f98SSadaf Ebrahimi        """Return all representations that are supported in positive test cases."""
283*62c56f98SSadaf Ebrahimi        return [cls.MONTGOMERY, cls.OPT_RED]
284*62c56f98SSadaf Ebrahimi
285*62c56f98SSadaf Ebrahimi
286*62c56f98SSadaf Ebrahimiclass ModOperationCommon(OperationCommon):
287*62c56f98SSadaf Ebrahimi    #pylint: disable=abstract-method
288*62c56f98SSadaf Ebrahimi    """Target for bignum mod_raw test case generation."""
289*62c56f98SSadaf Ebrahimi    moduli = MODULI_DEFAULT # type: List[str]
290*62c56f98SSadaf Ebrahimi    montgomery_form_a = False
291*62c56f98SSadaf Ebrahimi    disallow_zero_a = False
292*62c56f98SSadaf Ebrahimi
293*62c56f98SSadaf Ebrahimi    def __init__(self, val_n: str, val_a: str, val_b: str = "0",
294*62c56f98SSadaf Ebrahimi                 bits_in_limb: int = 64) -> None:
295*62c56f98SSadaf Ebrahimi        super().__init__(val_a=val_a, val_b=val_b, bits_in_limb=bits_in_limb)
296*62c56f98SSadaf Ebrahimi        self.val_n = val_n
297*62c56f98SSadaf Ebrahimi        # Setting the int versions here as opposed to making them @properties
298*62c56f98SSadaf Ebrahimi        # provides earlier/more robust input validation.
299*62c56f98SSadaf Ebrahimi        self.int_n = hex_to_int(val_n)
300*62c56f98SSadaf Ebrahimi
301*62c56f98SSadaf Ebrahimi    def to_montgomery(self, val: int) -> int:
302*62c56f98SSadaf Ebrahimi        return (val * self.r) % self.int_n
303*62c56f98SSadaf Ebrahimi
304*62c56f98SSadaf Ebrahimi    def from_montgomery(self, val: int) -> int:
305*62c56f98SSadaf Ebrahimi        return (val * self.r_inv) % self.int_n
306*62c56f98SSadaf Ebrahimi
307*62c56f98SSadaf Ebrahimi    def convert_from_canonical(self, canonical: int,
308*62c56f98SSadaf Ebrahimi                               rep: ModulusRepresentation) -> int:
309*62c56f98SSadaf Ebrahimi        """Convert values from canonical representation to the given representation."""
310*62c56f98SSadaf Ebrahimi        if rep is ModulusRepresentation.MONTGOMERY:
311*62c56f98SSadaf Ebrahimi            return self.to_montgomery(canonical)
312*62c56f98SSadaf Ebrahimi        elif rep is ModulusRepresentation.OPT_RED:
313*62c56f98SSadaf Ebrahimi            return canonical
314*62c56f98SSadaf Ebrahimi        else:
315*62c56f98SSadaf Ebrahimi            raise ValueError('Modulus representation not supported: {}'
316*62c56f98SSadaf Ebrahimi                             .format(rep.name))
317*62c56f98SSadaf Ebrahimi
318*62c56f98SSadaf Ebrahimi    @property
319*62c56f98SSadaf Ebrahimi    def boundary(self) -> int:
320*62c56f98SSadaf Ebrahimi        return self.int_n
321*62c56f98SSadaf Ebrahimi
322*62c56f98SSadaf Ebrahimi    @property
323*62c56f98SSadaf Ebrahimi    def arg_a(self) -> str:
324*62c56f98SSadaf Ebrahimi        if self.montgomery_form_a:
325*62c56f98SSadaf Ebrahimi            value_a = self.to_montgomery(self.int_a)
326*62c56f98SSadaf Ebrahimi        else:
327*62c56f98SSadaf Ebrahimi            value_a = self.int_a
328*62c56f98SSadaf Ebrahimi        return self.format_arg('{:x}'.format(value_a))
329*62c56f98SSadaf Ebrahimi
330*62c56f98SSadaf Ebrahimi    @property
331*62c56f98SSadaf Ebrahimi    def arg_n(self) -> str:
332*62c56f98SSadaf Ebrahimi        return self.format_arg(self.val_n)
333*62c56f98SSadaf Ebrahimi
334*62c56f98SSadaf Ebrahimi    def format_arg(self, val: str) -> str:
335*62c56f98SSadaf Ebrahimi        return super().format_arg(val).zfill(self.hex_digits)
336*62c56f98SSadaf Ebrahimi
337*62c56f98SSadaf Ebrahimi    def arguments(self) -> List[str]:
338*62c56f98SSadaf Ebrahimi        return [quote_str(self.arg_n)] + super().arguments()
339*62c56f98SSadaf Ebrahimi
340*62c56f98SSadaf Ebrahimi    @property
341*62c56f98SSadaf Ebrahimi    def r(self) -> int: # pylint: disable=invalid-name
342*62c56f98SSadaf Ebrahimi        l = limbs_mpi(self.int_n, self.bits_in_limb)
343*62c56f98SSadaf Ebrahimi        return bound_mpi_limbs(l, self.bits_in_limb)
344*62c56f98SSadaf Ebrahimi
345*62c56f98SSadaf Ebrahimi    @property
346*62c56f98SSadaf Ebrahimi    def r_inv(self) -> int:
347*62c56f98SSadaf Ebrahimi        return invmod(self.r, self.int_n)
348*62c56f98SSadaf Ebrahimi
349*62c56f98SSadaf Ebrahimi    @property
350*62c56f98SSadaf Ebrahimi    def r2(self) -> int: # pylint: disable=invalid-name
351*62c56f98SSadaf Ebrahimi        return pow(self.r, 2)
352*62c56f98SSadaf Ebrahimi
353*62c56f98SSadaf Ebrahimi    @property
354*62c56f98SSadaf Ebrahimi    def is_valid(self) -> bool:
355*62c56f98SSadaf Ebrahimi        if self.int_a >= self.int_n:
356*62c56f98SSadaf Ebrahimi            return False
357*62c56f98SSadaf Ebrahimi        if self.disallow_zero_a and self.int_a == 0:
358*62c56f98SSadaf Ebrahimi            return False
359*62c56f98SSadaf Ebrahimi        if self.arity == 2 and self.int_b >= self.int_n:
360*62c56f98SSadaf Ebrahimi            return False
361*62c56f98SSadaf Ebrahimi        return True
362*62c56f98SSadaf Ebrahimi
363*62c56f98SSadaf Ebrahimi    def description(self) -> str:
364*62c56f98SSadaf Ebrahimi        """Generate a description for the test case.
365*62c56f98SSadaf Ebrahimi
366*62c56f98SSadaf Ebrahimi        It uses the form A `symbol` B mod N, where symbol is used to represent
367*62c56f98SSadaf Ebrahimi        the operation.
368*62c56f98SSadaf Ebrahimi        """
369*62c56f98SSadaf Ebrahimi
370*62c56f98SSadaf Ebrahimi        if not self.case_description:
371*62c56f98SSadaf Ebrahimi            return super().description() + " mod {:x}".format(self.int_n)
372*62c56f98SSadaf Ebrahimi        return super().description()
373*62c56f98SSadaf Ebrahimi
374*62c56f98SSadaf Ebrahimi    @classmethod
375*62c56f98SSadaf Ebrahimi    def input_cases_args(cls) -> Iterator[Tuple[Any, Any, Any]]:
376*62c56f98SSadaf Ebrahimi        if cls.arity == 1:
377*62c56f98SSadaf Ebrahimi            yield from ((n, a, "0") for a, n in cls.input_cases)
378*62c56f98SSadaf Ebrahimi        elif cls.arity == 2:
379*62c56f98SSadaf Ebrahimi            yield from ((n, a, b) for a, b, n in cls.input_cases)
380*62c56f98SSadaf Ebrahimi        else:
381*62c56f98SSadaf Ebrahimi            raise ValueError("Unsupported number of operands!")
382*62c56f98SSadaf Ebrahimi
383*62c56f98SSadaf Ebrahimi    @classmethod
384*62c56f98SSadaf Ebrahimi    def generate_function_tests(cls) -> Iterator[test_case.TestCase]:
385*62c56f98SSadaf Ebrahimi        if cls.input_style not in cls.input_styles:
386*62c56f98SSadaf Ebrahimi            raise ValueError("Unknown input style!")
387*62c56f98SSadaf Ebrahimi        if cls.arity not in cls.arities:
388*62c56f98SSadaf Ebrahimi            raise ValueError("Unsupported number of operands!")
389*62c56f98SSadaf Ebrahimi        if cls.input_style == "arch_split":
390*62c56f98SSadaf Ebrahimi            test_objects = (cls(n, a, b, bits_in_limb=bil)
391*62c56f98SSadaf Ebrahimi                            for n in cls.moduli
392*62c56f98SSadaf Ebrahimi                            for a, b in cls.get_value_pairs()
393*62c56f98SSadaf Ebrahimi                            for bil in cls.limb_sizes)
394*62c56f98SSadaf Ebrahimi            special_cases = (cls(*args, bits_in_limb=bil)
395*62c56f98SSadaf Ebrahimi                             for args in cls.input_cases_args()
396*62c56f98SSadaf Ebrahimi                             for bil in cls.limb_sizes)
397*62c56f98SSadaf Ebrahimi        else:
398*62c56f98SSadaf Ebrahimi            test_objects = (cls(n, a, b)
399*62c56f98SSadaf Ebrahimi                            for n in cls.moduli
400*62c56f98SSadaf Ebrahimi                            for a, b in cls.get_value_pairs())
401*62c56f98SSadaf Ebrahimi            special_cases = (cls(*args) for args in cls.input_cases_args())
402*62c56f98SSadaf Ebrahimi        yield from (valid_test_object.create_test_case()
403*62c56f98SSadaf Ebrahimi                    for valid_test_object in filter(
404*62c56f98SSadaf Ebrahimi                        lambda test_object: test_object.is_valid,
405*62c56f98SSadaf Ebrahimi                        chain(test_objects, special_cases)
406*62c56f98SSadaf Ebrahimi                        ))
407