def nrootb(self, index, bits): """ Approximately compute the inverse of index-th root and return it. b stands for binary search. It should be used for: 1 <= bits <= ceil(lg(8 * index)) """ # lgy - 1 < lg self <= lgy lgy = arith1.log(self.abscissa) + self.exponent if DyadicRational(lgy, 1) < self: lgy += 1 assert DyadicRational(lgy - 1, 1) < self <= DyadicRational(lgy, 1) lgr = -lgy // index ebound = 66 * (2 * index + 1) bound = arith1.log(ebound) if 2**bound < ebound: bound += 1 rbound = DyadicRational(-10, 993) one = DyadicRational(0, 1) z = DyadicRational(lgr, 1) + DyadicRational(lgr - 1, 1) for j in range(1, bits): r = (z.power(index, bound) * self.trunc(bound)).trunc(bound) if r <= rbound: z += DyadicRational(lgr - j - 1, 1) if r > one: z = z.sub(DyadicRational(lgr - j - 1, 1)) return z
def __init__(self, n): """ Create a Bounds object from target number n. """ if _arith1.log(n, 10) < 20: self.first = 1000 elif _arith1.log(n, 10) < 25: self.first = 10000 else: self.first = 100000 self.second = self.first * 50
def zassenhaus(f): """ zassenhaus(f) -> list of factors of f. Factor a squarefree monic integer coefficient polynomial f with Berlekamp-Zassenhaus method. """ # keep leading coefficient lf = f.leading_coefficient() # p-adic factorization p, fp_factors = padic_factorization(f) if len(fp_factors) == 1: return [f] # purge leading coefficient from factors for i, g in enumerate(fp_factors): if g.degree() == 0: del fp_factors[i] break # lift to Mignotte bound blm = upper_bound_of_coefficient(f) bound = p**(arith1.log(2 * blm, p) + 1) # Hensel lifting lf_inv_modq = intresidue.IntegerResidueClass(lf, bound).inverse() fq = f.coefficients_map(lambda c: (lf_inv_modq * c).minimumAbsolute()) # fq is monic fq_factors, q = hensel.lift_upto(fq, fp_factors, p, bound) return brute_force_search(f, fq_factors, bound)
def _squarefree_decomp(p, field, base_multiply): """ decompose (p) as (p)= A_1 (A_2)^2 (A_3)^2 ..., where each A_i is squarefree """ #CCANT Algo 6.2.9 step 2-5 n = field.degree int_basis = field.integer_ring() # step 2 log_n_p = arith1.log(n, p) q = p ** log_n_p if q < n: q *= p base_p = _kernel_of_qpow(int_basis, q, p, field) I_p_over_pO = base_p # step 3 K_lst = [I_p_over_pO] while K_lst[-1]: # K[-1] is zero matrix in F_p K_lst.append(_mul_mod_pO(K_lst[0], K_lst[-1], base_multiply)) i = len(K_lst) # step 4 J_lst = [K_lst[0]] for j in range(1, i): J_lst.append(_div_mod_pO(K_lst[j], K_lst[j - 1], base_multiply, p)) # step 5 H_lst = [] for j in range(i - 1): H_lst.append(_div_mod_pO(J_lst[j], J_lst[j + 1], base_multiply, p)) H_lst.append(J_lst[-1]) return H_lst
def zassenhaus(f): """ zassenhaus(f) -> list of factors of f. Factor a squarefree monic integer coefficient polynomial f with Berlekamp-Zassenhaus method. """ # keep leading coefficient lf = f.leading_coefficient() # p-adic factorization p, fp_factors = padic_factorization(f) if len(fp_factors) == 1: return [f] # purge leading coefficient from factors for i,g in enumerate(fp_factors): if g.degree() == 0: del fp_factors[i] break # lift to Mignotte bound blm = upper_bound_of_coefficient(f) bound = p**(arith1.log(2*blm,p)+1) # Hensel lifting lf_inv_modq = intresidue.IntegerResidueClass(lf, bound).inverse() fq = f.coefficients_map(lambda c: (lf_inv_modq*c).minimumAbsolute()) # fq is monic fq_factors, q = hensel.lift_upto(fq, fp_factors, p, bound) return brute_force_search(f, fq_factors, bound)
def FFT(f, bound): """ Fast Fourier Transform. This function returns the result of valuations of f by fast fourier transform against number of bound different values. """ count = 1 << (bound >> 1) mod = count + 1 if isinstance(f, ArrayPoly): coefficients = f.coefficients[:] else: coefficients = f[:] coefficients.extend([0] * (bound - len(coefficients))) shuf_coefficients = perfect_shuffle(coefficients) shuf_coefficients = [min_abs_mod(i, mod) for i in shuf_coefficients] bound_log = arith1.log(bound, 2) for i in range(1, bound_log + 1): m = 1 << i wm = count wm = wm % mod w = 1 m1 = m >> 1 for j in range(m1): for k in range(j, bound, m): m2 = k + m1 plus = shuf_coefficients[k] minus = w * shuf_coefficients[m2] shuf_coefficients[k] = (plus + minus) % mod shuf_coefficients[m2] = (plus - minus) % mod w = w * wm shuf_coefficietns = [min_abs_mod(i, mod) for i in shuf_coefficients] count = arith1.floorsqrt(count) return shuf_coefficients
def nroot(self, index, bits): lg8k = arith1.log(8 * index) if 2**lg8k < 8 * index: lg8k += 1 if bits <= lg8k: return self.nrootb(index, bits) else: return self.nrootn(index, bits)
def ceillog(n, base=2): """ Return ceiling of log(n, 2) """ floor = arith1.log(n, base) if base**floor == n: return floor else: return floor + 1
def __init__(self, n, sieverange, factorbase): self.number = n self.sqrt_n = int(math.sqrt(n)) for i in [2,3,5,7,11,17,19]: if n % i == 0: raise ValueError("This number is divided by %d" % i) self.digit = arith1.log(self.number, 10) + 1 self.Srange = sieverange self.FBN = factorbase self.move_range = range(self.sqrt_n-self.Srange, self.sqrt_n+self.Srange+1) i = 0 k = 0 factor_base = [-1] FB_log = [0] while True: ii = primes_table[i] if arith1.legendre(self.number, ii) == 1: factor_base.append(ii) FB_log.append(primes_log_table[i]) k += 1 i += 1 if k == self.FBN: break else: i += 1 self.FB = factor_base self.FB_log = FB_log self.maxFB = factor_base[-1] N_sqrt_list = [] for i in self.FB: if i != 2 and i != -1: e = int(math.log(2*self.Srange, i)) N_sqrt_modp = sqroot_power(self.number, i, e) N_sqrt_list.append(N_sqrt_modp) self.solution = N_sqrt_list #This is square roots of N on Z/pZ, p in factor base. poly_table = [] log_poly = [] minus_val = [] for j in self.move_range: jj = (j**2)-self.number if jj < 0: jj = -jj minus_val.append(j-self.sqrt_n+self.Srange) elif jj == 0: jj = 1 lj = int((math.log(jj)*30)*0.97) # 0.97 is an erroe poly_table.append(jj) log_poly.append(lj) self.poly_table = poly_table # This is Q(x) value , x in [-M+sqrt_n,M+sqrt_n]. self.log_poly = log_poly # This is log(Q(x)) value. self.minus_check = minus_val # This is "x" that Q(x) is minus value.
def __init__(self, exponent, abscissa): self.exponent = exponent self.abscissa = abscissa if self.abscissa: while not (self.abscissa & 1): self.exponent += 1 self.abscissa >>= 1 # f - 1 <= lg abscissa < f self.log_absc = arith1.log(self.abscissa) if 2**self.log_absc <= self.abscissa: self.log_absc += 1
def perfect_power_detection(n): """ Return (m, k) if n = m**k. Note that k is the smallest possible and m still can be a perfect power; if n is not a perfect power, it returns (n, 1). """ f = arith1.log(n) + 1 y = DyadicRational(0, n).nroot(1, 3 + (f - 1) // 2 + 1) for p in prime.generator_eratosthenes(f): x = _is_kth_power(n, p, y, f) if x != 0: return (x, p) return (n, 1)
def div(self, divisor, bits): """ Divide a dyadic rational by a positive integer divisor within precision bits and return the result. """ # lgd - 1 < lg divisor <= lgd lgd = arith1.log(divisor) if 2**lgd < divisor: lgd += 1 offset = self.log_absc - lgd - bits if offset < 0: abscissa = (self.abscissa << -offset) // divisor else: abscissa = self.abscissa // (divisor << offset) return DyadicRational(self.exponent + offset, abscissa)
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 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 _p_maximal(p, e, minpoly_coeff): """ Return p-maximal basis with some related informations. The arguments: p: the prime e: the exponent minpoly_coeff: (intefer) list of coefficients of the minimal polynomial of theta """ # Apply the Dedekind criterion finished, omega = Dedekind(minpoly_coeff, p, e) if finished: _log.info("Dedekind(%d)" % p) return omega # main loop to construct p-maximal order minpoly = uniutil.polynomial(enumerate(minpoly_coeff), Z) theminpoly = minpoly.to_field_polynomial() n = theminpoly.degree() q = p ** (arith1.log(n, p) + 1) while True: # Ip: radical of pO # Ip = <alpha>, l = dim Ip/pO (essential part of Ip) alpha, l = _p_radical(omega, p, q, minpoly, n) # instead of step 9 big matrix, # Kida's LN section 2.2 # Up = {x in Ip | xIp \subset pIp} = <zeta> + p<omega> zeta = _p_module(alpha, l, p, theminpoly) if zeta.rank == 0: # no new basis is found break # new order # 1/p Up = 1/p<zeta> + <omega> omega2 = zeta / p + omega if all(o1 == o2 for o1, o2 in zip(omega.basis, omega2.basis)): break omega = omega2 # now <omega> is p-maximal. return omega
def _power_sign(n, x, k, f): """ Return the sign of n - x**k. All of n, x, and k are positive integers. """ lg8k = arith1.log(k) + 3 if 2**lg8k < 8 * k: lg8k += 1 bound = 1 drn = DyadicRational(0, n) while True: r = DyadicRational(0, x).power(k, bound + lg8k) if drn < r: return -1 elif r * (DyadicRational(0, 1) + DyadicRational(-bound, 1)) <= drn: return 1 elif bound >= f: return 0 bound = min(2 * bound, f)
def randrange(start, stop=None, step=1): """ Choose a random item from range([start,] stop[, step]). (Return long integer.) see random.randrange """ if stop is None: if abs(start) < sys.maxint: return _random.randrange(start) elif abs(stop - start) < sys.maxint: return _random.randrange(start, stop, step) negative_step = False if stop is None: start, stop = 0, start if step < 0: start, stop, step = -start, -stop, -step negative_step = True _validate_for_randrange(start, stop, step) if (stop - start) % step != 0: v = (stop - start)//step + 1 else: v = (stop - start)//step bitlength = arith1.log(v, base=2) if v != 2**bitlength: bitlength += 1 randomized = v + 1 # to pass the first test while randomized >= v: randomized = 0 for i in range(bitlength): if random() < 0.5: randomized += 1 << i result = randomized * step + start if negative_step: return -result return result
def randrange(start, stop=None, step=1): """ Choose a random item from range([start,] stop[, step]). (Return long integer.) see random.randrange """ if stop is None: if abs(start) < sys.maxint: return _random.randrange(start) elif abs(stop - start) < sys.maxint: return _random.randrange(start, stop, step) negative_step = False if stop is None: start, stop = 0, start if step < 0: start, stop, step = -start, -stop, -step negative_step = True _validate_for_randrange(start, stop, step) if (stop - start) % step != 0: v = (stop - start) // step + 1 else: v = (stop - start) // step bitlength = arith1.log(v, base=2) if v != 2**bitlength: bitlength += 1 randomized = v + 1 # to pass the first test while randomized >= v: randomized = 0 for i in range(bitlength): if random() < 0.5: randomized += 1 << i result = randomized * step + start if negative_step: return -result return result
def nrootn(self, index, bits): """ Approximately compute the inverse of index-th root and return it. n stands for Newton's method. It should be used for: bits > ceil(lg(8 * index)) """ lgk = arith1.log(index) if 2**lgk < index: lgk += 1 lg2k = lgk + 1 lg8k = lgk + 3 smallbits = lg2k + (bits - lg2k - 1) // 2 + 1 bound = 2 * smallbits + 4 - lgk if smallbits <= lg8k: z = self.nrootb(index, smallbits) else: z = self.nrootn(index, smallbits) r2 = z.trunc(bound).mul(index + 1) r3 = (z.power(index + 1, bound) * self.trunc(bound)).trunc(bound) r4 = r2.sub(r3).div(index, bound) return r4
def __init__(self, n, sieverange=0, factorbase=0, multiplier=0): self.number = n _log.info("The number is %d MPQS starting" % n) if prime.primeq(self.number): raise ValueError("This number is Prime Number") for i in [2,3,5,7,11,13]: if n % i == 0: raise ValueError("This number is divided by %d" % i) self.sievingtime = 0 self.coefficienttime = 0 self.d_list = [] self.a_list = [] self.b_list = [] #Decide prameters for each digits self.digit = arith1.log(self.number, 10) + 1 if sieverange != 0: self.Srange = sieverange if factorbase != 0: self.FBN = factorbase elif self.digit < 9: self.FBN = parameters_for_mpqs[0][1] else: self.FBN = parameters_for_mpqs[self.digit-9][1] elif factorbase != 0: self.FBN = factorbase if self.digit < 9: self.Srange = parameters_for_mpqs[0][0] else: self.Srange = parameters_for_mpqs[self.digit-9][0] elif self.digit < 9: self.Srange = parameters_for_mpqs[0][0] self.FBN = parameters_for_mpqs[0][1] elif self.digit > 53: self.Srange = parameters_for_mpqs[44][0] self.FBN = parameters_for_mpqs[44][1] else: self.Srange = parameters_for_mpqs[self.digit-9][0] self.FBN = parameters_for_mpqs[self.digit-9][1] self.move_range = range(-self.Srange, self.Srange+1) # Decide k such that k*n = 1 (mod4) and k*n has many factor base if multiplier == 0: self.sqrt_state = [] for i in [3,5,7,11,13]: s = arith1.legendre(self.number, i) self.sqrt_state.append(s) index8 = (self.number % 8) // 2 j = 0 while self.sqrt_state != prime_8[index8][j][1]: j += 1 k = prime_8[index8][j][0] else: if n % 4 == 1: k = 1 else: if multiplier == 1: raise ValueError("This number is 3 mod 4 ") else: k = multiplier self.number = k*self.number self.multiplier = k _log.info("%d - digits Number" % self.digit) _log.info("Multiplier is %d" % self.multiplier) # Table of (log p) , p in FB i = 0 k = 0 factor_base = [-1] FB_log = [0] while k < self.FBN: ii = primes_table[i] if arith1.legendre(self.number,ii) == 1: factor_base.append(ii) FB_log.append(primes_log_table[i]) k += 1 i += 1 self.FB = factor_base self.FB_log = FB_log self.maxFB = factor_base[-1] # Solve x^2 = n (mod p^e) N_sqrt_list = [] for i in self.FB: if i != 2 and i != -1: e = int(math.log(2*self.Srange, i)) N_sqrt_modp = sqroot_power(self.number, i, e) N_sqrt_list.append(N_sqrt_modp) self.Nsqrt = N_sqrt_list
def affine_multiple_method(lhs, field): """ Find and return a root of the equation lhs = 0 by brute force search in the given field. If there is no root in the field, ValueError is raised. The first argument lhs is a univariate polynomial with coefficients in a finite field. The second argument field is an extension field of the field of coefficients of lhs. Affine multiple A(X) is $\sum_{i=0}^{n} a_i X^{q^i} - a$ for some a_i's and a in the coefficient field of lhs, which is a multiple of the lhs. """ polynomial_ring = lhs.getRing() coeff_field = lhs.getCoefficientRing() q = card(coeff_field) n = lhs.degree() # residues = [x, x^q, x^{q^2}, ..., x^{q^{n-1}}] residues = [lhs.mod(polynomial_ring.one.term_mul((1, 1)))] # x for i in range(1, n): residues.append(pow(residues[-1], q, lhs)) # x^{q^i} # find a linear relation among residues and a constant coeff_matrix = matrix.createMatrix(n, n, [coeff_field.zero] * (n**2), coeff_field) for j, residue in enumerate(residues): for i in range(residue.degree() + 1): coeff_matrix[i + 1, j + 1] = residue[i] constant_components = [coeff_field.one] + [coeff_field.zero] * (n - 1) constant_vector = vector.Vector(constant_components) try: relation_vector, kernel = coeff_matrix.solve(constant_vector) for j in range(n, 0, -1): if relation_vector[j]: constant = relation_vector[j].inverse() relation = [constant * c for c in relation_vector] break except matrix.NoInverseImage: kernel_matrix = coeff_matrix.kernel() relation_vector = kernel_matrix[1] assert type(relation_vector) is vector.Vector for j in range(n, 0, -1): if relation_vector[j]: normalizer = relation_vector[j].inverse() relation = [normalizer * c for c in relation_vector] constant = coeff_field.zero break # define L(X) = A(X) + constant coeffs = {} for i, relation_i in enumerate(relation): coeffs[q**i] = relation_i linearized = uniutil.polynomial(coeffs, coeff_field) # Fq basis [1, X, ..., X^{s-1}] qbasis = [1] root = field.createElement(field.char) s = arith1.log(card(field), q) qbasis += [root**i for i in range(1, s)] # represent L as Matrix lmat = matrix.createMatrix(s, s, field.basefield) for j, base in enumerate(qbasis): imagei = linearized(base) if imagei.getRing() == field.basefield: lmat[1, j + 1] = imagei else: for i, coeff in imagei.rep.iterterms(): if coeff: lmat[i + 1, j + 1] = coeff # solve L(X) = the constant constant_components = [constant] + [coeff_field.zero] * (s - 1) constant_vector = vector.Vector(constant_components) solution, kernel = lmat.solve(constant_vector) assert lmat * solution == constant_vector solutions = [solution] for v in kernel: for i in range(card(field.basefield)): solutions.append(solution + i * v) # roots of A(X) contains the solutions of lhs = 0 for t in bigrange.multirange([(card(field.basefield), )] * len(kernel)): affine_root_vector = solution for i, ti in enumerate(t): affine_root_vector += ti * kernel[i] affine_root = field.zero for i, ai in enumerate(affine_root_vector): affine_root += ai * qbasis[i] if not lhs(affine_root): return affine_root raise ValueError("no root found")
def affine_multiple_method(lhs, field): """ Find and return a root of the equation lhs = 0 by brute force search in the given field. If there is no root in the field, ValueError is raised. The first argument lhs is a univariate polynomial with coefficients in a finite field. The second argument field is an extension field of the field of coefficients of lhs. Affine multiple A(X) is $\sum_{i=0}^{n} a_i X^{q^i} - a$ for some a_i's and a in the coefficient field of lhs, which is a multiple of the lhs. """ polynomial_ring = lhs.getRing() coeff_field = lhs.getCoefficientRing() q = card(coeff_field) n = lhs.degree() # residues = [x, x^q, x^{q^2}, ..., x^{q^{n-1}}] residues = [lhs.mod(polynomial_ring.one.term_mul((1, 1)))] # x for i in range(1, n): residues.append(pow(residues[-1], q, lhs)) # x^{q^i} # find a linear relation among residues and a constant coeff_matrix = matrix.createMatrix(n, n, [coeff_field.zero] * (n**2), coeff_field) for j, residue in enumerate(residues): for i in range(residue.degree() + 1): coeff_matrix[i + 1, j + 1] = residue[i] constant_components = [coeff_field.one] + [coeff_field.zero] * (n - 1) constant_vector = vector.Vector(constant_components) try: relation_vector, kernel = coeff_matrix.solve(constant_vector) for j in range(n, 0, -1): if relation_vector[j]: constant = relation_vector[j].inverse() relation = [constant * c for c in relation_vector] break except matrix.NoInverseImage: kernel_matrix = coeff_matrix.kernel() relation_vector = kernel_matrix[1] assert type(relation_vector) is vector.Vector for j in range(n, 0, -1): if relation_vector[j]: normalizer = relation_vector[j].inverse() relation = [normalizer * c for c in relation_vector] constant = coeff_field.zero break # define L(X) = A(X) + constant coeffs = {} for i, relation_i in enumerate(relation): coeffs[q**i] = relation_i linearized = uniutil.polynomial(coeffs, coeff_field) # Fq basis [1, X, ..., X^{s-1}] qbasis = [1] root = field.createElement(field.char) s = arith1.log(card(field), q) qbasis += [root**i for i in range(1, s)] # represent L as Matrix lmat = matrix.createMatrix(s, s, field.basefield) for j, base in enumerate(qbasis): imagei = linearized(base) if imagei.getRing() == field.basefield: lmat[1, j + 1] = imagei else: for i, coeff in imagei.rep.iterterms(): if coeff: lmat[i + 1, j + 1] = coeff # solve L(X) = the constant constant_components = [constant] + [coeff_field.zero] * (s - 1) constant_vector = vector.Vector(constant_components) solution, kernel = lmat.solve(constant_vector) assert lmat * solution == constant_vector solutions = [solution] for v in kernel: for i in range(card(field.basefield)): solutions.append(solution + i * v) # roots of A(X) contains the solutions of lhs = 0 for t in bigrange.multirange([(card(field.basefield),)] * len(kernel)): affine_root_vector = solution for i, ti in enumerate(t): affine_root_vector += ti * kernel[i] affine_root = field.zero for i, ai in enumerate(affine_root_vector): affine_root += ai * qbasis[i] if not lhs(affine_root): return affine_root raise ValueError("no root found")
def testLog(self): self.assertEqual(3, arith1.log(8, 2)) self.assertEqual(3, arith1.log(15, 2)) self.assertEqual(3, arith1.log(1000, 10)) self.assertEqual(9, arith1.log(1000000001, 10)) self.assertTrue(10**arith1.log(1000000001, 10) <= 1000000001)