def __init__(self, field = None): """ Create a linear feedback shift cryptosystem. INPUT: A string monoid over a binary alphabet. OUTPUT: EXAMPLES:: sage: E = LFSRCryptosystem(FiniteField(2)) sage: E LFSR cryptosystem over Finite Field of size 2 TESTS:: sage: E = LFSRCryptosystem(FiniteField(2)) sage: E == loads(dumps(E)) True TODO: Implement LFSR cryptosystem for arbitrary rings. The current implementation is limited to the finite field of 2 elements only because of the dependence on binary strings. """ if field is None: field = FiniteField(2) if field.cardinality() != 2: raise NotImplementedError("Not yet implemented.") S = BinaryStrings() P = PolynomialRing(FiniteField(2),'x') SymmetricKeyCryptosystem.__init__(self, S, S, None) self._field = field
def test_generic_02(self): F = FiniteField(2**8, 'a') shares = [(1,1), (2,121), (3,97), (4,77), (5,29)] points = [(F.fetch_int(x), F.fetch_int(y)) for x,y in shares] p = berlekamp_welsh(5, points) print(p) print([coeff.integer_representation() for coeff in p]) print(map(lambda x: x.integer_representation(), p))
def gfq_gap_to_sage(x, F): """ INPUT: - ``x`` - gap finite field element - ``F`` - Sage finite field OUTPUT: element of F EXAMPLES:: sage: x = gap('Z(13)') sage: F = GF(13, 'a') sage: F(x) 2 sage: F(gap('0*Z(13)')) 0 sage: F = GF(13^2, 'a') sage: x = gap('Z(13)') sage: F(x) 2 sage: x = gap('Z(13^2)^3') sage: F(x) 12*a + 11 sage: F.multiplicative_generator()^3 12*a + 11 AUTHOR: - David Joyner and William Stein """ from sage.rings.finite_rings.constructor import FiniteField s = str(x) if s[:2] == '0*': return F(0) i1 = s.index("(") i2 = s.index(")") q = eval(s[i1+1:i2].replace('^','**')) if q == F.order(): K = F else: K = FiniteField(q, F.variable_name()) if s.find(')^') == -1: e = 1 else: e = int(s[i2+2:]) if F.degree() == 1: g = int(gap.eval('Int(Z(%s))'%q)) else: g = K.multiplicative_generator() return F(K(g**e))
def test_generic_01(self): F = FiniteField(2**8, 'a') #shares = [(1, 22), (2, 82), (3, 110), (4, 218), (5, 230)] points = [(1, 22), (2, 82), (3, 110), (4, 219)] points = [(F.fetch_int(x), F.fetch_int(y)) for x,y in shares] p = berlekamp_welsh(1, points) pint = ([coeff.integer_representation() for coeff in p]) #pint = (map(lambda x: x.integer_representation(), p)) print(p, pint) assert [42,60] == pint
def DuadicCodeOddPair(F,S1,S2): """ Constructs the "odd pair" of duadic codes associated to the "splitting" S1, S2 of n. .. warning:: Maybe the splitting should be associated to a sum of q-cyclotomic cosets mod n, where q is a *prime*. EXAMPLES:: sage: from sage.coding.code_constructions import is_a_splitting sage: n = 11; q = 3 sage: C = Zmod(n).cyclotomic_cosets(q); C [[0], [1, 3, 4, 5, 9], [2, 6, 7, 8, 10]] sage: S1 = C[1] sage: S2 = C[2] sage: is_a_splitting(S1,S2,11) True sage: codes.DuadicCodeOddPair(GF(q),S1,S2) (Linear code of length 11, dimension 6 over Finite Field of size 3, Linear code of length 11, dimension 6 over Finite Field of size 3) This is consistent with Theorem 6.1.3 in [HP]_. """ n = len(S1) + len(S2) + 1 if not is_a_splitting(S1,S2,n): raise TypeError("%s, %s must be a splitting of %s."%(S1,S2,n)) q = F.order() k = Mod(q,n).multiplicative_order() FF = GF(q**k,"z") z = FF.gen() zeta = z**((q**k-1)/n) P1 = PolynomialRing(FF,"x") x = P1.gen() g1 = prod([x-zeta**i for i in S1+[0]]) g2 = prod([x-zeta**i for i in S2+[0]]) j = sum([x**i/n for i in range(n)]) P2 = PolynomialRing(F,"x") x = P2.gen() coeffs1 = [lift2smallest_field(c)[0] for c in (g1+j).coefficients(sparse=False)] coeffs2 = [lift2smallest_field(c)[0] for c in (g2+j).coefficients(sparse=False)] gg1 = P2(coeffs1) gg2 = P2(coeffs2) C1 = CyclicCodeFromGeneratingPolynomial(n,gg1) C2 = CyclicCodeFromGeneratingPolynomial(n,gg2) return C1,C2
def DuadicCodeEvenPair(F,S1,S2): r""" Constructs the "even pair" of duadic codes associated to the "splitting" (see the docstring for ``is_a_splitting`` for the definition) S1, S2 of n. .. warning:: Maybe the splitting should be associated to a sum of q-cyclotomic cosets mod n, where q is a *prime*. EXAMPLES:: sage: from sage.coding.code_constructions import is_a_splitting sage: n = 11; q = 3 sage: C = Zmod(n).cyclotomic_cosets(q); C [[0], [1, 3, 4, 5, 9], [2, 6, 7, 8, 10]] sage: S1 = C[1] sage: S2 = C[2] sage: is_a_splitting(S1,S2,11) True sage: codes.DuadicCodeEvenPair(GF(q),S1,S2) (Linear code of length 11, dimension 5 over Finite Field of size 3, Linear code of length 11, dimension 5 over Finite Field of size 3) """ n = len(S1) + len(S2) + 1 if not is_a_splitting(S1,S2,n): raise TypeError("%s, %s must be a splitting of %s."%(S1,S2,n)) q = F.order() k = Mod(q,n).multiplicative_order() FF = GF(q**k,"z") z = FF.gen() zeta = z**((q**k-1)/n) P1 = PolynomialRing(FF,"x") x = P1.gen() g1 = prod([x-zeta**i for i in S1+[0]]) g2 = prod([x-zeta**i for i in S2+[0]]) P2 = PolynomialRing(F,"x") x = P2.gen() gg1 = P2([lift2smallest_field(c)[0] for c in g1.coefficients(sparse=False)]) gg2 = P2([lift2smallest_field(c)[0] for c in g2.coefficients(sparse=False)]) C1 = CyclicCodeFromGeneratingPolynomial(n,gg1) C2 = CyclicCodeFromGeneratingPolynomial(n,gg2) return C1,C2
def check_consistency(self, n): """ Check that the pseudo-Conway polynomials of degree dividing `n` in this lattice satisfy the required compatibility conditions. EXAMPLES:: sage: from sage.rings.finite_rings.conway_polynomials import PseudoConwayLattice sage: PCL = PseudoConwayLattice(2, use_database=False) sage: PCL.check_consistency(6) sage: PCL.check_consistency(60) # long """ p = self.p K = FiniteField(p**n, modulus = self.polynomial(n), names='a') a = K.gen() for m in n.divisors(): assert (a**((p**n-1)//(p**m-1))).minimal_polynomial() == self.polynomial(m)
def BIBD_5q_5_for_q_prime_power(q): r""" Return a `(5q,5,1)`-BIBD with `q\equiv 1\pmod 4` a prime power. See Theorem 24 [ClaytonSmith]_. INPUT: - ``q`` (integer) -- a prime power such that `q\equiv 1\pmod 4`. EXAMPLES:: sage: from sage.combinat.designs.bibd import BIBD_5q_5_for_q_prime_power sage: for q in [25, 45, 65, 85, 125, 145, 185, 205, 305, 405, 605]: # long time ....: _ = BIBD_5q_5_for_q_prime_power(q/5) # long time """ from sage.rings.arith import is_prime_power from sage.rings.finite_rings.constructor import FiniteField if q%4 != 1 or not is_prime_power(q): raise ValueError("q is not a prime power or q%4!=1.") d = (q-1)/4 B = [] F = FiniteField(q,'x') a = F.primitive_element() L = {b:i for i,b in enumerate(F)} for b in L: B.append([i*q + L[b] for i in range(5)]) for i in range(5): for j in range(d): B.append([ i*q + L[b ], ((i+1)%5)*q + L[ a**j+b ], ((i+1)%5)*q + L[-a**j+b ], ((i+4)%5)*q + L[ a**(j+d)+b], ((i+4)%5)*q + L[-a**(j+d)+b], ]) return B
def __init__(self, field = None): """ Create a shrinking generator cryptosystem. INPUT: A string monoid over a binary alphabet. OUTPUT: EXAMPLES:: sage: E = ShrinkingGeneratorCryptosystem() sage: E Shrinking generator cryptosystem over Finite Field of size 2 """ if field is None: field = FiniteField(2) if field.cardinality() != 2: raise NotImplementedError("Not yet implemented.") S = BinaryStrings() P = PolynomialRing(field, 'x') SymmetricKeyCryptosystem.__init__(self, S, S, None) self._field = field
def test_01(self): order = 2**8 F = FiniteField(order, 'a') P = PolynomialRing(F, 'x') n = 7 deg = 2 poly = F.fetch_int(42) for i in range(1, deg+1): poly += F.random_element() * P.gen()**i # evaluate polynomial at different points (shares) print(poly) points = [(F.fetch_int(i), poly(F.fetch_int(i))) for i in range(1, n+1)] points[0] = (points[0][0], points[0][1] + F.fetch_int(9)) print(points) assert poly == berlekamp_welsh(deg, points)
def __init__(self, n=7, k=3, order=2**8): r""" Sharmir secret sharing. EXAMPLES:: sage: from sage.crypto.smc.shamir_ss import ShamirSS sage: sss = ShamirSS() sage: secret = 42 sage: shares = sss.share(secret) sage: secret == sss.reconstruct(shares) True """ self._k = k # threshold self._n = n # number shares self._order = order # order of field from sage.rings.finite_rings.constructor import FiniteField self._F = FiniteField(self._order, 'a') if not self._F.is_prime_field() and not hasattr(self._F, 'fetch_int'): raise TypeError("field order not supported") from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing self._P = PolynomialRing(self._F, 'x')
def T2starGeneralizedQuadrangleGraph(q, dual=False, hyperoval=None, field=None, check_hyperoval=True): r""" Return the collinearity graph of the generalized quadrangle `T_2^*(q)`, or of its dual Let `q=2^k` and `\Theta=PG(3,q)`. `T_2^*(q)` is a generalized quadrangle [GQwiki]_ of order `(q-1,q+1)`, see 3.1.3 in [PT09]_. Fix a plane `\Pi \subset \Theta` and a `hyperoval <http://en.wikipedia.org/wiki/Oval_(projective_plane)#Even_q>`__ `O \subset \Pi`. The points of `T_2^*(q):=T_2^*(O)` are the points of `\Theta` outside `\Pi`, and the lines are the lines of `\Theta` outside `\Pi` that meet `\Pi` in a point of `O`. INPUT: - ``q`` -- a power of two - ``dual`` -- if ``False`` (default), return the graph of `T_2^*(O)`. Otherwise return the graph of the dual `T_2^*(O)`. - ``hyperoval`` -- a hyperoval (i.e. a complete 2-arc; a set of points in the plane meeting every line in 0 or 2 points) in the plane of points with 0th coordinate 0 in `PG(3,q)` over the field ``field``. Each point of ``hyperoval`` must be a length 4 vector over ``field`` with 1st non-0 coordinate equal to 1. By default, ``hyperoval`` and ``field`` are not specified, and constructed on the fly. In particular, ``hyperoval`` we build is the classical one, i.e. a conic with the point of intersection of its tangent lines. - ``field`` -- an instance of a finite field of order `q`, must be provided if ``hyperoval`` is provided. - ``check_hyperoval`` -- (default: ``True``) if ``True``, check ``hyperoval`` for correctness. EXAMPLES: using the built-in construction:: sage: g=graphs.T2starGeneralizedQuadrangleGraph(4); g T2*(O,4); GQ(3, 5): Graph on 64 vertices sage: g.is_strongly_regular(parameters=True) (64, 18, 2, 6) sage: g=graphs.T2starGeneralizedQuadrangleGraph(4,dual=True); g T2*(O,4)*; GQ(5, 3): Graph on 96 vertices sage: g.is_strongly_regular(parameters=True) (96, 20, 4, 4) supplying your own hyperoval:: sage: F=GF(4,'b') sage: O=[vector(F,(0,0,0,1)),vector(F,(0,0,1,0))]+map(lambda x: vector(F, (0,1,x^2,x)),F) sage: g=graphs.T2starGeneralizedQuadrangleGraph(4, hyperoval=O, field=F); g T2*(O,4); GQ(3, 5): Graph on 64 vertices sage: g.is_strongly_regular(parameters=True) (64, 18, 2, 6) TESTS:: sage: F=GF(4,'b') # repeating a point... sage: O=[vector(F,(0,1,0,0)),vector(F,(0,0,1,0))]+map(lambda x: vector(F, (0,1,x^2,x)),F) sage: graphs.T2starGeneralizedQuadrangleGraph(4, hyperoval=O, field=F) Traceback (most recent call last): ... RuntimeError: incorrect hyperoval size sage: O=[vector(F,(0,1,1,0)),vector(F,(0,0,1,0))]+map(lambda x: vector(F, (0,1,x^2,x)),F) sage: graphs.T2starGeneralizedQuadrangleGraph(4, hyperoval=O, field=F) Traceback (most recent call last): ... RuntimeError: incorrect hyperoval """ from sage.combinat.designs.incidence_structures import IncidenceStructure from sage.combinat.designs.block_design import ProjectiveGeometryDesign as PG from sage.modules.free_module_element import free_module_element as vector p, k = is_prime_power(q,get_data=True) if k==0 or p!=2: raise ValueError('q must be a power of 2') if field is None: F = FiniteField(q, 'a') else: F = field Theta = PG(3, 1, F, point_coordinates=1) Pi = set(filter(lambda x: x[0]==F.zero(), Theta.ground_set())) if hyperoval is None: O = filter(lambda x: x[1]+x[2]*x[3]==0 or (x[1]==1 and x[2]==0 and x[3]==0), Pi) O = set(O) else: map(lambda x: x.set_immutable(), hyperoval) O = set(hyperoval) if check_hyperoval: if len(O) != q+2: raise RuntimeError("incorrect hyperoval size") for L in Theta.blocks(): if set(L).issubset(Pi): if not len(O.intersection(L)) in [0,2]: raise RuntimeError("incorrect hyperoval") L = map(lambda z: filter(lambda y: not y in O, z), filter(lambda x: len(O.intersection(x)) == 1, Theta.blocks())) if dual: G = IncidenceStructure(L).intersection_graph() G.name('T2*(O,'+str(q)+')*; GQ'+str((q+1,q-1))) else: G = IncidenceStructure(L).dual().intersection_graph() G.name('T2*(O,'+str(q)+'); GQ'+str((q-1,q+1))) return G
def v_4_1_BIBD(v, check=True): r""" Return a `(v,4,1)`-BIBD. A `(v,4,1)`-BIBD is an edge-decomposition of the complete graph `K_v` into copies of `K_4`. For more information, see :func:`balanced_incomplete_block_design`. It exists if and only if `v\equiv 1,4 \pmod {12}`. See page 167 of [Stinson2004]_ for the construction details. .. SEEALSO:: * :func:`balanced_incomplete_block_design` INPUT: - ``v`` (integer) -- number of points. - ``check`` (boolean) -- whether to check that output is correct before returning it. As this is expected to be useless (but we are cautious guys), you may want to disable it whenever you want speed. Set to ``True`` by default. EXAMPLES:: sage: from sage.combinat.designs.bibd import v_4_1_BIBD # long time sage: for n in range(13,100): # long time ....: if n%12 in [1,4]: # long time ....: _ = v_4_1_BIBD(n, check = True) # long time TESTS: Check that the `(25,4)` and `(37,4)`-difference family are available:: sage: assert designs.difference_family(25,4,existence=True) sage: _ = designs.difference_family(25,4) sage: assert designs.difference_family(37,4,existence=True) sage: _ = designs.difference_family(37,4) Check some larger `(v,4,1)`-BIBD (see :trac:`17557`):: sage: for v in range(400): # long time ....: if v%12 in [1,4]: # long time ....: _ = designs.balanced_incomplete_block_design(v,4) # long time """ k = 4 if v == 0: return [] if v <= 12 or v % 12 not in [1, 4]: raise EmptySetError( "A K_4-decomposition of K_v exists iif v=2,4 mod 12, v>12 or v==0") # Step 1. Base cases. if v == 13: # note: this construction can also be obtained from difference_family from block_design import projective_plane return projective_plane(3)._blocks if v == 16: from block_design import AffineGeometryDesign from sage.rings.finite_rings.constructor import FiniteField return AffineGeometryDesign(2, 1, FiniteField(4, 'x'))._blocks if v == 25 or v == 37: from difference_family import difference_family G, D = difference_family(v, 4) return BIBD_from_difference_family(G, D, check=False) if v == 28: return [[0, 1, 23, 26], [0, 2, 10, 11], [0, 3, 16, 18], [0, 4, 15, 20], [0, 5, 8, 9], [0, 6, 22, 25], [0, 7, 14, 21], [0, 12, 17, 27], [0, 13, 19, 24], [1, 2, 24, 27], [1, 3, 11, 12], [1, 4, 17, 19], [1, 5, 14, 16], [1, 6, 9, 10], [1, 7, 20, 25], [1, 8, 15, 22], [1, 13, 18, 21], [2, 3, 21, 25], [2, 4, 12, 13], [2, 5, 18, 20], [2, 6, 15, 17], [2, 7, 19, 22], [2, 8, 14, 26], [2, 9, 16, 23], [3, 4, 22, 26], [3, 5, 7, 13], [3, 6, 14, 19], [3, 8, 20, 23], [3, 9, 15, 27], [3, 10, 17, 24], [4, 5, 23, 27], [4, 6, 7, 8], [4, 9, 14, 24], [4, 10, 16, 21], [4, 11, 18, 25], [5, 6, 21, 24], [5, 10, 15, 25], [5, 11, 17, 22], [5, 12, 19, 26], [6, 11, 16, 26], [6, 12, 18, 23], [6, 13, 20, 27], [7, 9, 17, 18], [7, 10, 26, 27], [7, 11, 23, 24], [7, 12, 15, 16], [8, 10, 18, 19], [8, 11, 21, 27], [8, 12, 24, 25], [8, 13, 16, 17], [9, 11, 19, 20], [9, 12, 21, 22], [9, 13, 25, 26], [10, 12, 14, 20], [10, 13, 22, 23], [11, 13, 14, 15], [14, 17, 23, 25], [14, 18, 22, 27], [15, 18, 24, 26], [15, 19, 21, 23], [16, 19, 25, 27], [16, 20, 22, 24], [17, 20, 21, 26]] # Step 2 : this is function PBD_4_5_8_9_12 PBD = PBD_4_5_8_9_12((v - 1) / (k - 1), check=False) # Step 3 : Theorem 7.20 bibd = BIBD_from_PBD(PBD, v, k, check=False) if check: assert is_pairwise_balanced_design(bibd, v, [k]) return bibd
def SymplecticPolarGraph(d, q, algorithm=None): r""" Returns the Symplectic Polar Graph `Sp(d,q)`. The Symplectic Polar Graph `Sp(d,q)` is built from a projective space of dimension `d-1` over a field `F_q`, and a symplectic form `f`. Two vertices `u,v` are made adjacent if `f(u,v)=0`. See the page `on symplectic graphs on Andries Brouwer's website <http://www.win.tue.nl/~aeb/graphs/Sp.html>`_. INPUT: - ``d,q`` (integers) -- note that only even values of `d` are accepted by the function. - ``algorithm`` -- if set to 'gap' then the computation is carried via GAP library interface, computing totally singular subspaces, which is faster for `q>3`. Otherwise it is done directly. EXAMPLES: Computation of the spectrum of `Sp(6,2)`:: sage: g = graphs.SymplecticGraph(6,2) doctest:...: DeprecationWarning: SymplecticGraph is deprecated. Please use sage.graphs.generators.classical_geometries.SymplecticPolarGraph instead. See http://trac.sagemath.org/19136 for details. sage: g.is_strongly_regular(parameters=True) (63, 30, 13, 15) sage: set(g.spectrum()) == {-5, 3, 30} True The parameters of `Sp(4,q)` are the same as of `O(5,q)`, but they are not isomorphic if `q` is odd:: sage: G = graphs.SymplecticPolarGraph(4,3) sage: G.is_strongly_regular(parameters=True) (40, 12, 2, 4) sage: O=graphs.OrthogonalPolarGraph(5,3) sage: O.is_strongly_regular(parameters=True) (40, 12, 2, 4) sage: O.is_isomorphic(G) False sage: graphs.SymplecticPolarGraph(6,4,algorithm="gap").is_strongly_regular(parameters=True) # not tested (long time) (1365, 340, 83, 85) TESTS:: sage: graphs.SymplecticPolarGraph(4,4,algorithm="gap").is_strongly_regular(parameters=True) (85, 20, 3, 5) sage: graphs.SymplecticPolarGraph(4,4).is_strongly_regular(parameters=True) (85, 20, 3, 5) sage: graphs.SymplecticPolarGraph(4,4,algorithm="blah") Traceback (most recent call last): ... ValueError: unknown algorithm! """ if d < 1 or d%2 != 0: raise ValueError("d must be even and greater than 2") if algorithm == "gap": # faster for larger (q>3) fields from sage.libs.gap.libgap import libgap G = _polar_graph(d, q, libgap.SymplecticGroup(d, q)) elif algorithm == None: # faster for small (q<4) fields from sage.modules.free_module import VectorSpace from sage.schemes.projective.projective_space import ProjectiveSpace from sage.matrix.constructor import identity_matrix, block_matrix, zero_matrix F = FiniteField(q,"x") M = block_matrix(F, 2, 2, [zero_matrix(F,d/2), identity_matrix(F,d/2), -identity_matrix(F,d/2), zero_matrix(F,d/2)]) V = VectorSpace(F,d) PV = list(ProjectiveSpace(d-1,F)) G = Graph([[tuple(_) for _ in PV], lambda x,y:V(x)*(M*V(y)) == 0], loops = False) else: raise ValueError("unknown algorithm!") G.name("Symplectic Polar Graph Sp("+str(d)+","+str(q)+")") G.relabel() return G
def TaylorTwographDescendantSRG(q, clique_partition=None): r""" constructing the descendant graph of the Taylor's two-graph for `U_3(q)`, `q` odd This is a strongly regular graph with parameters `(v,k,\lambda,\mu)=(q^3, (q^2+1)(q-1)/2, (q-1)^3/4-1, (q^2+1)(q-1)/4)` obtained as a two-graph descendant of the :func:`Taylor's two-graph <sage.combinat.designs.twographs.taylor_twograph>` `T`. This graph admits a partition into cliques of size `q`, which are useful in :func:`TaylorTwographSRG <sage.graphs.generators.classical_geometries.TaylorTwographSRG>`, a strongly regular graph on `q^3+1` vertices in the Seidel switching class of `T`, for which we need `(q^2+1)/2` cliques. The cliques are the `q^2` lines on `v_0` of the projective plane containing the unital for `U_3(q)`, and intersecting the unital (i.e. the vertices of the graph and the point we remove) in `q+1` points. This is all taken from §7E of [BvL84]_. INPUT: - ``q`` -- a power of an odd prime number - ``clique_partition`` -- if ``True``, return `q^2-1` cliques of size `q` with empty pairwise intersection. (Removing all of them leaves a clique, too), and the point removed from the unital. EXAMPLES:: sage: g=graphs.TaylorTwographDescendantSRG(3); g Taylor two-graph descendant SRG: Graph on 27 vertices sage: g.is_strongly_regular(parameters=True) (27, 10, 1, 5) sage: from sage.combinat.designs.twographs import taylor_twograph sage: T = taylor_twograph(3) # long time sage: g.is_isomorphic(T.descendant(T.ground_set()[1])) # long time True sage: g=graphs.TaylorTwographDescendantSRG(5) # not tested (long time) sage: g.is_strongly_regular(parameters=True) # not tested (long time) (125, 52, 15, 26) TESTS:: sage: g,l,_=graphs.TaylorTwographDescendantSRG(3,clique_partition=True) sage: all(map(lambda x: g.is_clique(x), l)) True sage: graphs.TaylorTwographDescendantSRG(4) Traceback (most recent call last): ... ValueError: q must be an odd prime power sage: graphs.TaylorTwographDescendantSRG(6) Traceback (most recent call last): ... ValueError: q must be an odd prime power """ p, k = is_prime_power(q,get_data=True) if k==0 or p==2: raise ValueError('q must be an odd prime power') from sage.schemes.projective.projective_space import ProjectiveSpace from sage.modules.free_module_element import free_module_element as vector from sage.rings.finite_rings.integer_mod import mod from __builtin__ import sum Fq = FiniteField(q**2, 'a') PG = map(tuple,ProjectiveSpace(2, Fq)) def S(x,y): return sum(map(lambda j: x[j]*y[2-j]**q, xrange(3))) V = filter(lambda x: S(x,x)==0, PG) # the points of the unital v0 = V[0] V.remove(v0) if mod(q,4)==1: G = Graph([V,lambda y,z: not (S(v0,y)*S(y,z)*S(z,v0)).is_square()], loops=False) else: G = Graph([V,lambda y,z: (S(v0,y)*S(y,z)*S(z,v0)).is_square()], loops=False) G.name("Taylor two-graph descendant SRG") if clique_partition: lines = map(lambda x: filter(lambda t: t[0]+x*t[1]==0, V), filter(lambda z: z != 0, Fq)) return (G, lines, v0) else: return G
def AhrensSzekeresGeneralizedQuadrangleGraph(q, dual=False): r""" Return the collinearity graph of the generalized quadrangle `AS(q)`, or of its dual Let `q` be an odd prime power. `AS(q)` is a generalized quadrangle [GQwiki]_ of order `(q-1,q+1)`, see 3.1.5 in [PT09]_. Its points are elements of `F_q^3`, and lines are sets of size `q` of the form * `\{ (\sigma, a, b) \mid \sigma\in F_q \}` * `\{ (a, \sigma, b) \mid \sigma\in F_q \}` * `\{ (c \sigma^2 - b \sigma + a, -2 c \sigma + b, \sigma) \mid \sigma\in F_q \}`, where `a`, `b`, `c` are arbitrary elements of `F_q`. INPUT: - ``q`` -- a power of an odd prime number - ``dual`` -- if ``False`` (default), return the collinearity graph of `AS(q)`. Otherwise return the collinearity graph of the dual `AS(q)`. EXAMPLES:: sage: g=graphs.AhrensSzekeresGeneralizedQuadrangleGraph(5); g AS(5); GQ(4, 6): Graph on 125 vertices sage: g.is_strongly_regular(parameters=True) (125, 28, 3, 7) sage: g=graphs.AhrensSzekeresGeneralizedQuadrangleGraph(5,dual=True); g AS(5)*; GQ(6, 4): Graph on 175 vertices sage: g.is_strongly_regular(parameters=True) (175, 30, 5, 5) REFERENCE: .. [GQwiki] `Generalized quadrangle <http://en.wikipedia.org/wiki/Generalized_quadrangle>`__ .. [PT09] S. Payne, J. A. Thas. Finite generalized quadrangles. European Mathematical Society, 2nd edition, 2009. """ from sage.combinat.designs.incidence_structures import IncidenceStructure p, k = is_prime_power(q,get_data=True) if k==0 or p==2: raise ValueError('q must be an odd prime power') F = FiniteField(q, 'a') L = [] for a in F: for b in F: L.append(tuple(map(lambda s: (s, a, b), F))) L.append(tuple(map(lambda s: (a, s, b), F))) for c in F: L.append(tuple(map(lambda s: (c*s**2 - b*s + a, -2*c*s + b, s), F))) if dual: G = IncidenceStructure(L).intersection_graph() G.name('AS('+str(q)+')*; GQ'+str((q+1,q-1))) else: G = IncidenceStructure(L).dual().intersection_graph() G.name('AS('+str(q)+'); GQ'+str((q-1,q+1))) return G
def ButterflyGraph(self, n, vertices='strings'): """ Returns a n-dimensional butterfly graph. The vertices consist of pairs (v,i), where v is an n-dimensional tuple (vector) with binary entries (or a string representation of such) and i is an integer in [0..n]. A directed edge goes from (v,i) to (w,i+1) if v and w are identical except for possibly v[i] != w[i]. A butterfly graph has `(2^n)(n+1)` vertices and `n2^{n+1}` edges. INPUT: - ``vertices`` - 'strings' (default) or 'vectors', specifying whether the vertices are zero-one strings or actually tuples over GF(2). EXAMPLES:: sage: digraphs.ButterflyGraph(2).edges(labels=False) [(('00', 0), ('00', 1)), (('00', 0), ('10', 1)), (('00', 1), ('00', 2)), (('00', 1), ('01', 2)), (('01', 0), ('01', 1)), (('01', 0), ('11', 1)), (('01', 1), ('00', 2)), (('01', 1), ('01', 2)), (('10', 0), ('00', 1)), (('10', 0), ('10', 1)), (('10', 1), ('10', 2)), (('10', 1), ('11', 2)), (('11', 0), ('01', 1)), (('11', 0), ('11', 1)), (('11', 1), ('10', 2)), (('11', 1), ('11', 2))] sage: digraphs.ButterflyGraph(2,vertices='vectors').edges(labels=False) [(((0, 0), 0), ((0, 0), 1)), (((0, 0), 0), ((1, 0), 1)), (((0, 0), 1), ((0, 0), 2)), (((0, 0), 1), ((0, 1), 2)), (((0, 1), 0), ((0, 1), 1)), (((0, 1), 0), ((1, 1), 1)), (((0, 1), 1), ((0, 0), 2)), (((0, 1), 1), ((0, 1), 2)), (((1, 0), 0), ((0, 0), 1)), (((1, 0), 0), ((1, 0), 1)), (((1, 0), 1), ((1, 0), 2)), (((1, 0), 1), ((1, 1), 2)), (((1, 1), 0), ((0, 1), 1)), (((1, 1), 0), ((1, 1), 1)), (((1, 1), 1), ((1, 0), 2)), (((1, 1), 1), ((1, 1), 2))] """ # We could switch to Sage integers to handle arbitrary n. if vertices == 'strings': if n >= 31: raise NotImplementedError, "vertices='strings' is only valid for n<=30." from sage.graphs.generic_graph_pyx import binary butterfly = {} for v in xrange(2**n): for i in range(n): w = v w ^= (1 << i) # push 1 to the left by i and xor with w bv = binary(v) bw = binary(w) # pad and reverse the strings padded_bv = ('0' * (n - len(bv)) + bv)[::-1] padded_bw = ('0' * (n - len(bw)) + bw)[::-1] butterfly[(padded_bv, i)] = [(padded_bv, i + 1), (padded_bw, i + 1)] elif vertices == 'vectors': from sage.modules.free_module import VectorSpace from sage.rings.finite_rings.constructor import FiniteField from copy import copy butterfly = {} for v in VectorSpace(FiniteField(2), n): for i in xrange(n): w = copy(v) w[i] += 1 # Flip the ith bit # We must call tuple since vectors are mutable. To obtain # a vector from the tuple t, just call vector(t). butterfly[(tuple(v), i)] = [(tuple(v), i + 1), (tuple(w), i + 1)] else: raise NotImplementedError, "vertices must be 'strings' or 'vectors'." return DiGraph(butterfly)
def UnitaryPolarGraph(m, q, algorithm="gap"): r""" Returns the Unitary Polar Graph `U(m,q)`. For more information on Unitary Polar graphs, see the `page of Andries Brouwer's website <http://www.win.tue.nl/~aeb/graphs/srghub.html>`_. INPUT: - ``m,q`` (integers) -- `q` must be a prime power. - ``algorithm`` -- if set to 'gap' then the computation is carried via GAP library interface, computing totally singular subspaces, which is faster for large examples (especially with `q>2`). Otherwise it is done directly. EXAMPLES:: sage: G = graphs.UnitaryPolarGraph(4,2); G Unitary Polar Graph U(4, 2); GQ(4, 2): Graph on 45 vertices sage: G.is_strongly_regular(parameters=True) (45, 12, 3, 3) sage: graphs.UnitaryPolarGraph(5,2).is_strongly_regular(parameters=True) (165, 36, 3, 9) sage: graphs.UnitaryPolarGraph(6,2) # not tested (long time) Unitary Polar Graph U(6, 2): Graph on 693 vertices TESTS:: sage: graphs.UnitaryPolarGraph(4,3, algorithm="gap").is_strongly_regular(parameters=True) (280, 36, 8, 4) sage: graphs.UnitaryPolarGraph(4,3).is_strongly_regular(parameters=True) (280, 36, 8, 4) sage: graphs.UnitaryPolarGraph(4,3, algorithm="foo") Traceback (most recent call last): ... ValueError: unknown algorithm! """ if algorithm == "gap": from sage.libs.gap.libgap import libgap G = _polar_graph(m, q**2, libgap.GeneralUnitaryGroup(m, q)) elif algorithm == None: # slow on large examples from sage.schemes.projective.projective_space import ProjectiveSpace from sage.modules.free_module_element import free_module_element as vector Fq = FiniteField(q**2, 'a') PG = map(vector, ProjectiveSpace(m - 1, Fq)) map(lambda x: x.set_immutable(), PG) def P(x,y): return sum(map(lambda j: x[j]*y[m-1-j]**q, xrange(m)))==0 V = filter(lambda x: P(x,x), PG) G = Graph([V,lambda x,y: # bottleneck is here, of course: P(x,y)], loops=False) else: raise ValueError("unknown algorithm!") G.relabel() G.name("Unitary Polar Graph U" + str((m, q))) if m==4: G.name(G.name()+'; GQ'+str((q**2,q))) if m==5: G.name(G.name()+'; GQ'+str((q**2,q**3))) return G
def DesarguesianProjectivePlaneDesign(n, point_coordinates=True, check=True): r""" Return the Desarguesian projective plane of order ``n`` as a 2-design. The Desarguesian projective plane of order `n` can also be defined as the projective plane over a field of order `n`. For more information, have a look at :wikipedia:`Projective_plane`. INPUT: - ``n`` -- an integer which must be a power of a prime number - ``point_coordinates`` (boolean) -- whether to label the points with their homogeneous coordinates (default) or with integers. - ``check`` -- (boolean) Whether to check that output is correct before returning it. As this is expected to be useless (but we are cautious guys), you may want to disable it whenever you want speed. Set to ``True`` by default. .. SEEALSO:: :func:`ProjectiveGeometryDesign` EXAMPLES:: sage: designs.DesarguesianProjectivePlaneDesign(2) (7,3,1)-Balanced Incomplete Block Design sage: designs.DesarguesianProjectivePlaneDesign(3) (13,4,1)-Balanced Incomplete Block Design sage: designs.DesarguesianProjectivePlaneDesign(4) (21,5,1)-Balanced Incomplete Block Design sage: designs.DesarguesianProjectivePlaneDesign(5) (31,6,1)-Balanced Incomplete Block Design sage: designs.DesarguesianProjectivePlaneDesign(6) Traceback (most recent call last): ... ValueError: the order of a finite field must be a prime power """ K = FiniteField(n, 'a') n2 = n**2 relabel = {x:i for i,x in enumerate(K)} Kiter = relabel # it is much faster to iterate throug a dict than through # the finite field K # we decompose the (equivalence class) of points [x:y:z] of the projective # plane into an affine plane, an affine line and a point. At the same time, # we relabel the points with the integers from 0 to n^2 + n as follows: # - the affine plane is the set of points [x:y:1] (i.e. the third coordinate # is non-zero) and gets relabeled from 0 to n^2-1 affine_plane = lambda x,y: relabel[x] + n * relabel[y] # - the affine line is the set of points [x:1:0] (i.e. the third coordinate is # zero but not the second one) and gets relabeld from n^2 to n^2 + n - 1 line_infinity = lambda x: n2 + relabel[x] # - the point is [1:0:0] and gets relabeld n^2 + n point_infinity = n2 + n blcks = [] # the n^2 lines of the form "x = sy + az" for s in Kiter: for a in Kiter: # points in the affine plane blcks.append([affine_plane(s*y+a, y) for y in Kiter]) # point at infinity blcks[-1].append(line_infinity(s)) # the n horizontals of the form "y = az" for a in Kiter: # points in the affine plane blcks.append([affine_plane(x,a) for x in Kiter]) # point at infinity blcks[-1].append(point_infinity) # the line at infinity "z = 0" blcks.append(range(n2,n2+n+1)) if check: from designs_pyx import is_projective_plane if not is_projective_plane(blcks): raise RuntimeError('There is a problem in the function DesarguesianProjectivePlane') from bibd import BalancedIncompleteBlockDesign B = BalancedIncompleteBlockDesign(n2+n+1, blcks, check=check) if point_coordinates: zero = K.zero() one = K.one() d = {affine_plane(x,y): (x,y,one) for x in Kiter for y in Kiter} d.update({line_infinity(x): (x,one,zero) for x in Kiter}) d[n2+n]=(one,zero,zero) B.relabel(d) return B
def hadamard_matrix_paleyII(n): """ Implements the Paley type II construction. The Paley type II case corresponds to the case `p \cong 1 \mod{4}` for a prime `p` (see [Hora]_). EXAMPLES:: sage: sage.combinat.matrices.hadamard_matrix.hadamard_matrix_paleyII(12).det() 2985984 sage: 12^6 2985984 We note that the method returns a normalised Hadamard matrix :: sage: sage.combinat.matrices.hadamard_matrix.hadamard_matrix_paleyII(12) [ 1 1| 1 1| 1 1| 1 1| 1 1| 1 1] [ 1 -1|-1 1|-1 1|-1 1|-1 1|-1 1] [-----+-----+-----+-----+-----+-----] [ 1 -1| 1 -1| 1 1|-1 -1|-1 -1| 1 1] [ 1 1|-1 -1| 1 -1|-1 1|-1 1| 1 -1] [-----+-----+-----+-----+-----+-----] [ 1 -1| 1 1| 1 -1| 1 1|-1 -1|-1 -1] [ 1 1| 1 -1|-1 -1| 1 -1|-1 1|-1 1] [-----+-----+-----+-----+-----+-----] [ 1 -1|-1 -1| 1 1| 1 -1| 1 1|-1 -1] [ 1 1|-1 1| 1 -1|-1 -1| 1 -1|-1 1] [-----+-----+-----+-----+-----+-----] [ 1 -1|-1 -1|-1 -1| 1 1| 1 -1| 1 1] [ 1 1|-1 1|-1 1| 1 -1|-1 -1| 1 -1] [-----+-----+-----+-----+-----+-----] [ 1 -1| 1 1|-1 -1|-1 -1| 1 1| 1 -1] [ 1 1| 1 -1|-1 1|-1 1| 1 -1|-1 -1] TESTS:: sage: from sage.combinat.matrices.hadamard_matrix import (hadamard_matrix_paleyII, is_hadamard_matrix) sage: test_cases = [2*(x+1) for x in range(50) if is_prime_power(x) and x%4==1] sage: all(is_hadamard_matrix(hadamard_matrix_paleyII(n),normalized=True,verbose=True) ....: for n in test_cases) True """ q = n//2 - 1 if not(n%2==0 and is_prime_power(q) and (q % 4 == 1)): raise ValueError("The order %s is not covered by the Paley type II construction." % n) from sage.rings.finite_rings.constructor import FiniteField K = FiniteField(q,'x') K_list = list(K) K_list.insert(0,K.zero()) H = matrix(ZZ, [[(1 if (x-y).is_square() else -1) for x in K_list] for y in K_list]) for i in range(q+1): H[0,i] = 1 H[i,0] = 1 H[i,i] = 0 tr = { 0: matrix(2,2,[ 1,-1,-1,-1]), 1: matrix(2,2,[ 1, 1, 1,-1]), -1: matrix(2,2,[-1,-1,-1, 1])} H = block_matrix(q+1,q+1,[tr[v] for r in H for v in r]) return normalise_hadamard(H)
class ShamirSS(SageObject): r""" Shamir secret sharing. This class implements the original version of perfectly secure secret sharing as proposed by Shamir in [Shamir1979]_. It is a very basic implementation intended for educational purposes only. INPUT: - ``k`` -- (default: ``3``) the threshold for reconstruction. - ``n`` -- (default: ``7``) the number of shares. - ``order`` -- (default: ``2^8``) field order for data to share. EXAMPLES:: sage: from sage.crypto.smc.shamir_ss import ShamirSS Generate shares:: sage: k = 3; n = 7 sage: sss = ShamirSS(n,k) sage: secret = 42 sage: shares = sss.share(secret) Reconstruct secret (Lagrange interpolation):: sage: secret == sss.reconstruct(shares) True Reconstruct with error shares (Belekamp-Welsch decoder):: sage: shares[0] = (shares[0][0], shares[0][1]+1) sage: secret == sss.reconstruct(shares, decoder='bw') True Input vectors:: sage: sss = ShamirSS() sage: secret = [42, 43, 44, 45] sage: shares = sss.share(secret) sage: secret == sss.reconstruct(shares) True TESTS: More random input:: sage: secret = randint(0,255) sage: shares = sss.share(secret) sage: secret == sss.reconstruct(shares, decoder='lg') True sage: secret == sss.reconstruct(shares, decoder='bw') True Secret must be integer representation in field:: sage: secret = 333 sage: shares = sss.share(secret) Traceback (most recent call last): ... TypeError: secret must be within 0 and field order. Working in prime fields:: sage: from sage.rings.arith import random_prime sage: order = random_prime(10**10) sage: secret = randint(0, order-1) sage: sss = ShamirSS(7, 3, order) sage: shares = sss.share(secret) sage: secret == sss.reconstruct(shares) True sage: shares[0] = (shares[0][0], shares[0][1]+1) sage: secret == sss.reconstruct(shares, decoder='bw') True sage: shares[1] = (shares[1][0], shares[1][1]+1) sage: secret == sss.reconstruct(shares, decoder='bw') True sage: shares[-1] = (shares[-1][0], shares[-1][1]+1) sage: secret == sss.reconstruct(shares, decoder='bw') False Working in extension fields:: sage: order = 2**randint(3,15) sage: secret = randint(0, order) sage: sss = ShamirSS(7, 3, order) sage: shares = sss.share(secret) sage: secret == sss.reconstruct(shares[:-2]) True sage: shares[0] = (shares[0][0], shares[0][1]+1) sage: secret == sss.reconstruct(shares, decoder='bw') True sage: shares[1] = (shares[1][0], shares[1][1]+1) sage: secret == sss.reconstruct(shares, decoder='bw') True sage: shares[-1] = (shares[-1][0], shares[-1][1]+1) sage: secret == sss.reconstruct(shares, decoder='bw') False """ def __init__(self, n=7, k=3, order=2**8): r""" Sharmir secret sharing. EXAMPLES:: sage: from sage.crypto.smc.shamir_ss import ShamirSS sage: sss = ShamirSS() sage: secret = 42 sage: shares = sss.share(secret) sage: secret == sss.reconstruct(shares) True """ self._k = k # threshold self._n = n # number shares self._order = order # order of field from sage.rings.finite_rings.constructor import FiniteField self._F = FiniteField(self._order, 'a') if not self._F.is_prime_field() and not hasattr(self._F, 'fetch_int'): raise TypeError("field order not supported") from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing self._P = PolynomialRing(self._F, 'x') ### begin module private api def _latex_(self): r""" Return Latex representation of self. EXAMPLES:: sage: from sage.crypto.smc.shamir_ss import ShamirSS sage: sss=ShamirSS() sage: latex(sss) `(7,3)`-Shamir secret sharing over the field `\Bold{F}_{2^{8}}` """ from sage.misc.latex import latex return "`({},{})`-Shamir secret sharing over the field `{}`".format( self._n, self._k, latex(self._F)) def _to_GF(self, x): r""" Convert integer representation to finite field INPUT: - ``x`` -- the integer representation to be converted. OUTPUT: The finite field representation. EXAMPLES:: sage: from sage.crypto.smc.shamir_ss import ShamirSS sage: sss = ShamirSS() sage: sss._to_GF(42) a^5 + a^3 + a sage: sss._to_GF(255) a^7 + a^6 + a^5 + a^4 + a^3 + a^2 + a + 1 """ # input checking if x > self._F.order(): raise TypeError("secret must be within 0 and field order.") # convert to field type if self._F.is_prime_field(): return self._F(x) else: return self._F.fetch_int(x) def _to_Int(self, x): r""" Convert field representation to integer INPUT: - ``x`` -- the field element to be converted. OUTPUT: The integer representation of the field element. EXAMPLES:: sage: from sage.crypto.smc.shamir_ss import ShamirSS sage: sss = ShamirSS() sage: secret = 42 sage: test = sss._to_Int(sss._to_GF(42)) sage: test == secret True """ from sage.rings.all import Integer if self._F.is_prime_field(): return Integer(x) else: return x.integer_representation() def _rec_berlekamp_welsh(self, points): r""" Reconstruct with Berlekamp-Welsh decoding. INPUT: - ``points`` -- shares as list of tuples. OUTPUT: Reconstructed secret. EXAMPLES:: sage: from sage.crypto.smc.shamir_ss import ShamirSS Decoding with errors:: sage: sss = ShamirSS() sage: secret = 42 sage: shares = sss.share(secret) sage: shares[0] = (shares[0][0], shares[0][1]+1) sage: secret == sss.reconstruct(shares, decoder='bw') True sage: k = 4; n = 10 sage: sss = ShamirSS(n,k) sage: secret = 84 sage: shares = sss.share(secret) sage: shares[0] = (shares[0][0], shares[0][1]+1) sage: shares[1] = (shares[1][0], shares[1][1]+1) sage: shares[-1] = (shares[-1][0], shares[-1][1]+1) sage: secret == sss.reconstruct(shares, decoder='bw') True """ from berlekamp_welsh import berlekamp_welsh polycoeffs = berlekamp_welsh(self._k-1, points).coeffs() return polycoeffs def _rec_lagrange(self, points): r""" Reconstruct with Lagrange interpolation. INPUT: - ``points`` -- shares as list of tuples. OUTPUT: Reconstructed secret. EXAMPLES:: sage: from sage.crypto.smc.shamir_ss import ShamirSS Erasure decoding:: sage: sss = ShamirSS() sage: secret = 42 sage: shares = sss.share(secret) sage: secret == sss.reconstruct(shares) True sage: k = 4; n = 10 sage: sss = ShamirSS(n,k) sage: secret = 42 sage: shares = sss.share(secret) sage: secret == sss.reconstruct(shares) True """ polycoeffs = self._P.lagrange_polynomial(points).coeffs() if len(polycoeffs) != self._k: raise ValueError("lagrange polynomial degree mismatch.") return polycoeffs def _repr_(self): r""" Return String representation of self. EXAMPLES:: sage: from sage.crypto.smc.shamir_ss import ShamirSS sage: sss=ShamirSS() sage: print(sss) (7,3)-Shamir secret sharing over Finite Field in a of size 2^8 """ return "({},{})-Shamir secret sharing over {}".format(self._n, self._k, self._F) ### begin public api def reconstruct(self, shares, decoder='lg'): r""" Reconstruct shares. INPUT: - ``shares`` -- a list of shares ((x,y)-tuples of integer) or list of it. - ``decoder`` -- (default: ``'lg'``) decoder used to reconstruct secret. Must be one of the supported types ``'lg'`` or ``'bw'``. OUTPUT: The reconstructed secret or list of secrets. EXAMPLES:: sage: from sage.crypto.smc.shamir_ss import ShamirSS Simple interface:: sage: sss = ShamirSS() sage: secret = 42 sage: shares = sss.share(secret) sage: secret == sss.reconstruct(shares) True Decoding with errors:: sage: shares[0] = (shares[0][0], shares[0][1]+1) sage: secret == sss.reconstruct(shares, decoder='bw') True sage: k = 4; n = 10 sage: sss = ShamirSS(n,k) sage: secret = randint(0, 255) sage: shares = sss.share(secret) sage: shares[0] = (shares[0][0], shares[0][1]+1) sage: shares[1] = (shares[1][0], shares[1][1]+1) sage: shares[-1] = (shares[-1][0], shares[-1][1]+1) sage: secret == sss.reconstruct(shares, decoder='bw') True TESTS: Working in prime fields:: sage: from sage.rings.arith import random_prime sage: order = random_prime(10**10) sage: secret = randint(0, order-1) sage: sss = ShamirSS(7, 3, order) sage: shares = sss.share(secret) sage: secret == sss.reconstruct(shares) True sage: shares[0] = (shares[0][0], shares[0][1]+1) sage: secret == sss.reconstruct(shares, decoder='bw') True sage: shares[1] = (shares[1][0], shares[1][1]+1) sage: secret == sss.reconstruct(shares, decoder='bw') True sage: shares[-1] = (shares[-1][0], shares[-1][1]+1) sage: secret == sss.reconstruct(shares, decoder='bw') False Working in extension fields:: sage: order = 2**randint(3, 15) sage: secret = randint(0, order-1) sage: sss = ShamirSS(7, 3, order) sage: shares = sss.share(secret) sage: secret == sss.reconstruct(shares[:-2]) True sage: shares[0] = (shares[0][0], shares[0][1]+1) sage: secret == sss.reconstruct(shares, decoder='bw') True sage: shares[1] = (shares[1][0], shares[1][1]+1) sage: secret == sss.reconstruct(shares, decoder='bw') True sage: shares[-1] = (shares[-1][0], shares[-1][1]+1) sage: secret == sss.reconstruct(shares, decoder='bw') False """ # make shares iterable if type(shares[0]) == tuple: shares = [shares] # set decoder if decoder == 'lg': decode = self._rec_lagrange elif decoder == 'bw': decode = self._rec_berlekamp_welsh else: raise ValueError("unknown decoder.") # reconstruct secret secret = [] for element in shares: # convert to field points = [(self._to_GF(x), self._to_GF(y)) for x,y in element] # call decoder secret.append(self._to_Int(decode(points)[0])) if len(secret) == 1: secret = secret[0] return secret def share(self, secret): r""" Generate shares. A polynomial of degree `k-1` is generated at random with the secret being the constant coefficient. It is then evaluated at points starting from `1`. INPUT: - ``secret`` -- the secret to be shared as integer or list of integer. OUTPUT: The shares or a list of shares, if list input. EXAMPLES:: sage: from sage.crypto.smc.shamir_ss import ShamirSS Simple interface:: sage: sss = ShamirSS() sage: secret = 42 sage: shares = sss.share(secret) sage: [i+1 == share[0] for i, share in enumerate(shares)] [True, True, True, True, True, True, True] Input vector:: sage: sss = ShamirSS() sage: secret = [42, 43, 44, 45] sage: shares = sss.share(secret) sage: secret == sss.reconstruct(shares) True """ # make input iterable from sage.rings.integer import Integer if not type(secret) == list: secret = [secret] # generate shares shares = [] for s in secret: # random polynomial with s as constant coefficient ssp = self._to_GF(s) for i in range(1, self._k): ssp += self._F.random_element() * self._P.gen()**i # evaluate polynomial at different points (shares) shares.append([(i, self._to_Int(ssp(self._to_GF(i)))) for i in range(1, self._n+1)]) if len(shares) == 1: shares = shares[0] return shares
def v_4_1_rbibd(v, existence=False): r""" Return a `(v,4,1)`-RBIBD. INPUT: - `n` (integer) - ``existence`` (boolean; ``False`` by default) -- whether to build the design or only answer whether it exists. .. SEEALSO:: - :meth:`IncidenceStructure.is_resolvable` - :func:`resolvable_balanced_incomplete_block_design` .. NOTE:: A resolvable `(v,4,1)`-BIBD exists whenever `1\equiv 4\pmod(12)`. This function, however, only implements a construction of `(v,4,1)`-BIBD such that `v=3q+1\equiv 1\pmod{3}` where `q` is a prime power (see VII.7.5.a from [BJL99]_). EXAMPLE:: sage: rBIBD = designs.resolvable_balanced_incomplete_block_design(28,4) sage: rBIBD.is_resolvable() True sage: rBIBD.is_t_design(return_parameters=True) (True, (2, 28, 4, 1)) TESTS:: sage: for q in prime_powers(2,30): ....: if (3*q+1)%12 == 4: ....: _ = designs.resolvable_balanced_incomplete_block_design(3*q+1,4) # indirect doctest """ # Volume 1, VII.7.5.a from [BJL99]_ if v % 3 != 1 or not is_prime_power((v - 1) // 3): if existence: return Unknown raise NotImplementedError( "I don't know how to build a ({},{},1)-RBIBD!".format(v, 4)) from sage.rings.finite_rings.constructor import FiniteField as GF q = (v - 1) // 3 nn = (q - 1) // 4 G = GF(q, 'x') w = G.primitive_element() e = w**(nn) assert e**2 == -1 first_class = [[(w**i, j), (-w**i, j), (e * w**i, j + 1), (-e * w**i, j + 1)] for i in range(nn) for j in range(3)] first_class.append([(0, 0), (0, 1), (0, 2), 'inf']) label = {p: i for i, p in enumerate(G)} classes = [[[ v - 1 if x == 'inf' else (x[1] % 3) * q + label[x[0] + g] for x in S ] for S in first_class] for g in G] BIBD = BalancedIncompleteBlockDesign(v, blocks=sum(classes, []), k=4, check=True, copy=False) BIBD._classes = classes assert BIBD.is_resolvable() return BIBD
def kirkman_triple_system(v, existence=False): r""" Return a Kirkman Triple System on `v` points. A Kirkman Triple System `KTS(v)` is a resolvable Steiner Triple System. It exists if and only if `v\equiv 3\pmod{6}`. INPUT: - `n` (integer) - ``existence`` (boolean; ``False`` by default) -- whether to build the `KTS(n)` or only answer whether it exists. .. SEEALSO:: :meth:`IncidenceStructure.is_resolvable` EXAMPLES: A solution to Kirkmman's original problem:: sage: kts = designs.kirkman_triple_system(15) sage: classes = kts.is_resolvable(1)[1] sage: names = '0123456789abcde' sage: to_name = lambda (r,s,t): ' '+names[r]+names[s]+names[t]+' ' sage: rows = [' '.join(('Day {}'.format(i) for i in range(1,8)))] sage: rows.extend(' '.join(map(to_name,row)) for row in zip(*classes)) sage: print '\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.constructor import FiniteField as GF q = (v - 1) // 2 K = GF(q, 'x') a = K.primitive_element() t = (q - 1) / 6 # m is the solution of a^m=(a^t+1)/2 from sage.groups.generic import discrete_log m = discrete_log((a**t + 1) / 2, a) assert 2 * a**m == a**t + 1 # First parallel class first_class = [[(0, 1), (0, 2), 'inf']] b0 = K.one() b1 = a**t b2 = a**m first_class.extend([(b0 * a**i, 1), (b1 * a**i, 1), (b2 * a**i, 2)] for i in range(t) + range(2 * t, 3 * t) + range(4 * t, 5 * t)) b0 = a**(m + t) b1 = a**(m + 3 * t) b2 = a**(m + 5 * t) first_class.extend([[(b0 * a**i, 2), (b1 * a**i, 2), (b2 * a**i, 2)] for i in range(t)]) # Action of K on the points action = lambda v, x: (v + x[0], x[1]) if len(x) == 2 else x # relabel to integer relabel = {(p, x): i + (x - 1) * q for i, p in enumerate(K) for x in [1, 2]} relabel['inf'] = 2 * q classes = [[[relabel[action(p, x)] for x in tr] for tr in first_class] for p in K] KTS = BalancedIncompleteBlockDesign( v, [tr for cl in classes for tr in cl], k=3, lambd=1, copy=False) KTS._classes = classes return KTS # Construction 1.2 from [Stinson91] (originally Theorem 5 from [RCW71]) # # For all prime powers q=1 mod 6, there exists a KTS(3q) elif (v // 3) % 6 == 1 and is_prime_power(v // 3): from sage.rings.finite_rings.constructor import FiniteField as GF q = v // 3 K = GF(q, 'x') a = K.primitive_element() t = (q - 1) / 6 A0 = [(0, 0), (0, 1), (0, 2)] B = [[(a**i, j), (a**(i + 2 * t), j), (a**(i + 4 * t), j)] for j in range(3) for i in range(t)] A = [[(a**i, 0), (a**(i + 2 * t), 1), (a**(i + 4 * t), 2)] for i in range(6 * t)] # Action of K on the points action = lambda v, x: (v + x[0], x[1]) # relabel to integer relabel = {(p, j): i + j * q for i, p in enumerate(K) for j in range(3)} B0 = [A0] + B + A[t:2 * t] + A[3 * t:4 * t] + A[5 * t:6 * t] # Classes classes = [[[relabel[action(p, x)] for x in tr] for tr in B0] for p in K] for i in range(t) + range(2 * t, 3 * t) + range(4 * t, 5 * t): classes.append([[relabel[action(p, x)] for x in A[i]] for p in K]) KTS = BalancedIncompleteBlockDesign( v, [tr for cl in classes for tr in cl], k=3, lambd=1, copy=False) KTS._classes = classes return KTS else: # This is Lemma IX.6.4 from [BJL99]. # # This construction takes a (v,{4,7})-PBD. All points are doubled (x has # a copy x'), and an infinite point \infty is added. # # On all blocks of 2*4 points we "paste" a KTS(2*4+1) using the infinite # point, in such a way that all {x,x',infty} are set of the design. We # do the same for blocks with 2*7 points using a KTS(2*7+1). # # Note that the triples of points equal to {x,x',\infty} will be added # several times. # # As all those subdesigns are resolvable, each class of the KTS(n) is # obtained by considering a set {x,x',\infty} and all sets of all # parallel classes of the subdesign which contain this set. # We create the small KTS(n') we need, and relabel them such that # 01(n'-1),23(n'-1),... are blocks of the design. gdd4 = kirkman_triple_system(9) gdd7 = kirkman_triple_system(15) X = [B for B in gdd4 if 8 in B] for b in X: b.remove(8) X = sum(X, []) + [8] gdd4.relabel({v: i for i, v in enumerate(X)}) gdd4 = gdd4.is_resolvable(True)[1] # the relabeled classes X = [B for B in gdd7 if 14 in B] for b in X: b.remove(14) X = sum(X, []) + [14] gdd7.relabel({v: i for i, v in enumerate(X)}) gdd7 = gdd7.is_resolvable(True)[1] # the relabeled classes # The first parallel class contains 01(n'-1), the second contains # 23(n'-1), etc.. # Then remove the blocks containing (n'-1) for B in gdd4: for i, b in enumerate(B): if 8 in b: j = min(b) del B[i] B.insert(0, j) break gdd4.sort() for B in gdd4: B.pop(0) for B in gdd7: for i, b in enumerate(B): if 14 in b: j = min(b) del B[i] B.insert(0, j) break gdd7.sort() for B in gdd7: B.pop(0) # Pasting the KTS(n') without {x,x',\infty} blocks classes = [[] for i in range((v - 1) / 2)] gdd = {4: gdd4, 7: gdd7} for B in PBD_4_7((v - 1) // 2, check=False): for i, classs in enumerate(gdd[len(B)]): classes[B[i]].extend([[2 * B[x // 2] + x % 2 for x in BB] for BB in classs]) # The {x,x',\infty} blocks for i, classs in enumerate(classes): classs.append([2 * i, 2 * i + 1, v - 1]) KTS = BalancedIncompleteBlockDesign( v, blocks=[tr for cl in classes for tr in cl], k=3, lambd=1, check=True, copy=False) KTS._classes = classes assert KTS.is_resolvable() return KTS
def v_4_1_BIBD(v, check=True): r""" Returns a `(v,4,1)`-BIBD. A `(v,4,1)`-BIBD is an edge-decomposition of the complete graph `K_v` into copies of `K_4`. For more information, see :meth:`BalancedIncompleteBlockDesign`. It exists if and only if `v\equiv 1,4 \pmod {12}`. See page 167 of [Stinson2004]_ for the construction details. .. SEEALSO:: * :meth:`BalancedIncompleteBlockDesign` INPUT: - ``v`` (integer) -- number of points. - ``check`` (boolean) -- whether to check that output is correct before returning it. As this is expected to be useless (but we are cautious guys), you may want to disable it whenever you want speed. Set to ``True`` by default. EXAMPLES:: sage: from sage.combinat.designs.bibd import v_4_1_BIBD # long time sage: for n in range(13,100): # long time ....: if n%12 in [1,4]: # long time ....: _ = v_4_1_BIBD(n, check = True) # long time """ from sage.rings.finite_rings.constructor import FiniteField k = 4 if v == 0: return [] if v <= 12 or v % 12 not in [1, 4]: raise ValueError( "A K_4-decomposition of K_v exists iif v=2,4 mod 12, v>12 or v==0") # Step 1. Base cases. if v == 13: from block_design import projective_plane return projective_plane(3).blocks() if v == 16: from block_design import AffineGeometryDesign return AffineGeometryDesign(2, 1, FiniteField(4, 'x')).blocks() if v == 25: return [[0, 1, 17, 22], [0, 2, 11, 21], [0, 3, 15, 18], [0, 4, 7, 13], [0, 5, 12, 14], [0, 6, 19, 23], [0, 8, 16, 24], [0, 9, 10, 20], [1, 2, 3, 4], [1, 5, 6, 7], [1, 8, 12, 15], [1, 9, 13, 16], [1, 10, 11, 14], [1, 18, 20, 23], [1, 19, 21, 24], [2, 5, 15, 24], [2, 6, 9, 17], [2, 7, 14, 18], [2, 8, 22, 23], [2, 10, 12, 13], [2, 16, 19, 20], [3, 5, 16, 22], [3, 6, 11, 20], [3, 7, 12, 19], [3, 8, 9, 14], [3, 10, 17, 24], [3, 13, 21, 23], [4, 5, 10, 23], [4, 6, 8, 21], [4, 9, 18, 24], [4, 11, 15, 16], [4, 12, 17, 20], [4, 14, 19, 22], [5, 8, 13, 20], [5, 9, 11, 19], [5, 17, 18, 21], [6, 10, 15, 22], [6, 12, 16, 18], [6, 13, 14, 24], [7, 8, 11, 17], [7, 9, 15, 23], [7, 10, 16, 21], [7, 20, 22, 24], [8, 10, 18, 19], [9, 12, 21, 22], [11, 12, 23, 24], [11, 13, 18, 22], [13, 15, 17, 19], [14, 15, 20, 21], [14, 16, 17, 23]] if v == 28: return [[0, 1, 23, 26], [0, 2, 10, 11], [0, 3, 16, 18], [0, 4, 15, 20], [0, 5, 8, 9], [0, 6, 22, 25], [0, 7, 14, 21], [0, 12, 17, 27], [0, 13, 19, 24], [1, 2, 24, 27], [1, 3, 11, 12], [1, 4, 17, 19], [1, 5, 14, 16], [1, 6, 9, 10], [1, 7, 20, 25], [1, 8, 15, 22], [1, 13, 18, 21], [2, 3, 21, 25], [2, 4, 12, 13], [2, 5, 18, 20], [2, 6, 15, 17], [2, 7, 19, 22], [2, 8, 14, 26], [2, 9, 16, 23], [3, 4, 22, 26], [3, 5, 7, 13], [3, 6, 14, 19], [3, 8, 20, 23], [3, 9, 15, 27], [3, 10, 17, 24], [4, 5, 23, 27], [4, 6, 7, 8], [4, 9, 14, 24], [4, 10, 16, 21], [4, 11, 18, 25], [5, 6, 21, 24], [5, 10, 15, 25], [5, 11, 17, 22], [5, 12, 19, 26], [6, 11, 16, 26], [6, 12, 18, 23], [6, 13, 20, 27], [7, 9, 17, 18], [7, 10, 26, 27], [7, 11, 23, 24], [7, 12, 15, 16], [8, 10, 18, 19], [8, 11, 21, 27], [8, 12, 24, 25], [8, 13, 16, 17], [9, 11, 19, 20], [9, 12, 21, 22], [9, 13, 25, 26], [10, 12, 14, 20], [10, 13, 22, 23], [11, 13, 14, 15], [14, 17, 23, 25], [14, 18, 22, 27], [15, 18, 24, 26], [15, 19, 21, 23], [16, 19, 25, 27], [16, 20, 22, 24], [17, 20, 21, 26]] if v == 37: return [[0, 1, 3, 24], [0, 2, 23, 36], [0, 4, 26, 32], [0, 5, 9, 31], [0, 6, 11, 15], [0, 7, 17, 25], [0, 8, 20, 27], [0, 10, 18, 30], [0, 12, 19, 29], [0, 13, 14, 16], [0, 21, 34, 35], [0, 22, 28, 33], [1, 2, 4, 25], [1, 5, 27, 33], [1, 6, 10, 32], [1, 7, 12, 16], [1, 8, 18, 26], [1, 9, 21, 28], [1, 11, 19, 31], [1, 13, 20, 30], [1, 14, 15, 17], [1, 22, 35, 36], [1, 23, 29, 34], [2, 3, 5, 26], [2, 6, 28, 34], [2, 7, 11, 33], [2, 8, 13, 17], [2, 9, 19, 27], [2, 10, 22, 29], [2, 12, 20, 32], [2, 14, 21, 31], [2, 15, 16, 18], [2, 24, 30, 35], [3, 4, 6, 27], [3, 7, 29, 35], [3, 8, 12, 34], [3, 9, 14, 18], [3, 10, 20, 28], [3, 11, 23, 30], [3, 13, 21, 33], [3, 15, 22, 32], [3, 16, 17, 19], [3, 25, 31, 36], [4, 5, 7, 28], [4, 8, 30, 36], [4, 9, 13, 35], [4, 10, 15, 19], [4, 11, 21, 29], [4, 12, 24, 31], [4, 14, 22, 34], [4, 16, 23, 33], [4, 17, 18, 20], [5, 6, 8, 29], [5, 10, 14, 36], [5, 11, 16, 20], [5, 12, 22, 30], [5, 13, 25, 32], [5, 15, 23, 35], [5, 17, 24, 34], [5, 18, 19, 21], [6, 7, 9, 30], [6, 12, 17, 21], [6, 13, 23, 31], [6, 14, 26, 33], [6, 16, 24, 36], [6, 18, 25, 35], [6, 19, 20, 22], [7, 8, 10, 31], [7, 13, 18, 22], [7, 14, 24, 32], [7, 15, 27, 34], [7, 19, 26, 36], [7, 20, 21, 23], [8, 9, 11, 32], [8, 14, 19, 23], [8, 15, 25, 33], [8, 16, 28, 35], [8, 21, 22, 24], [9, 10, 12, 33], [9, 15, 20, 24], [9, 16, 26, 34], [9, 17, 29, 36], [9, 22, 23, 25], [10, 11, 13, 34], [10, 16, 21, 25], [10, 17, 27, 35], [10, 23, 24, 26], [11, 12, 14, 35], [11, 17, 22, 26], [11, 18, 28, 36], [11, 24, 25, 27], [12, 13, 15, 36], [12, 18, 23, 27], [12, 25, 26, 28], [13, 19, 24, 28], [13, 26, 27, 29], [14, 20, 25, 29], [14, 27, 28, 30], [15, 21, 26, 30], [15, 28, 29, 31], [16, 22, 27, 31], [16, 29, 30, 32], [17, 23, 28, 32], [17, 30, 31, 33], [18, 24, 29, 33], [18, 31, 32, 34], [19, 25, 30, 34], [19, 32, 33, 35], [20, 26, 31, 35], [20, 33, 34, 36], [21, 27, 32, 36]] # Step 2 : this is function PBD_4_5_8_9_12 PBD = PBD_4_5_8_9_12((v - 1) / (k - 1), check=False) # Step 3 : Theorem 7.20 bibd = BIBD_from_PBD(PBD, v, k, check=False) if check: _check_pbd(bibd, v, [k]) return bibd
def ProjectivePlaneDesign(n, type="Desarguesian"): r""" Returns a projective plane of order `n`. A finite projective plane is a 2-design with `n^2+n+1` lines (or blocks) and `n^2+n+1` points. For more information on finite projective planes, see the :wikipedia:`Projective_plane#Finite_projective_planes`. INPUT: - ``n`` -- the finite projective plane's order - ``type`` -- When set to ``"Desarguesian"``, the method returns Desarguesian projective planes, i.e. a finite projective plane obtained by considering the 1- and 2- dimensional spaces of `F_n^3`. For the moment, no other value is available for this parameter. .. SEEALSO:: :meth:`ProjectiveGeometryDesign` EXAMPLES:: sage: designs.ProjectivePlaneDesign(2) Incidence structure with 7 points and 7 blocks Non-existent ones:: sage: designs.ProjectivePlaneDesign(10) Traceback (most recent call last): ... ValueError: No projective plane design of order 10 exists. sage: designs.ProjectivePlaneDesign(14) Traceback (most recent call last): ... ValueError: By the Bruck-Ryser-Chowla theorem, no projective plane of order 14 exists. An unknown one:: sage: designs.ProjectivePlaneDesign(12) Traceback (most recent call last): ... ValueError: If such a projective plane exists, we do not know how to build it. TESTS:: sage: designs.ProjectivePlaneDesign(10, type="AnyThingElse") Traceback (most recent call last): ... ValueError: The value of 'type' must be 'Desarguesian'. """ from sage.rings.arith import two_squares if type != "Desarguesian": raise ValueError("The value of 'type' must be 'Desarguesian'.") try: F = FiniteField(n, 'x') except ValueError: if n == 10: raise ValueError("No projective plane design of order 10 exists.") try: if (n % 4) in [1, 2]: two_squares(n) except ValueError: raise ValueError("By the Bruck-Ryser-Chowla theorem, no projective" " plane of order " + str(n) + " exists.") raise ValueError("If such a projective plane exists, " "we do not know how to build it.") return ProjectiveGeometryDesign(2, 1, F)
def hadamard_matrix_paleyI(n, normalize=True): """ Implements the Paley type I construction. The Paley type I case corresponds to the case `p \cong 3 \mod{4}` for a prime `p` (see [Hora]_). INPUT: - ``n`` -- the matrix size - ``normalize`` (boolean) -- whether to normalize the result. EXAMPLES: We note that this method by default returns a normalised Hadamard matrix :: sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_paleyI sage: hadamard_matrix_paleyI(4) [ 1 1 1 1] [ 1 -1 1 -1] [ 1 -1 -1 1] [ 1 1 -1 -1] Otherwise, it returns a skew Hadamard matrix `H`, i.e. `H=S+I`, with `S=-S^\top` :: sage: M=hadamard_matrix_paleyI(4, normalize=False); M [ 1 1 1 1] [-1 1 1 -1] [-1 -1 1 1] [-1 1 -1 1] sage: S=M-identity_matrix(4); -S==S.T True TESTS:: sage: from sage.combinat.matrices.hadamard_matrix import is_hadamard_matrix sage: test_cases = [x+1 for x in range(100) if is_prime_power(x) and x%4==3] sage: all(is_hadamard_matrix(hadamard_matrix_paleyI(n),normalized=True,verbose=True) ....: for n in test_cases) True sage: all(is_hadamard_matrix(hadamard_matrix_paleyI(n,normalize=False),verbose=True) ....: for n in test_cases) True """ p = n - 1 if not(is_prime_power(p) and (p % 4 == 3)): raise ValueError("The order %s is not covered by the Paley type I construction." % n) from sage.rings.finite_rings.constructor import FiniteField K = FiniteField(p,'x') K_list = list(K) K_list.insert(0,K.zero()) H = matrix(ZZ, [[(1 if (x-y).is_square() else -1) for x in K_list] for y in K_list]) for i in range(n): H[i,0] = -1 H[0,i] = 1 if normalize: for i in range(n): H[i,i] = -1 H = normalise_hadamard(H) return H
def OA_from_Vmt(m,t,V): r""" Return an Orthogonal Array from a `V(m,t)` **Definition** Let `q` be a prime power and let `q=mt+1` for `m,t` integers. Let `\omega` be a primitive element of `\mathbb{F}_q`. A `V(m,t)` vector is a vector `(a_1,\dots,a_{m+1}` for which, for each `1\leq k < m`, the differences .. MATH:: \{a_{i+k}-a_i:1\leq i \leq m+1,i+k\neq m+2\} represent the `m` cyclotomic classes of `\mathbb{F}_{mt+1}` (compute subscripts modulo `m+2`). In other words, for fixed `k`, is `a_{i+k}-a_i=\omega^{mx+\alpha}` and `a_{j+k}-a_j=\omega^{my+\beta}` then `\alpha\not\equiv\beta \mod{m}` *Construction of a quasi-difference matrix from a `V(m,t)` vector* Starting with a `V(m,t)` vector `(a_1,\dots,a_{m+1})`, form a single column of length `m+2` whose first entry is empty, and whose remaining entries are `(a_1,\dots,a_{m+1})`. Form `t` columns by multiplying this column by the `t` th roots, i.e. the powers of `\omega^m`. From each of these `t` columns, form `m+2` columns by taking the `m+2` cyclic shifts of the column. The result is a `(a,m+2;1,0;t)-QDM`. For more information, refer to the Handbook of Combinatorial Designs [DesignHandbook]_. INPUT: - ``m,t`` (integers) - ``V`` -- the vector `V(m,t)`. .. SEEALSO:: :func:`OA_from_quasi_difference_matrix` EXAMPLES:: sage: _ = designs.orthogonal_array(6,46) # indirect doctest """ from sage.rings.finite_rings.constructor import FiniteField q = m*t+1 Fq = FiniteField(q) w = Fq.primitive_element() # Cyclic shift of a list cyclic_shift = lambda l,i : l[-i:]+l[:-i] M = [] wm = w**m for i in range(t): L = [None] for e in V: L.append(e*wm**i) for ii in range(m+2): M.append(cyclic_shift(L,ii)) M.append([0]*q) M = zip(*M) M = OA_from_quasi_difference_matrix(M,Fq,add_col = False) return M
def BCHCode(n,delta,F,b=0): r""" A 'Bose-Chaudhuri-Hockenghem code' (or BCH code for short) is the largest possible cyclic code of length n over field F=GF(q), whose generator polynomial has zeros (which contain the set) `Z = \{a^{b},a^{b+1}, ..., a^{b+delta-2}\}`, where a is a primitive `n^{th}` root of unity in the splitting field `GF(q^m)`, b is an integer `0\leq b\leq n-delta+1` and m is the multiplicative order of q modulo n. (The integers `b,...,b+delta-2` typically lie in the range `1,...,n-1`.) The integer `delta \geq 1` is called the "designed distance". The length n of the code and the size q of the base field must be relatively prime. The generator polynomial is equal to the least common multiple of the minimal polynomials of the elements of the set `Z` above. Special cases are b=1 (resulting codes are called 'narrow-sense' BCH codes), and `n=q^m-1` (known as 'primitive' BCH codes). It may happen that several values of delta give rise to the same BCH code. The largest one is called the Bose distance of the code. The true minimum distance, d, of the code is greater than or equal to the Bose distance, so `d\geq delta`. EXAMPLES:: sage: FF.<a> = GF(3^2,"a") sage: x = PolynomialRing(FF,"x").gen() sage: L = [b.minpoly() for b in [a,a^2,a^3]]; g = LCM(L) sage: f = x^(8)-1 sage: g.divides(f) True sage: C = codes.CyclicCode(8,g); C Linear code of length 8, dimension 4 over Finite Field of size 3 sage: C.minimum_distance() 4 sage: C = codes.BCHCode(8,3,GF(3),1); C Linear code of length 8, dimension 4 over Finite Field of size 3 sage: C.minimum_distance() 4 sage: C = codes.BCHCode(8,3,GF(3)); C Linear code of length 8, dimension 5 over Finite Field of size 3 sage: C.minimum_distance() 3 sage: C = codes.BCHCode(26, 5, GF(5), b=1); C Linear code of length 26, dimension 10 over Finite Field of size 5 """ from sage.misc.misc import srange q = F.order() R = IntegerModRing(n) m = R(q).multiplicative_order() FF = GF(q**m,"z") z = FF.gen() e = z.multiplicative_order()/n a = z**e # order n P = PolynomialRing(F,"x") x = P.gen() cosets = Set([]) for i in srange(b,b+delta-1): cosets = cosets.union(Set(cyclotomic_cosets(q, n, i))) L0 = [a**j for j in cosets] L1 = [P(ai.minpoly()) for ai in L0] g = P(LCM(L1)) #print cosets, "\n", g, "\n", (x**n-1).factor(), "\n", L1, "\n", g.divides(x**n-1) if not(g.divides(x**n-1)): raise ValueError, "BCH codes does not exist with the given input." return CyclicCodeFromGeneratingPolynomial(n,g)
def GDD_4_2(q,existence=False,check=True): r""" Return a `(2q,\{4\},\{2\})`-GDD for `q` a prime power with `q\equiv 1\pmod{6}`. This method implements Lemma VII.5.17 from [BJL99] (p.495). INPUT: - ``q`` (integer) - ``existence`` (boolean) -- instead of building the design, return: - ``True`` -- meaning that Sage knows how to build the design - ``Unknown`` -- meaning that Sage does not know how to build the design, but that the design may exist (see :mod:`sage.misc.unknown`). - ``False`` -- meaning that the design does not exist. - ``check`` -- (boolean) Whether to check that output is correct before returning it. As this is expected to be useless (but we are cautious guys), you may want to disable it whenever you want speed. Set to ``True`` by default. EXAMPLE:: sage: from sage.combinat.designs.group_divisible_designs import GDD_4_2 sage: GDD_4_2(7,existence=True) True sage: GDD_4_2(7) Group Divisible Design on 14 points of type 2^7 sage: GDD_4_2(8,existence=True) Unknown sage: GDD_4_2(8) Traceback (most recent call last): ... NotImplementedError """ if q <=1 or q%6 != 1 or not is_prime_power(q): if existence: return Unknown raise NotImplementedError if existence: return True from sage.rings.finite_rings.constructor import FiniteField as GF G = GF(q,'x') w = G.primitive_element() e = w**((q-1)/3) # A first parallel class is defined. G acts on it, which yields all others. first_class = [[(0,0),(1,w**i),(1,e*w**i),(1,e*e*w**i)] for i in range((q-1)/6)] label = {p:i for i,p in enumerate(G)} classes = [[[2*label[x[1]+g]+(x[0]+j)%2 for x in S] for S in first_class] for g in G for j in range(2)] return GroupDivisibleDesign(2*q, groups = [[i,i+1] for i in range(0,2*q,2)], blocks = sum(classes,[]), K = [4], G = [2], check = check, copy = False)
def GDD_4_2(q, existence=False, check=True): r""" Return a `(2q,\{4\},\{2\})`-GDD for `q` a prime power with `q\equiv 1\pmod{6}`. This method implements Lemma VII.5.17 from [BJL99] (p.495). INPUT: - ``q`` (integer) - ``existence`` (boolean) -- instead of building the design, return: - ``True`` -- meaning that Sage knows how to build the design - ``Unknown`` -- meaning that Sage does not know how to build the design, but that the design may exist (see :mod:`sage.misc.unknown`). - ``False`` -- meaning that the design does not exist. - ``check`` -- (boolean) Whether to check that output is correct before returning it. As this is expected to be useless (but we are cautious guys), you may want to disable it whenever you want speed. Set to ``True`` by default. EXAMPLE:: sage: from sage.combinat.designs.group_divisible_designs import GDD_4_2 sage: GDD_4_2(7,existence=True) True sage: GDD_4_2(7) Group Divisible Design on 14 points of type 2^7 sage: GDD_4_2(8,existence=True) Unknown sage: GDD_4_2(8) Traceback (most recent call last): ... NotImplementedError """ if q <= 1 or q % 6 != 1 or not is_prime_power(q): if existence: return Unknown raise NotImplementedError if existence: return True from sage.rings.finite_rings.constructor import FiniteField as GF G = GF(q, 'x') w = G.primitive_element() e = w**((q - 1) / 3) # A first parallel class is defined. G acts on it, which yields all others. first_class = [[(0, 0), (1, w**i), (1, e * w**i), (1, e * e * w**i)] for i in range((q - 1) / 6)] label = {p: i for i, p in enumerate(G)} classes = [[[2 * label[x[1] + g] + (x[0] + j) % 2 for x in S] for S in first_class] for g in G for j in range(2)] return GroupDivisibleDesign(2 * q, groups=[[i, i + 1] for i in range(0, 2 * q, 2)], blocks=sum(classes, []), K=[4], G=[2], check=check, copy=False)
def HughesPlane(q2, check=True): r""" Return the Hughes projective plane of order ``q2``. Let `q` be an odd prime, the Hughes plane of order `q^2` is a finite projective plane of order `q^2` introduced by D. Hughes in [Hu57]_. Its construction is as follows. Let `K = GF(q^2)` be a finite field with `q^2` elements and `F = GF(q) \subset K` be its unique subfield with `q` elements. We define a twisted multiplication on `K` as .. MATH:: x \circ y = \begin{cases} x\ y & \text{if y is a square in K}\\ x^q\ y & \text{otherwise} \end{cases} The points of the Hughes plane are the triples `(x, y, z)` of points in `K^3 \backslash \{0,0,0\}` up to the equivalence relation `(x,y,z) \sim (x \circ k, y \circ k, z \circ k)` where `k \in K`. For `a = 1` or `a \in (K \backslash F)` we define a block `L(a)` as the set of triples `(x,y,z)` so that `x + a \circ y + z = 0`. The rest of the blocks are obtained by letting act the group `GL(3, F)` by its standard action. For more information, see :wikipedia:`Hughes_plane` and [We07]. .. SEEALSO:: :func:`DesarguesianProjectivePlaneDesign` to build the Desarguesian projective planes INPUT: - ``q2`` -- an even power of an odd prime number - ``check`` -- (boolean) Whether to check that output is correct before returning it. As this is expected to be useless (but we are cautious guys), you may want to disable it whenever you want speed. Set to ``True`` by default. EXAMPLES:: sage: H = designs.HughesPlane(9) sage: H (91,10,1)-Balanced Incomplete Block Design We prove in the following computations that the Desarguesian plane ``H`` is not Desarguesian. Let us consider the two triangles `(0,1,10)` and `(57, 70, 59)`. We show that the intersection points `D_{0,1} \cap D_{57,70}`, `D_{1,10} \cap D_{70,59}` and `D_{10,0} \cap D_{59,57}` are on the same line while `D_{0,70}`, `D_{1,59}` and `D_{10,57}` are not concurrent:: sage: blocks = H.blocks() sage: line = lambda p,q: (b for b in blocks if p in b and q in b).next() sage: b_0_1 = line(0, 1) sage: b_1_10 = line(1, 10) sage: b_10_0 = line(10, 0) sage: b_57_70 = line(57, 70) sage: b_70_59 = line(70, 59) sage: b_59_57 = line(59, 57) sage: set(b_0_1).intersection(b_57_70) {2} sage: set(b_1_10).intersection(b_70_59) {73} sage: set(b_10_0).intersection(b_59_57) {60} sage: line(2, 73) == line(73, 60) True sage: b_0_57 = line(0, 57) sage: b_1_70 = line(1, 70) sage: b_10_59 = line(10, 59) sage: p = set(b_0_57).intersection(b_1_70) sage: q = set(b_1_70).intersection(b_10_59) sage: p == q False TESTS: Some wrong input:: sage: designs.HughesPlane(5) Traceback (most recent call last): ... EmptySetError: No Hughes plane of non-square order exists. sage: designs.HughesPlane(16) Traceback (most recent call last): ... EmptySetError: No Hughes plane of even order exists. Check that it works for non-prime `q`:: sage: designs.HughesPlane(3**4) # not tested - 10 secs (6643,82,1)-Balanced Incomplete Block Design """ if not q2.is_square(): raise EmptySetError("No Hughes plane of non-square order exists.") if q2%2 == 0: raise EmptySetError("No Hughes plane of even order exists.") q = q2.sqrt() K = FiniteField(q2, prefix='x', conway=True) F = FiniteField(q, prefix='y', conway=True) A = q3_minus_one_matrix(F) A = A.change_ring(K) m = K.list() V = VectorSpace(K, 3) zero = K.zero() one = K.one() points = [(x, y, one) for x in m for y in m] + \ [(x, one, zero) for x in m] + \ [(one, zero, zero)] relabel = {tuple(p):i for i,p in enumerate(points)} blcks = [] for a in m: if a not in F or a == 1: # build L(a) aa = ~a l = [] l.append(V((-a, one, zero))) for x in m: y = - aa * (x+one) if not y.is_square(): y *= aa**(q-1) l.append(V((x, y, one))) # compute the orbit of L(a) blcks.append([relabel[normalize_hughes_plane_point(p,q)] for p in l]) for i in range(q2 + q): l = [A*j for j in l] blcks.append([relabel[normalize_hughes_plane_point(p,q)] for p in l]) from bibd import BalancedIncompleteBlockDesign return BalancedIncompleteBlockDesign(q2**2+q2+1, blcks, check=check)
def DesarguesianProjectivePlaneDesign(n, check=True): r""" Return the Desarguesian projective plane of order ``n`` as a 2-design. The Desarguesian projective plane of order `n` can also be defined as the projective plane over a field of order `n`. For more information, have a look at :wikipedia:`Projective_plane`. INPUT: - ``n`` -- an integer which must be a power of a prime number - ``check`` -- (boolean) Whether to check that output is correct before returning it. As this is expected to be useless (but we are cautious guys), you may want to disable it whenever you want speed. Set to ``True`` by default. .. SEEALSO:: :func:`ProjectiveGeometryDesign` EXAMPLES:: sage: designs.DesarguesianProjectivePlaneDesign(2) Incidence structure with 7 points and 7 blocks sage: designs.DesarguesianProjectivePlaneDesign(3) Incidence structure with 13 points and 13 blocks sage: designs.DesarguesianProjectivePlaneDesign(4) Incidence structure with 21 points and 21 blocks sage: designs.DesarguesianProjectivePlaneDesign(5) Incidence structure with 31 points and 31 blocks sage: designs.DesarguesianProjectivePlaneDesign(6) Traceback (most recent call last): ... ValueError: the order of a finite field must be a prime power. """ K = FiniteField(n, 'x') n2 = n**2 relabel = {x: i for i, x in enumerate(K)} Kiter = relabel # it is much faster to iterate throug a dict than through # the finite field K # we decompose the (equivalence class) of points [x:y:z] of the projective # plane into an affine plane, an affine line and a point. At the same time, # we relabel the points with the integers from 0 to n^2 + n as follows: # - the affine plane is the set of points [x:y:1] (i.e. the third coordinate # is non-zero) and gets relabeled from 0 to n^2-1 affine_plane = lambda x, y: relabel[x] + n * relabel[y] # - the affine line is the set of points [x:1:0] (i.e. the third coordinate is # zero but not the second one) and gets relabeld from n^2 to n^2 + n - 1 line_infinity = lambda x: n2 + relabel[x] # - the point is [1:0:0] and gets relabeld n^2 + n point_infinity = n2 + n blcks = [] # the n^2 lines of the form "x = sy + az" for s in Kiter: for a in Kiter: # points in the affine plane blcks.append([affine_plane(s * y + a, y) for y in Kiter]) # point at infinity blcks[-1].append(line_infinity(s)) # the n horizontals of the form "y = az" for a in Kiter: # points in the affine plane blcks.append([affine_plane(x, a) for x in Kiter]) # point at infinity blcks[-1].append(point_infinity) # the line at infinity "z = 0" blcks.append(range(n2, n2 + n + 1)) return BlockDesign(n2 + n + 1, blcks, name="Desarguesian projective plane of order %d" % n, test=check)
def BIBD_from_arc_in_desarguesian_projective_plane(n,k,existence=False): r""" Returns a `(n,k,1)`-BIBD from a maximal arc in a projective plane. This function implements a construction from Denniston [Denniston69]_, who describes a maximal :meth:`arc <sage.combinat.designs.bibd.BalancedIncompleteBlockDesign.arc>` in a :func:`Desarguesian Projective Plane <sage.combinat.designs.block_design.DesarguesianProjectivePlaneDesign>` of order `2^k`. From two powers of two `n,q` with `n<q`, it produces a `((n-1)(q+1)+1,n,1)`-BIBD. INPUT: - ``n,k`` (integers) -- must be powers of two (among other restrictions). - ``existence`` (boolean) -- whether to return the BIBD obtained through this construction (default), or to merely indicate with a boolean return value whether this method *can* build the requested BIBD. EXAMPLES: A `(232,8,1)`-BIBD:: sage: from sage.combinat.designs.bibd import BIBD_from_arc_in_desarguesian_projective_plane sage: from sage.combinat.designs.bibd import BalancedIncompleteBlockDesign sage: D = BIBD_from_arc_in_desarguesian_projective_plane(232,8) sage: BalancedIncompleteBlockDesign(232,D) (232,8,1)-Balanced Incomplete Block Design A `(120,8,1)`-BIBD:: sage: D = BIBD_from_arc_in_desarguesian_projective_plane(120,8) sage: BalancedIncompleteBlockDesign(120,D) (120,8,1)-Balanced Incomplete Block Design Other parameters:: sage: all(BIBD_from_arc_in_desarguesian_projective_plane(n,k,existence=True) ....: for n,k in ....: [(120, 8), (232, 8), (456, 8), (904, 8), (496, 16), ....: (976, 16), (1936, 16), (2016, 32), (4000, 32), (8128, 64)]) True Of course, not all can be built this way:: sage: BIBD_from_arc_in_desarguesian_projective_plane(7,3,existence=True) False sage: BIBD_from_arc_in_desarguesian_projective_plane(7,3) Traceback (most recent call last): ... ValueError: This function cannot produce a (7,3,1)-BIBD REFERENCE: .. [Denniston69] R. H. F. Denniston, Some maximal arcs in finite projective planes. Journal of Combinatorial Theory 6, no. 3 (1969): 317-319. http://dx.doi.org/10.1016/S0021-9800(69)80095-5 """ q = (n-1)//(k-1)-1 if (k % 2 or q % 2 or q <= k or n != (k-1)*(q+1)+1 or not is_prime_power(k) or not is_prime_power(q)): if existence: return False raise ValueError("This function cannot produce a ({},{},1)-BIBD".format(n,k)) if existence: return True n = k # From now on, the code assumes the notations of [Denniston69] for n,q, so # that the BIBD returned by the method will have the requested parameters. from sage.rings.finite_rings.constructor import FiniteField as GF from sage.libs.gap.libgap import libgap from sage.matrix.constructor import Matrix K = GF(q,'a') one = K.one() # An irreducible quadratic form over K[X,Y] GO = libgap.GeneralOrthogonalGroup(-1,2,q) M = libgap.InvariantQuadraticForm(GO)['matrix'] M = Matrix(M) M = M.change_ring(K) Q = lambda xx,yy : M[0,0]*xx**2+(M[0,1]+M[1,0])*xx*yy+M[1,1]*yy**2 # Here, the additive subgroup H (of order n) of K mentioned in # [Denniston69] is the set of all elements of K of degree < log_n # (seeing elements of K as polynomials in 'a') K_iter = list(K) # faster iterations log_n = is_prime_power(n,get_data=True)[1] C = [(x,y,one) for x in K_iter for y in K_iter if Q(x,y).polynomial().degree() < log_n] from sage.combinat.designs.block_design import DesarguesianProjectivePlaneDesign return DesarguesianProjectivePlaneDesign(q).trace(C)._blocks
def QuaternionMatrixGroupGF3(): r""" The quaternion group as a set of `2\times 2` matrices over `GF(3)`. OUTPUT: A matrix group consisting of `2\times 2` matrices with elements from the finite field of order 3. The group is the quaternion group, the nonabelian group of order 8 that is not isomorphic to the group of symmetries of a square (the dihedral group `D_4`). .. note:: This group is most easily available via ``groups.matrix.QuaternionGF3()``. EXAMPLES: The generators are the matrix representations of the elements commonly called `I` and `J`, while `K` is the product of `I` and `J`. :: sage: from sage.groups.matrix_gps.finitely_generated import QuaternionMatrixGroupGF3 sage: Q = QuaternionMatrixGroupGF3() sage: Q.order() 8 sage: aye = Q.gens()[0]; aye [1 1] [1 2] sage: jay = Q.gens()[1]; jay [2 1] [1 1] sage: kay = aye*jay; kay [0 2] [1 0] TESTS:: sage: groups.matrix.QuaternionGF3() Matrix group over Finite Field of size 3 with 2 generators ( [1 1] [2 1] [1 2], [1 1] ) sage: Q = QuaternionMatrixGroupGF3() sage: QP = Q.as_permutation_group() sage: QP.is_isomorphic(QuaternionGroup()) True sage: H = DihedralGroup(4) sage: H.order() 8 sage: QP.is_abelian(), H.is_abelian() (False, False) sage: QP.is_isomorphic(H) False """ from sage.rings.finite_rings.constructor import FiniteField from sage.matrix.matrix_space import MatrixSpace MS = MatrixSpace(FiniteField(3), 2) aye = MS([1,1,1,2]) jay = MS([2,1,1,1]) return MatrixGroup([aye, jay])
def polynomial(self, n): r""" Return the pseudo-Conway polynomial of degree `n` in this lattice. INPUT: - ``n`` -- positive integer OUTPUT: - a pseudo-Conway polynomial of degree `n` for the prime `p`. ALGORITHM: Uses an algorithm described in [HL99]_, modified to find pseudo-Conway polynomials rather than Conway polynomials. The major difference is that we stop as soon as we find a primitive polynomial. REFERENCE: .. [HL99] L. Heath and N. Loehr (1999). New algorithms for generating Conway polynomials over finite fields. Proceedings of the tenth annual ACM-SIAM symposium on discrete algorithms, pp. 429-437. EXAMPLES:: sage: from sage.rings.finite_rings.conway_polynomials import PseudoConwayLattice sage: PCL = PseudoConwayLattice(2, use_database=False) sage: PCL.polynomial(3) x^3 + x + 1 sage: PCL.polynomial(4) x^4 + x^3 + 1 sage: PCL.polynomial(60) x^60 + x^59 + x^58 + x^55 + x^54 + x^53 + x^52 + x^51 + x^48 + x^46 + x^45 + x^42 + x^41 + x^39 + x^38 + x^37 + x^35 + x^32 + x^31 + x^30 + x^28 + x^24 + x^22 + x^21 + x^18 + x^17 + x^16 + x^15 + x^14 + x^10 + x^8 + x^7 + x^5 + x^3 + x^2 + x + 1 """ if n in self.nodes: return self.nodes[n] p = self.p if n == 1: f = self.ring.gen() - FiniteField(p).multiplicative_generator() self.nodes[1] = f return f # Work in an arbitrary field K of order p**n. K = FiniteField(p**n, names='a') # TODO: something like the following # gcds = [n.gcd(d) for d in self.nodes.keys()] # xi = { m: (...) for m in gcds } xi = {q: self.polynomial(n//q).any_root(K, -n//q, assume_squarefree=True) for q in n.prime_divisors()} # The following is needed to ensure that in the concrete instantiation # of the "new" extension all previous choices are compatible. _frobenius_shift(K, xi) # Construct a compatible element having order the lcm of orders q, x = xi.popitem() v = p**(n//q) - 1 for q, xitem in xi.iteritems(): w = p**(n//q) - 1 g, alpha, beta = v.xgcd(w) x = x**beta * xitem**alpha v = v.lcm(w) r = p**n - 1 # Get the missing part of the order to be primitive g = r // v # Iterate through g-th roots of x until a primitive one is found z = x.nth_root(g) root = K.multiplicative_generator()**v while z.multiplicative_order() != r: z *= root # The following should work but tries to create a huge list # whose length overflows Python's ints for large parameters #Z = x.nth_root(g, all=True) #for z in Z: # if z.multiplicative_order() == r: # break f = z.minimal_polynomial() self.nodes[n] = f return f
def kirkman_triple_system(v,existence=False): r""" Return a Kirkman Triple System on `v` points. A Kirkman Triple System `KTS(v)` is a resolvable Steiner Triple System. It exists if and only if `v\equiv 3\pmod{6}`. INPUT: - `n` (integer) - ``existence`` (boolean; ``False`` by default) -- whether to build the `KTS(n)` or only answer whether it exists. .. SEEALSO:: :meth:`IncidenceStructure.is_resolvable` EXAMPLES: A solution to Kirkmman's original problem:: sage: kts = designs.kirkman_triple_system(15) sage: classes = kts.is_resolvable(1)[1] sage: names = '0123456789abcde' sage: to_name = lambda (r,s,t): ' '+names[r]+names[s]+names[t]+' ' sage: rows = [join(('Day {}'.format(i) for i in range(1,8)), ' ')] sage: rows.extend(join(map(to_name,row), ' ') for row in zip(*classes)) sage: print join(rows,'\n') Day 1 Day 2 Day 3 Day 4 Day 5 Day 6 Day 7 07e 18e 29e 3ae 4be 5ce 6de 139 24a 35b 46c 05d 167 028 26b 03c 14d 257 368 049 15a 458 569 06a 01b 12c 23d 347 acd 7bd 78c 89d 79a 8ab 9bc TESTS:: sage: for i in range(3,300,6): ....: _ = designs.kirkman_triple_system(i) """ if v%6 != 3: if existence: return False raise ValueError("There is no KTS({}) as v!=3 mod(6)".format(v)) if existence: return False elif v == 3: return BalancedIncompleteBlockDesign(3,[[0,1,2]],k=3,lambd=1) elif v == 9: classes = [[[0, 1, 5], [2, 6, 7], [3, 4, 8]], [[1, 6, 8], [3, 5, 7], [0, 2, 4]], [[1, 4, 7], [0, 3, 6], [2, 5, 8]], [[4, 5, 6], [0, 7, 8], [1, 2, 3]]] KTS = BalancedIncompleteBlockDesign(v,[tr for cl in classes for tr in cl],k=3,lambd=1,copy=False) KTS._classes = classes return KTS # Construction 1.1 from [Stinson91] (originally Theorem 6 from [RCW71]) # # For all prime powers q=1 mod 6, there exists a KTS(2q+1) elif ((v-1)//2)%6 == 1 and is_prime_power((v-1)//2): from sage.rings.finite_rings.constructor import FiniteField as GF q = (v-1)//2 K = GF(q,'x') a = K.primitive_element() t = (q-1)/6 # m is the solution of a^m=(a^t+1)/2 from sage.groups.generic import discrete_log m = discrete_log((a**t+1)/2, a) assert 2*a**m == a**t+1 # First parallel class first_class = [[(0,1),(0,2),'inf']] b0 = K.one(); b1 = a**t; b2 = a**m first_class.extend([(b0*a**i,1),(b1*a**i,1),(b2*a**i,2)] for i in range(t)+range(2*t,3*t)+range(4*t,5*t)) b0 = a**(m+t); b1=a**(m+3*t); b2=a**(m+5*t) first_class.extend([[(b0*a**i,2),(b1*a**i,2),(b2*a**i,2)] for i in range(t)]) # Action of K on the points action = lambda v,x : (v+x[0],x[1]) if len(x) == 2 else x # relabel to integer relabel = {(p,x): i+(x-1)*q for i,p in enumerate(K) for x in [1,2]} relabel['inf'] = 2*q classes = [[[relabel[action(p,x)] for x in tr] for tr in first_class] for p in K] KTS = BalancedIncompleteBlockDesign(v,[tr for cl in classes for tr in cl],k=3,lambd=1,copy=False) KTS._classes = classes return KTS # Construction 1.2 from [Stinson91] (originally Theorem 5 from [RCW71]) # # For all prime powers q=1 mod 6, there exists a KTS(3q) elif (v//3)%6 == 1 and is_prime_power(v//3): from sage.rings.finite_rings.constructor import FiniteField as GF q = v//3 K = GF(q,'x') a = K.primitive_element() t = (q-1)/6 A0 = [(0,0),(0,1),(0,2)] B = [[(a**i,j),(a**(i+2*t),j),(a**(i+4*t),j)] for j in range(3) for i in range(t)] A = [[(a**i,0),(a**(i+2*t),1),(a**(i+4*t),2)] for i in range(6*t)] # Action of K on the points action = lambda v,x: (v+x[0],x[1]) # relabel to integer relabel = {(p,j): i+j*q for i,p in enumerate(K) for j in range(3)} B0 = [A0] + B + A[t:2*t] + A[3*t:4*t] + A[5*t:6*t] # Classes classes = [[[relabel[action(p,x)] for x in tr] for tr in B0] for p in K] for i in range(t)+range(2*t,3*t)+range(4*t,5*t): classes.append([[relabel[action(p,x)] for x in A[i]] for p in K]) KTS = BalancedIncompleteBlockDesign(v,[tr for cl in classes for tr in cl],k=3,lambd=1,copy=False) KTS._classes = classes return KTS else: # This is Lemma IX.6.4 from [BJL99]. # # This construction takes a (v,{4,7})-PBD. All points are doubled (x has # a copy x'), and an infinite point \infty is added. # # On all blocks of 2*4 points we "paste" a KTS(2*4+1) using the infinite # point, in such a way that all {x,x',infty} are set of the design. We # do the same for blocks with 2*7 points using a KTS(2*7+1). # # Note that the triples of points equal to {x,x',\infty} will be added # several times. # # As all those subdesigns are resolvable, each class of the KTS(n) is # obtained by considering a set {x,x',\infty} and all sets of all # parallel classes of the subdesign which contain this set. # We create the small KTS(n') we need, and relabel them such that # 01(n'-1),23(n'-1),... are blocks of the design. gdd4 = kirkman_triple_system(9) gdd7 = kirkman_triple_system(15) X = [B for B in gdd4 if 8 in B] for b in X: b.remove(8) X = sum(X, []) + [8] gdd4.relabel({v:i for i,v in enumerate(X)}) gdd4 = gdd4.is_resolvable(True)[1] # the relabeled classes X = [B for B in gdd7 if 14 in B] for b in X: b.remove(14) X = sum(X, []) + [14] gdd7.relabel({v:i for i,v in enumerate(X)}) gdd7 = gdd7.is_resolvable(True)[1] # the relabeled classes # The first parallel class contains 01(n'-1), the second contains # 23(n'-1), etc.. # Then remove the blocks containing (n'-1) for B in gdd4: for i,b in enumerate(B): if 8 in b: j = min(b); del B[i]; B.insert(0,j); break gdd4.sort() for B in gdd4: B.pop(0) for B in gdd7: for i,b in enumerate(B): if 14 in b: j = min(b); del B[i]; B.insert(0,j); break gdd7.sort() for B in gdd7: B.pop(0) # Pasting the KTS(n') without {x,x',\infty} blocks classes = [[] for i in range((v-1)/2)] gdd = {4:gdd4, 7: gdd7} for B in PBD_4_7((v-1)//2,check=False): for i,classs in enumerate(gdd[len(B)]): classes[B[i]].extend([[2*B[x//2]+x%2 for x in BB] for BB in classs]) # The {x,x',\infty} blocks for i,classs in enumerate(classes): classs.append([2*i,2*i+1,v-1]) KTS = BalancedIncompleteBlockDesign(v, blocks = [tr for cl in classes for tr in cl], k=3, lambd=1, check=True, copy =False) KTS._classes = classes assert KTS.is_resolvable() return KTS
def BCHCode(n,delta,F,b=0): r""" A 'Bose-Chaudhuri-Hockenghem code' (or BCH code for short) is the largest possible cyclic code of length n over field F=GF(q), whose generator polynomial has zeros (which contain the set) `Z = \{a^{b},a^{b+1}, ..., a^{b+delta-2}\}`, where a is a primitive `n^{th}` root of unity in the splitting field `GF(q^m)`, b is an integer `0\leq b\leq n-delta+1` and m is the multiplicative order of q modulo n. (The integers `b,...,b+delta-2` typically lie in the range `1,...,n-1`.) The integer `delta \geq 1` is called the "designed distance". The length n of the code and the size q of the base field must be relatively prime. The generator polynomial is equal to the least common multiple of the minimal polynomials of the elements of the set `Z` above. Special cases are b=1 (resulting codes are called 'narrow-sense' BCH codes), and `n=q^m-1` (known as 'primitive' BCH codes). It may happen that several values of delta give rise to the same BCH code. The largest one is called the Bose distance of the code. The true minimum distance, d, of the code is greater than or equal to the Bose distance, so `d\geq delta`. EXAMPLES:: sage: FF.<a> = GF(3^2,"a") sage: x = PolynomialRing(FF,"x").gen() sage: L = [b.minpoly() for b in [a,a^2,a^3]]; g = LCM(L) sage: f = x^(8)-1 sage: g.divides(f) True sage: C = codes.CyclicCode(8,g); C Linear code of length 8, dimension 4 over Finite Field of size 3 sage: C.minimum_distance() 4 sage: C = codes.BCHCode(8,3,GF(3),1); C Linear code of length 8, dimension 4 over Finite Field of size 3 sage: C.minimum_distance() 4 sage: C = codes.BCHCode(8,3,GF(3)); C Linear code of length 8, dimension 5 over Finite Field of size 3 sage: C.minimum_distance() 3 sage: C = codes.BCHCode(26, 5, GF(5), b=1); C Linear code of length 26, dimension 10 over Finite Field of size 5 """ q = F.order() R = IntegerModRing(n) m = R(q).multiplicative_order() FF = GF(q**m,"z") z = FF.gen() e = z.multiplicative_order()/n a = z**e # order n P = PolynomialRing(F,"x") x = P.gen() L1 = [] for coset in R.cyclotomic_cosets(q, range(b,b+delta-1)): L1.extend(P((a**j).minpoly()) for j in coset) g = P(LCM(L1)) #print cosets, "\n", g, "\n", (x**n-1).factor(), "\n", L1, "\n", g.divides(x**n-1) if not(g.divides(x**n-1)): raise ValueError("BCH codes does not exist with the given input.") return CyclicCodeFromGeneratingPolynomial(n,g)
def difference_matrix(g, k, lmbda=1, existence=False, check=True): r""" Return a `(g,k,\lambda)`-difference matrix A matrix `M` is a `(g,k,\lambda)`-difference matrix if its entries are element of a group `G` of cardinality `g`, and if for any two rows `R,R'` of `M` and `x\in G` there are exactly `\lambda` values `i` such that `R_i-R'_i=x`. INPUT: - ``k`` -- (integer) number of columns. If ``k=None`` it is set to the largest value available. - ``g`` -- (integer) cardinality of the group `G` - ``lmbda`` -- (integer; default: 1) -- number of times each element of `G` appears as a difference. - ``check`` -- (boolean) Whether to check that output is correct before returning it. As this is expected to be useless (but we are cautious guys), you may want to disable it whenever you want speed. Set to ``True`` by default. - ``existence`` (boolean) -- instead of building the design, return: - ``True`` -- meaning that Sage knows how to build the design - ``Unknown`` -- meaning that Sage does not know how to build the design, but that the design may exist (see :mod:`sage.misc.unknown`). - ``False`` -- meaning that the design does not exist. .. NOTE:: When ``k=None`` and ``existence=True`` the function returns an integer, i.e. the largest `k` such that we can build a `(g,k,\lambda)`-DM. EXAMPLES:: sage: G,M = designs.difference_matrix(25,10); G Finite Field in x of size 5^2 sage: designs.difference_matrix(993,None,existence=1) 32 TESTS:: sage: designs.difference_matrix(10,12,1,existence=True) False sage: designs.difference_matrix(10,12,1) Traceback (most recent call last): ... EmptySetError: No (10,12,1)-Difference Matrix exists as k(=12)>g(=10) sage: designs.difference_matrix(10,9,1,existence=True) Unknown sage: designs.difference_matrix(10,9,1) Traceback (most recent call last): ... NotImplementedError: I don't know how to build a (10,9,1)-Difference Matrix! """ if lmbda == 1 and k is not None and k > g: if existence: return False raise EmptySetError( "No ({},{},{})-Difference Matrix exists as k(={})>g(={})".format( g, k, lmbda, k, g)) # Prime powers elif lmbda == 1 and is_prime_power(g): if k is None: if existence: return g else: k = g elif existence: return True F = FiniteField(g, 'x') F_set = list(F) F_k_set = F_set[:k] G = F M = [[x * y for y in F_k_set] for x in F_set] # From the database elif (g, lmbda) in DM_constructions and ( k is None or DM_constructions[g, lmbda][0] >= k): if k is None: k = DM_constructions[g, lmbda][0] if existence: return k elif existence: return True _, f = DM_constructions[g, lmbda] G, M = f() M = [R[:k] for R in M] else: if existence: return Unknown raise NotImplementedError( "I don't know how to build a ({},{},{})-Difference Matrix!".format( g, k, lmbda)) if check: assert is_difference_matrix( M, G, k, lmbda, 1), "Sage built something which is not a ({},{},{})-DM!".format( g, k, lmbda) return G, M
def polynomial(self, n): r""" Return the pseudo-Conway polynomial of degree `n` in this lattice. INPUT: - ``n`` -- positive integer OUTPUT: - a pseudo-Conway polynomial of degree `n` for the prime `p`. ALGORITHM: Uses an algorithm described in [HL99]_, modified to find pseudo-Conway polynomials rather than Conway polynomials. The major difference is that we stop as soon as we find a primitive polynomial. REFERENCE: .. [HL99] L. Heath and N. Loehr (1999). New algorithms for generating Conway polynomials over finite fields. Proceedings of the tenth annual ACM-SIAM symposium on discrete algorithms, pp. 429-437. EXAMPLES:: sage: from sage.rings.finite_rings.conway_polynomials import PseudoConwayLattice sage: PCL = PseudoConwayLattice(2, use_database=False) sage: PCL.polynomial(3) x^3 + x + 1 sage: PCL.polynomial(4) x^4 + x^3 + 1 sage: PCL.polynomial(60) x^60 + x^59 + x^58 + x^55 + x^54 + x^53 + x^52 + x^51 + x^48 + x^46 + x^45 + x^42 + x^41 + x^39 + x^38 + x^37 + x^35 + x^32 + x^31 + x^30 + x^28 + x^24 + x^22 + x^21 + x^18 + x^17 + x^16 + x^15 + x^14 + x^10 + x^8 + x^7 + x^5 + x^3 + x^2 + x + 1 """ if n in self.nodes: return self.nodes[n] p = self.p if n == 1: f = self.ring.gen() - FiniteField(p).multiplicative_generator() self.nodes[1] = f return f # Work in an arbitrary field K of order p**n. K = FiniteField(p**n, names='a') # TODO: something like the following # gcds = [n.gcd(d) for d in self.nodes.keys()] # xi = { m: (...) for m in gcds } xi = { q: self.polynomial(n // q).any_root(K, -n // q, assume_squarefree=True) for q in n.prime_divisors() } # The following is needed to ensure that in the concrete instantiation # of the "new" extension all previous choices are compatible. _frobenius_shift(K, xi) # Construct a compatible element having order the lcm of orders q, x = xi.popitem() v = p**(n // q) - 1 for q, xitem in xi.iteritems(): w = p**(n // q) - 1 g, alpha, beta = v.xgcd(w) x = x**beta * xitem**alpha v = v.lcm(w) r = p**n - 1 # Get the missing part of the order to be primitive g = r // v # Iterate through g-th roots of x until a primitive one is found z = x.nth_root(g) root = K.multiplicative_generator()**v while z.multiplicative_order() != r: z *= root # The following should work but tries to create a huge list # whose length overflows Python's ints for large parameters #Z = x.nth_root(g, all=True) #for z in Z: # if z.multiplicative_order() == r: # break f = z.minimal_polynomial() self.nodes[n] = f return 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.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 difference_matrix(g, k, lmbda=1, existence=False, check=True): r""" Return a `(g,k,\lambda)`-difference matrix A matrix `M` is a `(g,k,\lambda)`-difference matrix if it has size `\lambda g\times k`, its entries belong to the group `G` of cardinality `g`, and for any two rows `R,R'` of `M` and `x\in G` there are exactly `\lambda` values `i` such that `R_i-R'_i=x`. INPUT: - ``k`` -- (integer) number of columns. If ``k=None`` it is set to the largest value available. - ``g`` -- (integer) cardinality of the group `G` - ``lmbda`` -- (integer; default: 1) -- number of times each element of `G` appears as a difference. - ``check`` -- (boolean) Whether to check that output is correct before returning it. As this is expected to be useless (but we are cautious guys), you may want to disable it whenever you want speed. Set to ``True`` by default. - ``existence`` (boolean) -- instead of building the design, return: - ``True`` -- meaning that Sage knows how to build the design - ``Unknown`` -- meaning that Sage does not know how to build the design, but that the design may exist (see :mod:`sage.misc.unknown`). - ``False`` -- meaning that the design does not exist. .. NOTE:: When ``k=None`` and ``existence=True`` the function returns an integer, i.e. the largest `k` such that we can build a `(g,k,\lambda)`-DM. EXAMPLES:: sage: G,M = designs.difference_matrix(25,10); G Finite Field in x of size 5^2 sage: designs.difference_matrix(993,None,existence=1) 32 Here we print for each `g` the maximum possible `k` for which Sage knows how to build a `(g,k,1)`-difference matrix:: sage: for g in range(2,30): ....: k_max = designs.difference_matrix(g=g,k=None,existence=True) ....: print "{:2} {}".format(g, k_max) ....: _ = designs.difference_matrix(g,k_max) 2 2 3 3 4 4 5 5 6 2 7 7 8 8 9 9 10 2 11 11 12 6 13 13 14 2 15 3 16 16 17 17 18 2 19 19 20 4 21 6 22 2 23 23 24 8 25 25 26 2 27 27 28 6 29 29 TESTS:: sage: designs.difference_matrix(10,12,1,existence=True) False sage: designs.difference_matrix(10,12,1) Traceback (most recent call last): ... EmptySetError: No (10,12,1)-Difference Matrix exists as k(=12)>g(=10) sage: designs.difference_matrix(10,9,1,existence=True) Unknown sage: designs.difference_matrix(10,9,1) Traceback (most recent call last): ... NotImplementedError: I don't know how to build a (10,9,1)-Difference Matrix! """ if lmbda == 1 and k is not None and k > g: if existence: return False raise EmptySetError( "No ({},{},{})-Difference Matrix exists as k(={})>g(={})".format( g, k, lmbda, k, g)) # Prime powers elif lmbda == 1 and is_prime_power(g): if k is None: if existence: return g else: k = g elif existence: return True F = FiniteField(g, 'x') F_set = list(F) F_k_set = F_set[:k] G = F M = [[x * y for y in F_k_set] for x in F_set] # Treat the case k=None # (find the max k such that there exists a DM) elif k is None: i = 2 while difference_matrix(g=g, k=i, lmbda=lmbda, existence=True): i += 1 return i - 1 # From the database elif (g, lmbda) in DM_constructions and DM_constructions[g, lmbda][0] >= k: if existence: return True _, f = DM_constructions[g, lmbda] G, M = f() M = [R[:k] for R in M] # Product construction elif find_product_decomposition(g, k, lmbda): if existence: return True (g1, lmbda1), (g2, lmbda2) = find_product_decomposition(g, k, lmbda) G1, M1 = difference_matrix(g1, k, lmbda1) G2, M2 = difference_matrix(g2, k, lmbda2) G, M = difference_matrix_product(k, M1, G1, lmbda1, M2, G2, lmbda2, check=False) else: if existence: return Unknown raise NotImplementedError( "I don't know how to build a ({},{},{})-Difference Matrix!".format( g, k, lmbda)) if check and not is_difference_matrix(M, G, k, lmbda, 1): raise RuntimeError( "Sage built something which is not a ({},{},{})-DM!".format( g, k, lmbda)) return G, M