def register(self, divisor, isprime=Unknown): """ Register a divisor of the number, if the divisor is a true divisor of the number. The number is divided by the divisor as many times as possible. """ for base, index in self.factors: if base == divisor: if isprime and not self.primality[base]: self.setPrimality(base, isprime) break common_divisor = gcd.gcd(base, divisor) if common_divisor == 1: continue # common_divisor > 1: if common_divisor == divisor: if isprime: self.setPrimality(divisor, isprime) k, coprime = arith1.vp(base, common_divisor) while not gcd.coprime(common_divisor, coprime): # try a smaller factor common_divisor = gcd.gcd(common_divisor, coprime) k, coprime = arith1.vp(base, common_divisor) if k: if coprime > 1: self.replace(base, [(common_divisor, k), (coprime, 1)]) else: self.replace(base, [(common_divisor, k)]) else: # common_divisor properly divides divisor. self.register(common_divisor) self.register(divisor // common_divisor)
def _factor_order(m, u, era=None): """ Return triple (k, q, primes) if m is factored as m = kq, where k > 1 and q is a probable prime > u. u is expected to be (n^(1/4)+1)^2 for Atkin-Morain ECPP. If m is not successfully factored into desired form, return (False, False, primes). The third component of both cases is a list of primes to do trial division. Algorithm 27 (Atkin-morain ECPP) Step3 """ if era is None: v = min(500000, int(log(m))) era = factor.mpqs.eratosthenes(v) k = 1 q = m for p in era: if p > u: break e, q = arith1.vp(q, p) k *= p**e if q < u or k == 1: return False, False, era if not prime.millerRabin(q): return False, False, era return k, q, era
def _factor_order(m, u, era=None): """ Return triple (k, q, primes) if m is factored as m = kq, where k > 1 and q is a probable prime > u. u is expected to be (n^(1/4)+1)^2 for Atkin-Morain ECPP. If m is not successfully factored into desired form, return (False, False, primes). The third component of both cases is a list of primes to do trial division. Algorithm 27 (Atkin-morain ECPP) Step3 """ if era is None: v = min(500000, int(log(m))) era = factor.mpqs.eratosthenes(v) k = 1 q = m for p in era: if p > u: break e, q = arith1.vp(q, p) k *= p ** e if q < u or k == 1: return False, False, era if not prime.millerRabin(q): return False, False, era return k, q, era
def primePowerTest(n): """ This program using Algo. 1.7.5 in Cohen's book judges whether n is of the form p**k with prime p or not. If it is True, then (p,k) will be returned, otherwise (n,0). """ if n & 1: q = n while True: if not prime.primeq(q): a = 2 while prime.spsp(n, a): a += 1 d = gcd.gcd(pow(a,q,q) - a, q) if d == 1 or d == q: return (n, 0) q = d else: p = q break else: p = 2 k, q = arith1.vp(n, p) if q == 1: return (p, k) else: return (n, 0)
def dumas(target, p): """ Return True if target is irreducible, False if reducible or None if undecidable. Dumas's criterion is the following. Let f = \sum a_i X^i be a integer coefficient polynomial with degree n, and p be a prime number. f is irreducible if (I) gcd(n, v(a_n) - v(a_0)) == 1 (where v(m) denotes the number e that p**e divide m but p**(e+1) doesn't) (II) for any i (0 < i < n), (i, v(a_i)) is above the line connecting (0, v(a_0)) and (n, v(a_n)). This criterion includes Eisenstein's case. """ segments = dict((d, arith1.vp(c, p=p)[0]) for (d, c) in target) # (I) degree = target.degree() if abs(gcd.gcd(degree, segments[degree] - segments[0])) != 1: return None # (II) #(segments[degree] - segments[0]) / degree * x + segment[0] < segments[x] slope = segments[degree] - segments[0] if all(slope * x <= degree * (v - segments[0]) for x, v in segments.iteritems()): return True # the criterion doesn't work return None
def primePowerTest(n): """ This program using Algo. 1.7.5 in Cohen's book judges whether n is of the form p**k with prime p or not. If it is True, then (p,k) will be returned, otherwise (n,0). """ if n % 2 == 1: q = n while True: if not prime.primeq(q): a = 2 while prime.spsp(n, a): a += 1 d = gcd.gcd(pow(a,q,q) - a, q) if d == 1 or d == q: return (n, 0) else: q = d else: p = q break else: p = 2 k, q = arith1.vp(n, p) if q == 1: return (p, k) else: return (n, 0)
def _flexible_pow(self, element, index): """ powering by using flexible base method. (Algorithm 1.2.4.1 & 1.2.4.2 of Cohen's book) size is selected by average analystic optimization """ log_n = int(math.log(index, 2)) # Find the proper window size size = 1 pow_size = 1 while log_n > (size + 1) * (size + 2) * pow_size: pow_size <<= 1 size += 1 pow_size <<= 1 # Compute win_lst, sqr_lst b, n = arith1.vp(index, 2) win_lst, sqr_lst = [], [b] while True: m, r = divmod(n, pow_size) win_lst.append(r) if not (m): break b, n = arith1.vp(m, 2) sqr_lst.append(b + size) e = len(win_lst) - 1 f = e # Precomputation sqrs = element pre_table = [element] pow_size >>= 1 for i in range(pow_size - 1): sqrs = self.square(sqrs) pre_table.append(self.mul(pre_table[-1], sqrs)) # Main Loop while f >= 0: if f == e: sol = pre_table[(win_lst[f] - 1) >> 1] else: sol = self.mul(sol, pre_table[(win_lst[f] - 1) >> 1]) for i in range(sqr_lst[f]): sol = self.square(sol) f -= 1 return sol
def _two_pow_ary_pow(self, element, index): """ powering by using left-right 2^size ary method.('size' is the proper integer) (Algorithm 1.2.4 of Cohen's book) size is selected by average analystic optimization """ # Find the proper size log_n = int(math.log(index, 2)) size = 1 pow_size = 1 while log_n > (size + 1) * (size + 2) * pow_size: pow_size <<= 1 size += 1 # Compute win_lst, sqr_lst pow_size <<= 1 s, m = arith1.vp(index, 2) win_lst, sqr_lst = [], [s] while m > pow_size: m, b = divmod(m, pow_size) win_lst.append(b) s, m = arith1.vp(m, 2) sqr_lst.append(s + size) win_lst.append(m) e = len(win_lst) - 1 f = e # Precomputation sqr = self.square(element) pre_table = [element] pow_size = (pow_size // 2) - 1 for i in range(pow_size): pre_table.append(self.mul(pre_table[-1], sqr)) # Main Loop while f >= 0: if f == e: sol = pre_table[(win_lst[f] - 1) >> 1] else: sol = self.mul(sol, pre_table[(win_lst[f] - 1) >> 1]) for i in range(sqr_lst[f]): sol = self.square(sol) f -= 1 return sol
def millerRabin(n, times=20): """ Miller-Rabin pseudo-primality test. Optional second argument times (default to 20) is the number of repetition. The error probability is at most 4**(-times). """ s, t = arith1.vp(n - 1, 2) for _ in range(times): b = bigrandom.randrange(2, n-1) if not spsp(n, b, s, t): return False return True
def millerRabin(n, times=20): """ Miller-Rabin pseudo-primality test. Optional second argument times (default to 20) is the number of repetition. The error probability is at most 4**(-times). """ s, t = arith1.vp(n - 1, 2) for _ in range(times): b = bigrandom.randrange(2, n - 1) if not spsp(n, b, s, t): return False return True
def smallSpsp(n, s=None, t=None): """ 4 spsp tests are sufficient to determine whether an integer less than 10**12 is prime or not. Optional third and fourth argument s and t are the numbers such that n - 1 = 2**s * t and t is odd. """ if s is None or t is None: s, t = arith1.vp(n - 1, 2) for p in (2, 13, 23, 1662803): if not spsp(n, p, s, t): return False return True
def _factor(n, bound=0): """ Trial division factorization for a natural number. Optional second argument is a search bound of primes. If the bound is given and less than the sqaure root of n, result is not proved to be a prime factorization. """ factors = [] if not (n % 2): v2, n = arith1.vp(n, 2) factors.append((2, v2)) m = _calc_bound(n, bound) p = 3 while p <= m: if not (n % p): v, n = arith1.vp(n, p) factors.append((p, v)) m = _calc_bound(n, bound) p += 2 if n > 1: factors.append((n, 1)) return factors
def miller_rabin(n, times=20): """ Miller-Rabin pseudo-primality test. The error probability is at most 4**(-times). Optional second argument times (default to 20) is the number of repetition. The testee 'n' is required to be much bigger than 'times'. """ nminus1 = n - 1 s, t = arith1.vp(nminus1, 2) randrange = bigrandom.randrange return all(spsp(n, randrange(i + 2, nminus1), s, t) for i in range(times))
def _factor(n, bound=0): """ Trial division factorization for a natural number. Optional second argument is a search bound of primes. If the bound is given and less than the sqaure root of n, result is not proved to be a prime factorization. """ factors = [] if not (n & 1): v2, n = arith1.vp(n, 2) factors.append((2, v2)) m = _calc_bound(n, bound) p = 3 while p <= m: if not (n % p): v, n = arith1.vp(n, p) factors.append((p, v)) m = _calc_bound(n, bound) p += 2 if n > 1: factors.append((n, 1)) return factors
def apr(n): """ apr is the main function for Adleman-Pomerance-Rumery primality test. Assuming n has no prime factors less than 32. Assuming n is spsp for several bases. """ L = Status() rb = arith1.floorsqrt(n) + 1 el = TestPrime() while el.et <= rb: el = el.next() plist = el.t.factors.keys() plist.remove(2) L.yet(2) for p in plist: if pow(n, p-1, p*p) != 1: L.done(p) else: L.yet(p) qlist = el.et.factors.keys() qlist.remove(2) J = JacobiSum() for q in qlist: for p in plist: if (q-1) % p != 0: continue if not L.subodd(p, q, n, J): return False k = arith1.vp(q-1, 2)[0] if k == 1: if not L.sub2(q, n): return False elif k == 2: if not L.sub4(q, n, J): return False else: if not L.sub8(q, k, n, J): return False for p in L.yet_keys(): if not L.subrest(p, n, el.et, J): return False r = int(n) for _ in bigrange.range(1, el.t.integer): r = (r*n) % el.et.integer if n % r == 0 and r != 1 and r != n: _log.info("%s divides %s.\n" %(r, n)) return False return True
def apr(n): """ apr is the main function for Adleman-Pomerance-Rumery primality test. Assuming n has no prime factors less than 32. Assuming n is spsp for several bases. """ L = Status() rb = arith1.floorsqrt(n) + 1 el = TestPrime() while el.et <= rb: el = next(el) plist = list(el.t.factors.keys()) plist.remove(2) L.yet(2) for p in plist: if pow(n, p - 1, p * p) != 1: L.done(p) else: L.yet(p) qlist = list(el.et.factors.keys()) qlist.remove(2) J = JacobiSum() for q in qlist: for p in plist: if (q - 1) % p != 0: continue if not L.subodd(p, q, n, J): return False k = arith1.vp(q - 1, 2)[0] if k == 1: if not L.sub2(q, n): return False elif k == 2: if not L.sub4(q, n, J): return False else: if not L.sub8(q, k, n, J): return False for p in L.yet_keys(): if not L.subrest(p, n, el.et, J): return False r = int(n) for _ in bigrange.range(1, el.t.integer): r = (r * n) % el.et.integer if n % r == 0 and r != 1 and r != n: _log.info("%s divides %s.\n" % (r, n)) return False return True
def kronecker(a, b): """ Compute the Kronecker symbol (a/b) using algo 1.4.10 in Cohen's book. """ tab2 = (0, 1, 0, -1, 0, -1, 0, 1) if b == 0: if abs(a) != 1: return 0 if abs(a) == 1: return 1 if a % 2 == 0 and b % 2 == 0: return 0 v, b = arith1.vp(b, 2) if v % 2 == 0: k = 1 else: k = tab2[a & 7] if b < 0: b = -b if a < 0: k = -k while a: v, a = arith1.vp(a, 2) if v % 2 == 1: k *= tab2[b & 7] if a & b & 2: # both a and b are 3 mod 4 k = -k r = abs(a) a = b % r b = r if b > 1: # a and be are not coprime return 0 return k
def kronecker(a, b): """ Compute the Kronecker symbol (a/b) using algo 1.4.10 in Cohen's book. """ tab2 = (0, 1, 0, -1, 0, -1, 0, 1) if b == 0: if abs(a) != 1: return 0 if abs(a) == 1: return 1 if a & 1 == 0 and b & 1 == 0: return 0 v, b = arith1.vp(b, 2) if v & 1 == 0: k = 1 else: k = tab2[a & 7] if b < 0: b = -b if a < 0: k = -k while a: v, a = arith1.vp(a, 2) if v & 1: k *= tab2[b & 7] if a & b & 2: # both a and b are 3 mod 4 k = -k r = abs(a) a = b % r b = r if b > 1: # a and be are not coprime return 0 return k
def miller(n): """ Miller's primality test. This test is valid under GRH. """ s, t = arith1.vp(n - 1, 2) # The O-constant 2 by E.Bach ## 2 log(n) = 2 log_2(n)/log_2(e) = 2 log(2) log_2(n) ## 2 log(2) <= 1.3862943611198906 bound = min(n - 2, 13862943611198906 * arith1.log(n) // 10 ** 16) + 1 _log.info("bound: %d" % bound) for b in range(2, bound): if not spsp(n, b, s, t): return False return True
def miller(n): """ Miller's primality test. This test is valid under GRH. """ s, t = arith1.vp(n - 1, 2) # The O-constant 2 by E.Bach ## ln(n) = log_2(n)/log_2(e) = ln(2) * log_2(n) ## 2 * ln(2) ** 2 <= 0.960906027836404 bound = min(n - 1, 960906027836404 * (arith1.log(n) + 1)**2 // 10**15 + 1) _log.info("bound: %d" % bound) for b in range(2, bound): if not spsp(n, b, s, t): return False return True
def generate(self, target, **options): """ Generate squarefree factors of the target number with their valuations. The method may terminate with yielding (1, 1) to indicate the factorization is incomplete. If a keyword option 'strict' is False (default to True), factorization will stop after the first square factor no matter whether it is squarefree or not. """ strict = options.get('strict', True) options['n'] = target primeseq = self._parse_seq(options) for p in primeseq: if not (target % p): e, target = arith1.vp(target, p) yield p, e if target == 1: break elif e > 1 and not strict: yield 1, 1 break elif trivial_test_ternary(target): # the factor remained is squarefree. yield target, 1 break q, e = factor_misc.primePowerTest(target) if e: yield q, e break sqrt = arith1.issquare(target) if sqrt: if strict: for q, e in self.factor(sqrt, iterator=primeseq): yield q, 2 * e else: yield sqrt, 2 break if p ** 3 > target: # there are no more square factors of target, # thus target is squarefree yield target, 1 break else: # primeseq is exhausted but target has not been proven prime yield 1, 1
def generate(self, target, **options): """ Generate prime factors of the target number with their valuations. The method may terminate with yielding (1, 1) to indicate the factorization is incomplete. """ primeseq = self._parse_seq(options) for p in primeseq: if not (target % p): e, target = arith1.vp(target // p, p, 1) yield p, e if p ** 2 > target: # there are no more factors of target, thus target is a prime yield target, 1 break else: # primeseq is exhausted but target has not been proven prime yield 1, 1
def generate(self, target, **options): """ Generate prime factors of the target number with their valuations. The method may terminate with yielding (1, 1) to indicate the factorization is incomplete. """ primeseq = self._parse_seq(options) for p in primeseq: if not (target % p): e, target = arith1.vp(target // p, p, 1) yield p, e if p**2 > target: # there are no more factors of target, thus target is a prime yield target, 1 break else: # primeseq is exhausted but target has not been proven prime yield 1, 1
def spsp(n, base, s=None, t=None): """ Strong Pseudo-Prime test. Optional third and fourth argument s and t are the numbers such that n-1 = 2**s * t and t is odd. """ if not s or not t: s, t = arith1.vp(n-1, 2) z = pow(base, t, n) if z != 1 and z != n-1: j = 0 while j < s: j += 1 z = pow(z, 2, n) if z == n-1: break else: return False return True
def spsp(n, base, s=None, t=None): """ Strong Pseudo-Prime test. Optional third and fourth argument s and t are the numbers such that n-1 = 2**s * t and t is odd. """ if s is None or t is None: s, t = arith1.vp(n - 1, 2) z = pow(base, t, n) if z != 1 and z != n - 1: j = 0 while j < s: j += 1 z = pow(z, 2, n) if z == n - 1: break else: return False return True
def general_lift(self): """ Continue lifting. f == a1*a2*...*ar (mod p*q) Update ai's, bi's, ui's, yi's and zi's. Then, update q with p*q. """ j = arith1.vp(self.q, self.p)[0] if len(self.dis) > j: mini_target = self.dis[j] else: mini_target = the_zero self.ais[-2], self.ais[-1] = self.ais[-1], self.ais[0] self.bis[-2], self.bis[-1] = self.bis[-1], self.bis[0] aj, bj = [], [] for i in range(self.r - 1): yi, zi = self.yis[i], self.zis[i] dividend = self.ais[-2][i] * yi + self.bis[-2][i] * zi - self.uis[i] v_j = dividend.scalar_exact_division(self.p) if j == 2: self.uis[i] = mini_target - v_j - yi * zi else: self.uis[i] = mini_target - v_j - (yi * zi).scalar_mul( self.p**(j - 2)) self.yis[i], self.zis[i] = self._solve_yz(i) aj.append(self.ais[-1][i] + self.zis[i].scalar_mul(self.q)) bj.append(self.bis[-1][i] + self.yis[i].scalar_mul(self.q)) self._assertEqualModulo(self.f, arith1.product(aj) * bj[i], self.q * self.p, (i, j)) mini_target = self.yis[i] aj.append(bj[-1]) self.q *= self.p for i in range(self.r - 1): self._assertEqualModulo(self.f, arith1.product(aj[:i + 1]) * bj[i], self.q, "f != a%d * b%d" % (i, i)) self._assertEqualModulo(self.gis[i], aj[i], self.p) self._assertEqualModulo(self.f, arith1.product(aj), self.q) self.ais[0] = tuple(aj) self.bis[0] = tuple(bj)
def _ones_and_zero_pows(self, element, index): """ powering by 'ones' window method. """ # Compute win_lst, sqr_lst b, n = arith1.vp(index, 2) win_lst, sqr_lst = [], [b] maxa = 1 while True: ones = 0 while n & 1: n >>= 1 ones += 1 win_lst.append(ones) if maxa < ones: maxa = ones if n == 0: break zeros = 0 while not (n & 1): n >>= 1 zeros += 1 sqr_lst.append(zeros + win_lst[-1]) e = len(win_lst) - 1 f = e # Precomputation sqrs = element pre_table = [element] for i in range(maxa - 1): sqrs = self.square(sqrs) pre_table.append(self.mul(pre_table[-1], sqrs)) # Main Loop while f >= 0: if f == e: sol = pre_table[win_lst[f] - 1] else: sol = self.mul(sol, pre_table[win_lst[f] - 1]) for i in range(sqr_lst[f]): sol = self.square(sol) f -= 1 return sol
def TonelliShanks(self, element): """ Return square root of element if exist. assume that characteristic have to be more than three. """ if self.char == 2: return self.sqrt(element) # should be error if self.Legendre(element) == -1: raise ValueError("There is no solution") # symbol and code reference from Cohen, CCANT 1.5.1 (e, q) = arith1.vp(card(self) - 1, 2) a = element n = self.createElement(self.char + 1) while self.Legendre(n) != -1: n = self.createElement( bigrandom.randrange(self.char + 1, card(self))) # field maybe large y = z = n**q r = e x = a**((q - 1) // 2) b = a * (x**2) x = a * x while True: if b == self.one: return x m = 1 while m < r: if b**(2**m) == self.one: break m = m + 1 if m == r: break t = y**(2**(r - m - 1)) y = t**2 r = m x = x * t b = b * y raise ValueError("There is no solution")
def subrest(self, p, n, et, J, ub=200): if p == 2: q = 5 while q < 2*ub + 5: q += 2 if not _isprime(q) or et % q == 0: continue if n % q == 0: _log.info("%s divides %s.\n" % (q, n)) return False k = arith1.vp(q-1, 2)[0] if k == 1: if n % 4 == 1 and not self.sub2(q, n): return False elif k == 2: if not self.sub4(q, n, J): return False else: if not self.sub8(q, k, n, J): return False if self.isDone(p): return True else: raise ImplementLimit("limit") else: step = p*2 q = 1 while q < step*ub + 1: q += step if not _isprime(q) or et % q == 0: continue if n % q == 0: _log.info("%s divides %s.\n" % (q, n)) return False if not self.subodd(p, q, n, J): return False if self.isDone(p): return True else: raise ImplementLimit("limit")
def subrest(self, p, n, et, J, ub=200): if p == 2: q = 5 while q < 2 * ub + 5: q += 2 if not _isprime(q) or et % q == 0: continue if n % q == 0: _log.info("%s divides %s.\n" % (q, n)) return False k = arith1.vp(q - 1, 2)[0] if k == 1: if n & 3 == 1 and not self.sub2(q, n): return False elif k == 2: if not self.sub4(q, n, J): return False else: if not self.sub8(q, k, n, J): return False if self.isDone(p): return True else: raise ImplementLimit("limit") else: step = p * 2 q = 1 while q < step * ub + 1: q += step if not _isprime(q) or et % q == 0: continue if n % q == 0: _log.info("%s divides %s.\n" % (q, n)) return False if not self.subodd(p, q, n, J): return False if self.isDone(p): return True else: raise ImplementLimit("limit")
def general_lift(self): """ Continue lifting. f == a1*a2*...*ar (mod p*q) Update ai's, bi's, ui's, yi's and zi's. Then, update q with p*q. """ j = arith1.vp(self.q, self.p)[0] if len(self.dis) > j: mini_target = self.dis[j] else: mini_target = the_zero self.ais[-2], self.ais[-1] = self.ais[-1], self.ais[0] self.bis[-2], self.bis[-1] = self.bis[-1], self.bis[0] aj, bj = [], [] for i in xrange(self.r - 1): yi, zi = self.yis[i], self.zis[i] dividend = self.ais[-2][i]*yi + self.bis[-2][i]*zi - self.uis[i] v_j = dividend.scalar_exact_division(self.p) if j == 2: self.uis[i] = mini_target - v_j - yi*zi else: self.uis[i] = mini_target - v_j - (yi*zi).scalar_mul(self.p**(j - 2)) self.yis[i], self.zis[i] = self._solve_yz(i) aj.append(self.ais[-1][i] + self.zis[i].scalar_mul(self.q)) bj.append(self.bis[-1][i] + self.yis[i].scalar_mul(self.q)) self._assertEqualModulo(self.f, arith1.product(aj)*bj[i], self.q*self.p, (i, j)) mini_target = self.yis[i] aj.append(bj[-1]) self.q *= self.p for i in range(self.r - 1): self._assertEqualModulo(self.f, arith1.product(aj[:i+1])*bj[i], self.q, "f != a%d * b%d" % (i, i)) self._assertEqualModulo(self.gis[i], aj[i], self.p) self._assertEqualModulo(self.f, arith1.product(aj), self.q) self.ais[0] = tuple(aj) self.bis[0] = tuple(bj)
def spsp(n, base, s=None, t=None): """ Strong PSeudo-Prime test. A composite can pass this test with probability at most 1/4. Optional third and fourth argument s and t are the numbers such that n-1 = 2**s * t and t is odd. """ nminus1 = n - 1 if s is None or t is None: s, t = arith1.vp(nminus1, 2) z = pow(base, t, n) if z != 1 and z != nminus1: j = 0 while j < s: j += 1 z = pow(z, 2, n) if z == nminus1: break else: return False return True
def TonelliShanks(self, element): """ Return square root of element if exist. assume that characteristic have to be more than three. """ if self.char == 2: return self.sqrt(element) # should be error if self.Legendre(element) == -1: raise ValueError("There is no solution") # symbol and code reference from Cohen, CCANT 1.5.1 (e, q) = arith1.vp(card(self)-1, 2) a = element n = self.createElement(self.char+1) while self.Legendre(n) != -1: n = self.random_element(2, card(self)) # field maybe large y = z = n ** q r = e x = a ** ((q-1) // 2) b = a * (x ** 2) x = a * x while True: if b == self.one: return x m = 1 while m < r: if b ** (2 ** m) == self.one: break m = m+1 if m == r: break t = y ** (2 ** (r-m-1)) y = t ** 2 r = m x = x * t b = b * y raise ValueError("There is no solution")
def _prepare_squarefactors(disc): """ Return a list of square factors of disc (=discriminant). PRECOND: d is integer """ squarefactors = [] if disc < 0: fund_disc, absd = -1, -disc else: fund_disc, absd = 1, disc v2, absd = arith1.vp(absd, 2) if squarefree.trivial_test_ternary(absd): fund_disc *= absd else: for p, e in factor_misc.FactoredInteger(absd): if e > 1: squareness, oddity = divmod(e, 2) squarefactors.append((p, squareness)) if oddity: fund_disc *= p else: fund_disc *= p if fund_disc & 3 == 1: if v2 & 1: squareness, oddity = divmod(v2, 2) squarefactors.append((2, squareness)) if oddity: fund_disc *= 2 else: squarefactors.append((2, v2 >> 1)) else: # fund_disc & 3 == 3 assert v2 >= 2 fund_disc *= 4 if v2 > 2: squarefactors.append((2, (v2 - 2) >> 1)) return squarefactors
def _prepare_squarefactors(disc): """ Return a list of square factors of disc (=discriminant). PRECOND: d is integer """ squarefactors = [] if disc < 0: fund_disc, absd = -1, -disc else: fund_disc, absd = 1, disc v2, absd = arith1.vp(absd, 2) if squarefree.trivial_test_ternary(absd): fund_disc *= absd else: for p, e in factor_misc.FactoredInteger(absd): if e > 1: squareness, oddity = divmod(e, 2) squarefactors.append((p, squareness)) if oddity: fund_disc *= p else: fund_disc *= p if fund_disc % 4 == 1: if v2 % 2: squareness, oddity = divmod(v2, 2) squarefactors.append((2, squareness)) if oddity: fund_disc *= 2 else: squarefactors.append((2, v2 // 2)) else: # fund_disc % 4 == 3 assert v2 >= 2 fund_disc *= 4 if v2 > 2: squarefactors.append((2, (v2 - 2) // 2)) return squarefactors
def testVp(self): self.assertEqual((3, 1), arith1.vp(8, 2)) self.assertEqual((0, 10), arith1.vp(10, 3)) self.assertEqual((1, 10), arith1.vp(10, 3, 1)) self.assertEqual((3, 10), arith1.vp(270, 3))
def mpqs(n, s=0, f=0, m=0): """ This is main function of MPQS. Arguments are (composite_number, sieve_range, factorbase_size, multiplier) You must input composite_number at least. """ T = time.time() M = MPQS(n, s, f, m) _log.info("Sieve range is [ %d , %d ] , Factorbase size = %d , Max Factorbase %d" % (M.move_range[0], M.move_range[-1], len(M.FB), M.maxFB)) M.get_vector() N = M.number // M.multiplier V = Elimination(M.smooth) A = V.gaussian() _log.info("Found %d linerly dependent relations" % len(A)) answerX_Y = [] N_prime_factors = [] N_factors = [] output = [] for i in A: B = V.history[i].keys() X = 1 Y = 1 for j in B: X *= M.smooth[j][1][0] Y *= M.smooth[j][1][1] Y = Y % M.number X = sqrt_modn(X, M.number) if X != Y: answerX_Y.append(X-Y) NN = 1 for k in answerX_Y: factor = gcd.gcd(k, N) if factor not in N_factors and factor != 1 and factor != N \ and factor not in N_prime_factors: if prime.primeq(factor): NN = NN*factor N_prime_factors.append(factor) else: N_factors.append(factor) _log.info("Total time = %f sec" % (time.time() - T)) if NN == N: _log.debug("Factored completely!") N_prime_factors.sort() for p in N_prime_factors: N = N // p i = arith1.vp(N, p, 1)[0] output.append((p, i)) return output elif NN != 1: f = N // NN if prime.primeq(f): N_prime_factors.append(f) _log.debug("Factored completely !") N_prime_factors.sort() for p in N_prime_factors: N = N // p i = arith1.vp(N, p, 1)[0] output.append((p, i)) return output for F in N_factors: for FF in N_factors: if F != FF: Q = gcd.gcd(F, FF) if prime.primeq(Q) and Q not in N_prime_factors: N_prime_factors.append(Q) NN = NN*Q N_prime_factors.sort() for P in N_prime_factors: i, N = arith1.vp(N, P) output.append((P, i)) if N == 1: _log.debug("Factored completely!! ") return output for F in N_factors: g = gcd.gcd(N, F) if prime.primeq(g): N_prime_factors.append(g) N = N // g i = arith1.vp(N, g, 1)[0] output.append((g, i)) if N == 1: _log.debug("Factored completely !! ") return output elif prime.primeq(N): output.append((N, 1)) _log.debug("Factored completely!!! ") return output else: N_factors.sort() _log.error("Sorry, not factored completely") return output, N_factors