Esempio n. 1
0
def _find_pow_of_frobenius(p, n, x, y):
    """
    Find the power of Frobenius which yields `x` when applied to `y`.

    INPUT:

    - ``p`` -- prime number

    - ``n`` -- positive integer

    - ``x`` -- an element of a field `K` of `p^n` elements so that
      the multiplicative order of `x` is `p^n - 1`.

    - ``y`` -- an element of `K` with the same minimal polynomial as
      `x`.

    OUTPUT:

    - an element `i` of the integers modulo `n` such that `x = y^{p^i}`.

    EXAMPLES::

        sage: from sage.rings.finite_rings.conway_polynomials import _find_pow_of_frobenius
        sage: K.<a> = GF(3^14)
        sage: x = K.multiplicative_generator()
        sage: y = x^27
        sage: _find_pow_of_frobenius(3, 14, x, y)
        11

    """
    from integer_mod import mod
    for i in xrange(n):
        if x == y: break
        y = y**p
    else:
        raise RuntimeError("No appropriate power of Frobenius found")
    return mod(i, n)
def _find_pow_of_frobenius(p, n, x, y):
    """
    Find the power of Frobenius which yields `x` when applied to `y`.

    INPUT:

    - ``p`` -- prime number

    - ``n`` -- positive integer

    - ``x`` -- an element of a field `K` of `p^n` elements so that
      the multiplicative order of `x` is `p^n - 1`.

    - ``y`` -- an element of `K` with the same minimal polynomial as
      `x`.

    OUTPUT:

    - an element `i` of the integers modulo `n` such that `x = y^{p^i}`.

    EXAMPLES::

        sage: from sage.rings.finite_rings.conway_polynomials import _find_pow_of_frobenius
        sage: K.<a> = GF(3^14)
        sage: x = K.multiplicative_generator()
        sage: y = x^27
        sage: _find_pow_of_frobenius(3, 14, x, y)
        11

    """
    from integer_mod import mod
    for i in xrange(n):
        if x == y: break
        y = y**p
    else:
        raise RuntimeError("No appropriate power of Frobenius found")
    return mod(i, n)
