Beispiel #1
0
    def _pc2(self, key):
        r"""
        Return Permuted Choice 2 of ``key``.

        EXAMPLES::

            sage: from sage.crypto.block_cipher.des import DES_KS
            sage: ks = DES_KS()
            sage: K = vector(GF(2),[1,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,0,1,0,1,0,
            ....:                   1,0,1,1,1,1,1,1,0,1,0,1,0,1,0,1,1,0,0,1,1,
            ....:                   0,0,1,1,1,1,0,0,0,1,1,1,1,0])
            sage: ks._pc2(K)
            (0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1,
             1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1,
             0, 0, 1, 0)
        """
        PC2 = [
            14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8,
            16, 7, 27, 20, 13, 2, 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33,
            48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32
        ]
        return vector(GF(2), 48, [key[i - 1] for i in PC2])
Beispiel #2
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 cannot 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 cannot exceed the dimension of the input space"
            )
        self._number_errors = number_errors
        self._number_erasures = number_erasures
Beispiel #3
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)
Beispiel #4
0
Datei: des.py Projekt: yjjcc/sage
    def _permutaion(self, block):
        r"""
        Apply the permutation function to ``block``.

        EXAMPLES::

            sage: from sage.crypto.block_cipher.des import DES
            sage: des = DES()
            sage: B = vector(GF(2), 32, [0,1,0,1,1,1,0,0,1,0,0,0,0,0,1,0,1,0,1,
            ....:                        1,0,1,0,1,1,0,0,1,0,1,1,1])
            sage: des._permutaion(B)
            (0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
             0, 1, 1, 0, 1, 1, 1, 0, 1, 1)
        """
        P = [16,  7, 20, 21,
             29, 12, 28, 17,
              1, 15, 23, 26,
              5, 18, 31, 10,
              2,  8, 24, 14,
             32, 27,  3,  9,
             19, 13, 30,  6,
             22, 11,  4, 25]
        return vector(GF(2), 32, [block[i-1] for i in P])
Beispiel #5
0
def small_finite_field():
    """
    Create a random finite field with cardinality at most 2^16.

    OUTPUT: a finite field

    EXAMPLES::

        sage: import sage.rings.tests
        sage: K = sage.rings.tests.small_finite_field(); K
        Finite Field...of size ...
        sage: q = K.cardinality()
        sage: q.is_prime_power()
        True
        sage: q <= 2^16
        True
    """
    from sage.rings.integer_ring import ZZ
    from sage.rings.finite_rings.finite_field_constructor import GF
    while True:
        q = ZZ.random_element(x=2, y=2**16)
        if q.is_prime_power():
            return GF(q, 'a')
Beispiel #6
0
def test_ellrains(pbound = 2**62, nbound = 100):
    import sys
    from sage.sets.primes import Primes
    for p in Primes():
        if p < 4:
            continue
        if p > pbound:
            break
        for n in xrange(2, nbound):
            k = GF(p**n, name='z')
            K = FiniteField_flint_fq_nmod(p, k.modulus(), name='z')
            try:
                a, b = find_gens(K, K)
                print "Testing p = {} and n = {}".format(p, n)
                print "Computing minpol...",
                sys.stdout.flush()
                f = a.minpoly()
                assert f.degree() == n
                g = b.minpoly()
                assert f == g
                print "done"
            except RuntimeError:
                pass
Beispiel #7
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
Beispiel #8
0
def det_GF(n=400, p=16411 , system='sage'):
    """
    Dense determinant over GF(p).
    Given an n x n matrix A over GF with random entries compute
    det(A).

    INPUT:

    - ``n`` - matrix dimension (default: 300)
    - ``p`` - prime number (default: ``16411``)
    - ``system`` - either 'magma' or 'sage' (default: 'sage')

    EXAMPLES::

        sage: import sage.matrix.benchmark as b
        sage: ts = b.det_GF(1000)
        sage: tm = b.det_GF(1000, system='magma')  # optional - magma
    """
    if system == 'sage':
        A = random_matrix(GF(p), n, n)
        t = cputime()
        d = A.determinant()
        return cputime(t)
    elif system == 'magma':
        code = """
n := %s;
A := Random(MatrixAlgebra(GF(%s), n));
t := Cputime();
d := Determinant(A);
s := Cputime(t);
"""%(n,p)
        if verbose:
            print(code)
        magma.eval(code)
        return float(magma.eval('s'))
    else:
        raise ValueError('unknown system "%s"'%system)
Beispiel #9
0
Datei: des.py Projekt: yjjcc/sage
    def sbox_layer(self, block):
        r"""
        Apply the Sboxes to ``block``.

        EXAMPLES::

            sage: from sage.crypto.block_cipher.des import DES
            sage: des = DES()
            sage: B = vector(GF(2), 48, [0,1,1,0,0,0,0,1,0,0,0,1,0,1,1,1,1,0,1,
            ....:                        1,1,0,1,0,1,0,0,0,0,1,1,0,0,1,1,0,0,1,
            ....:                        0,1,0,0,1,0,0,1,1,1])
            sage: des.sbox_layer(B)
            (0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1,
             0, 1, 1, 0, 0, 1, 0, 1, 1, 1)

        .. SEEALSO::

            :mod:`sage.crypto.sboxes`
        """
        s = self.sboxes
        block = [block[i:i+6] for i in range(0, 48, 6)]
        block = list(chain.from_iterable([s[i][ZZ([b[5], b[0]], 2)](b[1:5])
                                          for i, b in enumerate(block)]))
        return vector(GF(2), 32, block)
