def find_trace(n,m,k):
    '''
    INPUT : an integer n, an integer m, a base field k

    OUTPUT : a list of integer mod m or a list of a couple of integers mod m

    Algorithm :

    If m is a power of p, then we look for class modulo m with order equal to n.
    Then, we return the list of all such class.

    If m is a power of prime different from p, we look for a in (Z/m)* such 
    that :

    - ord_m(a) < ord_m(q/a) and ord_m(a) = n,

    or

    - ord_m(q/a) < ord_a and ord_m(q/a) = n.

    And we return a + q/a.

    Here a plays the role of one of the two roots of the future characteristic 
    polynomial of the Frobenius of the elliptic curve we'll use; i.e.

    X^2 - (a + q/a)*X + a*(q/a) = X^2 - t*X + q

    if we write t = a + q/a. From that, we will pick elliptic curves which have 
    one of the t's as trace of its Frobenius.
    '''
    Zm = Integers(m)
    p = k.characteristic()
    q = k.cardinality()
    sq = sqrt(float(2*q))
    q_m = Zm(q)

    # If m is a multiple of p, then we just need the trace to be of order 
    #exactly n in (Z/m)*
    if not m.is_prime_power():
        raise NotImplementedError
    elif m%p == 0:
        sol = []
        phi_m = euler_phi(m)
        alpha = phi_m/n
        g = Zm.unit_gens()[0]

        log_t = [i*alpha for i in n.coprime_integers(n)]

        for t in [g**i for i in log_t]:
            if abs(t.centerlift()) > sq:
                continue
            else:
                sol.append(t)

        return set(sol)
    # We don't want q to be of order n or dividing n, then q/a would be of order
    # n; which is unacceptable.
    elif q_m**n == 1:
        return []
    else:
        sol = []
        phi_m = euler_phi(m)
        alpha = phi_m/phi_m.gcd(n)
        g = Zm.unit_gens()[0]
        Zphi_m = Integers(phi_m)
        
        log_a = [i*alpha for i in n.coprime_integers(n)]
        a = [g**i for i in log_a]
        log_q = q_m.log(g)

        for i in range(len(log_a)):
            diff = log_q - log_a[i]
            b = g**diff
            ord_b = diff.order()

            if ord_b <= n:
                continue
            elif abs((a[i] + b).centerlift()) > sq:
                continue
            else:
                sol.append(a[i] + b)

        return set(sol)
Example #2
0
def find_trace(n,m,k):
    '''
    INPUT :

    - ``n`` -- an integer, the degree of the extension,

    - ``m`` -- an integer, a candidate for the paramater m,

    - ``k`` -- a finite field, the base field.

    OUTPUT : 

    - A list of integer modulo m with the good properties.

    EXAMPLES :

    sage: n = 281

    sage: m = 3373

    sage: k = GF(1747)

    sage: find_trace(n,m,k)

    {4, 14, 18, 43, 57, 3325, 3337, 3348, 3354, 3357, 3364}

    ALGORITHM :

    The algorithm is pretty straightforward. We select all the elements of 
    order n and look for some properties of their class modulo m and add 
    them to list if they meet the requirements. They will be the class 
    candidates for the trace of the future elliptic curves.

    - If m is a power of p, the characteristic, then we look for all the 
    elements of order n and check if they end in the Hasse interval. 

    - If m is a prime (power) different from p, then we start by computing the 
    logarithm of elements of order n in (Z/m)*. You have the minimal polynomial
    of the Frobenius equal to X**2 - t*X + q = (X - a)(X - q/a) mod m. We look 
    for a among the element of order n modulo m such that the other root q/a is
    of greater order. If their sum a + q/a = t falls into the Hasse interval, 
    then we add t in the good candidates.

    - If m is composite, we raise a NotImplementedError.
    '''
    Zm = Integers(m)
    p = k.characteristic()
    q = k.cardinality()
    sq = sqrt(float(2*q))
    q_m = Zm(q)
    alpha = (m-1)//n


    if q_m**n == 1:
        return []
    elif m == p:
        sol = []
        g = Zm.unit_gens()[0]

        log_t = [i*alpha for i in n.coprime_integers(n)]

        for t in [g**i for i in log_t]:
            if abs(t.centerlift()) > sq:
                continue
            else:
                sol.append(t)

        return set(sol)
    # We don't want q to be of order n or dividing n, then q/a would be of order
    # n; which is unacceptable => b order n, but why b order n is bad ?
    else:
        sol = []
        g = Zm.unit_gens()[0]
        
        # Computing the logarithm of element of order n.
        log_a = [i*alpha for i in n.coprime_integers(n)]
        a = [g**i for i in log_a]
        log_q = q_m.log(g)

        for i in range(len(log_a)):
            diff = log_q - log_a[i]
            b = g**diff

            if p == 2:
                if abs((a[i] + b).centerlift()) > 1:
                    continue
                else:
                    sol.append((a[i] + b))
            if abs((a[i] + b).centerlift()) > sq:
                continue
            elif diff%n == 0:
                continue
            else:
                sol.append(a[i] + b)

        return set(sol)