Esempio n. 3
0
def _frobenius_shift(K, generators, check_only=False):
    """
    Given a field `K` of degree `n` over ``GF(p)`` and a dictionary
    holding, for each divisor `q` of `n`, an element with minimal
    polynomial a pseudo-Conway polynomial of degree `n/q`, modify
    these generators into a compatible system.

    Such a system of generators is said to be compatible if for each
    pair of prime divisors `q_1` and `q_2` and each common divisor `m`
    of `n/q_1` and `n/q_2`, the equality

    ``generators[q1]^((p^(n/q1)-1)/(p^m-1)) == generators[q2]^((p^(n/q2)-1)/(p^m-1))``

    holds.

    INPUT:

    - ``K`` -- a finite field of degree `n` over its prime field

    - ``generators`` -- a dictionary, indexed by prime divisors `q` of
      `n`, whose entries are elements of `K` satisfying the `n/q`
      pseudo-Conway polynomial.

    - ``check_only`` -- if ``True``, just check that the given
      generators form a compatible system.

    EXAMPLES::

        sage: R.<x> = GF(2)[]
        sage: f30 = x^30 + x^28 + x^27 + x^25 + x^24 + x^20 + x^19 + x^18 + x^16 + x^15 + x^12 + x^10 + x^7 + x^2 + 1
        sage: f20 = x^20 + x^19 + x^15 + x^13 + x^12 + x^11 + x^9 + x^8 + x^7 + x^4 + x^2 + x + 1
        sage: f12 = x^12 + x^10 + x^9 + x^8 + x^4 + x^2 + 1
        sage: K.<a> = GF(2^60, modulus='first_lexicographic')
        sage: x30 = f30.any_root(K)
        sage: x20 = f20.any_root(K)
        sage: x12 = f12.any_root(K)
        sage: generators = {2: x30, 3: x20, 5: x12}
        sage: from sage.rings.finite_rings.conway_polynomials import _frobenius_shift, _find_pow_of_frobenius
        sage: _frobenius_shift(K, generators)
        sage: _find_pow_of_frobenius(2, 30, x30, generators[2])
        0
        sage: _find_pow_of_frobenius(2, 20, x20, generators[3])
        13
        sage: _find_pow_of_frobenius(2, 12, x12, generators[5])
        8

    """
    if len(generators) == 1:
        return generators
    p = K.characteristic()
    n = K.degree()
    compatible = {}
    from integer_mod import mod
    for m in n.divisors():
        compatible[m] = {}
    for q, x in generators.iteritems():
        for m in (n//q).divisors():
            compatible[m][q] = x**((p**(n//q)-1)//(p**m-1))
    if check_only:
        for m in n.divisors():
            try:
                q, x = compatible[m].popitem()
            except KeyError:
                break
            for qq, xx in compatible[m].iteritems():
                assert x == xx
        return
    crt = {}
    qlist = sorted(generators.keys())
    for j in range(1, len(qlist)):
        for i in range(j):
            crt[(i, j)] = []
    for m in n.divisors():
        mqlist = sorted(compatible[m].keys())
        for k in range(1,len(mqlist)):
            j = qlist.index(mqlist[k])
            i = qlist.index(mqlist[k-1])
            crt[(i,j)].append(_find_pow_of_frobenius(p, m, compatible[m][qlist[j]], compatible[m][qlist[i]]))
    from integer_mod import mod
    pairs = crt.keys()
    for i, j in pairs:
        L = crt[(i,j)]
        running = mod(0,1)
        for a in L:
            running = _crt_non_coprime(running, a)
        crt[(i,j)] = [(mod(running, q**(running.modulus().valuation(q))), running.modulus().valuation(q)) for q in qlist]
        crt[(j,i)] = [(-a, level) for a, level in crt[(i,j)]]
    # Let x_j be the power of Frobenius we apply to generators[qlist[j]], for 0 < j < len(qlist)
    # We have some direct conditions on the x_j: x_j reduces to each entry in crt[(0,j)].
    # But we also have the equations x_j - x_i reduces to each entry in crt[(i,j)].
    # We solve for x_j one prime at a time.  For each prime, we have an equations of the form
    # x_j - x_i = c_ij.  The modulus of the currently known value of x_j, x_i and c_ij will all be powers
    # (possibly 0, possibly different) of the same prime.

    # We can set x_0=0 everywhere, can get an initial setting of x_j from the c_0j.
    # We go through prime by prime.
    import bisect
    frob_powers=[mod(0,1) for q in qlist]
    def find_leveller(qindex, level, x, xleveled, searched, i):
        searched[i] = True
        crt_possibles = []
        for j in range(1,len(qlist)):
            if i==j: continue
            if crt[(i,j)][qindex][1] >= level:
                if xleveled[j]:
                    return [j]
                elif j not in searched:
                    crt_possibles.append(j)
        for j in crt_possibles:
            path = find_leveller(qindex, level, x, xleveled, searched, j)
            if path is not None:
                path.append(j)
                return path
        return None
    def propagate_levelling(qindex, level, x, xleveled, i):
        for j in range(1, len(qlist)):
            if i==j: continue
            if not xleveled[j] and crt[(i,j)][qindex][1] >= level:
                newxj = x[i][0] + crt[(i,j)][qindex][0]
                x[j] = (newxj, min(x[i][1], crt[(i,j)][qindex][1]))
                xleveled[j] = True
                propagate_levelling(qindex, level, x, xleveled, j)

    for qindex in range(len(qlist)):
        q = qlist[qindex]
        # We include the initial 0 to match up our indexing with crt.
        x = [0] + [crt[(0,j)][qindex] for j in range(1,len(qlist))]
        # We first check that our equations are consistent and
        # determine which powers of q occur as moduli.
        levels = []
        for j in range(2, len(qlist)):
            for i in range(j):
                # we need crt[(0,j)] = crt[(0,i)] + crt[(i,j)]
                if i != 0:
                    assert x[j][0] == x[i][0] + crt[(i,j)][qindex][0]
                level = crt[(i,j)][qindex][1]
                if level > 0:
                    ins = bisect.bisect_left(levels,level)
                    if ins == len(levels):
                        levels.append(level)
                    elif levels[ins] != level:
                        levels.insert(ins, level)
        for level in levels:
            xleveled = [0] + [x[i][1] >= level for i in range(1,len(qlist))]
            while True:
                try:
                    i = xleveled.index(False, 1)
                    searched = {}
                    levelling_path = find_leveller(qindex, level, x, xleveled, searched, i)
                    if levelling_path is None:
                        # Any lift will work, since there are no constraints.
                        x[i] = (mod(x[i][0].lift(), q**level), level)
                        xleveled[i] = True
                        propagate_levelling(qindex, level, x, xleveled, i)
                    else:
                        levelling_path.append(i)
                        for m in range(1,len(path)):
                            # This point on the path may have already
                            # been leveled in a previous propagation.
                            if not xleveled[path[m]]:
                                newx = x[path[m-1]][0] + crt[(path[m-1],path[m])][qindex][0]
                                x[path[m]] = (newx, min(x[path[m-1]][1], crt[(path[m-1],path[m])][qindex][1]))
                                xleveled[path[m]] = True
                                propagate_levelling(qindex, level, x, xleveled, path[m])
                except ValueError:
                    break
        for j in range(1,len(qlist)):
            frob_powers[j] = frob_powers[j].crt(x[j][0])
    for j in range(1, len(qlist)):
        generators[qlist[j]] = generators[qlist[j]]**(p**(-frob_powers[j]).lift())
    _frobenius_shift(K, generators, check_only=True)
def _frobenius_shift(K, generators, check_only=False):
    """
    Given a field `K` of degree `n` over ``GF(p)`` and a dictionary
    holding, for each divisor `q` of `n`, an element with minimal
    polynomial a pseudo-Conway polynomial of degree `n/q`, modify
    these generators into a compatible system.

    Such a system of generators is said to be compatible if for each
    pair of prime divisors `q_1` and `q_2` and each common divisor `m`
    of `n/q_1` and `n/q_2`, the equality

    ``generators[q1]^((p^(n/q1)-1)/(p^m-1)) == generators[q2]^((p^(n/q2)-1)/(p^m-1))``

    holds.

    INPUT:

    - ``K`` -- a finite field of degree `n` over its prime field

    - ``generators`` -- a dictionary, indexed by prime divisors `q` of
      `n`, whose entries are elements of `K` satisfying the `n/q`
      pseudo-Conway polynomial.

    - ``check_only`` -- if ``True``, just check that the given
      generators form a compatible system.

    EXAMPLES::

        sage: R.<x> = GF(2)[]
        sage: f30 = x^30 + x^28 + x^27 + x^25 + x^24 + x^20 + x^19 + x^18 + x^16 + x^15 + x^12 + x^10 + x^7 + x^2 + 1
        sage: f20 = x^20 + x^19 + x^15 + x^13 + x^12 + x^11 + x^9 + x^8 + x^7 + x^4 + x^2 + x + 1
        sage: f12 = x^12 + x^10 + x^9 + x^8 + x^4 + x^2 + 1
        sage: K.<a> = GF(2^60, modulus='first_lexicographic')
        sage: x30 = f30.any_root(K)
        sage: x20 = f20.any_root(K)
        sage: x12 = f12.any_root(K)
        sage: generators = {2: x30, 3: x20, 5: x12}
        sage: from sage.rings.finite_rings.conway_polynomials import _frobenius_shift, _find_pow_of_frobenius
        sage: _frobenius_shift(K, generators)
        sage: _find_pow_of_frobenius(2, 30, x30, generators[2])
        0
        sage: _find_pow_of_frobenius(2, 20, x20, generators[3])
        13
        sage: _find_pow_of_frobenius(2, 12, x12, generators[5])
        8

    """
    if len(generators) == 1:
        return generators
    p = K.characteristic()
    n = K.degree()
    compatible = {}
    from integer_mod import mod
    for m in n.divisors():
        compatible[m] = {}
    for q, x in generators.iteritems():
        for m in (n//q).divisors():
            compatible[m][q] = x**((p**(n//q)-1)//(p**m-1))
    if check_only:
        for m in n.divisors():
            try:
                q, x = compatible[m].popitem()
            except KeyError:
                break
            for qq, xx in compatible[m].iteritems():
                assert x == xx
        return
    crt = {}
    qlist = sorted(generators.keys())
    for j in range(1, len(qlist)):
        for i in range(j):
            crt[(i, j)] = []
    for m in n.divisors():
        mqlist = sorted(compatible[m].keys())
        for k in range(1,len(mqlist)):
            j = qlist.index(mqlist[k])
            i = qlist.index(mqlist[k-1])
            crt[(i,j)].append(_find_pow_of_frobenius(p, m, compatible[m][qlist[j]], compatible[m][qlist[i]]))
    from integer_mod import mod
    pairs = crt.keys()
    for i, j in pairs:
        L = crt[(i,j)]
        running = mod(0,1)
        for a in L:
            running = _crt_non_coprime(running, a)
        crt[(i,j)] = [(mod(running, q**(running.modulus().valuation(q))), running.modulus().valuation(q)) for q in qlist]
        crt[(j,i)] = [(-a, level) for a, level in crt[(i,j)]]
    # Let x_j be the power of Frobenius we apply to generators[qlist[j]], for 0 < j < len(qlist)
    # We have some direct conditions on the x_j: x_j reduces to each entry in crt[(0,j)].
    # But we also have the equations x_j - x_i reduces to each entry in crt[(i,j)].
    # We solve for x_j one prime at a time.  For each prime, we have an equations of the form
    # x_j - x_i = c_ij.  The modulus of the currently known value of x_j, x_i and c_ij will all be powers
    # (possibly 0, possibly different) of the same prime.

    # We can set x_0=0 everywhere, can get an initial setting of x_j from the c_0j.
    # We go through prime by prime.
    import bisect
    frob_powers=[mod(0,1) for q in qlist]
    def find_leveller(qindex, level, x, xleveled, searched, i):
        searched[i] = True
        crt_possibles = []
        for j in range(1,len(qlist)):
            if i==j: continue
            if crt[(i,j)][qindex][1] >= level:
                if xleveled[j]:
                    return [j]
                elif j not in searched:
                    crt_possibles.append(j)
        for j in crt_possibles:
            path = find_leveller(qindex, level, x, xleveled, searched, j)
            if path is not None:
                path.append(j)
                return path
        return None
    def propagate_levelling(qindex, level, x, xleveled, i):
        for j in range(1, len(qlist)):
            if i==j: continue
            if not xleveled[j] and crt[(i,j)][qindex][1] >= level:
                newxj = x[i][0] + crt[(i,j)][qindex][0]
                x[j] = (newxj, min(x[i][1], crt[(i,j)][qindex][1]))
                xleveled[j] = True
                propagate_levelling(qindex, level, x, xleveled, j)

    for qindex in range(len(qlist)):
        q = qlist[qindex]
        # We include the initial 0 to match up our indexing with crt.
        x = [0] + [crt[(0,j)][qindex] for j in range(1,len(qlist))]
        # We first check that our equations are consistent and
        # determine which powers of q occur as moduli.
        levels = []
        for j in range(2, len(qlist)):
            for i in range(j):
                # we need crt[(0,j)] = crt[(0,i)] + crt[(i,j)]
                if i != 0:
                    assert x[j][0] == x[i][0] + crt[(i,j)][qindex][0]
                level = crt[(i,j)][qindex][1]
                if level > 0:
                    ins = bisect.bisect_left(levels,level)
                    if ins == len(levels):
                        levels.append(level)
                    elif levels[ins] != level:
                        levels.insert(ins, level)
        for level in levels:
            xleveled = [0] + [x[i][1] >= level for i in range(1,len(qlist))]
            while True:
                try:
                    i = xleveled.index(False, 1)
                    searched = {}
                    levelling_path = find_leveller(qindex, level, x, xleveled, searched, i)
                    if levelling_path is None:
                        # Any lift will work, since there are no constraints.
                        x[i] = (mod(x[i][0].lift(), q**level), level)
                        xleveled[i] = True
                        propagate_levelling(qindex, level, x, xleveled, i)
                    else:
                        levelling_path.append(i)
                        for m in range(1,len(path)):
                            # This point on the path may have already
                            # been leveled in a previous propagation.
                            if not xleveled[path[m]]:
                                newx = x[path[m-1]][0] + crt[(path[m-1],path[m])][qindex][0]
                                x[path[m]] = (newx, min(x[path[m-1]][1], crt[(path[m-1],path[m])][qindex][1]))
                                xleveled[path[m]] = True
                                propagate_levelling(qindex, level, x, xleveled, path[m])
                except ValueError:
                    break
        for j in range(1,len(qlist)):
            frob_powers[j] = frob_powers[j].crt(x[j][0])
    for j in range(1, len(qlist)):
        generators[qlist[j]] = generators[qlist[j]]**(p**(-frob_powers[j]).lift())
    _frobenius_shift(K, generators, check_only=True)