def find_combination(f, d, factors, q): """ find_combination(f, d, factors, q) -> g, list Find a combination of d factors which divides f (or its complement). The returned values are: the product g of the combination and a list consisting of the combination itself. If there is no combination, return (0,[]). """ lf = f.leading_coefficient() ZqZX = poly_ring.PolynomialRing( intresidue.IntegerResidueClassRing.getInstance(q)) if d == 1: for g in factors: product = minimum_absolute_injection(ZqZX.createElement(lf*g)) if divisibility_test(lf*f, product): return (product.primitive_part(), [g]) else: for idx in combinatorial.combinationIndexGenerator(len(factors), d): picked = [factors[i] for i in idx] product = lf * arith1.product(picked) product = minimum_absolute_injection(ZqZX.createElement(product)) if divisibility_test(lf*f, product): return (product.primitive_part(), picked) return 0, [] # nothing found
def padic_lift_list(f, factors, p, q): """ padicLift(f, factors, p, q) -> lifted_factors Find a lifted integer coefficient polynomials such that: f = G1*G2*...*Gm (mod q*p), Gi = gi (mod q), from f and gi's of integer coefficient polynomials such that: f = g1*g2*...*gm (mod q), gi's are pairwise coprime with positive integers p dividing q. """ ZpZx = poly_ring.PolynomialRing( intresidue.IntegerResidueClassRing.getInstance(p)) gg = arith1.product(factors) h = ZpZx.createElement([(d, c // q) for (d, c) in (f - gg).iterterms()]) lifted = [] for g in factors: gg = gg.pseudo_floordiv(g) g_mod = ZpZx.createElement(g) if gg.degree() == 0: break u, v, w = extgcdp(g, gg, p) if w.degree() > 0: raise ValueError("factors must be pairwise coprime.") v_mod = ZpZx.createElement(v) t = v_mod * h // g_mod lifted.append(g + minimum_absolute_injection(v_mod * h - g_mod * t) * q) u_mod = ZpZx.createElement(u) gg_mod = ZpZx.createElement(gg) h = u_mod * h + gg_mod * t lifted.append(g + minimum_absolute_injection(h) * q) return lifted
def padic_lift_list(f, factors, p, q): """ padicLift(f, factors, p, q) -> lifted_factors Find a lifted integer coefficient polynomials such that: f = G1*G2*...*Gm (mod q*p), Gi = gi (mod q), from f and gi's of integer coefficient polynomials such that: f = g1*g2*...*gm (mod q), gi's are pairwise coprime with positive integers p dividing q. """ ZpZx = poly_ring.PolynomialRing( intresidue.IntegerResidueClassRing.getInstance(p)) gg = arith1.product(factors) h = ZpZx.createElement([(d, c // q) for (d, c) in (f - gg).iterterms()]) lifted = [] for g in factors: gg = gg.pseudo_floordiv(g) g_mod = ZpZx.createElement(g) if gg.degree() == 0: break u, v, w = extgcdp(g, gg, p) if w.degree() > 0: raise ValueError("factors must be pairwise coprime.") v_mod = ZpZx.createElement(v) t = v_mod * h // g_mod lifted.append(g + minimum_absolute_injection(v_mod * h - g_mod * t)*q) u_mod = ZpZx.createElement(u) gg_mod = ZpZx.createElement(gg) h = u_mod * h + gg_mod * t lifted.append(g + minimum_absolute_injection(h)*q) return lifted
def find_combination(f, d, factors, q): """ find_combination(f, d, factors, q) -> g, list Find a combination of d factors which divides f (or its complement). The returned values are: the product g of the combination and a list consisting of the combination itself. If there is no combination, return (0,[]). """ lf = f.leading_coefficient() ZqZX = poly_ring.PolynomialRing( intresidue.IntegerResidueClassRing.getInstance(q)) if d == 1: for g in factors: product = minimum_absolute_injection(ZqZX.createElement(lf * g)) if divisibility_test(lf * f, product): return (product.primitive_part(), [g]) else: for idx in combinatorial.combinationIndexGenerator(len(factors), d): picked = [factors[i] for i in idx] product = lf * arith1.product(picked) product = minimum_absolute_injection(ZqZX.createElement(product)) if divisibility_test(lf * f, product): return (product.primitive_part(), picked) return 0, [] # nothing found
def test_simultaneous(self): # f = (X**2 + 17)(X**4 + 2*(1-c)*X**2 + (1+c)**2) # c = 2**32*3 f = hensel.the_ring.createElement({ 0: 2822351843715648061457, 2: 166020696251069104163, 4: -25769803757, 6: 1 }) f1 = hensel.the_ring.createElement({0: 5, 1: 1}) f2 = hensel.the_ring.createElement({0: 2, 1: 1}) f3 = hensel.the_ring.createElement({0: 2, 2: 1}) f4 = hensel.the_ring.createElement({0: 4, 2: 1}) factors = [f1, f2, f3, f4] p = 7 # q is also 7 # PRECONDITIONS # target = g1*g2*...*gm (mod q) self.assertEqualModulo(f, f1 * f2 * f3 * f4, p) # EXECUTION lifted, q = hensel.lift_upto(target=f, factors=[f1, f2, f3, f4], p=p, bound=7**128) # POSTCONDITIONS: self.assertEqual(4, len(lifted)) self.assertEqual(7**128, q) # target = G1*G2*...*Gm (mod q*p), self.assertEqualModulo(f, arith1.product(lifted), q, str(lifted)) # Gi = gi (mod q) for gi, Gi in zip(factors, lifted): self.assertEqualModulo(gi, Gi, 7)
def first_lift(self): """ Start lifting. f == l1*l2*...*lr (mod p**2) Initialize di's, ui's, yi's and zi's. Update ai's, bi's. Then, update q with p**2. """ assert self.p == self.q # q has not been raised yet self._assertEqualModulo(self.f, arith1.product(self.gis), self.q) for i in range(self.r - 1): self._assertEqualModulo( self.f, arith1.product(self.gis[:i + 1]) * self.his[i], self.q) assert self.gis[-1] == self.his[self.r - 2] self._init_dis() if len(self.dis) > 1: mini_target = self.dis[0] + self.dis[1].scalar_mul(self.p) self._assertEqualModulo(self.f, mini_target, self.p**2) else: mini_target = self.dis[0] aj, bj = [], [] self.uis, self.yis, self.zis = [], {}, {} for i in range(self.r - 1): dividend = mini_target - self.gis[i] * self.his[i] self.uis.append(dividend.scalar_exact_division(self.p)) self.yis[i], self.zis[i] = self._solve_yz(i) aj.append(self.gis[i] + self.zis[i].scalar_mul(self.q)) bj.append(self.his[i] + self.yis[i].scalar_mul(self.q)) self._assertEqualModulo(mini_target, aj[i] * bj[i], self.q * self.p) mini_target = bj[i] self._assertEqualModulo(self.gis[-1], mini_target, self.q) 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 != l%d * m%d" % (i, i)) self._assertEqualModulo(self.gis[i], aj[i], self.p) self._assertEqualModulo(self.f, arith1.product(aj), self.q) self.ais[-1], self.ais[0] = self.gis, tuple(aj) self.bis[-1], self.bis[0] = self.his, tuple(bj)
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 first_lift(self): """ Start lifting. f == l1*l2*...*lr (mod p**2) Initialize di's, ui's, yi's and zi's. Update ai's, bi's. Then, update q with p**2. """ assert self.p == self.q # q has not been raised yet self._assertEqualModulo(self.f, arith1.product(self.gis), self.q) for i in range(self.r - 1): self._assertEqualModulo(self.f, arith1.product(self.gis[:i+1])*self.his[i], self.q) assert self.gis[-1] == self.his[self.r - 2] self._init_dis() if len(self.dis) > 1: mini_target = self.dis[0] + self.dis[1].scalar_mul(self.p) self._assertEqualModulo(self.f, mini_target, self.p**2) else: mini_target = self.dis[0] aj, bj = [], [] self.uis, self.yis, self.zis = [], {}, {} for i in xrange(self.r - 1): dividend = mini_target - self.gis[i] * self.his[i] self.uis.append(dividend.scalar_exact_division(self.p)) self.yis[i], self.zis[i] = self._solve_yz(i) aj.append(self.gis[i] + self.zis[i].scalar_mul(self.q)) bj.append(self.his[i] + self.yis[i].scalar_mul(self.q)) self._assertEqualModulo(mini_target, aj[i]*bj[i], self.q*self.p) mini_target = bj[i] self._assertEqualModulo(self.gis[-1], mini_target, self.q) 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 != l%d * m%d" % (i, i)) self._assertEqualModulo(self.gis[i], aj[i], self.p) self._assertEqualModulo(self.f, arith1.product(aj), self.q) self.ais[-1], self.ais[0] = self.gis, tuple(aj) self.bis[-1], self.bis[0] = self.his, tuple(bj)
def assert_roots_coefficients(self, solutions, coefficients): """ assert that the given 'solutions' and 'coefficients' satisfies the relations between roots and coefficients. limitation: only norm and trace are tested, now. """ # norm degree = len(coefficients) - 1 self.assertAlmostEqual( 0, abs((-1)**degree * coefficients[0] - product(solutions))) # trace self.assertAlmostEqual(0, abs(-coefficients[-2] - sum(solutions)))
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 _factor_minpoly_modp(minpoly_coeff, p): """ Factor theminpoly modulo p, and return two values in a tuple. We call gcd(square factors mod p, difference of minpoly and its modp) Z. 1) degree of Z 2) (minpoly mod p) / Z """ Fp = finitefield.FinitePrimeField.getInstance(p) theminpoly_p = uniutil.polynomial([(d, Fp.createElement(c)) for (d, c) in enumerate(minpoly_coeff)], Fp) modpfactors = theminpoly_p.factor() mini_p = arith1.product([t for (t, e) in modpfactors]) quot_p = theminpoly_p.exact_division(mini_p) mini = _min_abs_poly(mini_p) quot = _min_abs_poly(quot_p) minpoly = uniutil.polynomial(enumerate(minpoly_coeff), Z) f_p = _mod_p((mini * quot - minpoly).scalar_exact_division(p), p) gcd = f_p.getRing().gcd common_p = gcd(gcd(mini_p, quot_p), f_p) # called Z uniq_p = theminpoly_p // common_p uniq = _min_abs_poly(uniq_p) return common_p.degree(), uniq
def test_lift_multi(self): f1 = hensel.the_ring.createElement({0: 5, 1: 1}) f2 = hensel.the_ring.createElement({0: 1, 1: 1}) f3 = hensel.the_ring.createElement({0: 1, 2: 1}) factors = [f1, f2, f3] f = hensel.the_ring.createElement(enumerate([-9900, -1, -9899, -1, 1])) p = 7 # q is also 7 # PRECONDITIONS # target = g1*g2*...*gm (mod q) self.assertEqualModulo(f, f1 * f2 * f3, p) # EXECUTION lifted, q = hensel.lift_upto(target=f, factors=factors, p=p, bound=100) # POSTCONDITIONS: self.assertEqual(3, len(lifted)) self.assertEqual(7**4, q) # target = G1*G2*...*Gm (mod q*p), self.assertEqualModulo(f, arith1.product(lifted), q, str(lifted)) # Gi = gi (mod q) for gi, Gi in zip(factors, lifted): self.assertEqualModulo(gi, Gi, 7)
def primonial(p): """ Return 2*3*...*p for given prime p. """ return arith1.product(generator_eratosthenes(p))
def testProduct(self): self.assertEqual(1, arith1.product([])) self.assertEqual(120, arith1.product(range(1, 6))) self.assertEqual(14400, arith1.product(i**2 for i in range(1, 6)))
def determinant(self): """ Return determinant of the basis (basis ought to be of full rank and in Hermite normal form). """ return arith1.product([rational.Rational(self.basis[i][i], self.denominator) for i in range(self.rank)])
def embedding(f_q1, f_q2): """ Return embedding homomorphism function from f_q1 to f_q2, where q1 = p ** k1, q2 = p ** k2 and k1 divides k2. """ if card(f_q1) == card(f_q2): return fqiso(f_q1, f_q2) # search multiplicative generators of both fields and relate them. # 0. initialize basic variables p = f_q2.getCharacteristic() q1, q2 = card(f_q1), card(f_q2) q1_mult_order, q2_mult_order = q1 - 1, q2 - 1 # 1. find a multiplicative generator of f_q2 for i in bigrange.range(p, q2): f_q2_gen = f_q2.createElement(i) if f_q2.order(f_q2_gen) == q2_mult_order: break f_q2_subgen = f_q2_gen**((q2 - 1) // (q1 - 1)) # 2.1 minimal polynomial of g = f_q2_gen ** ((q2 - 1) // (q1 - 1)) # minpoly = (X - g)(X - g**p)...(X - g**(p**(k1 - 1))) q, gpow = 1, f_q2_subgen linear_factors = [] while q < q2: linear_factors.append(univar.BasicPolynomial({0: gpow, 1: f_q2.one})) # finally, raise power (Frobenius map). q *= p gpow **= p minpoly = arith1.product(linear_factors) # 2.2 minpoly has f_q2 coefficints but they are indeed f_p elements minpoly = univar.BasicPolynomial([(d, c.rep[0]) for (d, c) in minpoly]) # 3. find a multiplicative generator of f_q1 for i in bigrange.range(p, q1): f_q1_gen = f_q1.createElement(i) if f_q1.order(f_q1_gen) == q1_mult_order: break # 4. find f_q1_gen ** c of a root of minpoly and let it f_q1_gen for c in bigrange.range(1, q1): if gcd.coprime(c, q1_mult_order): if not minpoly(f_q1_gen**c): break f_q1_gen = f_q1_gen**c # 5. solve DLP: # x_1 = f_q1_gen ** t # (Here uses "brute force" method) x_1 = f_q1.createElement(p) gpow = f_q1_gen for i in bigrange.range(1, q): if gpow == x_1: image_of_x_1 = f_q2_subgen**i break gpow *= f_q1_gen # finally, define a function def f_q1_to_f_q2_homo(f_q1_elem): """ Return the image of the isomorphism of the given element. """ if not f_q1_elem: return f_q2.zero if f_q1_elem.rep.degree() == 0: # F_p elements return f_q2.createElement(f_q1_elem.rep) return f_q1_elem.rep(image_of_x_1) return f_q1_to_f_q2_homo
import logging import warnings import nzmath.arith1 as arith1 import nzmath.gcd as gcd import nzmath.bigrandom as bigrandom import nzmath.bigrange as bigrange import nzmath.poly.array as array_poly from nzmath.config import GRH from nzmath.plugins import MATHMODULE as math _log = logging.getLogger('nzmath.prime') _log.setLevel(logging.DEBUG) PRIMES_LE_31 = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31) PRIMONIAL_31 = arith1.product(PRIMES_LE_31) def trialDivision(n, bound=0): """ Trial division primality test for an odd natural number. Optional second argument is a search bound of primes. If the bound is given and less than the sqaure root of n and True is returned, it only means there is no prime factor less than the bound. """ if bound: m = min(bound, arith1.floorsqrt(n)) else: m = arith1.floorsqrt(n)