Ejemplo n.º 1
0
def walsh_matrix(m0):
    """
    This is the generator matrix of a Walsh code. The matrix of
    codewords correspond to a Hadamard matrix.

    EXAMPLES::

        sage: walsh_matrix(2)
        [0 0 1 1]
        [0 1 0 1]
        sage: walsh_matrix(3)
        [0 0 0 0 1 1 1 1]
        [0 0 1 1 0 0 1 1]
        [0 1 0 1 0 1 0 1]
        sage: C = LinearCode(walsh_matrix(4)); C
        Linear code of length 16, dimension 4 over Finite Field of size 2
        sage: C.spectrum()
        [1, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0]

    This last code has minimum distance 8.

    REFERENCES:

    - http://en.wikipedia.org/wiki/Hadamard_matrix
    """
    m = int(m0)
    if m == 1:
        return matrix(GF(2), 1, 2, [ 0, 1])
    if m > 1:
        row2 = [x.list() for x in walsh_matrix(m-1).augment(walsh_matrix(m-1)).rows()]
        return matrix(GF(2), m, 2**m, [[0]*2**(m-1) + [1]*2**(m-1)] + row2)
    raise ValueError, "%s must be an integer > 0."%m0
Ejemplo n.º 2
0
def nonisomorphic_cubes_Z2(n, avoid_complete=False):
    """
  Returns a generator for all n-dimensional Cube-like graphs
  (Cayley graphs over Z_2^n) with their generators.
  With avoid_complete=True avoids the complete graph.
  Iterates over tuples (generatorSet, G).
  """
    vs = VectorSpace(GF(2), n)
    basegens = vs.gens()
    optgens = [v for v in vs if sum(map(int, v)) >= 2]
    total = 2**len(optgens)
    seen_graphs = set()
    c = 0
    for g in powerset(optgens):
        c += 1
        gens = tuple(list(basegens) + g)
        if c % (total / 100 + 1) == 0:
            log.debug("Generating (%d of %d)" % (c, total))
        if avoid_complete:
            if len(g) >= len(optgens):
                continue
        G = CayleyGraph(vs, gens)

        canon = tuple(Graph(G).canonical_label().edges())
        if canon in seen_graphs:
            continue
        log.debug("Unique graph (%d of %d) gens=%s" % (c, total, gens))
        seen_graphs.add(canon)

        yield (gens, G)
Ejemplo n.º 3
0
def lift2smallest_field(a):
    """
    INPUT: a is an element of a finite field GF(q)

    OUTPUT: the element b of the smallest subfield F of GF(q) for
    which F(b)=a.

    EXAMPLES::

        sage: from sage.coding.code_constructions import lift2smallest_field
        sage: FF.<z> = GF(3^4,"z")
        sage: a = z^10
        sage: lift2smallest_field(a)
        (2*z + 1, Finite Field in z of size 3^2)
        sage: a = z^40
        sage: lift2smallest_field(a)
        (2, Finite Field of size 3)

    AUTHORS:

    - John Cremona
    """
    FF = a.parent()
    k = FF.degree()
    if k == 1:
        return a, FF
    pol = a.minimal_polynomial()
    d = pol.degree()
    if d == k:
        return a, FF
    p = FF.characteristic()
    F = GF(p**d,"z")
    b = pol.roots(F,multiplicities=False)[0]
    return b, F
Ejemplo n.º 4
0
def test(p, l, h, T, implementation=None, number=0, repeat=3):
    """
    This subroutine does the real job
    """
    tower = T(GF(p), l, 'x', implementation=implementation)

    tcreat, tlift, tpush = [], [], []
    for i in range(1, h + 1):
        context = globals()
        context.update(locals())
        tcreat.append(
            sage_timeit('tower[i]', context, number=1, repeat=1, seconds=True))

        if i > 1:
            tpush.append(
                sage_timeit('tower._push(tower[i].random_element())',
                            context,
                            number=number,
                            repeat=repeat,
                            seconds=True))
            tlift.append(
                sage_timeit(
                    'tower._lift([tower[i-1].random_element() for k in range(l)])',
                    context,
                    number=number,
                    repeat=repeat,
                    seconds=True))

    print 'Creation time:', sum(tcreat)
    print 'Push time:', sum(tpush)
    print 'Lift time:', sum(tlift)

    return tcreat, tpush, tlift
Ejemplo n.º 5
0
def TernaryGolayCode():
    r"""
    TernaryGolayCode returns a ternary Golay code. This is a perfect
    [11,6,5] code. It is also equivalent to a cyclic code, with
    generator polynomial `g(x)=2+x^2+2x^3+x^4+x^5`.

    EXAMPLES::

        sage: C = codes.TernaryGolayCode()
        sage: C
        Linear code of length 11, dimension 6 over Finite Field of size 3
        sage: C.minimum_distance()
        5
        sage: C.minimum_distance(algorithm='gap') # long time, check d=5
        5

    AUTHORS:

    - David Joyner (2007-5)
    """
    F = GF(3)
    B = [[2, 0, 1, 2, 1, 1, 0, 0, 0, 0, 0],\
         [0, 2, 0, 1, 2, 1, 1, 0, 0, 0, 0],\
         [0, 0, 2, 0, 1, 2, 1, 1, 0, 0, 0],\
         [0, 0, 0, 2, 0, 1, 2, 1, 1, 0, 0],\
         [0, 0, 0, 0, 2, 0, 1, 2, 1, 1, 0],\
         [0, 0, 0, 0, 0, 2, 0, 1, 2, 1, 1]]
    V = span(B, F)
    return LinearCodeFromVectorSpace(V, d=5)
Ejemplo n.º 6
0
    def zeta(self, n=None):
        r"""
        Returns a generator of the group of roots of unity.
        
        INPUT:
        
        - ``self`` -- a `p`-adic ring

        - ``n`` -- an integer or ``None`` (default: ``None``)

        OUTPUT:
        
        - ``element`` -- a generator of the `n^{th}` roots of unity,
          or a generator of the full group of roots of unity if ``n``
          is ``None``

        EXAMPLES::

            sage: R = Zp(37,5)
            sage: R.zeta(12)
            8 + 24*37 + 37^2 + 29*37^3 + 23*37^4 + O(37^5)
        """
        if (self.prime() == 2):
            if (n is None) or (n == 2):
                return self(-1)
            if n == 1:
                return self(1)
            else:
                raise ValueError, "No, %sth root of unity in self" % n
        else:
            from sage.rings.finite_rings.constructor import GF
            return self.teichmuller(GF(self.prime()).zeta(n).lift())
Ejemplo n.º 7
0
    def __init__(self, space, number_errors, number_erasures):
        r"""


        TESTS:

        If the sum of number of errors and number of erasures
        exceeds (or may exceed, in the case of tuples) the dimension of the input space,
        it will return an error::

            sage: n_err, n_era = 21, 21
            sage: Chan = channels.ErrorErasureChannel(GF(59)^40, n_err, n_era)
            Traceback (most recent call last):
            ...
            ValueError: The total number of errors and erasures can not exceed the dimension of the input space
        """
        if isinstance(number_errors, (Integer, int)):
            number_errors = (number_errors, number_errors)
        if not isinstance(number_errors, (tuple, list)):
            raise ValueError("number_errors must be a tuple, a list, an Integer or a Python int")

        if isinstance(number_erasures, (Integer, int)):
            number_erasures = (number_erasures, number_erasures)
        if not isinstance(number_erasures, (tuple, list)):
            raise ValueError("number_erasures must be a tuple, a list, an Integer or a Python int")

        output_space = cartesian_product([space, VectorSpace(GF(2), space.dimension())])
        super(ErrorErasureChannel, self).__init__(space, output_space)
        if number_errors[1] + number_erasures[1] > space.dimension():
            raise ValueError("The total number of errors and erasures can not exceed the dimension of the input space")
        self._number_errors = number_errors
        self._number_erasures = number_erasures
Ejemplo n.º 8
0
def ExtendedBinaryGolayCode():
    """
    ExtendedBinaryGolayCode() returns the extended binary Golay code.
    This is a perfect [24,12,8] code. This code is self-dual.

    EXAMPLES::

        sage: C = codes.ExtendedBinaryGolayCode()
        sage: C
        Linear code of length 24, dimension 12 over Finite Field of size 2
        sage: C.minimum_distance()
        8
        sage: C.minimum_distance(algorithm='gap') # long time, check d=8
        8

    AUTHORS:

    - David Joyner (2007-05)
    """
    B = [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1],\
         [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0],\
         [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1],\
         [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0],\
         [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1],\
         [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1],\
         [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1],\
         [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0],\
         [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0],\
         [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0],\
         [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1],\
         [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1]]
    V = span(B, GF(2))
    return LinearCodeFromVectorSpace(V, d=8)
Ejemplo n.º 9
0
    def __init__(self,segment,rho,rhoexp):
        """
        Initializes self.

        See ``AssociatedFactor`` for full documentation.

        """
        self.segment = segment
        self.rho = rho
        self.rhoexp = rhoexp
        self.Fplus = self.rho.degree()

        if self.segment.frame.is_first():
            # In the first frame, so FFbase is the residue class field of O
            self.FFbase = self.segment.frame.R
        else:
            # Not the first frame
            self.FFbase = self.segment.frame.prev.FF

        if self.Fplus == 1:
            self.FF = self.FFbase
            self.FFz = PolynomialRing(self.FF,'z'+str(self.segment.frame.depth))
            # rho is linear delta is the root of rho
            self.delta = self.rho.roots()[0][0]
        else:
            self.FF = GF(self.FFbase.order()**self.Fplus,'a'+str(self.segment.frame.depth))
            self.FFz = PolynomialRing(self.FF,'z'+str(self.segment.frame.depth))
            self.FFbase_gamma = (self.FFz(self.FFbase.modulus())).roots()[0][0]
            FFrho = self.FFz([self.FFbase_elt_to_FF(a) for a in list(rho)])
            self.gamma = FFrho.roots()[0][0]
            basis = [(self.gamma**j*self.FFbase_gamma**i).polynomial() for j in range(0,self.Fplus) for i in range(0,self.FFbase.degree())]
            self.basis_trans_mat = Matrix([self.FF(b)._vector_() for b in basis])
