passagemath-combinat 10.6.42__cp314-cp314-musllinux_1_2_x86_64.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 (400) hide show
  1. passagemath_combinat/__init__.py +3 -0
  2. passagemath_combinat-10.6.42.dist-info/METADATA +160 -0
  3. passagemath_combinat-10.6.42.dist-info/RECORD +400 -0
  4. passagemath_combinat-10.6.42.dist-info/WHEEL +5 -0
  5. passagemath_combinat-10.6.42.dist-info/top_level.txt +3 -0
  6. passagemath_combinat.libs/libgmp-0e7fc84e.so.10.5.0 +0 -0
  7. passagemath_combinat.libs/libsymmetrica-81fe8739.so.3.0.0 +0 -0
  8. sage/algebras/affine_nil_temperley_lieb.py +263 -0
  9. sage/algebras/all.py +24 -0
  10. sage/algebras/all__sagemath_combinat.py +35 -0
  11. sage/algebras/askey_wilson.py +935 -0
  12. sage/algebras/associated_graded.py +345 -0
  13. sage/algebras/cellular_basis.py +350 -0
  14. sage/algebras/cluster_algebra.py +2766 -0
  15. sage/algebras/down_up_algebra.py +860 -0
  16. sage/algebras/free_algebra.py +1698 -0
  17. sage/algebras/free_algebra_element.py +345 -0
  18. sage/algebras/free_algebra_quotient.py +405 -0
  19. sage/algebras/free_algebra_quotient_element.py +295 -0
  20. sage/algebras/free_zinbiel_algebra.py +885 -0
  21. sage/algebras/hall_algebra.py +783 -0
  22. sage/algebras/hecke_algebras/all.py +4 -0
  23. sage/algebras/hecke_algebras/ariki_koike_algebra.py +1796 -0
  24. sage/algebras/hecke_algebras/ariki_koike_specht_modules.py +475 -0
  25. sage/algebras/hecke_algebras/cubic_hecke_algebra.py +3520 -0
  26. sage/algebras/hecke_algebras/cubic_hecke_base_ring.py +1473 -0
  27. sage/algebras/hecke_algebras/cubic_hecke_matrix_rep.py +1079 -0
  28. sage/algebras/iwahori_hecke_algebra.py +3095 -0
  29. sage/algebras/jordan_algebra.py +1773 -0
  30. sage/algebras/lie_conformal_algebras/abelian_lie_conformal_algebra.py +113 -0
  31. sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra.py +156 -0
  32. sage/algebras/lie_conformal_algebras/all.py +18 -0
  33. sage/algebras/lie_conformal_algebras/bosonic_ghosts_lie_conformal_algebra.py +134 -0
  34. sage/algebras/lie_conformal_algebras/examples.py +43 -0
  35. sage/algebras/lie_conformal_algebras/fermionic_ghosts_lie_conformal_algebra.py +131 -0
  36. sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py +139 -0
  37. sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py +174 -0
  38. sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py +167 -0
  39. sage/algebras/lie_conformal_algebras/freely_generated_lie_conformal_algebra.py +107 -0
  40. sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py +135 -0
  41. sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py +353 -0
  42. sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py +236 -0
  43. sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py +78 -0
  44. sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py +328 -0
  45. sage/algebras/lie_conformal_algebras/n2_lie_conformal_algebra.py +117 -0
  46. sage/algebras/lie_conformal_algebras/neveu_schwarz_lie_conformal_algebra.py +86 -0
  47. sage/algebras/lie_conformal_algebras/virasoro_lie_conformal_algebra.py +82 -0
  48. sage/algebras/lie_conformal_algebras/weyl_lie_conformal_algebra.py +205 -0
  49. sage/algebras/nil_coxeter_algebra.py +191 -0
  50. sage/algebras/q_commuting_polynomials.py +673 -0
  51. sage/algebras/q_system.py +608 -0
  52. sage/algebras/quantum_clifford.py +959 -0
  53. sage/algebras/quantum_groups/ace_quantum_onsager.py +693 -0
  54. sage/algebras/quantum_groups/all.py +9 -0
  55. sage/algebras/quantum_groups/fock_space.py +2219 -0
  56. sage/algebras/quantum_groups/q_numbers.py +207 -0
  57. sage/algebras/quantum_groups/quantum_group_gap.py +2695 -0
  58. sage/algebras/quantum_groups/representations.py +591 -0
  59. sage/algebras/quantum_matrix_coordinate_algebra.py +1006 -0
  60. sage/algebras/quantum_oscillator.py +623 -0
  61. sage/algebras/quaternion_algebra.py +20 -0
  62. sage/algebras/quaternion_algebra_element.py +55 -0
  63. sage/algebras/rational_cherednik_algebra.py +525 -0
  64. sage/algebras/schur_algebra.py +670 -0
  65. sage/algebras/shuffle_algebra.py +1011 -0
  66. sage/algebras/splitting_algebra.py +779 -0
  67. sage/algebras/tensor_algebra.py +709 -0
  68. sage/algebras/yangian.py +1082 -0
  69. sage/algebras/yokonuma_hecke_algebra.py +1018 -0
  70. sage/all__sagemath_combinat.py +35 -0
  71. sage/combinat/SJT.py +255 -0
  72. sage/combinat/affine_permutation.py +2405 -0
  73. sage/combinat/algebraic_combinatorics.py +55 -0
  74. sage/combinat/all.py +53 -0
  75. sage/combinat/all__sagemath_combinat.py +195 -0
  76. sage/combinat/alternating_sign_matrix.py +2063 -0
  77. sage/combinat/baxter_permutations.py +346 -0
  78. sage/combinat/bijectionist.py +3220 -0
  79. sage/combinat/binary_recurrence_sequences.py +1180 -0
  80. sage/combinat/blob_algebra.py +685 -0
  81. sage/combinat/catalog_partitions.py +27 -0
  82. sage/combinat/chas/all.py +23 -0
  83. sage/combinat/chas/fsym.py +1180 -0
  84. sage/combinat/chas/wqsym.py +2601 -0
  85. sage/combinat/cluster_complex.py +326 -0
  86. sage/combinat/colored_permutations.py +2039 -0
  87. sage/combinat/colored_permutations_representations.py +964 -0
  88. sage/combinat/composition_signed.py +142 -0
  89. sage/combinat/composition_tableau.py +855 -0
  90. sage/combinat/constellation.py +1729 -0
  91. sage/combinat/core.py +751 -0
  92. sage/combinat/counting.py +12 -0
  93. sage/combinat/crystals/affine.py +742 -0
  94. sage/combinat/crystals/affine_factorization.py +518 -0
  95. sage/combinat/crystals/affinization.py +331 -0
  96. sage/combinat/crystals/alcove_path.py +2013 -0
  97. sage/combinat/crystals/all.py +22 -0
  98. sage/combinat/crystals/bkk_crystals.py +141 -0
  99. sage/combinat/crystals/catalog.py +115 -0
  100. sage/combinat/crystals/catalog_elementary_crystals.py +18 -0
  101. sage/combinat/crystals/catalog_infinity_crystals.py +33 -0
  102. sage/combinat/crystals/catalog_kirillov_reshetikhin.py +18 -0
  103. sage/combinat/crystals/crystals.py +257 -0
  104. sage/combinat/crystals/direct_sum.py +260 -0
  105. sage/combinat/crystals/elementary_crystals.py +1251 -0
  106. sage/combinat/crystals/fast_crystals.py +441 -0
  107. sage/combinat/crystals/fully_commutative_stable_grothendieck.py +1205 -0
  108. sage/combinat/crystals/generalized_young_walls.py +1076 -0
  109. sage/combinat/crystals/highest_weight_crystals.py +436 -0
  110. sage/combinat/crystals/induced_structure.py +695 -0
  111. sage/combinat/crystals/infinity_crystals.py +730 -0
  112. sage/combinat/crystals/kac_modules.py +863 -0
  113. sage/combinat/crystals/kirillov_reshetikhin.py +4196 -0
  114. sage/combinat/crystals/kyoto_path_model.py +497 -0
  115. sage/combinat/crystals/letters.cpython-314-x86_64-linux-musl.so +0 -0
  116. sage/combinat/crystals/letters.pxd +79 -0
  117. sage/combinat/crystals/letters.pyx +3056 -0
  118. sage/combinat/crystals/littelmann_path.py +1518 -0
  119. sage/combinat/crystals/monomial_crystals.py +1262 -0
  120. sage/combinat/crystals/multisegments.py +462 -0
  121. sage/combinat/crystals/mv_polytopes.py +467 -0
  122. sage/combinat/crystals/pbw_crystal.py +511 -0
  123. sage/combinat/crystals/pbw_datum.cpython-314-x86_64-linux-musl.so +0 -0
  124. sage/combinat/crystals/pbw_datum.pxd +4 -0
  125. sage/combinat/crystals/pbw_datum.pyx +487 -0
  126. sage/combinat/crystals/polyhedral_realization.py +372 -0
  127. sage/combinat/crystals/spins.cpython-314-x86_64-linux-musl.so +0 -0
  128. sage/combinat/crystals/spins.pxd +21 -0
  129. sage/combinat/crystals/spins.pyx +756 -0
  130. sage/combinat/crystals/star_crystal.py +290 -0
  131. sage/combinat/crystals/subcrystal.py +464 -0
  132. sage/combinat/crystals/tensor_product.py +1177 -0
  133. sage/combinat/crystals/tensor_product_element.cpython-314-x86_64-linux-musl.so +0 -0
  134. sage/combinat/crystals/tensor_product_element.pxd +35 -0
  135. sage/combinat/crystals/tensor_product_element.pyx +1870 -0
  136. sage/combinat/crystals/virtual_crystal.py +420 -0
  137. sage/combinat/cyclic_sieving_phenomenon.py +204 -0
  138. sage/combinat/debruijn_sequence.cpython-314-x86_64-linux-musl.so +0 -0
  139. sage/combinat/debruijn_sequence.pyx +355 -0
  140. sage/combinat/decorated_permutation.py +270 -0
  141. sage/combinat/degree_sequences.cpython-314-x86_64-linux-musl.so +0 -0
  142. sage/combinat/degree_sequences.pyx +588 -0
  143. sage/combinat/derangements.py +527 -0
  144. sage/combinat/descent_algebra.py +1008 -0
  145. sage/combinat/diagram.py +1551 -0
  146. sage/combinat/diagram_algebras.py +5886 -0
  147. sage/combinat/dyck_word.py +4349 -0
  148. sage/combinat/e_one_star.py +1623 -0
  149. sage/combinat/enumerated_sets.py +123 -0
  150. sage/combinat/expnums.cpython-314-x86_64-linux-musl.so +0 -0
  151. sage/combinat/expnums.pyx +148 -0
  152. sage/combinat/fast_vector_partitions.cpython-314-x86_64-linux-musl.so +0 -0
  153. sage/combinat/fast_vector_partitions.pyx +346 -0
  154. sage/combinat/fqsym.py +1977 -0
  155. sage/combinat/free_dendriform_algebra.py +954 -0
  156. sage/combinat/free_prelie_algebra.py +1141 -0
  157. sage/combinat/fully_commutative_elements.py +1077 -0
  158. sage/combinat/fully_packed_loop.py +1523 -0
  159. sage/combinat/gelfand_tsetlin_patterns.py +1409 -0
  160. sage/combinat/gray_codes.py +311 -0
  161. sage/combinat/grossman_larson_algebras.py +667 -0
  162. sage/combinat/growth.py +4352 -0
  163. sage/combinat/hall_polynomial.py +188 -0
  164. sage/combinat/hillman_grassl.py +866 -0
  165. sage/combinat/integer_matrices.py +329 -0
  166. sage/combinat/integer_vectors_mod_permgroup.py +1238 -0
  167. sage/combinat/k_tableau.py +4564 -0
  168. sage/combinat/kazhdan_lusztig.py +215 -0
  169. sage/combinat/key_polynomial.py +885 -0
  170. sage/combinat/knutson_tao_puzzles.py +2286 -0
  171. sage/combinat/lr_tableau.py +311 -0
  172. sage/combinat/matrices/all.py +24 -0
  173. sage/combinat/matrices/hadamard_matrix.py +3790 -0
  174. sage/combinat/matrices/latin.py +2912 -0
  175. sage/combinat/misc.py +401 -0
  176. sage/combinat/multiset_partition_into_sets_ordered.py +3541 -0
  177. sage/combinat/ncsf_qsym/all.py +21 -0
  178. sage/combinat/ncsf_qsym/combinatorics.py +317 -0
  179. sage/combinat/ncsf_qsym/generic_basis_code.py +1427 -0
  180. sage/combinat/ncsf_qsym/ncsf.py +5637 -0
  181. sage/combinat/ncsf_qsym/qsym.py +4053 -0
  182. sage/combinat/ncsf_qsym/tutorial.py +447 -0
  183. sage/combinat/ncsym/all.py +21 -0
  184. sage/combinat/ncsym/bases.py +855 -0
  185. sage/combinat/ncsym/dual.py +593 -0
  186. sage/combinat/ncsym/ncsym.py +2076 -0
  187. sage/combinat/necklace.py +551 -0
  188. sage/combinat/non_decreasing_parking_function.py +634 -0
  189. sage/combinat/nu_dyck_word.py +1474 -0
  190. sage/combinat/output.py +861 -0
  191. sage/combinat/parallelogram_polyomino.py +4326 -0
  192. sage/combinat/parking_functions.py +1602 -0
  193. sage/combinat/partition_algebra.py +1998 -0
  194. sage/combinat/partition_kleshchev.py +1982 -0
  195. sage/combinat/partition_shifting_algebras.py +584 -0
  196. sage/combinat/partition_tuple.py +3114 -0
  197. sage/combinat/path_tableaux/all.py +13 -0
  198. sage/combinat/path_tableaux/catalog.py +29 -0
  199. sage/combinat/path_tableaux/dyck_path.py +380 -0
  200. sage/combinat/path_tableaux/frieze.py +476 -0
  201. sage/combinat/path_tableaux/path_tableau.py +728 -0
  202. sage/combinat/path_tableaux/semistandard.py +510 -0
  203. sage/combinat/perfect_matching.py +779 -0
  204. sage/combinat/plane_partition.py +3300 -0
  205. sage/combinat/q_bernoulli.cpython-314-x86_64-linux-musl.so +0 -0
  206. sage/combinat/q_bernoulli.pyx +128 -0
  207. sage/combinat/quickref.py +81 -0
  208. sage/combinat/recognizable_series.py +2051 -0
  209. sage/combinat/regular_sequence.py +4316 -0
  210. sage/combinat/regular_sequence_bounded.py +543 -0
  211. sage/combinat/restricted_growth.py +81 -0
  212. sage/combinat/ribbon.py +20 -0
  213. sage/combinat/ribbon_shaped_tableau.py +489 -0
  214. sage/combinat/ribbon_tableau.py +1180 -0
  215. sage/combinat/rigged_configurations/all.py +46 -0
  216. sage/combinat/rigged_configurations/bij_abstract_class.py +548 -0
  217. sage/combinat/rigged_configurations/bij_infinity.py +370 -0
  218. sage/combinat/rigged_configurations/bij_type_A.py +163 -0
  219. sage/combinat/rigged_configurations/bij_type_A2_dual.py +338 -0
  220. sage/combinat/rigged_configurations/bij_type_A2_even.py +218 -0
  221. sage/combinat/rigged_configurations/bij_type_A2_odd.py +199 -0
  222. sage/combinat/rigged_configurations/bij_type_B.py +900 -0
  223. sage/combinat/rigged_configurations/bij_type_C.py +267 -0
  224. sage/combinat/rigged_configurations/bij_type_D.py +771 -0
  225. sage/combinat/rigged_configurations/bij_type_D_tri.py +392 -0
  226. sage/combinat/rigged_configurations/bij_type_D_twisted.py +576 -0
  227. sage/combinat/rigged_configurations/bij_type_E67.py +402 -0
  228. sage/combinat/rigged_configurations/bijection.py +143 -0
  229. sage/combinat/rigged_configurations/kleber_tree.py +1475 -0
  230. sage/combinat/rigged_configurations/kr_tableaux.py +1898 -0
  231. sage/combinat/rigged_configurations/rc_crystal.py +461 -0
  232. sage/combinat/rigged_configurations/rc_infinity.py +540 -0
  233. sage/combinat/rigged_configurations/rigged_configuration_element.py +2403 -0
  234. sage/combinat/rigged_configurations/rigged_configurations.py +1918 -0
  235. sage/combinat/rigged_configurations/rigged_partition.cpython-314-x86_64-linux-musl.so +0 -0
  236. sage/combinat/rigged_configurations/rigged_partition.pxd +15 -0
  237. sage/combinat/rigged_configurations/rigged_partition.pyx +680 -0
  238. sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py +499 -0
  239. sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py +428 -0
  240. sage/combinat/rsk.py +3438 -0
  241. sage/combinat/schubert_polynomial.py +508 -0
  242. sage/combinat/set_partition.py +3318 -0
  243. sage/combinat/set_partition_iterator.cpython-314-x86_64-linux-musl.so +0 -0
  244. sage/combinat/set_partition_iterator.pyx +136 -0
  245. sage/combinat/set_partition_ordered.py +1590 -0
  246. sage/combinat/sf/abreu_nigro.py +346 -0
  247. sage/combinat/sf/all.py +52 -0
  248. sage/combinat/sf/character.py +576 -0
  249. sage/combinat/sf/classical.py +319 -0
  250. sage/combinat/sf/dual.py +996 -0
  251. sage/combinat/sf/elementary.py +549 -0
  252. sage/combinat/sf/hall_littlewood.py +1028 -0
  253. sage/combinat/sf/hecke.py +336 -0
  254. sage/combinat/sf/homogeneous.py +464 -0
  255. sage/combinat/sf/jack.py +1428 -0
  256. sage/combinat/sf/k_dual.py +1458 -0
  257. sage/combinat/sf/kfpoly.py +447 -0
  258. sage/combinat/sf/llt.py +789 -0
  259. sage/combinat/sf/macdonald.py +2019 -0
  260. sage/combinat/sf/monomial.py +525 -0
  261. sage/combinat/sf/multiplicative.py +113 -0
  262. sage/combinat/sf/new_kschur.py +1786 -0
  263. sage/combinat/sf/ns_macdonald.py +964 -0
  264. sage/combinat/sf/orthogonal.py +246 -0
  265. sage/combinat/sf/orthotriang.py +355 -0
  266. sage/combinat/sf/powersum.py +963 -0
  267. sage/combinat/sf/schur.py +880 -0
  268. sage/combinat/sf/sf.py +1653 -0
  269. sage/combinat/sf/sfa.py +7053 -0
  270. sage/combinat/sf/symplectic.py +253 -0
  271. sage/combinat/sf/witt.py +721 -0
  272. sage/combinat/shifted_primed_tableau.py +2735 -0
  273. sage/combinat/shuffle.py +830 -0
  274. sage/combinat/sidon_sets.py +146 -0
  275. sage/combinat/similarity_class_type.py +1721 -0
  276. sage/combinat/sine_gordon.py +618 -0
  277. sage/combinat/six_vertex_model.py +784 -0
  278. sage/combinat/skew_partition.py +2053 -0
  279. sage/combinat/skew_tableau.py +2989 -0
  280. sage/combinat/sloane_functions.py +8935 -0
  281. sage/combinat/specht_module.py +1403 -0
  282. sage/combinat/species/all.py +48 -0
  283. sage/combinat/species/characteristic_species.py +321 -0
  284. sage/combinat/species/composition_species.py +273 -0
  285. sage/combinat/species/cycle_species.py +284 -0
  286. sage/combinat/species/empty_species.py +155 -0
  287. sage/combinat/species/functorial_composition_species.py +148 -0
  288. sage/combinat/species/generating_series.py +673 -0
  289. sage/combinat/species/library.py +148 -0
  290. sage/combinat/species/linear_order_species.py +169 -0
  291. sage/combinat/species/misc.py +83 -0
  292. sage/combinat/species/partition_species.py +290 -0
  293. sage/combinat/species/permutation_species.py +268 -0
  294. sage/combinat/species/product_species.py +423 -0
  295. sage/combinat/species/recursive_species.py +476 -0
  296. sage/combinat/species/set_species.py +192 -0
  297. sage/combinat/species/species.py +820 -0
  298. sage/combinat/species/structure.py +539 -0
  299. sage/combinat/species/subset_species.py +243 -0
  300. sage/combinat/species/sum_species.py +225 -0
  301. sage/combinat/subword.py +564 -0
  302. sage/combinat/subword_complex.py +2122 -0
  303. sage/combinat/subword_complex_c.cpython-314-x86_64-linux-musl.so +0 -0
  304. sage/combinat/subword_complex_c.pyx +119 -0
  305. sage/combinat/super_tableau.py +821 -0
  306. sage/combinat/superpartition.py +1154 -0
  307. sage/combinat/symmetric_group_algebra.py +3774 -0
  308. sage/combinat/symmetric_group_representations.py +1830 -0
  309. sage/combinat/t_sequences.py +877 -0
  310. sage/combinat/tableau.py +9506 -0
  311. sage/combinat/tableau_residues.py +860 -0
  312. sage/combinat/tableau_tuple.py +5353 -0
  313. sage/combinat/tiling.py +2432 -0
  314. sage/combinat/triangles_FHM.py +777 -0
  315. sage/combinat/tutorial.py +1857 -0
  316. sage/combinat/vector_partition.py +337 -0
  317. sage/combinat/words/abstract_word.py +1722 -0
  318. sage/combinat/words/all.py +59 -0
  319. sage/combinat/words/alphabet.py +268 -0
  320. sage/combinat/words/finite_word.py +7201 -0
  321. sage/combinat/words/infinite_word.py +113 -0
  322. sage/combinat/words/lyndon_word.py +652 -0
  323. sage/combinat/words/morphic.py +351 -0
  324. sage/combinat/words/morphism.py +3878 -0
  325. sage/combinat/words/paths.py +2932 -0
  326. sage/combinat/words/shuffle_product.py +278 -0
  327. sage/combinat/words/suffix_trees.py +1873 -0
  328. sage/combinat/words/word.py +769 -0
  329. sage/combinat/words/word_char.cpython-314-x86_64-linux-musl.so +0 -0
  330. sage/combinat/words/word_char.pyx +847 -0
  331. sage/combinat/words/word_datatypes.cpython-314-x86_64-linux-musl.so +0 -0
  332. sage/combinat/words/word_datatypes.pxd +4 -0
  333. sage/combinat/words/word_datatypes.pyx +1067 -0
  334. sage/combinat/words/word_generators.py +2026 -0
  335. sage/combinat/words/word_infinite_datatypes.py +1218 -0
  336. sage/combinat/words/word_options.py +99 -0
  337. sage/combinat/words/words.py +2396 -0
  338. sage/data_structures/all__sagemath_combinat.py +1 -0
  339. sage/databases/all__sagemath_combinat.py +13 -0
  340. sage/databases/findstat.py +4897 -0
  341. sage/databases/oeis.py +2058 -0
  342. sage/databases/sloane.py +393 -0
  343. sage/dynamics/all__sagemath_combinat.py +14 -0
  344. sage/dynamics/cellular_automata/all.py +7 -0
  345. sage/dynamics/cellular_automata/catalog.py +34 -0
  346. sage/dynamics/cellular_automata/elementary.py +612 -0
  347. sage/dynamics/cellular_automata/glca.py +477 -0
  348. sage/dynamics/cellular_automata/solitons.py +1463 -0
  349. sage/dynamics/finite_dynamical_system.py +1249 -0
  350. sage/dynamics/finite_dynamical_system_catalog.py +382 -0
  351. sage/games/all.py +7 -0
  352. sage/games/hexad.py +704 -0
  353. sage/games/quantumino.py +591 -0
  354. sage/games/sudoku.py +889 -0
  355. sage/games/sudoku_backtrack.cpython-314-x86_64-linux-musl.so +0 -0
  356. sage/games/sudoku_backtrack.pyx +189 -0
  357. sage/groups/all__sagemath_combinat.py +1 -0
  358. sage/groups/indexed_free_group.py +489 -0
  359. sage/libs/all__sagemath_combinat.py +6 -0
  360. sage/libs/lrcalc/__init__.py +1 -0
  361. sage/libs/lrcalc/lrcalc.py +525 -0
  362. sage/libs/symmetrica/__init__.py +7 -0
  363. sage/libs/symmetrica/all.py +101 -0
  364. sage/libs/symmetrica/kostka.pxi +168 -0
  365. sage/libs/symmetrica/part.pxi +193 -0
  366. sage/libs/symmetrica/plet.pxi +42 -0
  367. sage/libs/symmetrica/sab.pxi +196 -0
  368. sage/libs/symmetrica/sb.pxi +332 -0
  369. sage/libs/symmetrica/sc.pxi +192 -0
  370. sage/libs/symmetrica/schur.pxi +956 -0
  371. sage/libs/symmetrica/symmetrica.cpython-314-x86_64-linux-musl.so +0 -0
  372. sage/libs/symmetrica/symmetrica.pxi +1172 -0
  373. sage/libs/symmetrica/symmetrica.pyx +39 -0
  374. sage/monoids/all.py +13 -0
  375. sage/monoids/automatic_semigroup.py +1054 -0
  376. sage/monoids/free_abelian_monoid.py +315 -0
  377. sage/monoids/free_abelian_monoid_element.cpython-314-x86_64-linux-musl.so +0 -0
  378. sage/monoids/free_abelian_monoid_element.pxd +16 -0
  379. sage/monoids/free_abelian_monoid_element.pyx +397 -0
  380. sage/monoids/free_monoid.py +335 -0
  381. sage/monoids/free_monoid_element.py +431 -0
  382. sage/monoids/hecke_monoid.py +65 -0
  383. sage/monoids/string_monoid.py +817 -0
  384. sage/monoids/string_monoid_element.py +547 -0
  385. sage/monoids/string_ops.py +143 -0
  386. sage/monoids/trace_monoid.py +972 -0
  387. sage/rings/all__sagemath_combinat.py +2 -0
  388. sage/sat/all.py +4 -0
  389. sage/sat/boolean_polynomials.py +405 -0
  390. sage/sat/converters/__init__.py +6 -0
  391. sage/sat/converters/anf2cnf.py +14 -0
  392. sage/sat/converters/polybori.py +611 -0
  393. sage/sat/solvers/__init__.py +5 -0
  394. sage/sat/solvers/cryptominisat.py +287 -0
  395. sage/sat/solvers/dimacs.py +783 -0
  396. sage/sat/solvers/picosat.py +228 -0
  397. sage/sat/solvers/sat_lp.py +156 -0
  398. sage/sat/solvers/satsolver.cpython-314-x86_64-linux-musl.so +0 -0
  399. sage/sat/solvers/satsolver.pxd +3 -0
  400. sage/sat/solvers/satsolver.pyx +405 -0
