passagemath-graphs 10.6.1rc1__cp310-cp310-musllinux_1_2_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. passagemath_graphs-10.6.1rc1.dist-info/METADATA +292 -0
  2. passagemath_graphs-10.6.1rc1.dist-info/RECORD +260 -0
  3. passagemath_graphs-10.6.1rc1.dist-info/WHEEL +5 -0
  4. passagemath_graphs-10.6.1rc1.dist-info/top_level.txt +2 -0
  5. passagemath_graphs.libs/libgcc_s-69c45f16.so.1 +0 -0
  6. passagemath_graphs.libs/libgmp-8e78bd9b.so.10.5.0 +0 -0
  7. passagemath_graphs.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
  8. sage/all__sagemath_graphs.py +39 -0
  9. sage/combinat/abstract_tree.py +2723 -0
  10. sage/combinat/all__sagemath_graphs.py +34 -0
  11. sage/combinat/binary_tree.py +5306 -0
  12. sage/combinat/cluster_algebra_quiver/all.py +22 -0
  13. sage/combinat/cluster_algebra_quiver/cluster_seed.py +5208 -0
  14. sage/combinat/cluster_algebra_quiver/interact.py +124 -0
  15. sage/combinat/cluster_algebra_quiver/mutation_class.py +625 -0
  16. sage/combinat/cluster_algebra_quiver/mutation_type.py +1555 -0
  17. sage/combinat/cluster_algebra_quiver/quiver.py +2290 -0
  18. sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +2468 -0
  19. sage/combinat/designs/MOLS_handbook_data.py +570 -0
  20. sage/combinat/designs/all.py +58 -0
  21. sage/combinat/designs/bibd.py +1655 -0
  22. sage/combinat/designs/block_design.py +1071 -0
  23. sage/combinat/designs/covering_array.py +269 -0
  24. sage/combinat/designs/covering_design.py +530 -0
  25. sage/combinat/designs/database.py +5615 -0
  26. sage/combinat/designs/design_catalog.py +122 -0
  27. sage/combinat/designs/designs_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  28. sage/combinat/designs/designs_pyx.pxd +21 -0
  29. sage/combinat/designs/designs_pyx.pyx +993 -0
  30. sage/combinat/designs/difference_family.py +3951 -0
  31. sage/combinat/designs/difference_matrices.py +279 -0
  32. sage/combinat/designs/evenly_distributed_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  33. sage/combinat/designs/evenly_distributed_sets.pyx +661 -0
  34. sage/combinat/designs/ext_rep.py +1064 -0
  35. sage/combinat/designs/gen_quadrangles_with_spread.cpython-310-aarch64-linux-gnu.so +0 -0
  36. sage/combinat/designs/gen_quadrangles_with_spread.pyx +339 -0
  37. sage/combinat/designs/group_divisible_designs.py +361 -0
  38. sage/combinat/designs/incidence_structures.py +2357 -0
  39. sage/combinat/designs/latin_squares.py +581 -0
  40. sage/combinat/designs/orthogonal_arrays.py +2244 -0
  41. sage/combinat/designs/orthogonal_arrays_build_recursive.py +1780 -0
  42. sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-310-aarch64-linux-gnu.so +0 -0
  43. sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +967 -0
  44. sage/combinat/designs/resolvable_bibd.py +815 -0
  45. sage/combinat/designs/steiner_quadruple_systems.py +1306 -0
  46. sage/combinat/designs/subhypergraph_search.cpython-310-aarch64-linux-gnu.so +0 -0
  47. sage/combinat/designs/subhypergraph_search.pyx +530 -0
  48. sage/combinat/designs/twographs.py +306 -0
  49. sage/combinat/finite_state_machine.py +14874 -0
  50. sage/combinat/finite_state_machine_generators.py +2006 -0
  51. sage/combinat/graph_path.py +448 -0
  52. sage/combinat/interval_posets.py +3908 -0
  53. sage/combinat/nu_tamari_lattice.py +269 -0
  54. sage/combinat/ordered_tree.py +1446 -0
  55. sage/combinat/posets/all.py +46 -0
  56. sage/combinat/posets/bubble_shuffle.py +247 -0
  57. sage/combinat/posets/cartesian_product.py +493 -0
  58. sage/combinat/posets/d_complete.py +182 -0
  59. sage/combinat/posets/elements.py +273 -0
  60. sage/combinat/posets/forest.py +30 -0
  61. sage/combinat/posets/hasse_cython.cpython-310-aarch64-linux-gnu.so +0 -0
  62. sage/combinat/posets/hasse_cython.pyx +174 -0
  63. sage/combinat/posets/hasse_diagram.py +3672 -0
  64. sage/combinat/posets/hochschild_lattice.py +158 -0
  65. sage/combinat/posets/incidence_algebras.py +794 -0
  66. sage/combinat/posets/lattices.py +5117 -0
  67. sage/combinat/posets/linear_extension_iterator.cpython-310-aarch64-linux-gnu.so +0 -0
  68. sage/combinat/posets/linear_extension_iterator.pyx +292 -0
  69. sage/combinat/posets/linear_extensions.py +1037 -0
  70. sage/combinat/posets/mobile.py +275 -0
  71. sage/combinat/posets/moebius_algebra.py +776 -0
  72. sage/combinat/posets/poset_examples.py +2178 -0
  73. sage/combinat/posets/posets.py +9360 -0
  74. sage/combinat/rooted_tree.py +1070 -0
  75. sage/combinat/shard_order.py +239 -0
  76. sage/combinat/tamari_lattices.py +384 -0
  77. sage/combinat/yang_baxter_graph.py +923 -0
  78. sage/databases/all__sagemath_graphs.py +1 -0
  79. sage/databases/knotinfo_db.py +1231 -0
  80. sage/ext_data/all__sagemath_graphs.py +1 -0
  81. sage/ext_data/graphs/graph_plot_js.html +330 -0
  82. sage/ext_data/kenzo/CP2.txt +45 -0
  83. sage/ext_data/kenzo/CP3.txt +349 -0
  84. sage/ext_data/kenzo/CP4.txt +4774 -0
  85. sage/ext_data/kenzo/README.txt +49 -0
  86. sage/ext_data/kenzo/S4.txt +20 -0
  87. sage/graphs/all.py +42 -0
  88. sage/graphs/asteroidal_triples.cpython-310-aarch64-linux-gnu.so +0 -0
  89. sage/graphs/asteroidal_triples.pyx +320 -0
  90. sage/graphs/base/all.py +1 -0
  91. sage/graphs/base/boost_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  92. sage/graphs/base/boost_graph.pxd +106 -0
  93. sage/graphs/base/boost_graph.pyx +3045 -0
  94. sage/graphs/base/c_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  95. sage/graphs/base/c_graph.pxd +106 -0
  96. sage/graphs/base/c_graph.pyx +5096 -0
  97. sage/graphs/base/dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  98. sage/graphs/base/dense_graph.pxd +28 -0
  99. sage/graphs/base/dense_graph.pyx +801 -0
  100. sage/graphs/base/graph_backends.cpython-310-aarch64-linux-gnu.so +0 -0
  101. sage/graphs/base/graph_backends.pxd +5 -0
  102. sage/graphs/base/graph_backends.pyx +797 -0
  103. sage/graphs/base/overview.py +85 -0
  104. sage/graphs/base/sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  105. sage/graphs/base/sparse_graph.pxd +90 -0
  106. sage/graphs/base/sparse_graph.pyx +1653 -0
  107. sage/graphs/base/static_dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  108. sage/graphs/base/static_dense_graph.pxd +5 -0
  109. sage/graphs/base/static_dense_graph.pyx +1032 -0
  110. sage/graphs/base/static_sparse_backend.cpython-310-aarch64-linux-gnu.so +0 -0
  111. sage/graphs/base/static_sparse_backend.pxd +27 -0
  112. sage/graphs/base/static_sparse_backend.pyx +1583 -0
  113. sage/graphs/base/static_sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  114. sage/graphs/base/static_sparse_graph.pxd +37 -0
  115. sage/graphs/base/static_sparse_graph.pyx +1375 -0
  116. sage/graphs/bipartite_graph.py +2732 -0
  117. sage/graphs/centrality.cpython-310-aarch64-linux-gnu.so +0 -0
  118. sage/graphs/centrality.pyx +1038 -0
  119. sage/graphs/cographs.py +519 -0
  120. sage/graphs/comparability.cpython-310-aarch64-linux-gnu.so +0 -0
  121. sage/graphs/comparability.pyx +851 -0
  122. sage/graphs/connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  123. sage/graphs/connectivity.pxd +157 -0
  124. sage/graphs/connectivity.pyx +4813 -0
  125. sage/graphs/convexity_properties.cpython-310-aarch64-linux-gnu.so +0 -0
  126. sage/graphs/convexity_properties.pxd +16 -0
  127. sage/graphs/convexity_properties.pyx +870 -0
  128. sage/graphs/digraph.py +4754 -0
  129. sage/graphs/digraph_generators.py +1993 -0
  130. sage/graphs/distances_all_pairs.cpython-310-aarch64-linux-gnu.so +0 -0
  131. sage/graphs/distances_all_pairs.pxd +12 -0
  132. sage/graphs/distances_all_pairs.pyx +2938 -0
  133. sage/graphs/domination.py +1363 -0
  134. sage/graphs/dot2tex_utils.py +100 -0
  135. sage/graphs/edge_connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  136. sage/graphs/edge_connectivity.pyx +1215 -0
  137. sage/graphs/generators/all.py +1 -0
  138. sage/graphs/generators/basic.py +1769 -0
  139. sage/graphs/generators/chessboard.py +538 -0
  140. sage/graphs/generators/classical_geometries.py +1611 -0
  141. sage/graphs/generators/degree_sequence.py +235 -0
  142. sage/graphs/generators/distance_regular.cpython-310-aarch64-linux-gnu.so +0 -0
  143. sage/graphs/generators/distance_regular.pyx +2846 -0
  144. sage/graphs/generators/families.py +4759 -0
  145. sage/graphs/generators/intersection.py +565 -0
  146. sage/graphs/generators/platonic_solids.py +262 -0
  147. sage/graphs/generators/random.py +2623 -0
  148. sage/graphs/generators/smallgraphs.py +5741 -0
  149. sage/graphs/generators/world_map.py +724 -0
  150. sage/graphs/generic_graph.py +26867 -0
  151. sage/graphs/generic_graph_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  152. sage/graphs/generic_graph_pyx.pxd +34 -0
  153. sage/graphs/generic_graph_pyx.pyx +1673 -0
  154. sage/graphs/genus.cpython-310-aarch64-linux-gnu.so +0 -0
  155. sage/graphs/genus.pyx +622 -0
  156. sage/graphs/graph.py +9645 -0
  157. sage/graphs/graph_coloring.cpython-310-aarch64-linux-gnu.so +0 -0
  158. sage/graphs/graph_coloring.pyx +2284 -0
  159. sage/graphs/graph_database.py +1177 -0
  160. sage/graphs/graph_decompositions/all.py +1 -0
  161. sage/graphs/graph_decompositions/bandwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  162. sage/graphs/graph_decompositions/bandwidth.pyx +428 -0
  163. sage/graphs/graph_decompositions/clique_separators.cpython-310-aarch64-linux-gnu.so +0 -0
  164. sage/graphs/graph_decompositions/clique_separators.pyx +616 -0
  165. sage/graphs/graph_decompositions/cutwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  166. sage/graphs/graph_decompositions/cutwidth.pyx +753 -0
  167. sage/graphs/graph_decompositions/fast_digraph.cpython-310-aarch64-linux-gnu.so +0 -0
  168. sage/graphs/graph_decompositions/fast_digraph.pxd +13 -0
  169. sage/graphs/graph_decompositions/fast_digraph.pyx +212 -0
  170. sage/graphs/graph_decompositions/graph_products.cpython-310-aarch64-linux-gnu.so +0 -0
  171. sage/graphs/graph_decompositions/graph_products.pyx +508 -0
  172. sage/graphs/graph_decompositions/modular_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  173. sage/graphs/graph_decompositions/modular_decomposition.pxd +27 -0
  174. sage/graphs/graph_decompositions/modular_decomposition.pyx +1536 -0
  175. sage/graphs/graph_decompositions/slice_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  176. sage/graphs/graph_decompositions/slice_decomposition.pxd +18 -0
  177. sage/graphs/graph_decompositions/slice_decomposition.pyx +1106 -0
  178. sage/graphs/graph_decompositions/tree_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  179. sage/graphs/graph_decompositions/tree_decomposition.pxd +17 -0
  180. sage/graphs/graph_decompositions/tree_decomposition.pyx +1996 -0
  181. sage/graphs/graph_decompositions/vertex_separation.cpython-310-aarch64-linux-gnu.so +0 -0
  182. sage/graphs/graph_decompositions/vertex_separation.pxd +5 -0
  183. sage/graphs/graph_decompositions/vertex_separation.pyx +1963 -0
  184. sage/graphs/graph_editor.py +82 -0
  185. sage/graphs/graph_generators.py +3314 -0
  186. sage/graphs/graph_generators_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  187. sage/graphs/graph_generators_pyx.pyx +95 -0
  188. sage/graphs/graph_input.py +812 -0
  189. sage/graphs/graph_latex.py +2064 -0
  190. sage/graphs/graph_list.py +410 -0
  191. sage/graphs/graph_plot.py +1756 -0
  192. sage/graphs/graph_plot_js.py +338 -0
  193. sage/graphs/hyperbolicity.cpython-310-aarch64-linux-gnu.so +0 -0
  194. sage/graphs/hyperbolicity.pyx +1704 -0
  195. sage/graphs/hypergraph_generators.py +364 -0
  196. sage/graphs/independent_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  197. sage/graphs/independent_sets.pxd +13 -0
  198. sage/graphs/independent_sets.pyx +402 -0
  199. sage/graphs/isgci.py +1033 -0
  200. sage/graphs/isoperimetric_inequalities.cpython-310-aarch64-linux-gnu.so +0 -0
  201. sage/graphs/isoperimetric_inequalities.pyx +489 -0
  202. sage/graphs/line_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  203. sage/graphs/line_graph.pyx +743 -0
  204. sage/graphs/lovasz_theta.py +77 -0
  205. sage/graphs/matching.py +1633 -0
  206. sage/graphs/matching_covered_graph.py +3590 -0
  207. sage/graphs/orientations.py +1489 -0
  208. sage/graphs/partial_cube.py +459 -0
  209. sage/graphs/path_enumeration.cpython-310-aarch64-linux-gnu.so +0 -0
  210. sage/graphs/path_enumeration.pyx +2040 -0
  211. sage/graphs/pq_trees.py +1129 -0
  212. sage/graphs/print_graphs.py +201 -0
  213. sage/graphs/schnyder.py +865 -0
  214. sage/graphs/spanning_tree.cpython-310-aarch64-linux-gnu.so +0 -0
  215. sage/graphs/spanning_tree.pyx +1457 -0
  216. sage/graphs/strongly_regular_db.cpython-310-aarch64-linux-gnu.so +0 -0
  217. sage/graphs/strongly_regular_db.pyx +3340 -0
  218. sage/graphs/traversals.cpython-310-aarch64-linux-gnu.so +0 -0
  219. sage/graphs/traversals.pxd +9 -0
  220. sage/graphs/traversals.pyx +1872 -0
  221. sage/graphs/trees.cpython-310-aarch64-linux-gnu.so +0 -0
  222. sage/graphs/trees.pxd +15 -0
  223. sage/graphs/trees.pyx +310 -0
  224. sage/graphs/tutte_polynomial.py +713 -0
  225. sage/graphs/views.cpython-310-aarch64-linux-gnu.so +0 -0
  226. sage/graphs/views.pyx +794 -0
  227. sage/graphs/weakly_chordal.cpython-310-aarch64-linux-gnu.so +0 -0
  228. sage/graphs/weakly_chordal.pyx +604 -0
  229. sage/groups/all__sagemath_graphs.py +1 -0
  230. sage/groups/perm_gps/all__sagemath_graphs.py +1 -0
  231. sage/groups/perm_gps/partn_ref/all__sagemath_graphs.py +1 -0
  232. sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-310-aarch64-linux-gnu.so +0 -0
  233. sage/groups/perm_gps/partn_ref/refinement_graphs.pxd +38 -0
  234. sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +1666 -0
  235. sage/knots/all.py +6 -0
  236. sage/knots/free_knotinfo_monoid.py +507 -0
  237. sage/knots/gauss_code.py +291 -0
  238. sage/knots/knot.py +682 -0
  239. sage/knots/knot_table.py +284 -0
  240. sage/knots/knotinfo.py +2900 -0
  241. sage/knots/link.py +4715 -0
  242. sage/sandpiles/all.py +13 -0
  243. sage/sandpiles/examples.py +225 -0
  244. sage/sandpiles/sandpile.py +6365 -0
  245. sage/topology/all.py +22 -0
  246. sage/topology/cell_complex.py +1214 -0
  247. sage/topology/cubical_complex.py +1976 -0
  248. sage/topology/delta_complex.py +1806 -0
  249. sage/topology/filtered_simplicial_complex.py +744 -0
  250. sage/topology/moment_angle_complex.py +823 -0
  251. sage/topology/simplicial_complex.py +5160 -0
  252. sage/topology/simplicial_complex_catalog.py +92 -0
  253. sage/topology/simplicial_complex_examples.py +1680 -0
  254. sage/topology/simplicial_complex_homset.py +205 -0
  255. sage/topology/simplicial_complex_morphism.py +836 -0
  256. sage/topology/simplicial_set.py +4102 -0
  257. sage/topology/simplicial_set_catalog.py +55 -0
  258. sage/topology/simplicial_set_constructions.py +2954 -0
  259. sage/topology/simplicial_set_examples.py +865 -0
  260. sage/topology/simplicial_set_morphism.py +1464 -0
