def crt_permutation(rep_crt, b, y, c, z, crt_a=None, crt_x=None):
    
    if crt_a is None:
        crt_a = []
    
    if crt_x is None:
        crt_x = []
    
    if not rep_crt:
        a = crt(crt_a)
        x = crt(crt_x)
        
        ps = prob_solution(a, x, b, y, c, z)
        
        if ps[0]:
            return [[a, x]]
        else:
            return []
    
    car, cdr = rep_crt[0], rep_crt[1:]    
    ax_mod, res_exp_pairs = car[0], car[1]
    
    ax_pairs = []
    
    for re_pair in res_exp_pairs:
        a_res, x_res = re_pair        
        a_rm, x_rm = [[a_res, ax_mod]], [[x_res, ax_mod]]
        
        ax_pairs.extend(crt_permutation(cdr, b, y, c, z, crt_a + a_rm, crt_x + x_rm))
    
    return ax_pairs
def get_y(a, x, c, z, b, lrab, max_yc, b_gp):
    
    y_res_mod = []
    
    max_ya = x * lrab
    max_y = max(max_ya, max_yc)
    
    p = 1
    
    for pprime, pprime_sq, prime_sq_tot, prime_div in b_gp:
        #squared prime modulus ensures totient is divisible by prime
        ax, cz = exp_bs(a, x, pprime_sq), exp_bs(c, z, pprime_sq)
        
        d = (ax - cz) % pprime_sq
        
        if is_coprime(d, pprime_sq):                
            y_sp = dlog_mod(b, d, pprime_sq, prime_sq_tot, prime_div)
            y_res_mod.append([y_sp, pprime])
            
            p *= pprime
            
            if p > max_y:
                break
    else:
        print("Not enough primes to get y")
        #raise Error("Not enough primes to get y")
    
    return crt(y_res_mod)
 def dlog(self, g, h):
     """finds discrete log of rh base rg modulo m using Pohlig-Hellman algorithm"""
     
     if self.t is None:
         return None
     
     x_crt = []
     
     for pe in self.tf:
         r = self.dlog_mod(g, h, pe)
         x_crt.append([r, pe[0]**pe[1]])
         
         #print("x = {!s} (mod {!s}^{!s})".format(r, pe[0], pe[1]))
     
     return crt(x_crt)
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)