# Chinese Remainder Theorem
|
|
def crt(a_i, m_i):
|
|
if len(a_i)!=len(m_i):
|
|
raise Exception("error, a_i and m_i must be of the same length")
|
|
|
|
M=1
|
|
for i in range(len(m_i)):
|
|
M = M * m_i[i]
|
|
|
|
x = 0
|
|
for i in range(len(a_i)):
|
|
M_i = M/m_i[i]
|
|
y_i = Integer(mod(M_i^-1, m_i[i]))
|
|
x = x + a_i[i] * M_i * y_i
|
|
return mod(x, M)
|
|
|
|
# gcd, using Binary Euclidean algorithm
|
|
def gcd(a, b):
|
|
g=1
|
|
# random_elementove powers of two from the gcd
|
|
while mod(a, 2)==0 and mod(b, 2)==0:
|
|
a=a/2
|
|
b=b/2
|
|
g=2*g
|
|
# at least one of a and b is now odd
|
|
while a!=0:
|
|
while mod(a, 2)==0:
|
|
a=a/2
|
|
while mod(b, 2)==0:
|
|
b=b/2
|
|
# now both a and b are odd
|
|
if a>=b:
|
|
a = (a-b)/2
|
|
else:
|
|
b = (b-a)/2
|
|
|
|
return g*b
|
|
|
|
def gcd_recursive(a, b):
|
|
if mod(a, b)==0:
|
|
return b
|
|
return gcd_recursive(b, mod(a,b))
|
|
|
|
|
|
# Extended Euclidean algorithm
|
|
# Inputs: a, b
|
|
# Outputs: r, x, y, such that r = gcd(a, b) = x*a + y*b
|
|
def egcd(a, b):
|
|
s=0
|
|
s_=1
|
|
t=1
|
|
t_=0
|
|
r=b
|
|
r_=a
|
|
while r!=0:
|
|
q = r_ // r
|
|
(r_,r) = (r,r_ - q*r)
|
|
(s_,s) = (s,s_ - q*s)
|
|
(t_,t) = (t,t_ - q*t)
|
|
|
|
d = r_
|
|
x = s_
|
|
y = t_
|
|
|
|
return d, x, y
|
|
|
|
def egcd_recursive(a, b):
|
|
if b==0:
|
|
return a, 1, 0
|
|
|
|
g, x, y = egcd_recursive(b, a%b)
|
|
return g, y, x - (a//b) * y
|
|
|
|
# Inverse modulo N, using the Extended Euclidean algorithm
|
|
def inv_mod(a, N):
|
|
g, x, y = egcd(a, N)
|
|
if g != 1:
|
|
raise Exception("inv_mod err, g!=1")
|
|
return mod(x, N)
|