def _log_rho(g, a, p, *, partition="d"): """ For internal use only - see log() for other purposes. Finds the discreet logarithm log_g(a) modulo p using Pollard's rho algorithm. """ phi_p = phi(p) for i, j in product(range(phi_p), repeat=2): u, v = [i], [j] x = [a ** u[0] * g ** v[0] % p] for i in count(1): if partition == "d": r = x[-1] if r < p // 3: x.append(x[-1] ** 2 % p) u.append(2 * u[-1] % phi_p) v.append(2 * v[-1] % phi_p) elif r < 2 * p // 3: x.append(a * x[-1] % p) u.append((u[-1] + 1) % phi_p) v.append(v[-1]) else: x.append(g * x[-1] % p) u.append(u[-1]) v.append((v[-1] + 1) % phi_p) elif partition == "m": r = x[-1] % 3 if r == 0: x.append(x[-1] ** 2 % p) u.append(2 * u[-1] % phi_p) v.append(2 * v[-1] % phi_p) elif r == 1: x.append(a * x[-1] % p) u.append((u[-1] + 1) % phi_p) v.append(v[-1]) else: x.append(g * x[-1] % p) u.append(u[-1]) v.append((v[-1] + 1) % phi_p) if i % 2 == 0 and x[i] == x[i // 2]: break b, c = u[i // 2] - u[i], v[i] - v[i // 2] if (b - c) % p != 0: log = solve_cong(b, c, phi_p) if log is not None and log[1] == phi_p: return log[0] raise ValueError("Other partition is needed.")
def algebras(m_maxs): """ Generator for the possible algebras. Args: m_maxs: Maximum possible m values given p and d as a tuple e.g. (m, p, d). Returns: A 5-tuple ( m, p, d, phi_m, nSlots ) for the algebra. Usage: >>> print( '\\n'.join(map(str,algebras( ((24, 5, 2),) )))) (2, 5, 1, 1, 1) (4, 5, 1, 2, 2) (8, 5, 2, 4, 2) (3, 5, 2, 2, 1) (6, 5, 2, 2, 1) (12, 5, 2, 4, 2) (24, 5, 2, 8, 4) """ # Generate the divisors for each max_m for m_max,p,d in m_maxs: factors = numth.factorize(m_max) for mFactors in numth.divisorsFactors(factors): d_real = d m = numth.calcDivisor(mFactors) if m == 1: continue phi_m = numth.phi(mFactors) # Correct for order of p in mod m a.k.a. d phimFactors = numth.factorize(phi_m) for e in sorted(numth.divisors(phimFactors)): if p**e % m == 1: d_real = e break nSlots, r = divmod(phi_m, d_real) if r != 0: raise ArithmeticError("Fractional nslots should not exist.") # SolnObj just a 5-tuple ( m, p, d, phi_m, nSlots ) yield (m,p,d_real,phi_m,nSlots)
if __name__ == "__main__": parser = argparse.ArgumentParser(description="") parser.add_argument("-p", type=parsePrimeRange, default="2", help="Prime number for plaintext space.") parser.add_argument("-d", type=parseRange, default="1", help="How many coefficients in a slot (the order of p in ZmStar).") parser.add_argument("--self-test", action="store_true", help="Run doctest on itself.") args = parser.parse_args() # Run doctest instead of normal operation if args.self_test: import doctest print(doctest.testmod()) exit(0) # Generator for m_max(s) m_maxs = [ (p**d - 1, p, d) for p in args.p for d in args.d ] # SolnObj just a 5-tuple ( m, p, d, phi_m, nSlots ) solns = sorted( algebras(m_maxs) ) m_max = max(m_maxs) minWidths = [ math.ceil(math.log10(t)) for t in m_max ] minWidths.append( math.ceil(math.log10(numth.phi(numth.factorize(m_max[0]))))) minWidths.append(minWidths[-1]) printTable( ('m','p','d','phi(m)','nSlots'), solns, minWidths)