def quadratic_sieve(n, bound=1024, sieve=1000000): """ quadratic sieve to find a factor of an integer """ if n % 2 == 0: return 2 if isprime(n): return n if ispower(n): return ispower(n) memo = defaultdict(list) for a0 in range(isqrt(n) + 1, isqrt(2 * n), sieve): # primes where n is a quadratic residue p_list = [p for p in primes(bound) if 0 < pow(n, (p - 1) // 2, p) < p - 1] # sieving for b smooth numbers v_list = [a * a - n for a in range(a0, a0 + sieve)] for p in p_list: r = mod_sqrt(n, p) for i in range((r - a0) % p, sieve, p): while v_list[i] % p == 0: v_list[i] //= p for i in range((p - r - a0) % p, sieve, p): while v_list[i] % p == 0: v_list[i] //= p # factoring and checking for ai, r in enumerate(v_list): if r != 1 and not issquare(r): continue # factor all b smooth numbers ai += a0 b2 = ai * ai - n v = 0 for i, p in enumerate(p_list): while b2 % p == 0: b2 //= p v ^= 1 << i if b2 == 1: break # if b2 is square, use fermat's method if not v: return ai - isqrt(ai * ai - n) # compare against sieved congruent a values for aj in memo[v]: x = ai * aj - n y = isqrt((ai * ai - n) * (aj * aj - n)) if 1 < gcd(y - x, n) < n: return gcd(y - x, n) memo[v].append(ai) # increase bound size bound *= 2
def fermat_sieve(n): """ returns factor of N using fermat sieve method """ if n <= 1: return 1 if n % 2 == 0: return 2 if isprime(n): return n def prime_powers(): # iterates through increasingly smaller powers of primes # modeled after powers of highly composite numbers # also finds squares mod p to simply check if square perc = 1.0 for p in primegen(): p = p**(max(1, ilog(ilog(n, p), p))) yield p, frozenset(i * i % p for i in range(p // 2 + 1)) def worker(a, b2, astep, i): # finishes off steps if it deems it low enough # otherwise it finds more squares via quadratic residue double = astep + astep square = astep * astep m, s = modulus[i] if astep * m + a >= aend: while a < aend: b = isqrt(b2) if b * b == b2: return a - b, a + b else: b2 += double * a + square a += astep else: for _ in range(m): if b2 % m in s: ret = worker(a, b2, astep * m, i + 1) if ret is not None: return ret b2 += double * a + square a += astep # prime power iterator, saved powers, start and end ps = prime_powers() modulus = defaultdict(lambda: next(ps)) astart = isqrt(n) aend = isqrt(n + n) # call to helper function while True: ret = worker(astart, astart * astart - n, 1, 0) if ret is not None: return ret astart, aend = aend, 2 * aend
def quadratic_factor(n, bound=None): """ return factor of n using congruence of squares looks for two congruent b^2s using a dictionary of bitsets """ if n <= 1: return 1 if isprime(n): return n if ispower(n): return ispower(n) # heuristic optimization of bound if bound is None: bound = max(int(10 ** (log(n, 10) / 4)), 3) # look for b^2s by iterating a above the sqrt of n p_list = list(primes(bound)) memo = defaultdict(list) for ai in count(isqrt(n) + 1): vi = 0 b2 = ai * ai - n # odd powers bitset of the factorization of b2 using trial division for i, p in enumerate(p_list): while b2 % p == 0: b2 //= p vi ^= 1 << i if b2 == 1: break else: # not b-smooth if not issquare(b2): continue # if b2 is square, use fermat's method if not vi: return ai - isqrt(ai * ai - n) # compare against sieved congruent a values for aj in memo[vi]: x = ai * aj - n y = isqrt((ai * ai - n) * (aj * aj - n)) # pow(x, 2, n) == pow(y, 2, n) if 1 < gcd(y - x, n) < n: return gcd(y - x, n) # add the current a to the memo memo[vi].append(ai)
def william_pp1(n): """ williams p+1 algorithm for finding factors of a number """ if n == 0: return 0 if n == 1: return 1 if n % 2 == 0: return 2 if isprime(n): return n nsqrt = isqrt(n) if nsqrt * nsqrt == n: return nsqrt for v in count(1): for p in primegen(): e = ilog(nsqrt, p) if e == 0: break for _ in range(e): v = mlucas(v, p, n) g = gcd(v - 2, n) if g == n: break if 1 < g: return g
def pollard_pm2(n, lo=2, hi=4): p_gen = primegen() p_lo = list() p_hi = list() p_next = next(p_gen) while p_next < lo: p_lo.append(p_next) p_next = next(p_gen) am1 = 2 nsqrt = isqrt(n) while True: a = am1 for p in p_lo: a = pow(a, p, n) d = gcd(a - 1, n) if 1 < d < n: return d am1 = a while p_next < hi: p_hi.append(p_next) p_next = next(p_gen) for p in p_hi: a = pow(a, p, n) d = gcd(a - 1, n) if 1 < d < n: return d lo, hi = hi, hi * 2 p_lo, p_hi = p_hi, list()
def fermat(n): """ return factor of n using fermat method """ if n <= 1: return 1 if n % 2 == 0: return 2 a = isqrt(n) b2 = a * a - n while not issquare(b2): b2 += 2 * a + 1 a += 1 b = isqrt(b2) return a - b, a + b
def trial_division(n): """ returns the lowest prime that divides n """ for p in primes(isqrt(n)): if n % p == 0: return p return n
def isPrime(n): """ Returns True if n is prime, false otherwise n: positive integer """ for num in range(2, isqrt(n) + 1): if(n % num == 0): return False return True
def euler(n): """ uses euler's method to factor n """ for b in range(1, n): a2 = n - b * b if issquare(a2): a = isqrt(a2) break else: return for d in range(b + 1, n): c2 = n - d * d if issquare(c2): c = isqrt(c2) break if a == d: return A, B, C, D = a - c, a + c, d - b, d + b k, h, i, m = gcd(A, C) // 2, gcd(B, D) // 2, gcd(A, D) // 2, gcd(B, C) // 2 return (k * k + h * h), (i * i + m * m)
def primeFactorize(n, list=[]): """ Gives the prime factors of n n: integer > 0 returns: list of integers """ if(isPrime(n)): list.append(n) return list for num in range(2, 1 + isqrt(n)):#2 - ceil(n) if(n % num == 0): list + primeFactorize(int(n/num), list) list + primeFactorize(num, list) return list
def worker(a, b2, astep, i): # finishes off steps if it deems it low enough # otherwise it finds more squares via quadratic residue double = astep + astep square = astep * astep m, s = modulus[i] if astep * m + a >= aend: while a < aend: b = isqrt(b2) if b * b == b2: return a - b, a + b else: b2 += double * a + square a += astep else: for _ in range(m): if b2 % m in s: ret = worker(a, b2, astep * m, i + 1) if ret is not None: return ret b2 += double * a + square a += astep
def quadratic_sieve2(n, bound=100000): global c_list, v_list p_list = list(primes(bound)) ai = isqrt(n) b2 = ai * ai - n def vectorize(b2): v = 0 for i, p in enumerate(p_list): while b2 % p == 0: b2 //= p v ^= 1 << i if b2 == 1: return v a_list = list() v_list = list() b_list = list() c_list = [0] * len(p_list) # sieving for i in range(len(p_list) + 1): # find b that is smooth vi = None while vi is None: b2 += 2 * ai + 1 ai += 1 vi = vectorize(b2) # check fermat simple case if not vi: print("fermat") return ai - isqrt(b2) # add to lists a_list.append(ai) b_list.append(b2) v_list.append(vi) # add column v = vi c = 0 for _ in range(len(p_list)): c_list[c] *= 2 if v % 2: c_list[c] += 1 v //= 2 c += 1 # attempt at gauss c_copy = c_list[:] marks = set(range(len(v_list))) for col_j, bits_j in enumerate(c_copy): if not bits_j: continue row_i = 0 bit_i = 1 while not bits_j & bit_i: row_i += 1 bit_i *= 2 marks.discard(row_i) for col_k in range(len(c_copy)): if col_k == col_j: continue if bit_i & c_copy[col_k]: c_copy[col_k] ^= bits_j # find lone rows v_copy = list() for i in range(len(v_list)): v = 0 for c in range(len(c_copy)): v *= 2 if c_copy[c] % 2: v += 1 c_copy[c] //= 2 v_copy.append(v) # find dependences for m in marks: j_list = [m] row_j = v_copy[m] bit_j = 1 # could be dictionary for i in range(row_j.bit_length()): if bit_j & row_j: j_list.append(v_copy.index(bit_j)) bit_j *= 2 # calculate x and y x = y = 1 for j in j_list: x = x * a_list[-j - 1] % n y = y * b_list[-j - 1] y = iroot(y, len(j_list)) # check if valid if 1 < gcd(y - x, n) < n: print(len(j_list)) return gcd(y - x, n)
def quadratic_sieve(n, bound=1024, sieve=1000000): """ quadratic sieve to find a factor of an integer """ if n % 2 == 0: return 2 if isprime(n): return n if ispower(n): return ispower(n) # primes where n is a quadratic residue - 0 would mean divisable ... p_list = [p for p in primes(bound) if pow(n, (p - 1) // 2, p) != p - 1] pi = len(p_list) rows = list() a_list = list() b_list = list() for a0 in range(isqrt(n) + 1, isqrt(2 * n), sieve): # sieving for b smooth numbers v_list = [a * a - n for a in range(a0, a0 + sieve)] for p in p_list: r = mod_sqrt(n, p) for i in range((r - a0) % p, sieve, p): while v_list[i] % p == 0: v_list[i] //= p for i in range((p - r - a0) % p, sieve, p): while v_list[i] % p == 0: v_list[i] //= p # factoring and checking for ai, r in enumerate(v_list): if r != 1 and not issquare(r): continue # factor all b smooth numbers ai += a0 b2 = ai * ai - n v = 0 factors = Counter() for i, p in enumerate(p_list): power = 0 while b2 % p == 0: b2 //= p power += 1 if power: factors[p] = power if power % 2: v |= 1 << i if b2 == 1: break # if b2 is square, use fermat's method if not v: print("fermat") return ai - isqrt(ai * ai - n) rows.append(v) a_list.append(ai) b_list.append(factors) # look for 3+ that combine print(len(rows)) cols = [ sum(1 << i for i, x in enumerate(rows) if (1 << j) & x) for j in range(pi) ] marks = set(range(len(a_list))) # gaussian elimination for j, col in enumerate(cols): if not col: continue mask = 1 i = 0 while not mask & col: mask *= 2 i += 1 marks.remove(i) for k, col2 in enumerate(cols): if j == k: continue elif mask & col2: cols[k] ^= col # find matches rows2 = [ sum(1 << i for i, x in enumerate(reversed(cols)) if (1 << j) & x) for j in range(len(a_list)) ] for mark in marks: r_list = [mark] mask = 1 row = rows2[mark] for _ in range(pi): if mask & row: r_list.append(rows2.index(mask)) mask *= 2 # combine with 0s? x = y = 1 sq = Counter() for r in r_list: x = x * a_list[r] % n sq += b_list[r] for k, v in sq.items(): y = y * pow(k, v // 2, n) % n f = gcd(y - x, n) if 1 < f < n: return f
def mpqs(n): # When the bound proves insufficiently large, we throw out all our work and start over. # TODO: When this happens, get more data, but don't trash what we already have. # TODO: Rewrite to get a few more relations before proceeding to the linear algebra. # TODO: When we need to increase the bound, what is the optimal increment? # Special cases: this function poorly handles primes and perfect powers: m = ispower(n) if m: return m if isprime(n): return n root_n, root_2n = isqrt(n), isqrt(2 * n) bound = ilog(n**6, 10)**2 # formula chosen by experiment while True: try: prime, mod_root, log_p, num_prime = [], [], [], 0 # find a number of small primes for which n is a quadratic residue p = 2 while p < bound or num_prime < 3: leg = legendre(n % p, p) if leg == 1: prime += [p] mod_root += [ mod_sqrt(n, p) ] # the rhs was [int(mod_sqrt(n, p))]. If we get errors, put it back. log_p += [log(p, 10)] num_prime += 1 elif leg == 0: return p p = nextprime(p) x_max = len(prime) * 60 # size of the sieve m_val = (x_max * root_2n) >> 1 # maximum value on the sieved range # fudging the threshold down a bit makes it easier to find powers of primes as factors # as well as partial-partial relationships, but it also makes the smoothness check slower. # there's a happy medium somewhere, depending on how efficient the smoothness check is thresh = log(m_val, 10) * 0.735 # skip small primes. they contribute very little to the log sum # and add a lot of unnecessary entries to the table # instead, fudge the threshold down a bit, assuming ~1/4 of them pass min_prime = thresh * 3 fudge = sum(log_p[i] for i, p in enumerate(prime) if p < min_prime) // 4 thresh -= fudge smooth, used_prime, partial = [], set(), {} num_smooth, num_used_prime, num_partial, num_poly, root_A = ( 0, 0, 0, 0, isqrt(root_2n // x_max), ) while num_smooth <= num_used_prime: # find an integer value A such that: # A is =~ sqrt(2*n) / x_max # A is a perfect square # sqrt(A) is prime, and n is a quadratic residue mod sqrt(A) while True: root_A = nextprime(root_A) leg = legendre(n, root_A) if leg == 1: break elif leg == 0: return root_A A = root_A**2 # solve for an adequate B # B*B is a quadratic residue mod n, such that B*B-A*C = n # this is unsolvable if n is not a quadratic residue mod sqrt(A) b = mod_sqrt(n, root_A) B = (b + (n - b * b) * modinv(b + b, root_A)) % A C = (B * B - n) // A # B*B-A*C = n <=> C = (B*B-n)/A num_poly += 1 # sieve for prime factors sums, i = [0.0] * (2 * x_max), 0 for p in prime: if p < min_prime: i += 1 continue logp = log_p[i] inv_A = modinv(A, p) # modular root of the quadratic a, b, k = ( (((mod_root[i] - B) * inv_A) % p), (((p - mod_root[i] - B) * inv_A) % p), 0, ) while k < x_max: if k + a < x_max: sums[k + a] += logp if k + b < x_max: sums[k + b] += logp if k: sums[k - a + x_max] += logp sums[k - b + x_max] += logp k += p i += 1 # check for smooths i = 0 for v in sums: if v > thresh: x, vec, sqr = x_max - i if i > x_max else i, set(), [] # because B*B-n = A*C # (A*x+B)^2 - n = A*A*x*x+2*A*B*x + B*B - n # = A*(A*x*x+2*B*x+C) # gives the congruency # (A*x+B)^2 = A*(A*x*x+2*B*x+C) (mod n) # because A is chosen to be square, it doesn't need to be sieved # val = sieve_val = (A*x + 2*B)*x + C sieve_val = (A * x + 2 * B) * x + C if sieve_val < 0: vec, sieve_val = {-1}, -sieve_val for p in prime: while sieve_val % p == 0: if p in vec: sqr += [ p ] # track perfect sqr facs to avoid sqrting something huge at the end vec ^= {p} sieve_val = sieve_val // p if sieve_val == 1: # smooth smooth.append((vec, (sqr, (A * x + B), root_A))) used_prime |= vec elif sieve_val in partial: # combine two partials to make a (xor) smooth # that is, every prime factor with an odd power is in our factor base pair_vec, pair_vals = partial[sieve_val] sqr.extend(vec & pair_vec) sqr.append(sieve_val) vec ^= pair_vec smooth += [( vec, ( sqr + pair_vals[0], (A * x + B) * pair_vals[1], root_A * pair_vals[2], ), )] used_prime |= vec num_partial += 1 else: partial[sieve_val] = ( vec, (sqr, A * x + B, root_A), ) # save partial for later pairing i += 1 num_smooth, num_used_prime = len(smooth), len(used_prime) used_prime = sorted(list(used_prime)) # set up bit fields for gaussian elimination masks, mask, bitfields = [], 1, [0] * num_used_prime for vec, vals in smooth: masks += [mask] i = 0 for p in used_prime: if p in vec: bitfields[i] |= mask i += 1 mask <<= 1 # row echelon form offset = 0 null_cols = [] for col in range(num_smooth): pivot = (bitfields[col - offset] & masks[col] == 0 ) # This occasionally throws IndexErrors. # TODO: figure out why it throws errors and fix it. for row in range(col + 1 - offset, num_used_prime): if bitfields[row] & masks[col]: if pivot: bitfields[col - offset], bitfields[row], pivot = ( bitfields[row], bitfields[col - offset], False, ) else: bitfields[row] ^= bitfields[col - offset] if pivot: null_cols += [col] offset += 1 # reduced row echelon form for row in range(num_used_prime): mask = bitfields[row] & -bitfields[row] # lowest set bit for up_row in range(row): if bitfields[up_row] & mask: bitfields[up_row] ^= bitfields[row] # check for non-trivial congruencies # TODO: if none exist, check combinations of null space columns... # if _still_ none exist, sieve more values for col in null_cols: all_vec, (lh, rh, ra) = smooth[col] lhs = lh # sieved values (left hand side) rhs = [rh] # sieved values - n (right hand side) ras = [ra] # root_As (cofactor of lhs) i = 0 for field in bitfields: if field & masks[col]: vec, (lh, rh, ra) = smooth[i] lhs.extend(all_vec & vec) lhs.extend(lh) all_vec ^= vec rhs.append(rh) ras.append(ra) i += 1 f = gcd( reduce(mul, ras) * reduce(mul, lhs) - reduce(mul, rhs), n) if 1 < f < n: return f except IndexError: pass bound *= 1.2