Ejemplo n.º 10
0
def ExtendedTernaryGolayCode():
    """
    ExtendedTernaryGolayCode returns a ternary Golay code. This is a
    self-dual perfect [12,6,6] code.

    EXAMPLES::

        sage: C = codes.ExtendedTernaryGolayCode()
        sage: C
        Linear code of length 12, dimension 6 over Finite Field of size 3
        sage: C.minimum_distance()
        6
        sage: C.minimum_distance(algorithm='gap') # long time, check d=6
        6

    AUTHORS:

    - David Joyner (11-2005)
    """
    B = [[1, 0, 0, 0, 0, 0, 2, 0, 1, 2, 1, 2],\
         [0, 1, 0, 0, 0, 0, 1, 2, 2, 2, 1, 0],\
         [0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1],\
         [0, 0, 0, 1, 0, 0, 1, 1, 0, 2, 2, 2],\
         [0, 0, 0, 0, 1, 0, 2, 1, 2, 2, 0, 1],\
         [0, 0, 0, 0, 0, 1, 0, 2, 1, 2, 2, 1]]
    V = span(B, GF(3))
    return LinearCodeFromVectorSpace(V, d=6)
Ejemplo n.º 11
0
    def __init__(self, poly, prec, print_mode, names, element_class):
        """
        Initializes self

        INPUT:

            - poly -- Polynomial defining this extension.
            - prec -- The precision cap
            - print_mode -- a dictionary with print options
            - names -- a 4-tuple, (variable_name, residue_name,
              unramified_subextension_variable_name, uniformizer_name)
            - element_class -- the class for elements of this unramified extension.

        EXAMPLES::

            sage: R.<a> = Zq(27) #indirect doctest
        """
        #base = poly.base_ring()
        #if base.is_field():
        #    self._PQR = pqr.PolynomialQuotientRing_field(poly.parent(), poly, name = names)
        #else:
        #    self._PQR = pqr.PolynomialQuotientRing_domain(poly.parent(), poly, name = names)
        pAdicExtensionGeneric.__init__(self, poly, prec, print_mode, names,
                                       element_class)
        self._res_field = GF(self.prime_pow.pow_Integer_Integer(poly.degree()),
                             name=names[1],
                             modulus=poly.change_ring(
                                 poly.base_ring().residue_field()))
Ejemplo n.º 12
0
def test():
    "Run unit tests for the module."
    log.getLogger().setLevel(log.DEBUG)

    from sage.graphs.graph_generators import graphs
    K2 = graphs.CompleteGraph(2)
    K4 = graphs.CompleteGraph(4)
    Z2_3 = VectorSpace(GF(2), 3)
    Z2_2 = VectorSpace(GF(2), 2)

    # nonisomorphic_cubes_Z2
    assert len(list(nonisomorphic_cubes_Z2(1))) == 1
    assert len(list(nonisomorphic_cubes_Z2(2))) == 2
    assert len(list(nonisomorphic_cubes_Z2(3))) == 6

    # CayleyGraph
    K4b = CayleyGraph(Z2_2, [(1, 0), (0, 1), (1, 1)])
    assert K4.is_isomorphic(K4b)

    # squash_cube
    Ge = CayleyGraph(Z2_2, [(1, 0), (0, 1)])
    Smap = squash_cube(Ge, (1, 1))
    K2e = Ge.subgraph(Smap.values())
    assert K2.is_isomorphic(K2e)
    assert is_hom(Ge, K2e, Smap)

    # hom_is_by_subspace
    Gg = CayleyGraph(Z2_3, [(1, 0, 0), (0, 1, 0)])
    Hg = CayleyGraph(Z2_2, [(1, 0), (0, 1), (1, 1)])
    homg = {
        (0, 0, 0): (0, 0),
        (1, 0, 0): (1, 0),
        (0, 1, 0): (0, 1),
        (1, 1, 0): (1, 1),
        (0, 0, 1): (0, 0),
        (1, 0, 1): (1, 0),
        (0, 1, 1): (1, 1),
        (1, 1, 1): (0, 1)
    }
    assert is_hom(Gg, Hg, homg)
    assert hom_is_by_subspace(Gg, Z2_3, homg, require_isomorphic=False)
    assert not hom_is_by_subspace(Gg, Z2_3, homg, require_isomorphic=True)
    for h in extend_hom(Gg, K2, limit=10):
        assert hom_is_by_subspace(Gg, Z2_3, h, require_isomorphic=True)

    log.info("All tests passed.")
Ejemplo n.º 13
0
def squash_cube(G, c):
    """
  Assuming G is a cube-like _undirected_ graph (Cayley over Z_2^n),
  given a vector c from Z_2^n, finds a homomorphism unifying
  vectors differing by c. One of the vertives (v, v+c) still must
  be chosen as the target for the homomorphism in each such pair.

  Returns a hom map to a subgraph or None if none such exists.
  """
    n = len(c)
    vs = VectorSpace(GF(2), n)
    c = vs(c)

    pairs = {}  # vertex -> pair
    for v in G.vertices():
        w = tuple(vs(v) + c)
        if G.has_edge(v, w):
            return False  # contracting an edge!
        if w in pairs or v in pairs: continue
        pairs[v] = (v, w)
        pairs[w] = (v, w)
    chosen = {}  # pair -> 0 or 1 (first or second in the pair)
    undecided = set(pairs.values())  # undecided pairs

    def extend(pair, sel):
        """Extend `chosen[]` by setting pair to sel (0 or 1).
    Recursively chooses for pairs forced by this coloring.
    Returns False on inconsistency, True if all the forcing
    succeeded."""
        if pair in chosen:
            if chosen[pair] != sel:
                return False
            return True
        chosen[pair] = sel
        if pair in undecided:
            undecided.remove(pair)
        v = pair[sel]
        w = pair[1 - sel]

        for n in G.neighbors(v):
            np = pairs[n]
            ni = np.index(n)
            if G.has_edge(n, w):
                continue
            if not extend(np, ni):
                return False
        return True

    while undecided:
        p = undecided.pop()
        if not extend(p, 0):
            return None
    # create the hom-map
    hom = {}
    for p, sel in chosen.items():
        hom[p[0]] = p[sel]
        hom[p[1]] = p[sel]
    return hom
Ejemplo n.º 14
0
    def transmit_unsafe(self, message):
        r"""
        Returns ``message`` with as many errors as ``self._number_errors`` in it, and as many erasures
        as ``self._number_erasures`` in it.

        If ``self._number_errors`` was passed as an tuple for the number of errors, it will
        pick a random integer between the bounds of the tuple and use it as the number of errors.
        It does the same with ``self._number_erasures``.

        All erased positions are set to 0 in the transmitted message.
        It is guaranteed that the erasures and the errors will never overlap:
        the received message will always contains exactly as many errors and erasures
        as expected.

        This method does not check if ``message`` belongs to the input space of``self``.

        INPUT:

        - ``message`` -- a vector

        OUTPUT:

        - a couple of vectors, namely:

            - the transmitted message, which is ``message`` with erroneous and erased positions
            - the erasure vector, which contains ``1`` at the erased positions of the transmitted message
              , 0 elsewhere.

        EXAMPLES::

            sage: F = GF(59)^11
            sage: n_err, n_era = 2, 2
            sage: Chan = channels.ErrorErasureChannel(F, n_err, n_era)
            sage: msg = F((3, 14, 15, 9, 26, 53, 58, 9, 7, 9, 3))
            sage: set_random_seed(10)
            sage: Chan.transmit_unsafe(msg)
            ((31, 0, 15, 9, 38, 53, 58, 9, 0, 9, 3), (0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0))
        """
        number_errors = randint(*self.number_errors())
        number_erasures = randint(*self.number_erasures())
        V = self.input_space()
        n = V.dimension()
        zero = V.base_ring().zero()

        errors = sample(xrange(n), number_errors + number_erasures)
        error_positions = errors[:number_errors]
        erasure_positions = errors[number_errors:]

        error_vector = random_error_vector(n, V.base_ring(), error_positions)
        erasure_vector = random_error_vector(n, GF(2), erasure_positions)

        message = message + error_vector

        for i in erasure_positions:
            message[i] = zero
        return message, erasure_vector
def _find_cong1(p, R, ell):
    """
    Find  the list of permissible indices `n` for which `u_n = y^p` mod ``ell``.

    INPUT:

    - ``p`` -- a prime number

    - ``R`` -- an object in class BinaryRecurrenceSequence

    - ``ell`` -- a prime number

    OUTPUT:

    - A list of permissible values of `n` modulo ``period(ell)`` and the integer ``period(ell)``.

    EXAMPLES::

        sage: R = BinaryRecurrenceSequence(1,1)
        sage: sage.combinat.binary_recurrence_sequences._find_cong1(7, R, 29)
        ([0, 1, 2, 12, 13], 14)

    """

    F = GF(ell)
    u0 = F(R.u0)
    u1 = F(R.u1)
    bf, cf = F(R.b), F(R.c)
    a0 = u0
    a1 = u1  #a0 and a1 are variables for terms in sequence

    #The set of pth powers mod ell
    PPowers = set([])
    for i in F:
        PPowers.add(i**p)

    #The period of R mod ell
    modu = R.period(ell)

    #cong1 keeps track of congruences mod modu for the sequence mod ell
    cong1 = []

    for n in xrange(modu):  # n is the index of the a0

        #Check whether a0 is a perfect power mod ell
        if a0 in PPowers:
            #if a0 is a perfect power mod ell, add the index
            #to the list of necessary congruences
            cong1.append(n)

        a0, a1 = a1, bf * a1 + cf * a0  #step up the variables

    cong1.sort()

    return cong1, modu