Beispiel #10
0
def nullspace_GF(n=300, p=16411, system='sage'):
    """
    Given a n+1 x n  matrix over GF(p) with random
    entries, compute the nullspace.

    INPUT:

    - ``n`` - matrix dimension (default: 300)
    - ``p`` - prime number (default: ``16411``)
    - ``system`` - either 'magma' or 'sage' (default: 'sage')

    EXAMPLES::

        sage: import sage.matrix.benchmark as b
        sage: ts = b.nullspace_GF(300)
        sage: tm = b.nullspace_GF(300, system='magma')  # optional - magma
    """
    if system == 'sage':
        A = random_matrix(GF(p), n, n+1)
        t = cputime()
        v = A.kernel()
        return cputime(t)
    elif system == 'magma':
        code = """
n := %s;
A := Random(RMatrixSpace(GF(%s), n, n+1));
t := Cputime();
K := Kernel(A);
s := Cputime(t);
"""%(n,p)
        if verbose:
            print(code)
        magma.eval(code)
        return magma.eval('s')
    else:
        raise ValueError('unknown system "%s"'%system)
Beispiel #11
0
    def _inv_ip(self, block):
        r"""
        Apply the inverse permutation function to ``block``.

        EXAMPLES::

            sage: from sage.crypto.block_cipher.des import DES
            sage: des = DES()
            sage: B = vector(GF(2), 64, [0,0,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,1,0,
            ....:                        1,1,0,0,1,1,0,0,1,0,1,0,1,0,1,0,0,0,0,
            ....:                        1,1,0,1,0,0,0,0,1,0,0,0,1,1,0,0,1,0,0,
            ....:                        0,1,1,0,1,0,0])
            sage: des._inv_ip(B)
            (1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0,
             1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,
             1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1)
        """
        invIP = [
            40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, 38,
            6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, 36, 4,
            44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42,
            10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25
        ]
        return vector(GF(2), 64, [block[i - 1] for i in invIP])
Beispiel #12
0
    def _ip(self, block):
        r"""
        Return the initial permutation of ``block``.

        EXAMPLES::

            sage: from sage.crypto.block_cipher.des import DES
            sage: des = DES()
            sage: B = vector(GF(2), 64, [0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,1,0,1,0,
            ....:                        0,0,1,0,1,0,1,1,0,0,1,1,1,1,0,0,0,1,0,
            ....:                        0,1,1,0,1,0,1,0,1,1,1,1,0,0,1,1,0,1,1,
            ....:                        1,1,0,1,1,1,1])
            sage: des._ip(B)
            (1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1,
             0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0,
             1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0)
        """
        IP = [
            58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62,
            54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, 57, 49,
            41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45,
            37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7
        ]
        return vector(GF(2), 64, [block[i - 1] for i in IP])
Beispiel #13
0
def charpoly_GF(n=100, p=16411, system='sage'):
    """
    Given a n x n matrix over GF with random entries, compute the
    charpoly.

    INPUT:

    - ``n`` - matrix dimension (default: 100)
    - ``p`` - prime number (default: ``16411``)
    - ``system`` - either 'magma' or 'sage' (default: 'sage')

    EXAMPLES::

        sage: import sage.matrix.benchmark as b
        sage: ts = b.charpoly_GF(100)
        sage: tm = b.charpoly_GF(100, system='magma')  # optional - magma
    """
    if system == 'sage':
        A = random_matrix(GF(p), n, n)
        t = cputime()
        v = A.charpoly()
        return cputime(t)
    elif system == 'magma':
        code = """
n := %s;
A := Random(MatrixAlgebra(GF(%s), n));
t := Cputime();
K := CharacteristicPolynomial(A);
s := Cputime(t);
"""%(n,p)
        if verbose:
            print(code)
        magma.eval(code)
        return magma.eval('s')
    else:
        raise ValueError('unknown system "%s"'%system)
Beispiel #14
0
Datei: des.py Projekt: yjjcc/sage
    def _expand(self, right):
        r"""
        Apply the expansion function to ``right``.

        EXAMPLES::

            sage: from sage.crypto.block_cipher.des import DES
            sage: des = DES()
            sage: R = vector(GF(2), 32, [1,1,1,1,0,0,0,0,1,0,1,0,1,0,1,0,1,1,1,
            ....:                        1,0,0,0,0,1,0,1,0,1,0,1,0])
            sage: des._expand(R)
            (0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
             0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
             0, 1, 0, 1)
        """
        E = [32,  1,  2,  3,  4,  5,
              4,  5,  6,  7,  8,  9,
              8,  9, 10, 11, 12, 13,
             12, 13, 14, 15, 16, 17,
             16, 17, 18, 19, 20, 21,
             20, 21, 22, 23, 24, 25,
             24, 25, 26, 27, 28, 29,
             28, 29, 30, 31, 32,  1]
        return vector(GF(2), 48, [right[i-1] for i in E])
Beispiel #15
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()))
Beispiel #16
0
def rank2_GF(n=500, p=16411, system='sage'):
    """
    Rank over GF(p): Given a (n + 10) x n matrix over GF(p) with
    random entries, compute the rank.

    INPUT:

    - ``n`` - matrix dimension (default: 300)
    - ``p`` - prime number (default: ``16411``)
    - ``system`` - either 'magma' or 'sage' (default: 'sage')

    EXAMPLES::

        sage: import sage.matrix.benchmark as b
        sage: ts = b.rank2_GF(500)
        sage: tm = b.rank2_GF(500, system='magma')  # optional - magma
    """
    if system == 'sage':
        A = random_matrix(GF(p), n+10, n)
        t = cputime()
        v = A.rank()
        return cputime(t)
    elif system == 'magma':
        code = """
n := %s;
A := Random(MatrixAlgebra(GF(%s), n));
t := Cputime();
K := Rank(A);
s := Cputime(t);
"""%(n,p)
        if verbose:
            print(code)
        magma.eval(code)
        return float(magma.eval('s'))
    else:
        raise ValueError('unknown system "%s"'%system)