@@ -0,0 +1,3951 @@
1
+ # sage_setup: distribution = sagemath-graphs
2
+ r"""
3
+ Difference families
4
+
5
+ This module gathers everything related to difference families. One can build a
6
+ difference family (or check that it can be built) with :func:`difference_family`::
7
+
8
+ sage: G,F = designs.difference_family(13,4,1) # needs sage.libs.pari sage.modules
9
+
10
+ It defines the following functions:
11
+
12
+ {INDEX_OF_FUNCTIONS}
13
+
14
+ REFERENCES:
15
+
16
+ .. [BJL99-1] \T. Beth, D. Jungnickel, H. Lenz "Design theory Vol. I."
17
+ Second edition. Encyclopedia of Mathematics and its Applications, 69. Cambridge
18
+ University Press, (1999).
19
+
20
+ .. [BLJ99-2] \T. Beth, D. Jungnickel, H. Lenz "Design theory Vol. II."
21
+ Second edition. Encyclopedia of Mathematics and its Applications, 78. Cambridge
22
+ University Press, (1999).
23
+
24
+ .. [Bo39] \R. C. Bose, "On the construction of balanced incomplete block
25
+ designs", Ann. Eugenics, 9 (1939), 353--399.
26
+
27
+ .. [Bu95] \M. Buratti "On simple radical difference families", J.
28
+ Combinatorial Designs, 3 (1995) 161--168.
29
+
30
+ .. [Tu1965] \R. J. Turyn "Character sum and difference sets"
31
+ Pacific J. Math. 15 (1965) 319--346.
32
+
33
+ .. [Tu1984] \R. J. Turyn "A special class of Williamson matrices and
34
+ difference sets" J. Combinatorial Theory (A) 36 (1984) 111--115.
35
+
36
+ .. [Wi72] \R. M. Wilson "Cyclotomy and difference families in elementary Abelian
37
+ groups", J. Number Theory, 4 (1972) 17--47.
38
+
39
+ Functions
40
+ ---------
41
+ """
42
+ # ****************************************************************************
43
+ # Copyright (C) 2014 Vincent Delecroix <20100.delecroix@gmail.com>
44
+ #
45
+ # Distributed under the terms of the GNU General Public License (GPL)
46
+ # as published by the Free Software Foundation; either version 2 of
47
+ # the License, or (at your option) any later version.
48
+ # https://www.gnu.org/licenses/
49
+ # ****************************************************************************
50
+
51
+ from sage.arith.misc import is_prime_power
52
+ from sage.misc.cachefunc import cached_function
53
+
54
+ from sage.categories.sets_cat import EmptySetError
55
+ import sage.arith.all as arith
56
+ from sage.misc.unknown import Unknown
57
+ from sage.rings.finite_rings.integer_mod_ring import Zmod
58
+ from sage.rings.integer import Integer
59
+ from sage.rings.integer_ring import ZZ
60
+ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
61
+
62
+
63
+ def group_law(G):
64
+ r"""
65
+ Return a triple ``(identity, operation, inverse)`` that define the
66
+ operations on the group ``G``.
67
+
68
+ EXAMPLES::
69
+
70
+ sage: from sage.combinat.designs.difference_family import group_law
71
+ sage: group_law(Zmod(3))
72
+ (0, <built-in function add>, <built-in function neg>)
73
+ sage: group_law(SymmetricGroup(5)) # needs sage.groups
74
+ ((), <built-in function mul>, <built-in function inv>)
75
+ sage: group_law(VectorSpace(QQ, 3)) # needs sage.modules
76
+ ((0, 0, 0), <built-in function add>, <built-in function neg>)
77
+ """
78
+ import operator
79
+ from sage.categories.groups import Groups
80
+ from sage.categories.additive_groups import AdditiveGroups
81
+
82
+ if G in Groups(): # multiplicative groups
83
+ return (G.one(), operator.mul, operator.inv)
84
+ elif G in AdditiveGroups(): # additive groups
85
+ return (G.zero(), operator.add, operator.neg)
86
+ else:
87
+ raise ValueError("%s does not seem to be a group" % G)
88
+
89
+
90
+ def block_stabilizer(G, B):
91
+ r"""
92
+ Compute the left stabilizer of the block ``B`` under the action of ``G``.
93
+
94
+ This function return the list of all `x\in G` such that `x\cdot B=B` (as a
95
+ set).
96
+
97
+ INPUT:
98
+
99
+ - ``G`` -- a group (additive or multiplicative)
100
+ - ``B`` -- a subset of ``G``
101
+
102
+ EXAMPLES::
103
+
104
+ sage: from sage.combinat.designs.difference_family import block_stabilizer
105
+
106
+ sage: Z8 = Zmod(8)
107
+ sage: block_stabilizer(Z8, [Z8(0),Z8(2),Z8(4),Z8(6)])
108
+ [0, 2, 4, 6]
109
+ sage: block_stabilizer(Z8, [Z8(0),Z8(2)])
110
+ [0]
111
+
112
+ sage: C = cartesian_product([Zmod(4),Zmod(3)])
113
+ sage: block_stabilizer(C, [C((0,0)),C((2,0)),C((0,1)),C((2,1))])
114
+ [(0, 0), (2, 0)]
115
+
116
+ sage: b = list(map(Zmod(45),[1, 3, 7, 10, 22, 25, 30, 35, 37, 38, 44]))
117
+ sage: block_stabilizer(Zmod(45),b)
118
+ [0]
119
+ """
120
+ if not B:
121
+ return list(G)
122
+ identity, op, inv = group_law(G)
123
+ b0 = inv(B[0])
124
+ S = []
125
+ for b in B:
126
+ # fun: if we replace +(-b) with -b it completely fails!!
127
+ bb0 = op(b, b0) # bb0 = b-B[0]
128
+ if all(op(bb0, c) in B for c in B):
129
+ S.append(bb0)
130
+ return S
131
+
132
+
133
+ def is_difference_family(G, D, v=None, k=None, l=None, verbose=False):
134
+ r"""
135
+ Check whether ``D`` forms a difference family in the group ``G``.
136
+
137
+ INPUT:
138
+
139
+ - ``G`` -- group of cardinality ``v``
140
+ - ``D`` -- set of ``k``-subsets of ``G``
141
+ - ``v``, ``k``, ``l`` -- (optional) parameters of the difference family
142
+ - ``verbose`` -- boolean (default: ``False``); whether to print additional
143
+ information
144
+
145
+ .. SEEALSO::
146
+
147
+ :func:`difference_family`
148
+
149
+ EXAMPLES::
150
+
151
+ sage: from sage.combinat.designs.difference_family import is_difference_family
152
+ sage: G = Zmod(21)
153
+ sage: D = [[0,1,4,14,16]]
154
+ sage: is_difference_family(G, D, 21, 5)
155
+ True
156
+
157
+ sage: G = Zmod(41)
158
+ sage: D = [[0,1,4,11,29],[0,2,8,17,21]]
159
+ sage: is_difference_family(G, D, verbose=True)
160
+ Too few:
161
+ 5 is obtained 0 times in blocks []
162
+ 14 is obtained 0 times in blocks []
163
+ 27 is obtained 0 times in blocks []
164
+ 36 is obtained 0 times in blocks []
165
+ Too much:
166
+ 4 is obtained 2 times in blocks [0, 1]
167
+ 13 is obtained 2 times in blocks [0, 1]
168
+ 28 is obtained 2 times in blocks [0, 1]
169
+ 37 is obtained 2 times in blocks [0, 1]
170
+ False
171
+ sage: D = [[0,1,4,11,29],[0,2,8,17,22]]
172
+ sage: is_difference_family(G, D)
173
+ True
174
+
175
+ sage: G = Zmod(61)
176
+ sage: D = [[0,1,3,13,34],[0,4,9,23,45],[0,6,17,24,32]]
177
+ sage: is_difference_family(G, D)
178
+ True
179
+
180
+ sage: # needs sage.modules
181
+ sage: G = AdditiveAbelianGroup([3]*4)
182
+ sage: a,b,c,d = G.gens()
183
+ sage: D = [[d, -a+d, -c+d, a-b-d, b+c+d],
184
+ ....: [c, a+b-d, -b+c, a-b+d, a+b+c],
185
+ ....: [-a-b+c+d, a-b-c-d, -a+c-d, b-c+d, a+b],
186
+ ....: [-b-d, a+b+d, a-b+c-d, a-b+c, -b+c+d]]
187
+ sage: is_difference_family(G, D)
188
+ True
189
+
190
+ The following example has a third block with a non-trivial stabilizer::
191
+
192
+ sage: G = Zmod(15)
193
+ sage: D = [[0,1,4],[0,2,9],[0,5,10]]
194
+ sage: is_difference_family(G,D,verbose=True)
195
+ It is a (15,3,1)-difference family
196
+ True
197
+
198
+ The function also supports multiplicative groups (non necessarily Abelian)::
199
+
200
+ sage: # needs sage.groups
201
+ sage: G = DihedralGroup(8)
202
+ sage: x,y = G.gens()
203
+ sage: i = G.one()
204
+ sage: D1 = [[i,x,x^4], [i,x^2, y*x], [i,x^5,y], [i,x^6,y*x^2], [i,x^7,y*x^5]]
205
+ sage: is_difference_family(G, D1, 16, 3, 2)
206
+ True
207
+ sage: from sage.combinat.designs.bibd import BIBD_from_difference_family
208
+ sage: bibd = BIBD_from_difference_family(G, D1, lambd=2)
209
+
210
+ TESTS::
211
+
212
+ sage: # needs sage.rings.finite_rings
213
+ sage: K = GF(3^2,'z')
214
+ sage: z = K.gen()
215
+ sage: D = [[1,z+1,2]]
216
+ sage: _ = is_difference_family(K, D, verbose=True)
217
+ the number of differences (=6) must be a multiple of v-1=8
218
+ sage: _
219
+ False
220
+ """
221
+ identity, mul, inv = group_law(G)
222
+
223
+ Glist = list(G)
224
+
225
+ D = [[G(_) for _ in d] for d in D]
226
+
227
+ # Check v (and define it if needed)
228
+ if v is None:
229
+ v = len(Glist)
230
+ else:
231
+ if len(Glist) != v:
232
+ if verbose:
233
+ print("G must have cardinality v (=%d)" % int(v))
234
+ return False
235
+
236
+ # Check k (and define it if needed)
237
+ if k is None:
238
+ k = len(D[0])
239
+ else:
240
+ k = int(k)
241
+
242
+ for d in D:
243
+ if len(d) != k:
244
+ if verbose:
245
+ print("the block {} does not have length {}".format(d, k))
246
+ return False
247
+
248
+ # Check l (and define it if needed)
249
+ #
250
+ # - nb_diff: the number of pairs (with multiplicity) covered by the BIBD
251
+ # generated by the DF.
252
+ #
253
+ # - stab: the stabilizer of each set.
254
+ nb_diff = 0
255
+ stab = []
256
+ for d in D:
257
+ s = block_stabilizer(G,d)
258
+ stab.append(s)
259
+ nb_diff += k*(k-1) // len(s)
260
+ if l is None:
261
+ if nb_diff % (v-1) != 0:
262
+ if verbose:
263
+ print("the number of differences (={}) must be a multiple of v-1={}".format(nb_diff, v-1))
264
+ return False
265
+ l = nb_diff // (v-1)
266
+ else:
267
+ if nb_diff != l*(v-1):
268
+ if verbose:
269
+ print("the number of differences (={}) is not equal to l*(v-1) = {}".format(nb_diff, l*(v-1)))
270
+ return False
271
+
272
+ # Check that every x \in G-{0},occurs exactly l times as a difference
273
+ counter = {g: 0 for g in Glist}
274
+ where = {g: set() for g in Glist}
275
+ del counter[identity]
276
+
277
+ for i,d in enumerate(D):
278
+ tmp_counter = {}
279
+ for b in d:
280
+ for c in d:
281
+ if b == c:
282
+ continue
283
+ gg = mul(b, inv(c)) # = b-c or bc^{-1}
284
+ if gg not in tmp_counter:
285
+ tmp_counter[gg] = 0
286
+ where[gg].add(i)
287
+ tmp_counter[gg] += 1
288
+
289
+ if sum(tmp_counter.values()) != k * (k - 1):
290
+ if verbose:
291
+ print("repeated element in the {}-th block {}".format(i, d))
292
+ return False
293
+
294
+ # Normalized number of occurrences added to counter
295
+ stabi = len(stab[i])
296
+ for gg, tmp_gg in tmp_counter.items():
297
+ counter[gg] += tmp_gg // stabi
298
+
299
+ # Check the counter and report any error
300
+ too_few = []
301
+ too_much = []
302
+ for g in Glist:
303
+ if g == identity:
304
+ continue
305
+ if counter[g] < l:
306
+ if verbose:
307
+ too_few.append(g)
308
+ else:
309
+ return False
310
+ if counter[g] > l:
311
+ if verbose:
312
+ too_much.append(g)
313
+ else:
314
+ return False
315
+
316
+ if too_few:
317
+ print("Too few:")
318
+ for g in too_few:
319
+ print(" {} is obtained {} times in blocks {}".format(
320
+ g, counter[g], sorted(where[g])))
321
+ if too_much:
322
+ print("Too much:")
323
+ for g in too_much:
324
+ print(" {} is obtained {} times in blocks {}".format(
325
+ g, counter[g], sorted(where[g])))
326
+ if too_few or too_much:
327
+ return False
328
+
329
+ if verbose:
330
+ print("It is a ({},{},{})-difference family".format(v, k, l))
331
+ return True
332
+
333
+
334
+ def singer_difference_set(q, d):
335
+ r"""
336
+ Return a difference set associated to the set of hyperplanes in a projective
337
+ space of dimension `d` over `GF(q)`.
338
+
339
+ A Singer difference set has parameters:
340
+
341
+ .. MATH::
342
+
343
+ v = \frac{q^{d+1}-1}{q-1}, \quad
344
+ k = \frac{q^d-1}{q-1}, \quad
345
+ \lambda = \frac{q^{d-1}-1}{q-1}.
346
+
347
+ The idea of the construction is as follows. One consider the finite field
348
+ `GF(q^{d+1})` as a vector space of dimension `d+1` over `GF(q)`. The set of
349
+ `GF(q)`-lines in `GF(q^{d+1})` is a projective plane and its set of
350
+ hyperplanes form a balanced incomplete block design.
351
+
352
+ Now, considering a multiplicative generator `z` of `GF(q^{d+1})`, we get a
353
+ transitive action of a cyclic group on our projective plane from which it is
354
+ possible to build a difference set.
355
+
356
+ The construction is given in details in [Stinson2004]_, section 3.3.
357
+
358
+ EXAMPLES::
359
+
360
+ sage: from sage.combinat.designs.difference_family import singer_difference_set, is_difference_family
361
+ sage: G,D = singer_difference_set(3,2) # needs sage.rings.finite_rings
362
+ sage: is_difference_family(G, D, verbose=True) # needs sage.rings.finite_rings
363
+ It is a (13,4,1)-difference family
364
+ True
365
+
366
+ sage: G,D = singer_difference_set(4,2) # needs sage.rings.finite_rings
367
+ sage: is_difference_family(G, D, verbose=True) # needs sage.rings.finite_rings
368
+ It is a (21,5,1)-difference family
369
+ True
370
+
371
+ sage: G,D = singer_difference_set(3,3) # needs sage.rings.finite_rings
372
+ sage: is_difference_family(G, D, verbose=True) # needs sage.rings.finite_rings
373
+ It is a (40,13,4)-difference family
374
+ True
375
+
376
+ sage: G,D = singer_difference_set(9,3) # needs sage.rings.finite_rings
377
+ sage: is_difference_family(G, D, verbose=True) # needs sage.rings.finite_rings
378
+ It is a (820,91,10)-difference family
379
+ True
380
+ """
381
+ q = Integer(q)
382
+ assert q.is_prime_power()
383
+ assert d >= 2
384
+
385
+ from sage.rings.finite_rings.finite_field_constructor import GF
386
+ from sage.rings.finite_rings.conway_polynomials import conway_polynomial
387
+ from sage.rings.finite_rings.integer_mod_ring import Zmod
388
+
389
+ # build a polynomial c over GF(q) such that GF(q)[x] / (c(x)) is a
390
+ # GF(q**(d+1)) and such that x is a multiplicative generator.
391
+ p,e = q.factor()[0]
392
+ c = conway_polynomial(p, e*(d+1))
393
+ if e != 1:
394
+ # i.e. q is not a prime, so we factorize c over GF(q) and pick
395
+ # one of its factor
396
+ K = GF(q,'z')
397
+ c = c.change_ring(K).factor()[0][0]
398
+ else:
399
+ K = GF(q)
400
+ z = c.parent().gen()
401
+
402
+ # Now we consider the GF(q)-subspace V spanned by (1,z,z^2,...,z^(d-1)) inside
403
+ # GF(q^(d+1)). The multiplication by z is an automorphism of the
404
+ # GF(q)-projective space built from GF(q^(d+1)). The difference family is
405
+ # obtained by taking the integers i such that z^i belong to V.
406
+ powers = [0]
407
+ i = 1
408
+ x = z
409
+ k = (q**d-1)//(q-1)
410
+ while len(powers) < k:
411
+ if x.degree() <= (d-1):
412
+ powers.append(i)
413
+ x = (x*z).mod(c)
414
+ i += 1
415
+
416
+ return Zmod((q**(d+1)-1)//(q-1)), [powers]
417
+
418
+
419
+ def df_q_6_1(K, existence=False, check=True):
420
+ r"""
421
+ Return a `(q,6,1)`-difference family over the finite field `K`.
422
+
423
+ The construction uses Theorem 11 of [Wi72]_.
424
+
425
+ EXAMPLES::
426
+
427
+ sage: # needs sage.rings.finite_rings
428
+ sage: from sage.combinat.designs.difference_family import is_difference_family, df_q_6_1
429
+ sage: prime_powers = [v for v in range(31,500,30) if is_prime_power(v)]
430
+ sage: parameters = [v for v in prime_powers
431
+ ....: if df_q_6_1(GF(v,'a'), existence=True) is True]
432
+ sage: parameters
433
+ [31, 151, 181, 211, 241, 271, 331, 361, 421]
434
+ sage: for v in parameters:
435
+ ....: K = GF(v, 'a')
436
+ ....: df = df_q_6_1(K, check=True)
437
+ ....: assert is_difference_family(K, df, v, 6, 1)
438
+
439
+ .. TODO::
440
+
441
+ Do improvements due to Zhen and Wu 1999.
442
+ """
443
+ v = K.cardinality()
444
+ x = K.multiplicative_generator()
445
+ one = K.one()
446
+ if v % 30 != 1:
447
+ if existence:
448
+ return False
449
+ raise EmptySetError("k(k-1)=30 should divide (v-1)")
450
+ t = (v-1) // 30 # number of blocks
451
+
452
+ r = x**((v-1)//3) # primitive cube root of unity
453
+ r2 = r*r # the other primitive cube root
454
+
455
+ # we now compute the cosets of x**i
456
+ xx = x**5
457
+ to_coset = {x**i * xx**j: i for i in range(5) for j in range((v-1)/5)}
458
+
459
+ for c in to_coset: # the loop runs through all nonzero elements of K
460
+ if c == one or c == r or c == r2:
461
+ continue
462
+ if len(set(to_coset[elt] for elt in (r-one, c*(r-one), c-one, c-r, c-r**2))) == 5:
463
+ if existence:
464
+ return True
465
+ B = [one,r,r2,c,c*r,c*r2]
466
+ D = [[xx**i * b for b in B] for i in range(t)]
467
+ break
468
+ else:
469
+ if existence:
470
+ return Unknown
471
+ raise NotImplementedError("Wilson construction failed for v={}".format(v))
472
+
473
+ if check and not is_difference_family(K, D, v, 6, 1):
474
+ raise RuntimeError("Wilson 1972 construction failed! Please e-mail sage-devel@googlegroups.com")
475
+
476
+ return D
477
+
478
+
479
+ def radical_difference_set(K, k, l=1, existence=False, check=True):
480
+ r"""
481
+ Return a difference set made of a cyclotomic coset in the finite field
482
+ ``K`` and with parameters ``k`` and ``l``.
483
+
484
+ Most of these difference sets appear in chapter VI.18.48 of the Handbook of
485
+ combinatorial designs.
486
+
487
+ EXAMPLES::
488
+
489
+ sage: from sage.combinat.designs.difference_family import radical_difference_set
490
+
491
+ sage: D = radical_difference_set(GF(7), 3, 1); D # needs sage.rings.finite_rings
492
+ [[1, 2, 4]]
493
+ sage: sorted(x-y for x in D[0] for y in D[0] if x != y) # needs sage.rings.finite_rings
494
+ [1, 2, 3, 4, 5, 6]
495
+
496
+ sage: D = radical_difference_set(GF(16,'a'), 6, 2) # needs sage.rings.finite_rings
497
+ sage: sorted(x-y for x in D[0] for y in D[0] if x != y) # needs sage.rings.finite_rings
498
+ [1,
499
+ 1,
500
+ a,
501
+ a,
502
+ a + 1,
503
+ a + 1,
504
+ a^2,
505
+ a^2,
506
+ ...
507
+ a^3 + a^2 + a + 1,
508
+ a^3 + a^2 + a + 1]
509
+
510
+ sage: for k in range(2,50): # needs sage.rings.finite_rings
511
+ ....: for l in reversed(divisors(k*(k-1))):
512
+ ....: v = k*(k-1)//l + 1
513
+ ....: if is_prime_power(v) and radical_difference_set(GF(v,'a'),k,l,existence=True) is True:
514
+ ....: _ = radical_difference_set(GF(v,'a'),k,l)
515
+ ....: print("{:3} {:3} {:3}".format(v,k,l))
516
+ 3 2 1
517
+ 4 3 2
518
+ 7 3 1
519
+ 5 4 3
520
+ 7 4 2
521
+ 13 4 1
522
+ 11 5 2
523
+ 7 6 5
524
+ 11 6 3
525
+ 16 6 2
526
+ 8 7 6
527
+ 9 8 7
528
+ 19 9 4
529
+ 37 9 2
530
+ 73 9 1
531
+ 11 10 9
532
+ 19 10 5
533
+ 23 11 5
534
+ 13 12 11
535
+ 23 12 6
536
+ 27 13 6
537
+ 27 14 7
538
+ 16 15 14
539
+ 31 15 7
540
+ ...
541
+ 41 40 39
542
+ 79 40 20
543
+ 83 41 20
544
+ 43 42 41
545
+ 83 42 21
546
+ 47 46 45
547
+ 49 48 47
548
+ 197 49 12
549
+ """
550
+ v = K.cardinality()
551
+
552
+ if l*(v-1) != k*(k-1):
553
+ if existence:
554
+ return False
555
+ raise EmptySetError("l*(v-1) is not equal to k*(k-1)")
556
+
557
+ # trivial case
558
+ if (v-1) == k:
559
+ if existence:
560
+ return True
561
+ add_zero = False
562
+
563
+ # q = 3 mod 4
564
+ elif v % 4 == 3 and k == (v-1)//2:
565
+ if existence:
566
+ return True
567
+ add_zero = False
568
+
569
+ # q = 3 mod 4
570
+ elif v % 4 == 3 and k == (v+1)//2:
571
+ if existence:
572
+ return True
573
+ add_zero = True
574
+
575
+ # q = 4t^2 + 1, t odd
576
+ elif v % 8 == 5 and k == (v-1)//4 and arith.is_square((v-1)//4):
577
+ if existence:
578
+ return True
579
+ add_zero = False
580
+
581
+ # q = 4t^2 + 9, t odd
582
+ elif v % 8 == 5 and k == (v+3)//4 and arith.is_square((v-9)//4):
583
+ if existence:
584
+ return True
585
+ add_zero = True
586
+
587
+ # exceptional case 1
588
+ elif (v,k,l) == (16,6,2):
589
+ if existence:
590
+ return True
591
+ add_zero = True
592
+
593
+ # exceptional case 2
594
+ elif (v,k,l) == (73,9,1):
595
+ if existence:
596
+ return True
597
+ add_zero = False
598
+
599
+ # are there more ??
600
+ else:
601
+ x = K.multiplicative_generator()
602
+ D = K.cyclotomic_cosets(x**((v-1)//k), [K.one()])
603
+ if is_difference_family(K, D, v, k, l):
604
+ print("** You found a new example of radical difference set **\n"
605
+ "** for the parameters (v,k,l)=({},{},{}). **\n"
606
+ "** Please contact sage-devel@googlegroups.com **\n".format(v, k, l))
607
+ if existence:
608
+ return True
609
+ add_zero = False
610
+
611
+ else:
612
+ D = K.cyclotomic_cosets(x**((v-1)//(k-1)), [K.one()])
613
+ D[0].insert(0,K.zero())
614
+ if is_difference_family(K, D, v, k, l):
615
+ print("** You found a new example of radical difference set **\n"
616
+ "** for the parameters (v,k,l)=({},{},{}). **\n"
617
+ "** Please contact sage-devel@googlegroups.com **\n".format(v, k, l))
618
+ if existence:
619
+ return True
620
+ add_zero = True
621
+
622
+ elif existence:
623
+ return False
624
+ else:
625
+ raise EmptySetError("no radical difference set exist "
626
+ "for the parameters (v,k,l) = ({},{},{}".format(v,k,l))
627
+
628
+ x = K.multiplicative_generator()
629
+ if add_zero:
630
+ r = x**((v-1)//(k-1))
631
+ D = K.cyclotomic_cosets(r, [K.one()])
632
+ D[0].insert(0, K.zero())
633
+ else:
634
+ r = x**((v-1)//k)
635
+ D = K.cyclotomic_cosets(r, [K.one()])
636
+
637
+ if check and not is_difference_family(K, D, v, k, l):
638
+ raise RuntimeError("Sage tried to build a radical difference set with "
639
+ "parameters ({},{},{}) but it seems that it failed! Please "
640
+ "e-mail sage-devel@googlegroups.com".format(v,k,l))
641
+
642
+ return D
643
+
644
+
645
+ def one_cyclic_tiling(A, n):
646
+ r"""
647
+ Given a subset ``A`` of the cyclic additive group `G = Z / nZ` return
648
+ another subset `B` so that `A + B = G` and `|A| |B| = n` (i.e. any element
649
+ of `G` is uniquely expressed as a sum `a+b` with `a` in `A` and `b` in `B`).
650
+
651
+ EXAMPLES::
652
+
653
+ sage: from sage.combinat.designs.difference_family import one_cyclic_tiling
654
+ sage: tile = [0,2,4]
655
+ sage: m = one_cyclic_tiling(tile,6); m
656
+ [0, 3]
657
+ sage: sorted((i+j)%6 for i in tile for j in m)
658
+ [0, 1, 2, 3, 4, 5]
659
+
660
+ sage: def print_tiling(tile, translat, n):
661
+ ....: for x in translat:
662
+ ....: print(''.join('X' if (i-x)%n in tile else '.' for i in range(n)))
663
+
664
+ sage: tile = [0, 1, 2, 7]
665
+ sage: m = one_cyclic_tiling(tile, 12)
666
+ sage: print_tiling(tile, m, 12)
667
+ XXX....X....
668
+ ....XXX....X
669
+ ...X....XXX.
670
+
671
+ sage: tile = [0, 1, 5]
672
+ sage: m = one_cyclic_tiling(tile, 12)
673
+ sage: print_tiling(tile, m, 12)
674
+ XX...X......
675
+ ...XX...X...
676
+ ......XX...X
677
+ ..X......XX.
678
+
679
+ sage: tile = [0, 2]
680
+ sage: m = one_cyclic_tiling(tile, 8)
681
+ sage: print_tiling(tile, m, 8)
682
+ X.X.....
683
+ ....X.X.
684
+ .X.X....
685
+ .....X.X
686
+
687
+ ALGORITHM:
688
+
689
+ Uses dancing links :mod:`sage.combinat.dlx`
690
+ """
691
+ # we first try a naive approach which correspond to what Wilson used in his
692
+ # 1972 article
693
+ n = int(n)
694
+ d = len(A)
695
+ if len(set(a % d for a in A)) == d:
696
+ return [i*d for i in range(n//d)]
697
+
698
+ # next, we consider an exhaustive search
699
+ from sage.combinat.dlx import DLXMatrix
700
+
701
+ rows = []
702
+ for i in range(n):
703
+ rows.append([i+1, [(i+a) % n+1 for a in A]])
704
+ M = DLXMatrix(rows)
705
+ for c in M:
706
+ return [i-1 for i in c]
707
+
708
+
709
+ def one_radical_difference_family(K, k):
710
+ r"""
711
+ Search for a radical difference family on ``K`` using dancing links
712
+ algorithm.
713
+
714
+ For the definition of radical difference family, see
715
+ :func:`radical_difference_family`. Here, we consider only radical difference
716
+ family with `\lambda = 1`.
717
+
718
+ INPUT:
719
+
720
+ - ``K`` -- a finite field of cardinality `q`
721
+ - ``k`` -- positive integer so that `k(k-1)` divides `q-1`
722
+
723
+ OUTPUT: either a difference family or ``None`` if it does not exist
724
+
725
+ ALGORITHM:
726
+
727
+ The existence of a radical difference family is equivalent to a one
728
+ dimensional tiling (or packing) problem in a cyclic group. This subsequent
729
+ problem is solved by a call to the function :func:`one_cyclic_tiling`.
730
+
731
+ Let `K^*` be the multiplicative group of the finite field `K`. A radical
732
+ family has the form `\mathcal B = \{x_1 B, \ldots, x_k B\}`, where
733
+ `B=\{x:x^{k}=1\}` (for `k` odd) or `B=\{x:x^{k-1}=1\}\cup \{0\}` (for
734
+ `k` even). Equivalently, `K^*` decomposes as:
735
+
736
+ .. MATH::
737
+
738
+ K^* = \Delta (x_1 B) \cup \cdots \cup \Delta (x_k B)
739
+ = x_1 \Delta B \cup \cdots \cup x_k \Delta B.
740
+
741
+ We observe that `C=B\backslash 0` is a subgroup of the (cyclic) group
742
+ `K^*`, that can thus be generated by some element `r`. Furthermore, we
743
+ observe that `\Delta B` is always a union of cosets of `\pm C` (which is
744
+ twice larger than `C`).
745
+
746
+ .. MATH::
747
+
748
+ \begin{array}{llll}
749
+ (k\text{ odd} ) & \Delta B &= \{r^i-r^j:r^i\neq r^j\} &= \pm C\cdot \{r^i-1: 0 < i \leq m\}\\
750
+ (k\text{ even}) & \Delta B &= \{r^i-r^j:r^i\neq r^j\}\cup C &= \pm C\cdot \{r^i-1: 0 < i < m\}\cup \pm C
751
+ \end{array}
752
+
753
+ where
754
+
755
+ .. MATH::
756
+
757
+ (k\text{ odd})\ m = (k-1)/2 \quad \text{and} \quad (k\text{ even})\ m = k/2.
758
+
759
+ Consequently, `\mathcal B = \{x_1 B, \ldots, x_k B\}` is a radical
760
+ difference family if and only if `\{x_1 (\Delta B/(\pm C)), \ldots, x_k
761
+ (\Delta B/(\pm C))\}` is a partition of the cyclic group `K^*/(\pm C)`.
762
+
763
+ EXAMPLES::
764
+
765
+ sage: from sage.combinat.designs.difference_family import (
766
+ ....: one_radical_difference_family,
767
+ ....: is_difference_family)
768
+
769
+ sage: one_radical_difference_family(GF(13),4) # needs sage.rings.finite_rings
770
+ [[0, 1, 3, 9]]
771
+
772
+ The parameters that appear in [Bu95]_::
773
+
774
+ sage: df = one_radical_difference_family(GF(449), 8); df # needs sage.rings.finite_rings
775
+ [[0, 1, 18, 25, 176, 324, 359, 444],
776
+ [0, 9, 88, 162, 222, 225, 237, 404],
777
+ [0, 11, 140, 198, 275, 357, 394, 421],
778
+ [0, 40, 102, 249, 271, 305, 388, 441],
779
+ [0, 49, 80, 93, 161, 204, 327, 433],
780
+ [0, 70, 99, 197, 230, 362, 403, 435],
781
+ [0, 121, 141, 193, 293, 331, 335, 382],
782
+ [0, 191, 285, 295, 321, 371, 390, 392]]
783
+ sage: is_difference_family(GF(449), df, 449, 8, 1) # needs sage.rings.finite_rings
784
+ True
785
+ """
786
+ q = K.cardinality()
787
+ x = K.multiplicative_generator()
788
+
789
+ e = k*(k-1)
790
+ if q % e != 1:
791
+ raise ValueError("q%e is not 1")
792
+
793
+ # We define A by (see the function's documentation):
794
+ # ΔB = C.A
795
+ if k % 2 == 1:
796
+ m = (k-1) // 2
797
+ r = x ** ((q-1) // k) # k-th root of unity
798
+ A = [r**i - 1 for i in range(1,m+1)]
799
+ else:
800
+ m = k // 2
801
+ r = x ** ((q-1) // (k-1)) # (k-1)-th root of unity
802
+ A = [r**i - 1 for i in range(1,m)]
803
+ A.append(K.one())
804
+
805
+ # instead of the complicated multiplicative group K^*/(±C) we use the
806
+ # discrete logarithm to convert everything into the additive group Z/cZ
807
+ c = m * (q-1) // e # cardinal of ±C
808
+ from sage.groups.generic import discrete_log
809
+ logA = [discrete_log(a,x) % c for a in A]
810
+
811
+ # if two elements of A are equal modulo c then no tiling is possible
812
+ if len(set(logA)) != m:
813
+ return None
814
+
815
+ # brute force
816
+ tiling = one_cyclic_tiling(logA, c)
817
+ if tiling is None:
818
+ return None
819
+
820
+ D = K.cyclotomic_cosets(r, [x**i for i in tiling])
821
+ if k % 2 == 0:
822
+ for d in D:
823
+ d.insert(K.zero(),0)
824
+ return D
825
+
826
+
827
+ def radical_difference_family(K, k, l=1, existence=False, check=True):
828
+ r"""
829
+ Return a ``(v,k,l)``-radical difference family.
830
+
831
+ Let fix an integer `k` and a prime power `q = t k(k-1) + 1`. Let `K` be a
832
+ field of cardinality `q`. A `(q,k,1)`-difference family is *radical* if
833
+ its base blocks are either: a coset of the `k`-th root of unity for `k` odd
834
+ or a coset of `k-1`-th root of unity and `0` if `k` is even (the number `t`
835
+ is the number of blocks of that difference family).
836
+
837
+ The terminology comes from M. Buratti article [Bu95]_ but the first
838
+ constructions go back to R. Wilson [Wi72]_.
839
+
840
+ INPUT:
841
+
842
+ - ``K`` -- a finite field
843
+ - ``k`` -- positive integer; the size of the blocks
844
+ - ``l`` -- integer (default: `1`); the `\lambda` parameter
845
+ - ``existence`` -- if ``True``, then return either ``True`` if Sage knows
846
+ how to build such design, ``Unknown`` if it does not and ``False`` if it
847
+ knows that the design does not exist
848
+ - ``check`` -- boolean (default: ``True``); if ``True`` then the result of
849
+ the computation is checked before being returned. This should not be
850
+ needed but ensures that the output is correct
851
+
852
+ EXAMPLES::
853
+
854
+ sage: from sage.combinat.designs.difference_family import radical_difference_family
855
+
856
+ sage: radical_difference_family(GF(73), 9) # needs sage.rings.finite_rings
857
+ [[1, 2, 4, 8, 16, 32, 37, 55, 64]]
858
+
859
+ sage: radical_difference_family(GF(281), 5) # needs sage.rings.finite_rings
860
+ [[1, 86, 90, 153, 232],
861
+ [4, 50, 63, 79, 85],
862
+ [5, 36, 149, 169, 203],
863
+ [7, 40, 68, 219, 228],
864
+ [9, 121, 212, 248, 253],
865
+ [29, 81, 222, 246, 265],
866
+ [31, 137, 167, 247, 261],
867
+ [32, 70, 118, 119, 223],
868
+ [39, 56, 66, 138, 263],
869
+ [43, 45, 116, 141, 217],
870
+ [98, 101, 109, 256, 279],
871
+ [106, 124, 145, 201, 267],
872
+ [111, 123, 155, 181, 273],
873
+ [156, 209, 224, 264, 271]]
874
+
875
+ sage: for k in range(5,10): # needs sage.rings.finite_rings
876
+ ....: print("k = {}".format(k))
877
+ ....: list_q = []
878
+ ....: for q in range(k*(k-1)+1, 2000, k*(k-1)):
879
+ ....: if is_prime_power(q):
880
+ ....: K = GF(q,'a')
881
+ ....: if radical_difference_family(K, k, existence=True) is True:
882
+ ....: list_q.append(q)
883
+ ....: _ = radical_difference_family(K,k)
884
+ ....: print(" ".join(str(p) for p in list_q))
885
+ k = 5
886
+ 41 61 81 241 281 401 421 601 641 661 701 761 821 881 1181 1201 1301 1321
887
+ 1361 1381 1481 1601 1681 1801 1901
888
+ k = 6
889
+ 181 211 241 631 691 1531 1831 1861
890
+ k = 7
891
+ 337 421 463 883 1723
892
+ k = 8
893
+ 449 1009
894
+ k = 9
895
+ 73 1153 1873
896
+ """
897
+ v = K.cardinality()
898
+ x = K.multiplicative_generator()
899
+ e = k*(k-1)
900
+ if (l*(v-1)) % e:
901
+ raise ValueError("k (k-1) = {} should be a multiple of l (v-1) ={}".format(
902
+ k*(k-1), l*(v-1)))
903
+ t = l*(v-1) // e # number of blocks
904
+
905
+ if t == 1:
906
+ return radical_difference_set(K, k, l, existence=existence, check=check)
907
+
908
+ elif l == (k-1):
909
+ if existence:
910
+ return True
911
+ else:
912
+ return K.cyclotomic_cosets(x**((v-1)//k))[1:]
913
+
914
+ # all the other cases below concern the case l == 1
915
+ elif l != 1:
916
+ if existence:
917
+ return Unknown
918
+ raise NotImplementedError("No radical families implemented for l > 2")
919
+
920
+ else:
921
+ D = one_radical_difference_family(K,k)
922
+ if D is None:
923
+ if existence:
924
+ return False
925
+ raise EmptySetError("No such difference family")
926
+ elif existence:
927
+ return True
928
+
929
+ if check and not is_difference_family(K, D, v, k, l):
930
+ raise RuntimeError("radical_difference_family produced a wrong "
931
+ "difference family with parameters v={}, "
932
+ "k={}, l={}. Please contact "
933
+ "sage-devel@googlegroups.com".format(v,k,l))
934
+
935
+ return D
936
+
937
+
938
+ def twin_prime_powers_difference_set(p, check=True):
939
+ r"""
940
+ Return a difference set on `GF(p) \times GF(p+2)`.
941
+
942
+ The difference set is built from the following element of the Cartesian
943
+ product of finite fields `GF(p) \times GF(p+2)`:
944
+
945
+ - `(x,0)` with any `x`
946
+ - `(x,y)` with `x` and `y` squares
947
+ - `(x,y)` with `x` and `y` non-squares
948
+
949
+ For more information see :wikipedia:`Difference_set`.
950
+
951
+ INPUT:
952
+
953
+ - ``check`` -- boolean (default: ``True``); if ``True``, then the result of
954
+ the computation is checked before being returned. This should not be
955
+ needed but ensures that the output is correct
956
+
957
+ EXAMPLES::
958
+
959
+ sage: from sage.combinat.designs.difference_family import twin_prime_powers_difference_set
960
+ sage: G, D = twin_prime_powers_difference_set(3)
961
+ sage: G
962
+ The Cartesian product of (Finite Field of size 3, Finite Field of size 5)
963
+ sage: D
964
+ [[(1, 1), (1, 4), (2, 2), (2, 3), (0, 0), (1, 0), (2, 0)]]
965
+ """
966
+ from sage.rings.finite_rings.finite_field_constructor import FiniteField
967
+ from sage.categories.cartesian_product import cartesian_product
968
+ from itertools import product
969
+ Fp = FiniteField(p,'x')
970
+ Fq = FiniteField(p+2,'x')
971
+ Fpset = set(Fp)
972
+ Fqset = set(Fq)
973
+ Fp_squares = set(x**2 for x in Fpset)
974
+ Fq_squares = set(x**2 for x in Fqset)
975
+
976
+ # Pairs of squares, pairs of non-squares
977
+ d = []
978
+ d.extend(product(Fp_squares.difference([0]),Fq_squares.difference([0])))
979
+ d.extend(product(Fpset.difference(Fp_squares),Fqset.difference(Fq_squares)))
980
+
981
+ # All (x,0)
982
+ d.extend((x,0) for x in Fpset)
983
+
984
+ G = cartesian_product([Fp,Fq])
985
+
986
+ if check and not is_difference_family(G, [d]):
987
+ raise RuntimeError("twin_prime_powers_difference_set produced a wrong "
988
+ "difference set with p={}. Please contact "
989
+ "sage-devel@googlegroups.com".format(p))
990
+
991
+ return G, [d]
992
+
993
+
994
+ def are_mcfarland_1973_parameters(v, k, lmbda, return_parameters=False):
995
+ r"""
996
+ Test whether ``(v,k,lmbda)`` is a triple that can be obtained from the
997
+ construction from [McF1973]_.
998
+
999
+ See :func:`mcfarland_1973_construction`.
1000
+
1001
+ INPUT:
1002
+
1003
+ - ``v``, ``k``, ``lmbda`` -- integers; parameters of the difference family
1004
+ - ``return_parameters`` -- boolean (default: ``False``); if ``True``, return a
1005
+ pair ``(True, (q, s))`` so that ``(q,s)`` can be used in the function
1006
+ :func:`mcfarland_1973_construction` to actually build a
1007
+ ``(v,k,lmbda)``-difference family. Or ``(False, None)`` if the
1008
+ construction is not possible
1009
+
1010
+ EXAMPLES::
1011
+
1012
+ sage: # needs sage.rings.finite_rings
1013
+ sage: from sage.combinat.designs.difference_family import are_mcfarland_1973_parameters
1014
+ sage: are_mcfarland_1973_parameters(64, 28, 12)
1015
+ True
1016
+ sage: are_mcfarland_1973_parameters(64, 28, 12, return_parameters=True)
1017
+ (True, (2, 2))
1018
+ sage: are_mcfarland_1973_parameters(60, 13, 5)
1019
+ False
1020
+ sage: are_mcfarland_1973_parameters(98125, 19500, 3875)
1021
+ True
1022
+ sage: are_mcfarland_1973_parameters(98125, 19500, 3875, True)
1023
+ (True, (5, 3))
1024
+
1025
+ sage: from sage.combinat.designs.difference_family import are_mcfarland_1973_parameters
1026
+ sage: for v in range(1, 100): # needs sage.rings.finite_rings
1027
+ ....: for k in range(1,30):
1028
+ ....: for l in range(1,15):
1029
+ ....: if are_mcfarland_1973_parameters(v,k,l):
1030
+ ....: answer, (q,s) = are_mcfarland_1973_parameters(v,k,l,return_parameters=True)
1031
+ ....: print("{} {} {} {} {}".format(v,k,l,q,s))
1032
+ ....: assert answer is True
1033
+ ....: assert designs.difference_family(v,k,l,existence=True) is True
1034
+ ....: G,D = designs.difference_family(v,k,l)
1035
+ 16 6 2 2 1
1036
+ 45 12 3 3 1
1037
+ 64 28 12 2 2
1038
+ 96 20 4 4 1
1039
+ """
1040
+ if v <= k or k <= lmbda:
1041
+ return (False, None) if return_parameters else False
1042
+ k = ZZ(k)
1043
+ lmbda = ZZ(lmbda)
1044
+ qs, r = (k - lmbda).sqrtrem() # sqrt(k-l) should be q^s
1045
+ if r or (qs*(qs-1)) % lmbda:
1046
+ return (False, None) if return_parameters else False
1047
+
1048
+ q = qs*(qs-1) // lmbda + 1
1049
+ if (q <= 1 or
1050
+ v * (q-1) != qs*q * (qs*q+q-2) or
1051
+ k * (q-1) != qs * (qs*q-1)):
1052
+ return (False, None) if return_parameters else False
1053
+
1054
+ # NOTE: below we compute the value of s so that qs = q^s. If the method
1055
+ # is_power_of of integers would be able to return the exponent, we could use
1056
+ # that... but currently this is not the case
1057
+ # see github issue #19792
1058
+ p1,a1 = qs.is_prime_power(get_data=True)
1059
+ p2,a2 = q.is_prime_power(get_data=True)
1060
+
1061
+ if a1 == 0 or a2 == 0 or p1 != p2 or a1 % a2:
1062
+ return (False, None) if return_parameters else False
1063
+
1064
+ return (True, (q, a1//a2)) if return_parameters else True
1065
+
1066
+
1067
+ def mcfarland_1973_construction(q, s):
1068
+ r"""
1069
+ Return a difference set.
1070
+
1071
+ The difference set returned has the following parameters
1072
+
1073
+ .. MATH::
1074
+
1075
+ v = \frac{q^{s+1}(q^{s+1}+q-2)}{q-1},
1076
+ k = \frac{q^s (q^{s+1}-1)}{q-1},
1077
+ \lambda = \frac{q^s(q^s-1)}{q-1}
1078
+
1079
+ This construction is due to [McF1973]_.
1080
+
1081
+ INPUT:
1082
+
1083
+ - ``q``, ``s`` -- integers; parameters for the difference set (see the above
1084
+ formulas for the expression of ``v``, ``k``, ``l`` in terms of ``q`` and
1085
+ ``s``)
1086
+
1087
+ .. SEEALSO::
1088
+
1089
+ The function :func:`are_mcfarland_1973_parameters` makes the translation
1090
+ between the parameters `(q,s)` corresponding to a given triple
1091
+ `(v,k,\lambda)`.
1092
+
1093
+ REFERENCES:
1094
+
1095
+ .. [McF1973] Robert L. McFarland
1096
+ "A family of difference sets in non-cyclic groups"
1097
+ J. Combinatorial Theory (A) 15 (1973) 1--10.
1098
+ :doi:`10.1016/0097-3165(73)90031-9`
1099
+
1100
+ EXAMPLES::
1101
+
1102
+ sage: from sage.combinat.designs.difference_family import (
1103
+ ....: mcfarland_1973_construction, is_difference_family)
1104
+
1105
+ sage: G,D = mcfarland_1973_construction(3, 1) # needs sage.modules
1106
+ sage: assert is_difference_family(G, D, 45, 12, 3) # needs sage.modules
1107
+
1108
+ sage: G,D = mcfarland_1973_construction(2, 2) # needs sage.modules
1109
+ sage: assert is_difference_family(G, D, 64, 28, 12) # needs sage.modules
1110
+ """
1111
+ from sage.rings.finite_rings.finite_field_constructor import GF
1112
+ from sage.modules.free_module import VectorSpace
1113
+ from sage.rings.finite_rings.integer_mod_ring import Zmod
1114
+ from sage.categories.cartesian_product import cartesian_product
1115
+
1116
+ r = (q**(s+1)-1) // (q-1)
1117
+ F = GF(q,'a')
1118
+ V = VectorSpace(F, s+1)
1119
+ K = Zmod(r+1)
1120
+
1121
+ G = cartesian_product([F]*(s+1) + [K])
1122
+
1123
+ D = []
1124
+ for k, H in zip(K, V.subspaces(s)):
1125
+ for v in H:
1126
+ D.append(G(tuple(v) + (k,)))
1127
+
1128
+ return G,[D]
1129
+
1130
+
1131
+ def are_hadamard_difference_set_parameters(v, k, lmbda):
1132
+ r"""
1133
+ Check whether ``(v,k,lmbda)`` is of the form ``(4N^2, 2N^2 - N, N^2 - N)``.
1134
+
1135
+ INPUT:
1136
+
1137
+ - ``(v, k, lmbda)`` -- parameters of a difference set
1138
+
1139
+ EXAMPLES::
1140
+
1141
+ sage: from sage.combinat.designs.difference_family import are_hadamard_difference_set_parameters
1142
+ sage: are_hadamard_difference_set_parameters(36, 15, 6)
1143
+ True
1144
+ sage: are_hadamard_difference_set_parameters(60, 13, 5)
1145
+ False
1146
+ """
1147
+ N = k - 2*lmbda
1148
+ N2 = N*N
1149
+ return v == 4*N2 and k == 2*N2 - N and lmbda == N2 - N
1150
+
1151
+
1152
+ @cached_function
1153
+ def hadamard_difference_set_product_parameters(N):
1154
+ r"""
1155
+ Check whether a product construction is available for Hadamard difference
1156
+ set with parameter ``N``.
1157
+
1158
+ This function looks for two integers `N_1` and `N_2` greater than `1`
1159
+ and so that `N = 2 N_1 N_2` and there exists Hadamard difference set with
1160
+ parameters `(4 N_i^2, 2N_i^2 - N_i, N_i^2 - N_i)`. If such pair exists,
1161
+ the output is the pair ``(N_1, N_2)`` otherwise it is ``None``.
1162
+
1163
+ INPUT:
1164
+
1165
+ - ``N`` -- positive integer
1166
+
1167
+ EXAMPLES::
1168
+
1169
+ sage: from sage.combinat.designs.difference_family import hadamard_difference_set_product_parameters
1170
+ sage: hadamard_difference_set_product_parameters(8) # needs sage.rings.finite_rings
1171
+ (2, 2)
1172
+ """
1173
+ if N % 2:
1174
+ return False
1175
+
1176
+ for N1 in (N//2).divisors()[1:]:
1177
+ if 4*N1 > N:
1178
+ break
1179
+ v1 = 4*N1*N1
1180
+ k1 = 2*N1*N1 - N1
1181
+ l1 = N1*N1 - N1
1182
+ if not difference_family(v1, k1, l1, existence=True):
1183
+ continue
1184
+ N2 = N // (2*N1)
1185
+ v2 = 4*N2*N2
1186
+ k2 = 2*N2*N2 - N2
1187
+ l2 = N2*N2 - N2
1188
+ if not difference_family(v2, k2, l2, existence=True):
1189
+ continue
1190
+
1191
+ return (N1,N2)
1192
+
1193
+ return None
1194
+
1195
+
1196
+ def hadamard_difference_set_product(G1, D1, G2, D2):
1197
+ r"""
1198
+ Make a product of two Hadamard difference sets.
1199
+
1200
+ This product construction appears in [Tu1984]_.
1201
+
1202
+ INPUT:
1203
+
1204
+ - ``G1, D1``, ``G2, D2`` -- two Hadamard difference sets
1205
+
1206
+ EXAMPLES::
1207
+
1208
+ sage: from sage.combinat.designs.difference_family import hadamard_difference_set_product
1209
+ sage: from sage.combinat.designs.difference_family import is_difference_family
1210
+
1211
+ sage: G1,D1 = designs.difference_family(16,6,2) # needs sage.rings.finite_rings
1212
+ sage: G2,D2 = designs.difference_family(36,15,6) # needs sage.rings.finite_rings
1213
+
1214
+ sage: G11,D11 = hadamard_difference_set_product(G1,D1,G1,D1) # needs sage.rings.finite_rings
1215
+ sage: assert is_difference_family(G11, D11, 256, 120, 56) # needs sage.rings.finite_rings
1216
+ sage: assert designs.difference_family(256, 120, 56, existence=True) is True # needs sage.rings.finite_rings
1217
+
1218
+ sage: G12,D12 = hadamard_difference_set_product(G1,D1,G2,D2) # needs sage.rings.finite_rings
1219
+ sage: assert is_difference_family(G12, D12, 576, 276, 132) # needs sage.rings.finite_rings
1220
+ sage: assert designs.difference_family(576, 276, 132, existence=True) is True # needs sage.rings.finite_rings
1221
+ """
1222
+ from sage.categories.cartesian_product import cartesian_product
1223
+
1224
+ G = cartesian_product([G1,G2])
1225
+ D1 = set(D1[0])
1226
+ D1c = set(s for s in G1 if s not in D1)
1227
+ D2 = set(D2[0])
1228
+ D2c = set(s for s in G2 if s not in D2)
1229
+
1230
+ D = set().union((G((s1,s2)) for s1 in D1 for s2 in D2),
1231
+ (G((s1,s2)) for s1 in D1c for s2 in D2c))
1232
+
1233
+ return G, [[s for s in G if s not in D]]
1234
+
1235
+
1236
+ def turyn_1965_3x3xK(k=4):
1237
+ r"""
1238
+ Return a difference set in either `C_3 \times C_3 \times C_4` or `C_3 \times
1239
+ C_3 \times C_2 \times C_2` with parameters `v=36`, `k=15`, `\lambda=6`.
1240
+
1241
+ This example appears in [Tu1965]_.
1242
+
1243
+ INPUT:
1244
+
1245
+ - ``k`` -- either ``2`` (to get a difference set in `C_3 \times C_3 \times
1246
+ C_2 \times C_2`) or ``4`` (to get a difference set in `C_3 \times C_3
1247
+ \times C_3 \times C_4`)
1248
+
1249
+ EXAMPLES::
1250
+
1251
+ sage: from sage.combinat.designs.difference_family import turyn_1965_3x3xK
1252
+ sage: from sage.combinat.designs.difference_family import is_difference_family
1253
+ sage: G,D = turyn_1965_3x3xK(4)
1254
+ sage: assert is_difference_family(G, D, 36, 15, 6)
1255
+ sage: G,D = turyn_1965_3x3xK(2)
1256
+ sage: assert is_difference_family(G, D, 36, 15, 6)
1257
+ """
1258
+ from sage.categories.cartesian_product import cartesian_product
1259
+ from sage.rings.finite_rings.integer_mod_ring import Zmod
1260
+
1261
+ if k == 2:
1262
+ G = cartesian_product([Zmod(3), Zmod(3), Zmod(2), Zmod(2)])
1263
+ K = [(0,0), (0,1), (1,0), (1,1)]
1264
+ elif k == 4:
1265
+ G = cartesian_product([Zmod(3), Zmod(3), Zmod(4)])
1266
+ K = [(0,), (1,), (2,), (3,)]
1267
+ else:
1268
+ raise ValueError("k must be 2 or 4")
1269
+
1270
+ L = [[(0,1),(1,1),(2,1),(0,2),(1,2),(2,2)], # complement of y=0
1271
+ [(0,0),(1,1),(2,2)], # x-y=0
1272
+ [(0,0),(1,2),(2,1)], # x+y=0
1273
+ [(0,0),(0,1),(0,2)]] # x=0
1274
+
1275
+ return G, [[G(v + k) for l, k in zip(L, K) for v in l]]
1276
+
1277
+
1278
+ def _is_periodic_sequence(seq, period):
1279
+ r"""
1280
+ Check if the sequence is periodic with correct period.
1281
+
1282
+ The sequence should have length at least twice the period, so that
1283
+ periodicity can be checked.
1284
+
1285
+ INPUT:
1286
+
1287
+ - ``seq`` -- the sequence to be tested (must have length at least twice the period)
1288
+ - ``period`` -- integer; the period that the sequence should have
1289
+
1290
+ EXAMPLES::
1291
+
1292
+ sage: from sage.combinat.designs.difference_family import _is_periodic_sequence
1293
+ sage: _is_periodic_sequence([0, 1, 2, 3, 0, 1, 2, 3], 4)
1294
+ True
1295
+ sage: _is_periodic_sequence([0, 1, 0, 1, 0, 1, 0, 1], 4)
1296
+ False
1297
+ sage: _is_periodic_sequence([0, 1, 1, 1, 0, 1, 2, 1], 4)
1298
+ False
1299
+ """
1300
+ assert len(seq) >= 2*period
1301
+
1302
+ for per in range(1, period):
1303
+ first = seq[:per]
1304
+ periodic = True
1305
+ for j in range(1, len(seq)//per):
1306
+ if seq[j*per : (j+1)*per] != first:
1307
+ periodic = False
1308
+ break
1309
+ if periodic:
1310
+ return False
1311
+ if seq[:period] != seq[period : 2*period]:
1312
+ return False
1313
+ return True
1314
+
1315
+
1316
+ def _create_m_sequence(q, n, check=True):
1317
+ r"""
1318
+ Create an m-sequence over GF(q) with period `q^n - 1`.
1319
+
1320
+ Given a prime power `q`, the m-sequence is created as described by [Zie1959]_
1321
+ from a primitive function over the finite field `GF(q)`.
1322
+
1323
+ Given a primitive function `f=c_0+c_1x+...+c_nx^n` over `K = GF(q)` of degree `n`,
1324
+ the recurrence is given by: `a_i = -c_0^{-1}(c_1a_{i-1} + ... + c_na{i-n})`.
1325
+ The first `n` elements will be `0, 0, ..., 0, 1` and these will give a maximal length recurrence sequence
1326
+ as shown in [Mit2008]_.
1327
+
1328
+ INPUT:
1329
+
1330
+ - ``q`` -- a prime power
1331
+ - ``n`` -- a nonnegative number
1332
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the
1333
+ result is a sequence with correct period; detting it to ``False`` may
1334
+ speed up considerably the computation
1335
+
1336
+ EXAMPLES::
1337
+
1338
+ sage: from sage.combinat.designs.difference_family import _create_m_sequence
1339
+ sage: _create_m_sequence(3, 2) # random # needs sage.rings.finite_rings
1340
+ [1, 0, 1, 2, 2, 0, 2, 1]
1341
+ sage: _create_m_sequence(4, 2, check=False) # random # needs sage.rings.finite_rings
1342
+ [1, 0, a, a + 1, a, a, 0, a + 1, 1, a + 1, a + 1, 0, 1, a, 1]
1343
+ sage: _create_m_sequence(6, 2)
1344
+ Traceback (most recent call last):
1345
+ ...
1346
+ ValueError: q must be a prime power
1347
+ """
1348
+ from sage.rings.finite_rings.finite_field_constructor import GF
1349
+
1350
+ if not is_prime_power(q):
1351
+ raise ValueError('q must be a prime power')
1352
+ if n < 0:
1353
+ raise ValueError('n cannot be negative')
1354
+
1355
+ K = GF(q, 'a')
1356
+
1357
+ T = PolynomialRing(K, 'x')
1358
+ primitive = T.irreducible_element(n, algorithm='random')
1359
+ while not primitive.is_primitive():
1360
+ primitive = T.irreducible_element(n, algorithm='random')
1361
+ coeffs = primitive.coefficients()
1362
+ exps = primitive.exponents()
1363
+
1364
+ period = q**n - 1
1365
+ seq_len = period*2 if check else period
1366
+ seq = [1] + [0]*(n-1)
1367
+
1368
+ while len(seq) < seq_len:
1369
+ nxt = 0
1370
+ for i, coeff in zip(exps[1:], coeffs[1:]):
1371
+ nxt += coeff * seq[-i]
1372
+ seq.append(-coeffs[0].inverse() * nxt)
1373
+
1374
+ if check:
1375
+ assert _is_periodic_sequence(seq, period)
1376
+ return seq[:period]
1377
+
1378
+
1379
+ def _get_submodule_of_order(G, order):
1380
+ r"""
1381
+ Construct a submodule of the given order from group ``G``.
1382
+
1383
+ This method tries to construct submodules from various elements of `G` until
1384
+ a submodule of the correct order is found.
1385
+
1386
+ INPUT:
1387
+
1388
+ - ``G`` -- an additive abelian group
1389
+ - ``order`` -- integer; the order of the desired submodule
1390
+
1391
+ TESTS:
1392
+
1393
+ sage: # needs sage.modules
1394
+ sage: from sage.combinat.designs.difference_family import _get_submodule_of_order
1395
+ sage: G = AdditiveAbelianGroup([48])
1396
+ sage: _get_submodule_of_order(G, 6).order()
1397
+ 6
1398
+ sage: G = AdditiveAbelianGroup([13^2 - 1])
1399
+ sage: _get_submodule_of_order(G, 12).order()
1400
+ 12
1401
+ """
1402
+ for el in G:
1403
+ H = G.submodule([el])
1404
+ if H.order() == order:
1405
+ return H
1406
+ return None
1407
+
1408
+
1409
+ def relative_difference_set_from_m_sequence(q, N, check=True, return_group=False):
1410
+ r"""
1411
+ Construct `R((q^N-1)/(q-1), q-1, q^{N-1}, q^{N-2})` where ``q`` is a prime power and `N\ge 2`.
1412
+
1413
+ The relative difference set is constructed over the set of additive integers modulo `q^N-1`,
1414
+ as described in Theorem 5.1 of [EB1966]_. Given an m-sequence `(a_i)` of period `q^N-1`, the
1415
+ set is: `R=\{i | 0 \le i \le q^{N-1}, a_i=1\}`.
1416
+
1417
+ INPUT:
1418
+
1419
+ - ``q`` -- a prime power
1420
+ - ``N`` -- a nonnegative number
1421
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the
1422
+ result is a relative difference set before returning it
1423
+ - ``return_group`` -- boolean (default: ``False``); if ``True``, the function
1424
+ will also return the group from which the set is created
1425
+
1426
+ OUTPUT:
1427
+
1428
+ If ``return_group=False``, the function return only the relative difference
1429
+ set. Otherwise, it returns a tuple containing the group and the set.
1430
+
1431
+ EXAMPLES::
1432
+
1433
+ sage: from sage.combinat.designs.difference_family import relative_difference_set_from_m_sequence
1434
+ sage: relative_difference_set_from_m_sequence(2, 4, # random # needs sage.modules sage.rings.finite_rings
1435
+ ....: return_group=True)
1436
+ (Additive abelian group isomorphic to Z/15,
1437
+ [(0), (4), (5), (6), (7), (9), (11), (12)])
1438
+ sage: relative_difference_set_from_m_sequence(8, 2, check=False) # random # needs sage.modules sage.rings.finite_rings
1439
+ [(0), (6), (30), (40), (41), (44), (56), (61)]
1440
+ sage: relative_difference_set_from_m_sequence(6, 2) # needs sage.modules
1441
+ Traceback (most recent call last):
1442
+ ...
1443
+ ValueError: q must be a prime power
1444
+
1445
+ TESTS::
1446
+
1447
+ sage: from sage.combinat.designs.difference_family import is_relative_difference_set, _get_submodule_of_order
1448
+ sage: q, N = 5, 3
1449
+ sage: G, D = relative_difference_set_from_m_sequence(q, N, check=False, # needs sage.modules sage.rings.finite_rings
1450
+ ....: return_group=True)
1451
+ sage: H = _get_submodule_of_order(G, q-1) # needs sage.modules sage.rings.finite_rings
1452
+ sage: is_relative_difference_set(D, G, H, # needs sage.modules sage.rings.finite_rings
1453
+ ....: ((q^N-1)//(q-1), q-1, q^(N-1), q^(N-2)))
1454
+ True
1455
+ sage: q, N = 13, 2
1456
+ sage: G, D = relative_difference_set_from_m_sequence(q, N, check=False, # needs sage.modules sage.rings.finite_rings
1457
+ ....: return_group=True)
1458
+ sage: H = _get_submodule_of_order(G, q-1) # needs sage.modules sage.rings.finite_rings
1459
+ sage: is_relative_difference_set(D, G, H, # needs sage.modules sage.rings.finite_rings
1460
+ ....: ((q^N-1)//(q-1), q-1, q^(N-1), q^(N-2)))
1461
+ True
1462
+ """
1463
+ from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroup
1464
+
1465
+ if not is_prime_power(q):
1466
+ raise ValueError('q must be a prime power')
1467
+ if N < 2:
1468
+ raise ValueError('N must be at least 2')
1469
+
1470
+ m_seq = _create_m_sequence(q, N, check=False)
1471
+ period = q**N - 1
1472
+ G = AdditiveAbelianGroup([period])
1473
+
1474
+ set1 = [i for i in G if m_seq[i[0]] == 1]
1475
+
1476
+ if check:
1477
+ H = _get_submodule_of_order(G, q-1)
1478
+ assert is_relative_difference_set(set1, G, H, (period // (q-1), q - 1, q**(N-1), q**(N-2)))
1479
+
1480
+ if return_group:
1481
+ return G, set1
1482
+ return set1
1483
+
1484
+
1485
+ def relative_difference_set_from_homomorphism(q, N, d, check=True, return_group=False):
1486
+ r"""
1487
+ Construct `R((q^N-1)/(q-1), n, q^{N-1}, q^{N-2}d)` where `nd = q-1`.
1488
+
1489
+ Given a prime power `q`, a number `N \ge 2` and integers `d` such that `d | q-1` we create the
1490
+ relative difference set using the construction from Corollary 5.1.1 of [EB1966]_.
1491
+
1492
+ INPUT:
1493
+
1494
+ - ``q`` -- a prime power
1495
+ - ``N`` -- integer greater than 1
1496
+ - ``d`` -- integer which divides `q-1`
1497
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the
1498
+ result is a relative difference set before returning it
1499
+ - ``return_group`` -- boolean (default: ``False``); if ``True``, the function
1500
+ will also return the group from which the set is created
1501
+
1502
+ OUTPUT:
1503
+
1504
+ If ``return_group=False``, the function return only the relative difference
1505
+ set. Otherwise, it returns a tuple containing the group and the set.
1506
+
1507
+ EXAMPLES::
1508
+
1509
+ sage: from sage.combinat.designs.difference_family import relative_difference_set_from_homomorphism
1510
+ sage: relative_difference_set_from_homomorphism(7, 2, 3) # random # needs sage.modules sage.rings.finite_rings
1511
+ [(0), (3), (4), (2), (13), (7), (14)]
1512
+ sage: relative_difference_set_from_homomorphism(9, 2, 4, # random # needs sage.modules sage.rings.finite_rings
1513
+ ....: check=False, return_group=True)
1514
+ (Additive abelian group isomorphic to Z/80,
1515
+ [(0), (4), (6), (13), (7), (12), (15), (8), (9)])
1516
+ sage: relative_difference_set_from_homomorphism(9, 2, 5) # needs sage.modules sage.rings.finite_rings
1517
+ Traceback (most recent call last):
1518
+ ...
1519
+ ValueError: q-1 must be a multiple of d
1520
+
1521
+ TESTS::
1522
+
1523
+ sage: from sage.combinat.designs.difference_family import is_relative_difference_set, _get_submodule_of_order
1524
+ sage: q, N, d = 11, 2, 5
1525
+ sage: G, D = relative_difference_set_from_homomorphism(q, N, d, check=False, # needs sage.modules sage.rings.finite_rings
1526
+ ....: return_group=True)
1527
+ sage: H = _get_submodule_of_order(G, (q-1)//d) # needs sage.modules sage.rings.finite_rings
1528
+ sage: is_relative_difference_set(D, G, H, # needs sage.modules sage.rings.finite_rings
1529
+ ....: ((q**N-1)//(q-1), (q-1)//d, q**(N-1), q**(N-2)*d))
1530
+ True
1531
+ sage: q, N, d = 9, 2, 4
1532
+ sage: G, D = relative_difference_set_from_homomorphism(q, N, d, check=False, # needs sage.modules sage.rings.finite_rings
1533
+ ....: return_group=True)
1534
+ sage: H = _get_submodule_of_order(G, (q-1)//d) # needs sage.modules sage.rings.finite_rings
1535
+ sage: is_relative_difference_set(D, G, H, # needs sage.modules sage.rings.finite_rings
1536
+ ....: ((q**N-1)//(q-1), (q-1)//d, q**(N-1), q**(N-2)*d))
1537
+ True
1538
+ """
1539
+ from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroup
1540
+
1541
+ if not is_prime_power(q):
1542
+ raise ValueError('q must be a prime power')
1543
+ if N < 2:
1544
+ raise ValueError('N must be at least 2')
1545
+ if (q-1) % d != 0:
1546
+ raise ValueError('q-1 must be a multiple of d')
1547
+
1548
+ G = AdditiveAbelianGroup([q**N - 1])
1549
+ K = _get_submodule_of_order(G, d)
1550
+ assert K is not None, 'Could not find kernel'
1551
+
1552
+ G2 = G/K
1553
+
1554
+ theta = G.hom([G2.gen(0)], G2)
1555
+ diff_set = relative_difference_set_from_m_sequence(q, N, check=False)
1556
+ second_diff_set = [theta(x) for x in diff_set]
1557
+
1558
+ if check:
1559
+ H = _get_submodule_of_order(G2, (q-1) // d)
1560
+ assert is_relative_difference_set(second_diff_set, G2, H, ((q**N-1) // (q-1), (q-1) // d, q**(N-1), q**(N-2) * d))
1561
+
1562
+ if return_group:
1563
+ return G2, second_diff_set
1564
+ return second_diff_set
1565
+
1566
+
1567
+ def is_relative_difference_set(R, G, H, params, verbose=False):
1568
+ r"""
1569
+ Check if ``R`` is a difference set of ``G`` relative to ``H``, with the given parameters.
1570
+
1571
+ This function checks that `G`, `H` and `R` have the orders specified in the parameters, and
1572
+ that `R` satisfies the definition of relative difference set (from [EB1966]_): the collection of
1573
+ differences `r-s`, `r,s \in R`, `r \neq s` contains only elements of `G` which are not in `H`, and contains
1574
+ every such element exactly `d` times.
1575
+
1576
+ INPUT:
1577
+
1578
+ - ``R`` -- list; the relative diffeence set of length `k`
1579
+ - ``G`` -- an additive abelian group of order `mn`
1580
+ - ``H`` -- list; a submodule of ``G`` of order `n`
1581
+ - ``params`` -- tuple in the form `(m, n, k, d)`
1582
+ - ``verbose`` -- boolean (default: ``False``); if ``True``, the function
1583
+ will be verbose when the sequences do not satisfy the constraints
1584
+
1585
+ EXAMPLES::
1586
+
1587
+ sage: from sage.combinat.designs.difference_family import _get_submodule_of_order, relative_difference_set_from_m_sequence, is_relative_difference_set
1588
+ sage: q, N = 5, 2
1589
+ sage: params = ((q^N-1) // (q-1), q - 1, q^(N-1), q^(N-2))
1590
+ sage: G, R = relative_difference_set_from_m_sequence(q, N, return_group=True) # needs sage.libs.pari sage.modules
1591
+ sage: H = _get_submodule_of_order(G, q - 1) # needs sage.libs.pari sage.modules
1592
+ sage: is_relative_difference_set(R, G, H, params) # needs sage.libs.pari sage.modules
1593
+ True
1594
+
1595
+ If we pass the ``verbose`` argument, the function will explain why it failed::
1596
+
1597
+ sage: R2 = [G[1], G[2], G[3], G[5], G[6]] # needs sage.libs.pari sage.modules
1598
+ sage: is_relative_difference_set(R2, G, H, params, verbose=True) # needs sage.libs.pari sage.modules
1599
+ There is a value in the difference set which is not repeated d times
1600
+ False
1601
+ """
1602
+ m, n, k, d = params
1603
+ if G.order() != m * n:
1604
+ if verbose:
1605
+ print('Incorrect order of G:', G.order())
1606
+ return False
1607
+
1608
+ if H.order() != n:
1609
+ if verbose:
1610
+ print('Incorect order of H:', H.order())
1611
+
1612
+ if len(R) != k:
1613
+ if verbose:
1614
+ print('Length of R not correct:', len(R))
1615
+ return False
1616
+
1617
+ diff_set = {}
1618
+ for el1 in R:
1619
+ for el2 in R:
1620
+ if el1 != el2:
1621
+ idx = el1 - el2
1622
+ if idx not in diff_set:
1623
+ diff_set[idx] = 0
1624
+ diff_set[idx] += 1
1625
+ values = [diff_set[x] for x in diff_set]
1626
+ if max(values) != d or min(values) != d:
1627
+ if verbose:
1628
+ print('There is a value in the difference set which is not repeated d times')
1629
+ return False
1630
+
1631
+ for el in G:
1632
+ if el in H and el in diff_set:
1633
+ if verbose:
1634
+ print('An element of G is present in both the difference set and in H')
1635
+ return False
1636
+ if el not in H and el not in diff_set:
1637
+ if verbose:
1638
+ print('An element of G is not present in either one of H or the difference set')
1639
+ return False
1640
+
1641
+ return True
1642
+
1643
+
1644
+ def is_supplementary_difference_set(Ks, v=None, lmbda=None, G=None, verbose=False):
1645
+ r"""
1646
+ Check that the sets in ``Ks`` are `n-\{v; k_1, ..., k_n; \lambda \}` supplementary
1647
+ difference sets over group ``G`` of order ``v``.
1648
+
1649
+ From the definition in [Spe1975]_: let `S_1, S_2, ..., S_n` be `n` subsets of a group `G` of order `v`
1650
+ such that `|S_i| = k_i`. If, for each `g \in G`, `g \neq 0`, the total number of solutions of `a_i - a'_i = g`, with
1651
+ `a_i, a'_i \in S_i` is `\lambda`, then `S_1, S_2, ..., S_n` are `n-\{v; k_1, ..., k_n; \lambda\}` supplementary difference sets.
1652
+
1653
+ One of the parameters ``v`` or ``G`` must always be specified. If ``G`` is not
1654
+ given, the function will use an ``AdditiveAbelianGroup`` of order ``v``.
1655
+
1656
+ INPUT:
1657
+
1658
+ - ``Ks`` -- list of sets to be checked
1659
+ - ``v`` -- integer; the parameter `v` of the supplementary difference sets
1660
+ - ``lmbda`` -- integer; the parameter `\lambda` of the supplementary difference sets
1661
+ - ``G`` -- a group of order `v`
1662
+ - ``verbose`` -- boolean (default: ``False``); if ``True``, the function will
1663
+ be verbose when the sets do not satisfy the constraints
1664
+
1665
+ EXAMPLES::
1666
+
1667
+ sage: from sage.combinat.designs.difference_family import supplementary_difference_set_from_rel_diff_set, is_supplementary_difference_set
1668
+ sage: G, [S1, S2, S3, S4] = supplementary_difference_set_from_rel_diff_set(17) # needs sage.modules sage.rings.finite_rings
1669
+ sage: is_supplementary_difference_set([S1, S2, S3, S4], lmbda=16, G=G) # needs sage.modules sage.rings.finite_rings
1670
+ True
1671
+
1672
+ The parameter ``v`` can be given instead of ``G``::
1673
+
1674
+ sage: is_supplementary_difference_set([S1, S2, S3, S4], v=16, lmbda=16) # needs sage.modules sage.rings.finite_rings
1675
+ True
1676
+ sage: is_supplementary_difference_set([S1, S2, S3, S4], v=20, lmbda=16) # needs sage.modules sage.rings.finite_rings
1677
+ False
1678
+
1679
+ If ``verbose=True``, the function will be verbose::
1680
+
1681
+ sage: is_supplementary_difference_set([S1, S2, S3, S4], lmbda=14, G=G, # needs sage.modules sage.rings.finite_rings
1682
+ ....: verbose=True)
1683
+ Number of pairs with difference (1) is 16, but lambda is 14
1684
+ False
1685
+
1686
+ TESTS::
1687
+
1688
+ sage: # needs sage.modules sage.rings.finite_rings
1689
+ sage: is_supplementary_difference_set([[1], [1]], lmbda=0, G=Zmod(3))
1690
+ True
1691
+ sage: is_supplementary_difference_set([S1, S2, S3, S4], v=17, lmbda=16, G=G)
1692
+ False
1693
+ sage: is_supplementary_difference_set([S1, S2, S3, S4], G=G)
1694
+ True
1695
+ sage: is_supplementary_difference_set([S1, S2, S3, S4], lmbda=16)
1696
+ Traceback (most recent call last):
1697
+ ...
1698
+ ValueError: one of G or v must be specified
1699
+
1700
+ .. SEEALSO::
1701
+
1702
+ :func:`supplementary_difference_set_from_rel_diff_set`
1703
+ """
1704
+
1705
+ if G is None and v is None:
1706
+ raise ValueError('one of G or v must be specified')
1707
+
1708
+ if G is None:
1709
+ from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroup
1710
+ G = AdditiveAbelianGroup([v])
1711
+
1712
+ if v is not None and G.order() != v:
1713
+ if verbose:
1714
+ print(f'G has order {G.order()}, but it should be {v}')
1715
+ return False
1716
+
1717
+ differences_counter = {el: 0 for el in G}
1718
+ for K in Ks:
1719
+ for el1 in K:
1720
+ for el2 in K:
1721
+ diff = G(el1) - G(el2)
1722
+ differences_counter[diff] += 1
1723
+
1724
+ for key, diff in differences_counter.items():
1725
+ if key == 0:
1726
+ continue
1727
+ if lmbda is None:
1728
+ lmbda = diff
1729
+ if diff != lmbda:
1730
+ if verbose:
1731
+ print(f'Number of pairs with difference {key} is {diff}, but lambda is {lmbda}')
1732
+ return False
1733
+
1734
+ return True
1735
+
1736
+
1737
+ def supplementary_difference_set_from_rel_diff_set(q, existence=False, check=True):
1738
+ r"""
1739
+ Construct `4-\{2v; v, v+1, v, v; 2v\}` supplementary difference sets where `q=2v+1`.
1740
+
1741
+ The sets are created from relative difference sets as detailed in Theorem 3.3 of [Spe1975]_. this construction
1742
+ requires that `q` is an odd prime power and that there exists `s \ge 0` such that `(q-(2^{s+1}+1))/2^{s+1}` is
1743
+ an odd prime power.
1744
+
1745
+ Note that the construction from [Spe1975]_ states that the resulting sets are `4-\{2v; v+1, v, v, v; 2v\}`
1746
+ supplementary difference sets. However, the implementation of that construction returns
1747
+ `4-\{2v; v, v+1, v, v; 2v\}` supplementary difference sets. This is not important, since the supplementary
1748
+ difference sets are not ordered.
1749
+
1750
+ INPUT:
1751
+
1752
+ - ``q`` -- an odd prime power
1753
+ - ``existence`` -- boolean (default: ``False``); if ``True``, only check
1754
+ whether the supplementary difference sets can be constructed
1755
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the sets
1756
+ are supplementary difference sets before returning them
1757
+
1758
+ OUTPUT:
1759
+
1760
+ If ``existence=False``, the function returns the 4 sets (containing integers),
1761
+ or raises an error if ``q`` does not satisfy the constraints.
1762
+ If ``existence=True``, the function returns a boolean representing whether
1763
+ supplementary difference sets can be constructed.
1764
+
1765
+ EXAMPLES::
1766
+
1767
+ sage: from sage.combinat.designs.difference_family import supplementary_difference_set_from_rel_diff_set
1768
+ sage: supplementary_difference_set_from_rel_diff_set(17) #random # needs sage.libs.pari
1769
+ (Additive abelian group isomorphic to Z/16,
1770
+ [[(1), (5), (6), (7), (9), (13), (14), (15)],
1771
+ [(0), (2), (3), (5), (6), (10), (11), (13), (14)],
1772
+ [(0), (1), (2), (3), (5), (6), (7), (12)],
1773
+ [(0), (2), (3), (5), (6), (7), (9), (12)]])
1774
+
1775
+ If ``existence=True``, the function returns a boolean::
1776
+
1777
+ sage: supplementary_difference_set_from_rel_diff_set(7, existence=True)
1778
+ False
1779
+ sage: supplementary_difference_set_from_rel_diff_set(17, existence=True)
1780
+ True
1781
+
1782
+ TESTS::
1783
+
1784
+ sage: # needs sage.libs.pari
1785
+ sage: from sage.combinat.designs.difference_family import is_supplementary_difference_set
1786
+ sage: G, sets = supplementary_difference_set_from_rel_diff_set(17, check=False)
1787
+ sage: is_supplementary_difference_set(sets, lmbda=16, G=G)
1788
+ True
1789
+ sage: G, sets = supplementary_difference_set_from_rel_diff_set(9, check=False)
1790
+ sage: is_supplementary_difference_set(sets, lmbda=8, G=G)
1791
+ True
1792
+ sage: supplementary_difference_set_from_rel_diff_set(7)
1793
+ Traceback (most recent call last):
1794
+ ...
1795
+ ValueError: There is no s for which m-1 is an odd prime power
1796
+ sage: supplementary_difference_set_from_rel_diff_set(8)
1797
+ Traceback (most recent call last):
1798
+ ...
1799
+ ValueError: q must be an odd prime power
1800
+ sage: supplementary_difference_set_from_rel_diff_set(8, existence=True)
1801
+ False
1802
+ sage: supplementary_difference_set_from_rel_diff_set(7, existence=True)
1803
+ False
1804
+ sage: supplementary_difference_set_from_rel_diff_set(1, existence=True)
1805
+ False
1806
+
1807
+ Check that the function works even when s > 1::
1808
+
1809
+ sage: G, sets = supplementary_difference_set_from_rel_diff_set(353, check=False) # long time, needs sage.libs.pari
1810
+ sage: is_supplementary_difference_set(sets, lmbda=352, G=G) # long time, needs sage.libs.pari
1811
+ True
1812
+
1813
+ .. SEEALSO::
1814
+
1815
+ :func:`is_supplementary_difference_set`
1816
+ """
1817
+ s = 0
1818
+ m = -1
1819
+
1820
+ while q > 2**(s+1) and (q-1) % 2**(s+1) == 0:
1821
+ prime_pow = (q-1)//2**(s+1) - 1
1822
+ if is_prime_power(prime_pow) and prime_pow % 2 == 1:
1823
+ m = (q - (2**(s+1) + 1)) // 2**(s+1) + 1
1824
+ break
1825
+ s += 1
1826
+
1827
+ if existence:
1828
+ return is_prime_power(q) and q % 2 == 1 and m != -1
1829
+
1830
+ if not is_prime_power(q) or q % 2 != 1:
1831
+ raise ValueError('q must be an odd prime power')
1832
+ if m == -1:
1833
+ raise ValueError('There is no s for which m-1 is an odd prime power')
1834
+
1835
+ set1 = relative_difference_set_from_homomorphism(m - 1, 2, (m-2) // 2, check=False)
1836
+
1837
+ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
1838
+ P = PolynomialRing(ZZ, 'x')
1839
+
1840
+ # Compute psi3, psi4
1841
+ hall = 0
1842
+ for d in set1:
1843
+ hall += P.monomial(d[0])
1844
+
1845
+ def get_T(k):
1846
+ T = P.monomial(0) - 1
1847
+ for i in range(k):
1848
+ T += P.monomial(i)
1849
+ return T
1850
+
1851
+ modulo = P.monomial(2*m) - 1
1852
+
1853
+ diff = get_T(2*m) - (1+P.monomial(m))*hall
1854
+ diff = diff.mod(modulo)
1855
+ exp1, exp2 = diff.exponents()
1856
+ a = (exp1+exp2-m) // 2
1857
+
1858
+ psi3 = (P.monomial(a) + hall).mod(modulo)
1859
+ psi4 = (P.monomial(a+m) + hall).mod(modulo)
1860
+
1861
+ for i in range(s):
1862
+ m_start = 2**i * m
1863
+ psi3, psi4 = (psi3(P.monomial(2)) + P.monomial(1)*psi4(P.monomial(2))).mod(P.monomial(4*m_start)-1), \
1864
+ (psi3(P.monomial(2)) + P.monomial(1)*(get_T(2*m_start)(P.monomial(2)) - psi4(P.monomial(2)))).mod(P.monomial(4*m_start)-1)
1865
+
1866
+ # Construction of psi1, psi2
1867
+ G2, set2 = relative_difference_set_from_m_sequence(q, 2, check=False, return_group=True)
1868
+ s3 = get_fixed_relative_difference_set(G2, set2)
1869
+
1870
+ phi_exps = []
1871
+ for i in range(len(s3)):
1872
+ for j in range(i+1, len(s3)):
1873
+ diff = s3[i] - s3[j]
1874
+ if diff % (q-1) == 0 and diff % (q**2-1) != 0:
1875
+ phi_exps.append(s3[i])
1876
+
1877
+ exps1 = [(x+1)//2 for x in phi_exps if x % 2 == 1]
1878
+ exps2 = [x//2 for x in phi_exps if x % 2 == 0]
1879
+
1880
+ theta1 = 0
1881
+ for exp in exps1:
1882
+ theta1 += P.monomial(exp)
1883
+ theta1 = theta1.mod(P.monomial(q-1)-1)
1884
+
1885
+ theta2 = 0
1886
+ for exp in exps2:
1887
+ theta2 += P.monomial(exp)
1888
+ theta2 = theta2.mod(P.monomial(q-1) - 1)
1889
+
1890
+ psi1 = ((1 + P.monomial((q-1)//2)) * theta1).mod(P.monomial(q-1) - 1)
1891
+ psi2 = (1 + (1 + P.monomial((q-1)//2)) * theta2).mod(P.monomial(q-1) - 1)
1892
+
1893
+ from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroup
1894
+ G = AdditiveAbelianGroup([q-1])
1895
+ K1 = [G[x] for x in psi1.exponents()]
1896
+ K2 = [G[x] for x in psi2.exponents()]
1897
+ K3 = [G[x] for x in psi3.exponents()]
1898
+ K4 = [G[x] for x in psi4.exponents()]
1899
+
1900
+ if check:
1901
+ assert is_supplementary_difference_set([K1, K2, K3, K4], lmbda=q-1, G=G)
1902
+
1903
+ return G, [K1, K2, K3, K4]
1904
+
1905
+
1906
+ def supplementary_difference_set(q, existence=False, check=True):
1907
+ r"""
1908
+ Construct `4-\{2v; v, v+1, v, v; 2v\}` supplementary difference sets where `q=2v+1`.
1909
+
1910
+ This is a deprecated version of :func:`supplementary_difference_set_from_rel_diff_set`,
1911
+ please use that instead.
1912
+ """
1913
+ from sage.misc.superseded import deprecation
1914
+ deprecation(35211, 'This function is deprecated, please use supplementary_difference_set_from_rel_diff_set instead.')
1915
+
1916
+ if existence:
1917
+ return supplementary_difference_set_from_rel_diff_set(q, existence=True)
1918
+ _, s = supplementary_difference_set_from_rel_diff_set(q, check=check)
1919
+ return s
1920
+
1921
+
1922
+ def get_fixed_relative_difference_set(G, rel_diff_set, as_elements=False):
1923
+ r"""
1924
+ Construct an equivalent relative difference set fixed by the size of the set.
1925
+
1926
+ Given a relative difference set `R(q+1, q-1, q, 1)`, it is possible to find a translation
1927
+ of this set fixed by `q` (see Section 3 of [Spe1975]_). We say that a set is fixed by `t` if
1928
+ `\{td | d\in R\}= R`.
1929
+
1930
+ In addition, the set returned by this function will contain the element `0`. This is needed in the
1931
+ construction of supplementary difference sets (see :func:`supplementary_difference_set_from_rel_diff_set`).
1932
+
1933
+ INPUT:
1934
+
1935
+ - ``G`` -- a group, of which ``rel_diff_set`` is a subset
1936
+ - ``rel_diff_set`` -- the relative difference set
1937
+ - ``as_elements`` -- boolean (default: ``False``); if ``True``, the list
1938
+ returned will contain elements of the abelian group (this may slow down
1939
+ the computation considerably)
1940
+
1941
+ OUTPUT:
1942
+
1943
+ By default, this function returns the set as a list of integers. However, if
1944
+ ``as_elements=True`` it will return the set as a list containing elements of
1945
+ the abelian group.
1946
+ If no such set can be found, the function will raise an error.
1947
+
1948
+ EXAMPLES::
1949
+
1950
+ sage: from sage.combinat.designs.difference_family import relative_difference_set_from_m_sequence, get_fixed_relative_difference_set
1951
+ sage: G, s1 = relative_difference_set_from_m_sequence(5, 2, return_group=True) # needs sage.libs.pari sage.modules
1952
+ sage: get_fixed_relative_difference_set(G, s1) # random # needs sage.libs.pari sage.modules
1953
+ [2, 10, 19, 23, 0]
1954
+
1955
+ If ``as_elements=True``, the result will contain elements of the group::
1956
+
1957
+ sage: get_fixed_relative_difference_set(G, s1, as_elements=True) # random # needs sage.libs.pari sage.modules
1958
+ [(2), (10), (19), (23), (0)]
1959
+
1960
+ TESTS::
1961
+
1962
+ sage: # needs sage.libs.pari sage.modules
1963
+ sage: from sage.combinat.designs.difference_family import is_fixed_relative_difference_set
1964
+ sage: G, s1 = relative_difference_set_from_m_sequence(5, 2, return_group=True)
1965
+ sage: s2 = get_fixed_relative_difference_set(G, s1, as_elements=True)
1966
+ sage: is_fixed_relative_difference_set(s2, len(s2))
1967
+ True
1968
+ sage: G, s1 = relative_difference_set_from_m_sequence(9, 2, return_group=True)
1969
+ sage: s2 = get_fixed_relative_difference_set(G, s1, as_elements=True)
1970
+ sage: is_fixed_relative_difference_set(s2, len(s2))
1971
+ True
1972
+ sage: type(s2[0])
1973
+ <class 'sage.groups.additive_abelian.additive_abelian_group.AdditiveAbelianGroup_fixed_gens_with_category.element_class'>
1974
+ sage: s2 = get_fixed_relative_difference_set(G, s1)
1975
+ sage: type(s2[0])
1976
+ <class 'sage.rings.integer.Integer'>
1977
+ """
1978
+ q = len(rel_diff_set)
1979
+
1980
+ s2 = None
1981
+ for el in G:
1982
+ fixed_set = [el+x for x in rel_diff_set]
1983
+ if is_fixed_relative_difference_set(fixed_set, q):
1984
+ s2 = fixed_set
1985
+ break
1986
+ assert s2 is not None, 'Cannot find fixed translation of the set'
1987
+
1988
+ s3 = None
1989
+ for i in range(G.order()):
1990
+ temp = [((q+1)*i+x[0]) % G.order() for x in s2]
1991
+ if 0 in temp:
1992
+ s3 = temp
1993
+ break
1994
+ assert s3 is not None, 'Cannot find fixed set containing 0'
1995
+
1996
+ if as_elements:
1997
+ return [G[x] for x in s3]
1998
+ return s3
1999
+
2000
+
2001
+ def is_fixed_relative_difference_set(R, q):
2002
+ r"""
2003
+ Check if the relative difference set ``R`` is fixed by ``q``.
2004
+
2005
+ A relative difference set `R` is fixed by `q` if `\{qd | d \in R\}= R` (see Section 3 of [Spe1975]_).
2006
+
2007
+ INPUT:
2008
+
2009
+ - ``R`` -- list containing elements of an abelian group; the relative
2010
+ difference set
2011
+ - ``q`` -- integer
2012
+
2013
+ EXAMPLES::
2014
+
2015
+ sage: # needs sage.modules
2016
+ sage: from sage.combinat.designs.difference_family import relative_difference_set_from_m_sequence, get_fixed_relative_difference_set, is_fixed_relative_difference_set
2017
+ sage: G, s1 = relative_difference_set_from_m_sequence(7, 2, return_group=True) # needs sage.libs.pari
2018
+ sage: s2 = get_fixed_relative_difference_set(G, s1, as_elements=True) # needs sage.libs.pari
2019
+ sage: is_fixed_relative_difference_set(s2, len(s2)) # needs sage.libs.pari
2020
+ True
2021
+ sage: G = AdditiveAbelianGroup([15])
2022
+ sage: s3 = [G[1], G[2], G[3], G[4]]
2023
+ sage: is_fixed_relative_difference_set(s3, len(s3))
2024
+ False
2025
+
2026
+ If the relative difference set does not contain elements of the group, the method returns false::
2027
+
2028
+ sage: G, s1 = relative_difference_set_from_m_sequence(7, 2, return_group=True) # needs sage.libs.pari sage.modules
2029
+ sage: s2 = get_fixed_relative_difference_set(G, s1, as_elements=False) # needs sage.libs.pari sage.modules
2030
+ sage: is_fixed_relative_difference_set(s2, len(s2)) # needs sage.libs.pari sage.modules
2031
+ False
2032
+ """
2033
+ for el in R:
2034
+ if q * el not in R:
2035
+ return False
2036
+ return True
2037
+
2038
+
2039
+ def skew_supplementary_difference_set_over_polynomial_ring(n, existence=False, check=True):
2040
+ r"""
2041
+ Construct skew supplementary difference sets over a polynomial ring of order ``n``.
2042
+
2043
+ The skew supplementary difference sets for `n=81, 169` are taken from [Djo1994a]_.
2044
+
2045
+ INPUT:
2046
+
2047
+ - ``n`` -- integer; the parameter of the supplementary difference sets
2048
+ - ``existence`` -- boolean (default: ``False``); if ``True``, only check
2049
+ whether the supplementary difference sets can be constructed
2050
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the sets
2051
+ are supplementary difference sets with `S_1` skew before returning them;
2052
+ setting this parameter to ``False`` may speed up the computation considerably
2053
+
2054
+ OUTPUT:
2055
+
2056
+ If ``existence=False``, the function returns a Polynomial Ring of order ``n``
2057
+ and a list containing 4 sets, or raises an error if data for the given ``n``
2058
+ is not available.
2059
+ If ``existence=True``, the function returns a boolean representing whether
2060
+ skew supplementary difference sets can be constructed.
2061
+
2062
+ EXAMPLES::
2063
+
2064
+ sage: from sage.combinat.designs.difference_family import skew_supplementary_difference_set_over_polynomial_ring
2065
+ sage: G, [S1, S2, S3, S4] = skew_supplementary_difference_set_over_polynomial_ring(81) # needs sage.libs.pari
2066
+
2067
+ If ``existence=True``, the function returns a boolean::
2068
+
2069
+ sage: skew_supplementary_difference_set_over_polynomial_ring(81, existence=True)
2070
+ True
2071
+ sage: skew_supplementary_difference_set_over_polynomial_ring(17, existence=True)
2072
+ False
2073
+
2074
+ TESTS::
2075
+
2076
+ sage: skew_supplementary_difference_set_over_polynomial_ring(7)
2077
+ Traceback (most recent call last):
2078
+ ...
2079
+ NotImplementedError: skew SDS of order 7 not yet implemented
2080
+ """
2081
+ data = {
2082
+ 81: (3, lambda x: x**4 - x**3 - 1, 16, 5,
2083
+ [1, 2, 4, 6, 8, 10, 12, 14], [1, 2, 3, 4, 10, 11, 13],
2084
+ [4, 5, 6, 8, 12, 13, 14], [2, 4, 5, 6, 7, 11, 12, 13, 15]),
2085
+ 169: (13, lambda x: x**2 - 4*x + 6, 24, 7,
2086
+ [0, 2, 5, 7, 9, 10, 12, 15, 16, 18, 21, 22], [0, 1, 2, 7, 8, 9, 13, 14, 18, 20, 23],
2087
+ [1, 4, 6, 7, 9, 14, 16, 17, 20, 21, 23], [3, 5, 6, 9, 10, 12, 13, 14, 15, 17, 20])
2088
+ }
2089
+
2090
+ if existence:
2091
+ return n in data
2092
+
2093
+ if n not in data:
2094
+ raise NotImplementedError(f'skew SDS of order {n} not yet implemented')
2095
+
2096
+ mod, poly, exp, order, ind1, ind2, ind3, ind4 = data[n]
2097
+
2098
+ from sage.rings.finite_rings.integer_mod_ring import Zmod
2099
+
2100
+ Z3 = Zmod(mod)
2101
+ R = ZZ['x']
2102
+ x = R.gen()
2103
+ F = Z3.extension(poly(x))
2104
+
2105
+ H = [F.gen() ** (exp * i) for i in range(order)]
2106
+
2107
+ cosets = []
2108
+ for i in range((n - 1) // (2 * order)):
2109
+ cosets.append([F.gen()**i * el for el in H])
2110
+ cosets.append([-F.gen()**i * el for el in H])
2111
+
2112
+ def generate_set(index_set, cosets):
2113
+ return sum((cosets[idx] for idx in index_set), [])
2114
+
2115
+ S1 = generate_set(ind1, cosets)
2116
+ S2 = generate_set(ind2, cosets)
2117
+ S3 = generate_set(ind3, cosets)
2118
+ S4 = generate_set(ind4, cosets)
2119
+
2120
+ if check:
2121
+ lmbda = len(S1) + len(S2) + len(S3) + len(S4) - n
2122
+ assert is_supplementary_difference_set([S1, S2, S3, S4], lmbda=lmbda, G=F)
2123
+ assert _is_skew_set(F, S1)
2124
+
2125
+ return F, [S1, S2, S3, S4]
2126
+
2127
+
2128
+ def skew_supplementary_difference_set_with_paley_todd(n, existence=False, check=True):
2129
+ r"""
2130
+ Construct `4-\{n; n_1, n_2, n_3, n_4; \lambda\}` skew supplementary difference sets where `S_1` is the Paley-Todd difference set.
2131
+
2132
+ The skew SDS returned have the property that `n_1 + n_2 + n_3 + n_4 = n + \lambda`.
2133
+
2134
+ This construction is described in [DK2016]_. The function contains, for each
2135
+ value of `n`, a set `H` containing integers modulo `n`, and four sets `J, K, L`.
2136
+ Then, these are used to construct `(n; k_2, k_3, k_4; \lambda_2)` difference family,
2137
+ with `\lambda_2 = k_2 + k_3 + k_4 + (3n - 1) / 4`. Finally, these sets together
2138
+ with the Paley-Todd difference set form a skew supplementary difference set.
2139
+
2140
+ INPUT:
2141
+
2142
+ - ``n`` -- integer; the parameter of the supplementary difference set
2143
+ - ``existence`` -- boolean (default: ``False``); if ``True``, only check
2144
+ whether the supplementary difference sets can be constructed
2145
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the sets
2146
+ are supplementary difference sets with `S_1` skew before returning them;
2147
+ setting this parameter to ``False`` may speed up the computation considerably
2148
+
2149
+ OUTPUT:
2150
+
2151
+ If ``existence=False``, the function returns the group G of integers modulo
2152
+ ``n`` and a list containing 4 sets, or raises an error if data for the given
2153
+ ``n`` is not available.
2154
+ If ``existence=True``, the function returns a boolean representing whether
2155
+ skew supplementary difference sets can be constructed.
2156
+
2157
+ EXAMPLES::
2158
+
2159
+ sage: from sage.combinat.designs.difference_family import skew_supplementary_difference_set_with_paley_todd
2160
+ sage: G, [S1, S2, S3, S4] = skew_supplementary_difference_set_with_paley_todd(239)
2161
+
2162
+ If existence is ``True``, the function returns a boolean::
2163
+
2164
+ sage: skew_supplementary_difference_set_with_paley_todd(239, existence=True)
2165
+ True
2166
+ sage: skew_supplementary_difference_set_with_paley_todd(17, existence=True)
2167
+ False
2168
+
2169
+ TESTS::
2170
+
2171
+ sage: skew_supplementary_difference_set_with_paley_todd(7)
2172
+ Traceback (most recent call last):
2173
+ ...
2174
+ NotImplementedError: data for skew SDS of order 7 not yet implemented
2175
+ """
2176
+ H_db = {
2177
+ 239: [1, 10, 24, 44, 98, 100, 201],
2178
+ }
2179
+
2180
+ indices = {
2181
+ 239: [[1, 3, 5, 6, 15, 17, 19, 28, 34, 38, 39, 57, 58, 63, 85, 95, 107],
2182
+ [1, 3, 4, 5, 15, 16, 17, 18, 19, 21, 23, 29, 35, 45, 58, 63],
2183
+ [0, 1, 4, 6, 7, 8, 13, 16, 18, 34, 35, 45, 47, 58, 63, 95]],
2184
+ }
2185
+
2186
+ if existence:
2187
+ return n in H_db
2188
+
2189
+ if n not in H_db:
2190
+ raise NotImplementedError(f'data for skew SDS of order {n} not yet implemented')
2191
+
2192
+ G = Zmod(n)
2193
+ H = {G(el) for el in H_db[n]}
2194
+
2195
+ def generate_subset(indices, H):
2196
+ return list({el * idx for el in H for idx in indices})
2197
+
2198
+ from sage.arith.misc import quadratic_residues
2199
+
2200
+ S1 = [G(el) for el in quadratic_residues(n) if el != 0]
2201
+ S2 = generate_subset(indices[n][0], H)
2202
+ S3 = generate_subset(indices[n][1], H)
2203
+ S4 = generate_subset(indices[n][2], H)
2204
+
2205
+ if check:
2206
+ lmbda = len(S1) + len(S2) + len(S3) + len(S4) - n
2207
+ assert is_supplementary_difference_set([S1, S2, S3, S4], lmbda=lmbda, G=G)
2208
+ assert _is_skew_set(G, S1)
2209
+
2210
+ return G, [S1, S2, S3, S4]
2211
+
2212
+
2213
+ def _construct_gs_difference_family_from_full(S1, S2, mu):
2214
+ r"""
2215
+ Construct a spin type Goethals-Seidel difference family given the first two
2216
+ sets.
2217
+
2218
+ This construction is described in [Djo2024]_. Given the first two sets
2219
+ `S_1, S_2`, and the multiplier `\mu`, the last two sets are computed as
2220
+ `S_3 = \mu S_2`, `S_4 =\mu S_3`.
2221
+
2222
+ The sets should contain elements of the additive group of integers modulo `n`.
2223
+
2224
+ INPUT:
2225
+
2226
+ - ``S1`` -- list of integer modulo `n`; the first set
2227
+ - ``S2`` -- list of integer modulo `n`; the second set
2228
+
2229
+ OUTPUT:
2230
+
2231
+ The Goethals-Seidel difference family ``(S_1, S_2, S_3, S_4)``.
2232
+
2233
+ TESTS::
2234
+
2235
+ sage: from sage.combinat.designs.difference_family import _construct_gs_difference_family_from_full
2236
+ sage: G = Zmod(9)
2237
+ sage: S1 = list(map(G, [0,3,6]))
2238
+ sage: S2 = list(map(G, [0, 1, 8]))
2239
+ sage: _construct_gs_difference_family_from_full(S1, S2, 4)
2240
+ [[0, 3, 6], [0, 1, 8], [0, 4, 5], [0, 7, 2]]
2241
+ """
2242
+ S3 = [mu * el for el in S2]
2243
+ S4 = [mu * el for el in S3]
2244
+ return [S1, S2, S3, S4]
2245
+
2246
+
2247
+ def _construct_gs_difference_family_from_compact(rep1, rep2, H, mu):
2248
+ r"""
2249
+ Construct a spin type Goethals-Seidel difference family given a compact
2250
+ representation of the first two sets.
2251
+
2252
+ This construction is described in [Djo2024]_. Given a subgroup `H` of a group
2253
+ `G`, and two sets of representatives `rep_1, rep_2` we can construct the first
2254
+ two sets: `S_1 = \bigcup_{x \in rep_1} x H` and `S_2 = \bigcup_{x \in rep_2} x H`.
2255
+ The list ``H`` should contain elements of the additive group of integers
2256
+ modulo `n`.
2257
+
2258
+ The other two sets are constructed using
2259
+ :func:`_construct_gs_difference_family_from_full`.
2260
+
2261
+ INPUT:
2262
+
2263
+ - ``rep1`` -- list of integers; the first set of representatives
2264
+ - ``rep2`` -- list of integers; the second set of representatives
2265
+ - ``H`` -- list of integers modulo `n`; the subgroup used to generate the
2266
+ first two sets
2267
+
2268
+ OUTPUT:
2269
+
2270
+ The Goethals-Seidel difference family ``(S_1, S_2, S_3, S_4)``.
2271
+
2272
+ TESTS::
2273
+
2274
+ sage: from sage.combinat.designs.difference_family import _construct_gs_difference_family_from_compact, is_supplementary_difference_set
2275
+ sage: G = Zmod(61)
2276
+ sage: H = list(map(G, [1, 9, 20, 34, 58]))
2277
+ sage: rep1 = [3, 4, 5, 6, 8, 10]
2278
+ sage: rep2 = [0, 8, 10, 13, 23, 26]
2279
+ sage: [S1, S2, S3, S4] = _construct_gs_difference_family_from_compact(rep1, rep2, H, 13)
2280
+ sage: is_supplementary_difference_set([S1, S2, S3, S4], G=G)
2281
+ True
2282
+ """
2283
+ S1 = set()
2284
+ S2 = set()
2285
+ for el in H:
2286
+ S1 = S1.union({x * el for x in rep1})
2287
+ S2 = S2.union({x * el for x in rep2})
2288
+ S1 = list(S1)
2289
+ S2 = list(S2)
2290
+ return _construct_gs_difference_family_from_full(S1, S2, mu)
2291
+
2292
+
2293
+ def spin_goethals_seidel_difference_family(n, existence=False, check=True):
2294
+ r"""
2295
+ Construct a spin type Goethals-Seidel difference family with parameters
2296
+ `(n; k_1, k_2, k_3, k_4; \lambda)`.
2297
+
2298
+ The construction is described in [Djo2024]_. This function contains, for
2299
+ each value of `n`, either a full representation of `S_1, S_2` together with
2300
+ the multiplier `\mu`, or a subgroup `H`, two sets of representatives, and the
2301
+ multiplier.
2302
+ This data is used to construct the difference family using the functions
2303
+ :func:`_construct_gs_difference_family_from_full` and
2304
+ :func:`_construct_gs_difference_family_from_compact`.
2305
+
2306
+ Additionally, this function also checks if a (skew) difference family can be
2307
+ constructed using :func:`skew_spin_goethals_seidel_difference_family`.
2308
+
2309
+ INPUT:
2310
+
2311
+ - ``n`` -- integer; the parameter of the GS difference family
2312
+ - ``existence`` -- boolean (default: ``False``); if ``True``, only check
2313
+ whether the difference family can be constructed
2314
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the sets
2315
+ are a difference family before returning them;
2316
+ setting this parameter to ``False`` may speed up the computation considerably
2317
+
2318
+ OUTPUT:
2319
+
2320
+ If ``existence=False``, the function returns the group G of integers modulo
2321
+ ``n`` and a list containing 4 sets, or raises an error if data for the given
2322
+ ``n`` is not available.
2323
+ If ``existence=True``, the function returns a boolean representing whether
2324
+ the difference family can be constructed.
2325
+
2326
+ EXAMPLES::
2327
+
2328
+ sage: from sage.combinat.designs.difference_family import spin_goethals_seidel_difference_family
2329
+ sage: G, [S1, S2, S3, S4] = spin_goethals_seidel_difference_family(73)
2330
+
2331
+ If existence is ``True``, the function returns a boolean::
2332
+
2333
+ sage: spin_goethals_seidel_difference_family(73, existence=True)
2334
+ True
2335
+ sage: spin_goethals_seidel_difference_family(5, existence=True)
2336
+ False
2337
+
2338
+ TESTS::
2339
+
2340
+ sage: from sage.combinat.designs.difference_family import is_supplementary_difference_set
2341
+ sage: G, [S1, S2, S3, S4] = spin_goethals_seidel_difference_family(9, check=False)
2342
+ sage: lmbda = len(S1) + len(S2) + len(S3) + len(S4) - 9
2343
+ sage: is_supplementary_difference_set([S1, S2, S3, S4], lmbda=lmbda, G=G)
2344
+ True
2345
+ sage: spin_goethals_seidel_difference_family(5)
2346
+ Traceback (most recent call last):
2347
+ ...
2348
+ NotImplementedError: Data for spin type Goethals Seidel family of order 5 not yet implemented
2349
+ """
2350
+ full_data = {
2351
+ 7: ([0], [0, 1, 6], 2),
2352
+ 9: ([0, 3, 6], [0, 1, 8], 4),
2353
+ 13: ([0, 1, 4, 6], [0, 4, 6, 7, 9], 3),
2354
+ 19: ([4, 6, 9, 10, 13, 15], [0, 1, 5, 8, 9, 10, 11, 13], 7),
2355
+ 21: ([1, 4, 5, 8, 10, 11, 12, 17, 19], [1, 3, 8, 9, 12, 13, 18, 20], 4),
2356
+ 31: ([2, 4, 6, 12, 14, 16, 17, 19, 25, 26, 28, 29],
2357
+ [0, 3, 9, 11, 13, 14, 15, 16, 17, 18, 20, 22, 28], 5),
2358
+ 37: ([0, 3, 4, 5, 7, 13, 18, 19, 24, 30, 32, 33, 34],
2359
+ [0, 1, 2, 3, 4, 6, 12, 13, 18, 19, 24, 25, 31, 33, 34, 35, 36], 10),
2360
+ 39: ([1, 4, 6, 10, 14, 15, 16, 17, 18, 21, 22, 23, 24, 25, 29, 33, 35, 38],
2361
+ [0, 2, 4, 6, 7, 10, 11, 14, 16, 19, 20, 22, 26, 32, 33, 38], 16),
2362
+ 57: ([0, 4, 11, 12, 18, 19, 20, 25, 26, 27, 28, 29, 30, 31, 32, 37, 38, 39, 45, 46, 53],
2363
+ [1, 2, 5, 6, 7, 8, 9, 10, 12, 17, 19, 21, 22, 24, 25, 28, 30, 31, 34, 37, 39, 41, 42, 43, 44, 46, 53, 54],
2364
+ 7)
2365
+ }
2366
+ compact_data = {
2367
+ 73: ([1, 8, 64],
2368
+ [0, 9, 13, 18, 25, 26, 27, 35, 36, 43],
2369
+ [1, 2, 4, 9, 11, 14, 18, 21, 26, 34, 36, 43], 4),
2370
+ 91: ([1, 16, 74],
2371
+ [0, 3, 4, 5, 8, 11, 19, 25, 27, 43, 45, 50, 55],
2372
+ [0, 1, 4, 5, 13, 14, 15, 25, 28, 33, 38, 43, 44, 49, 55], 9),
2373
+ 93: ([1, 4, 16, 64, 70],
2374
+ [3, 10, 11, 14, 21, 23, 33, 34, 46],
2375
+ [3, 9, 11, 17, 23, 33, 34, 46, 62], 25),
2376
+ 129: ([1, 4, 16, 64, 97, 121, 127],
2377
+ [1, 9, 10, 14, 19, 21, 23, 26, 27],
2378
+ [2, 5, 9, 10, 13, 18, 22, 27, 43, 86], 13),
2379
+ 397: ([1, 16, 31, 99, 126, 167, 256, 273, 290, 333, 393],
2380
+ [3, 5, 9, 10, 11, 12, 18, 20, 21, 23, 29, 33, 36, 40, 44, 47, 61, 72],
2381
+ [2, 3, 6, 10, 17, 22, 24, 33, 34, 36, 40, 46, 47, 53, 58, 71, 72],
2382
+ 34)
2383
+ }
2384
+
2385
+ exist = n in full_data or n in compact_data or \
2386
+ skew_spin_goethals_seidel_difference_family(n, existence=True)
2387
+ if existence:
2388
+ return exist
2389
+
2390
+ if not exist:
2391
+ raise NotImplementedError(f'Data for spin type Goethals Seidel family of order {n} not yet implemented')
2392
+
2393
+ G = Zmod(n)
2394
+ if skew_spin_goethals_seidel_difference_family(n, existence=True):
2395
+ G, [S1, S2, S3, S4] = skew_spin_goethals_seidel_difference_family(n, check=False)
2396
+ elif n in full_data:
2397
+ S1, S2, mu = full_data[n]
2398
+ S1 = list(map(G, S1))
2399
+ S2 = list(map(G, S2))
2400
+ S1, S2, S3, S4 = _construct_gs_difference_family_from_full(S1, S2, mu)
2401
+ elif n in compact_data:
2402
+ H, rep1, rep2, mu = compact_data[n]
2403
+ H = list(map(G, H))
2404
+ S1, S2, S3, S4 = _construct_gs_difference_family_from_compact(rep1, rep2, H, mu)
2405
+
2406
+ if check:
2407
+ lmbda = len(S1) + len(S2) + len(S3) + len(S4) - n
2408
+ assert is_supplementary_difference_set([S1, S2, S3, S4], lmbda=lmbda, G=G)
2409
+ return G, [S1, S2, S3, S4]
2410
+
2411
+
2412
+ def skew_spin_goethals_seidel_difference_family(n, existence=False, check=True):
2413
+ r"""
2414
+ Construct skew spin type Goethals-Seidel difference family with parameters
2415
+ `(n; k_1, k_2, k_3, k_4; \lambda)`.
2416
+
2417
+ The construction is described in [Djo2024]_. This function contains, for
2418
+ each value of `n`, either a full representation of `S_1, S_2` together with
2419
+ the multiplier `\mu`, or a subgroup `H`, two sets of representatives, and the
2420
+ multiplier.
2421
+
2422
+ This data is used to construct the difference family using the functions
2423
+ :func:`_construct_gs_difference_family_from_full` and
2424
+ :func:`_construct_gs_difference_family_from_compact`.
2425
+
2426
+ INPUT:
2427
+
2428
+ - ``n`` -- integer; the parameter of the GS difference family
2429
+ - ``existence`` -- boolean (default: ``False``); if ``True``, only check
2430
+ whether the skew difference family can be constructed
2431
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the sets
2432
+ are a skew difference family before returning them;
2433
+ setting this parameter to ``False`` may speed up the computation considerably
2434
+
2435
+ OUTPUT:
2436
+
2437
+ If ``existence=False``, the function returns the group G of integers modulo
2438
+ ``n`` and a list containing 4 sets, or raises an error if data for the given
2439
+ ``n`` is not available.
2440
+ If ``existence=True``, the function returns a boolean representing whether
2441
+ the skew difference family can be constructed.
2442
+
2443
+ EXAMPLES::
2444
+
2445
+ sage: from sage.combinat.designs.difference_family import skew_spin_goethals_seidel_difference_family
2446
+ sage: G, [S1, S2, S3, S4] = skew_spin_goethals_seidel_difference_family(61)
2447
+
2448
+ If existence is ``True``, the function returns a boolean::
2449
+
2450
+ sage: skew_spin_goethals_seidel_difference_family(61, existence=True)
2451
+ True
2452
+ sage: skew_spin_goethals_seidel_difference_family(5, existence=True)
2453
+ False
2454
+
2455
+ TESTS::
2456
+ sage: from sage.combinat.designs.difference_family import is_supplementary_difference_set, _is_skew_set
2457
+ sage: G, [S1, S2, S3, S4] = skew_spin_goethals_seidel_difference_family(7, check=False)
2458
+ sage: lmbda = len(S1) + len(S2) + len(S3) + len(S4) - 7
2459
+ sage: is_supplementary_difference_set([S1, S2, S3, S4], lmbda=lmbda, G=G)
2460
+ True
2461
+ sage: _is_skew_set(G, S1)
2462
+ True
2463
+ sage: skew_spin_goethals_seidel_difference_family(5)
2464
+ Traceback (most recent call last):
2465
+ ...
2466
+ NotImplementedError: Data for skew spin type Goethals Seidel family of order 5 not yet implemented
2467
+ """
2468
+ full_data = {
2469
+ 7: ([1, 2, 4], [1, 6], 2),
2470
+ 19: ([1, 4, 5, 6, 7, 9, 11, 16, 17], [0, 1, 7, 8, 11, 12, 18], -2),
2471
+ 37: ([2, 3, 4, 6, 8, 11, 15, 18, 20, 21, 23, 24, 25, 27, 28, 30, 32, 36],
2472
+ [0, 1, 2, 5, 9, 13, 14, 15, 22, 23, 24, 28, 32, 35, 36],
2473
+ 10)
2474
+ }
2475
+ compact_data = {
2476
+ 61: ([1, 9, 20, 34, 58], [3, 4, 5, 6, 8, 10], [0, 8, 10, 13, 23, 26], 13),
2477
+ 127: ([1, 2, 4, 8, 16, 32, 64],
2478
+ [1, 3, 7, 9, 11, 19, 21, 23, 47],
2479
+ [0, 3, 7, 9, 11, 15, 29, 31, 55], 19),
2480
+ 271: ([1, 28, 106, 125, 169, 178, 242, 248, 258],
2481
+ [1, 4, 5, 7, 8, 11, 14, 16, 19, 21, 22, 25, 31, 43, 44],
2482
+ [1, 2, 3, 5, 7, 8, 12, 19, 22, 27, 38, 42, 44, 51], 5),
2483
+ 331: ([1, 74, 80, 85, 111, 120, 167, 180, 270, 274, 293],
2484
+ [5, 10, 11, 13, 16, 19, 20, 22, 32, 38, 53, 56, 64, 76, 101],
2485
+ [0, 4, 11, 16, 20, 28, 31, 37, 41, 49, 53, 56, 73, 88, 101], 31),
2486
+ 397: ([1, 16, 31, 99, 126, 167, 256, 273, 290, 333, 393],
2487
+ [1, 6, 7, 8, 9, 10, 11, 12, 17, 18, 20, 21, 29, 34, 46, 47, 53, 106],
2488
+ [2, 11, 12, 17, 18, 20, 24, 27, 33, 34, 36, 40, 46, 47, 53, 58, 71],
2489
+ 34),
2490
+ 547: ([1, 46, 237, 261, 293, 350, 353, 375, 440, 475, 509, 517, 519],
2491
+ [1, 4, 5, 6, 10, 11, 13, 14, 17, 25, 29, 34, 35, 40, 49, 52, 55, 64, 69, 110, 123],
2492
+ [1, 4, 5, 11, 16, 17, 20, 26, 32, 33, 34, 41, 49, 52, 55, 64, 70, 80, 123, 207],
2493
+ 40),
2494
+ 631: ([1, 8, 43, 64, 79, 188, 228, 242, 279, 310, 339, 344, 512, 562, 587],
2495
+ [1, 2, 3, 4, 6, 7, 12, 13, 14, 17, 19, 21, 26, 27, 31, 38, 42, 52, 62, 76, 124],
2496
+ [0, 11, 13, 14, 18, 19, 21, 22, 29, 35, 39, 46, 62, 63, 65, 66, 67, 92, 117, 124, 187],
2497
+ 2)
2498
+ }
2499
+
2500
+ if existence:
2501
+ return n in full_data or n in compact_data
2502
+
2503
+ if n not in full_data and n not in compact_data:
2504
+ raise NotImplementedError(f'Data for skew spin type Goethals Seidel family of order {n} not yet implemented')
2505
+
2506
+ G = Zmod(n)
2507
+ if n in full_data:
2508
+ S1, S2, mu = full_data[n]
2509
+ S1 = list(map(G, S1))
2510
+ S2 = list(map(G, S2))
2511
+ S1, S2, S3, S4 = _construct_gs_difference_family_from_full(S1, S2, mu)
2512
+ if n in compact_data:
2513
+ H, rep1, rep2, mu = compact_data[n]
2514
+ H = list(map(G, H))
2515
+ S1, S2, S3, S4 = _construct_gs_difference_family_from_compact(rep1, rep2, H, mu)
2516
+
2517
+ if check:
2518
+ lmbda = len(S1) + len(S2) + len(S3) + len(S4) - n
2519
+ assert is_supplementary_difference_set([S1, S2, S3, S4], lmbda=lmbda, G=G)
2520
+ assert _is_skew_set(G, S1)
2521
+ return G, [S1, S2, S3, S4]
2522
+
2523
+
2524
+ def skew_supplementary_difference_set(n, existence=False, check=True, return_group=False):
2525
+ r"""
2526
+ Construct `4-\{n; n_1, n_2, n_3, n_4; \lambda\}` supplementary difference sets,
2527
+ where `S_1` is skew and `n_1 + n_2 + n_3 + n_4 = n+\lambda`.
2528
+
2529
+ These sets are constructed from available data, as described in [Djo1994a]_. The set `S_1 \subset G` is
2530
+ always skew, i.e. `S_1 \cap (-S_1) = \emptyset` and `S_1 \cup (-S_1) = G \setminus \{0\}`.
2531
+
2532
+ The data is taken from:
2533
+
2534
+ * `n = 103, 151`: [Djo1994a]_
2535
+ * `n = 67, 113, 127, 157, 163, 181, 241`: [Djo1992a]_
2536
+ * `n = 37, 43`: [Djo1992b]_
2537
+ * `n = 39, 49, 65, 93, 121, 129, 133, 217, 219, 267`: [Djo1992c]_
2538
+ * `n = 97`: [Djo2008a]_
2539
+ * `n = 109, 145, 247`: [Djo2008b]_
2540
+ * `n = 73`: [Djo2023b]_
2541
+ * `n = 213, 631`: [DGK2014]_
2542
+ * `n = 331`: [DK2016]_
2543
+
2544
+ Additional skew Supplementary difference sets are built using the function
2545
+ :func:`skew_supplementary_difference_set_over_polynomial_ring`, and
2546
+ :func:`skew_supplementary_difference_set_with_paley_todd`.
2547
+
2548
+ INPUT:
2549
+
2550
+ - ``n`` -- integer; the parameter of the supplementary difference set
2551
+ - ``existence`` -- boolean (default: ``False``); if ``True``, only check
2552
+ whether the supplementary difference sets can be constructed
2553
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the sets
2554
+ are supplementary difference sets with `S_1` skew before returning them;
2555
+ setting this parameter to ``False`` may speed up the computation considerably
2556
+ - ``return_group`` -- boolean (default: ``False``); if ``True``, the function
2557
+ will also return the group from which the sets are created
2558
+
2559
+ OUTPUT:
2560
+
2561
+ If ``existence=False``, the function returns a list containing 4 sets,
2562
+ or raises an error if data for the given ``n`` is not available. If
2563
+ ``return_group=True`` the function will additionally return the group from
2564
+ which the sets are created.
2565
+ If ``existence=True``, the function returns a boolean representing whether
2566
+ skew supplementary difference sets can be constructed.
2567
+
2568
+ EXAMPLES::
2569
+
2570
+ sage: from sage.combinat.designs.difference_family import skew_supplementary_difference_set
2571
+ sage: [S1, S2, S3, S4] = skew_supplementary_difference_set(39)
2572
+
2573
+ If ``return_group=True``, the function will also return the group::
2574
+
2575
+ sage: G, [S1, S2, S3, S4] = skew_supplementary_difference_set(103, return_group=True)
2576
+
2577
+ If ``existence=True``, the function returns a boolean::
2578
+
2579
+ sage: skew_supplementary_difference_set(103, existence=True)
2580
+ True
2581
+ sage: skew_supplementary_difference_set(17, existence=True)
2582
+ False
2583
+
2584
+ TESTS::
2585
+
2586
+ sage: from sage.combinat.designs.difference_family import is_supplementary_difference_set, _is_skew_set
2587
+ sage: G, [S1, S2, S3, S4] = skew_supplementary_difference_set(113, check=False, return_group=True)
2588
+ sage: is_supplementary_difference_set([S1, S2, S3, S4], lmbda=len(S1)+len(S2)+len(S3)+len(S4)-113, G=G)
2589
+ True
2590
+ sage: _is_skew_set(G, S1)
2591
+ True
2592
+ sage: G, [S1, S2, S3, S4] = skew_supplementary_difference_set(67, check=False, return_group=True)
2593
+ sage: is_supplementary_difference_set([S1, S2, S3, S4], lmbda=len(S1)+len(S2)+len(S3)+len(S4)-67, G=G)
2594
+ True
2595
+ sage: _is_skew_set(G, S1)
2596
+ True
2597
+ sage: skew_supplementary_difference_set(17)
2598
+ Traceback (most recent call last):
2599
+ ...
2600
+ ValueError: Skew SDS of order 17 not yet implemented.
2601
+ sage: skew_supplementary_difference_set(17, existence=True)
2602
+ False
2603
+ sage: skew_supplementary_difference_set(127, existence=True)
2604
+ True
2605
+ sage: skew_supplementary_difference_set(81, existence=True)
2606
+ True
2607
+
2608
+ .. NOTE::
2609
+
2610
+ The data for `n=247` in [Djo2008b]_ contains a typo: the set `\alpha_2` should contain `223` instead of `233`.
2611
+ This can be verified by checking the resulting sets, which are given explicitly in the paper.
2612
+ """
2613
+
2614
+ # If -1 is present in an index set, it means that {0} should be added to that set
2615
+ indices = {
2616
+ 37: [[0, 3, 5, 7, 9, 10], [0, 5, 6, 7, 8],
2617
+ [1, 2, 6, 7, 9], [2, 6, 8, 9, 10]],
2618
+ 39: [[1, 3, 5, 6, 8, 10, 12], [0, 1, 5, 8, 12, 13],
2619
+ [1, 3, 4, 7, 9, 12, 13], [0, 1, 2, 3, 7, 8]],
2620
+ 43: [[1, 2, 4], [1, 2, 4], [0, 2, 3], [3, 4, -1]],
2621
+ 49: [[1, 2, 5, 7, 8, 10, 13, 14], [4, 5, 6, 7, 10, 11],
2622
+ [0, 1, 2, 4, 6, 7, 12, 14], [1, 2, 3, 5, 6, 10, 12, 13, 14]],
2623
+ 65: [[1, 3, 5, 6, 8, 10, 13, 14, 17, 18, 20, 22],
2624
+ [0, 3, 7, 10, 16, 17, 18, 20, 21],
2625
+ [2, 4, 6, 8, 9, 10, 14, 15, 16, 17, 18, 20],
2626
+ [5, 7, 8, 9, 11, 12, 13, 14, 16, 18, 19, 20, 21]],
2627
+ 67: [[0, 3, 5, 6, 9, 10, 13, 14, 17, 18, 20],
2628
+ [0, 2, 4, 9, 11, 12, 13, 16, 19, 21],
2629
+ [1, 3, 6, 10, 11, 13, 14, 16, 20, 21],
2630
+ [2, 4, 6, 8, 9, 11, 14, 17, 19]],
2631
+ 73: [[4, 6, 8, 14], [8, 10, 12, 14], [4, 6, 10, 12], [-1, 0, 2, 10]],
2632
+ 93: [[0, 3, 4, 6, 9, 10, 12, 14, 17, 18],
2633
+ [2, 3, 4, 5, 9, 13, 15, 18, 19],
2634
+ [1, 2, 3, 4, 5, 6, 7, 8, 16],
2635
+ [1, 4, 6, 11, 12, 13, 15, 16, 17, 18]],
2636
+ 97: [[1, 2, 4, 6, 9, 11, 13, 14, 17, 18, 21, 23, 25, 27, 29, 30],
2637
+ [1, 2, 6, 7, 8, 9, 10, 11, 12, 13, 23, 27, 29],
2638
+ [0, 1, 2, 5, 6, 12, 13, 15, 16, 20, 24, 25, 26, 29, 30, 31],
2639
+ [0, 2, 3, 4, 7, 8, 9, 11, 12, 13, 15, 16, 17, 18, 23, 28, 29]],
2640
+ 103: [[1, 3, 4, 6, 8, 11, 12, 14, 17, 18, 20, 22, 25, 27, 28, 30, 32],
2641
+ [2, 9, 10, 12, 13, 14, 15, 16, 20, 21, 22, 23, 24, 26, 28, 29, 30],
2642
+ [0, 1, 2, 3, 4, 11, 12, 13, 16, 17, 19, 20, 21, 24, 25, 26, 28, 30, 31],
2643
+ [0, 1, 2, 3, 4, 5, 6, 13, 15, 18, 19, 20, 23, 24, 25, 26, 27, 28, 29, 31]],
2644
+ 109: [[0, 2, 5, 7, 8, 10, 12, 15, 16, 19, 20, 23, 24, 26, 29, 30, 33, 34],
2645
+ [4, 5, 6, 7, 11, 15, 18, 19, 20, 22, 25, 30, 32, 33, 35],
2646
+ [0, 1, 5, 6, 9, 10, 11, 14, 17, 20, 24, 26, 27, 28, 29, 31, 32],
2647
+ [0, 3, 4, 6, 7, 9, 10, 12, 13, 22, 24, 25, 26, 27, 28, 29, 31, 33, 35]],
2648
+ 113: [[0, 3, 4, 6, 8, 10, 13, 14],
2649
+ [1, 3, 8, 9, 10, 11, 12, 13],
2650
+ [0, 2, 3, 5, 6, 7, 12],
2651
+ [1, 2, 3, 5, 8, 9, 15]],
2652
+ 121: [[0, 2, 4, 7, 8, 11, 13, 14, 16, 19, 20, 22],
2653
+ [0, 1, 4, 5, 8, 9, 10, 15, 17, 20, 23],
2654
+ [1, 2, 3, 7, 9, 16, 18, 19, 20, 21, 22, 23],
2655
+ [0, 2, 9, 10, 11, 12, 13, 14, 15, 17, 18, 21, 22, 23]],
2656
+ 127: [[0, 3, 5, 7, 8, 10, 12, 14, 16],
2657
+ [0, 1, 3, 6, 7, 9, 10, 12, 14, 15],
2658
+ [0, 1, 3, 4, 5, 7, 8, 9, 15, 16],
2659
+ [1, 4, 5, 6, 9, 10, 13, 14, 15, 16]],
2660
+ 129: [[1, 2, 4, 7, 9, 11, 12, 14, 16, 18],
2661
+ [0, 1, 2, 3, 9, 11, 14, 15, 19],
2662
+ [0, 1, 3, 6, 8, 10, 12, 16, 18, 19],
2663
+ [0, 3, 7, 8, 9, 10, 12, 14, 15, 17]],
2664
+ 133: [[1, 2, 5, 6, 9, 11, 12, 14], [1, 4, 7, 9, 10, 12, 13, 15],
2665
+ [0, 5, 6, 8, 11, 12, 13, 15], [0, 1, 2, 5, 7, 8, 9, 13, 14, 15]],
2666
+ 145: [[1, 2, 4, 7, 9, 10, 13, 14, 16, 19, 20, 22], [0, 2, 4, 7, 10, 11, 14, 18, 19, 20, 21, 22],
2667
+ [1, 3, 6, 9, 12, 13, 14, 17, 19, 20, 21, 22, 23], [2, 3, 5, 6, 7, 9, 12, 13, 15, 16, 19, 20, 21, 22, 23]],
2668
+ 151: [[0, 3, 5, 6, 8, 11, 13, 14, 16, 19, 21, 23, 25, 27, 28],
2669
+ [2, 3, 6, 13, 16, 17, 20, 23, 25, 26, 27, 28, 29],
2670
+ [0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 23, 24, 27, 28],
2671
+ [1, 4, 5, 10, 11, 12, 13, 14, 16, 18, 19, 22, 25, 26, 27, 28]],
2672
+ 157: [[0, 2, 5, 7, 8, 11],
2673
+ [0, 4, 5, 6, 9, 11],
2674
+ [6, 7, 8, 9, 10, 11],
2675
+ [0, 5, 6, 7, 8, 10, 11]],
2676
+ 163: [[0, 2, 5, 6, 9, 10, 13, 14, 17],
2677
+ [0, 1, 7, 10, 12, 15, 16, 17],
2678
+ [0, 1, 3, 5, 8, 13, 15, 16, 17],
2679
+ [3, 6, 7, 8, 11, 12, 13, 14, 16, 17]],
2680
+ 181: [[0, 3, 5, 6, 8, 10, 13, 15, 16, 19],
2681
+ [4, 5, 7, 8, 11, 14, 15, 16, 18, 19],
2682
+ [0, 4, 10, 11, 13, 15, 16, 18, 19],
2683
+ [2, 4, 5, 7, 11, 13, 15, 17, 19]],
2684
+ 213: [[1, 2, 5, 6, 9, 11, 12, 14, 16, 19, 20, 23, 24, 26, 29, 30],
2685
+ [3, 6, 8, 12, 13, 14, 15, 17, 20, 22, 23, 25, 26, 27, 28, 31],
2686
+ [2, 3, 5, 7, 9, 13, 16, 17, 19, 21, 23, 24, 27, 28, 29],
2687
+ [0, 5, 6, 9, 11, 13, 14, 17, 20, 22, 23, 26, 29, 31]],
2688
+ 217: [[0, 3, 5, 7, 8, 11, 12, 14], [1, 3, 4, 7, 9, 11, 12, 15],
2689
+ [3, 4, 5, 6, 7, 9, 10, 14, 15], [1, 3, 4, 5, 7, 8, 11, 13, 14]],
2690
+ 219: [[1, 3, 5, 6, 8, 11, 12, 15, 17, 18, 21, 22, 24],
2691
+ [2, 6, 8, 10, 11, 12, 13, 16, 19, 22, 23, 24],
2692
+ [0, 1, 5, 6, 10, 11, 13, 14, 17, 20, 21, 24, 25],
2693
+ [0, 2, 3, 4, 5, 6, 7, 11, 12, 13, 16, 20, 23]],
2694
+ 241: [[0, 2, 4, 6, 8, 11, 12, 14],
2695
+ [1, 3, 4, 6, 7, 13, 14, 15],
2696
+ [6, 8, 9, 10, 12, 13, 14, 15],
2697
+ [3, 4, 5, 9, 10, 13, 14]],
2698
+ 247: [[0, 2, 4, 7, 8, 10, 12, 15, 16, 18, 20, 23, 25, 27, 29],
2699
+ [0, 2, 7, 9, 11, 12, 14, 15, 16, 18, 20, 22, 26],
2700
+ [2, 3, 4, 12, 13, 14, 15, 16, 18, 20, 23, 24, 26, 27, 29],
2701
+ [0, 3, 4, 6, 10, 11, 12, 14, 18, 19, 20, 22, 25, 29]],
2702
+ 267: [[0, 3, 4, 7, 8, 11, 13, 15, 16, 19, 21, 22, 25],
2703
+ [0, 1, 4, 5, 6, 8, 14, 15, 18, 21, 23],
2704
+ [0, 2, 4, 5, 7, 9, 10, 11, 14, 15, 16, 17, 25],
2705
+ [0, 1, 3, 4, 6, 14, 15, 16, 17, 18, 20, 22, 23, 25]],
2706
+ 331: [[1, 2, 4, 7, 9, 10, 12, 15, 16, 18, 21, 22, 24, 26, 28],
2707
+ [-1, 0, 2, 6, 9, 11, 12, 14, 15, 17, 20, 21, 24, 25, 28],
2708
+ [-1, 0, 1, 5, 6, 7, 8, 9, 10, 12, 15, 18, 23, 28, 29],
2709
+ [-1, 0, 3, 7, 8, 10, 11, 12, 14, 16, 19, 20, 21, 26, 29]],
2710
+ 631: [[0, 2, 4, 6, 9, 10, 12, 15, 16, 18, 20, 23, 24, 26, 29, 30, 32, 35, 36, 38, 40],
2711
+ [0, 1, 2, 4, 6, 8, 9, 10, 11, 12, 13, 14, 16, 20, 23, 28, 29, 30, 32, 36, 38, 41],
2712
+ [0, 2, 3, 4, 6, 9, 10, 12, 14, 15, 16, 17, 18, 19, 20, 22, 24, 25, 26, 29, 34, 40],
2713
+ [0, 2, 4, 5, 6, 7, 8, 10, 15, 16, 18, 22, 23, 24, 26, 30, 31, 33, 35, 36, 37, 38]],
2714
+ }
2715
+
2716
+ # If the element is a list, that is the coset.
2717
+ cosets_gens = {
2718
+ 37: [1, 2, 3, 5, 6, 9],
2719
+ 39: [1, 2, 3, 4, 6, 8, [13]],
2720
+ 43: [1, 3, 7],
2721
+ 49: [1, 2, 3, 4, 6, 7, 9, 12],
2722
+ 65: [1, 2, 3, 5, 6, 7, 9, 10, 11, [13], 22, [26]],
2723
+ 67: [1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 17],
2724
+ 73: [1, 3, 5, 9, 11, 13, 17, 25],
2725
+ 93: [1, 2, 3, 5, 7, 9, 10, 14, 15, [31]],
2726
+ 97: [1, 2, 3, 4, 5, 6, 7, 9, 10, 12, 13, 15, 18, 20, 23, 26],
2727
+ 103: [1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 15, 17, 19, 21, 23, 30],
2728
+ 109: [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 13, 15, 16, 18, 20, 23, 25, 30],
2729
+ 113: [1, 2, 3, 5, 6, 9, 10, 13],
2730
+ 121: [1, 2, 4, 5, 7, 8, 10, 11, 16, 17, 19, 20],
2731
+ 127: [1, 3, 5, 7, 9, 11, 13, 19, 21],
2732
+ 129: [1, 3, 5, 7, 9, 11, 13, 19, 21, [43]],
2733
+ 133: [1, 2, 3, 6, 7, 9, 18, [19, 38, 76]],
2734
+ 145: [1, [2, 17, 32, 72, 77, 127, 137], [3, 43, 48, 98, 108, 118, 133], [6, 51, 71, 86, 91, 96, 121],
2735
+ [7, 52, 82, 107, 112, 117, 132], [11, 21, 31, 46, 61, 101, 106], [14, 19, 69, 79, 89, 104, 119],
2736
+ [22, 42, 57, 62, 67, 92, 122], [5, 35, 80, 100, 115, 120, 125], [10, 15, 55, 70, 85, 95, 105],
2737
+ [29], [58]],
2738
+ 151: [1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 15, 22, 27, 29, 30],
2739
+ 157: [1, 2, 3, 5, 9, 15],
2740
+ 163: [1, 2, 3, 5, 6, 9, 10, 15, 18],
2741
+ 181: [1, 2, 3, 4, 6, 7, 8, 12, 13, 24],
2742
+ 213: [1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 15, 20, 22, 30, 43, 71],
2743
+ 217: [1, 2, 4, 5, 7, 10, 19, [31, 62, 124]],
2744
+ 219: [1, 2, 3, 5, 7, 9, 11, 15, 19, 22, 23, 33, [73]],
2745
+ 241: [1, 2, 4, 5, 7, 13, 19, 35],
2746
+ 247: [1, [2, 18, 31, 32, 41, 110, 122, 162, 223], [3, 27, 48, 165, 170, 183, 185, 211, 243],
2747
+ [5, 28, 45, 58, 80, 158, 187, 201, 226], [6, 54, 83, 93, 96, 119, 123, 175, 239], [7, 20, 63, 73, 112, 138, 163, 180, 232],
2748
+ [10, 56, 69, 90, 116, 127, 155, 160, 205], [11, 47, 99, 102, 111, 115, 150, 176, 177], [13, 52, 65, 78, 91, 117, 143, 208, 221],
2749
+ [14, 29, 40, 79, 113, 126, 146, 217, 224], [17, 25, 43, 49, 140, 142, 153, 194, 225], [19, 57, 171],
2750
+ [33, 34, 37, 50, 59, 86, 98, 141, 203], [35, 66, 68, 74, 100, 118, 159, 172, 196], [38, 95, 114]],
2751
+ 267: [1, 2, 3, 5, 7, 9, 10, 13, 14, 15, 19, 39, [89]],
2752
+ 331: [1, 2, 4, 5, 7, 8, 10, 13, 14, 16, 19, 20, 28, 32, 56],
2753
+ 631: [1, 2, 3, 4, 5, 6, 7, 9, 12, 14, 17, 18, 19, 21, 23, 27, 31, 35, 38, 42, 62],
2754
+ }
2755
+
2756
+ H_db = {
2757
+ 37: [1, 10, -11],
2758
+ 39: [1, 16, 22],
2759
+ 43: [1, 4, 11, 16, 21, -2, -8],
2760
+ 49: [1, 18, 30],
2761
+ 65: [1, 16, 61],
2762
+ 67: [1, 29, 37],
2763
+ 73: [1, 2, 4, 8, 16, 32, 64, 55, 37],
2764
+ 93: [1, 4, 16, 64, 70],
2765
+ 97: [1, 35, 61],
2766
+ 103: [1, 46, 56],
2767
+ 109: [1, 45, 63],
2768
+ 113: [1, 16, 28, 30, 49, 106, 109],
2769
+ 121: [1, 3, 9, 27, 81],
2770
+ 127: [1, 2, 4, 8, 16, 32, 64],
2771
+ 129: [1, 4, 16, 64, 97, 121, 127],
2772
+ 133: [1, 4, 16, 25, 64, 93, 100, 106, 123],
2773
+ 145: [1, 16, 36, 81, 111, 136, 141],
2774
+ 151: [1, 8, 19, 59, 64],
2775
+ 157: [1, 14, 16, 39, 46, 67, 75, 93, 99, 101, 108, 130, 153],
2776
+ 163: [1, 38, 40, 53, 58, 85, 104, 133, 140],
2777
+ 181: [1, 39, 43, 48, 62, 65, 73, 80, 132],
2778
+ 213: [1, 37, 91, 103, 172, 187, 190],
2779
+ 217: [1, 8, 9, 25, 51, 64, 72, 78, 81, 142, 190, 191, 193, 200, 214],
2780
+ 219: [1, 4, 16, 37, 55, 64, 148, 154, 178],
2781
+ 241: [1, 15, 24, 54, 87, 91, 94, 98, 100, 119, 160, 183, 205, 225, 231],
2782
+ 247: [1, 9, 16, 55, 61, 81, 139, 144, 235],
2783
+ 267: [1, 4, 16, 64, 67, 91, 97, 121, 217, 223, 256],
2784
+ 331: [1, 74, 80, 85, 111, 120, 167, 180, 270, 274, 293],
2785
+ 631: [1, 8, 43, 64, 79, 188, 228, 242, 279, 310, 339, 344, 512, 562, 587],
2786
+ }
2787
+
2788
+ G, S1, S2, S3, S4 = None, [], [], [], []
2789
+
2790
+ if n in indices:
2791
+ if existence:
2792
+ return True
2793
+ G, [S1, S2, S3, S4] = _construction_supplementary_difference_set(n, H_db[n], indices[n], cosets_gens[n], check=False)
2794
+ elif skew_supplementary_difference_set_over_polynomial_ring(n, existence=True):
2795
+ if existence:
2796
+ return True
2797
+ G, [S1, S2, S3, S4] = skew_supplementary_difference_set_over_polynomial_ring(n, check=False)
2798
+ elif skew_supplementary_difference_set_with_paley_todd(n, existence=True):
2799
+ if existence:
2800
+ return True
2801
+ G, [S1, S2, S3, S4] = skew_supplementary_difference_set_with_paley_todd(n, check=False)
2802
+ elif skew_spin_goethals_seidel_difference_family(n, existence=True):
2803
+ if existence:
2804
+ return True
2805
+ G, [S1, S2, S3, S4] = skew_spin_goethals_seidel_difference_family(n, check=False)
2806
+
2807
+ if existence:
2808
+ return False
2809
+
2810
+ if G is None:
2811
+ raise ValueError(f'Skew SDS of order {n} not yet implemented.')
2812
+
2813
+ if check:
2814
+ lmbda = len(S1) + len(S2) + len(S3) + len(S4) - n
2815
+ assert is_supplementary_difference_set([S1, S2, S3, S4], lmbda=lmbda, G=G)
2816
+ assert _is_skew_set(G, S1)
2817
+
2818
+ if return_group:
2819
+ return G, [S1, S2, S3, S4]
2820
+ return [S1, S2, S3, S4]
2821
+
2822
+
2823
+ def _construction_supplementary_difference_set(n, H, indices, cosets_gen, check=True):
2824
+ r"""
2825
+ Construct `4-\{n; n_1, n_2, n_3, n_4; \lambda\}` supplementary difference sets,
2826
+ where `n_1 + n_2 + n_3 + n_4 = n+\lambda`.
2827
+
2828
+ This construction is described in [Djo1994a]_.
2829
+
2830
+ Let H be a subgroup of Zmod(n) of order `t`. We construct the `2s = n/t` cosets
2831
+ `\alpha_0, .., \alpha_{2s-1}` by using the elements contained in a sequence `A`:
2832
+ `\alpha_{2i} = a_iH` and `\alpha_{2i+1} = -\alpha_{2i}`.
2833
+
2834
+ Then, we use four indices sets `J_1, J_2, J_3, J_4` to construct the four
2835
+ supplementary difference sets: `S_i = \bigcup_{j\in J__i} \alpha_i`. Note that
2836
+ if `J_i` contains the value `-1`, this function will add `0` to the set `S_i`.
2837
+
2838
+ To construct a coset `\alpha_{2i}` by listing it directly, replace the `2i`-th
2839
+ element of the list `A` with the desired set.
2840
+
2841
+ INPUT:
2842
+
2843
+ - ``n`` -- integer; the parameter of the supplementary difference set
2844
+ - ``H`` -- list of integers; the set `H` used to construct the cosets
2845
+ - ``indices`` -- list containing four lists of integers; the sets
2846
+ `J_1, J_2, J_3, J_4` described above
2847
+ - ``cosets_gen`` -- list containing integers or list of integers; the set `A`
2848
+ described above
2849
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the sets
2850
+ are supplementary difference sets; setting this parameter to ``False`` may
2851
+ speed up the computation considerably
2852
+
2853
+ OUTPUT:
2854
+
2855
+ The function returns the ring of integers modulo ``n`` and a list containing
2856
+ the 4 sets.
2857
+
2858
+ TESTS::
2859
+
2860
+ sage: from sage.combinat.designs.difference_family import is_supplementary_difference_set, _construction_supplementary_difference_set
2861
+ sage: H = [1, 10, -11]
2862
+ sage: cosets_gen = [1, 2, 3, 5, 6, 9]
2863
+ sage: indices = [[0, 3, 5, 7, 9, 10], [0, 5, 6, 7, 8], [1, 2, 6, 7, 9], [2, 6, 8, 9, 10]]
2864
+ sage: _construction_supplementary_difference_set(37, H, indices, cosets_gen)
2865
+ (Ring of integers modulo 37,
2866
+ [[32, 1, 33, 35, 34, 7, 9, 10, 12, 14, 16, 17, 18, 22, 24, 26, 29, 31],
2867
+ [32, 1, 33, 34, 5, 6, 7, 8, 10, 13, 18, 19, 23, 24, 26],
2868
+ [32, 2, 36, 5, 11, 13, 14, 15, 18, 19, 20, 24, 27, 29, 31],
2869
+ [2, 5, 6, 8, 9, 12, 13, 14, 15, 16, 19, 20, 23, 29, 31]])
2870
+ sage: H = [1, 16, 22]
2871
+ sage: cosets_gen = [1, 2, 3, 4, 6, 8, [13]]
2872
+ sage: indices = [[1, 3, 5, 6, 8, 10, 12], [0, 1, 5, 8, 12, 13], [1, 3, 4, 7, 9, 12, 13], [0, 1, 2, 3, 7, 8]]
2873
+ sage: _construction_supplementary_difference_set(39, H, indices, cosets_gen)
2874
+ (Ring of integers modulo 39,
2875
+ [[4, 6, 7, 8, 10, 11, 12, 13, 15, 17, 18, 20, 23, 25, 30, 34, 36, 37, 38],
2876
+ [1, 36, 38, 6, 12, 13, 15, 16, 17, 18, 22, 23, 26, 30],
2877
+ [3, 7, 9, 13, 14, 17, 21, 23, 24, 26, 27, 29, 33, 34, 35, 37, 38],
2878
+ [32, 1, 2, 34, 35, 5, 38, 37, 7, 6, 14, 15, 16, 17, 18, 22, 23, 29]])
2879
+ sage: H = [1, 4, 11, 16, 21, -2, -8]
2880
+ sage: cosets_gen = [1, 3, 7]
2881
+ sage: indices = [[1, 2, 4], [1, 2, 4], [0, 2, 3], [3, 4, -1]]
2882
+ sage: G, sets = _construction_supplementary_difference_set(43, H, indices, cosets_gen, check=False)
2883
+ sage: is_supplementary_difference_set(sets, lmbda=35, G=G)
2884
+ True
2885
+
2886
+ .. SEEALSO::
2887
+
2888
+ :func:`skew_supplementary_difference_set`
2889
+ """
2890
+ def generate_set(index_set, cosets):
2891
+ S = set()
2892
+ for idx in index_set:
2893
+ if idx == -1:
2894
+ S.add(Z(0))
2895
+ else:
2896
+ S = S.union(cosets[idx])
2897
+ return list(S)
2898
+
2899
+ Z = Zmod(n)
2900
+ H = set(map(Z, H))
2901
+
2902
+ cosets = []
2903
+ for el in cosets_gen:
2904
+ if isinstance(el, list):
2905
+ even_coset = {Z(x) for x in el}
2906
+ else:
2907
+ even_coset = {x*el for x in H}
2908
+ odd_coset = {-x for x in even_coset}
2909
+ cosets.append(even_coset)
2910
+ cosets.append(odd_coset)
2911
+
2912
+ S1 = generate_set(indices[0], cosets)
2913
+ S2 = generate_set(indices[1], cosets)
2914
+ S3 = generate_set(indices[2], cosets)
2915
+ S4 = generate_set(indices[3], cosets)
2916
+
2917
+ if check:
2918
+ lmbda = len(S1) + len(S2) + len(S3) + len(S4) - n
2919
+ assert is_supplementary_difference_set([S1, S2, S3, S4], lmbda=lmbda, G=Z)
2920
+
2921
+ return Z, [S1, S2, S3, S4]
2922
+
2923
+
2924
+ def supplementary_difference_set_hadamard(n, existence=False, check=True):
2925
+ r"""
2926
+ Construct `4-\{n; n_1, n_2, n_3, n_4; \lambda\}` supplementary difference sets,
2927
+ where `n_1 + n_2 + n_3 + n_4 = n+\lambda`.
2928
+
2929
+ These sets are constructed from available data, as described in [Djo1994a]_.
2930
+ The data is taken from:
2931
+
2932
+ * `n = 191`: [Djo2008c]_
2933
+ * `n = 239`: [Djo1994b]_
2934
+ * `n = 251`: [DGK2014]_
2935
+
2936
+ Additional SDS are constructed using :func:`skew_supplementary_difference_set`.
2937
+
2938
+ INPUT:
2939
+
2940
+ - ``n`` -- integer; the parameter of the supplementary difference set
2941
+ - ``existence`` -- boolean (default: ``False``); if ``True``, only check
2942
+ whether the supplementary difference sets can be constructed
2943
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the sets
2944
+ are supplementary difference sets before returning them; Setting this
2945
+ parameter to ``False`` may speed up the computation considerably
2946
+
2947
+ OUTPUT:
2948
+
2949
+ If ``existence=False``, the function returns the ring of integers modulo
2950
+ ``n`` and a list containing the 4 sets, or raises an error if data for the
2951
+ given ``n`` is not available.
2952
+ If ``existence=True``, the function returns a boolean representing whether
2953
+ skew supplementary difference sets can be constructed.
2954
+
2955
+ EXAMPLES::
2956
+
2957
+ sage: from sage.combinat.designs.difference_family import supplementary_difference_set_hadamard
2958
+ sage: G, [S1, S2, S3, S4] = supplementary_difference_set_hadamard(191)
2959
+
2960
+ If ``existence=True``, the function returns a boolean::
2961
+
2962
+ sage: supplementary_difference_set_hadamard(191, existence=True)
2963
+ True
2964
+ sage: supplementary_difference_set_hadamard(17, existence=True)
2965
+ False
2966
+
2967
+ TESTS::
2968
+
2969
+ sage: from sage.combinat.designs.difference_family import is_supplementary_difference_set
2970
+ sage: G, [S1, S2, S3, S4] = supplementary_difference_set_hadamard(191, check=False)
2971
+ sage: is_supplementary_difference_set([S1, S2, S3, S4], lmbda=len(S1)+len(S2)+len(S3)+len(S4)-191, G=G)
2972
+ True
2973
+ sage: G, [S1, S2, S3, S4] = supplementary_difference_set_hadamard(37, check=False)
2974
+ sage: is_supplementary_difference_set([S1, S2, S3, S4], lmbda=len(S1)+len(S2)+len(S3)+len(S4)-37, G=G)
2975
+ True
2976
+ sage: supplementary_difference_set_hadamard(11)
2977
+ Traceback (most recent call last):
2978
+ ...
2979
+ ValueError: SDS of order 11 not yet implemented.
2980
+ sage: supplementary_difference_set_hadamard(11, existence=True)
2981
+ False
2982
+ sage: supplementary_difference_set_hadamard(127, existence=True)
2983
+ True
2984
+ """
2985
+
2986
+ indices = {
2987
+ 191: [[1, 7, 9, 10, 11, 13, 17, 18, 25, 26, 30, 31, 33, 34, 35, 36, 37],
2988
+ [1, 4, 7, 9, 11, 12, 13, 14, 19, 21, 22, 23, 24, 25, 26, 29, 36, 37],
2989
+ [0, 3, 4, 5, 7, 8, 9, 16, 17, 19, 24, 25, 29, 30, 31, 33, 35, 37],
2990
+ [1, 3, 4, 5, 8, 11, 14, 18, 19, 20, 21, 23, 24, 25, 28, 29, 30, 32, 34, 35]],
2991
+ 239: [[0, 1, 2, 3, 4, 5, 6, 7, 14, 18, 19, 21, 24, 25, 29, 30],
2992
+ [0, 1, 3, 7, 9, 12, 15, 18, 20, 22, 26, 28, 29, 30, 31, 32, 33],
2993
+ [2, 3, 4, 5, 8, 9, 10, 11, 13, 17, 19, 21, 22, 24, 27, 31, 32],
2994
+ [0, 1, 2, 3, 6, 7, 8, 11, 13, 15, 17, 18, 19, 22, 25, 26, 27, 32, 33]],
2995
+ 251: [[2, 6, 8, 10, 12, 13, 14, 15, 16, 18, 19, 20, 21, 22, 23, 27, 28, 35, 36, 39, 41, 43, 44, 47, 48],
2996
+ [2, 5, 10, 11, 17, 18, 21, 23, 24, 25, 26, 28, 29, 30, 34, 35, 38, 39, 40, 41, 42, 43, 44, 49],
2997
+ [0, 2, 6, 7, 10, 11, 14, 15, 16, 18, 21, 22, 24, 26, 30, 35, 37, 38, 45, 46, 47, 48, 49],
2998
+ [1, 2, 3, 4, 8, 9, 12, 17, 21, 22, 27, 28, 29, 30, 33, 34, 39, 41, 42, 43, 46, 47, 48]],
2999
+ }
3000
+
3001
+ cosets_gens = {
3002
+ 191: [1, 2, 3, 4, 6, 8, 9, 11, 12, 13, 16, 17, 18, 19, 22, 32, 36, 38, 41],
3003
+ 239: [1, 2, 3, 4, 5, 6, 7, 8, 9, 13, 14, 15, 18, 21, 28, 35, 42],
3004
+ 251: [1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 14, 15, 17, 18, 19, 21, 28, 30, 33, 34, 35, 41, 43, 45, 68],
3005
+ }
3006
+
3007
+ H_db = {
3008
+ 191: [1, 39, 184, 109, 49],
3009
+ 239: [1, 10, 24, 44, 98, 100, 201],
3010
+ 251: [1, 20, 113, 149, 219],
3011
+ }
3012
+
3013
+ if existence:
3014
+ return n in indices or skew_supplementary_difference_set(n, existence=True)
3015
+
3016
+ sets = None
3017
+ G = None
3018
+ if n in indices:
3019
+ G, sets = _construction_supplementary_difference_set(n, H_db[n], indices[n], cosets_gens[n], check=False)
3020
+ elif skew_supplementary_difference_set(n, existence=True):
3021
+ G, sets = skew_supplementary_difference_set(n, check=False, return_group=True)
3022
+ elif spin_goethals_seidel_difference_family(n, existence=True):
3023
+ if existence:
3024
+ return True
3025
+ G, [S1, S2, S3, S4] = spin_goethals_seidel_difference_family(n, check=False)
3026
+
3027
+ if sets is None:
3028
+ raise ValueError(f'SDS of order {n} not yet implemented.')
3029
+
3030
+ S1, S2, S3, S4 = sets
3031
+ if check:
3032
+ lmbda = len(S1) + len(S2) + len(S3) + len(S4) - n
3033
+ assert is_supplementary_difference_set([S1, S2, S3, S4], lmbda=lmbda, G=G)
3034
+
3035
+ return G, [S1, S2, S3, S4]
3036
+
3037
+
3038
+ def _is_skew_set(G, S):
3039
+ r"""
3040
+ Check if ``S`` is a skew set over the group ``G``.
3041
+
3042
+ From [Djo1994a]_, a set `S \subset G` (where `G` is a finite abelian group of order `n`) is of skew
3043
+ type if `S_1 \cap (-S_1) = \emptyset` and `S_1 \cup (-S_1) = G\setminus \{0\}`.
3044
+
3045
+ INPUT:
3046
+
3047
+ - ``G`` -- a group
3048
+ - ``S`` -- list containing elements of ``G``; the set to be checked
3049
+
3050
+ EXAMPLES::
3051
+
3052
+ sage: from sage.combinat.designs.difference_family import _is_skew_set
3053
+ sage: Z5 = Zmod(5)
3054
+ sage: _is_skew_set(Z5, [Z5(1), Z5(2)])
3055
+ True
3056
+ sage: _is_skew_set(Z5, [Z5(1), Z5(2), Z5(3)])
3057
+ False
3058
+ sage: _is_skew_set(Z5, [Z5(1)])
3059
+ False
3060
+ """
3061
+ for el in S:
3062
+ if -el in S:
3063
+ return False
3064
+ for el in G:
3065
+ if el == 0:
3066
+ continue
3067
+ if el not in S and -el not in S:
3068
+ return False
3069
+ return True
3070
+
3071
+
3072
+ def are_complementary_difference_sets(G, A, B, verbose=False):
3073
+ r"""
3074
+ Check if ``A`` and ``B`` are complementary difference sets over the group ``G``.
3075
+
3076
+ According to [Sze1971]_, two sets `A`, `B` of size `m` are complementary
3077
+ difference sets over a group `G` of size `2m+1` if:
3078
+
3079
+ 1. they are `2-\{2m+1; m, m; m-1\}` supplementary difference sets
3080
+ 2. `A` is skew, i.e. `a \in A` implies `-a \not \in A`
3081
+
3082
+ INPUT:
3083
+
3084
+ - ``G`` -- a group of odd order
3085
+ - ``A`` -- set of elements of ``G``
3086
+ - ``B`` -- set of elements of ``G``
3087
+ - ``verbose`` -- boolean (default: ``False``); if ``True`` the function will
3088
+ be verbose when the sets do not satisfy the constraints
3089
+
3090
+ EXAMPLES::
3091
+
3092
+ sage: from sage.combinat.designs.difference_family import are_complementary_difference_sets
3093
+ sage: are_complementary_difference_sets(Zmod(7), [1, 2, 4], [1, 2, 4])
3094
+ True
3095
+
3096
+ If ``verbose=True``, the function will be verbose::
3097
+
3098
+ sage: are_complementary_difference_sets(Zmod(7), [1, 2, 5], [1, 2, 4], verbose=True)
3099
+ The sets are not supplementary difference sets with lambda = 2
3100
+ False
3101
+
3102
+ TESTS::
3103
+
3104
+ sage: are_complementary_difference_sets(Zmod(16), [1, 2, 4], [1, 2, 4])
3105
+ False
3106
+ sage: are_complementary_difference_sets(Zmod(7), [1, 2, 4], [1, 2, 3, 4])
3107
+ False
3108
+ sage: are_complementary_difference_sets(Zmod(19), [1, 4, 5, 6, 7, 9, 11, 16, 17], [1, 4, 5, 6, 7, 9, 11, 16, 17])
3109
+ True
3110
+
3111
+ .. SEEALSO::
3112
+
3113
+ :func:`is_supplementary_difference_set`
3114
+ """
3115
+ n = G.order()
3116
+
3117
+ if n % 2 != 1:
3118
+ if verbose:
3119
+ print('G must have odd order')
3120
+ return False
3121
+
3122
+ m = (n - 1) // 2
3123
+
3124
+ if len(A) != m or len(B) != m:
3125
+ if verbose:
3126
+ print(f'A and B must have size {m}')
3127
+ return False
3128
+
3129
+ if not is_supplementary_difference_set([A, B], lmbda=m-1, G=G):
3130
+ if verbose:
3131
+ print(f'The sets are not supplementary difference sets with lambda = {m-1}')
3132
+ return False
3133
+
3134
+ if not _is_skew_set(G, A):
3135
+ if verbose:
3136
+ print('The set A is not skew')
3137
+ return False
3138
+
3139
+ return True
3140
+
3141
+
3142
+ def complementary_difference_setsI(n, check=True):
3143
+ r"""
3144
+ Construct complementary difference sets in a group of order `n \cong 3 \mod 4`, `n` a prime power.
3145
+
3146
+ Let `G` be a Galois Field of order `n`, where `n` satisfies the requirements
3147
+ above. Let `A` be the set of nonzero quadratic elements in `G`, and `B = A`.
3148
+ Then `A` and `B` are complementary difference sets over a group of order `n`.
3149
+ This construction is described in [Sze1971]_.
3150
+
3151
+ INPUT:
3152
+
3153
+ - ``n`` -- integer; the order of the group `G`
3154
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the sets
3155
+ are complementary difference sets before returning them
3156
+
3157
+ OUTPUT:
3158
+
3159
+ The function returns the Galois field of order ``n`` and the two sets, or raises
3160
+ an error if ``n`` does not satisfy the requirements of this construction.
3161
+
3162
+ EXAMPLES::
3163
+
3164
+ sage: from sage.combinat.designs.difference_family import complementary_difference_setsI
3165
+ sage: complementary_difference_setsI(19)
3166
+ (Finite Field of size 19,
3167
+ [1, 4, 5, 6, 7, 9, 11, 16, 17],
3168
+ [1, 4, 5, 6, 7, 9, 11, 16, 17])
3169
+
3170
+ TESTS::
3171
+
3172
+ sage: from sage.combinat.designs.difference_family import are_complementary_difference_sets
3173
+ sage: G, A, B = complementary_difference_setsI(23, check=False)
3174
+ sage: are_complementary_difference_sets(G, A, B)
3175
+ True
3176
+ sage: complementary_difference_setsI(17)
3177
+ Traceback (most recent call last):
3178
+ ...
3179
+ ValueError: the parameter 17 is not valid
3180
+ sage: complementary_difference_setsI(15) # needs sage.libs.pari
3181
+ Traceback (most recent call last):
3182
+ ...
3183
+ ValueError: the parameter 15 is not valid
3184
+
3185
+ .. SEEALSO::
3186
+
3187
+ :func:`are_complementary_difference_sets`
3188
+ :func:`complementary_difference_sets`
3189
+ """
3190
+ if not (n % 4 == 3 and is_prime_power(n)):
3191
+ raise ValueError(f'the parameter {n} is not valid')
3192
+
3193
+ from sage.rings.finite_rings.finite_field_constructor import GF
3194
+
3195
+ G = GF(n, 'a')
3196
+ A = list({x**2 for x in G if x**2 != 0})
3197
+ B = A
3198
+ if check:
3199
+ assert are_complementary_difference_sets(G, A, B)
3200
+
3201
+ return G, A, B
3202
+
3203
+
3204
+ def complementary_difference_setsII(n, check=True):
3205
+ r"""
3206
+ Construct complementary difference sets in a group of order `n = p^t`, where `p \cong 5 \mod 8` and `t \cong 1, 2, 3 \mod 4`.
3207
+
3208
+ Consider a finite field `G` of order `n` and let `\rho` be the generator of
3209
+ the corresponding multiplicative group. Then, there are two different constructions,
3210
+ depending on whether `t` is even or odd.
3211
+
3212
+ If `t \cong 2 \mod 4`, let `C_0` be the set of nonzero octic residues in `G`,
3213
+ and let `C_i = \rho^i C_0` for `1 \le i \le 7`.
3214
+ Then, `A = C_0 \cup C_1 \cup C_2 \cup C_3` and `B = C_0 \cup C_1 \cup C_6 \cup C_7`.
3215
+
3216
+ If `t` is odd, let `C_0` be the set of nonzero fourth powers in `G`, and let
3217
+ `C_i = \rho^i C_0` for `1 \le i \le 3`.
3218
+ Then, `A = C_0 \cup C_1` and `B = C_0 \cup C_3`.
3219
+
3220
+ For more details on this construction, see [Sze1971]_.
3221
+
3222
+ INPUT:
3223
+
3224
+ - ``n`` -- integer; the order of the group `G`
3225
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the sets
3226
+ are complementary difference sets before returning them; setting this to
3227
+ ``False`` might speed up the computation for large values of ``n``
3228
+
3229
+ OUTPUT:
3230
+
3231
+ The function returns the Galois field of order ``n`` and the two sets, or raises
3232
+ an error if ``n`` does not satisfy the requirements of this construction.
3233
+
3234
+ EXAMPLES::
3235
+
3236
+ sage: from sage.combinat.designs.difference_family import complementary_difference_setsII
3237
+ sage: complementary_difference_setsII(5) # needs sage.libs.pari
3238
+ (Finite Field of size 5, [1, 2], [1, 3])
3239
+
3240
+ TESTS::
3241
+
3242
+ sage: # needs sage.libs.pari
3243
+ sage: from sage.combinat.designs.difference_family import are_complementary_difference_sets
3244
+ sage: G, A, B = complementary_difference_setsII(25, check=False)
3245
+ sage: are_complementary_difference_sets(G, A, B)
3246
+ True
3247
+ sage: G, A, B = complementary_difference_setsII(13, check=False)
3248
+ sage: are_complementary_difference_sets(G, A, B)
3249
+ True
3250
+ sage: complementary_difference_setsII(49)
3251
+ Traceback (most recent call last):
3252
+ ...
3253
+ ValueError: the parameter 49 is not valid
3254
+ sage: complementary_difference_setsII(15)
3255
+ Traceback (most recent call last):
3256
+ ...
3257
+ ValueError: the parameter 15 is not valid
3258
+
3259
+ .. SEEALSO::
3260
+
3261
+ :func:`are_complementary_difference_sets`
3262
+ :func:`complementary_difference_sets`
3263
+ """
3264
+ p, t = is_prime_power(n, get_data=True)
3265
+ if not (p % 8 == 5 and t > 0 and t % 4 in [1, 2, 3]):
3266
+ raise ValueError(f'the parameter {n} is not valid')
3267
+
3268
+ from sage.rings.finite_rings.finite_field_constructor import GF
3269
+ G = GF(n, 'a')
3270
+ A, B = None, None
3271
+
3272
+ if t % 2 == 0:
3273
+ rho = G.multiplicative_generator()
3274
+ C0 = list({el**8 for el in G if el != 0})
3275
+ C1, C2, C3, C6, C7 = ([rho**i * el for el in C0] for i in [1, 2, 3, 6, 7])
3276
+ A = C0 + C1 + C2 + C3
3277
+ B = C0 + C1 + C6 + C7
3278
+ else:
3279
+ rho = G.multiplicative_generator()
3280
+ C0 = list({el**4 for el in G if el**4 != 0})
3281
+ C1 = [rho * el for el in C0]
3282
+ C3 = [rho**3 * el for el in C0]
3283
+ A = C0 + C1
3284
+ B = C0 + C3
3285
+
3286
+ if check:
3287
+ assert are_complementary_difference_sets(G, A, B)
3288
+
3289
+ return G, A, B
3290
+
3291
+
3292
+ def complementary_difference_setsIII(n, check=True):
3293
+ r"""
3294
+ Construct complementary difference sets in a group of order `n = 2m + 1`, where `4m + 3` is a prime power.
3295
+
3296
+ Consider a finite field `G` of order `n` and let `\rho` be a primite element
3297
+ of this group. Now let `Q` be the set of nonzero quadratic residues in `G`,
3298
+ and let `A = \{ a | \rho^{2a} - 1 \in Q\}`, `B' = \{ b | -(\rho^{2b} + 1) \in Q\}`.
3299
+ Then `A` and `B = Q \setminus B'` are complementary difference sets over the ring
3300
+ of integers modulo `n`. For more details, see [Sz1969]_.
3301
+
3302
+ INPUT:
3303
+
3304
+ - ``n`` -- integer; the order of the group over which the sets are constructed
3305
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the sets
3306
+ are complementary difference sets before returning them; setting this to
3307
+ ``False`` might speed up the computation for large values of ``n``
3308
+
3309
+ OUTPUT:
3310
+
3311
+ The function returns the Galois field of order ``n`` and the two sets, or raises
3312
+ an error if ``n`` does not satisfy the requirements of this construction.
3313
+
3314
+ EXAMPLES::
3315
+
3316
+ sage: from sage.combinat.designs.difference_family import complementary_difference_setsIII
3317
+ sage: complementary_difference_setsIII(11) # needs sage.libs.pari
3318
+ (Ring of integers modulo 11, [1, 2, 5, 7, 8], [0, 1, 3, 8, 10])
3319
+
3320
+ TESTS::
3321
+
3322
+ sage: from sage.combinat.designs.difference_family import are_complementary_difference_sets
3323
+ sage: G, A, B = complementary_difference_setsIII(21, check=False) # needs sage.libs.pari
3324
+ sage: are_complementary_difference_sets(G, A, B) # needs sage.libs.pari
3325
+ True
3326
+ sage: G, A, B = complementary_difference_setsIII(65, check=False) # needs sage.libs.pari
3327
+ sage: are_complementary_difference_sets(G, A, B) # needs sage.libs.pari
3328
+ True
3329
+ sage: complementary_difference_setsIII(10)
3330
+ Traceback (most recent call last):
3331
+ ...
3332
+ ValueError: the parameter 10 is not valid
3333
+ sage: complementary_difference_setsIII(17) # needs sage.libs.pari
3334
+ Traceback (most recent call last):
3335
+ ...
3336
+ ValueError: the parameter 17 is not valid
3337
+
3338
+ .. SEEALSO::
3339
+
3340
+ :func:`are_complementary_difference_sets`
3341
+ :func:`complementary_difference_sets`
3342
+ """
3343
+ m = (n - 1) // 2
3344
+ q = 4*m + 3
3345
+ if n % 2 != 1 or not is_prime_power(q):
3346
+ raise ValueError(f'the parameter {n} is not valid')
3347
+
3348
+ from sage.rings.finite_rings.finite_field_constructor import GF
3349
+ G = Zmod(n)
3350
+ G2 = GF(q)
3351
+ rho = G2.primitive_element()
3352
+
3353
+ Q = [rho ** (2*b) for b in range(1, n+1)]
3354
+
3355
+ A = [G(a) for a in range(n) if rho**(2*a) - 1 in Q]
3356
+ B = [G(b) for b in range(n) if -rho**(2*b) - 1 not in Q]
3357
+
3358
+ if check:
3359
+ assert are_complementary_difference_sets(G, A, B)
3360
+
3361
+ return G, A, B
3362
+
3363
+
3364
+ def complementary_difference_sets(n, existence=False, check=True):
3365
+ r"""
3366
+ Compute complementary difference sets over a group of order `n = 2m + 1`.
3367
+
3368
+ According to [Sze1971]_, two sets `A`, `B` of size `m` are complementary
3369
+ difference sets over a group `G` of size `n = 2m + 1` if:
3370
+
3371
+ 1. they are `2-\{2m+1; m, m; m-1\}` supplementary difference sets
3372
+ 2. `A` is skew, i.e. `a \in A` implies `-a \not \in A`
3373
+
3374
+ This method tries to call :func:`complementary_difference_setsI`,
3375
+ :func:`complementary_difference_setsII` or :func:`complementary_difference_setsIII`
3376
+ if the parameter `n` satisfies the requirements of one of these functions.
3377
+
3378
+ INPUT:
3379
+
3380
+ - ``n`` -- integer; the order of the group over which the sets are constructed
3381
+ - ``existence`` -- boolean (default: ``False``); if ``True``, only check
3382
+ whether the supplementary difference sets can be constructed
3383
+ - ``check`` -- boolean (default: ``True``); if ``True``, check that the sets
3384
+ are complementary difference sets before returning them; setting this to
3385
+ ``False`` might speed up the computation for large values of ``n``
3386
+
3387
+ OUTPUT:
3388
+
3389
+ If ``existence=False``, the function returns group ``G`` and two complementary
3390
+ difference sets, or raises an error if data for the given ``n`` is not available.
3391
+ If ``existence=True``, the function returns a boolean representing whether
3392
+ complementary difference sets can be constructed for the given ``n``.
3393
+
3394
+ EXAMPLES::
3395
+
3396
+ sage: from sage.combinat.designs.difference_family import complementary_difference_sets
3397
+ sage: complementary_difference_sets(15) # needs sage.libs.pari
3398
+ (Ring of integers modulo 15, [1, 2, 4, 6, 7, 10, 12], [0, 1, 2, 6, 9, 13, 14])
3399
+
3400
+ If ``existence=True``, the function returns a boolean::
3401
+
3402
+ sage: complementary_difference_sets(15, existence=True) # needs sage.libs.pari
3403
+ True
3404
+ sage: complementary_difference_sets(16, existence=True)
3405
+ False
3406
+
3407
+ TESTS::
3408
+
3409
+ sage: from sage.combinat.designs.difference_family import are_complementary_difference_sets
3410
+ sage: G, A, B = complementary_difference_sets(29) # needs sage.libs.pari
3411
+ sage: are_complementary_difference_sets(G, A, B) # needs sage.libs.pari
3412
+ True
3413
+ sage: G, A, B = complementary_difference_sets(65) # needs sage.libs.pari
3414
+ sage: are_complementary_difference_sets(G, A, B) # needs sage.libs.pari
3415
+ True
3416
+ sage: complementary_difference_sets(10)
3417
+ Traceback (most recent call last):
3418
+ ...
3419
+ ValueError: the parameter n must be odd
3420
+ sage: complementary_difference_sets(17) # needs sage.libs.pari
3421
+ Traceback (most recent call last):
3422
+ ...
3423
+ NotImplementedError: complementary difference sets of order 17 are not implemented yet
3424
+
3425
+ .. SEEALSO::
3426
+
3427
+ :func:`are_complementary_difference_sets`
3428
+ """
3429
+ if n % 2 == 0:
3430
+ if existence:
3431
+ return False
3432
+ raise ValueError('the parameter n must be odd')
3433
+
3434
+ p, t = is_prime_power(n, get_data=True)
3435
+ G, A, B = None, None, None
3436
+
3437
+ if n % 4 == 3 and t > 0:
3438
+ if existence:
3439
+ return True
3440
+ G, A, B = complementary_difference_setsI(n, check=False)
3441
+ elif p % 8 == 5 and t > 0 and t % 4 in [1, 2, 3]:
3442
+ if existence:
3443
+ return True
3444
+ G, A, B = complementary_difference_setsII(n, check=False)
3445
+ elif is_prime_power(2*n + 1):
3446
+ if existence:
3447
+ return True
3448
+ G, A, B = complementary_difference_setsIII(n, check=False)
3449
+
3450
+ if existence:
3451
+ return False
3452
+
3453
+ if G is None:
3454
+ raise NotImplementedError(f'complementary difference sets of order {n} are not implemented yet')
3455
+
3456
+ if check:
3457
+ assert are_complementary_difference_sets(G, A, B)
3458
+ return G, A, B
3459
+
3460
+
3461
+ def difference_family(v, k, l=1, existence=False, explain_construction=False, check=True):
3462
+ r"""
3463
+ Return a (``k``, ``l``)-difference family on an Abelian group of cardinality ``v``.
3464
+
3465
+ Let `G` be a finite Abelian group. For a given subset `D` of `G`, we define
3466
+ `\Delta D` to be the multi-set of differences `\Delta D = \{x - y; x \in D,
3467
+ y \in D, x \not= y\}`. A `(G,k,\lambda)`-*difference family* is a collection
3468
+ of `k`-subsets of `G`, `D = \{D_1, D_2, \ldots, D_b\}` such that the union
3469
+ of the difference sets `\Delta D_i` for `i=1,...b`, seen as a multi-set,
3470
+ contains each element of `G \backslash \{0\}` exactly `\lambda`-times.
3471
+
3472
+ When there is only one block, i.e. `\lambda(v - 1) = k(k-1)`, then a
3473
+ `(G,k,\lambda)`-difference family is also called a *difference set*.
3474
+
3475
+ See also :wikipedia:`Difference_set`.
3476
+
3477
+ If there is no such difference family, an :exc:`EmptySetError` is raised and
3478
+ if there is no construction at the moment :exc:`NotImplementedError`
3479
+ is raised.
3480
+
3481
+ INPUT:
3482
+
3483
+ - ``v``, ``k``, ``l`` -- parameters of the difference family. If ``l`` is
3484
+ not provided it is assumed to be ``1``
3485
+ - ``existence`` -- if ``True``, then return either ``True`` if Sage knows
3486
+ how to build such design, ``Unknown`` if it does not and ``False`` if it
3487
+ knows that the design does not exist
3488
+ - ``explain_construction`` -- instead of returning a difference family,
3489
+ returns a string that explains the construction used
3490
+ - ``check`` -- boolean (default: ``True``); if ``True``, then the result of
3491
+ the computation is checked before being returned. This should not be
3492
+ needed but ensures that the output is correct
3493
+
3494
+ OUTPUT:
3495
+
3496
+ A pair ``(G,D)`` made of a group `G` and a difference family `D` on that
3497
+ group. Or, if ``existence=True`` a troolean or if
3498
+ ``explain_construction=True`` a string.
3499
+
3500
+ EXAMPLES::
3501
+
3502
+ sage: G,D = designs.difference_family(73,4) # needs sage.libs.pari
3503
+ sage: G # needs sage.libs.pari
3504
+ Finite Field of size 73
3505
+ sage: D # needs sage.libs.pari
3506
+ [[0, 1, 5, 18],
3507
+ [0, 3, 15, 54],
3508
+ [0, 9, 45, 16],
3509
+ [0, 27, 62, 48],
3510
+ [0, 8, 40, 71],
3511
+ [0, 24, 47, 67]]
3512
+
3513
+ sage: print(designs.difference_family(73, 4, explain_construction=True))
3514
+ The database contains a (73,4)-evenly distributed set
3515
+
3516
+ sage: # needs sage.libs.pari
3517
+ sage: G,D = designs.difference_family(15,7,3)
3518
+ sage: G
3519
+ Ring of integers modulo 15
3520
+ sage: D
3521
+ [[0, 1, 2, 4, 5, 8, 10]]
3522
+ sage: print(designs.difference_family(15,7,3,explain_construction=True))
3523
+ Singer difference set
3524
+
3525
+ sage: # needs sage.libs.pari
3526
+ sage: print(designs.difference_family(91,10,1,explain_construction=True))
3527
+ Singer difference set
3528
+ sage: print(designs.difference_family(64,28,12, explain_construction=True))
3529
+ McFarland 1973 construction
3530
+ sage: print(designs.difference_family(576, 276, 132, explain_construction=True))
3531
+ Hadamard difference set product from N1=2 and N2=3
3532
+
3533
+ For `k=6,7` we look at the set of small prime powers for which a
3534
+ construction is available::
3535
+
3536
+ sage: def prime_power_mod(r, m):
3537
+ ....: k = m+r
3538
+ ....: while True:
3539
+ ....: if is_prime_power(k):
3540
+ ....: yield k
3541
+ ....: k += m
3542
+
3543
+ sage: # needs sage.libs.pari
3544
+ sage: from itertools import islice
3545
+ sage: l6 = {True: [], False: [], Unknown: []}
3546
+ sage: for q in islice(prime_power_mod(1,30), int(60)):
3547
+ ....: l6[designs.difference_family(q,6,existence=True)].append(q)
3548
+ sage: l6[True]
3549
+ [31, 121, 151, 181, 211, ..., 3061, 3121, 3181]
3550
+ sage: l6[Unknown]
3551
+ [61]
3552
+ sage: l6[False]
3553
+ []
3554
+
3555
+ sage: # needs sage.libs.pari
3556
+ sage: l7 = {True: [], False: [], Unknown: []}
3557
+ sage: for q in islice(prime_power_mod(1,42), int(60)):
3558
+ ....: l7[designs.difference_family(q,7,existence=True)].append(q)
3559
+ sage: l7[True]
3560
+ [169, 337, 379, 421, 463, 547, 631, 673, 757, 841, 883, 967, ..., 4621, 4957, 5167]
3561
+ sage: l7[Unknown]
3562
+ [43, 127, 211, 2017, 2143, 2269, 2311, 2437, 2521, 2647, ..., 4999, 5041, 5209]
3563
+ sage: l7[False]
3564
+ []
3565
+
3566
+ List available constructions::
3567
+
3568
+ sage: for v in range(2,100): # needs sage.libs.pari
3569
+ ....: constructions = []
3570
+ ....: for k in range(2,10):
3571
+ ....: for l in range(1,10):
3572
+ ....: ret = designs.difference_family(v,k,l,existence=True)
3573
+ ....: if ret is True:
3574
+ ....: constructions.append((k,l))
3575
+ ....: _ = designs.difference_family(v,k,l)
3576
+ ....: if constructions:
3577
+ ....: print("%2d: %s"%(v, ', '.join('(%d,%d)'%(k,l) for k,l in constructions)))
3578
+ 3: (2,1)
3579
+ 4: (3,2)
3580
+ 5: (2,1), (4,3)
3581
+ 6: (5,4)
3582
+ 7: (2,1), (3,1), (3,2), (4,2), (6,5)
3583
+ 8: (7,6)
3584
+ 9: (2,1), (4,3), (8,7)
3585
+ 10: (9,8)
3586
+ 11: (2,1), (4,6), (5,2), (5,4), (6,3)
3587
+ 13: (2,1), (3,1), (3,2), (4,1), (4,3), (5,5), (6,5)
3588
+ 15: (3,1), (4,6), (5,6), (7,3), (7,6)
3589
+ 16: (3,2), (5,4), (6,2)
3590
+ 17: (2,1), (4,3), (5,5), (8,7)
3591
+ 19: (2,1), (3,1), (3,2), (4,2), (6,5), (9,4), (9,8)
3592
+ 21: (3,1), (4,3), (5,1), (6,3), (6,5)
3593
+ 22: (4,2), (6,5), (7,4), (8,8)
3594
+ 23: (2,1)
3595
+ 25: (2,1), (3,1), (3,2), (4,1), (4,3), (6,5), (7,7), (8,7)
3596
+ 27: (2,1), (3,1)
3597
+ 28: (3,2), (6,5)
3598
+ 29: (2,1), (4,3), (7,3), (7,6), (8,4), (8,6)
3599
+ 31: (2,1), (3,1), (3,2), (4,2), (5,2), (5,4), (6,1), (6,5)
3600
+ 33: (3,1), (5,5), (6,5)
3601
+ 34: (4,2)
3602
+ 35: (5,2)
3603
+ 37: (2,1), (3,1), (3,2), (4,1), (4,3), (6,5), (9,2), (9,8)
3604
+ 39: (3,1), (6,5)
3605
+ 40: (3,2), (4,1)
3606
+ 41: (2,1), (4,3), (5,1), (5,4), (6,3), (8,7)
3607
+ 43: (2,1), (3,1), (3,2), (4,2), (6,5), (7,2), (7,3), (7,6), (8,4)
3608
+ 45: (3,1), (5,1)
3609
+ 46: (4,2), (6,2)
3610
+ 47: (2,1)
3611
+ 49: (2,1), (3,1), (3,2), (4,1), (4,3), (6,5), (8,7), (9,3)
3612
+ 51: (3,1), (5,2), (6,3)
3613
+ 52: (4,1)
3614
+ 53: (2,1), (4,3)
3615
+ 55: (3,1), (9,4)
3616
+ 57: (3,1), (7,3), (8,1)
3617
+ 59: (2,1)
3618
+ 61: (2,1), (3,1), (3,2), (4,1), (4,3), (5,1), (5,4), (6,2), (6,3), (6,5)
3619
+ 63: (3,1)
3620
+ 64: (3,2), (4,1), (7,2), (7,6), (9,8)
3621
+ 65: (5,1)
3622
+ 67: (2,1), (3,1), (3,2), (6,5)
3623
+ 69: (3,1)
3624
+ 71: (2,1), (5,2), (5,4), (7,3), (7,6), (8,4)
3625
+ 73: (2,1), (3,1), (3,2), (4,1), (4,3), (6,5), (8,7), (9,1), (9,8)
3626
+ 75: (3,1), (5,2)
3627
+ 76: (4,1)
3628
+ 79: (2,1), (3,1), (3,2), (6,5)
3629
+ 81: (2,1), (3,1), (4,3), (5,1), (5,4), (8,7)
3630
+ 83: (2,1)
3631
+ 85: (4,1), (7,2), (7,3), (8,2)
3632
+ 89: (2,1), (4,3), (8,7)
3633
+ 91: (6,1), (7,1)
3634
+ 97: (2,1), (3,1), (3,2), (4,1), (4,3), (6,5), (8,7), (9,3)
3635
+
3636
+ TESTS:
3637
+
3638
+ Check more of the Wilson constructions from [Wi72]_::
3639
+
3640
+ sage: Q5 = [241, 281,421,601,641, 661, 701, 821,881]
3641
+ sage: Q9 = [73, 1153, 1873, 2017]
3642
+ sage: Q15 = [76231]
3643
+ sage: Q4 = [13, 73, 97, 109, 181, 229, 241, 277, 337, 409, 421, 457]
3644
+ sage: Q8 = [1009, 3137, 3697]
3645
+ sage: for Q,k in [(Q4,4),(Q5,5),(Q8,8),(Q9,9),(Q15,15)]: # needs sage.libs.pari
3646
+ ....: for q in Q:
3647
+ ....: assert designs.difference_family(q,k,1,existence=True) is True
3648
+ ....: _ = designs.difference_family(q,k,1)
3649
+
3650
+ Check Singer difference sets::
3651
+
3652
+ sage: sgp = lambda q,d: ((q**(d+1)-1)//(q-1), (q**d-1)//(q-1), (q**(d-1)-1)//(q-1))
3653
+
3654
+ sage: for q in range(2,10): # needs sage.libs.pari
3655
+ ....: if is_prime_power(q):
3656
+ ....: for d in [2,3,4]:
3657
+ ....: v,k,l = sgp(q,d)
3658
+ ....: assert designs.difference_family(v,k,l,existence=True) is True
3659
+ ....: _ = designs.difference_family(v,k,l)
3660
+
3661
+ Check twin primes difference sets::
3662
+
3663
+ sage: for p in [3,5,7,9,11]: # needs sage.libs.pari
3664
+ ....: v = p*(p+2); k = (v-1)/2; lmbda = (k-1)/2
3665
+ ....: G,D = designs.difference_family(v,k,lmbda)
3666
+
3667
+ Check Complementary difference sets::
3668
+
3669
+ sage: for v in [15, 33, 35, 39, 51]: # needs sage.libs.pari
3670
+ ....: G, D = designs.difference_family(v, (v-1)//2, (v-1)//2-1)
3671
+
3672
+ Check the database::
3673
+
3674
+ sage: from sage.combinat.designs.database import DF,EDS
3675
+ sage: for v,k,l in DF:
3676
+ ....: assert designs.difference_family(v,k,l,existence=True) is True
3677
+ ....: df = designs.difference_family(v,k,l,check=True)
3678
+
3679
+ sage: for k in EDS: # needs sage.libs.pari
3680
+ ....: for v in EDS[k]:
3681
+ ....: assert designs.difference_family(v,k,1,existence=True) is True
3682
+ ....: df = designs.difference_family(v,k,1,check=True)
3683
+
3684
+ Check the known Hadamard parameters::
3685
+
3686
+ sage: for N in range(2,21): # needs sage.libs.pari
3687
+ ....: v = 4*N^2; k = 2*N^2-N; l = N^2-N
3688
+ ....: status = designs.difference_family(v,k,l,existence=True)
3689
+ ....: print("{:2} {}".format(N,designs.difference_family(v,k,l,explain_construction=True) if status is True else status))
3690
+ 2 McFarland 1973 construction
3691
+ 3 Turyn 1965 construction
3692
+ 4 McFarland 1973 construction
3693
+ 5 False
3694
+ 6 The database contains a (144,66,30)-difference family
3695
+ 7 False
3696
+ 8 McFarland 1973 construction
3697
+ 9 Unknown
3698
+ 10 Unknown
3699
+ 11 False
3700
+ 12 Hadamard difference set product from N1=2 and N2=3
3701
+ 13 False
3702
+ 14 Unknown
3703
+ 15 Unknown
3704
+ 16 McFarland 1973 construction
3705
+ 17 False
3706
+ 18 Hadamard difference set product from N1=3 and N2=3
3707
+ 19 False
3708
+ 20 Unknown
3709
+
3710
+ Check a failing construction (:issue:`17528`)::
3711
+
3712
+ sage: designs.difference_family(9,3)
3713
+ Traceback (most recent call last):
3714
+ ...
3715
+ NotImplementedError: No construction available for (9,3,1)-difference family
3716
+
3717
+ Check that when ``existence=True`` we always obtain ``True``, ``False`` or ``Unknown``,
3718
+ and when ``explain_construction=True``, it is a string (see :issue:`24513`)::
3719
+
3720
+ sage: designs.difference_family(3, 2, 1, existence=True)
3721
+ True
3722
+ sage: designs.difference_family(3, 2, 1, explain_construction=True)
3723
+ 'Trivial difference family'
3724
+
3725
+ sage: for _ in range(100): # needs sage.libs.pari
3726
+ ....: v = randint(1, 30)
3727
+ ....: k = randint(2, 30)
3728
+ ....: l = randint(1, 30)
3729
+ ....: res = designs.difference_family(v, k, l, existence=True)
3730
+ ....: assert res is True or res is False or res is Unknown
3731
+ ....: if res is True:
3732
+ ....: assert isinstance(designs.difference_family(3, 2, 1, explain_construction=True), str)
3733
+
3734
+ .. TODO::
3735
+
3736
+ Implement recursive constructions from Buratti "Recursive for difference
3737
+ matrices and relative difference families" (1998) and Jungnickel
3738
+ "Composition theorems for difference families and regular planes" (1978)
3739
+ """
3740
+ from .block_design import are_hyperplanes_in_projective_geometry_parameters
3741
+
3742
+ from .database import DF, EDS
3743
+
3744
+ v = ZZ(v)
3745
+ k = ZZ(k)
3746
+ l = ZZ(l)
3747
+
3748
+ if v < 0 or k < 0 or l < 0:
3749
+ if existence:
3750
+ return False
3751
+ raise EmptySetError("No difference family eixsts with negative parameters")
3752
+
3753
+ if (v,k,l) in DF:
3754
+ if existence:
3755
+ return True
3756
+ elif explain_construction:
3757
+ return "The database contains a ({},{},{})-difference family".format(v,k,l)
3758
+
3759
+ vv, blocks = next(iter(DF[v,k,l].items()))
3760
+
3761
+ # Build the group
3762
+ from sage.rings.finite_rings.integer_mod_ring import Zmod
3763
+ if len(vv) == 1:
3764
+ G = Zmod(vv[0])
3765
+ else:
3766
+ from sage.categories.cartesian_product import cartesian_product
3767
+ G = cartesian_product([Zmod(i) for i in vv])
3768
+
3769
+ df = [[G(i) for i in b] for b in blocks]
3770
+
3771
+ if check and not is_difference_family(G, df, v=v, k=k, l=l):
3772
+ raise RuntimeError("There is an invalid ({},{},{})-difference "
3773
+ "family in the database... Please contact "
3774
+ "sage-devel@googlegroups.com".format(v,k,l))
3775
+
3776
+ return G,df
3777
+
3778
+ elif l == 1 and k in EDS and v in EDS[k]:
3779
+ if existence:
3780
+ return True
3781
+ elif explain_construction:
3782
+ return "The database contains a ({},{})-evenly distributed set".format(v,k)
3783
+
3784
+ from sage.rings.finite_rings.finite_field_constructor import GF
3785
+ poly,B = EDS[k][v]
3786
+ if poly is None: # q is prime
3787
+ K = G = GF(v)
3788
+ else:
3789
+ K = G = GF(v,'a',modulus=poly)
3790
+
3791
+ B = [K(b) for b in B]
3792
+ e = k*(k-1)//2
3793
+ xe = G.multiplicative_generator()**e
3794
+ df = [[xe**j*b for b in B] for j in range((v-1)//(2*e))]
3795
+ if check and not is_difference_family(G, df, v=v, k=k, l=l):
3796
+ raise RuntimeError("There is an invalid ({},{})-evenly distributed "
3797
+ "set in the database... Please contact "
3798
+ "sage-devel@googlegroups.com".format(v, k))
3799
+ return G, df
3800
+
3801
+ if k in [0,1]:
3802
+ # Then \Delta D_i is empty
3803
+ # So if G\{0} is empty is good, otherwise not
3804
+ if v == 1:
3805
+ if existence:
3806
+ return True
3807
+ from sage.rings.finite_rings.integer_mod_ring import Zmod
3808
+ l = [0] if k == 1 else []
3809
+ return Zmod(1),[l]
3810
+
3811
+ if existence:
3812
+ return False
3813
+ raise EmptySetError("No difference family exists with k=1 and v!=1")
3814
+
3815
+ e = k*(k-1)
3816
+ if (l*(v-1)) % e:
3817
+ if existence:
3818
+ return Unknown
3819
+ raise NotImplementedError("No construction available for ({},{},{})-difference family".format(v,k,l))
3820
+
3821
+ # trivial construction
3822
+ if k == (v-1) and l == (v-2):
3823
+ if existence:
3824
+ return True
3825
+ elif explain_construction:
3826
+ return "Trivial difference family"
3827
+
3828
+ from sage.rings.finite_rings.integer_mod_ring import Zmod
3829
+ G = Zmod(v)
3830
+ return G, [list(range(1, v))]
3831
+
3832
+ factorization = arith.factor(v)
3833
+ if len(factorization) == 1:
3834
+ from sage.rings.finite_rings.finite_field_constructor import GF
3835
+ K = GF(v,'z')
3836
+
3837
+ if are_mcfarland_1973_parameters(v,k,l):
3838
+ if existence:
3839
+ return True
3840
+ elif explain_construction:
3841
+ return "McFarland 1973 construction"
3842
+ else:
3843
+ _, (q,s) = are_mcfarland_1973_parameters(v,k,l,True)
3844
+ G,D = mcfarland_1973_construction(q,s)
3845
+
3846
+ elif are_hyperplanes_in_projective_geometry_parameters(v,k,l):
3847
+ if existence:
3848
+ return True
3849
+ elif explain_construction:
3850
+ return "Singer difference set"
3851
+ else:
3852
+ _, (q,d) = are_hyperplanes_in_projective_geometry_parameters(v,k,l,True)
3853
+ G,D = singer_difference_set(q,d)
3854
+
3855
+ elif are_hadamard_difference_set_parameters(v,k,l) and k-2*l == 3:
3856
+ if existence:
3857
+ return True
3858
+ elif explain_construction:
3859
+ return "Turyn 1965 construction"
3860
+ else:
3861
+ G,D = turyn_1965_3x3xK(4)
3862
+
3863
+ elif are_hadamard_difference_set_parameters(v,k,l) and hadamard_difference_set_product_parameters(k-2*l):
3864
+ N1,N2 = hadamard_difference_set_product_parameters(k-2*l)
3865
+ if existence:
3866
+ return True
3867
+ elif explain_construction:
3868
+ return "Hadamard difference set product from N1={} and N2={}".format(N1,N2)
3869
+ else:
3870
+ v1 = 4*N1*N1
3871
+ v2 = 4*N2*N2
3872
+ k1 = 2*N1*N1 - N1
3873
+ k2 = 2*N2*N2 - N2
3874
+ l1 = N1*N1 - N1
3875
+ l2 = N2*N2 - N2
3876
+ G1, D1 = difference_family(v1,k1,l1)
3877
+ G2, D2 = difference_family(v2,k2,l2)
3878
+ G, D = hadamard_difference_set_product(G1,D1,G2,D2)
3879
+
3880
+ elif are_hadamard_difference_set_parameters(v,k,l) and (k-2*l).is_prime():
3881
+ if existence:
3882
+ return False
3883
+ else:
3884
+ raise EmptySetError("by McFarland 1989 such difference family does not exist")
3885
+
3886
+ elif len(factorization) == 1 and radical_difference_family(K, k, l, existence=True) is True:
3887
+ if existence:
3888
+ return True
3889
+ elif explain_construction:
3890
+ return "Radical difference family on a finite field"
3891
+ else:
3892
+ D = radical_difference_family(K,k,l)
3893
+ G = K
3894
+
3895
+ elif (len(factorization) == 1
3896
+ and l == 1
3897
+ and k == 6
3898
+ and df_q_6_1(K, existence=True) is True):
3899
+ if existence:
3900
+ return True
3901
+ elif explain_construction:
3902
+ return "Wilson 1972 difference family made from the union of two cyclotomic cosets"
3903
+ else:
3904
+ D = df_q_6_1(K)
3905
+ G = K
3906
+
3907
+ elif (k == (v-1)//2 and
3908
+ l == (k-1)//2 and
3909
+ len(factorization) == 2 and
3910
+ abs(pow(*factorization[0]) - pow(*factorization[1])) == 2):
3911
+ # Twin prime powers construction
3912
+ # i.e. v = p(p+2) where p and p+2 are prime powers
3913
+ # k = (v-1)/2
3914
+ # lambda = (k-1)/2 (ie 2l+1 = k)
3915
+ if existence:
3916
+ return True
3917
+ elif explain_construction:
3918
+ return "Twin prime powers difference family"
3919
+ else:
3920
+ p = pow(*factorization[0])
3921
+ q = pow(*factorization[1])
3922
+ if p > q:
3923
+ p,q = q,p
3924
+ G,D = twin_prime_powers_difference_set(p,check=False)
3925
+
3926
+ elif (v-1)//2 == k and (v-1)//2-1 == l and complementary_difference_sets(v, existence=True):
3927
+ if existence:
3928
+ return True
3929
+ elif explain_construction:
3930
+ return "Complementary difference sets"
3931
+ else:
3932
+ G, A, B = complementary_difference_sets(v)
3933
+ D = [A, B]
3934
+
3935
+ else:
3936
+ if existence:
3937
+ return Unknown
3938
+ raise NotImplementedError("No constructions for these parameters")
3939
+
3940
+ if check and not is_difference_family(G,D,v=v,k=k,l=l,verbose=False):
3941
+ raise RuntimeError("There is a problem. Sage built the following "
3942
+ "difference family on G='{}' with parameters ({},{},{}):\n "
3943
+ "{}\nwhich seems to not be a difference family... "
3944
+ "Please contact sage-devel@googlegroups.com".format(G,v,k,l,D))
3945
+
3946
+ return G, D
3947
+
3948
+
3949
+ from sage.misc.rest_index_of_methods import gen_rest_table_index
3950
+ import sys
3951
+ __doc__ = __doc__.format(INDEX_OF_FUNCTIONS=gen_rest_table_index(sys.modules[__name__]))