def CRTM(mods): '''Return the solution to the Chinese Remainder Theorem as an element of a Mod class, x mod N. The parameter is a nonempty sequence of Mod class instances, where all the parameter moduli are coprime. N is the product of the parameter moduli. For each element a mod m of mods, x satisfies: a mod m equals x mod m. >>> vals = [Mod(2, 3), Mod(3, 4), Mod(1, 5)] >>> x = CRTM(vals) >>> print x 11 mod 60 >>> for val in vals: ... print val, int(val - x.value) 2 mod 3 0 3 mod 4 0 1 mod 5 0 ''' N = 1 # will be product of moduli for a in mods: N *= a.modulus() x = Mod(0, N) for a in mods: m = a.modulus() Nd = N // m gcd, s, t = xgcd(m, Nd) # t*Nd = 1 mod m assert gcd == 1, 'moduli not coprime!' # not required, but good idea x += a.value * t * Nd return x
def ChineseRemainder(pairs): '''Return the solution to the Chinese Remainder Theorem, (x, M) Pairs contains tuples (a, m) with all m's positive and coprime. Return the smallest nonnegative integer x so x mod m = a mod m for each (a, m) in pairs. M is the product ofthe m's. >>> pairs = [(2, 3), (3, 4), (1, 5)] >>> x, M = ChineseRemainder(pairs) >>> (x, M) (11, 60) >>> for (a, m) in pairs: ... print a % m, x % m 2 2 3 3 1 1 ''' (a, m) = pairs[0] for (b, p) in pairs[1:]: k = ((b - a) * xgcd(m, p)[1]) % p #moduli coprime so inverse exists for m mod p a = (a + m * k) % (m * p ) # joining a mod m and b mod p gives a mod(mp) m *= p # mod mp return (a, m)
def CRTM(mods): '''Return the solution to the Chinese Remainder Theorem as an element of a Mod class, x mod N. The parameter is a nonempty sequence of Mod class instances, where all the parameter moduli are coprime. N is the product of the parameter moduli. For each element a mod m of mods, x satisfies: a mod m equals x mod m. >>> vals = [Mod(2, 3), Mod(3, 4), Mod(1, 5)] >>> x = CRTM(vals) >>> print x 11 mod 60 >>> for val in vals: ... print val, int(val - x.value) 2 mod 3 0 3 mod 4 0 1 mod 5 0 ''' N = 1 # will be product of moduli for a in mods: N *= a.modulus() x = Mod(0, N) for a in mods: m = a.modulus() Nd = N//m gcd, s, t = xgcd(m, Nd) # t*Nd = 1 mod m assert gcd == 1, 'moduli not coprime!' # not required, but good idea x += a.value*t*Nd return x
def __invert__(self): """ TESTS: >>> ~Mod(5, 19) Mod(4, 19) """ y = xgcd(self.x, self.p) assert y[ 0] == 1, 'multiplicative inverse only defined for elements relatively prime to modulus' x = y[1] return Mod(x, self.p)
def __div__(self, other): """ TESTS: >>> Mod(13, 19) / Mod(5, 19) Mod(14, 19) >>> Mod(13, 19) / Mod(0, 19) Traceback (most recent call last): ... ZeroDivisionError >>> Mod(3, 5) / Mod(1, 5) Mod(3, 5) """ if other.x == 0: raise ZeroDivisionError y = xgcd(other.x, other.p) x = self.x * y[1] return Mod(x, self.p)
def getXModP(beta, alpha, p, q, r): ''' return (x, q**r) with (p-1)/q**r = k, 0 <= x < q**r, os beta^(x*k) = alpha^k mod p ''' oDiv = (p-1)/q # first divided group order bCurrent=beta xFinal=0 # returns x=x0+x1q+x2q^2+...+xiq^i with 0<=xi<q-1 alphaRaisedModp=pow(alpha, oDiv, p) qPow = 1 alphaInv = xgcd(alpha, p)[1] for i in range(0,r): betaRaisedModp=pow(bCurrent, oDiv, p) xCurrent = discreteLogModP(alphaRaisedModp, betaRaisedModp, p) xFinal += xCurrent*qPow #now we calculate the next beta, power of q, order factor bCurrent = bCurrent*pow(alphaInv, xCurrent*qPow, p) % p qPow *= q oDiv /= q return (xFinal,qPow)
def ChineseRemainder(pairs): '''Return the solution to the Chinese Remainder Theorem, (x, M) Pairs contains tuples (a, m) with all m's positive and coprime. Return the smallest nonnegative integer x so x mod m = a mod m for each (a, m) in pairs. M is the product ofthe m's. >>> pairs = [(2, 3), (3, 4), (1, 5)] >>> x, M = ChineseRemainder(pairs) >>> (x, M) (11, 60) >>> for (a, m) in pairs: ... print a % m, x % m 2 2 3 3 1 1 ''' (a, m)=pairs[0] for (b,p) in pairs[1:]: k=((b-a)*xgcd(m,p)[1]) % p #moduli coprime so inverse exists for m mod p a=(a+m*k) % (m*p)# joining a mod m and b mod p gives a mod(mp) m *= p # mod mp return (a,m)
def inverse(self): '''Return the multiplicative inverse or else raise a ValueError.''' (g,x,y) = xgcd(self.value, self.m) if g == 1: return Mod(x, self.m) raise ValueError, 'Value not invertible'
def gauss_jordanMod(m): """Puts given matrix (2D array) into the Reduced Row Echelon Form. Returns True if successful, False if 'm' is singular. Assumes all element are modular with some mod n. This version is the most general. More efficient alternatives: If n is prime: gauss_jordanExactField If n is a power of 2: gauss_jordanModPow2 Based on floating point code by Jarno Elonen,April 2005, released into Public Domain""" ## setVerbose(False) # for pyc only version (h, w) = (len(m), len(m[0])) N = m[0][0].modulus() commentM('Starting m', m) for y in range(0,h): comment('working on row', y) for y2 in range(y+1, h): # Eliminate column y2, below row y a = int(m[y][y]) # while converting m[y][y] b = int(m[y2][y]) # to gcd of all down from y. gcd, s, t, u, v = mgcd(a, b) # convert both rows; use original rows m[y][y:], m[y2][y:] = ([s*m[y][i] + t*m[y2][i] for i in range(y, w)], [u*m[y][i] + v*m[y2][i] for i in range(y, w)]) commentM('row', y, '<-', s, '* row', y, '+', t, '* row', y2, '\n', 'row', y2, '<-', u, '* row', y, '+', v, '* row', y2, m) gcd, inv, t = xgcd(int(m[y][y]), N) # must have result invertible if gcd != 1: return False for x in range(y, w): # Normalize row y m[y][x] *= inv commentM('normalize row', y, 'Mult by inverse', inv % N, m) for y in range(h-1, 0-1, -1): # Backsubstitute for y2 in range(0,y): for x in range(w-1, y-1, -1): m[y2][x] -= m[y][x] * m[y2][y] commentM('Back substitute to get final solution', m) ## oldVerbose() # for pyc only version return True def comment(*args): '''print variable length parameter list.''' if VERBOSE: for arg in args: print arg, print def commentM(*args): '''Print variable length parameter list, with a matrix/list last.''' if VERBOSE: comment(*args[:-1]) display(args[-1]) def makeMat(height, width, elt=0.0): ''' Make a matrix with given height, width, filled with elt.''' return [[elt for i in range(width)] for j in range(height)] def identity(height, ONE=1.0): '''Return the ientity matrix of size height. The value of ONE sets the type of the elements.''' ans = makeMat(height, height, ONE-ONE) for i, r in enumerate(ans): r[i] = ONE return ans def adjoin(m, b): '''mutate matrix m, appending rows of matrix b(of same height)''' for i, r in enumerate(m): # enumerate pairs index and element r += b[i] # augment the row with b's row, mutating the row of m def collapseColumns(m, start, past): ''' mutate m, removing column c: start <= c < past.''' for r in m: del(r[start:past]) # remove part from row, mutating the row def mul(m1, m2, ans=None): '''Multiply matrices m1*m2, using ans as place for the answer if not None. Returns the answer. If m1 is a simple list, it is taken as a row matrix. If m2 is a simple list, it is taken as a column matrix. The result is always a new matrix (list of lists). ''' if not isinstance(m1[0], list): # left lit to row matrix m1 = [m1] h = len(m1) k = len(m1[0]) if not isinstance(m2[0], list): # right list to column matrix m2 = transpose([m2]) assert k == len(m2) w = len(m2[0]) if ans is None: ans = makeMat(h, w) for r in range(h): for c in range(w): tot = 0 for i in range(k): tot += m1[r][i]*m2[i][c] ans[r][c] = tot return ans def linComb(m1, m2, a=1, b=1): '''return a*m1 + b*m2 for same sized matrices/lists m1 and m2, and scalar (not list) multipliers a, b.''' if isinstance(m1, list): return [linComb(r1, r2, a, b) for (r1, r2) in zip(m1, m2)] # if seq1 contains s0, s1, ..., seq2 contains t0, y1, ..., # then zip(seq1, seq2) contains (s0, t0), (s1, t1), .... return m1*a + m2*b add = linComb # just addition with default parameters used def sub(m1, m2): '''return m1 - m2 for same sized matrices/lists m1 and m2.''' return linComb(m1, m2, 1, -1) def matConvert(mat, cls): '''Return a new matrix/list with all elements e of matrix m replaced by cls(e). The name is chosen to suggest a class conversion, but any function could be used for 1-1 replacements of non-list elements. ''' if isinstance(mat, list): return [matConvert(r, cls) for r in mat] return cls(mat) convertMat = matConvert # original naming convention inconsistent! def copyMat(m): '''Return a matrix/list copying each non-list element in matrix m.''' def same(x): return x return matConvert(m, same) def transpose(m): '''Return a new matrix that is the transpose of m. If a single list is provided rather than a list of lists, it is treated as a single row matrix, [m]. ''' if not isinstance(m[0], list): m = [m] return [[r[j] for r in m] for j in range(len(m[0]))] def mxvSolve(m, v, gj = gauss_jordan): '''solve mx = v for square matrix m, replacing v with the solution x. If v is a single list, it is understood as a column matrix. Return True on success. ''' isVec = not isinstance(v[0], list) vOrig = v if isVec: # convenience conversion v = transpose(v) m = copyMat(m) adjoin(m, v) success = gj(m) collapseColumns(m, 0, len(m)) if isVec: vOrig[:] = transpose(m)[0] else: vOrig[:] = m return success def invert(m, gj=gauss_jordan, cls='ignore!'): '''Return True and convert m to its inverse in place if possible, using Gauss-Jordan variant gj. Return False otherwise, and m is not meaningful. cls is ignored - obsolete, not needed.''' ONE = m[0][0] - m[0][0] + 1 # right class for identity v = identity(len(m), ONE) success = mxvSolve(m, v, gj) m[:] = v return success # Only testing/display functions follow. def display(m, label=None, colWidth=5): '''Pretty print a matrix or list; also prints a scalar.''' if not isinstance(m, list): if label: print label, print m return if not isinstance(m[0], list): if not label: label = 'Plain list:' #? note source as list vs 1 row matrix m = [m] if label: print label if isinstance(m[0][0], Mod): print 'Elements are mod', m[0][0].modulus() m = matConvert(m, int) for r in m: for x in r: print format(x, str(colWidth)), print def showOff(m, cls=float, f=gauss_jordan): '''Show off inversion of matrix m using Gauss-Jordan version f, where the elements e of m are transformed first to cls(e). ''' m = matConvert(m, cls) mc = copyMat(m) display(m, 'Matrix to reduce, using ' + f.func_name) adjoin(m, identity(len(m), cls(1))) display(m, 'With lines adjoined') if not f(m): print 'Failed!' else: display(m, 'After GJ variant ' + f.func_name) showInvert(mc, f, cls) def showInvert(m, gj=gauss_jordan, cls=float): '''Show off inversion of matrix m using Gauss-Jordan version gj, where the elements e of m are transformed first to cls(e). ''' display(m, 'Matrix; Now do inverse in one function call:') mc = convertMat(m, cls) # fixed! setVerbose(False) if not invert(mc, gj): print 'Not invertible' oldVerbose() display(mc, 'Inverted') check = mul(m, mc) commentM('Multiply and check', check) assert check == identity(len(m), cls(1)) def showMxvSolve(m, v, gj=gauss_jordan, cls=float, verbose=False): '''Show off inversion of matrix m using Gauss-Jordan version gj, where the elements e of m are transformed first to cls(e). ''' m = matConvert(m, cls) v = matConvert(v, cls) display(m, 'Matrix') display(v, 'v:') vc = copyMat(v) isVec = not isinstance(v[0], list) setVerbose(verbose) if not mxvSolve(m, vc, gj): print 'Not invertible' oldVerbose() display(vc, 'Solution') check = mul(m, vc) if isVec: check = transpose(check)[0] commentM('Multiply and check', check) assert check == v def showLinComb(*param): '''See linComb for parameters. Display all parametersand result.''' showOp(linComb, ['Testing linComb, return a*m1 + b*m2', 'm1', 'm2', 'a', 'b'], *param) def showOp(f, labels, *param): print labels[0] for i, p in enumerate(param): display(p, labels[i+1]) display(f(*param), 'result') VERBOSE = False _V_STACK = [] def setVerbose(verbose): '''Set global verbosity, remember old value; pair with an oldVerbose call. ''' global VERBOSE _V_STACK.append(VERBOSE) VERBOSE = verbose def oldVerbose(): '''Recall last global verbosity value remembered by setVerbose.''' global VERBOSE VERBOSE = _V_STACK.pop() def test(): '''test of GassJordan versions.''' m = ([[1., 2.], [4., 7.] ]) m2 = ([[0., 1.], [1., 7.] ]) m3 = [[8, 7], [3, 5]] m4 = [[88, 5, 119], [26, 2, 37], [55, 29,53]] m5 = [[88, 5, 19], [33, 2, 37], [55, 29,53]] m6 = [[ 84, 17, 23], [140, 11, 2], [105, 19, 44]] Mod11 = ZMod(11) Mod16 = ZMod(16) Mod128 = ZMod(128) Mod90 = ZMod(90) Mod180 = ZMod(180) showOff(m) showOff(m2) m = matConvert(m, int) m2 = matConvert(m2, int) showOff(m, Mod11, gauss_jordanExactField) showOff(m2, Mod11, gauss_jordanExactField) showOff(m3, Mod16, gauss_jordanModPow2) showOff(m4, Mod128, gauss_jordanModPow2) v = [1, 2, 3] showMxvSolve(m4, v, gauss_jordanModPow2, Mod16) showOff(m3, Mod16, gauss_jordanMod) # homework! showOff(m4, Mod128, gauss_jordanMod) showOff(m5, Mod90, gauss_jordanMod) showOff(m6, Mod180, gauss_jordanMod) showLinComb(m, m2, 2, -1) showOp(sub, ['Testing sub(m1,m2)', 'm1', 'm2'], matConvert(m4, Mod128), matConvert(m5, Mod128)) if __name__ == '__main__': VERBOSE = True test()
def __validate_e(self, e, phi): x, y = xgcd(e, phi) if e * x + phi * y == 1: return x % phi return None
def inverse(self): '''Return the multiplicative inverse or else raise a ValueError.''' (g, x, y) = xgcd(self.value, self.m) if g == 1: return Mod(x, self.m) raise ValueError, 'Value not invertible'