Skip to content

Commit 84c5784

Browse files
pranlawatecclauss
andauthored
Add comprehensive doctests to find_mod_inverse function (#13217)
- Added detailed docstring explaining modular multiplicative inverse - Included 10 valid test cases with verification calculations - Added 3 error cases testing ValueError for non-coprime inputs - Added Wikipedia reference for educational value - All doctests pass locally (python -m doctest -v) - Passes ruff, mypy, and pre-commit hooks Contributes to #9943 Co-authored-by: Christian Clauss <cclauss@me.com>
1 parent b285263 commit 84c5784

1 file changed

Lines changed: 43 additions & 8 deletions

File tree

ciphers/cryptomath_module.py

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,64 @@ def find_mod_inverse(a: int, m: int) -> int:
1212
"""
1313
Find the modular multiplicative inverse of a modulo m.
1414
15-
The modular multiplicative inverse of a modulo m is an integer x
16-
such that (a * x) % m == 1. This only exists when gcd(a, m) == 1.
15+
The modular multiplicative inverse of a modulo m is an integer x such that:
16+
(a * x) % m = 1
17+
18+
This function uses the Extended Euclidean Algorithm to find the inverse.
19+
An inverse exists if and only if a and m are coprime (gcd(a, m) = 1).
1720
1821
Args:
19-
a: The number to find the inverse of
22+
a: The integer to find the inverse of
2023
m: The modulus
2124
2225
Returns:
2326
The modular multiplicative inverse of a modulo m
2427
2528
Raises:
26-
ValueError: If gcd(a, m) != 1 (inverse doesn't exist)
29+
ValueError: If gcd(a, m) != 1 (inverse does not exist)
30+
31+
Reference:
32+
https://en.wikipedia.org/wiki/Modular_multiplicative_inverse
2733
34+
Examples:
35+
>>> find_mod_inverse(3, 7)
36+
5
37+
>>> (3 * 5) % 7 # Verify: 3 * 5 ≡ 1 (mod 7)
38+
1
39+
>>> find_mod_inverse(3, 10)
40+
7
41+
>>> (3 * 7) % 10 # Verify: 3 * 7 ≡ 1 (mod 10)
42+
1
43+
>>> find_mod_inverse(4, 11)
44+
3
45+
>>> (4 * 3) % 11 # Verify: 4 * 3 ≡ 1 (mod 11)
46+
1
2847
>>> find_mod_inverse(7, 26)
2948
15
30-
>>> find_mod_inverse(3, 11)
31-
4
32-
>>> find_mod_inverse(5, 17)
33-
7
49+
>>> (7 * 15) % 26 # Verify: 7 * 15 ≡ 1 (mod 26)
50+
1
3451
>>> find_mod_inverse(1, 5)
3552
1
3653
>>> find_mod_inverse(2, 7)
3754
4
55+
>>> find_mod_inverse(3, 11)
56+
4
57+
>>> find_mod_inverse(5, 11)
58+
9
59+
>>> find_mod_inverse(5, 17)
60+
7
61+
>>> find_mod_inverse(2, 4)
62+
Traceback (most recent call last):
63+
...
64+
ValueError: mod inverse of 2 and 4 does not exist
65+
>>> find_mod_inverse(6, 9)
66+
Traceback (most recent call last):
67+
...
68+
ValueError: mod inverse of 6 and 9 does not exist
69+
>>> find_mod_inverse(10, 20)
70+
Traceback (most recent call last):
71+
...
72+
ValueError: mod inverse of 10 and 20 does not exist
3873
"""
3974
if gcd_by_iterative(a, m) != 1:
4075
msg = f"mod inverse of {a!r} and {m!r} does not exist"

0 commit comments

Comments
 (0)