@@ -0,0 +1,1873 @@
1
+ # sage_setup: distribution = sagemath-combinat
2
+ r"""
3
+ Suffix tries and suffix trees
4
+ """
5
+ # ****************************************************************************
6
+ # Copyright (C) 2008 Franco Saliola <saliola@gmail.com>
7
+ #
8
+ # This program is free software: you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation, either version 2 of the License, or
11
+ # (at your option) any later version.
12
+ # https://www.gnu.org/licenses/
13
+ # ****************************************************************************
14
+ from itertools import chain
15
+
16
+ from sage.structure.sage_object import SageObject
17
+ from sage.sets.set import Set
18
+ from sage.combinat.words.words import Words
19
+ from sage.combinat.words.word import Word
20
+ from sage.misc.lazy_import import lazy_import
21
+ from sage.rings.integer import Integer
22
+
23
+ lazy_import('sage.graphs.digraph', 'DiGraph')
24
+
25
+
26
+ # ------------
27
+ # Suffix Tries
28
+ # ------------
29
+
30
+
31
+ class SuffixTrie(SageObject):
32
+ def __init__(self, word):
33
+ r"""
34
+ Construct the suffix trie of the word w.
35
+
36
+ The suffix trie of a finite word w is a data structure representing
37
+ the factors of w. It is a tree whose edges are labelled with
38
+ letters of w, and whose leafs correspond to suffixes of w.
39
+
40
+ This is a straightforward implementation of Algorithm 1 from
41
+ [Ukko1995]_. It constructs the suffix trie of w[:i] from that
42
+ of w[:i-1].
43
+
44
+ A suffix trie is modelled as a deterministic finite-state automaton
45
+ together with the suffix_link map. The set of states corresponds to
46
+ factors of the word (below we write x' for the state corresponding
47
+ to x); these are always 0, 1, .... The state 0 is the initial
48
+ state, and it corresponds to the empty word. For the purposes of
49
+ the algorithm, there is also an auxiliary state -1. The transition
50
+ function t is defined as::
51
+
52
+ t(-1,a) = 0 for all letters a; and
53
+ t(x',a) = y' for all x',y' \in Q such that y = xa,
54
+
55
+ and the suffix link function is defined as::
56
+
57
+ suffix_link(0) = -1;
58
+ suffix_link(x') = y', if x = ay for some letter a.
59
+
60
+ REFERENCES:
61
+
62
+ - [Ukko1995]_
63
+
64
+ EXAMPLES::
65
+
66
+ sage: from sage.combinat.words.suffix_trees import SuffixTrie
67
+ sage: w = Words("cao")("cacao")
68
+ sage: t = SuffixTrie(w); t
69
+ Suffix Trie of the word: cacao
70
+
71
+ ::
72
+
73
+ sage: e = Words("ab")()
74
+ sage: t = SuffixTrie(e); t
75
+ Suffix Trie of the word:
76
+ sage: t.process_letter("a"); t
77
+ Suffix Trie of the word: a
78
+ sage: t.process_letter("b"); t
79
+ Suffix Trie of the word: ab
80
+ sage: t.process_letter("a"); t
81
+ Suffix Trie of the word: aba
82
+
83
+ TESTS::
84
+
85
+ sage: from sage.combinat.words.suffix_trees import SuffixTrie
86
+ sage: w = Words("cao")("cacao")
87
+ sage: s = SuffixTrie(w)
88
+ sage: loads(dumps(s))
89
+ Suffix Trie of the word: cacao
90
+ """
91
+ # Create the suffix trie for the empty word.
92
+ self._active_state = 0
93
+ self._transition_function = {}
94
+ self._suffix_link = [-1]
95
+ self._alphabet = word.parent().alphabet()
96
+
97
+ # Process each letter, in order.
98
+ W = word.parent()
99
+ for letter in word:
100
+ self._process_letter(W([letter]))
101
+
102
+ def _process_letter(self, letter):
103
+ r"""
104
+ Process a letter. That is, modify the current suffix trie producing
105
+ the suffix trie for ``self.word() + letter``.
106
+
107
+ .. NOTE::
108
+
109
+ ``letter`` must occur within the alphabet of the word.
110
+
111
+ EXAMPLES::
112
+
113
+ sage: from sage.combinat.words.suffix_trees import SuffixTrie
114
+ sage: t = SuffixTrie(Word("ababba"))
115
+ sage: t._process_letter(Words("ab")("b")); t
116
+ Suffix Trie of the word: ababbab
117
+ """
118
+ r = self._active_state
119
+ old_s = None
120
+ # While r is not the auxiliary vertex, or
121
+ # there is not transition from r along letter, ...
122
+ while r != -1 and (r, letter) not in self._transition_function:
123
+ # adjoin a new state s
124
+ s = len(self._suffix_link)
125
+ self._suffix_link.append(None)
126
+ # create a transition from r to s along letter
127
+ self._transition_function[(r, letter)] = s
128
+ if r != self._active_state:
129
+ # update the suffix link
130
+ self._suffix_link[old_s] = s
131
+ old_s = s
132
+ r = self._suffix_link[r]
133
+ # update the suffix link for the last visited state
134
+ if r == -1:
135
+ self._suffix_link[old_s] = 0
136
+ else:
137
+ self._suffix_link[old_s] = self._transition_function[(r, letter)]
138
+ # update the active state
139
+ self._active_state = \
140
+ self._transition_function[(self._active_state, letter)]
141
+
142
+ def process_letter(self, letter):
143
+ r"""
144
+ Modify ``self`` to produce the suffix trie for ``self.word() +
145
+ letter``.
146
+
147
+ .. NOTE::
148
+
149
+ ``letter`` must occur within the alphabet of the word.
150
+
151
+ EXAMPLES::
152
+
153
+ sage: from sage.combinat.words.suffix_trees import SuffixTrie
154
+ sage: w = Words("ab")("ababba")
155
+ sage: t = SuffixTrie(w); t
156
+ Suffix Trie of the word: ababba
157
+ sage: t.process_letter("a"); t
158
+ Suffix Trie of the word: ababbaa
159
+
160
+ TESTS::
161
+
162
+ sage: from sage.combinat.words.suffix_trees import SuffixTrie
163
+ sage: w = Words("cao")("cacao")
164
+ sage: t = SuffixTrie(w); t
165
+ Suffix Trie of the word: cacao
166
+ sage: t.process_letter("d")
167
+ Traceback (most recent call last):
168
+ ...
169
+ ValueError: d not in alphabet
170
+ """
171
+ # Make certain that letter is a word containing one letter.
172
+ letter = Words(self._alphabet)([letter])
173
+ self._process_letter(letter)
174
+
175
+ #####
176
+ # The following are not necessary for constructing the suffix trie (just
177
+ # the __init__ and process_letter are needed). They just add additional
178
+ # functionality to the class.
179
+ #####
180
+
181
+ def _repr_(self) -> str:
182
+ """
183
+ TESTS::
184
+
185
+ sage: from sage.combinat.words.suffix_trees import SuffixTrie
186
+ sage: SuffixTrie(Word("abcba"))._repr_()
187
+ 'Suffix Trie of the word: abcba'
188
+ """
189
+ return 'Suffix Trie of the %s' % repr(self.word())
190
+
191
+ def node_to_word(self, state=0):
192
+ r"""
193
+ Return the word obtained by reading the edge labels from 0 to
194
+ ``state``.
195
+
196
+ INPUT:
197
+
198
+ - ``state`` -- (default: 0) a state
199
+
200
+ EXAMPLES::
201
+
202
+ sage: from sage.combinat.words.suffix_trees import SuffixTrie
203
+ sage: w = Words("abc")("abcba")
204
+ sage: t = SuffixTrie(w)
205
+ sage: t.node_to_word(10)
206
+ word: abcba
207
+ sage: t.node_to_word(7)
208
+ word: abcb
209
+ """
210
+ if state == 0:
211
+ return Words(self._alphabet)()
212
+ # We first invert the transition function
213
+ tf_inv = {b: a for a, b in self._transition_function.items()}
214
+
215
+ # Starting from the active state,
216
+ # read labels along the unique path to the root.
217
+ u, letter = tf_inv[state]
218
+ w = letter
219
+ s = u
220
+ while s != 0:
221
+ u, letter = tf_inv[s]
222
+ w = letter * w
223
+ s = u
224
+ return w
225
+
226
+ def word(self):
227
+ r"""
228
+ Return the word whose suffix tree this is.
229
+
230
+ EXAMPLES::
231
+
232
+ sage: from sage.combinat.words.suffix_trees import SuffixTrie
233
+ sage: w = Words("abc")("abcba")
234
+ sage: t = SuffixTrie(w)
235
+ sage: t.word()
236
+ word: abcba
237
+ sage: t.word() == w
238
+ True
239
+ """
240
+ return self.node_to_word(self._active_state)
241
+
242
+ def __eq__(self, other) -> bool:
243
+ r"""
244
+ If ``self`` and ``other`` have the same transition function,
245
+ the same suffix link, and the same word, then they are equal.
246
+
247
+ TESTS::
248
+
249
+ sage: from sage.combinat.words.suffix_trees import SuffixTrie
250
+ sage: SuffixTrie(Word("cacao")) == SuffixTrie(Word("ababc"))
251
+ False
252
+ sage: W = Words("cao")
253
+ sage: s = SuffixTrie(W("cacao"))
254
+ sage: t = SuffixTrie(W())
255
+ sage: t.process_letter("c")
256
+ sage: t.process_letter("a")
257
+ sage: t.process_letter("c")
258
+ sage: t.process_letter("a")
259
+ sage: t.process_letter("o")
260
+ sage: t == s
261
+ True
262
+ """
263
+ if not isinstance(other, SuffixTrie):
264
+ return False
265
+ return self._transition_function == other._transition_function \
266
+ and self._suffix_link == other._suffix_link \
267
+ and self.word() == other.word()
268
+
269
+ def transition_function(self, node, word):
270
+ r"""
271
+ Return the state reached by beginning at ``node`` and following the
272
+ arrows in the transition graph labelled by the letters of ``word``.
273
+
274
+ INPUT:
275
+
276
+ - ``node`` -- a node
277
+ - ``word`` -- a word
278
+
279
+ EXAMPLES::
280
+
281
+ sage: from sage.combinat.words.suffix_trees import SuffixTrie
282
+ sage: w = Words([0,1])([0,1,0,1,1])
283
+ sage: t = SuffixTrie(w)
284
+ sage: all(t.transition_function(u, letter) == v
285
+ ....: for (u, letter), v in t._transition_function.items())
286
+ True
287
+ """
288
+ if node == -1:
289
+ return self.transition_function(0, word[1:])
290
+ if word.is_empty():
291
+ return 0
292
+ if word.length() == 1:
293
+ return self._transition_function[(node, word)]
294
+ return self.transition_function(
295
+ self._transition_function[(node, word[0:1])], word[1:])
296
+
297
+ def states(self):
298
+ r"""
299
+ Return the states of the automaton defined by the suffix trie.
300
+
301
+ EXAMPLES::
302
+
303
+ sage: from sage.combinat.words.suffix_trees import SuffixTrie
304
+ sage: w = Words([0,1])([0,1,1])
305
+ sage: t = SuffixTrie(w)
306
+ sage: t.states()
307
+ [0, 1, 2, 3, 4]
308
+
309
+ ::
310
+
311
+ sage: u = Words("aco")("cacao")
312
+ sage: s = SuffixTrie(u)
313
+ sage: s.states()
314
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
315
+ """
316
+ return list(range(len(self._transition_function)))
317
+
318
+ def suffix_link(self, state):
319
+ r"""
320
+ Evaluate the suffix link map of the suffix trie on ``state``.
321
+
322
+ Note that the suffix link map is not defined on -1.
323
+
324
+ INPUT:
325
+
326
+ - ``state`` -- a state
327
+
328
+ EXAMPLES::
329
+
330
+ sage: from sage.combinat.words.suffix_trees import SuffixTrie
331
+ sage: w = Words("cao")("cacao")
332
+ sage: t = SuffixTrie(w)
333
+ sage: list(map(t.suffix_link, range(13)))
334
+ [-1, 0, 3, 0, 5, 1, 7, 2, 9, 10, 11, 12, 0]
335
+ sage: t.suffix_link(0)
336
+ -1
337
+
338
+ TESTS::
339
+
340
+ sage: from sage.combinat.words.suffix_trees import SuffixTrie
341
+ sage: w = Words("cao")("cacao")
342
+ sage: t = SuffixTrie(w)
343
+ sage: t.suffix_link([1])
344
+ Traceback (most recent call last):
345
+ ...
346
+ TypeError: [1] is not an integer
347
+ sage: t.suffix_link(-1)
348
+ Traceback (most recent call last):
349
+ ...
350
+ TypeError: suffix link is not defined for -1
351
+ sage: t.suffix_link(17)
352
+ Traceback (most recent call last):
353
+ ...
354
+ TypeError: 17 is not a state
355
+ """
356
+ if not isinstance(state, (int, Integer)):
357
+ raise TypeError("%s is not an integer" % state)
358
+ if state == -1:
359
+ raise TypeError("suffix link is not defined for -1")
360
+ if state not in range(len(self._suffix_link)):
361
+ raise TypeError("%s is not a state" % state)
362
+ return self._suffix_link[state]
363
+
364
+ def active_state(self):
365
+ r"""
366
+ Return the active state of the suffix trie.
367
+
368
+ This is the state corresponding to the word as a suffix of
369
+ itself.
370
+
371
+ EXAMPLES::
372
+
373
+ sage: from sage.combinat.words.suffix_trees import SuffixTrie
374
+ sage: w = Words("cao")("cacao")
375
+ sage: t = SuffixTrie(w)
376
+ sage: t.active_state()
377
+ 8
378
+
379
+ ::
380
+
381
+ sage: u = Words([0,1])([0,1,1,0,1,0,0,1])
382
+ sage: s = SuffixTrie(u)
383
+ sage: s.active_state()
384
+ 22
385
+ """
386
+ return self._active_state
387
+
388
+ def final_states(self):
389
+ r"""
390
+ Return the set of final states of the suffix trie.
391
+
392
+ These are the states corresponding to the suffixes of
393
+ ``self.word()``. They are obtained be repeatedly following the
394
+ suffix link from the active state until we reach 0.
395
+
396
+ EXAMPLES::
397
+
398
+ sage: from sage.combinat.words.suffix_trees import SuffixTrie
399
+ sage: w = Words("cao")("cacao")
400
+ sage: t = SuffixTrie(w)
401
+ sage: t.final_states() == Set([8, 9, 10, 11, 12, 0])
402
+ True
403
+ """
404
+ s = self._active_state
405
+ F = [s]
406
+ while s != 0:
407
+ s = self._suffix_link[s]
408
+ F.append(s)
409
+ return Set(F)
410
+
411
+ def has_suffix(self, word) -> bool:
412
+ r"""
413
+ Return ``True`` if and only if ``word`` is a suffix of ``self.word()``.
414
+
415
+ EXAMPLES::
416
+
417
+ sage: from sage.combinat.words.suffix_trees import SuffixTrie
418
+ sage: w = Words("cao")("cacao")
419
+ sage: t = SuffixTrie(w)
420
+ sage: [t.has_suffix(w[i:]) for i in range(w.length()+1)]
421
+ [True, True, True, True, True, True]
422
+ sage: [t.has_suffix(w[:i]) for i in range(w.length()+1)]
423
+ [True, False, False, False, False, True]
424
+ """
425
+ # Find the state corresponding to word, and
426
+ # check to see if s is a final state.
427
+ s = self.transition_function(0, word)
428
+ q = self._active_state
429
+ if q == s:
430
+ return True
431
+
432
+ while q != 0:
433
+ q = self._suffix_link[q]
434
+ if q == s:
435
+ return True
436
+ return False
437
+
438
+ def to_digraph(self):
439
+ r"""
440
+ Return a ``DiGraph`` object of the transition graph of the suffix
441
+ trie.
442
+
443
+ EXAMPLES::
444
+
445
+ sage: from sage.combinat.words.suffix_trees import SuffixTrie
446
+ sage: w = Words("cao")("cac")
447
+ sage: t = SuffixTrie(w)
448
+ sage: d = t.to_digraph(); d # needs sage.graphs
449
+ Digraph on 6 vertices
450
+ sage: d.adjacency_matrix() # needs sage.graphs sage.modules
451
+ [0 1 0 1 0 0]
452
+ [0 0 1 0 0 0]
453
+ [0 0 0 0 1 0]
454
+ [0 0 0 0 0 1]
455
+ [0 0 0 0 0 0]
456
+ [0 0 0 0 0 0]
457
+ """
458
+ dag = {}
459
+ for (u, letter), v in self._transition_function.items():
460
+ dag.setdefault(u, {})[v] = letter
461
+ return DiGraph(dag)
462
+
463
+ def plot(self, layout='tree', tree_root=0, tree_orientation='up',
464
+ vertex_colors=None, edge_labels=True, *args, **kwds):
465
+ r"""
466
+ Return a Graphics object corresponding to the transition graph of
467
+ the suffix trie.
468
+
469
+ EXAMPLES::
470
+
471
+ sage: from sage.combinat.words.suffix_trees import SuffixTrie
472
+ sage: SuffixTrie(Word("cacao")).plot() # needs sage.plot
473
+ Graphics object consisting of 38 graphics primitives
474
+
475
+ TESTS::
476
+
477
+ sage: from sage.combinat.words.suffix_trees import SuffixTrie
478
+ sage: type(SuffixTrie(Word("cacao")).plot()) # needs sage.plot
479
+ <class 'sage.plot.graphics.Graphics'>
480
+ """
481
+ tree = self.to_digraph()
482
+ for u, v, label in tree.edge_iterator():
483
+ tree.set_edge_label(u, v, label.string_rep())
484
+ if vertex_colors is None:
485
+ suffix_nodes = self.final_states()
486
+ non_suffix_nodes = list(set(self.states()) - set(suffix_nodes))
487
+ vertex_colors = {'#fec7b8': suffix_nodes, '#ffffff': non_suffix_nodes}
488
+ return tree.plot(layout=layout, tree_root=tree_root,
489
+ tree_orientation=tree_orientation,
490
+ vertex_colors=vertex_colors, edge_labels=edge_labels,
491
+ *args, **kwds)
492
+
493
+ def show(self, *args, **kwds):
494
+ r"""
495
+ Display the output of :meth:`plot`.
496
+
497
+ EXAMPLES::
498
+
499
+ sage: from sage.combinat.words.suffix_trees import SuffixTrie
500
+ sage: w = Words("cao")("cac")
501
+ sage: t = SuffixTrie(w)
502
+ sage: t.show() # needs sage.plot
503
+ """
504
+ self.plot(*args, **kwds).show()
505
+
506
+
507
+ # ------------
508
+ # Suffix Trees
509
+ # ------------
510
+
511
+
512
+ class ImplicitSuffixTree(SageObject):
513
+ def __init__(self, word):
514
+ r"""
515
+ Construct the implicit suffix tree of a word w.
516
+
517
+ The suffix tree of a word w is a compactification of the suffix
518
+ trie for w. The compactification removes all nodes that have
519
+ exactly one incoming edge and exactly one outgoing edge. It
520
+ consists of two components: a tree and a word. Thus, instead of
521
+ labelling the edges by factors of w, we can labelled them by
522
+ indices of the occurrence of the factors in w.
523
+
524
+ The following is a straightforward implementation of Ukkonen's
525
+ on-line algorithm for constructing the
526
+ implicit suffix tree [Ukko1995]_. It constructs the suffix tree for
527
+ w[:i] from that of w[:i-1].
528
+
529
+ GENERAL IDEA. The suffix tree of w[:i+1] can be obtained from that
530
+ of w[:i] by visiting each node corresponding to a suffix of w[:i]
531
+ and modifying the tree by applying one of two rules (either append
532
+ a new node to the tree, or split an edge into two). The "active
533
+ state" is the node where the algorithm begins and the "suffix link"
534
+ carries us to the next node that needs to be dealt with.
535
+
536
+ TREE. The tree is modelled as an automaton, which is stored as a
537
+ dictionary of dictionaries: it is keyed by the nodes of the tree,
538
+ and the corresponding dictionary is keyed by pairs `(i,j)` of
539
+ integers representing the word w[i-1:j]. This makes it faster to
540
+ look up a particular transition beginning at a specific node.
541
+
542
+ STATES/NODES. The states will always be -1, 0, 1, ..., n. The state
543
+ -1 is special and is only used for the purposes of the algorithm.
544
+ All transitions map -1 to 0, so this information is not explicitly
545
+ stored in the transition function.
546
+
547
+ EXPLICIT/IMPLICIT NODES. By definition, some of the nodes will not
548
+ be states, but merely locations along an edge; these are called
549
+ implicit nodes. A node r (implicit or explicit) is referenced as a
550
+ pair (s,(k,p)) where s is an ancestor of r and w[k-1:p] is the word
551
+ read by transitioning from s to r in the suffix trie. A reference
552
+ pair is canonical if s is the closest ancestor of r.
553
+
554
+ SUFFIX LINK. The algorithm makes use of a map from (some) nodes to
555
+ other nodes, called the suffix link. This is stored as a
556
+ dictionary.
557
+
558
+ ACTIVE STATE. We store as ._active_state the active state of the
559
+ tree, the state where the algorithm will begin when processing the
560
+ next letter.
561
+
562
+ RUNNING TIME. The running time and storage space of the algorithm
563
+ is linear in the length of the word w (whereas for a suffix tree it
564
+ is quadratic).
565
+
566
+ REFERENCES:
567
+
568
+ - [Ukko1995]_
569
+
570
+ EXAMPLES::
571
+
572
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree
573
+ sage: w = Words("aco")("cacao")
574
+ sage: t = ImplicitSuffixTree(w); t
575
+ Implicit Suffix Tree of the word: cacao
576
+ sage: ababb = Words([0,1])([0,1,0,1,1])
577
+ sage: s = ImplicitSuffixTree(ababb); s
578
+ Implicit Suffix Tree of the word: 01011
579
+
580
+ TESTS::
581
+
582
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree
583
+ sage: w = Words("cao")("cacao")
584
+ sage: s = ImplicitSuffixTree(w)
585
+ sage: loads(dumps(s))
586
+ Implicit Suffix Tree of the word: cacao
587
+ """
588
+ # For constructing the suffix tree.
589
+ self._transition_function = {0: {}}
590
+ self._suffix_link = {0: -1}
591
+ self._active_state = (0, (1, 1))
592
+ self._letters = []
593
+ for letter in word:
594
+ self._letters.append(letter)
595
+ self._process_letter(letter)
596
+ # _word is not needed for constructing the suffix tree,
597
+ # but it is useful for the other methods.
598
+ self._word = word
599
+
600
+ def _process_letter(self, letter):
601
+ r"""
602
+ This is the main part of Ukkonen's algorithm.
603
+
604
+ This corresponds to the algorithm "update" in [Ukko1995]_.
605
+
606
+ .. NOTE::
607
+
608
+ This function is a helper and does not update ``self._data`` and
609
+ ``self._word``.
610
+
611
+ REFERENCES:
612
+
613
+ - [Ukko1995]_
614
+
615
+ TESTS::
616
+
617
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree
618
+ sage: w = Words("aco")("caca")
619
+ sage: t = ImplicitSuffixTree(w); t
620
+ Implicit Suffix Tree of the word: caca
621
+ sage: new_letter = "o"
622
+ sage: t._letters.append("o")
623
+ sage: t._process_letter("o")
624
+ sage: t._word = Words("aco")("cacao")
625
+ sage: t
626
+ Implicit Suffix Tree of the word: cacao
627
+
628
+ ::
629
+
630
+ sage: W = Words([0,1])
631
+ sage: s = ImplicitSuffixTree(W([0,1,0,1])); s
632
+ Implicit Suffix Tree of the word: 0101
633
+ sage: s._letters.append(1)
634
+ sage: s._process_letter(1)
635
+ sage: s._word = W([0,1,0,1,1])
636
+ sage: s
637
+ Implicit Suffix Tree of the word: 01011
638
+ """
639
+ s, (k, i) = self._active_state
640
+ old_r = 0
641
+ end_state, r = self._test_and_split(s, (k, i-1), letter)
642
+ while not end_state:
643
+ # adjoin a new state rr and create a transition from r to rr
644
+ rr = len(self._transition_function)
645
+ self._transition_function[rr] = {}
646
+ self._transition_function[r][(i, None)] = rr
647
+ # update the suffix link, if necessary
648
+ if old_r != 0:
649
+ self._suffix_link[old_r] = r
650
+ old_r = r
651
+ # follow the suffix link to the next state
652
+ s, k = self._canonize(self._suffix_link[s], (k, i-1))
653
+ end_state, r = self._test_and_split(s, (k, i-1), letter)
654
+ # update the suffix link, if necessary
655
+ if old_r != 0:
656
+ self._suffix_link[old_r] = s
657
+ # set the active state
658
+ s, k = self._canonize(s, (k, i))
659
+ self._active_state = (s, (k, i+1))
660
+
661
+ def _test_and_split(self, s, k_p, letter):
662
+ r"""
663
+ Helper function for :meth:`_process_letter`. Test to see
664
+ whether an edge needs to be split.
665
+
666
+ OUTPUT:
667
+
668
+ ``(True, state)``, where ``state`` is the next state to
669
+ process (either a newly created state or the original ``s``).
670
+
671
+ TESTS::
672
+
673
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree
674
+ sage: w = Words("aco")("caca")
675
+ sage: t = ImplicitSuffixTree(w)
676
+ sage: t._letters.append(w.parent().alphabet().rank("o"))
677
+ sage: t._test_and_split(0, (4,5), w.parent().alphabet().rank("o"))
678
+ (False, 3)
679
+ """
680
+ k, p = k_p
681
+ if k <= p:
682
+ # find the transition from s that begins with k-th letter
683
+ (kk, pp), ss = self._find_transition(s, self._letters[k - 1])
684
+ if letter == self._letters[kk + p - k]:
685
+ return (True, s)
686
+ else:
687
+ # replace transition above by transitions
688
+ del self._transition_function[s][(kk, pp)]
689
+ r = len(self._transition_function)
690
+ self._transition_function[r] = {}
691
+ self._transition_function[s][(kk, kk+p-k)] = r
692
+ self._transition_function[r][(kk+p-k+1, pp)] = ss
693
+ return (False, r)
694
+ else:
695
+ transition = self._find_transition(s, letter)
696
+ if transition is None:
697
+ return (False, s)
698
+ else:
699
+ return (True, s)
700
+
701
+ def _canonize(self, s, k_p):
702
+ r"""
703
+ Given an implicit or explicit reference pair for a node, return
704
+ the canonical reference pair.
705
+
706
+ Recall that a node r is referenced as (s, (k,p)), where s is an
707
+ ancestor or r and w[k-1:p] is the word obtained by reading the edge
708
+ labels along the path from s to r. A reference pair is canonical if
709
+ s is the closest ancestor of r.
710
+
711
+ TESTS::
712
+
713
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree
714
+ sage: t = ImplicitSuffixTree(Word("cacao"))
715
+ sage: t._canonize(0,(3,5))
716
+ (3, 5)
717
+ sage: t._canonize(0,(2,5))
718
+ (5, 3)
719
+ """
720
+ k, p = k_p
721
+ if p < k:
722
+ return (s, k)
723
+ else:
724
+ (kk, pp), ss = self._find_transition(s, self._letters[k - 1])
725
+ while pp is not None and pp - kk <= p - k:
726
+ k = k + pp - kk + 1
727
+ s = ss
728
+ if k <= p:
729
+ (kk, pp), ss = self._find_transition(s, self._letters[k-1])
730
+ return (s, k)
731
+
732
+ def _find_transition(self, state, letter):
733
+ r"""
734
+ Return the transition from state that begins with letter.
735
+
736
+ This returns ``None`` if no such transition exists.
737
+
738
+ The transitions are stored as a dictionary of dictionaries: keyed
739
+ by the nodes, with the corresponding dictionary keyed by pairs
740
+ `(i,j)` of integers representing the word w[i-1:j].
741
+
742
+ ._transition_function = {..., node: {(i,j): target_node, ...} }
743
+
744
+ TESTS::
745
+
746
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree
747
+ sage: t = ImplicitSuffixTree(Word("cacao"))
748
+ sage: t._find_transition(-1, "c")
749
+ ((0, 0), 0)
750
+ sage: t._find_transition(0, "a")
751
+ ((2, 2), 5)
752
+ sage: t._find_transition(0, "c")
753
+ ((1, 2), 3)
754
+ sage: t._find_transition(5, "c")
755
+ ((3, None), 2)
756
+ sage: t._find_transition(5, "a")
757
+
758
+ ::
759
+
760
+ sage: t = ImplicitSuffixTree(Word([0,1,0,1,1]))
761
+ sage: t._find_transition(3, 1)
762
+ ((5, None), 4)
763
+ """
764
+ if state == -1:
765
+ return ((0, 0), 0)
766
+
767
+ if state in self._transition_function:
768
+ for (k, p), s in self._transition_function[state].items():
769
+ if self._letters[k - 1] == letter:
770
+ return ((k, p), s)
771
+ return None
772
+
773
+ # The following are not necessary for constructing the implicit suffix
774
+ # tree; they add additional functionality to the class.
775
+
776
+ # -------------
777
+ # Visualization
778
+ # -------------
779
+
780
+ def _repr_(self) -> str:
781
+ r"""
782
+ TESTS::
783
+
784
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree
785
+ sage: ImplicitSuffixTree(Word("abcba"))._repr_()
786
+ 'Implicit Suffix Tree of the word: abcba'
787
+ """
788
+ return 'Implicit Suffix Tree of the %s' % repr(self.word())
789
+
790
+ def word(self):
791
+ r"""
792
+ Return the word whose implicit suffix tree this is.
793
+
794
+ TESTS::
795
+
796
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree
797
+ sage: ImplicitSuffixTree(Word([0,1,0,1,0])).word() == Word([0,1,0,1,0])
798
+ True
799
+ """
800
+ return self._word
801
+
802
+ def transition_function_dictionary(self) -> dict:
803
+ r"""
804
+ Return the transition function as a dictionary of dictionaries.
805
+
806
+ The format is consistent with the input format for ``DiGraph``.
807
+
808
+ EXAMPLES::
809
+
810
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree
811
+ sage: W = Words("aco")
812
+ sage: t = ImplicitSuffixTree(W("cac"))
813
+ sage: t.transition_function_dictionary()
814
+ {0: {1: (0, None), 2: (1, None)}}
815
+
816
+ ::
817
+
818
+ sage: W = Words([0,1])
819
+ sage: t = ImplicitSuffixTree(W([0,1,0]))
820
+ sage: t.transition_function_dictionary()
821
+ {0: {1: (0, None), 2: (1, None)}}
822
+ """
823
+ d = {}
824
+ for u, v, (i, j) in self.edge_iterator():
825
+ d.setdefault(u, {})[v] = (i, j)
826
+ return d
827
+
828
+ def to_digraph(self, word_labels=False):
829
+ r"""
830
+ Return a ``DiGraph`` object of the transition graph of the suffix tree.
831
+
832
+ INPUT:
833
+
834
+ - ``word_labels`` -- boolean (default: ``False``); if ``False``, labels
835
+ the edges by pairs `(i, j)`. If ``True``, labels the edges by ``word[i:j]``
836
+
837
+ EXAMPLES::
838
+
839
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree
840
+ sage: W = Words([0,1,2])
841
+ sage: t = ImplicitSuffixTree(W([0,1,0,1,2]))
842
+ sage: t.to_digraph() # needs sage.graphs
843
+ Digraph on 8 vertices
844
+ """
845
+ if not self._letters:
846
+ d = {0: {}}
847
+ return DiGraph(d)
848
+ d = self.transition_function_dictionary()
849
+ for u in d:
850
+ for v, (i, j) in d[u].items():
851
+ if word_labels:
852
+ d[u][v] = self._word[i:j]
853
+ elif j is None:
854
+ d[u][v] = (i, len(self._letters))
855
+ return DiGraph(d)
856
+
857
+ def plot(self, word_labels=False, layout='tree', tree_root=0,
858
+ tree_orientation='up', vertex_colors=None, edge_labels=True,
859
+ *args, **kwds):
860
+ r"""
861
+ Return a Graphics object corresponding to the transition graph of
862
+ the suffix tree.
863
+
864
+ INPUT:
865
+
866
+ - ``word_labels`` -- boolean (default: ``False``); if ``False``, labels
867
+ the edges by pairs `(i, j)`; if ``True``, labels the edges by ``word[i:j]``
868
+ - ``layout`` -- (default: ``'tree'``)
869
+ - ``tree_root`` -- (default: 0)
870
+ - ``tree_orientation`` -- (default: ``'up'``)
871
+ - ``vertex_colors`` -- (default: ``None``)
872
+ - ``edge_labels`` -- (default: ``True``)
873
+
874
+ EXAMPLES::
875
+
876
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree
877
+ sage: ImplicitSuffixTree(Word('cacao')).plot(word_labels=True) # needs sage.graphs sage.plot
878
+ Graphics object consisting of 23 graphics primitives
879
+ sage: ImplicitSuffixTree(Word('cacao')).plot(word_labels=False) # needs sage.graphs sage.plot
880
+ Graphics object consisting of 23 graphics primitives
881
+
882
+ TESTS::
883
+
884
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree
885
+ sage: type(ImplicitSuffixTree(Word('cacao')).plot(word_labels=True)) # needs sage.graphs sage.plot
886
+ <class 'sage.plot.graphics.Graphics'>
887
+ sage: type(ImplicitSuffixTree(Word('cacao')).plot(word_labels=False)) # needs sage.graphs sage.plot
888
+ <class 'sage.plot.graphics.Graphics'>
889
+ """
890
+ tree = self.to_digraph(word_labels=word_labels)
891
+ if word_labels:
892
+ for u, v, label in tree.edge_iterator():
893
+ tree.set_edge_label(u, v, label.string_rep())
894
+ if vertex_colors is None:
895
+ vertex_colors = {'#fec7b8': tree.vertices(sort=True)}
896
+ return tree.plot(layout=layout, tree_root=tree_root,
897
+ tree_orientation=tree_orientation,
898
+ vertex_colors=vertex_colors, edge_labels=edge_labels,
899
+ *args, **kwds)
900
+
901
+ def show(self, word_labels=None, *args, **kwds):
902
+ r"""
903
+ Display the output of :meth:`plot`.
904
+
905
+ INPUT:
906
+
907
+ - ``word_labels`` -- (default: ``None``) if ``False``, labels the edges
908
+ by pairs `(i, j)`; if ``True``, labels the edges by ``word[i:j]``
909
+
910
+ EXAMPLES::
911
+
912
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree
913
+ sage: w = Words("cao")("cacao")
914
+ sage: t = ImplicitSuffixTree(w)
915
+ sage: t.show(word_labels=True) # needs sage.plot
916
+ sage: t.show(word_labels=False) # needs sage.plot
917
+ """
918
+ self.plot(word_labels=word_labels, *args, **kwds).show()
919
+
920
+ # ---------------
921
+ # Various methods
922
+ # ---------------
923
+
924
+ def __eq__(self, other) -> bool:
925
+ r"""
926
+ If ``self`` and ``other`` have the same transition function
927
+ and the same word, then they are equal.
928
+
929
+ TESTS::
930
+
931
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree
932
+ sage: w = Words([0,1,2])([0,1,0,1,2])
933
+ sage: u = Words([0,1,2])(iter([0,1,0,1,2]))[:5]
934
+ sage: ImplicitSuffixTree(w) == ImplicitSuffixTree(u)
935
+ True
936
+ """
937
+ if not isinstance(other, ImplicitSuffixTree):
938
+ return False
939
+ return self._transition_function == other._transition_function \
940
+ and self._letters == other._letters
941
+
942
+ def transition_function(self, word, node=0):
943
+ r"""
944
+ Return the node obtained by starting from ``node`` and following the
945
+ edges labelled by the letters of ``word``.
946
+
947
+ OUTPUT:
948
+
949
+ ``("explicit", end_node)`` if we end at ``end_node``, or
950
+ ``("implicit", edge, d)`` if we end `d` spots along an edge.
951
+
952
+ INPUT:
953
+
954
+ - ``word`` -- a word
955
+ - ``node`` -- (default: 0) starting node
956
+
957
+ EXAMPLES::
958
+
959
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree
960
+ sage: W = Words([0,1,2])
961
+ sage: t = ImplicitSuffixTree(W([0,1,0,1,2]))
962
+ sage: t.transition_function(W([0,1,0]))
963
+ ('implicit', (3, 1), 1)
964
+ sage: t.transition_function(W([0,1,2]))
965
+ ('explicit', 4)
966
+ sage: t.transition_function(W([0,1,2]), 5)
967
+ ('explicit', 2)
968
+ sage: t.transition_function(W([0,1]), 5)
969
+ ('implicit', (5, 2), 2)
970
+ """
971
+ if word.is_empty():
972
+ return "explicit", node
973
+ (k, p), s = self._find_transition(node, word[0])
974
+ if p is None:
975
+ # test that word is a prefix of self._letters[k-1:]
976
+ if word == self._word[k-1:(k-1)+word.length()]:
977
+ if word.length() == len(self._letters) - k + 1:
978
+ return "explicit", s
979
+ else:
980
+ edge = (node, s)
981
+ return "implicit", edge, word.length()
982
+ else:
983
+ # find longest common prefix
984
+ m = min(p-k+1, word.length())
985
+ i = 0
986
+ while i < m and self._word[k-1+i] == word[i]:
987
+ i += 1
988
+ if i == p-k+1:
989
+ return self.transition_function(word[p-k+1:], s)
990
+ else:
991
+ edge = (node, s)
992
+ return "implicit", edge, i
993
+ return "explicit", node
994
+
995
+ def states(self) -> list:
996
+ r"""
997
+ Return the states (explicit nodes) of the suffix tree.
998
+
999
+ EXAMPLES::
1000
+
1001
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree
1002
+ sage: W = Words([0,1,2])
1003
+ sage: t = ImplicitSuffixTree(W([0,1,0,1,2]))
1004
+ sage: t.states()
1005
+ [0, 1, 2, 3, 4, 5, 6, 7]
1006
+ """
1007
+ return list(range(len(self._transition_function)))
1008
+
1009
+ def suffix_link(self, state):
1010
+ r"""
1011
+ Evaluate the suffix link map of the implicit suffix tree on ``state``.
1012
+
1013
+ Note that the suffix link is not defined for all states.
1014
+
1015
+ The suffix link of a state `x'` that corresponds to the suffix `x` is
1016
+ defined to be -1 is `x'` is the root (0) and `y'` otherwise, where `y'`
1017
+ is the state corresponding to the suffix ``x[1:]``.
1018
+
1019
+ INPUT:
1020
+
1021
+ - ``state`` -- a state
1022
+
1023
+ EXAMPLES::
1024
+
1025
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree
1026
+ sage: W = Words([0,1,2])
1027
+ sage: t = ImplicitSuffixTree(W([0,1,0,1,2]))
1028
+ sage: t.suffix_link(3)
1029
+ 5
1030
+ sage: t.suffix_link(5)
1031
+ 0
1032
+ sage: t.suffix_link(0)
1033
+ -1
1034
+ sage: t.suffix_link(-1)
1035
+ Traceback (most recent call last):
1036
+ ...
1037
+ TypeError: there is no suffix link from -1
1038
+ """
1039
+ if state in self._suffix_link:
1040
+ return self._suffix_link[state]
1041
+ raise TypeError("there is no suffix link from %s" % state)
1042
+
1043
+ def active_state(self):
1044
+ r"""
1045
+ Return the active state of the suffix tree.
1046
+
1047
+ EXAMPLES::
1048
+
1049
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree
1050
+ sage: W = Words([0,1,2])
1051
+ sage: t = ImplicitSuffixTree(W([0,1,0,1,2]))
1052
+ sage: t.active_state()
1053
+ (0, (6, 6))
1054
+ """
1055
+ return self._active_state
1056
+
1057
+ def process_letter(self, letter):
1058
+ r"""
1059
+ Modify the current implicit suffix tree producing the implicit
1060
+ suffix tree for ``self.word() + letter``.
1061
+
1062
+ EXAMPLES::
1063
+
1064
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree
1065
+ sage: w = Words("aco")("cacao")
1066
+ sage: t = ImplicitSuffixTree(w[:-1]); t
1067
+ Implicit Suffix Tree of the word: caca
1068
+ sage: t.process_letter(w[-1]); t
1069
+ Implicit Suffix Tree of the word: cacao
1070
+
1071
+ ::
1072
+
1073
+ sage: W = Words([0,1])
1074
+ sage: s = ImplicitSuffixTree(W([0,1,0,1])); s
1075
+ Implicit Suffix Tree of the word: 0101
1076
+ sage: s.process_letter(W([1])[0]); s
1077
+ Implicit Suffix Tree of the word: 01011
1078
+ """
1079
+ self._word = self._word * self._word._parent([letter])
1080
+ self._letters.append(letter)
1081
+ self._process_letter(letter)
1082
+
1083
+ def to_explicit_suffix_tree(self):
1084
+ r"""
1085
+ Convert ``self`` to an explicit suffix tree.
1086
+
1087
+ It is obtained by processing an end of string letter as if it
1088
+ were a regular letter, except that no new leaf nodes are
1089
+ created (thus, the only thing that happens is that some
1090
+ implicit nodes become explicit).
1091
+
1092
+ EXAMPLES::
1093
+
1094
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree
1095
+ sage: w = Words("aco")("cacao")
1096
+ sage: t = ImplicitSuffixTree(w)
1097
+ sage: t.to_explicit_suffix_tree()
1098
+
1099
+ ::
1100
+
1101
+ sage: W = Words([0,1])
1102
+ sage: s = ImplicitSuffixTree(W([0,1,0,1,1]))
1103
+ sage: s.to_explicit_suffix_tree()
1104
+ """
1105
+ # append a new unique symbol to the word and process the new letter
1106
+ end_of_string = object()
1107
+ self._letters.append(end_of_string)
1108
+ s, (k, i) = self._active_state
1109
+ end_state, r = self._test_and_split(s, (k, i-1), end_of_string)
1110
+ while not end_state:
1111
+ s, k = self._canonize(self._suffix_link[s], (k, i-1))
1112
+ end_state, r = self._test_and_split(s, (k, i-1), end_of_string)
1113
+ # remove the end of string symbol from the word
1114
+ self._letters.pop()
1115
+
1116
+ def edge_iterator(self):
1117
+ r"""
1118
+ Return an iterator over the edges of the suffix tree.
1119
+
1120
+ The edge from `u` to `v` labelled by `(i,j)` is yielded as
1121
+ the tuple `(u,v,(i,j))`.
1122
+
1123
+ EXAMPLES::
1124
+
1125
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree
1126
+ sage: sorted( ImplicitSuffixTree(Word("aaaaa")).edge_iterator() )
1127
+ [(0, 1, (0, None))]
1128
+ sage: sorted( ImplicitSuffixTree(Word([0,1,0,1])).edge_iterator() )
1129
+ [(0, 1, (0, None)), (0, 2, (1, None))]
1130
+ sage: sorted( ImplicitSuffixTree(Word()).edge_iterator() )
1131
+ []
1132
+ """
1133
+ queue = [0]
1134
+ while queue:
1135
+ v = queue.pop()
1136
+ for (i, j), u in self._transition_function[v].items():
1137
+ yield (v, u, (i - 1, j))
1138
+ queue.append(u)
1139
+
1140
+ def number_of_factors(self, n=None):
1141
+ r"""
1142
+ Count the number of distinct factors of ``self.word()``.
1143
+
1144
+ INPUT:
1145
+
1146
+ - ``n`` -- integer or ``None``
1147
+
1148
+ OUTPUT:
1149
+
1150
+ If ``n`` is an integer, returns the number of distinct factors
1151
+ of length ``n``. If ``n`` is ``None``, returns the total number of
1152
+ distinct factors.
1153
+
1154
+ EXAMPLES::
1155
+
1156
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree
1157
+ sage: t = ImplicitSuffixTree(Word([1,2,1,3,1,2,1]))
1158
+ sage: t.number_of_factors()
1159
+ 22
1160
+ sage: t.number_of_factors(1)
1161
+ 3
1162
+ sage: t.number_of_factors(9)
1163
+ 0
1164
+ sage: t.number_of_factors(0)
1165
+ 1
1166
+
1167
+ ::
1168
+
1169
+ sage: t = ImplicitSuffixTree(Word("cacao"))
1170
+ sage: t.number_of_factors()
1171
+ 13
1172
+ sage: list(map(t.number_of_factors, range(10)))
1173
+ [1, 3, 3, 3, 2, 1, 0, 0, 0, 0]
1174
+
1175
+ ::
1176
+
1177
+ sage: t = ImplicitSuffixTree(Word("c"*1000))
1178
+ sage: t.number_of_factors()
1179
+ 1001
1180
+ sage: t.number_of_factors(17)
1181
+ 1
1182
+ sage: t.number_of_factors(0)
1183
+ 1
1184
+
1185
+ ::
1186
+
1187
+ sage: ImplicitSuffixTree(Word()).number_of_factors()
1188
+ 1
1189
+
1190
+ ::
1191
+
1192
+ sage: blueberry = ImplicitSuffixTree(Word("blueberry"))
1193
+ sage: blueberry.number_of_factors()
1194
+ 43
1195
+ sage: list(map(blueberry.number_of_factors, range(10)))
1196
+ [1, 6, 8, 7, 6, 5, 4, 3, 2, 1]
1197
+ """
1198
+ if n is None:
1199
+ length_word = self.word().length()
1200
+ num_factors = 1 # empty word
1201
+ for u, v, (i, j) in self.edge_iterator():
1202
+ if j is None:
1203
+ num_factors += length_word - i
1204
+ else:
1205
+ num_factors += j - i
1206
+ elif isinstance(n, (int, Integer)):
1207
+ num_factors = 0
1208
+ queue = [(0, 0)]
1209
+ while queue:
1210
+ v, l = queue.pop()
1211
+ if l == n:
1212
+ num_factors += 1
1213
+ if l < n:
1214
+ if self._transition_function[v]:
1215
+ for (i, j), u in self._transition_function[v].items():
1216
+ if j is None:
1217
+ j = self.word().length()
1218
+ if j - i >= n - l:
1219
+ num_factors += 1
1220
+ else:
1221
+ queue.append((u, l + j - i + 1))
1222
+ else:
1223
+ raise TypeError("not an integer or None: %s" % n)
1224
+ return num_factors
1225
+
1226
+ def factor_iterator(self, n=None):
1227
+ r"""
1228
+ Generate distinct factors of ``self``.
1229
+
1230
+ INPUT:
1231
+
1232
+ - ``n`` -- integer or ``None``
1233
+
1234
+ OUTPUT:
1235
+
1236
+ If ``n`` is an integer, returns an iterator over all distinct
1237
+ factors of length ``n``. If ``n`` is ``None``, returns an iterator
1238
+ generating all distinct factors.
1239
+
1240
+ EXAMPLES::
1241
+
1242
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree
1243
+ sage: sorted( ImplicitSuffixTree(Word("cacao")).factor_iterator() )
1244
+ [word: , word: a, word: ac, word: aca, word: acao, word: ao, word: c, word: ca, word: cac, word: caca, word: cacao, word: cao, word: o]
1245
+ sage: sorted( ImplicitSuffixTree(Word("cacao")).factor_iterator(1) )
1246
+ [word: a, word: c, word: o]
1247
+ sage: sorted( ImplicitSuffixTree(Word("cacao")).factor_iterator(2) )
1248
+ [word: ac, word: ao, word: ca]
1249
+ sage: sorted( ImplicitSuffixTree(Word([0,0,0])).factor_iterator() )
1250
+ [word: , word: 0, word: 00, word: 000]
1251
+ sage: sorted( ImplicitSuffixTree(Word([0,0,0])).factor_iterator(2) )
1252
+ [word: 00]
1253
+ sage: sorted( ImplicitSuffixTree(Word([0,0,0])).factor_iterator(0) )
1254
+ [word: ]
1255
+ sage: sorted( ImplicitSuffixTree(Word()).factor_iterator() )
1256
+ [word: ]
1257
+ sage: sorted( ImplicitSuffixTree(Word()).factor_iterator(2) )
1258
+ []
1259
+ """
1260
+ # Every factor is a prefix of a suffix, so we do a depth
1261
+ # first search of the implicit suffix tree of the word.
1262
+ w = self.word()
1263
+ wlen = self.word().length()
1264
+ if n is None:
1265
+ queue = [(0, 0, -1, 0)]
1266
+ yield w[0:0]
1267
+ while queue:
1268
+ v, i, j, l = queue.pop()
1269
+ for k in range(i, j+1):
1270
+ yield w[j-l:k]
1271
+ for (i, j), u in self._transition_function[v].items():
1272
+ if j is None:
1273
+ j = wlen
1274
+ queue.append((u, i, j, l+j-i+1))
1275
+ elif isinstance(n, (int, Integer)):
1276
+ queue = [(0, 0, -1, 0)]
1277
+ while queue:
1278
+ v, i, j, l = queue.pop()
1279
+ if l == n:
1280
+ yield w[j-l:j]
1281
+ if l < n:
1282
+ for (i, j), u in self._transition_function[v].items():
1283
+ if j is None:
1284
+ j = wlen
1285
+ if j - i >= n - l:
1286
+ yield w[i-l-1:i-l+n-1]
1287
+ else:
1288
+ queue.append((u, i, j, l+j-i+1))
1289
+ else:
1290
+ raise TypeError("not an integer or None: %s" % n)
1291
+
1292
+ def LZ_decomposition(self):
1293
+ r"""
1294
+ Return a list of index of the beginning of the block of the Lempel-Ziv
1295
+ decomposition of ``self.word``
1296
+
1297
+ The *Lempel-Ziv decomposition* is the factorisation `u_1...u_k` of a
1298
+ word `w=x_1...x_n` such that `u_i` is the longest prefix of `u_i...u_k`
1299
+ that has an occurrence starting before `u_i` or a letter if this prefix
1300
+ is empty.
1301
+
1302
+ OUTPUT:
1303
+
1304
+ Return a list ``iB`` of index such that the blocks of the decomposition
1305
+ are ``self.word()[iB[k]:iB[k+1]]``
1306
+
1307
+ EXAMPLES::
1308
+
1309
+ sage: w = Word('abababb')
1310
+ sage: T = w.suffix_tree()
1311
+ sage: T.LZ_decomposition()
1312
+ [0, 1, 2, 6, 7]
1313
+ sage: w = Word('abaababacabba')
1314
+ sage: T = w.suffix_tree()
1315
+ sage: T.LZ_decomposition()
1316
+ [0, 1, 2, 3, 6, 8, 9, 11, 13]
1317
+ sage: w = Word([0, 0, 0, 1, 1, 0, 1])
1318
+ sage: T = w.suffix_tree()
1319
+ sage: T.LZ_decomposition()
1320
+ [0, 1, 3, 4, 5, 7]
1321
+ sage: w = Word('0000100101')
1322
+ sage: T = w.suffix_tree()
1323
+ sage: T.LZ_decomposition()
1324
+ [0, 1, 4, 5, 9, 10]
1325
+ """
1326
+ iB = [0]
1327
+ i = 0
1328
+ w = self.word()
1329
+ while i < len(w):
1330
+ l = 0
1331
+ (x, y), successor = self._find_transition(0, w[i])
1332
+ x = x-1
1333
+ while x < i+l:
1334
+ if y is None:
1335
+ l = len(w)-i
1336
+ else:
1337
+ l += y-x
1338
+ if i+l >= len(w):
1339
+ l = len(w)-i
1340
+ break
1341
+ (x, y), successor = self._find_transition(successor, w[i+l])
1342
+ x = x-1
1343
+ i += max(1, l)
1344
+ iB.append(i)
1345
+ return iB
1346
+
1347
+ def _count_and_skip(self, node, i, j):
1348
+ r"""
1349
+ Use count and skip trick to follow the path starting at ``node`` and
1350
+ reading ``self.word()[i:j]``. We assume that reading
1351
+ ``self.word()[i:j]`` is possible from ``node``
1352
+
1353
+ INPUT:
1354
+
1355
+ - ``node`` -- explicit node of ``self``
1356
+ - ``i`` -- beginning of factor ``T.word()[i:j]``
1357
+ - ``j`` -- end of factor ``T.word()[i:j]``
1358
+
1359
+ OUTPUT:
1360
+
1361
+ The node obtained by starting at ``node`` and following the edges
1362
+ labeled by the letters of ``T.word()[i:j]``.
1363
+ Return ``("explicit", end_node)`` if w ends at the node ``end_node``,
1364
+ and ``("implicit", edge, d)`` if it ends after reading ``d`` letters along
1365
+ the edge ``edge``.
1366
+
1367
+ EXAMPLES::
1368
+
1369
+ sage: T = Word('00110111011').suffix_tree()
1370
+ sage: T._count_and_skip(5, 2, 5)
1371
+ ('implicit', (9, 10), 2)
1372
+ sage: T._count_and_skip(0, 1, 4)
1373
+ ('explicit', 7)
1374
+ sage: T._count_and_skip(0, 8, 10)
1375
+ ('implicit', (2, 7), 1)
1376
+ sage: T = Word('cacao').suffix_tree()
1377
+ sage: T._count_and_skip(3, 2, 5)
1378
+ ('explicit', 1)
1379
+ """
1380
+ trans = self._find_transition(node, self._letters[i])
1381
+ while trans[0][1] is not None and trans[0][1] - trans[0][0] + 1 <= j - i:
1382
+ node = trans[1]
1383
+ i += trans[0][1] - trans[0][0] + 1
1384
+ if i == j:
1385
+ return ('explicit', node)
1386
+ else:
1387
+ trans = self._find_transition(node, self._letters[i])
1388
+ if trans[0][1] is None and len(self.word()) - trans[0][0] + 1 <= j - i:
1389
+ return ('explicit', trans[1])
1390
+ else:
1391
+ return ('implicit', (node, trans[1]), j - i)
1392
+
1393
+ def suffix_walk(self, edge, l):
1394
+ r"""
1395
+ Return the state of "w" if the input state is "aw".
1396
+
1397
+ If the input state ``(edge, l)`` is path labeled "aw" with "a" a letter, the output is
1398
+ the state which is path labeled "w".
1399
+
1400
+ INPUT:
1401
+
1402
+ - ``edge`` -- the edge containing the state
1403
+ - ``l`` -- the string-depth of the state on edge (``l``>0)
1404
+
1405
+ OUTPUT:
1406
+
1407
+ Return ``("explicit", end_node)`` if the state of w is an explicit
1408
+ state and ``("implicit", edge, d)`` is obtained by reading ``d``
1409
+ letters on ``edge``.
1410
+
1411
+ EXAMPLES::
1412
+
1413
+ sage: T = Word('00110111011').suffix_tree()
1414
+ sage: T.suffix_walk((0, 5), 1)
1415
+ ('explicit', 0)
1416
+ sage: T.suffix_walk((7, 3), 1)
1417
+ ('implicit', (9, 4), 1)
1418
+ """
1419
+ start, end = edge
1420
+ # Select the transition that corresponds to edge
1421
+ ij = next(ij for ij, target in self._transition_function[start].items()
1422
+ if target == end)
1423
+
1424
+ # self.word()[i-1:j] is the word on the edges
1425
+ i = ij[0] - 1
1426
+ parent = self.suffix_link(start)
1427
+ return self._count_and_skip(parent, i, i + l)
1428
+
1429
+ def leftmost_covering_set(self):
1430
+ r"""
1431
+ Compute the leftmost covering set of square pairs in ``self.word()``.
1432
+ Return a square as a pair ``(i,l)`` designating factor
1433
+ ``self.word()[i:i+l]``.
1434
+
1435
+ A leftmost covering set is a set such that the leftmost occurrence
1436
+ `(j,l)` of a square in ``self.word()`` is covered by a pair
1437
+ `(i,l)` in the set for all types of squares. We say that `(j,l)` is
1438
+ covered by `(i,l)` if `(i,l)` (i+1,l), \ldots, (j,l)` are all
1439
+ squares.
1440
+
1441
+ The set is returned in the form of a list ``P`` such that ``P[i]``
1442
+ contains all the lengths of squares starting at ``i`` in the set.
1443
+ The lists ``P[i]`` are sorted in decreasing order.
1444
+
1445
+ The algorithm used is described in [DS2004]_.
1446
+
1447
+ EXAMPLES::
1448
+
1449
+ sage: w = Word('abaabaabbaaabaaba')
1450
+ sage: T = w.suffix_tree()
1451
+ sage: T.leftmost_covering_set()
1452
+ [[6], [6], [2], [], [], [], [], [2], [], [], [6, 2], [], [], [], [], [], []]
1453
+ sage: w = Word('abaca')
1454
+ sage: T = w.suffix_tree()
1455
+ sage: T.leftmost_covering_set()
1456
+ [[], [], [], [], []]
1457
+ sage: T = Word('aaaaa').suffix_tree()
1458
+ sage: T.leftmost_covering_set()
1459
+ [[4, 2], [], [], [], []]
1460
+ """
1461
+
1462
+ def condition1_square_pairs(i):
1463
+ r"""
1464
+ Compute the squares that have their center (the last letter of the
1465
+ first occurrence of ``w`` in ``ww``) in the `i`-th block of the
1466
+ LZ-decomposition and that start in the `i`-th block and end in the
1467
+ `(i+1)`-th.
1468
+ """
1469
+ for k in range(1, B[i+1]-B[i]+1):
1470
+ q = B[i+1]-k
1471
+ k1 = w.longest_forward_extension(B[i+1], q) if B[i+1] < len(w) else 0
1472
+ k2 = w.longest_backward_extension(B[i+1]-1, q-1) if q > 0 else 0
1473
+ start = max(q-k2, q-k+1)
1474
+ if k1+k2 >= k and k1 > 0 and start >= B[i]:
1475
+ yield (start, 2*k)
1476
+
1477
+ def condition2_square_pairs(i):
1478
+ r"""
1479
+ Compute the squares that have their center (the last letter of the
1480
+ first occurrence of ``w`` in ``ww``) in the `i`-th block of the
1481
+ LZ-decomposition and that starts in the `(i-1)`-th block or before.
1482
+ Their end is either in the `i`-th or the `(i+1)`-th block.
1483
+ """
1484
+ if i+2 < len(B):
1485
+ end = B[i+2] - B[i] + 1
1486
+ else:
1487
+ end = B[i+1] - B[i] + 1
1488
+ for k in range(2, end):
1489
+ q = B[i] + k
1490
+ k1 = w.longest_forward_extension(B[i], q) if q < len(w) else 0
1491
+ k2 = w.longest_backward_extension(B[i]-1, q-1) if B[i] > 0 else 0
1492
+ start = max(B[i]-k2, B[i]-k+1)
1493
+ if k1+k2 >= k and k1 > 0 and start+k <= B[i+1] and k2 > 0:
1494
+ yield (start, 2*k)
1495
+
1496
+ w = self.word()
1497
+ B = self.LZ_decomposition()
1498
+ P = [[] for _ in w]
1499
+ for i in range(len(B) - 1):
1500
+ for i, l in chain(condition2_square_pairs(i), condition1_square_pairs(i)):
1501
+ P[i].append(l)
1502
+ for l in P:
1503
+ l.reverse()
1504
+ return P
1505
+
1506
+ # ---------------------
1507
+ # Miscellaneous methods
1508
+ # ---------------------
1509
+
1510
+ def uncompactify(self):
1511
+ r"""
1512
+ Return the tree obtained from ``self`` by splitting edges so that they
1513
+ are labelled by exactly one letter.
1514
+
1515
+ The resulting tree is isomorphic to the suffix trie.
1516
+
1517
+ EXAMPLES::
1518
+
1519
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree, SuffixTrie
1520
+ sage: abbab = Words("ab")("abbab")
1521
+ sage: s = SuffixTrie(abbab)
1522
+ sage: t = ImplicitSuffixTree(abbab)
1523
+ sage: t.uncompactify().is_isomorphic(s.to_digraph()) # needs sage.graphs
1524
+ True
1525
+ """
1526
+ tree = self.to_digraph(word_labels=True)
1527
+ newtree = DiGraph()
1528
+ newtree.add_vertices(range(tree.order()))
1529
+ new_node = tree.order() + 1
1530
+ for u, v, label in tree.edge_iterator():
1531
+ if len(label) == 1:
1532
+ newtree.add_edge(u, v)
1533
+ else:
1534
+ newtree.add_edge(u, new_node, label[0])
1535
+ for w in label[1:-1]:
1536
+ newtree.add_edge(new_node, new_node+1, w)
1537
+ new_node += 1
1538
+ newtree.add_edge(new_node, v, label[-1])
1539
+ new_node += 1
1540
+ return newtree
1541
+
1542
+ def trie_type_dict(self):
1543
+ r"""
1544
+ Return a dictionary in a format compatible with that of the suffix
1545
+ trie transition function.
1546
+
1547
+ EXAMPLES::
1548
+
1549
+ sage: from sage.combinat.words.suffix_trees import ImplicitSuffixTree, SuffixTrie
1550
+ sage: W = Words("ab")
1551
+ sage: t = ImplicitSuffixTree(W("aba"))
1552
+ sage: d = t.trie_type_dict()
1553
+ sage: len(d)
1554
+ 5
1555
+ sage: d # random
1556
+ {(4, word: b): 5, (0, word: a): 4, (0, word: b): 3, (5, word: a): 1, (3, word: a): 2}
1557
+ """
1558
+ d = {}
1559
+ new_node = len(self._transition_function)
1560
+ for u, dd in self._transition_function.items():
1561
+ for sl, v in dd.items():
1562
+ w = self._word[sl[0]-1:sl[1]]
1563
+ if w.length() == 1:
1564
+ d[u, w] = v
1565
+ else:
1566
+ d[u, w[0:1]] = new_node
1567
+ for i in range(1, w.length()-1):
1568
+ d[new_node, w[i:i+1]] = new_node + 1
1569
+ new_node += 1
1570
+ d[new_node, w[-1:]] = v
1571
+ new_node += 1
1572
+ return d
1573
+
1574
+
1575
+ # ---------------------
1576
+ # Decorated Suffix Tree
1577
+ # ---------------------
1578
+
1579
+
1580
+ class DecoratedSuffixTree(ImplicitSuffixTree):
1581
+ r"""
1582
+ The decorated suffix tree of a word.
1583
+
1584
+ A *decorated suffix tree* of a word `w` is the suffix tree of `w`
1585
+ marked with the end point of all squares in the `w`.
1586
+
1587
+ The symbol ``$`` is appended to ``w`` to ensure that each final
1588
+ state is a leaf of the suffix tree.
1589
+
1590
+ INPUT:
1591
+
1592
+ - ``w`` -- a finite word
1593
+
1594
+ EXAMPLES::
1595
+
1596
+ sage: from sage.combinat.words.suffix_trees import DecoratedSuffixTree
1597
+ sage: w = Word('0011001')
1598
+ sage: DecoratedSuffixTree(w)
1599
+ Decorated suffix tree of : 0011001$
1600
+ sage: w = Word('0011001', '01')
1601
+ sage: DecoratedSuffixTree(w)
1602
+ Decorated suffix tree of : 0011001$
1603
+
1604
+ ALGORITHM:
1605
+
1606
+ When using ``'pair'`` as output, the squares are retrieved in linear
1607
+ time. The algorithm is an implementation of the one proposed in
1608
+ [DS2004]_.
1609
+ """
1610
+ def __init__(self, w):
1611
+ """
1612
+ Initialize ``self``.
1613
+
1614
+ EXAMPLES::
1615
+
1616
+ sage: from sage.combinat.words.suffix_trees import DecoratedSuffixTree
1617
+ sage: w = Word('0011001')
1618
+ sage: DST = DecoratedSuffixTree(w)
1619
+
1620
+ We skip the ``_test_and_split`` test because it is not a test meant
1621
+ for the ``TestSuite``::
1622
+
1623
+ sage: TestSuite(DST).run(skip='_test_and_split')
1624
+
1625
+ Test that we do not allow ``'$'`` to appear in the word::
1626
+
1627
+ sage: w = Word('0011001$')
1628
+ sage: DecoratedSuffixTree(w)
1629
+ Traceback (most recent call last):
1630
+ ...
1631
+ ValueError: the symbol '$' is reserved for this class
1632
+ """
1633
+ if "$" in w:
1634
+ raise ValueError("the symbol '$' is reserved for this class")
1635
+ end_symbol = '$'
1636
+ w = Word(str(w) + end_symbol)
1637
+ ImplicitSuffixTree.__init__(self, w)
1638
+ self.labeling = self._complete_labeling()
1639
+
1640
+ def __repr__(self):
1641
+ """
1642
+ Return a string representation of ``self``.
1643
+
1644
+ EXAMPLES::
1645
+
1646
+ sage: from sage.combinat.words.suffix_trees import DecoratedSuffixTree
1647
+ sage: w = Word('0011001')
1648
+ sage: t = DecoratedSuffixTree(w)
1649
+ sage: t.__repr__()
1650
+ 'Decorated suffix tree of : 0011001$'
1651
+ """
1652
+ w = self.word()
1653
+ if len(w) > 40:
1654
+ w = str(w[:40]) + '...'
1655
+ return f"Decorated suffix tree of : {w}"
1656
+
1657
+ def _partial_labeling(self):
1658
+ r"""
1659
+ Make a depth-first search in the suffix tree and mark some squares of a
1660
+ leftmost covering set of the tree.
1661
+
1662
+ This is used by :meth:`_complete_labeling`.
1663
+
1664
+ EXAMPLES::
1665
+
1666
+ sage: from sage.combinat.words.suffix_trees import DecoratedSuffixTree
1667
+ sage: w = Word('abaababbabba')
1668
+ sage: T = DecoratedSuffixTree(w)
1669
+ sage: T._partial_labeling()
1670
+ {(3, 4): [1], (5, 1): [3], (5, 6): [1], (11, 17): [1], (13, 8): [1], (15, 10): [2]}
1671
+ """
1672
+ def node_processing(node, parent, head):
1673
+ r"""
1674
+ Marks points along the edge ``(parent, node)`` if the string depth
1675
+ of parent is smaller than the length of the square at the head of
1676
+ ``P(node)``.
1677
+ Make it for all such square pairs and remove them from ``P(node)``.
1678
+
1679
+ INPUT:
1680
+
1681
+ - ``node`` -- a node of ``self``
1682
+ - ``parent`` -- the parent of a node in ``self``
1683
+ - ``head`` -- tuple indicating the head of the list ``P(node)``
1684
+
1685
+ OUTPUT: ``(i, pos)``, the new head of ``P(node)``
1686
+ """
1687
+ i, pos = head
1688
+ pano = (parent, node)
1689
+ while pos < len(P[i]) and P[i][pos] > string_depth[parent]:
1690
+ label = P[i][pos] - string_depth[parent]
1691
+ if pano in labeling:
1692
+ labeling[pano].append(label)
1693
+ else:
1694
+ labeling[pano] = [label]
1695
+ pos += 1
1696
+ return (i, pos)
1697
+
1698
+ def treat_node(current_node, parent):
1699
+ r"""
1700
+ Proceed to a depth-first search in ``self``, counting the
1701
+ string_depth of each node and processing each node for marking.
1702
+
1703
+ To initiate the depth first search call ``self.treat_node(0,None)``
1704
+
1705
+ INPUT:
1706
+
1707
+ - ``current_node`` -- a node
1708
+ - ``parent`` -- parent of ``current_node`` in ``self``
1709
+
1710
+ OUTPUT:
1711
+
1712
+ The resulting list P(current_node) with current_node have been
1713
+ processed by ``node_processing``. The output is a pair ``(i,
1714
+ pos)`` such that ``P[i][pos:]`` is the list of current_node.
1715
+ """
1716
+
1717
+ # Call recursively on children of current_node
1718
+ if current_node in D:
1719
+ node_list = (n, 0)
1720
+ for child in D[current_node]:
1721
+ i, j = D[current_node][child]
1722
+ if j is None:
1723
+ j = n
1724
+ string_depth[child] = string_depth[current_node] + j - i
1725
+ child_list = treat_node(child, current_node)
1726
+ if child_list[0] < node_list[0]:
1727
+ node_list = child_list
1728
+ else: # The node is a child
1729
+ node_list = (n - string_depth[current_node], 0)
1730
+ # Make treatment on current node head
1731
+ return node_processing(current_node, parent, node_list)
1732
+
1733
+ P = self.leftmost_covering_set()
1734
+ D = self.transition_function_dictionary()
1735
+ string_depth = {0: 0}
1736
+ n = len(self.word())
1737
+ labeling = {}
1738
+ treat_node(0, None)
1739
+ return labeling
1740
+
1741
+ def _complete_labeling(self):
1742
+ r"""
1743
+ Return a dictionary of edges of ``self``, with marked points for the end
1744
+ of each distinct squares that can be found in ``self.word()``.
1745
+
1746
+ EXAMPLES::
1747
+
1748
+ sage: from sage.combinat.words.suffix_trees import DecoratedSuffixTree
1749
+ sage: w = Word('aabbaaba')
1750
+ sage: DecoratedSuffixTree(w)._complete_labeling()
1751
+ {(2, 7): [1], (5, 4): [1]}
1752
+ """
1753
+
1754
+ def walk_chain(u, v, l, start):
1755
+ r"""
1756
+ Execute a chain of suffix walk until a walk is unsuccessful or it
1757
+ got to a point already registered in ``QP``. Registers all visited
1758
+ point in ``Q``.
1759
+
1760
+ INPUT:
1761
+
1762
+ - ``(u, v)`` -- edge on which the point is registered
1763
+ - ``l`` -- depth of the registered point on (u,v)
1764
+ - ``start`` -- beginning of the squares registered by the label
1765
+ ``(u, v), l``
1766
+ """
1767
+ # Mark the point in labeling
1768
+ if (u, v) in labeling:
1769
+ labeling[(u, v)].append(l)
1770
+ else:
1771
+ labeling[(u, v)] = [l]
1772
+ # Make the walk
1773
+ final_state = self.suffix_walk((u, v), l)
1774
+ successful = False
1775
+ if final_state[0] == 'explicit':
1776
+ parent = final_state[1]
1777
+ transition = self._find_transition(parent, self._letters[start])
1778
+ if transition is not None:
1779
+ child = transition[1]
1780
+ successful = True
1781
+ depth = 1
1782
+ else:
1783
+ parent = final_state[1][0]
1784
+ child = final_state[1][1]
1785
+ depth = final_state[2]
1786
+ next_letter = self._letters[D[parent][child][0]+depth]
1787
+ if next_letter == self._letters[start]:
1788
+ successful = True
1789
+ depth += 1
1790
+ # If needed start a new walk
1791
+ if successful:
1792
+ if (parent, child) in prelabeling:
1793
+ if depth not in prelabeling[(parent, child)]:
1794
+ walk_chain(parent, child, depth, start+1)
1795
+ else:
1796
+ walk_chain(parent, child, depth, start+1)
1797
+
1798
+ def treat_node(current_node, i, j):
1799
+ r"""
1800
+ Execute a depth-first search on ``self`` and start a suffix walk
1801
+ for labeled points on each edges of T.
1802
+
1803
+ The function is recursive, call
1804
+ ``treat_node(0,0,0)`` to initiate the search.
1805
+
1806
+ INPUT:
1807
+
1808
+ - ``current_node`` -- the node to treat
1809
+ - ``(i, j)`` -- pair of index such that the path from 0 to
1810
+ ``current_node`` reads ``self.word()[i:j]``
1811
+ """
1812
+
1813
+ if current_node in D:
1814
+ for child in D[current_node]:
1815
+ edge = (current_node, child)
1816
+ edge_label = D[edge[0]][edge[1]]
1817
+ treat_node(child, edge_label[0]-(j-i), edge_label[1])
1818
+ if (current_node, child) in prelabeling:
1819
+ for l in prelabeling[edge]:
1820
+ square_start = edge_label[0] - (j - i)
1821
+ walk_chain(current_node, child, l, square_start)
1822
+
1823
+ prelabeling = self._partial_labeling()
1824
+ labeling = {}
1825
+ D = self.transition_function_dictionary()
1826
+ treat_node(0, 0, 0)
1827
+ return labeling
1828
+
1829
+ def square_vocabulary(self, output='pair'):
1830
+ r"""
1831
+ Return the list of distinct squares of ``self.word``.
1832
+
1833
+ Two types of outputs are available `pair` and `word`. The algorithm
1834
+ is only truly linear if `output` is set to `pair`. A pair is a tuple
1835
+ `(i, l)` that indicates the factor ``self.word()[i:i+l]``.
1836
+ The option ``'word'`` return word objects.
1837
+
1838
+ INPUT:
1839
+
1840
+ - ``output`` -- (default: ``'pair'``) either ``'pair'`` or ``'word'``
1841
+
1842
+ EXAMPLES::
1843
+
1844
+ sage: from sage.combinat.words.suffix_trees import DecoratedSuffixTree
1845
+ sage: w = Word('aabb')
1846
+ sage: sorted(DecoratedSuffixTree(w).square_vocabulary())
1847
+ [(0, 0), (0, 2), (2, 2)]
1848
+ sage: w = Word('00110011010')
1849
+ sage: sorted(DecoratedSuffixTree(w).square_vocabulary(output='word'))
1850
+ [word: , word: 00, word: 00110011, word: 01100110, word: 1010, word: 11]
1851
+ """
1852
+ def treat_node(current_node, i, j):
1853
+ if current_node in D:
1854
+ for child in D[current_node]:
1855
+ edge = (current_node, child)
1856
+ edge_label = (D[edge[0]][edge[1]])
1857
+ treat_node(child, edge_label[0]-(j-i), edge_label[1])
1858
+ if (current_node, child) in Q:
1859
+ for l in Q[(current_node, child)]:
1860
+ square_start = edge_label[0]-(j-i)
1861
+ pair = (square_start, edge_label[0]+l-square_start)
1862
+ squares.append(pair)
1863
+
1864
+ if output not in ["pair", "word"]:
1865
+ msg = f"output should be 'pair' or 'word'; got {output}"
1866
+ raise ValueError(msg)
1867
+ D = self.transition_function_dictionary()
1868
+ Q = self.labeling
1869
+ squares = [(0, 0)]
1870
+ treat_node(0, 0, 0)
1871
+ if output == "pair":
1872
+ return squares
1873
+ return [self.word()[i:i + l] for i, l in squares]