-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnum_theory.py
More file actions
50 lines (43 loc) · 1.27 KB
/
num_theory.py
File metadata and controls
50 lines (43 loc) · 1.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# pylint: disable=C0103
"""
This script handles number theoretic functions.
"""
from typing import Optional
def ext_gcd(x: int, y: int) -> tuple[int, int, int]:
"""
Returns (g, a, b) s.t. g = x * a + y * b > 0
"""
if y == 0:
return (x, 1, 0) if x > 0 else (-x, -1, 0)
q = x // y
r = x % y
(g, a, b) = ext_gcd(y, r)
return (g, b, a - b * q)
def inverse_with_gcd(x: int, y: int) -> tuple[int, int]:
"""
Returns a that satisfies ax = g (mod y) along with the minimum g with which this is possible.
"""
assert y > 0
(g, a, _) = ext_gcd(x % y, y)
return (a % (y // g), g)
def solve_equation_2(ab: tuple[int, int], cd: tuple[int, int]) -> Optional[tuple[int, int]]:
"""
>>> num_theory.solve_equation_2((1, 6), (3, 8))
(19, 24)
"""
(a, b) = ab
(c, d) = cd
(bm1, g) = inverse_with_gcd(b, d)
(dm1, _) = inverse_with_gcd(d, b)
if a % g != c % g:
return None
value = a % g + (a // g * dm1 * d + c // g * bm1 * b)
lcm = b // g * d
return (value % lcm, lcm)
def solve_equation(exprs: list[tuple[int, int]]) -> Optional[tuple[int, int]]:
cur = (0, 1)
for cons in exprs:
cur = solve_equation_2(cur, cons)
if cur is None:
return None
return cur