263 lines
6.3 KiB
Python
263 lines
6.3 KiB
Python
'''
|
|
Source: extendedeuclideanalgorithm.com
|
|
|
|
Modified to generate CSV tables
|
|
for iterative calculation of the modular inverse
|
|
'''
|
|
|
|
import math
|
|
|
|
|
|
# Warning: can't handle b=0. See extendedeuclideanalgorithm.com/code for a version that can
|
|
def gcd_iterative(a, b):
|
|
""" Calculating the greatest common divisor
|
|
using the Euclidean Algorithm (non-recursive)
|
|
(Source: extendedeuclideanalgorithm.com/code)
|
|
"""
|
|
|
|
# Set default values for the quotient and the remainder
|
|
q = 0
|
|
r = 1
|
|
|
|
'''
|
|
In each iteration of the loop below, we
|
|
calculate the new quotient, remainder, a and b.
|
|
r decreases, so we stop when r = 0
|
|
'''
|
|
while (r > 0):
|
|
# The calculations
|
|
q = math.floor(a / b)
|
|
r = a - q * b
|
|
|
|
# The values for the next iteration
|
|
a = b
|
|
b = r if (r > 0) else b
|
|
|
|
return abs(b)
|
|
|
|
|
|
# Can handle b=0
|
|
def gcd_iterative_2(a, b):
|
|
""" Calculating the greatest common divisor
|
|
using the Euclidean Algorithm (non-recursive)
|
|
(Source: extendedeuclideanalgorithm.com/code)
|
|
"""
|
|
|
|
# Set default values for the quotient and the remainder
|
|
q = 0
|
|
r = 1
|
|
|
|
'''
|
|
In each iteration of the loop below, we
|
|
calculate the new quotient, remainder, a and b.
|
|
r decreases, so we stop when r = 0
|
|
'''
|
|
while (b > 0):
|
|
# The calculations
|
|
q = math.floor(a / b)
|
|
r = a - q * b
|
|
|
|
# The values for the next iteration
|
|
a = b
|
|
b = r
|
|
|
|
return abs(a)
|
|
|
|
|
|
def gcd(a, b):
|
|
""" Calculating the greatest common divisor
|
|
using the Euclidean Algorithm (recursive)
|
|
(Source: extendedeuclideanalgorithm.com/code)
|
|
"""
|
|
if (b == 0):
|
|
return abs(a)
|
|
|
|
q = math.floor(a / b)
|
|
r = a - q * b
|
|
return abs(b) if (r == 0) else gcd(b, r)
|
|
|
|
|
|
# Warning: this version can't handle b=0. See extendedeuclideanalgorithm.com/code for a version that can.
|
|
def xgcd_iterative(a, b):
|
|
""" Calculates the gcd and Bezout coefficients,
|
|
using the Extended Euclidean Algorithm (non-recursive).
|
|
(Source: extendedeuclideanalgorithm.com/code)
|
|
"""
|
|
# Set default values for the quotient, remainder,
|
|
# s-variables and t-variables
|
|
q = 0
|
|
r = 1
|
|
s1 = 1
|
|
s2 = 0
|
|
s3 = 1
|
|
t1 = 0
|
|
t2 = 1
|
|
t3 = 0
|
|
|
|
'''
|
|
In each iteration of the loop below, we
|
|
calculate the new quotient, remainder, a, b,
|
|
and the new s-variables and t-variables.
|
|
r decreases, so we stop when r = 0
|
|
'''
|
|
while (r > 0):
|
|
# The calculations
|
|
q = math.floor(a / b)
|
|
r = a - q * b
|
|
s3 = s1 - q * s2
|
|
t3 = t1 - q * t2
|
|
|
|
'''
|
|
The values for the next iteration,
|
|
(but only if there is a next iteration)
|
|
'''
|
|
if (r > 0):
|
|
a = b
|
|
b = r
|
|
s1 = s2
|
|
s2 = s3
|
|
t1 = t2
|
|
t2 = t3
|
|
|
|
return abs(b), s2, t2
|
|
|
|
|
|
# Can handle b=0
|
|
def xgcd_iterative_2(a, b):
|
|
""" Calculates the gcd and Bezout coefficients,
|
|
using the Extended Euclidean Algorithm (non-recursive).
|
|
(Source: extendedeuclideanalgorithm.com/code)
|
|
"""
|
|
# Set default values for the quotient, remainder,
|
|
# s-variables and t-variables
|
|
q = 0
|
|
r = 1
|
|
s1 = 1
|
|
s2 = 0
|
|
s3 = 1
|
|
t1 = 0
|
|
t2 = 1
|
|
t3 = 0
|
|
|
|
'''
|
|
In each iteration of the loop below, we
|
|
calculate the new quotient, remainder, a, b,
|
|
and the new s-variables and t-variables.
|
|
r decreases, so we stop when r = 0
|
|
'''
|
|
|
|
# CSV output
|
|
print("i, n, b, q, r, t1, t2, t3")
|
|
i = 1
|
|
|
|
while (b > 0):
|
|
# The calculations
|
|
q = math.floor(a / b)
|
|
r = a - q * b
|
|
s3 = s1 - q * s2
|
|
t3 = t1 - q * t2
|
|
|
|
# CSV output
|
|
print("{}, {}, {}, {}, {}, {}, {}, {}".format(i, a, b, q, r, t1, t2, t3))
|
|
i += 1
|
|
|
|
'''
|
|
The values for the next iteration,
|
|
(but only if there is a next iteration)
|
|
'''
|
|
|
|
a = b
|
|
b = r
|
|
s1 = s2
|
|
s2 = s3
|
|
t1 = t2
|
|
t2 = t3
|
|
|
|
return abs(a), s1, t1
|
|
|
|
|
|
def xgcd(a, b, s1=1, s2=0, t1=0, t2=1):
|
|
""" Calculates the gcd and Bezout coefficients,
|
|
using the Extended Euclidean Algorithm (recursive).
|
|
(Source: extendedeuclideanalgorithm.com/code)
|
|
"""
|
|
if (b == 0):
|
|
return abs(a), 1, 0
|
|
|
|
q = math.floor(a / b)
|
|
r = a - q * b
|
|
s3 = s1 - q * s2
|
|
t3 = t1 - q * t2
|
|
|
|
# if r==0, then b will be the gcd and s2, t2 the Bezout coefficients
|
|
return (abs(b), s2, t2) if (r == 0) else xgcd(b, r, s2, s3, t2, t3)
|
|
|
|
|
|
def multinv(b, n):
|
|
"""
|
|
Calculates the multiplicative inverse of a number b mod n,
|
|
using the Extended Euclidean Algorithm. If b does not have a
|
|
multiplicative inverse mod n, then throw an exception.
|
|
(Source: extendedeuclideanalgorithm.com/code)
|
|
"""
|
|
|
|
# Get the gcd and the second Bezout coefficient (t)
|
|
# from the Extended Euclidean Algorithm. (We don't need s)
|
|
my_gcd, _, t = xgcd(n, b)
|
|
|
|
# It only has a multiplicative inverse if the gcd is 1
|
|
if (my_gcd == 1):
|
|
return t % n
|
|
else:
|
|
raise ValueError('{} has no multiplicative inverse modulo {}'.format(b, n))
|
|
|
|
|
|
def make_eea_table(a : int, b : int):
|
|
|
|
|
|
'''
|
|
Euclidean algorithm:
|
|
see the output of gcd(a, b)
|
|
'''
|
|
print('Euclidean Algorithm:')
|
|
print('The gcd of', a, 'and', b, 'is', gcd(a, b))
|
|
|
|
# -------------------------------------------------------------
|
|
|
|
'''
|
|
Extended Euclidean Algorithm:
|
|
see the output of xgcd(a,b) and Bezout coefficients
|
|
And verify that they are correct
|
|
'''
|
|
my_gcd, s, t = xgcd(a, b)
|
|
verification = abs(s * a + t * b)
|
|
print('Extended Euclidean Algorithm:')
|
|
print('The gcd of', a, 'and', b, 'is', my_gcd)
|
|
print('And the Bezout coefficients: s=', s, ' and t=', t, '.', sep='')
|
|
print('And', s, '*', a, '+', t, '*', b, '=', verification)
|
|
if (my_gcd == verification):
|
|
print('So as we expect, s*a+t*b is equal to the gcd we found.')
|
|
else:
|
|
print('Something went wrong')
|
|
|
|
# ------------------------------------------------------------
|
|
b = b
|
|
n = a
|
|
|
|
'''
|
|
Multiplicative Inverse:
|
|
Try to compute the multiplicative inverse of b mod n.
|
|
If that succeeds, verify that it's correct.
|
|
If it doesn't succeed, show the error raised by the function.
|
|
'''
|
|
|
|
print('Multiplicative inverse:')
|
|
try:
|
|
# inverse = multinv(b, n);
|
|
inverse = xgcd_iterative_2(b, n)
|
|
|
|
except ValueError as error:
|
|
print(error)
|
|
|
|
|