Beispiel #17
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)
        doctest:...: DeprecationWarning: lift2smallest_field is deprecated. Please use sage.coding.code_constructions._lift2smallest_field instead.
        See http://trac.sagemath.org/21165 for details.
        (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
Beispiel #18
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.finite_field_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
Beispiel #19
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: def to_name(r_s_t):
        ....:     r, s, t = r_s_t
        ....:     return ' ' + 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('\n'.join(rows))
        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.finite_field_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 list(range(t)) +
                           list(range(2 * t, 3 * t)) +
                           list(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.finite_field_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 list(range(t)) + list(range(2 * t, 3 * t)) + list(
                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
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 range(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 range(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 range(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 range(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 range(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 range(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 range(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 diagonalizable.  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 separately 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 range(len(Mfac)):
                        for j in range(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 stepping 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
Beispiel #23
0
def benchmark(pbound=[3, 2**10],
              nbound=[3, 2**8],
              cbound=[1, Infinity],
              obound=[1, Infinity],
              loops=10,
              tloop=Infinity,
              tmax=Infinity,
              prime=False,
              even=False,
              check=False,
              fname=None,
              write=False,
              overwrite=False,
              verbose=True,
              skip_pari=False,
              skip_magma=False,
              skip_rains=False,
              skip_kummer=False):
    if write:
        mode = 'w' if overwrite else 'a'
        f = open(fname, mode, 0)
    else:
        f = sys.stdout
    pmin, pmax = pbound
    nmin, nmax = nbound
    omin, omax = obound
    cmin, cmax = cbound
    M = Magma()
    for p in xrange(pmin, pmax):
        p = ZZ(p)
        if not p.is_prime():
            continue
        for n in xrange(nmin, nmax):
            n = ZZ(n)
            if (prime == 1 and not is_prime(n)) or (prime == 2
                                                    and not is_prime_power(n)):
                continue
            if n < 2:
                continue
            if n % p == 0:
                continue
            if (not even) and (n % 2 == 0):
                continue
            o, G = find_root_order(p, [n, n], n, verbose=False)
            m = G[0][0].parent().order()
            c = Mod(p, n).multiplicative_order()
            if verbose:
                sys.stdout.write("\r" + " " * 79)
                print("\rp = {}, n = {}, (o = {}, c = {})".format(p, n, o, c))
            if verbose:
                t = mytime()
                sys.stdout.write("Constructing fields ({})".format(
                    time.strftime("%c")))
                sys.stdout.flush()
            q = p**n
            k = GF(q, name='z')
            k_rand = GF(q, modulus='random', name='z')
            k_flint = GF_flint(p, k.modulus(), name='z')
            if verbose > 1:
                sys.stdout.write("\ntotal: {}s\n".format(mytime(t)))
                sys.stdout.flush()
            # Magma
            if verbose:
                sys.stdout.write("\r" + " " * 79)
                sys.stdout.write("\rMagma ({})".format(time.strftime("%c")))
                sys.stdout.flush()
            tloops = 0
            for l in xrange(loops):
                if skip_magma:
                    break
                if (o > omax) or (o == p):
                    break
                # let's assume that launching a new Magma instance is cheaper
                # than computing random irreducible polynomials
                try:
                    M._start()
                except OSError as err:
                    # but it can also cause fork issues...
                    # let's accept this
                    # and fail as the situation will only worsen
                    # unless it is "just" a memory issue
                    # which should be mitigated by COW but is not
                    #print(err)
                    if err.errno == errno.ENOMEM:
                        break
                    else:
                        raise
                try:
                    k_magma = M(k)
                    k_rand_magma = M(k_rand)
                    if tloop is not Infinity:
                        alarm(tloop)
                    t = mytime()
                    k_magma.Embed(k_rand_magma, nvals=0)
                    #M._eval_line("Embed(k_magma, k_rand_magma);", wait_for_prompt=False)
                    tloops += mytime(t)
                except TypeError:
                    # sage/magma interface sometimes gets confused
                    pass
                except (KeyboardInterrupt, AlarmInterrupt):
                    # sage interface eats KeyboardInterrupt
                    # and AlarmInterrupt derives from it
                    tloops = 0
                    break
                finally:
                    if tloop is not Infinity:
                        cancel_alarm()
                    M.quit()
                    # sage pexpect interface leaves zombies around
                    try:
                        while os.waitpid(-1, os.WNOHANG)[0]:
                            pass
                    # but sometimes every child is already buried
                    # and we get an ECHILD error...
                    except OSError:
                        pass
                if tloops > tmax:
                    break
            tmagma = tloops / (l + 1)
            if verbose > 1:
                sys.stdout.write("\ntotal: {}s, per loop: {}s\n".format(
                    tloops, tloops / (l + 1)))
                sys.stdout.flush()
            # Rains algorithms
            if verbose:
                sys.stdout.write("\r" + " " * 79)
                sys.stdout.write("\rCyclotomic Rains ({})".format(
                    time.strftime("%c")))
                sys.stdout.flush()
            trains = []
            tloops = 0
            for l in xrange(loops):
                if skip_rains:
                    break
                if (o > omax) or (o == p):
                    break
                try:
                    if tloop is not Infinity:
                        alarm(tloop)
                    t = mytime()
                    a, b = find_gens_cyclorains(k_flint,
                                                k_flint,
                                                use_lucas=False)
                    tloops += mytime(t)
                except (KeyboardInterrupt, AlarmInterrupt):
                    tloops = 0
                    break
                finally:
                    if tloop is not Infinity:
                        cancel_alarm()
                if check and (l == 0 or check > 1):
                    g = a.minpoly()
                    if g.degree() != n:
                        raise RuntimeError("wrong degree")
                    if g != b.minpoly():
                        raise RuntimeError("different minpolys")
                if tloops > tmax:
                    break
            trains.append(tloops / (l + 1))
            if verbose > 1:
                sys.stdout.write("\ntotal: {}s, per loop: {}s\n".format(
                    tloops, tloops / (l + 1)))
                sys.stdout.flush()
            # Conic Rains
            if verbose:
                sys.stdout.write("\r" + " " * 79)
                sys.stdout.write("\rConic Rains ({})".format(
                    time.strftime("%c")))
                sys.stdout.flush()
            tloops = 0
            for l in xrange(loops):
                if skip_rains:
                    break
                if (o != 2) or (o > omax) or (o == p):
                    break
                try:
                    if tloop is not Infinity:
                        alarm(tloop)
                    t = mytime()
                    a, b = find_gens_cyclorains(k_flint,
                                                k_flint,
                                                use_lucas=True)
                    tloops += mytime(t)
                except (KeyboardInterrupt, AlarmInterrupt):
                    tloops = 0
                    break
                finally:
                    if tloop is not Infinity:
                        cancel_alarm()
                if check and (l == 0 or check > 1):
                    g = a.minpoly()
                    if g.degree() != n:
                        raise RuntimeError("wrong degree")
                    if g != b.minpoly():
                        raise RuntimeError("different minpolys")
                if tloops > tmax:
                    break
            trains.append(tloops / (l + 1))
            if verbose > 1:
                sys.stdout.write("\ntotal: {}s, per loop: {}s\n".format(
                    tloops, tloops / (l + 1)))
                sys.stdout.flush()
            # Elliptic Rains
            if verbose:
                sys.stdout.write("\r" + " " * 79)
                sys.stdout.write("\rElliptic Rains ({})".format(
                    time.strftime("%c")))
                sys.stdout.flush()
            tloops = 0
            for l in xrange(loops):
                if skip_rains:
                    break
                try:
                    if tloop is not Infinity:
                        alarm(tloop)
                    t = mytime()
                    a, b = find_gens_ellrains(k_flint, k_flint)
                    tloops += mytime(t)
                except RuntimeError:
                    # sometimes no suitable elliptic curve exists
                    pass
                except (KeyboardInterrupt, AlarmInterrupt):
                    tloops = 0
                    break
                finally:
                    if tloop is not Infinity:
                        cancel_alarm()
                if check and (l == 0 or check > 1):
                    g = a.minpoly()
                    if g.degree() != n:
                        raise RuntimeError("wrong degree")
                    if g != b.minpoly():
                        raise RuntimeError("different minpolys")
                if tloops > tmax:
                    break
            trains.append(tloops / (l + 1))
            if verbose > 1:
                sys.stdout.write("\ntotal: {}s, per loop: {}s\n".format(
                    tloops, tloops / (l + 1)))
                sys.stdout.flush()
            # PARI/GP
            if verbose:
                sys.stdout.write("\r" + " " * 79)
                sys.stdout.write("\rPARI/GP ({})".format(time.strftime("%c")))
                sys.stdout.flush()
            tloops = 0
            for l in xrange(loops):
                if skip_pari:
                    break
                if c < cmin or c > cmax:
                    break
                try:
                    if tloop is not Infinity:
                        alarm(tloop)
                    t = mytime()
                    a, b = find_gens_pari(k, k)
                    tloops += mytime(t)
                except (KeyboardInterrupt, AlarmInterrupt):
                    tloops = 0
                    break
                finally:
                    if tloop is not Infinity:
                        cancel_alarm()
                if check and (l == 0 or check > 1):
                    g = a.minpoly()
                    if g.degree() != n:
                        raise RuntimeError("wrong degree")
                    if g != b.minpoly():
                        raise RuntimeError("different minpolys")
                if tloops > tmax:
                    break
            tpari = tloops / (l + 1)
            # Kummer algorithms
            tkummer = []
            # only linalg and modcomp implemented for c==1
            for i, algo in enumerate(kummer_algolist[2 * (c == 1):-2 *
                                                     (c == 1) - 1]):
                if verbose:
                    sys.stdout.write("\r" + " " * 79)
                    sys.stdout.write("\rKummer {} ({})".format(
                        kummer_namelist[2 * (c == 1) + i],
                        time.strftime("%c")))
                    sys.stdout.flush()
                tloops = 0
                for l in xrange(loops):
                    if skip_kummer:
                        break
                    if c < cmin or c > cmax:
                        break
                    try:
                        if tloop is not Infinity:
                            alarm(tloop)
                        t = mytime()
                        a, b = find_gens_kummer(k_flint, k_flint, n, algo)
                        tloops += mytime(t)
                    except (KeyboardInterrupt, AlarmInterrupt):
                        tloops = 0
                        break
                    finally:
                        if tloop is not Infinity:
                            cancel_alarm()
                    if check and (l == 0 or check > 1):
                        g = a.minpoly()
                        if g.degree() != n:
                            raise RuntimeError("wrong degree")
                        if g != b.minpoly():
                            raise RuntimeError("different minpolys")
                    if tloops > tmax:
                        break
                tkummer.append(tloops / (l + 1))
                if verbose > 1:
                    sys.stdout.write("\ntotal: {}s, per loop: {}s\n".format(
                        tloops, tloops / (l + 1)))
                    sys.stdout.flush()
            tkummer = 2 * (c == 1) * [0] + tkummer + 2 * (c == 1) * [0]
            if verbose:
                sys.stdout.write("\r")
                sys.stdout.flush()
            f.write(("{} {} ({}, {})" + " {}" + " {}" * len(trains) + " {}" +
                     " {}" * len(tkummer) + "\n").format(
                         p, n, o, c, *([tmagma] + trains + [tpari] + tkummer)))
    if write:
        f.close()
Beispiel #24
0
    def peterson(self, idx, coeff=1):
        """
        Peterson element omega_idx

        TESTS::

            sage: from yacop.modules.dickson import DicksonAlgebra
            sage: D = DicksonAlgebra(3,3)
            sage: [D.peterson(i) for i in (9,12,13)]
            [d36, d48, d52]
            sage: D = DicksonAlgebra(2,3)
            sage: for i in range(4,30):
            ....:     if i in [5,9,17,]: continue
            ....:     print( "%-3d %s" % (i,D.peterson(i)))
            4   d4
            6   d6
            7   d7
            8   d4**2
            10  d4*d6
            11  d4*d7
            12  d6**2
            13  d6*d7
            14  d7**2
            15  0
            16  d4**4
            18  d6**3 + d4*d7**2 + d4**3*d6
            19  d6**2*d7 + d4**3*d7
            20  d4**2*d6**2
            21  d7**3 + d4**2*d6*d7
            22  d4**2*d7**2
            23  0
            24  d6**4
            25  d6**3*d7 + d4*d7**3
            26  d6**2*d7**2
            27  0
            28  d7**4
            29  0
            sage: D.peterson(17)
            Traceback (most recent call last):
            ...
            ValueError: Peterson element 17 not in Dickson algebra D(3) for prime 2

        """
        GF = self.base_ring()
        dec = PetersonPolynomials.decompose(self._prime, idx)
        comp = []
        startpow = self._prime**(self._index - 1)
        for (cf, expos) in dec:
            if len(expos) > self._index:
                continue
            pow = startpow
            key = []
            for e in expos:
                if e % pow != 0:
                    raise ValueError("Peterson element %d not in %s" %
                                     (idx, self))
                key.append(0)
                key.append(e // pow)
                pow //= self._prime
            comp.append((tuple(key), GF(coeff * cf)))
        return self._from_dict(dict(comp))
def AffineGeometryDesign(n, d, F, point_coordinates=True, check=True):
    r"""
    Return an affine geometry design.

    The affine geometry design `AG_d(n,q)` is the 2-design whose blocks are the
    `d`-vector subspaces in `\GF{q}^n`. It has parameters

    .. MATH::

        v = q^n,\ k = q^d,\ \lambda = \binom{n-1}{d-1}_q

    where the `q`-binomial coefficient `\binom{m}{r}_q` is defined by

    .. MATH::

        \binom{m}{r}_q = \frac{(q^m - 1)(q^{m-1} - 1) \cdots (q^{m-r+1}-1)}
              {(q^r-1)(q^{r-1}-1)\cdots (q-1)}

    .. SEEALSO::

        :func:`ProjectiveGeometryDesign`

    INPUT:

    - ``n`` (integer) -- the Euclidean dimension. The number of points of the
      design is `v=|\GF{q}^n|`.

    - ``d`` (integer) -- the dimension of the (affine) subspaces of `\GF{q}^n`
      which make up the blocks.

    - ``F`` -- a finite field or a prime power.

    - ``point_coordinates`` -- (optional, default ``True``) whether we use
      coordinates in `\GF{q}^n` or plain integers for the points of the design.

    - ``check`` -- (optional, default ``True``) whether to check the output.

    EXAMPLES::

        sage: BD = designs.AffineGeometryDesign(3, 1, GF(2))
        sage: BD.is_t_design(return_parameters=True)
        (True, (2, 8, 2, 1))
        sage: BD = designs.AffineGeometryDesign(3, 2, GF(4))
        sage: BD.is_t_design(return_parameters=True)
        (True, (2, 64, 16, 5))
        sage: BD = designs.AffineGeometryDesign(4, 2, GF(3))
        sage: BD.is_t_design(return_parameters=True)
        (True, (2, 81, 9, 13))

    With ``F`` an integer instead of a finite field::

        sage: BD = designs.AffineGeometryDesign(3, 2, 4)
        sage: BD.is_t_design(return_parameters=True)
        (True, (2, 64, 16, 5))

    Testing the option ``point_coordinates``::

        sage: designs.AffineGeometryDesign(3, 1, GF(2), point_coordinates=True).blocks()[0]
        [(0, 0, 0), (0, 0, 1)]
        sage: designs.AffineGeometryDesign(3, 1, GF(2), point_coordinates=False).blocks()[0]
        [0, 1]
    """
    try:
        q = int(F)
    except TypeError:
        q = F.cardinality()
    else:
        from sage.rings.finite_rings.finite_field_constructor import GF
        F = GF(q)

    n = int(n)
    d = int(d)

    from itertools import islice
    from sage.combinat.q_analogues import q_binomial
    from sage.matrix.echelon_matrix import reduced_echelon_matrix_iterator

    points = {
        p: i
        for i, p in enumerate(
            reduced_echelon_matrix_iterator(
                F, 1, n + 1, copy=True, set_immutable=True)) if p[0, 0]
    }

    blocks = []
    l1 = int(q_binomial(n + 1, d + 1, q) - q_binomial(n, d + 1, q))
    l2 = q**d
    for m1 in islice(
            reduced_echelon_matrix_iterator(F, d + 1, n + 1, copy=False),
            int(l1)):
        b = []
        for m2 in islice(
                reduced_echelon_matrix_iterator(F, 1, d + 1, copy=False),
                int(l2)):
            m = m2 * m1
            m.echelonize()
            m.set_immutable()
            b.append(points[m])
        blocks.append(b)

    B = BlockDesign(len(points),
                    blocks,
                    name="AffineGeometryDesign",
                    check=check)

    if point_coordinates:
        rd = {i: p[0][1:] for p, i in points.items()}
        for v in rd.values():
            v.set_immutable()
        B.relabel(rd)

    if check:
        if not B.is_t_design(
                t=2, v=q**n, k=q**d, l=q_binomial(n - 1, d - 1, q)):
            raise RuntimeError(
                "error in AffineGeometryDesign "
                "construction. Please e-mail [email protected]")
    return B
def ProjectiveGeometryDesign(n,
                             d,
                             F,
                             algorithm=None,
                             point_coordinates=True,
                             check=True):
    r"""
    Return a projective geometry design.

    The projective geometry design `PG_d(n,q)` has for points the lines of
    `\GF{q}^{n+1}`, and for blocks the `d+1`-dimensional subspaces of
    `\GF{q}^{n+1}`, each of which contains `\frac {|\GF{q}|^{d+1}-1} {|\GF{q}|-1}` lines.
    It is a `2`-design with parameters

    .. MATH::

        v = \binom{n+1}{1}_q,\ k = \binom{d+1}{1}_q,\ \lambda =
        \binom{n-1}{d-1}_q

    where the `q`-binomial coefficient `\binom{m}{r}_q` is defined by

    .. MATH::

        \binom{m}{r}_q = \frac{(q^m - 1)(q^{m-1} - 1) \cdots (q^{m-r+1}-1)}
              {(q^r-1)(q^{r-1}-1)\cdots (q-1)}

    .. SEEALSO::

        :func:`AffineGeometryDesign`

    INPUT:

    - ``n`` is the projective dimension

    - ``d`` is the dimension of the subspaces which make up the blocks.

    - ``F`` -- a finite field or a prime power.

    - ``algorithm`` -- set to ``None`` by default, which results in using Sage's
      own implementation. In order to use GAP's implementation instead (i.e. its
      ``PGPointFlatBlockDesign`` function) set ``algorithm="gap"``. Note that
      GAP's "design" package must be available in this case, and that it can be
      installed with the ``gap_packages`` spkg.

    - ``point_coordinates`` -- ``True`` by default. Ignored and assumed to be ``False`` if
      ``algorithm="gap"``. If ``True``, the ground set is indexed by coordinates
      in `\GF{q}^{n+1}`.  Otherwise the ground set is indexed by integers.

    - ``check`` -- (optional, default to ``True``) whether to check the output.

    EXAMPLES:

    The set of `d`-dimensional subspaces in a `n`-dimensional projective space
    forms `2`-designs (or balanced incomplete block designs)::

        sage: PG = designs.ProjectiveGeometryDesign(4, 2, GF(2))
        sage: PG
        Incidence structure with 31 points and 155 blocks
        sage: PG.is_t_design(return_parameters=True)
        (True, (2, 31, 7, 7))

        sage: PG = designs.ProjectiveGeometryDesign(3, 1, GF(4))
        sage: PG.is_t_design(return_parameters=True)
        (True, (2, 85, 5, 1))

    Check with ``F`` being a prime power::

        sage: PG = designs.ProjectiveGeometryDesign(3, 2, 4)
        sage: PG
        Incidence structure with 85 points and 85 blocks

    Use coordinates::

        sage: PG = designs.ProjectiveGeometryDesign(2, 1, GF(3))
        sage: PG.blocks()[0]
        [(1, 0, 0), (1, 0, 1), (1, 0, 2), (0, 0, 1)]

    Use indexing by integers::

        sage: PG = designs.ProjectiveGeometryDesign(2,1,GF(3),point_coordinates=0)
        sage: PG.blocks()[0]
        [0, 1, 2, 12]

    Check that the constructor using gap also works::

        sage: BD = designs.ProjectiveGeometryDesign(2, 1, GF(2), algorithm="gap") # optional - gap_packages (design package)
        sage: BD.is_t_design(return_parameters=True)                              # optional - gap_packages (design package)
        (True, (2, 7, 3, 1))
    """
    try:
        q = int(F)
    except TypeError:
        q = F.cardinality()
    else:
        from sage.rings.finite_rings.finite_field_constructor import GF
        F = GF(q)

    if algorithm is None:
        from sage.matrix.echelon_matrix import reduced_echelon_matrix_iterator

        points = {
            p: i
            for i, p in enumerate(
                reduced_echelon_matrix_iterator(
                    F, 1, n + 1, copy=True, set_immutable=True))
        }
        blocks = []
        for m1 in reduced_echelon_matrix_iterator(F, d + 1, n + 1, copy=False):
            b = []
            for m2 in reduced_echelon_matrix_iterator(F, 1, d + 1, copy=False):
                m = m2 * m1
                m.echelonize()
                m.set_immutable()
                b.append(points[m])
            blocks.append(b)
        B = BlockDesign(len(points),
                        blocks,
                        name="ProjectiveGeometryDesign",
                        check=check)
        if point_coordinates:
            B.relabel({i: p[0] for p, i in points.items()})

    elif algorithm == "gap":  # Requires GAP's Design
        libgap.load_package("design")
        D = libgap.PGPointFlatBlockDesign(n, F.order(), d)
        v = D['v'].sage()
        gblcks = D['blocks'].sage()
        gB = []
        for b in gblcks:
            gB.append([x - 1 for x in b])
        B = BlockDesign(v, gB, name="ProjectiveGeometryDesign", check=check)

    if check:
        from sage.combinat.q_analogues import q_binomial
        q = F.cardinality()
        if not B.is_t_design(t=2,
                             v=q_binomial(n + 1, 1, q),
                             k=q_binomial(d + 1, 1, q),
                             l=q_binomial(n - 1, d - 1, q)):
            raise RuntimeError(
                "error in ProjectiveGeometryDesign "
                "construction. Please e-mail [email protected]")
    return B
Beispiel #27
0
    def __call__(self, X):
        """
        Apply substitution to ``X``.

        If ``X`` is a list, it is interpreted as a sequence of bits
        depending on the bit order of this S-box.

        INPUT:

        - ``X`` - either an integer, a tuple of `\GF{2}` elements of
          length ``len(self)`` or a finite field element in
          `\GF{2^n}`. As a last resort this function tries to convert
          ``X`` to an integer.

        EXAMPLES::

            sage: from sage.crypto.sbox import SBox
            sage: S = SBox([7,6,0,4,2,5,1,3])
            sage: S(7)
            3

            sage: S((0,2,3))
            [0, 1, 1]

            sage: S[0]
            7

            sage: S[(0,0,1)]
            [1, 1, 0]

            sage: k.<a> = GF(2^3)
            sage: S(a^2)
            a

            sage: S(QQ(3))
            4

            sage: S([1]*10^6)
            Traceback (most recent call last):
            ...
            TypeError: Cannot apply SBox to provided element.

            sage: S(1/2)
            Traceback (most recent call last):
            ...
            TypeError: Cannot apply SBox to 1/2.

            sage: S = SBox(3, 0, 1, 3, 1, 0, 2, 2)
            sage: S(0)
            3
            sage: S([0,0,0])
            [1, 1]
        """
        if isinstance(X, integer_types + (Integer,)):
            return self._S[ZZ(X)]

        try:
            from sage.modules.free_module_element import vector
            K = X.parent()
            if K.order() == 2**self.n:
                X = vector(X)
            else:
                raise TypeError
            if not self._big_endian:
                X = list(reversed(X))
            else:
                X = list(X)
            X = ZZ([ZZ(_) for _ in X], 2)
            out =  self.to_bits(self._S[X], self.n)
            if self._big_endian:
                out = list(reversed(out))
            return K(vector(GF(2),out))
        except (AttributeError, TypeError):
            pass

        try:
            if len(X) == self.m:
                if self._big_endian:
                    X = list(reversed(X))
                X = ZZ([ZZ(_) for _ in X], 2)
                out =  self._S[X]
                return self.to_bits(out,self.n)
        except TypeError:
            pass

        try:
            return self._S[ZZ(X)]
        except TypeError:
            pass

        if len(str(X)) > 50:
            raise TypeError("Cannot apply SBox to provided element.")
        else:
            raise TypeError("Cannot apply SBox to %s."%(X,))
Beispiel #28
0
    def spin_parity(self,check=True,verbose=False):
        r"""
        Return the spin parity of the Ribbon graph with angles.

        The surface should be holonomy free and with odd multiple of 2 pi
        angles. Implements the formula of [Joh80]_.

        EXAMPLES:

            sage: from surface_dynamics import *

        We first consider the case of the torus::

            sage: e = '(0,1)(2,3)'
            sage: f = '(0,2,1,3)'
            sage: a = [1/2,1/2,1/2,1/2]
            sage: r = RibbonGraphWithAngles(edges=e,faces=f,angles=a)
            sage: r.spin_parity()
            1

        Then the case of genus 2 surface (with an angle of 6pi)::

            sage: e = '(0,1)(2,3)(4,5)(6,7)'
            sage: f = '(0,2,4,3,6,1,7,5)'
            sage: a = [1/2,1/2,1,1/2,1/2,1,3/2,1/2]
            sage: r = RibbonGraphWithAngles(edges=e,faces=f,angles=a)
            sage: r.spin_parity()
            1

            sage: e = '(0,1)(2,3)(4,5)(6,7)'
            sage: f = '(0,2,4,6,1,3,5,7)'
            sage: a = [1/2,1/2,1,1,1,1,1/2,1/2]
            sage: r = RibbonGraphWithAngles(edges=e,faces=f,angles=a)
            sage: r.spin_parity()
            1

            sage: e = '(0,1)(2,3)(4,5)(6,7)'
            sage: f = '(0,2,4,6,1,3,5,7)'
            sage: a = [3/4]*8
            sage: r = RibbonGraphWithAngles(edges=e,faces=f,angles=a)
            sage: r.spin_parity()
            1

        In genus 3 two spin parities occur for one conical angle 10pi::

            sage: e = '(0,1)(2,3)(4,5)(6,7)(8,9)(10,11)'
            sage: f1 = '(0,4,6,8,10,2,1,9,11,5,7,3)'
            sage: f2 = '(0,4,6,8,10,2,1,5,7,9,11,3)'
            sage: a = [1/2,1/2,1/2,1/2] + [1]*8
            sage: r1 = RibbonGraphWithAngles(edges=e,faces=f1,angles=a)
            sage: r1.spin_parity()
            1
            sage: r2 = RibbonGraphWithAngles(edges=e,faces=f2,angles=a)
            sage: r2.spin_parity()
            0
        """
        from sage.rings.finite_rings.finite_field_constructor import GF
        # mod F2 we have: q(x+y) = B(x,y) + q(x) + q(y)

        if not self.has_trivial_holonomy():
            raise ValueError("the surface does not have trivial holonomy")
        if any((i+2)%4 for i in self.angle_at_vertices()):
            raise ValueError("each angle should be odd multiple of 2pi")

        GF2 = GF(2)

        c,M = self.cycle_basis(intersection=True)

        winding = []
        for cc in c:
            w = self.winding(cc)
            if w % 2 != 0:
                raise ValueError("fatal error ! each winding should be a multiple of 2")
            winding.append(GF2(w//2))

        if verbose:
            print("cycles with winding")
            for i in range(len(c)):
                print(c[i], winding[i])
            print("intersection matrix on Z")
            print(M)

        # compute a base change to get a symplectic basis
        _,P = M.symplectic_form()
        M = M.change_ring(GF2)
        P = P.change_ring(GF2)
        if verbose:
            print("base change for symplectic basis on GF(2)")
            print(P)

        g = self.genus()

        s = GF2(0)
        for i in range(g):
            # 1. computation of q(P.row(i))
            a = P.row(i)
            a_indices = [j for j in range(2*g) if a[j] != 0]
            ## winding + nb_components
            t_a = sum(winding[i]+1 for i in a_indices)
            ## self intersection
            for j1 in range(len(a_indices)):
                for j2 in range(j1+1,len(a_indices)):
                    t_a += M[a_indices[j1],a_indices[j2]]

            # 2. computation of q(P.row(g+i))
            b = P.row(g+i)
            b_indices = [j for j in range(2*g) if b[j] != 0]
            ## winding + nb_components
            t_b = sum(winding[i]+1 for i in b_indices)
            ## self intersection
            for j1 in range(len(b_indices)):
                for j2 in range(j1+1,len(b_indices)):
                    t_b += M[b_indices[j1],b_indices[j2]]

            # 3. add to s the contribution of the couple
            if verbose:
                print("contribution from %d is %d * %d = %d" % (i, t_a, t_b, t_a * t_b))
            s += t_a*t_b

        return s
Beispiel #29
0
    def __init__(self, *args,  **kwargs):
        """
        Construct a substitution box (S-box) for a given lookup table
        `S`.

        INPUT:

        - ``S`` - a finite iterable defining the S-box with integer or
          finite field elements

        - ``big_endian`` - controls whether bits shall be ordered in
          big endian order (default: ``True``)

        EXAMPLES:

        We construct a 3-bit S-box where e.g. the bits (0,0,1) are
        mapped to (1,1,1).::

            sage: from sage.crypto.sbox import SBox
            sage: S = SBox(7,6,0,4,2,5,1,3); S
            (7, 6, 0, 4, 2, 5, 1, 3)

            sage: S(0)
            7

        TESTS::

            sage: from sage.crypto.sbox import SBox
            sage: S = SBox()
            Traceback (most recent call last):
            ...
            TypeError: No lookup table provided.
            sage: S = SBox(1, 2, 3)
            Traceback (most recent call last):
            ...
            TypeError: Lookup table length is not a power of 2.
            sage: S = SBox(5, 6, 0, 3, 4, 2, 1, 2)
            sage: S.n
            3
        """
        if "S" in kwargs:
            S = kwargs["S"]
        elif len(args) == 1:
            S = args[0]
        elif len(args) > 1:
            S = args
        else:
            raise TypeError("No lookup table provided.")

        _S = []
        for e in S:
            if is_FiniteFieldElement(e):
                e = e.polynomial().change_ring(ZZ).subs( e.parent().characteristic() )
            _S.append(e)
        S = _S

        if not ZZ(len(S)).is_power_of(2):
            raise TypeError("Lookup table length is not a power of 2.")
        self._S = S

        self.m = ZZ(len(S)).exact_log(2)
        self.n = ZZ(max(S)).nbits()
        self._F = GF(2)
        self._big_endian = kwargs.get("big_endian",True)

        self.differential_uniformity = self.maximal_difference_probability_absolute
Beispiel #30
0
    def residue(self, absprec=1, field=None, check_prec=True):
        r"""
        Reduces this element modulo `p^{\mathrm{absprec}}`.

        INPUT:

        - ``absprec`` -- a non-negative integer (default: ``1``)

        - ``field`` -- boolean (default ``None``).  Whether to return an element of GF(p) or Zmod(p).

        - ``check_prec`` -- boolean (default ``True``).  Whether to raise an error if this
          element has insufficient precision to determine the reduction.

        OUTPUT:

        This element reduced modulo `p^\mathrm{absprec}` as an element of
        `\ZZ/p^\mathrm{absprec}\ZZ`

        EXAMPLES::

            sage: R = ZpLC(7,4)
            sage: a = R(8)
            sage: a.residue(1)
            1

        TESTS::

            sage: R = ZpLC(7,4)
            sage: a = R(8)
            sage: a.residue(0)
            0
            sage: a.residue(-1)
            Traceback (most recent call last):
            ...
            ValueError: cannot reduce modulo a negative power of p.
            sage: a.residue(5)
            Traceback (most recent call last):
            ...
            PrecisionError: not enough precision known in order to compute residue.
            sage: a.residue(5, check_prec=False)
            8

            sage: a.residue(field=True).parent()
            Finite Field of size 7
        """
        if not isinstance(absprec, Integer):
            absprec = Integer(absprec)
        if check_prec and absprec > self.precision_absolute():
            raise PrecisionError(
                "not enough precision known in order to compute residue.")
        elif absprec < 0:
            raise ValueError("cannot reduce modulo a negative power of p.")
        if self.valuation() < 0:
            raise ValueError(
                "element must have non-negative valuation in order to compute residue."
            )
        if field is None:
            field = (absprec == 1)
        elif field and absprec != 1:
            raise ValueError("field keyword may only be set at precision 1")
        p = self._parent.prime()
        if field:
            from sage.rings.finite_rings.finite_field_constructor import GF
            ring = GF(p)
        else:
            from sage.rings.finite_rings.integer_mod_ring import Integers
            ring = Integers(p**absprec)
        return ring(self.value())