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])
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
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)
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])
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')
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
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
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)
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)
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)
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])
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])
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)
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])
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()))
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)
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
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
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
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()
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
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,))
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
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
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())