def prob_solution(a, x, b, y, c, z): num_pp = len(safe_primes) mr = MontgomeryReduction() plus = True minus = True for i in range(num_ps_tests): #using safe primes ensures that the order of a and b modulo m is high, thus ensuring the probability of a^x+/-b^y=c^z is low, on the order of 1/m m = safe_primes[randrange(num_pp)] mr.set_modulus(m) ra, rb, rc = mr.convert(a), mr.convert(b), mr.convert(c) rax, rby, rcz = mr.exp(ra, x), mr.exp(rb, y), mr.exp(rc, z) if plus and (rax + rby) % m != rcz: plus = False s = '-' if not minus: break if minus and (rax - rby) % m != rcz: minus = False s = '+' if not plus: break else: return [True, s] return [False, s]
def beals_solver(a, b, c, z): """finds x and y s.t. a^x +/- b^y = c^z a, b, c mutually coprime and a is a generator modulo b b should be prime, but we'll accept (very) strong probable primes to make finding generators modulo b easier to find, b will be a safe prime c must be positive""" x = 0 y = 0 #take ceiling for rounding errors (possible underflow) lrab = math.ceil(math.log(a, b)) max_yc = z * math.ceil(math.log(c, b)) b_fact = all_fact[b] #for modulus b^k for k > 1, we want the discrete log to be over a "totient" of b, since the possible values of x satisfying g^x=h (mod b^k) will range over 0 to b. Giving the factorization of b as the factorization of the totient allows us to use the speedup afforded by the Pohlig-Hellman algorithm. The second element is None because we don't need the factorization of the inverse totient of b bt = [b, None, b_fact[1]] m = b lt = 1 t = b_fact[0] mr = MontgomeryReduction(m, b_fact) n = 0 ps = [False, None] while x < max_pow and y < max_pow and not ps[0]: x = get_x(a, x, c, z, mr, t, lt) y = get_y(a, x, c, z, b, lrab, max_yc) lt = t m *= b t *= b mr.set_modulus(m, bt) ps = prob_solution(a, x, b, y, c, z) n += 1 #print("{!s}: ({!s}, {!s})".format(n, x, y)) return [x, y, ps]
def get_y(a, x, c, z, b, lrab, max_yc): y_res_mod = [] max_ya = x * lrab max_y = max(max_ya, max_yc) p = 1 mr = MontgomeryReduction() for i, pprime in enumerate(prob_primes): if is_gen_n_k(b, pprime, all_fact[pprime]): m = pprime**2 #squaring ensures totient is divisible by prime mr.set_modulus(m, prime_sq_fact[i]) ra, rc, rb = mr.convert(a), mr.convert(c), mr.convert(b) rax, rcz = mr.exp(ra, x), mr.exp(rc, z) rd = (rax - rcz) % m if is_coprime(rd, m): y_sp = mr.dlog_mod(rb, rd, [pprime, 1]) y_res_mod.append([y_sp, pprime]) p *= pprime if p > max_y: break else: ipdb.set_trace() print("Not enough primes to get y") #raise Error("Not enough primes to get y") return crt(y_res_mod)