def bound_primes_from_approximate_sum(self, sumpq, eps): """ p + q = s + e, 0 <= e < eps p + n/p = s+e p**2 - (s+e)*p + n = 0 p = (s + e - sqrt((s + e)**2 - 4*n)) / 2 q = (s + e + sqrt((s + e)**2 - 4*n)) / 2 """ n = self.n if sumpq**2 <= 4 * n: mx = sumpq + eps sumpq = ceil(ZZ(4 * n).sqrt()) eps = mx - sumpq assert (sumpq + eps)**2 > 4 * n assert eps >= 0 Dmin = floor(ZZ(sumpq**2 - 4 * n).sqrt()) Dmax = ceil(ZZ((sumpq + eps)**2 - 4 * n).sqrt()) p_max = (sumpq - Dmin) // 2 p_min = (sumpq + eps - Dmax) // 2 assert p_min <= p_max q_min = (sumpq + Dmin) // 2 q_max = (sumpq + eps + Dmax) // 2 assert q_min <= q_max # do not have to hold if not (p_max * q_min <= n) or not (p_min * q_max >= n): warnings.warn("strangely, bounds can be improved") q_min = max(q_min, n // p_max + 1) q_max = min(q_max, n // p_min) return (p_min, p_max), (q_min, q_max)
def __init__(self, spec, m=None): if is_Polynomial(spec): poly = spec assert len(poly.variables()) == 1 fld = poly.base_ring() if m is None: m = ZZ(fld.order() - 1).nbits() spec = [ poly.subs(fld.fetch_int(x)).integer_representation() for x in range(len(fld)) ] self._S = tuple(map(int, spec)) self.n = ZZ(len(self._S) - 1).nbits() self.m = ZZ(max(self._S)).nbits() if m is not None: m = int(m) lim = 1 << self.m assert all(0 <= y < lim for y in self._S) else: m = ZZ(max(self._S)).nbits() self.m = m assert self.input_size() >= 1 assert self.output_size() >= 0
def factor_with_prime_hint(self, low_mod=None, bounds=None, beta=0.49): """ """ if low_mod is None: low_mod = 0, 1 low, mod = low_mod assert 0 <= low < mod if bounds is None: bounds = 0, self.n_sqrt() pmin, pmax = bounds if beta < 0.5: assert pmax <= self.n_sqrt(), \ "upper bound must be <= sqrt(n) when beta < 0.5" else: assert pmin >= self.n_sqrt(), \ "lower bound must be >= sqrt(n) when beta >= 0.5" assert 1 <= pmin <= pmax # next value hitting low % mod if pmin % mod <= low: pmin = pmin - pmin % mod + low else: pmin = pmin - pmin % mod + mod + low xmax = ZZ(pmax - pmin) // mod PR, x = self.polyring() poly = (mod * x + pmin).monic() # for debugging # global p # assert p % mod == low # sol = (p-pmin)//mod # print("sol size", ZZ(sol).nbits(), "<xmax?", 0 <= sol <= xmax) # print(poly.subs(x=sol) % p ) # print("poly", poly) # X = 1/2 n**(beta**2/degree - epsilon) # log_n 2X = beta**2 - epsilon # epsilon = beta**2 - log_n 2X epsilon = beta**2 - RR(log(2 * xmax, self.n)) assert epsilon > 0, "impossible attack" roots = poly.small_roots(X=xmax, beta=beta, epsilon=epsilon) for sol in roots: p = int(poly.subs(x=sol)) p = abs(gcd(p, self.n)) if 1 < p < self.n: q = self.n // p assert self.n == p * q, "multi-prime? ouch" self.update(p=p, q=q) break else: raise RuntimeError("attack failed") return p, q
def n_sqrt(self): if self._n_sqrt is None: self._n_sqrt = floor(ZZ(self.n).sqrt()) return self._n_sqrt