Ejemplo n.º 16
0
def DuadicCodeOddPair(F, S1, S2):
    """
    Constructs the "odd pair" of duadic codes associated to the
    "splitting" S1, S2 of n.

    .. warning::

       Maybe the splitting should be associated to a sum of
       q-cyclotomic cosets mod n, where q is a *prime*.

    EXAMPLES::

        sage: from sage.coding.code_constructions import is_a_splitting
        sage: n = 11; q = 3
        sage: C = Zmod(n).cyclotomic_cosets(q); C
        [[0], [1, 3, 4, 5, 9], [2, 6, 7, 8, 10]]
        sage: S1 = C[1]
        sage: S2 = C[2]
        sage: is_a_splitting(S1,S2,11)
        True
        sage: codes.DuadicCodeOddPair(GF(q),S1,S2)
        (Linear code of length 11, dimension 6 over Finite Field of size 3,
         Linear code of length 11, dimension 6 over Finite Field of size 3)

    This is consistent with Theorem 6.1.3 in [HP]_.
    """
    n = len(S1) + len(S2) + 1
    if not is_a_splitting(S1, S2, n):
        raise TypeError("%s, %s must be a splitting of %s." % (S1, S2, n))
    q = F.order()
    k = Mod(q, n).multiplicative_order()
    FF = GF(q**k, "z")
    z = FF.gen()
    zeta = z**((q**k - 1) / n)
    P1 = PolynomialRing(FF, "x")
    x = P1.gen()
    g1 = prod([x - zeta**i for i in S1 + [0]])
    g2 = prod([x - zeta**i for i in S2 + [0]])
    j = sum([x**i / n for i in range(n)])
    P2 = PolynomialRing(F, "x")
    x = P2.gen()
    coeffs1 = [
        lift2smallest_field(c)[0] for c in (g1 + j).coefficients(sparse=False)
    ]
    coeffs2 = [
        lift2smallest_field(c)[0] for c in (g2 + j).coefficients(sparse=False)
    ]
    gg1 = P2(coeffs1)
    gg2 = P2(coeffs2)
    C1 = CyclicCodeFromGeneratingPolynomial(n, gg1)
    C2 = CyclicCodeFromGeneratingPolynomial(n, gg2)
    return C1, C2
Ejemplo n.º 17
0
def complementary_spaces_modp(N, p, k0, n, elldash, LWBModp, bound):
    r"""
    Returns a list of lists of lists of lists ``[j,a]``. The pairs ``[j,a]``
    encode the choice of the `a`-th element in the `j`-th list of the input
    ``LWBModp``, i.e., the `a`-th element in a particular basis modulo
    `(p,q^\text{elldash})` for the space of modular forms of level
    `\Gamma_0(N)` and weight `2(j+1)`. The list ``[[j_1,a_1],...,[j_r,a_r]]``
    then encodes the product of the r modular forms associated to each
    ``[j_i,a_i]``; this has weight `k + (p-1)i` for some `0 \le i \le n`; here
    the i is such that this *list of lists* occurs in the ith list of the
    output. The ith list of the output thus encodes a choice of basis for the
    complementary space `W_i` which occurs in Step 2 of Algorithm 2 in [AGBL]_.
    The idea is that one searches for this space `W_i` first modulo
    `(p,q^\text{elldash})` and then, having found the correct products of
    generating forms, one can reconstruct these spaces modulo
    `(p^\text{mdash},q^\text{elldashp})` using the output of this function.
    (This idea is based upon a suggestion of John Voight.)

    INPUT:

    - ``N`` -- positive integer at least 2 and not divisible by p (level).
    - ``p`` -- prime at least 5.
    - ``k0`` -- integer in range 0 to `p-1`.
    - ``n,elldash`` -- positive integers.
    - ``LWBModp`` -- list of lists of `q`-expansions over `GF(p)`.
    - ``bound`` -- positive even integer (twice the length of the list ``LWBModp``).

    OUTPUT:

    - list of list of list of lists.

    EXAMPLES::

        sage: from sage.modular.overconvergent.hecke_series import random_low_weight_bases, complementary_spaces_modp
        sage: LWB = random_low_weight_bases(2,5,2,4,6)
        sage: LWBModp = [[f.change_ring(Zmod(5)) for f in x] for x in LWB]
        sage: complementary_spaces_modp(2,5,0,3,4,LWBModp,6) # random, indirect doctest
        [[[]], [[[0, 0], [0, 0]]], [[[0, 0], [2, 1]]], [[[0, 0], [0, 0], [0, 0], [2, 1]]]]
    """
    CompSpacesCode = []
    ell = dimension_modular_forms(N, k0 + n * (p - 1))
    TotalBasisModp = matrix(GF(p), ell, elldash)
    # zero matrix

    for i in xrange(n + 1):
        NewBasisCodemi = random_new_basis_modp(N, p, k0 + i * (p - 1), LWBModp,
                                               TotalBasisModp, elldash, bound)
        # TotalBasisModp is passed by reference and updated in function
        CompSpacesCode.append(NewBasisCodemi)

    return CompSpacesCode
Ejemplo n.º 18
0
def BinaryReedMullerCode(r, k):
    r"""
    The binary 'Reed-Muller code' with dimension k and
    order r is a code with length `2^k` and minimum distance `2^k-r`
    (see for example, section 1.10 in [HP]_). By definition, the
    `r^{th}` order binary Reed-Muller code of length `n=2^m`, for
    `0 \leq r \leq m`, is the set of all vectors `(f(p)\ |\ p \in GF(2)^m)`,
    where `f` is a multivariate polynomial of degree at most `r`
    in `m` variables.

    INPUT:

    - ``r, k`` -- positive integers with `2^k>r`.

    OUTPUT:

    Returns the binary 'Reed-Muller code' with dimension `k` and order `r`.

    EXAMPLE::

        sage: C = codes.BinaryReedMullerCode(2,4); C  # optional - gap_packages (Guava package)
        Linear code of length 16, dimension 11 over Finite Field of size 2
        sage: C.minimum_distance()              # optional - gap_packages (Guava package)
        4
        sage: C.generator_matrix()                       # optional - gap_packages (Guava package)
        [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]
        [0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1]
        [0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1]
        [0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1]
        [0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1]
        [0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1]
        [0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 1]
        [0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 1]
        [0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1]
        [0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 1]
        [0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1]

    AUTHOR: David Joyner (11-2005)
    """
    F = GF(2)
    gap.load_package("guava")
    gap.eval("C:=ReedMullerCode(" + str(r) + ", " + str(k) + ")")
    gap.eval("G:=GeneratorMat(C)")
    k = int(gap.eval("Length(G)"))
    n = int(gap.eval("Length(G[1])"))
    G = [[
        gfq_gap_to_sage(gap.eval("G[" + str(i) + "][" + str(j) + "]"), F)
        for j in range(1, n + 1)
    ] for i in range(1, k + 1)]
    MS = MatrixSpace(F, k, n)
    return LinearCode(MS(G))
