def frobenius_trace_mod_l(torsion_group): """ Compute the trace of the Frobenius endomorphism modulo @f$ l @f$, where @f$ l @f$ is the torsion of @p torsion_group. The function guesses candidates and verifies the frobenius_equation() for every point in the @p torsion_group. @note A torsion of 2 requires multivariate polynomial arithmetic, which is unavailable. Therefore @f$ l @f$ must be greater than 2. Use frobenius_trace_mod_2() to get the trace modulo 2. @return The congruence class of the trace of the Frobenius endomorphism. This is an element of QuotientRing( Integers, l ). """ assert torsion_group.torsion() > 2, \ "torsion 2 requires multivariate polynomial arithmetic" ints_mod_torsion = QuotientRing(Integers, torsion_group.torsion()) field_size = torsion_group.curve().field().size() for trace_candidate in range(0, torsion_group.torsion()): candidate_congruence = ints_mod_torsion(trace_candidate) for point in torsion_group.elements(): if not frobenius_equation(candidate_congruence, field_size, point): # Exit inner loop and skip the 'else' clause. break else: # Execute after the iteration completed; skip upon break. return candidate_congruence message = "Frobenius equation held for no trace candidate" raise ArithmeticError(message)
def __init_point(self): """ Create the point that implicitly represents the whole l-torsion group and store it in the cache. Calling the method has no effect if the point already exists. """ if self.__point: return # R = F[x] / (y**2 - x**3 - A*x - B) R = self._division_polynomial_list().curve_polynomials() # The n-th division polynomial psi = self._division_polynomial_list()[self.__torsion] # T = ( F[x] / (y**2 - x**3 - A*x - B) ) / psi(l) S = QuotientRing(R, psi) T = FractionField(S) A, B = R.curve().parameters() # Polynomials x and y on the curve interpreted # as elements of the field of fractions x = T(R((0, 1), 0)) y = T(R(0, 1)) self.__point = EllipticCurve(T, A, B)(x, y)
def frobenius_trace_mod_2(curve): """ Compute the trace of the Frobenius endomorphism modulo 2. We cannot use the implicit torsion group representation of frobenius_trace_mod_l() in the case @f$ l=2 @f$ because the second division polynomial is @f$ y @f$, and multivariate polynomial arithmetic is unavailable. Implementing it for this single case would be overkill. Instead, we test whether there are any rational 2-torsion points. If so, then @f$ E[2] @f$ is a subgroup of the @p curve and its order divides the order of the curve (which is the number of rational points). Now, the number of rational points in @f$ E[2] @f$ can only be 2 or 4 in this case, so we know that the number of rational points is even. Hence, the Frobenius trace must then be 0 modulo 2. @return 0 if the number of points on the curve is even, 1 otherwise. The result is a congruence class modulo 2, that is, an element of @c QuotientRing( Integers, 2 ). """ R = Polynomials(curve.field()) x = R(0, 1) A, B = curve.parameters() defining_polynomial = x**3 + A * x + B rational_characteristic = x**curve.field().size() - x # gcd() has an arbitrary unit as leading coefficient; # relatively prime polynomials have a constant gcd. d = gcd(rational_characteristic, defining_polynomial) if d.degree() == 0: # The rational characteristic and the defining polynomial # are relatively prime: no rational point of order 2 exists # and the Frobenius trace must be odd. return QuotientRing(Integers, 2)(1) else: return QuotientRing(Integers, 2)(0)
def solve_congruence_equations(congruences): """ Return a quotient class that simultaneously solves the list of @p congruences; this is the Chinese Remainder Theorem. All representatives of the returned congruence have the remainders of @p congruences when taken modulo the respective moduli. The result is a congruence modulo the product of all moduli. Thus the function returns a number @f$ z \mod \prod_{i} m_i @f$ such that @f{align*}{ z &\equiv a_1 \mod m_1 \\ \vdots \\ z &\equiv a_k \mod m_k @f} @note The moduli @f$ m_i @f$ of all congruences must be relatively prime. @exception ValueError if @p congruences is empty. @param congruences An iterable of objects of QuotientClass over the Integers. Every pair of two different moduli must have a greatest common divisor (gcd()) of 1. @return An instance of rings.quotients.naive.QuotientRing over the rings.integers.naive.Integers solving the @p congruences. @see Robinson, D. J. S., "An Introduction to Abstract Algebra", p. 27 """ # The Chinese remainder theorem if not congruences: raise ValueError("cannot solve empty equation system") if __debug__: # This test is expensive; remove it in optimized execution pairs = [(c1, c2) for c1 in congruences for c2 in congruences if c1 != c2] pairwise_gcds = [gcd(c1.modulus(), c2.modulus()) for c1, c2 in pairs] assert set(pairwise_gcds) == set([1]), \ "the Chinese Remainder Theorem requires relatively prime moduli" common_modulus = reduce(mul, [c.modulus() for c in congruences]) common_representative = 0 for c in congruences: neutralizer = common_modulus // c.modulus() common_representative += c.remainder() * neutralizer \ * inverse_modulo(neutralizer, c.modulus()) quotient_ring = QuotientRing(Integers, common_modulus) return quotient_ring(common_representative)
def frobenius_trace_mod_l(torsion_group): """ Compute the trace of the Frobenius endomorphism modulo @f$ l @f$, where @f$ l @f$ is the torsion of @p torsion_group. The function guesses candidates and verifies whether the function that results from applying the characteristic polynomial @f$ \chi_\phi @f$ to @f$ \phi @f$ maps every point in the @p torsion_group onto the point at infinity. @note A torsion of 2 requires multivariate polynomial arithmetic, which is unavailable. Therefore @f$ l @f$ must be greater than 2. Use frobenius_trace_mod_2() to get the trace modulo 2. @return The congruence class of the trace of the Frobenius endomorphism. This is an element of @c QuotientRing( Integers, l ). """ assert torsion_group.torsion() > 2, \ "torsion 2 requires multivariate polynomial arithmetic" torsion_quotient_ring = QuotientRing(Integers, torsion_group.torsion()) field_size = torsion_group.curve().field().size() # Note: Technically, there could be several points so we would have to # filter the one candidate that worked for all points in the end. # Luckily, there is only one point. for point in torsion_group.elements(): frobenius_point = frobenius(point, field_size) frobenius2_point = frobenius(frobenius_point, field_size) determinant_point = (field_size % torsion_group.torsion()) * point point_sum = frobenius2_point + determinant_point if point_sum.is_infinite(): return torsion_quotient_ring(0) trace_point = frobenius_point for trace_candidate in range(1, (torsion_group.torsion() + 1) // 2): if point_sum.x() == trace_point.x(): if point_sum.y() == trace_point.y(): return torsion_quotient_ring(trace_candidate) else: return torsion_quotient_ring(-trace_candidate) else: trace_point += frobenius_point message = "Frobenius equation held for no trace candidate" raise ArithmeticError(message)
def setUp(self): Z3 = QuotientRing(Integers, 3) Z5 = QuotientRing(Integers, 5) self._remainders = [Z3(2), Z5(1)]