Exemplo n.º 1
0
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)
Exemplo n.º 2
0
    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)
Exemplo n.º 3
0
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)
Exemplo n.º 4
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)
Exemplo n.º 6
0
 def setUp(self):
     Z3 = QuotientRing(Integers, 3)
     Z5 = QuotientRing(Integers, 5)
     self._remainders = [Z3(2), Z5(1)]