Ejemplo n.º 19
0
def _helper_payley_matrix(n):
    r"""
    Return the marix constructed in Lemma 1.19 page 291 of [SWW72]_.

    This function return a `n^2` matrix `M` whose rows/columns are indexed by
    the element of a finite field on `n` elements `x_1,...,x_n`. The value
    `M_{i,j}` is equal to `\chi(x_i-x_j)`. Note that `n` must be an odd prime power.

    The elements `x_1,...,x_n` are ordered in such a way that the matrix is
    symmetric with respect to its second diagonal. The matrix is symmetric if
    n==4k+1, and skew-symmetric if n=4k-1.

    INPUT:

    - ``n`` -- a prime power

    .. SEEALSO::

        :func:`rshcd_from_close_prime_powers`

    EXAMPLE::

        sage: from sage.combinat.matrices.hadamard_matrix import _helper_payley_matrix
        sage: _helper_payley_matrix(5)
        [ 0  1 -1 -1  1]
        [ 1  0  1 -1 -1]
        [-1  1  0  1 -1]
        [-1 -1  1  0  1]
        [ 1 -1 -1  1  0]
    """
    from sage.rings.finite_rings.constructor import FiniteField as GF
    K = GF(n,conway=True,prefix='x')

    # Order the elements of K in K_list
    # so that K_list[i] = -K_list[n-i-1]
    K_pairs = set(frozenset([x,-x]) for x in K)
    K_pairs.discard(frozenset([0]))
    K_list = [None]*n
    for i,(x,y) in enumerate(K_pairs):
        K_list[i]   = x
        K_list[-i-1] = y
    K_list[n//2] = K(0)

    M = matrix(n,[[2*((x-y).is_square())-1
                   for x in K_list]
                  for y in K_list])
    M = M-I(n)
    assert (M*J(n)).is_zero()
    assert (M*M.transpose()) == n*I(n)-J(n)
    return M
Ejemplo n.º 20
0
    def interpolation_polynomial(self, k=None):
        r"""
        Return a univariate polynomial over an extension field
        representing this S-box.

        If ``m`` is the input length of this S-box then the extension
        field is of degree ``m``.

        If the output length does not match the input length then a
        ``TypeError`` is raised.

        INPUT:

        - ``k`` - an instance of `\GF{2^m}` (default: ``None``)

        EXAMPLE::

            sage: S = mq.SBox(7,6,0,4,2,5,1,3)
            sage: f = S.interpolation_polynomial()
            sage: f
            x^6 + a*x^5 + (a + 1)*x^4 + (a^2 + a + 1)*x^3
              + (a^2 + 1)*x^2 + (a + 1)*x + a^2 + a + 1

            sage: a = f.base_ring().gen()

            sage: f(0), S(0)
            (a^2 + a + 1, 7)

            sage: f(a^2 + 1), S(5)
            (a^2 + 1, 5)
        """
        if self.m != self.n:
            raise TypeError(
                "Lagrange interpolation only supported if self.m == self.n.")

        if k is None:
            k = GF(2**self.m, 'a')
        l = []
        for i in xrange(2**self.m):
            i = self.to_bits(i, self.m)
            o = self(i)
            if self._big_endian:
                i = reversed(i)
                o = reversed(o)
            l.append((k(vector(i)), k(vector(o))))

        P = PolynomialRing(k, 'x')
        return P.lagrange_polynomial(l)
Ejemplo n.º 21
0
def DuadicCodeEvenPair(F, S1, S2):
    r"""
    Constructs the "even pair" of duadic codes associated to the
    "splitting" (see the docstring for ``is_a_splitting``
    for the definition) S1, S2 of n.

    .. warning::

       Maybe the splitting should be associated to a sum of
       q-cyclotomic cosets mod n, where q is a *prime*.

    EXAMPLES::

        sage: from sage.coding.code_constructions import is_a_splitting
        sage: n = 11; q = 3
        sage: C = Zmod(n).cyclotomic_cosets(q); C
        [[0], [1, 3, 4, 5, 9], [2, 6, 7, 8, 10]]
        sage: S1 = C[1]
        sage: S2 = C[2]
        sage: is_a_splitting(S1,S2,11)
        True
        sage: codes.DuadicCodeEvenPair(GF(q),S1,S2)
        (Linear code of length 11, dimension 5 over Finite Field of size 3,
         Linear code of length 11, dimension 5 over Finite Field of size 3)
    """
    n = len(S1) + len(S2) + 1
    if not is_a_splitting(S1, S2, n):
        raise TypeError("%s, %s must be a splitting of %s." % (S1, S2, n))
    q = F.order()
    k = Mod(q, n).multiplicative_order()
    FF = GF(q**k, "z")
    z = FF.gen()
    zeta = z**((q**k - 1) / n)
    P1 = PolynomialRing(FF, "x")
    x = P1.gen()
    g1 = prod([x - zeta**i for i in S1 + [0]])
    g2 = prod([x - zeta**i for i in S2 + [0]])
    P2 = PolynomialRing(F, "x")
    x = P2.gen()
    gg1 = P2(
        [lift2smallest_field(c)[0] for c in g1.coefficients(sparse=False)])
    gg2 = P2(
        [lift2smallest_field(c)[0] for c in g2.coefficients(sparse=False)])
    C1 = CyclicCodeFromGeneratingPolynomial(n, gg1)
    C2 = CyclicCodeFromGeneratingPolynomial(n, gg2)
    return C1, C2
Ejemplo n.º 22
0
def QuasiQuadraticResidueCode(p):
    r"""
    A (binary) quasi-quadratic residue code (or QQR code), as defined by
    Proposition 2.2 in [BM]_, has a generator matrix in the block form `G=(Q,N)`.
    Here `Q` is a `p \times p` circulant matrix whose top row
    is `(0,x_1,...,x_{p-1})`, where `x_i=1` if and only if `i`
    is a quadratic residue `\mod p`, and `N` is a `p \times p` circulant
    matrix whose top row is `(0,y_1,...,y_{p-1})`, where `x_i+y_i=1` for all `i`.

    INPUT:

    - ``p`` -- a prime `>2`.

    OUTPUT:

    Returns a QQR code of length `2p`.

    EXAMPLES::

        sage: C = codes.QuasiQuadraticResidueCode(11); C   # optional - gap_packages (Guava package)
        Linear code of length 22, dimension 11 over Finite Field of size 2

    REFERENCES:

    .. [BM] Bazzi and Mitter, {\it Some constructions of codes from group actions}, (preprint
      March 2003, available on Mitter's MIT website).

    .. [Jresidue] D. Joyner, {\it On quadratic residue codes and hyperelliptic curves},
      (preprint 2006)

    These are self-orthogonal in general and self-dual when $p \\equiv 3 \\pmod 4$.

    AUTHOR: David Joyner (11-2005)
    """
    F = GF(2)
    gap.load_package("guava")
    gap.eval("C:=QQRCode(" + str(p) + ")")
    gap.eval("G:=GeneratorMat(C)")
    k = int(gap.eval("Length(G)"))
    n = int(gap.eval("Length(G[1])"))
    G = [[
        gfq_gap_to_sage(gap.eval("G[%s][%s]" % (i, j)), F)
        for j in range(1, n + 1)
    ] for i in range(1, k + 1)]
    MS = MatrixSpace(F, k, n)
    return LinearCode(MS(G))
Ejemplo n.º 23
0
    def residue_class_field(self):
        """
        Returns the residue class field.

        INPUT::

            self -- a p-adic ring

        OUTPUT::

            the residue field

        EXAMPLES::

            sage: R = Zp(3,5,'fixed-mod')
            sage: k = R.residue_class_field()
            sage: k
            Finite Field of size 3
        """
        from sage.rings.finite_rings.constructor import GF
        return GF(self.prime())
Ejemplo n.º 24
0
def BinaryGolayCode():
    r"""
    BinaryGolayCode() returns a binary Golay code. This is a perfect
    [23,12,7] code. It is also (equivalent to) a cyclic code, with
    generator polynomial
    `g(x)=1+x^2+x^4+x^5+x^6+x^{10}+x^{11}`. Extending it yields
    the extended Golay code (see ExtendedBinaryGolayCode).

    EXAMPLE::

        sage: C = codes.BinaryGolayCode()
        sage: C
        Linear code of length 23, dimension 12 over Finite Field of size 2
        sage: C.minimum_distance()
        7
        sage: C.minimum_distance(algorithm='gap') # long time, check d=7
        7

    AUTHORS:

    - David Joyner (2007-05)
    """
    F = GF(2)
    B = [[1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\
          [0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\
          [0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],\
          [0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],\
          [0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0],\
          [0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0],\
          [0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0],\
          [0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0],\
          [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0],\
          [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0],\
          [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0],\
          [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1]]
    # MS = MatrixSpace(F,12,23)
    # V = VectorSpace(F,23)
    V = span(B, F)
    return LinearCodeFromVectorSpace(V, d=7)
Ejemplo n.º 25
0
def lift2smallest_field2(a):
    """
    INPUT: a is an element of a finite field GF(q) OUTPUT: the element
    b of the smallest subfield F of GF(q) for which F(b)=a.

    EXAMPLES::

        sage: from sage.coding.code_constructions import lift2smallest_field2
        sage: FF.<z> = GF(3^4,"z")
        sage: a = z^40
        sage: lift2smallest_field2(a)
        (2, Finite Field of size 3)
        sage: FF.<z> = GF(2^4,"z")
        sage: a = z^15
        sage: lift2smallest_field2(a)
        (1, Finite Field of size 2)

    .. warning::

       Since coercion (the FF(b) step) has a bug in it, this
       *only works* in the case when you *know* F is a prime field.

    AUTHORS:

    - David Joyner
    """
    FF = a.parent()
    q = FF.order()
    if q.is_prime():
        return a,FF
    p = q.factor()[0][0]
    k = q.factor()[0][1]
    for d in divisors(k):
        F = GF(p**d,"zz")
        for b in F:
            if FF(b) == a:
                return b, F
Ejemplo n.º 26
0
def complementary_spaces(N, p, k0, n, mdash, elldashp, elldash, modformsring,
                         bound):
    r"""
    Returns a list ``Ws``, each element in which is a list ``Wi`` of
    q-expansions modulo `(p^\text{mdash},q^\text{elldashp})`. The list ``Wi`` is
    a basis for a choice of complementary space in level `\Gamma_0(N)` and
    weight `k` to the image of weight `k - (p-1)` forms under multiplication by
    the Eisenstein series `E_{p-1}`.

    The lists ``Wi`` play the same role as `W_i` in Step 2 of Algorithm 2 in
    [AGBL]_. (The parameters ``k0,n,mdash,elldash,elldashp = elldash*p`` are
    defined as in Step 1 of that algorithm when this function is used in
    :func:`hecke_series`.) However, the complementary spaces are computed in a
    different manner, combining a suggestion of David Loeffler with one of John
    Voight. That is, one builds these spaces recursively using random products
    of forms in low weight, first searching for suitable products modulo
    `(p,q^\text{elldash})`, and then later reconstructing only the required
    products to the full precision modulo `(p^\text{mdash},q^{elldashp})`. The
    forms in low weight are chosen from either bases of all forms up to weight
    ``bound`` or from a (tentative) generating set for the ring of all modular
    forms, according to whether ``modformsring`` is ``False`` or ``True``.

    INPUT:

    - ``N`` -- positive integer at least 2 and not divisible by p (level).
    - ``p`` -- prime at least 5.
    - ``k0`` -- integer in range 0 to ``p-1``.
    - ``n,mdash,elldashp,elldash`` -- positive integers.
    - ``modformsring`` -- True or False.
    - ``bound`` -- positive (even) integer (ignored if ``modformsring`` is True).

    OUTPUT:

    - list of lists of q-expansions modulo
      ``(p^\text{mdash},q^\text{elldashp})``.

    EXAMPLES::

        sage: from sage.modular.overconvergent.hecke_series import complementary_spaces
        sage: complementary_spaces(2,5,0,3,2,5,4,true,6) # random
        [[1],
        [1 + 23*q + 24*q^2 + 19*q^3 + 7*q^4 + O(q^5)],
        [1 + 21*q + 2*q^2 + 17*q^3 + 14*q^4 + O(q^5)],
        [1 + 19*q + 9*q^2 + 11*q^3 + 9*q^4 + O(q^5)]]
        sage: complementary_spaces(2,5,0,3,2,5,4,false,6) # random
        [[1],
        [3 + 4*q + 2*q^2 + 12*q^3 + 11*q^4 + O(q^5)],
        [2 + 2*q + 14*q^2 + 19*q^3 + 18*q^4 + O(q^5)],
        [6 + 8*q + 10*q^2 + 23*q^3 + 4*q^4 + O(q^5)]]
    """
    if modformsring == False:
        LWB = random_low_weight_bases(N, p, mdash, elldashp, bound)
    else:
        LWB, bound = low_weight_generators(N, p, mdash, elldashp)

    LWBModp = [[f.change_ring(GF(p)).truncate_powerseries(elldash) for f in x]
               for x in LWB]

    CompSpacesCode = complementary_spaces_modp(N, p, k0, n, elldash, LWBModp,
                                               bound)

    Ws = []
    Epm1 = eisenstein_series_qexp(p - 1,
                                  prec=elldashp,
                                  K=Zmod(p**mdash),
                                  normalization="constant")
    for i in xrange(n + 1):
        CompSpacesCodemi = CompSpacesCode[i]
        Wi = []
        for k in xrange(len(CompSpacesCodemi)):
            CompSpacesCodemik = CompSpacesCodemi[k]
            Wik = Epm1.parent()(1)
            for j in xrange(len(CompSpacesCodemik)):
                l = CompSpacesCodemik[j][0]
                index = CompSpacesCodemik[j][1]
                Wik = Wik * LWB[l][index]
            Wi.append(Wik)
        Ws.append(Wi)

    return Ws
def _is_p_power_mod(a, p, N):
    """
    Determine if ``a`` is a ``p`` th power modulo ``N``.

    By the CRT, this is equivalent to the condition that ``a`` be a ``p`` th power mod all
    distinct prime powers dividing ``N``.  For each of these, we use the strong statement of
    Hensel's lemma to lift ``p`` th powers mod `q` or `q^2` or `q^3` to ``p`` th powers mod `q^e`.

    INPUT:

    - ``a`` -- an integer

    - ``p`` -- a rational prime number

    - ``N`` -- a positive integer

    OUTPUT:

    - True if ``a`` is a ``p`` th power modulo ``N``; False otherwise.

    EXAMPLES::

        sage: sage.combinat.binary_recurrence_sequences._is_p_power_mod(2**3,7,29)
        False
        sage: sage.combinat.binary_recurrence_sequences._is_p_power_mod(2**3,3,29)
        True

    """

    #By the chinese remainder theorem, we can answer this question by examining whether
    #a is a pth power mod q^e, for all distinct prime powers q^e dividing N.

    for q, e in N.factor():

        #If a = q^v*x, with

        v = a.valuation(q)

        #then if v>=e, a is congruent to 0 mod q^e and is thus a pth power trivially.

        if v >= e:
            continue

        #otherwise, it can only be a pth power if v is a multiple of p.

        if v % p != 0:
            return False

        #in this cse it is a pth power if x is a pth power mod q^(e-v), so let x = aa,
        #and (e-v) = ee:

        aa = a / q**v
        ee = e - v

        #The above steps are equivalent to the statement that we may assume a and qq are
        #relatively prime, if we replace a with aa and e with ee.  Now we must determine when
        #aa is a pth power mod q^ee for (aa,q)=1.

        #If q != p, then by Hensel's lemma, we may lift a pth power mod q, to a pth power
        #mod q^2, etc.

        if q != p:

            #aa is necessarily a pth power mod q if p does not divide the order of the multiplicative
            #group mod q, ie if q is not 1 mod p.

            if q % p == 1:

                #otherwise aa if a pth power mod q iff aa^(q-1)/p == 1

                if GF(q)(aa)**((q - 1) / p) != 1:
                    return False

        #If q = p and ee = 1, then everything is a pth power p by Fermat's little theorem.

        elif ee > 1:

            #We use the strong statement of Hensel's lemma, which implies that if p is odd
            #and aa is a pth power mod p^2, then aa is a pth power mod any higher power of p

            if p % 2 == 1:

                #ZZ/(p^2)ZZ^\times is abstractly isomorphic to ZZ/(p)ZZ cross ZZ/(p-1)ZZ. then
                #aa is a pth power mod p^2 if (aa)^(p*(p-1)/p) == 1, ie if aa^(p-1) == 1.

                if Integers(p**2)(aa)**(p - 1) != 1:
                    return False

            #Otherwise, p=2.  By the strong statement of Hensel's lemma, if aa is a pth power
            #mod p^3, then it is a pth power mod higher powers of p.  So we need only check if it
            #is a pth power mod p^2 and p^3.

            elif ee == 2:

                #all odd squares a 1 mod 4

                if aa % 4 != 1:
                    return False

            #all odd squares are 1 mod 8

            elif aa % 8 != 1:
                return False

    return True
    def pthpowers(self, p, Bound):
        """
        Find the indices of proveably all pth powers in the recurrence sequence bounded by Bound.

        Let `u_n` be a binary recurrence sequence.  A ``p`` th power in `u_n` is a solution
        to `u_n = y^p` for some integer `y`.  There are only finitely many ``p`` th powers in
        any recurrence sequence [SS].

        INPUT:

        - ``p`` - a rational prime integer (the fixed p in `u_n = y^p`)

        - ``Bound`` - a natural number (the maximum index `n` in `u_n = y^p` that is checked).

        OUTPUT:

        - A list of the indices of all ``p`` th powers less bounded by ``Bound``.  If the sequence is degenerate and there are many ``p`` th powers, raises ``ValueError``.

        EXAMPLES::

            sage: R = BinaryRecurrenceSequence(1,1)        #the Fibonacci sequence
            sage: R.pthpowers(2, 10**30)        # long time (7 seconds) -- in fact these are all squares, c.f. [BMS06]
            [0, 1, 2, 12]

            sage: S = BinaryRecurrenceSequence(8,1) #a Lucas sequence
            sage: S.pthpowers(3,10**30)    # long time (3 seconds) -- provably finds the indices of all 3rd powers less than 10^30
            [0, 1, 2]

            sage: Q = BinaryRecurrenceSequence(3,3,2,1)
            sage: Q.pthpowers(11,10**30)          # long time (7.5 seconds)
            [1]

        If the sequence is degenerate, and there are are no ``p`` th powers, returns `[]`.  Otherwise, if
        there are many ``p`` th powers, raises ``ValueError``.

        ::

            sage: T = BinaryRecurrenceSequence(2,0,1,2)
            sage: T.is_degenerate()
            True
            sage: T.is_geometric()
            True
            sage: T.pthpowers(7,10**30)
            Traceback (most recent call last):
            ...
            ValueError: The degenerate binary recurrence sequence is geometric or quasigeometric and has many pth powers.

            sage: L = BinaryRecurrenceSequence(4,0,2,2)
            sage: [L(i).factor() for i in xrange(10)]
            [2, 2, 2^3, 2^5, 2^7, 2^9, 2^11, 2^13, 2^15, 2^17]
            sage: L.is_quasigeometric()
            True
            sage: L.pthpowers(2,10**30)
            []

        NOTE: This function is primarily optimized in the range where ``Bound`` is much larger than ``p``.

        """

        #Thanks to Jesse Silliman for helpful conversations!

        #Reset the dictionary of good primes, as this depends on p
        self._PGoodness = {}
        #Starting lower bound on good primes
        self._ell = 1

        #If the sequence is geometric, then the `n`th term is `a*r^n`.  Thus the
        #property of being a ``p`` th power is periodic mod ``p``.  So there are either
        #no ``p`` th powers if there are none in the first ``p`` terms, or many if there
        #is at least one in the first ``p`` terms.

        if self.is_geometric() or self.is_quasigeometric():
            no_powers = True
            for i in xrange(1, 6 * p + 1):
                if _is_p_power(self(i), p):
                    no_powers = False
                    break
            if no_powers:
                if _is_p_power(self.u0, p):
                    return [0]
                return []
            else:
                raise ValueError(
                    "The degenerate binary recurrence sequence is geometric or quasigeometric and has many pth powers."
                )

        #If the sequence is degenerate without being geometric or quasigeometric, there
        #may be many ``p`` th powers or no ``p`` th powers.

        elif (self.b**2 + 4 * self.c) == 0:

            #This is the case if the matrix F is not diagonalizable, ie b^2 +4c = 0, and alpha/beta = 1.

            alpha = self.b / 2

            #In this case, u_n = u_0*alpha^n + (u_1 - u_0*alpha)*n*alpha^(n-1) = alpha^(n-1)*(u_0 +n*(u_1 - u_0*alpha)),
            #that is, it is a geometric term (alpha^(n-1)) times an arithmetic term (u_0 + n*(u_1-u_0*alpha)).

            #Look at classes n = k mod p, for k = 1,...,p.

            for k in xrange(1, p + 1):

                #The linear equation alpha^(k-1)*u_0 + (k+pm)*(alpha^(k-1)*u1 - u0*alpha^k)
                #must thus be a pth power.  This is a linear equation in m, namely, A + B*m, where

                A = (alpha**(k - 1) * self.u0 + k *
                     (alpha**(k - 1) * self.u1 - self.u0 * alpha**k))
                B = p * (alpha**(k - 1) * self.u1 - self.u0 * alpha**k)

                #This linear equation represents a pth power iff A is a pth power mod B.

                if _is_p_power_mod(A, p, B):
                    raise ValueError(
                        "The degenerate binary recurrence sequence has many pth powers."
                    )
            return []

        #We find ``p`` th powers using an elementary sieve.  Term `u_n` is a ``p`` th
        #power if and only if it is a ``p`` th power modulo every prime `\\ell`.  This condition
        #gives nontrivial information if ``p`` divides the order of the multiplicative group of
        #`\\Bold(F)_{\\ell}`, i.e. if `\\ell` is ` 1 \mod{p}`, as then only `1/p` terms are ``p`` th
        #powers modulo `\\ell``.

        #Thus, given such an `\\ell`, we get a set of necessary congruences for the index modulo the
        #the period of the sequence mod `\\ell`.  Then we intersect these congruences for many primes
        #to get a tight list modulo a growing modulus.  In order to keep this step manageable, we
        #only use primes `\\ell` that are have particularly smooth periods.

        #Some congruences in the list will remain as the modulus grows.  If a congruence remains through
        #7 rounds of increasing the modulus, then we check if this corresponds to a perfect power (if
        #it does, we add it to our list of indices corresponding to ``p`` th powers).  The rest of the congruences
        #are transient and grow with the modulus.  Once the smallest of these is greater than the bound,
        #the list of known indices corresponding to ``p`` th powers is complete.

        else:

            if Bound < 3 * p:

                powers = []
                ell = p + 1

                while not is_prime(ell):
                    ell = ell + p

                F = GF(ell)
                a0 = F(self.u0)
                a1 = F(self.u1)  #a0 and a1 are variables for terms in sequence
                bf, cf = F(self.b), F(self.c)

                for n in xrange(Bound):  # n is the index of the a0

                    #Check whether a0 is a perfect power mod ell
                    if _is_p_power_mod(a0, p, ell):
                        #if a0 is a perfect power mod ell, check if nth term is ppower
                        if _is_p_power(self(n), p):
                            powers.append(n)

                    a0, a1 = a1, bf * a1 + cf * a0  #step up the variables

            else:

                powers = [
                ]  #documents the indices of the sequence that provably correspond to pth powers
                cong = [
                    0
                ]  #list of necessary congruences on the index for it to correspond to pth powers
                Possible_count = {
                }  #keeps track of the number of rounds a congruence lasts in cong

                #These parameters are involved in how we choose primes to increase the modulus
                qqold = 1  #we believe that we know complete information coming from primes good by qqold
                M1 = 1  #we have congruences modulo M1, this may not be the tightest list
                M2 = p  #we want to move to have congruences mod M2
                qq = 1  #the largest prime power divisor of M1 is qq

                #This loop ups the modulus.
                while True:

                    #Try to get good data mod M2

                    #patience of how long we should search for a "good prime"
                    patience = 0.01 * _estimated_time(
                        lcm(M2, p * next_prime_power(qq)), M1, len(cong), p)
                    tries = 0

                    #This loop uses primes to get a small set of congruences mod M2.
                    while True:

                        #only proceed if took less than patience time to find the next good prime
                        ell = _next_good_prime(p, self, qq, patience, qqold)
                        if ell:

                            #gather congruence data for the sequence mod ell, which will be mod period(ell) = modu
                            cong1, modu = _find_cong1(p, self, ell)

                            CongNew = [
                            ]  #makes a new list from cong that is now mod M = lcm(M1, modu) instead of M1
                            M = lcm(M1, modu)
                            for k in xrange(M / M1):
                                for i in cong:
                                    CongNew.append(k * M1 + i)
                            cong = set(CongNew)

                            M1 = M

                            killed_something = False  #keeps track of when cong1 can rule out a congruence in cong

                            #CRT by hand to gain speed
                            for i in list(cong):
                                if not (
                                        i % modu in cong1
                                ):  #congruence in cong is inconsistent with any in cong1
                                    cong.remove(i)  #remove that congruence
                                    killed_something = True

                            if M1 == M2:
                                if not killed_something:
                                    tries += 1
                                    if tries == 2:  #try twice to rule out congruences
                                        cong = list(cong)
                                        qqold = qq
                                        qq = next_prime_power(qq)
                                        M2 = lcm(M2, p * qq)
                                        break

                        else:
                            qq = next_prime_power(qq)
                            M2 = lcm(M2, p * qq)
                            cong = list(cong)
                            break

                    #Document how long each element of cong has been there
                    for i in cong:
                        if i in Possible_count:
                            Possible_count[i] = Possible_count[i] + 1
                        else:
                            Possible_count[i] = 1

                    #Check how long each element has persisted, if it is for at least 7 cycles,
                    #then we check to see if it is actually a perfect power
                    for i in Possible_count:
                        if Possible_count[i] == 7:
                            n = Integer(i)
                            if n < Bound:
                                if _is_p_power(self(n), p):
                                    powers.append(n)

                    #check for a contradiction
                    if len(cong) > len(powers):
                        if cong[len(powers)] > Bound:
                            break
                    elif M1 > Bound:
                        break

            return powers
    def period(self, m):
        """
        Return the period of the binary recurrence sequence modulo
        an integer ``m``.

        If `n_1` is congruent to `n_2` modulu ``period(m)``, then `u_{n_1}` is
        is congruent to `u_{n_2}` modulo ``m``.

        INPUT:

        - ``m`` -- an integer (modulo which the period of the recurrence relation is calculated).

        OUTPUT:

        - The integer (the period of the sequence modulo m)

        EXAMPLES:

        If `p = \\pm 1 \\mod 5`, then the period of the Fibonacci sequence
        mod `p` is `p-1` (c.f. Lemma 3.3 of [BMS06]).

        ::

            sage: R = BinaryRecurrenceSequence(1,1)
            sage: R.period(31)
            30

            sage: [R(i) % 4 for i in xrange(12)]
            [0, 1, 1, 2, 3, 1, 0, 1, 1, 2, 3, 1]
            sage: R.period(4)
            6

        This function works for degenerate sequences as well.

        ::

            sage: S = BinaryRecurrenceSequence(2,0,1,2)
            sage: S.is_degenerate()
            True
            sage: S.is_geometric()
            True
            sage: [S(i) % 17 for i in xrange(16)]
            [1, 2, 4, 8, 16, 15, 13, 9, 1, 2, 4, 8, 16, 15, 13, 9]
            sage: S.period(17)
            8

        Note: the answer is cached.
        """

        #If we have already computed the period mod m, then we return the stored value.

        if m in self._period_dict:
            return self._period_dict[m]

        else:
            R = Integers(m)
            A = matrix(R, [[0, 1], [self.c, self.b]])
            w = matrix(R, [[self.u0], [self.u1]])
            Fac = list(m.factor())
            Periods = {}

            #To compute the period mod m, we compute the least integer n such that A^n*w == w.  This necessarily
            #divides the order of A as a matrix in GL_2(Z/mZ).

            #We compute the period modulo all distinct prime powers dividing m, and combine via the lcm.
            #To compute the period mod p^e, we first compute the order mod p.  Then the period mod p^e
            #must divide p^{4e-4}*period(p), as the subgroup of matrices mod p^e, which reduce to
            #the identity mod p is of order (p^{e-1})^4.  So we compute the period mod p^e by successively
            #multiplying the period mod p by powers of p.

            for i in Fac:
                p = i[0]
                e = i[1]
                #first compute the period mod p
                if p in self._period_dict:
                    perp = self._period_dict[p]
                else:
                    F = A.change_ring(GF(p))
                    v = w.change_ring(GF(p))
                    FF = F**(p - 1)
                    p1fac = list((p - 1).factor())

                    #The order of any matrix in GL_2(F_p) either divides p(p-1) or (p-1)(p+1).
                    #The order divides p-1 if it is diagaonalizable.  In any case, det(F^(p-1))=1,
                    #so if tr(F^(p-1)) = 2, then it must be triangular of the form [[1,a],[0,1]].
                    #The order of the subgroup of matrices of this form is p, so the order must divide
                    #p(p-1) -- in fact it must be a multiple of p.  If this is not the case, then the
                    #order divides (p-1)(p+1).  As the period divides the order of the matrix in GL_2(F_p),
                    #these conditions hold for the period as well.

                    #check if the order divides (p-1)
                    if FF * v == v:
                        M = p - 1
                        Mfac = p1fac

                    #check if the trace is 2, then the order is a multiple of p dividing p*(p-1)
                    elif (FF).trace() == 2:
                        M = p - 1
                        Mfac = p1fac
                        F = F**p  #replace F by F^p as now we only need to determine the factor dividing (p-1)

                    #otherwise it will divide (p+1)(p-1)
                    else:
                        M = (p + 1) * (p - 1)
                        p2fac = list(
                            (p + 1).factor()
                        )  #factor the (p+1) and (p-1) terms seperately and then combine for speed
                        Mfac_dic = {}
                        for i in list(p1fac + p2fac):
                            if i[0] not in Mfac_dic:
                                Mfac_dic[i[0]] = i[1]
                            else:
                                Mfac_dic[i[0]] = Mfac_dic[i[0]] + i[1]
                        Mfac = [(i, Mfac_dic[i]) for i in Mfac_dic]

                    #Now use a fast order algorithm to compute the period.  We know that the period divides
                    #M = i_1*i_2*...*i_l where the i_j denote not necessarily distinct prime factors.  As
                    #F^M*v == v, for each i_j, if F^(M/i_j)*v == v, then the period divides (M/i_j).  After
                    #all factors have been iterated over, the result is the period mod p.

                    Mfac = list(Mfac)
                    C = []

                    #expand the list of prime factors so every factor is with multiplicity 1

                    for i in xrange(len(Mfac)):
                        for j in xrange(Mfac[i][1]):
                            C.append(Mfac[i][0])

                    Mfac = C
                    n = M
                    for i in Mfac:
                        b = Integer(n / i)
                        if F**b * v == v:
                            n = b
                    perp = n

                #Now compute the period mod p^e by steping up by multiples of p
                F = A.change_ring(Integers(p**e))
                v = w.change_ring(Integers(p**e))
                FF = F**perp
                if FF * v == v:
                    perpe = perp
                else:
                    tries = 0
                    while True:
                        tries += 1
                        FF = FF**p
                        if FF * v == v:
                            perpe = perp * p**tries
                            break
                Periods[p] = perpe

            #take the lcm of the periods mod all distinct primes dividing m
            period = 1
            for p in Periods:
                period = lcm(Periods[p], period)

            self._period_dict[m] = period  #cache the period mod m
            return period
Ejemplo n.º 30
0
def v_4_1_rbibd(v, existence=False):
    r"""
    Return a `(v,4,1)`-RBIBD.

    INPUT:

    - `n` (integer)

    - ``existence`` (boolean; ``False`` by default) -- whether to build the
      design or only answer whether it exists.

    .. SEEALSO::

        - :meth:`IncidenceStructure.is_resolvable`
        - :func:`resolvable_balanced_incomplete_block_design`

    .. NOTE::

        A resolvable `(v,4,1)`-BIBD exists whenever `1\equiv 4\pmod(12)`. This
        function, however, only implements a construction of `(v,4,1)`-BIBD such
        that `v=3q+1\equiv 1\pmod{3}` where `q` is a prime power (see VII.7.5.a
        from [BJL99]_).

    EXAMPLE::

        sage: rBIBD = designs.resolvable_balanced_incomplete_block_design(28,4)
        sage: rBIBD.is_resolvable()
        True
        sage: rBIBD.is_t_design(return_parameters=True)
        (True, (2, 28, 4, 1))

    TESTS::

        sage: for q in prime_powers(2,30):
        ....:     if (3*q+1)%12 == 4:
        ....:         _ = designs.resolvable_balanced_incomplete_block_design(3*q+1,4) # indirect doctest
    """
    # Volume 1, VII.7.5.a from [BJL99]_
    if v % 3 != 1 or not is_prime_power((v - 1) // 3):
        if existence:
            return Unknown
        raise NotImplementedError(
            "I don't know how to build a ({},{},1)-RBIBD!".format(v, 4))
    from sage.rings.finite_rings.constructor import FiniteField as GF
    q = (v - 1) // 3
    nn = (q - 1) // 4
    G = GF(q, 'x')
    w = G.primitive_element()
    e = w**(nn)
    assert e**2 == -1

    first_class = [[(w**i, j), (-w**i, j), (e * w**i, j + 1),
                    (-e * w**i, j + 1)] for i in range(nn) for j in range(3)]

    first_class.append([(0, 0), (0, 1), (0, 2), 'inf'])

    label = {p: i for i, p in enumerate(G)}

    classes = [[[
        v - 1 if x == 'inf' else (x[1] % 3) * q + label[x[0] + g] for x in S
    ] for S in first_class] for g in G]

    BIBD = BalancedIncompleteBlockDesign(v,
                                         blocks=sum(classes, []),
                                         k=4,
                                         check=True,
                                         copy=False)
    BIBD._classes = classes
    assert BIBD.is_resolvable()
    return BIBD
Ejemplo n.º 31
0
def kirkman_triple_system(v, existence=False):
    r"""
    Return a Kirkman Triple System on `v` points.

    A Kirkman Triple System `KTS(v)` is a resolvable Steiner Triple System. It
    exists if and only if `v\equiv 3\pmod{6}`.

    INPUT:

    - `n` (integer)

    - ``existence`` (boolean; ``False`` by default) -- whether to build the
      `KTS(n)` or only answer whether it exists.

    .. SEEALSO::

        :meth:`IncidenceStructure.is_resolvable`

    EXAMPLES:

    A solution to Kirkmman's original problem::

        sage: kts = designs.kirkman_triple_system(15)
        sage: classes = kts.is_resolvable(1)[1]
        sage: names = '0123456789abcde'
        sage: to_name = lambda (r,s,t): ' '+names[r]+names[s]+names[t]+' '
        sage: rows = [join(('Day {}'.format(i) for i in range(1,8)), '   ')]
        sage: rows.extend(join(map(to_name,row), '   ') for row in zip(*classes))
        sage: print join(rows,'\n')
        Day 1   Day 2   Day 3   Day 4   Day 5   Day 6   Day 7
         07e     18e     29e     3ae     4be     5ce     6de
         139     24a     35b     46c     05d     167     028
         26b     03c     14d     257     368     049     15a
         458     569     06a     01b     12c     23d     347
         acd     7bd     78c     89d     79a     8ab     9bc

    TESTS::

        sage: for i in range(3,300,6):
        ....:     _ = designs.kirkman_triple_system(i)
    """
    if v % 6 != 3:
        if existence:
            return False
        raise ValueError("There is no KTS({}) as v!=3 mod(6)".format(v))

    if existence:
        return False

    elif v == 3:
        return BalancedIncompleteBlockDesign(3, [[0, 1, 2]], k=3, lambd=1)

    elif v == 9:
        classes = [[[0, 1, 5], [2, 6, 7], [3, 4, 8]],
                   [[1, 6, 8], [3, 5, 7], [0, 2, 4]],
                   [[1, 4, 7], [0, 3, 6], [2, 5, 8]],
                   [[4, 5, 6], [0, 7, 8], [1, 2, 3]]]
        KTS = BalancedIncompleteBlockDesign(
            v, [tr for cl in classes for tr in cl], k=3, lambd=1, copy=False)
        KTS._classes = classes
        return KTS

    # Construction 1.1 from [Stinson91] (originally Theorem 6 from [RCW71])
    #
    # For all prime powers q=1 mod 6, there exists a KTS(2q+1)
    elif ((v - 1) // 2) % 6 == 1 and is_prime_power((v - 1) // 2):
        from sage.rings.finite_rings.constructor import FiniteField as GF
        q = (v - 1) // 2
        K = GF(q, 'x')
        a = K.primitive_element()
        t = (q - 1) / 6

        # m is the solution of a^m=(a^t+1)/2
        from sage.groups.generic import discrete_log
        m = discrete_log((a**t + 1) / 2, a)
        assert 2 * a**m == a**t + 1

        # First parallel class
        first_class = [[(0, 1), (0, 2), 'inf']]
        b0 = K.one()
        b1 = a**t
        b2 = a**m
        first_class.extend([(b0 * a**i, 1), (b1 * a**i, 1), (b2 * a**i, 2)]
                           for i in range(t) + range(2 * t, 3 * t) +
                           range(4 * t, 5 * t))
        b0 = a**(m + t)
        b1 = a**(m + 3 * t)
        b2 = a**(m + 5 * t)
        first_class.extend([[(b0 * a**i, 2), (b1 * a**i, 2), (b2 * a**i, 2)]
                            for i in range(t)])

        # Action of K on the points
        action = lambda v, x: (v + x[0], x[1]) if len(x) == 2 else x

        # relabel to integer
        relabel = {(p, x): i + (x - 1) * q
                   for i, p in enumerate(K) for x in [1, 2]}
        relabel['inf'] = 2 * q

        classes = [[[relabel[action(p, x)] for x in tr] for tr in first_class]
                   for p in K]

        KTS = BalancedIncompleteBlockDesign(
            v, [tr for cl in classes for tr in cl], k=3, lambd=1, copy=False)

        KTS._classes = classes
        return KTS

    # Construction 1.2 from [Stinson91] (originally Theorem 5 from [RCW71])
    #
    # For all prime powers q=1 mod 6, there exists a KTS(3q)
    elif (v // 3) % 6 == 1 and is_prime_power(v // 3):
        from sage.rings.finite_rings.constructor import FiniteField as GF
        q = v // 3
        K = GF(q, 'x')
        a = K.primitive_element()
        t = (q - 1) / 6
        A0 = [(0, 0), (0, 1), (0, 2)]
        B = [[(a**i, j), (a**(i + 2 * t), j), (a**(i + 4 * t), j)]
             for j in range(3) for i in range(t)]
        A = [[(a**i, 0), (a**(i + 2 * t), 1), (a**(i + 4 * t), 2)]
             for i in range(6 * t)]

        # Action of K on the points
        action = lambda v, x: (v + x[0], x[1])

        # relabel to integer
        relabel = {(p, j): i + j * q
                   for i, p in enumerate(K) for j in range(3)}

        B0 = [A0] + B + A[t:2 * t] + A[3 * t:4 * t] + A[5 * t:6 * t]

        # Classes
        classes = [[[relabel[action(p, x)] for x in tr] for tr in B0]
                   for p in K]

        for i in range(t) + range(2 * t, 3 * t) + range(4 * t, 5 * t):
            classes.append([[relabel[action(p, x)] for x in A[i]] for p in K])

        KTS = BalancedIncompleteBlockDesign(
            v, [tr for cl in classes for tr in cl], k=3, lambd=1, copy=False)
        KTS._classes = classes
        return KTS

    else:
        # This is Lemma IX.6.4 from [BJL99].
        #
        # This construction takes a (v,{4,7})-PBD. All points are doubled (x has
        # a copy x'), and an infinite point \infty is added.
        #
        # On all blocks of 2*4 points we "paste" a KTS(2*4+1) using the infinite
        # point, in such a way that all {x,x',infty} are set of the design. We
        # do the same for blocks with 2*7 points using a KTS(2*7+1).
        #
        # Note that the triples of points equal to {x,x',\infty} will be added
        # several times.
        #
        # As all those subdesigns are resolvable, each class of the KTS(n) is
        # obtained by considering a set {x,x',\infty} and all sets of all
        # parallel classes of the subdesign which contain this set.

        # We create the small KTS(n') we need, and relabel them such that
        # 01(n'-1),23(n'-1),... are blocks of the design.
        gdd4 = kirkman_triple_system(9)
        gdd7 = kirkman_triple_system(15)

        X = [B for B in gdd4 if 8 in B]
        for b in X:
            b.remove(8)
        X = sum(X, []) + [8]
        gdd4.relabel({v: i for i, v in enumerate(X)})
        gdd4 = gdd4.is_resolvable(True)[1]  # the relabeled classes

        X = [B for B in gdd7 if 14 in B]
        for b in X:
            b.remove(14)
        X = sum(X, []) + [14]
        gdd7.relabel({v: i for i, v in enumerate(X)})
        gdd7 = gdd7.is_resolvable(True)[1]  # the relabeled classes

        # The first parallel class contains 01(n'-1), the second contains
        # 23(n'-1), etc..
        # Then remove the blocks containing (n'-1)
        for B in gdd4:
            for i, b in enumerate(B):
                if 8 in b:
                    j = min(b)
                    del B[i]
                    B.insert(0, j)
                    break
        gdd4.sort()
        for B in gdd4:
            B.pop(0)

        for B in gdd7:
            for i, b in enumerate(B):
                if 14 in b:
                    j = min(b)
                    del B[i]
                    B.insert(0, j)
                    break
        gdd7.sort()
        for B in gdd7:
            B.pop(0)

        # Pasting the KTS(n') without {x,x',\infty} blocks
        classes = [[] for i in range((v - 1) / 2)]
        gdd = {4: gdd4, 7: gdd7}
        for B in PBD_4_7((v - 1) // 2, check=False):
            for i, classs in enumerate(gdd[len(B)]):
                classes[B[i]].extend([[2 * B[x // 2] + x % 2 for x in BB]
                                      for BB in classs])

        # The {x,x',\infty} blocks
        for i, classs in enumerate(classes):
            classs.append([2 * i, 2 * i + 1, v - 1])

        KTS = BalancedIncompleteBlockDesign(
            v,
            blocks=[tr for cl in classes for tr in cl],
            k=3,
            lambd=1,
            check=True,
            copy=False)
        KTS._classes = classes
        assert KTS.is_resolvable()

        return KTS
Ejemplo n.º 32
0
class AssociatedFactor:
    r"""
    An irreducible factors of the associated polynomials of higher order
    newton polygon segments needed for OM computation.

    For each distinct irreducible factor of the associated polynomial,
    the tree of OM representations branches, thus producing distinct factors
    of the original polynomial.

    If ``rho`` is not linear, then we have found inertia. Future associated
    polynomials wll need to be produced over an extension over our ground
    field by ``rho``.  This can produce a tower of finite field extensions
    to be worked in.

    INPUT:

    - ``segment`` -- The segment whose associated polynomial self is a factor of.

    - ``rho`` -- The irreducible finite field polynomial factor of the
      associated polynomial.

    - ``rhoexp`` -- The multiplicity of the factor.

    """
    def __init__(self,segment,rho,rhoexp):
        """
        Initializes self.

        See ``AssociatedFactor`` for full documentation.

        """
        self.segment = segment
        self.rho = rho
        self.rhoexp = rhoexp
        self.Fplus = self.rho.degree()

        if self.segment.frame.is_first():
            # In the first frame, so FFbase is the residue class field of O
            self.FFbase = self.segment.frame.R
        else:
            # Not the first frame
            self.FFbase = self.segment.frame.prev.FF

        if self.Fplus == 1:
            self.FF = self.FFbase
            self.FFz = PolynomialRing(self.FF,'z'+str(self.segment.frame.depth))
            # rho is linear delta is the root of rho
            self.delta = self.rho.roots()[0][0]
        else:
            self.FF = GF(self.FFbase.order()**self.Fplus,'a'+str(self.segment.frame.depth))
            self.FFz = PolynomialRing(self.FF,'z'+str(self.segment.frame.depth))
            self.FFbase_gamma = (self.FFz(self.FFbase.modulus())).roots()[0][0]
            FFrho = self.FFz([self.FFbase_elt_to_FF(a) for a in list(rho)])
            self.gamma = FFrho.roots()[0][0]
            basis = [(self.gamma**j*self.FFbase_gamma**i).polynomial() for j in range(0,self.Fplus) for i in range(0,self.FFbase.degree())]
            self.basis_trans_mat = Matrix([self.FF(b)._vector_() for b in basis])

    def FF_elt_to_FFbase_vector(self,a):
        """
        Represents an element in our current extended residue field as a
        vector over its ground residue field.

        INPUT:

        - ``a`` -- Element of our extended residue field

        OUTPUT:

        - A list representing a vector of ``a`` over the ground field of
          the latest extension.

        EXAMPLES::

        First we set up AssociatedFactors building a tower of extensions::

            sage: from sage.rings.polynomial.padics.factor.factoring import OM_tree
            sage: k = ZpFM(2,20,'terse'); kx.<x> = k[]
            sage: t = OM_tree(x^4+20*x^3+44*x^2+80*x+1040)
            sage: t[0].prev
            AssociatedFactor of rho z^2 + z + 1
            sage: t[0].polygon[0].factors[0]
            AssociatedFactor of rho z0^2 + a0*z0 + 1

        Then we take elements in the different finite fields and represent
        them as vectors over their base residue field::

            sage: K.<a0> = t[0].prev.FF;K
            Finite Field in a0 of size 2^2
            sage: t[0].prev.FF_elt_to_FFbase_vector(a0+1)
            [1, 1]
            sage: L.<a1> = t[0].polygon[0].factors[0].FF;L
            Finite Field in a1 of size 2^4
            sage: t[0].polygon[0].factors[0].FF_elt_to_FFbase_vector(a1)         
            [1, a0 + 1]

        """
        if self.segment.frame.is_first() and self.Fplus == 1:
            return a
        elif self.Fplus == 1:
            return self.segment.frame.prev.FF_elt_to_FFbase_vector(a)
        else:
            basedeg = self.FFbase.degree()
            avec = self.FF(a)._vector_()
            svector = self.basis_trans_mat.solve_left(Matrix(self.FF.prime_subfield(),avec))
            s_list = svector.list()
            s_split = [ s_list[i*basedeg:(i+1)*basedeg] for i in range(0,self.Fplus)]
            s = [sum([ss[i]*self.FFbase.gen()**i for i in range(0,len(ss))]) for ss in s_split]
            return s

    def FFbase_elt_to_FF(self,b):
        """
        Lifts an element up from the previous residue field to the current
        extended residue field.

        INPUT:

        - ``b`` -- Element in the previous residue field.

        OUTPUT:

        - An element in the current extended residue field.

        EXAMPLES::

        First we set up AssociatedFactors building a tower of extensions::

            sage: from sage.rings.polynomial.padics.factor.factoring import OM_tree
            sage: k = ZpFM(2,20,'terse'); kx.<x> = k[]
            sage: t = OM_tree(x^4+20*x^3+44*x^2+80*x+1040)

        Then we take elements in the different finite fields and lift them
        to the next residue field upward in the extension tower::

            sage: K.<a0> = t[0].prev.FF;K
            Finite Field in a0 of size 2^2
            sage: L.<a1> = t[0].polygon[0].factors[0].FF;L
            Finite Field in a1 of size 2^4
            sage: t[0].prev.FFbase_elt_to_FF(1)
            1
            sage: t[0].polygon[0].factors[0].FFbase_elt_to_FF(a0+1)
            a1^2 + a1 + 1

        """
        if self.segment.frame.is_first():
            return b
        elif self.Fplus == 1:
            return self.segment.frame.prev.FFbase_elt_to_FF(b)
        elif self.segment.frame.F == 1:
            return b * self.FFbase_gamma
        else:
            bvec = b._vector_()
            return sum([ bvec[i]*self.FFbase_gamma**i for i in range(len(bvec))])

    def __repr__(self):
        """
        Representation of self.

        EXAMPLES::

            sage: from sage.rings.polynomial.padics.factor.factoring import OM_tree
            sage: k = ZpFM(2,20,'terse'); kx.<x> = k[]
            sage: t = OM_tree(x^4+20*x^3+44*x^2+80*x+1040)
            sage: t[0].prev.__repr__()
            'AssociatedFactor of rho z^2 + z + 1'
            sage: t[0].polygon[0].factors[0].__repr__()
            'AssociatedFactor of rho z0^2 + a0*z0 + 1'

        """
        return "AssociatedFactor of rho "+repr(self.rho)

    def lift(self,delta):
        """
        FrameElt representation of a lift of residue field element ``delta``.

        EXAMPLES::

            sage: from sage.rings.polynomial.padics.factor.factoring import OM_tree
            sage: k = ZpFM(2,20,'terse'); kx.<x> = k[]
            sage: t = OM_tree(x^4+20*x^3+44*x^2+80*x+1040)
            sage: K.<a0> = t[0].prev.FF;K
            Finite Field in a0 of size 2^2
            sage: t[0].polygon[0].factors[0].lift(a0+1)
            [[1*2^0]phi1^0, [1*2^-1]phi1^1]

        """
        if self.segment.frame.F == 1:
            return FrameElt(self.segment.frame,self.segment.frame.Ox(delta))
        elif self.segment.frame.prev.Fplus == 1:
            return FrameElt(self.segment.frame,self.segment.frame.prev.lift(delta),this_exp=0)
        else:
            dvec = self.segment.frame.prev.FF_elt_to_FFbase_vector(delta)
            return sum([self.segment.frame.prev.gamma_frameelt**i*FrameElt(self.segment.frame,self.segment.frame.prev.lift(dvec[i]),this_exp=0) for i in range(len(dvec)) if dvec[i] != 0])

    def next_frame(self,length=infinity):
        """
        Produce the child Frame in the tree of OM representations with the
        partitioning from self.

        This method generates a new Frame with the ``self`` as previous and
        seeds it with a new approximation with strictly greater valuation
        than the current one.

        INPUT:

        - ``length`` -- Integer or infinity, default infinity; The length of
          the segment generating this factor.  This is used to reduce the
          total number of quotient with remainder operations needed in the
          resulting Frame.

        EXAMPLES::

            sage: from sage.rings.polynomial.padics.factor.frame import Frame
            sage: Phi = ZpFM(2,20,'terse')['x'](x^32+16)
            sage: f = Frame(Phi)
            sage: f.seed(Phi.parent().gen());f
            Frame with phi (1 + O(2^20))*x
            sage: f = f.polygon[0].factors[0].next_frame();f 
            Frame with phi (1 + O(2^20))*x^8 + (1048574 + O(2^20))
            sage: f = f.polygon[0].factors[0].next_frame();f
            Frame with phi (1 + O(2^20))*x^8 + (1048574 + O(2^20))*x^2 + (1048574 + O(2^20))
            sage: f = f.polygon[0].factors[0].next_frame();f
            Frame with phi (1 + O(2^20))*x^16 + (1048572 + O(2^20))*x^10 + (1048572 + O(2^20))*x^8 + (1048572 + O(2^20))*x^5 + (4 + O(2^20))*x^4 + (8 + O(2^20))*x^2 + (4 + O(2^20))

        """
        from frame import Frame
        if self.segment.slope == infinity:
            next = Frame(self.segment.frame.Phi,self,self.segment.frame.iteration)
            self.next = next
            next.seed(self.segment.frame.phi,length=length)
            return next            
        if self.Fplus == 1 and self.segment.Eplus == 1:
            next = Frame(self.segment.frame.Phi,self.segment.frame.prev,self.segment.frame.iteration)
        else:
            next = Frame(self.segment.frame.Phi,self,self.segment.frame.iteration)
        self.next = next
        self.gamma_frameelt = FrameElt(next,self.segment.psi**-1,self.segment.Eplus)
        if self.Fplus == 1 and self.segment.frame.F == 1:
            next_phi = self.segment.frame.phi**self.segment.Eplus-(self.segment.psi.polynomial()*self.segment.frame.Ox(self.delta))
            self.reduce_elt = FrameElt(next,self.segment.psi*self.lift(self.delta),0)
            next.seed(next_phi,length=length)
        elif self.Fplus == 1 and self.segment.Eplus == 1:
            delta_elt = self.lift(self.delta)
            next_phi_tail = self.segment.psi*delta_elt.reduce()
            next_phi = self.segment.frame.phi-next_phi_tail.polynomial()
            self.reduce_elt = FrameElt(next,next_phi_tail,0)
            next.seed(next_phi,length=length)
        else:
            lifted_rho_coeffs = [self.lift(r) for r in list(self.rho)]
            lifted_rho_coeffs_with_psi = [FrameElt(next,(self.segment.psi**(self.Fplus-i)*lifted_rho_coeffs[i]).reduce(),0) for i in range(len(lifted_rho_coeffs))]
            phi_elt = FrameElt(next,self.segment.frame.Ox(1),1)
            next_phi_tail = sum([phi_elt**(self.segment.Eplus*i)*lifted_rho_coeffs_with_psi[i] for i in range(len(lifted_rho_coeffs_with_psi)-1)])
            next_phi = (phi_elt**(self.segment.Eplus*self.Fplus)+next_phi_tail).polynomial()
            self.reduce_elt = FrameElt(next)+(-next_phi_tail) # that is -next_phi_tail
            next.seed(next_phi,length=length)
        return next