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,4349 @@
1
+ # sage_setup: distribution = sagemath-combinat
2
+ r"""
3
+ Dyck words
4
+
5
+ A class of an object enumerated by the
6
+ :func:`Catalan numbers<sage.combinat.combinat.catalan_number>`,
7
+ see [Sta-EC2]_, [StaCat98]_ for details.
8
+
9
+ AUTHORS:
10
+
11
+ - Mike Hansen
12
+
13
+ - Dan Drake (2008-05-30): DyckWordBacktracker support
14
+
15
+ - Florent Hivert (2009-02-01): Bijections with NonDecreasingParkingFunctions
16
+
17
+ - Christian Stump (2011-12): added combinatorial maps and statistics
18
+
19
+ - Mike Zabrocki:
20
+
21
+ * (2012-10): added pretty print, characteristic function, more functions
22
+ * (2013-01): added inverse of area/dinv, bounce/area map
23
+
24
+ - Jean--Baptiste Priez, Travis Scrimshaw (2013-05-17): Added ASCII art
25
+
26
+ - Travis Scrimshaw (2013-07-09): Removed ``CombinatorialClass`` and added
27
+ global options.
28
+
29
+ REFERENCES:
30
+
31
+ .. [Sta-EC2] Richard P. Stanley.
32
+ *Enumerative Combinatorics*, Volume 2.
33
+ Cambridge University Press, 2001.
34
+
35
+ .. [StaCat98] Richard Stanley. *Exercises on Catalan and Related Numbers
36
+ excerpted from Enumerative Combinatorics, vol. 2 (CUP 1999)*,
37
+ version of 23 June 1998.
38
+ http://www-math.mit.edu/~rstan/ec/catalan.pdf
39
+
40
+ .. [Hag2008] James Haglund. *The* `q,t` -- *Catalan Numbers and the
41
+ Space of Diagonal Harmonics:
42
+ With an Appendix on the Combinatorics of Macdonald Polynomials*.
43
+ University of Pennsylvania, Philadelphia -- AMS, 2008, 167 pp.
44
+
45
+ .. [BK2001] \J. Bandlow, K. Killpatrick -- *An area-to_inv bijection
46
+ between Dyck paths and 312-avoiding permutations*, Electronic
47
+ Journal of Combinatorics, Volume 8, Issue 1 (2001).
48
+
49
+ .. [EP2004] \S. Elizalde, I. Pak. *Bijections for refined restricted
50
+ permutations**. JCTA 105(2) 2004.
51
+
52
+ .. [CK2008] \A. Claesson, S. Kitaev. *Classification of bijections
53
+ between `321`- and `132`- avoiding permutations*. Séminaire
54
+ Lotharingien de Combinatoire **60** 2008. :arxiv:`0805.1325`.
55
+
56
+ .. [Knu1973] \D. Knuth. *The Art of Computer Programming, Vol. III*.
57
+ Addison-Wesley. Reading, MA. 1973.
58
+
59
+ .. [Kra2001] \C. Krattenthaler -- *Permutations with restricted
60
+ patterns and Dyck paths*, Adv. Appl. Math. 27 (2001), 510--530.
61
+
62
+ .. [DS1992] \A. Denise, R. Simion, *Two combinatorial statistics on
63
+ Dyck paths*, Discrete Math 137 (1992), 155--176.
64
+ """
65
+
66
+ # ****************************************************************************
67
+ # Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>,
68
+ #
69
+ # Distributed under the terms of the GNU General Public License (GPL)
70
+ #
71
+ # This code is distributed in the hope that it will be useful,
72
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
73
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
74
+ # General Public License for more details.
75
+ #
76
+ # The full text of the GPL is available at:
77
+ #
78
+ # https://www.gnu.org/licenses/
79
+ # ****************************************************************************
80
+ from __future__ import annotations
81
+ from collections.abc import Iterator
82
+
83
+ from .combinat import CombinatorialElement, catalan_number
84
+ from sage.combinat.combinatorial_map import combinatorial_map
85
+ from .backtrack import GenericBacktracker
86
+
87
+ from sage.structure.global_options import GlobalOptions
88
+ from sage.structure.parent import Parent
89
+ from sage.structure.unique_representation import UniqueRepresentation
90
+ from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
91
+ from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets
92
+ from sage.categories.posets import Posets
93
+
94
+ from sage.rings.integer import Integer
95
+ from sage.rings.rational_field import QQ
96
+ from sage.combinat.permutation import Permutation, Permutations
97
+ from sage.combinat.words.word import Word
98
+ from sage.combinat.set_partition import SetPartitions
99
+ from sage.misc.latex import latex
100
+ from sage.misc.lazy_import import lazy_import
101
+
102
+ lazy_import('sage.combinat.alternating_sign_matrix', 'AlternatingSignMatrices')
103
+
104
+
105
+ open_symbol = 1
106
+ close_symbol = 0
107
+
108
+
109
+ def replace_parens(x):
110
+ r"""
111
+ A map sending ``'('`` to ``open_symbol`` and ``')'`` to
112
+ ``close_symbol``, and raising an error on any input other than
113
+ ``'('`` and ``')'``. The values of the constants ``open_symbol``
114
+ and ``close_symbol`` are subject to change.
115
+
116
+ This is the inverse map of :func:`replace_symbols`.
117
+
118
+ INPUT:
119
+
120
+ - ``x`` -- either an opening or closing parenthesis
121
+
122
+ OUTPUT:
123
+
124
+ - If ``x`` is an opening parenthesis, replace ``x`` with the
125
+ constant ``open_symbol``.
126
+
127
+ - If ``x`` is a closing parenthesis, replace ``x`` with the
128
+ constant ``close_symbol``.
129
+
130
+ - Raise a :exc:`ValueError` if ``x`` is neither an opening nor a
131
+ closing parenthesis.
132
+
133
+ .. SEEALSO:: :func:`replace_symbols`
134
+
135
+ EXAMPLES::
136
+
137
+ sage: from sage.combinat.dyck_word import replace_parens
138
+ sage: replace_parens('(')
139
+ 1
140
+ sage: replace_parens(')')
141
+ 0
142
+ sage: replace_parens(1)
143
+ Traceback (most recent call last):
144
+ ...
145
+ ValueError
146
+ """
147
+ if x == '(':
148
+ return open_symbol
149
+ if x == ')':
150
+ return close_symbol
151
+ raise ValueError
152
+
153
+
154
+ def replace_symbols(x):
155
+ r"""
156
+ A map sending ``open_symbol`` to ``'('`` and ``close_symbol`` to ``')'``,
157
+ and raising an error on any input other than ``open_symbol`` and
158
+ ``close_symbol``. The values of the constants ``open_symbol``
159
+ and ``close_symbol`` are subject to change.
160
+
161
+ This is the inverse map of :func:`replace_parens`.
162
+
163
+ INPUT:
164
+
165
+ - ``x`` -- either ``open_symbol`` or ``close_symbol``
166
+
167
+ OUTPUT:
168
+
169
+ - If ``x`` is ``open_symbol``, replace ``x`` with ``'('``.
170
+
171
+ - If ``x`` is ``close_symbol``, replace ``x`` with ``')'``.
172
+
173
+ - If ``x`` is neither ``open_symbol`` nor ``close_symbol``, a
174
+ :exc:`ValueError` is raised.
175
+
176
+ .. SEEALSO:: :func:`replace_parens`
177
+
178
+ EXAMPLES::
179
+
180
+ sage: from sage.combinat.dyck_word import replace_symbols
181
+ sage: replace_symbols(1)
182
+ '('
183
+ sage: replace_symbols(0)
184
+ ')'
185
+ sage: replace_symbols(3)
186
+ Traceback (most recent call last):
187
+ ...
188
+ ValueError
189
+ """
190
+ if x == open_symbol:
191
+ return '('
192
+ if x == close_symbol:
193
+ return ')'
194
+ raise ValueError
195
+
196
+
197
+ class DyckWord(CombinatorialElement):
198
+ r"""
199
+ A Dyck word.
200
+
201
+ A Dyck word is a sequence of open and close symbols such that every close
202
+ symbol has a corresponding open symbol preceding it. That is to say, a
203
+ Dyck word of length `n` is a list with `k` entries 1 and `n - k`
204
+ entries 0 such that the first `i` entries always have at least as many 1s
205
+ among them as 0s. (Here, the 1 serves as the open symbol and the 0 as the
206
+ close symbol.) Alternatively, the alphabet 1 and 0 can be replaced by
207
+ other characters such as '(' and ')'.
208
+
209
+ A Dyck word is *complete* if every open symbol moreover has a corresponding
210
+ close symbol.
211
+
212
+ A Dyck word may also be specified by either a noncrossing partition or
213
+ by an area sequence or the sequence of heights.
214
+
215
+ A Dyck word may also be thought of as a lattice path in the `\ZZ^2`
216
+ grid, starting at the origin `(0,0)`, and with steps in the North
217
+ `N = (0,1)` and east `E = (1,0)` directions such that it does not pass
218
+ below the `x = y` diagonal. The diagonal is referred to as the "main
219
+ diagonal" in the documentation. A North step is represented by a 1 in
220
+ the list and an East step is represented by a 0.
221
+
222
+ Equivalently, the path may be represented with steps in
223
+ the `NE = (1,1)` and the `SE = (1,-1)` direction such that it does not
224
+ pass below the horizontal axis.
225
+
226
+ .. PLOT::
227
+ :width: 400 px
228
+
229
+ d = DyckWord([1,0,1,1,1,1,0,1,0,0,1,0,1,1,0,1,0,1,1,0,0,0,0,0])
230
+ sphinx_plot(d.plot(aspect_ratio=1))
231
+
232
+ A path representing a Dyck word (either using `N` and `E` steps, or
233
+ using `NE` and `SE` steps) is called a Dyck path.
234
+
235
+ EXAMPLES::
236
+
237
+ sage: dw = DyckWord([1, 0, 1, 0]); dw
238
+ [1, 0, 1, 0]
239
+ sage: print(dw)
240
+ ()()
241
+ sage: dw.height()
242
+ 1
243
+ sage: dw.to_noncrossing_partition()
244
+ {{1}, {2}}
245
+
246
+ ::
247
+
248
+ sage: DyckWord('()()')
249
+ [1, 0, 1, 0]
250
+ sage: DyckWord('(())')
251
+ [1, 1, 0, 0]
252
+ sage: DyckWord('((')
253
+ [1, 1]
254
+ sage: DyckWord('')
255
+ []
256
+
257
+ ::
258
+
259
+ sage: DyckWord(noncrossing_partition=[[1],[2]])
260
+ [1, 0, 1, 0]
261
+ sage: DyckWord(noncrossing_partition=[[1,2]])
262
+ [1, 1, 0, 0]
263
+ sage: DyckWord(noncrossing_partition=[])
264
+ []
265
+
266
+ ::
267
+
268
+ sage: DyckWord(area_sequence=[0,0])
269
+ [1, 0, 1, 0]
270
+ sage: DyckWord(area_sequence=[0,1])
271
+ [1, 1, 0, 0]
272
+ sage: DyckWord(area_sequence=[0,1,2,2,0,1,1,2])
273
+ [1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0]
274
+ sage: DyckWord(area_sequence=[])
275
+ []
276
+
277
+ ::
278
+
279
+ sage: DyckWord(heights_sequence=(0,1,0,1,0))
280
+ [1, 0, 1, 0]
281
+ sage: DyckWord(heights_sequence=(0,1,2,1,0))
282
+ [1, 1, 0, 0]
283
+ sage: DyckWord(heights_sequence=(0,))
284
+ []
285
+
286
+ ::
287
+
288
+ sage: print(DyckWord([1,0,1,1,0,0]).to_path_string())
289
+ /\
290
+ /\/ \
291
+ sage: DyckWord([1,0,1,1,0,0]).pretty_print()
292
+ ___
293
+ | x
294
+ _| .
295
+ | . .
296
+ """
297
+ @staticmethod
298
+ def __classcall_private__(cls, dw=None, noncrossing_partition=None,
299
+ area_sequence=None, heights_sequence=None,
300
+ catalan_code=None):
301
+ """
302
+ Return an element with the appropriate parent.
303
+
304
+ EXAMPLES::
305
+
306
+ sage: DyckWord([1,0,1,1,0,0])
307
+ [1, 0, 1, 1, 0, 0]
308
+ sage: DyckWord(heights_sequence=(0,1,2,1,0))
309
+ [1, 1, 0, 0]
310
+ sage: DyckWord(noncrossing_partition=[[1],[2]])
311
+ [1, 0, 1, 0]
312
+ """
313
+ if dw is None:
314
+ if catalan_code is not None:
315
+ return CompleteDyckWords_all().from_Catalan_code(catalan_code)
316
+ if area_sequence is not None:
317
+ return CompleteDyckWords_all().from_area_sequence(area_sequence)
318
+ if noncrossing_partition is not None:
319
+ return CompleteDyckWords_all().from_noncrossing_partition(noncrossing_partition)
320
+ if heights_sequence is not None:
321
+ if heights_sequence[-1] == 0:
322
+ P = CompleteDyckWords_all()
323
+ else:
324
+ P = DyckWords_all()
325
+ return P.from_heights(heights_sequence)
326
+
327
+ raise ValueError("you have not specified a Dyck word")
328
+
329
+ if isinstance(dw, str):
330
+ l = [replace_parens(x) for x in dw]
331
+ else:
332
+ l = dw
333
+
334
+ if isinstance(l, DyckWord):
335
+ return l
336
+
337
+ if l in CompleteDyckWords_all():
338
+ return CompleteDyckWords_all()(l)
339
+
340
+ if is_a(l):
341
+ return DyckWords_all()(l)
342
+
343
+ raise ValueError("invalid Dyck word")
344
+
345
+ def __init__(self, parent, l, latex_options={}):
346
+ r"""
347
+ TESTS::
348
+
349
+ sage: DW = DyckWords(complete=False).from_heights((0,))
350
+ sage: TestSuite(DW).run()
351
+ sage: DW = DyckWords(complete=False).min_from_heights((0,))
352
+ sage: TestSuite(DW).run()
353
+ sage: DW = DyckWords().from_Catalan_code([])
354
+ sage: TestSuite(DW).run()
355
+ sage: DW = DyckWords().from_area_sequence([])
356
+ sage: TestSuite(DW).run()
357
+ """
358
+ CombinatorialElement.__init__(self, parent, l)
359
+ self._latex_options = dict(latex_options)
360
+
361
+ def set_latex_options(self, D):
362
+ r"""
363
+ Set the latex options for use in the ``_latex_`` function.
364
+
365
+ The default values are set in the ``__init__`` function.
366
+
367
+ - ``tikz_scale`` -- (default: 1) scale for use with the tikz package
368
+
369
+ - ``diagonal`` -- boolean (default: ``False``); value to draw the
370
+ diagonal or not
371
+
372
+ - ``line width`` -- (default: ``2*tikz_scale``) value representing the
373
+ line width
374
+
375
+ - ``color`` -- (default: black) the line color
376
+
377
+ - ``bounce path`` -- boolean (default: ``False``); value to indicate
378
+ if the bounce path should be drawn
379
+
380
+ - ``peaks`` -- boolean (default: ``False``); value to indicate if the
381
+ peaks should be displayed
382
+
383
+ - ``valleys`` -- boolean (default: ``False``); value to indicate if the
384
+ valleys should be displayed
385
+
386
+ INPUT:
387
+
388
+ - ``D`` -- dictionary with a list of latex parameters to change
389
+
390
+ EXAMPLES::
391
+
392
+ sage: D = DyckWord([1,0,1,0,1,0])
393
+ sage: D.set_latex_options({"tikz_scale":2})
394
+ sage: D.set_latex_options({"valleys":True, "color":"blue"})
395
+
396
+ .. TODO::
397
+
398
+ This should probably be merged into DyckWord.options.
399
+ """
400
+ for opt in D:
401
+ self._latex_options[opt] = D[opt]
402
+
403
+ def latex_options(self):
404
+ r"""
405
+ Return the latex options for use in the ``_latex_`` function as a
406
+ dictionary.
407
+
408
+ The default values are set using the options.
409
+
410
+ - ``tikz_scale`` -- (default: 1) scale for use with the tikz package
411
+
412
+ - ``diagonal`` -- boolean (default: ``False``); value to draw the
413
+ diagonal or not
414
+
415
+ - ``line width`` -- (default: ``2*tikz_scale``) value representing the
416
+ line width
417
+
418
+ - ``color`` -- (default: black) the line color
419
+
420
+ - ``bounce path`` -- boolean (default: ``False``); value to indicate
421
+ if the bounce path should be drawn
422
+
423
+ - ``peaks`` -- boolean (default: ``False``); value to indicate if the
424
+ peaks should be displayed
425
+
426
+ - ``valleys`` -- boolean (default: ``False``); value to indicate if the
427
+ valleys should be displayed
428
+
429
+ EXAMPLES::
430
+
431
+ sage: D = DyckWord([1,0,1,0,1,0])
432
+ sage: D.latex_options()
433
+ {'bounce path': False,
434
+ 'color': black,
435
+ 'diagonal': False,
436
+ 'line width': 2,
437
+ 'peaks': False,
438
+ 'tikz_scale': 1,
439
+ 'valleys': False}
440
+
441
+ .. TODO::
442
+
443
+ This should probably be merged into DyckWord.options.
444
+ """
445
+ d = self._latex_options.copy()
446
+ if "tikz_scale" not in d:
447
+ d["tikz_scale"] = self.parent().options.latex_tikz_scale
448
+ if "diagonal" not in d:
449
+ d["diagonal"] = self.parent().options.latex_diagonal
450
+ if "line width" not in d:
451
+ d["line width"] = self.parent().options.latex_line_width_scalar * d["tikz_scale"]
452
+ if "color" not in d:
453
+ d["color"] = self.parent().options.latex_color
454
+ if "bounce path" not in d:
455
+ d["bounce path"] = self.parent().options.latex_bounce_path
456
+ if "peaks" not in d:
457
+ d["peaks"] = self.parent().options.latex_peaks
458
+ if "valleys" not in d:
459
+ d["valleys"] = self.parent().options.latex_valleys
460
+ return d
461
+
462
+ def _repr_(self) -> str:
463
+ r"""
464
+ Return a string representation of ``self`` depending on
465
+ :meth:`DyckWords.options`.
466
+
467
+ TESTS::
468
+
469
+ sage: DyckWord([1, 0, 1, 0])
470
+ [1, 0, 1, 0]
471
+ sage: DyckWord([1, 1, 0, 0])
472
+ [1, 1, 0, 0]
473
+ sage: DyckWords.options.display="lattice"
474
+ sage: DyckWords.options.diagram_style="line"
475
+ sage: DyckWord([1, 0, 1, 0])
476
+ /\/\
477
+ sage: DyckWord([1, 1, 0, 0])
478
+ /\
479
+ / \
480
+ sage: DyckWords.options._reset()
481
+ """
482
+ return self.parent().options._dispatch(self, '_repr_', 'display')
483
+
484
+ def _repr_list(self) -> str:
485
+ r"""
486
+ Return a string representation of ``self`` as a list.
487
+
488
+ TESTS::
489
+
490
+ sage: DyckWord([])
491
+ []
492
+ sage: DyckWord([1, 0])
493
+ [1, 0]
494
+ sage: DyckWord('(())')
495
+ [1, 1, 0, 0]
496
+ """
497
+ return super()._repr_()
498
+
499
+ def _repr_lattice(self, type=None, labelling=None, underpath=True) -> str:
500
+ r"""
501
+ See :meth:`pretty_print()`.
502
+
503
+ TESTS::
504
+
505
+ sage: print(DyckWord(area_sequence=[0,1,0])._repr_lattice(type="NE-SE"))
506
+ /\
507
+ / \/\
508
+ sage: print(DyckWord(area_sequence=[0,1,0])._repr_lattice(labelling=[1,3,2],underpath=False))
509
+ _
510
+ ___| 2
511
+ | x . 3
512
+ | . . 1
513
+ """
514
+ if type is None:
515
+ type = self.parent().options.diagram_style
516
+ if type == "grid":
517
+ type = "N-E"
518
+ elif type == "line":
519
+ type = "NE-SE"
520
+
521
+ if type == "NE-SE":
522
+ if labelling is not None or underpath is not True:
523
+ raise ValueError("the labelling cannot be shown with Northeast-Southeast paths")
524
+ return self.to_path_string()
525
+ elif type == "N-E":
526
+ alst = self.to_area_sequence()
527
+ n = len(alst)
528
+ if n == 0:
529
+ return ".\n"
530
+ if labelling is None:
531
+ labels = [" "] * n
532
+ else:
533
+ if len(labelling) != n:
534
+ raise ValueError("the given labelling has the wrong length")
535
+ labels = [str(label) for label in labelling]
536
+ if not underpath:
537
+ max_length = max(len(label) for label in labels)
538
+ labels = [lbl.rjust(max_length + 1) for lbl in labels]
539
+
540
+ length_of_final_fall = list(reversed(self)).index(open_symbol)
541
+ if length_of_final_fall == 0:
542
+ final_fall = " "
543
+ else:
544
+ final_fall = " _" + "__" * (length_of_final_fall - 1)
545
+ row = " " * (n - alst[-1] - 1) + final_fall + "\n"
546
+ for i in range(n - 1):
547
+ c = 0
548
+ row = row + " " * (n-i-2-alst[-i-2])
549
+ c += n-i-2-alst[-i-2]
550
+ if alst[-i-2]+1 != alst[-i-1]:
551
+ row += " _"
552
+ c += alst[-i-2] - alst[-i-1]
553
+ if underpath:
554
+ row += "__" * (alst[-i-2]-alst[-i-1]) + "|" + labels[-1] + "x "*(n-c-2-i) + " ." * i + "\n"
555
+ else:
556
+ row += "__"*(alst[-i-2]-alst[-i-1])+"| " + "x "*(n-c-2-i) + " ."*i + labels[-1] + "\n"
557
+ labels.pop()
558
+ if underpath:
559
+ row += "|" + labels[-1] + " ." * (n - 1) + "\n"
560
+ else:
561
+ row += "| " + " ." * (n - 1) + labels[-1] + "\n"
562
+ return row
563
+ else:
564
+ raise ValueError("the given type (=%s) is not valid" % type)
565
+
566
+ def _ascii_art_(self):
567
+ r"""
568
+ Return an ASCII art representation of ``self``.
569
+
570
+ TESTS::
571
+
572
+ sage: ascii_art(list(DyckWords(3)))
573
+ [ /\ ]
574
+ [ /\ /\ /\/\ / \ ]
575
+ [ /\/\/\, /\/ \, / \/\, / \, / \ ]
576
+ """
577
+ from sage.typeset.ascii_art import AsciiArt
578
+ rep = self.parent().options.ascii_art
579
+ if rep == "path":
580
+ ret = self.to_path_string()
581
+ elif rep == "pretty_output":
582
+ ret = self._repr_lattice()
583
+ return AsciiArt(ret.splitlines(), baseline=0)
584
+
585
+ def _unicode_art_(self):
586
+ r"""
587
+ Return an unicode art representation of this Dyck word.
588
+
589
+ EXAMPLES::
590
+
591
+ sage: unicode_art(list(DyckWords(3)))
592
+ ⎡ ╱╲ ⎤
593
+ ⎢ ╱╲ ╱╲ ╱╲╱╲ ╱ ╲ ⎥
594
+ ⎣ ╱╲╱╲╱╲, ╱╲╱ ╲, ╱ ╲╱╲, ╱ ╲, ╱ ╲ ⎦
595
+ """
596
+ from sage.typeset.unicode_art import UnicodeArt
597
+ return UnicodeArt(self.to_path_string(unicode=True).splitlines())
598
+
599
+ def __str__(self) -> str:
600
+ r"""
601
+ Return a string consisting of matched parentheses corresponding to
602
+ the Dyck word.
603
+
604
+ EXAMPLES::
605
+
606
+ sage: print(DyckWord([1, 0, 1, 0]))
607
+ ()()
608
+ sage: print(DyckWord([1, 1, 0, 0]))
609
+ (())
610
+ """
611
+ return "".join(replace_symbols(x) for x in self)
612
+
613
+ def to_path_string(self, unicode=False) -> str:
614
+ r"""
615
+ Return a path representation of the Dyck word consisting of steps
616
+ ``/`` and ``\`` .
617
+
618
+ INPUT:
619
+
620
+ - ``unicode`` -- boolean (default: ``False``); whether to use unicode
621
+
622
+ EXAMPLES::
623
+
624
+ sage: print(DyckWord([1, 0, 1, 0]).to_path_string())
625
+ /\/\
626
+ sage: print(DyckWord([1, 1, 0, 0]).to_path_string())
627
+ /\
628
+ / \
629
+ sage: print(DyckWord([1,1,0,1,1,0,0,1,0,1,0,0]).to_path_string())
630
+ /\
631
+ /\/ \/\/\
632
+ / \
633
+ """
634
+ if unicode:
635
+ import unicodedata
636
+ space = ' '
637
+ up = unicodedata.lookup('BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT')
638
+ down = unicodedata.lookup('BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT')
639
+ else:
640
+ space = ' '
641
+ up = '/'
642
+ down = '\\'
643
+
644
+ res = [([space] * len(self)) for _ in range(self.height())]
645
+ h = 1
646
+ for i, p in enumerate(self):
647
+ if p == open_symbol:
648
+ res[-h][i] = up
649
+ h += 1
650
+ else:
651
+ h -= 1
652
+ res[-h][i] = down
653
+ return "\n".join("".join(l) for l in res)
654
+
655
+ def pretty_print(self, type=None, labelling=None, underpath=True):
656
+ r"""
657
+ Display a DyckWord as a lattice path in the `\ZZ^2` grid.
658
+
659
+ If the ``type`` is "N-E", then a cell below the diagonal is
660
+ indicated by a period, whereas a cell below the path but above
661
+ the diagonal is indicated by an x. If a list of labels is
662
+ included, they are displayed along the vertical edges of the
663
+ Dyck path.
664
+
665
+ If the ``type`` is "NE-SE", then the path is simply printed
666
+ as up steps and down steps.
667
+
668
+ INPUT:
669
+
670
+ - ``type`` -- (default: ``None``) can either be:
671
+
672
+ - ``None`` to use the option default
673
+ - "N-E" to show ``self`` as a path of north and east steps, or
674
+ - "NE-SE" to show ``self`` as a path of north-east and
675
+ south-east steps.
676
+
677
+ - ``labelling`` -- (if type is "N-E") a list of labels assigned to
678
+ the up steps in ``self``
679
+
680
+ - ``underpath`` -- (if type is "N-E", default: ``True``) if ``True``,
681
+ the labelling is shown under the path; otherwise, it is shown to
682
+ the right of the path
683
+
684
+ EXAMPLES::
685
+
686
+ sage: for D in DyckWords(3): D.pretty_print()
687
+ _
688
+ _|
689
+ _| .
690
+ | . .
691
+ ___
692
+ | x
693
+ _| .
694
+ | . .
695
+ _
696
+ ___|
697
+ | x .
698
+ | . .
699
+ ___
700
+ _| x
701
+ | x .
702
+ | . .
703
+ _____
704
+ | x x
705
+ | x .
706
+ | . .
707
+
708
+ ::
709
+
710
+ sage: for D in DyckWords(3): D.pretty_print(type="NE-SE")
711
+ /\/\/\
712
+ /\
713
+ /\/ \
714
+ /\
715
+ / \/\
716
+ /\/\
717
+ / \
718
+ /\
719
+ / \
720
+ / \
721
+
722
+ ::
723
+
724
+ sage: D = DyckWord([1,1,1,0,1,0,0,1,1])
725
+ sage: D.pretty_print()
726
+ | x x
727
+ ___| x .
728
+ _| x x . .
729
+ | x x . . .
730
+ | x . . . .
731
+ | . . . . .
732
+
733
+ sage: D = DyckWord([1,1,1,0,1,0,0,1,1,0])
734
+ sage: D.pretty_print()
735
+ _
736
+ | x x
737
+ ___| x .
738
+ _| x x . .
739
+ | x x . . .
740
+ | x . . . .
741
+ | . . . . .
742
+
743
+ sage: D = DyckWord([1,1,1,0,1,0,0,1,1,0,0])
744
+ sage: D.pretty_print()
745
+ ___
746
+ | x x
747
+ ___| x .
748
+ _| x x . .
749
+ | x x . . .
750
+ | x . . . .
751
+ | . . . . .
752
+
753
+ ::
754
+
755
+ sage: DyckWord(area_sequence=[0,1,0]).pretty_print(labelling=[1,3,2])
756
+ _
757
+ ___|2
758
+ |3x .
759
+ |1 . .
760
+
761
+ sage: DyckWord(area_sequence=[0,1,0]).pretty_print(labelling=[1,3,2],underpath=False)
762
+ _
763
+ ___| 2
764
+ | x . 3
765
+ | . . 1
766
+
767
+ ::
768
+
769
+ sage: DyckWord(area_sequence=[0,1,1,2,3,2,3,3,2,0,1,1,2,3,4,2,3]).pretty_print()
770
+ _______
771
+ | x x x
772
+ _____| x x .
773
+ | x x x x . .
774
+ | x x x . . .
775
+ | x x . . . .
776
+ _| x . . . . .
777
+ | x . . . . . .
778
+ _____| . . . . . . .
779
+ ___| x x . . . . . . . .
780
+ _| x x x . . . . . . . . .
781
+ | x x x . . . . . . . . . .
782
+ ___| x x . . . . . . . . . . .
783
+ | x x x . . . . . . . . . . . .
784
+ | x x . . . . . . . . . . . . .
785
+ _| x . . . . . . . . . . . . . .
786
+ | x . . . . . . . . . . . . . . .
787
+ | . . . . . . . . . . . . . . . .
788
+
789
+ sage: DyckWord(area_sequence=[0,1,1,2,3,2,3,3,2,0,1,1,2,3,4,2,3]).pretty_print(labelling=list(range(17)),underpath=False)
790
+ _______
791
+ | x x x 16
792
+ _____| x x . 15
793
+ | x x x x . . 14
794
+ | x x x . . . 13
795
+ | x x . . . . 12
796
+ _| x . . . . . 11
797
+ | x . . . . . . 10
798
+ _____| . . . . . . . 9
799
+ ___| x x . . . . . . . . 8
800
+ _| x x x . . . . . . . . . 7
801
+ | x x x . . . . . . . . . . 6
802
+ ___| x x . . . . . . . . . . . 5
803
+ | x x x . . . . . . . . . . . . 4
804
+ | x x . . . . . . . . . . . . . 3
805
+ _| x . . . . . . . . . . . . . . 2
806
+ | x . . . . . . . . . . . . . . . 1
807
+ | . . . . . . . . . . . . . . . . 0
808
+
809
+ ::
810
+
811
+ sage: DyckWord([]).pretty_print()
812
+ .
813
+ """
814
+ print(self._repr_lattice(type, labelling, underpath))
815
+
816
+ pp = pretty_print
817
+
818
+ def _latex_(self) -> str:
819
+ r"""
820
+ A latex representation of ``self`` using the tikzpicture package.
821
+
822
+ EXAMPLES::
823
+
824
+ sage: D = DyckWord([1,0,1,1,1,0,1,1,0,0,0,1,0,0])
825
+ sage: D.set_latex_options({"valleys":True, "peaks":True, "bounce path":True})
826
+ sage: latex(D)
827
+ \vcenter{\hbox{$\begin{tikzpicture}[scale=1]
828
+ \draw[line width=2,color=red,fill=red] (2, 0) circle (0.21);
829
+ \draw[line width=2,color=red,fill=red] (6, 2) circle (0.21);
830
+ \draw[line width=2,color=red,fill=red] (11, 1) circle (0.21);
831
+ \draw[line width=2,color=red,fill=red] (1, 1) circle (0.21);
832
+ \draw[line width=2,color=red,fill=red] (5, 3) circle (0.21);
833
+ \draw[line width=2,color=red,fill=red] (8, 4) circle (0.21);
834
+ \draw[line width=2,color=red,fill=red] (12, 2) circle (0.21);
835
+ \draw[rounded corners=1, color=green, line width=4] (0, 0) -- (1, 1) -- (2, 0) -- (3, 1) -- (4, 0) -- (5, 1) -- (6, 2) -- (7, 3) -- (8, 2) -- (9, 1) -- (10, 0) -- (11, 1) -- (12, 2) -- (13, 1) -- (14, 0);
836
+ \draw[dotted] (0, 0) grid (14, 4);
837
+ \draw[rounded corners=1, color=black, line width=2] (0, 0) -- (1, 1) -- (2, 0) -- (3, 1) -- (4, 2) -- (5, 3) -- (6, 2) -- (7, 3) -- (8, 4) -- (9, 3) -- (10, 2) -- (11, 1) -- (12, 2) -- (13, 1) -- (14, 0);
838
+ \end{tikzpicture}$}}
839
+ sage: DyckWord([1,0])._latex_()
840
+ '\\vcenter{\\hbox{$\\begin{tikzpicture}[scale=1]\n \\draw[dotted] (0, 0) grid (2, 1);\n \\draw[rounded corners=1, color=black, line width=2] (0, 0) -- (1, 1) -- (2, 0);\n\\end{tikzpicture}$}}'
841
+ sage: DyckWord([1,0,1,1,0,0])._latex_()
842
+ '\\vcenter{\\hbox{$\\begin{tikzpicture}[scale=1]\n \\draw[dotted] (0, 0) grid (6, 2);\n \\draw[rounded corners=1, color=black, line width=2] (0, 0) -- (1, 1) -- (2, 0) -- (3, 1) -- (4, 2) -- (5, 1) -- (6, 0);\n\\end{tikzpicture}$}}'
843
+ """
844
+ latex.add_package_to_preamble_if_available("tikz")
845
+ heights = self.heights()
846
+ latex_options = self.latex_options()
847
+ diagonal = latex_options["diagonal"]
848
+ ht = [(0, 0)]
849
+ valleys = []
850
+ peaks = []
851
+ for i in range(1, len(heights)):
852
+ a, b = ht[-1]
853
+ if heights[i] > heights[i - 1]:
854
+ if diagonal:
855
+ ht.append((a, b + 1))
856
+ else:
857
+ ht.append((a + 1, b + 1))
858
+ if i < len(heights) - 1 and heights[i + 1] < heights[i]:
859
+ peaks.append(ht[-1])
860
+ else:
861
+ if diagonal:
862
+ ht.append((a + 1, b))
863
+ else:
864
+ ht.append((a + 1, b - 1))
865
+ if i < len(heights) - 1 and heights[i + 1] > heights[i]:
866
+ valleys.append(ht[-1])
867
+ hti = iter(ht)
868
+ if diagonal:
869
+ grid = [((0, i), (i, i + 1))
870
+ for i in range(self.number_of_open_symbols())]
871
+ else:
872
+ grid = [((0, 0), (len(self), self.height()))]
873
+ res = "\\vcenter{\\hbox{$\\begin{tikzpicture}[scale=" + str(latex_options['tikz_scale']) + "]\n"
874
+ mark_points = []
875
+ if latex_options['valleys']:
876
+ mark_points.extend(valleys)
877
+ if latex_options['peaks']:
878
+ mark_points.extend(peaks)
879
+ for v in mark_points:
880
+ res += " \\draw[line width=2,color=red,fill=red] %s circle (%s);\n" % (str(v), 0.15 + .03 * latex_options['line width'])
881
+ if latex_options["bounce path"]:
882
+ D = self.bounce_path()
883
+ D.set_latex_options(latex_options)
884
+ D.set_latex_options({"color": "green",
885
+ "line width": 2 * latex_options['line width'],
886
+ "bounce path": False,
887
+ "peaks": False, "valleys": False})
888
+ res += D._latex_().split("\n")[-2] + "\n"
889
+ for v1, v2 in grid:
890
+ res += " \\draw[dotted] %s grid %s;\n" % (str(v1), str(v2))
891
+ if diagonal:
892
+ res += " \\draw (0,0) -- %s;\n" % str((self.number_of_open_symbols(), self.number_of_open_symbols()))
893
+ res += " \\draw[rounded corners=1, color=%s, line width=%s] (0, 0)" % (latex_options['color'], str(latex_options['line width']))
894
+ next(hti)
895
+ for i, j in hti:
896
+ res += " -- (%s, %s)" % (i, j)
897
+ res += ";\n"
898
+ res += "\\end{tikzpicture}$}}"
899
+ return res
900
+
901
+ def _repr_svg_(self) -> str:
902
+ """
903
+ Return the svg picture of ``self``.
904
+
905
+ This can be displayed by Jupyter.
906
+
907
+ EXAMPLES::
908
+
909
+ sage: PP = DyckWords(6).random_element()
910
+ sage: PP._repr_svg_()
911
+ '<?xml...</g></svg>'
912
+ """
913
+ N = self.length()
914
+ width = 0.1 if N < 20 else N / 200
915
+ resu = '<?xml version=\"1.0\" standalone=\"no\"?>'
916
+ resu += '<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" '
917
+ resu += '\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">'
918
+ resu += '<svg xmlns=\"http://www.w3.org/2000/svg\" '
919
+ resu += 'xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"500\" viewBox='
920
+
921
+ resu1 = '<g style=\"stroke-width:{};stroke-linejoin:bevel; '.format(width)
922
+ resu1 += 'stroke-linecap:butt; stroke:black; fill:snow\">'
923
+
924
+ resu3 = '<g style=\"stroke-width:{};stroke-linejoin:bevel;stroke-dasharray:0.25; '.format(width / 2)
925
+ resu3 += 'stroke-linecap:butt; stroke:gray; fill:none\">'
926
+
927
+ horizontal = "<line x1=\"{}\" y1=\"{}\" x2=\"{}\" y2=\"{}\"/>"
928
+ hori_lines = []
929
+ path = ['<polyline points=\"0,0']
930
+ x, y = 0, 0
931
+ max_y = 0
932
+ last_seen_level = [0]
933
+ for e in self:
934
+ x += 1
935
+ if e == open_symbol:
936
+ y += 1
937
+ last_seen_level.append(x - 1)
938
+ max_y = max(max_y, y)
939
+ else:
940
+ y -= 1
941
+ old_x = last_seen_level.pop()
942
+ hori_lines.append(horizontal.format(old_x, -y, x, -y))
943
+ path.append(f"{x},{-y}")
944
+ path.append('\"/>')
945
+ path.append('</g>')
946
+ resu1 += " ".join(path)
947
+ hori_lines.append('</g></svg>')
948
+ resu3 += "".join(hori_lines)
949
+
950
+ margin = 2 * width
951
+ resu += '\"{} {} {} {} \">'.format(-margin, -max_y - margin,
952
+ N + 2 * margin, max_y + 2 * margin)
953
+
954
+ return resu + resu1 + resu3
955
+
956
+ def plot(self, **kwds):
957
+ """
958
+ Plot a Dyck word as a continuous path.
959
+
960
+ EXAMPLES::
961
+
962
+ sage: w = DyckWords(100).random_element()
963
+ sage: w.plot() # needs sage.plot
964
+ Graphics object consisting of 1 graphics primitive
965
+ """
966
+ from sage.plot.plot import list_plot
967
+ step = [-1, 1]
968
+ sigma = 0
969
+ list_sigma = [0]
970
+ for l in self:
971
+ sigma += step[l]
972
+ list_sigma.append(sigma)
973
+ return list_plot(list_sigma, plotjoined=True, **kwds)
974
+
975
+ def length(self) -> int:
976
+ r"""
977
+ Return the length of ``self``.
978
+
979
+ EXAMPLES::
980
+
981
+ sage: DyckWord([1, 0, 1, 0]).length()
982
+ 4
983
+ sage: DyckWord([1, 0, 1, 1, 0]).length()
984
+ 5
985
+
986
+ TESTS::
987
+
988
+ sage: DyckWord([]).length()
989
+ 0
990
+ """
991
+ return len(self)
992
+
993
+ def number_of_open_symbols(self) -> int:
994
+ r"""
995
+ Return the number of open symbols in ``self``.
996
+
997
+ EXAMPLES::
998
+
999
+ sage: DyckWord([1, 0, 1, 0]).number_of_open_symbols()
1000
+ 2
1001
+ sage: DyckWord([1, 0, 1, 1, 0]).number_of_open_symbols()
1002
+ 3
1003
+
1004
+ TESTS::
1005
+
1006
+ sage: DyckWord([]).number_of_open_symbols()
1007
+ 0
1008
+ """
1009
+ return len([x for x in self if x == open_symbol])
1010
+
1011
+ def number_of_close_symbols(self) -> int:
1012
+ r"""
1013
+ Return the number of close symbols in ``self``.
1014
+
1015
+ EXAMPLES::
1016
+
1017
+ sage: DyckWord([1, 0, 1, 0]).number_of_close_symbols()
1018
+ 2
1019
+ sage: DyckWord([1, 0, 1, 1, 0]).number_of_close_symbols()
1020
+ 2
1021
+
1022
+ TESTS::
1023
+
1024
+ sage: DyckWord([]).number_of_close_symbols()
1025
+ 0
1026
+ """
1027
+ return len([x for x in self if x == close_symbol])
1028
+
1029
+ def is_complete(self) -> bool:
1030
+ r"""
1031
+ Return ``True`` if ``self`` is complete.
1032
+
1033
+ A Dyck word `d` is complete if `d` contains as many closers as openers.
1034
+
1035
+ EXAMPLES::
1036
+
1037
+ sage: DyckWord([1, 0, 1, 0]).is_complete()
1038
+ True
1039
+ sage: DyckWord([1, 0, 1, 1, 0]).is_complete()
1040
+ False
1041
+
1042
+ TESTS::
1043
+
1044
+ sage: DyckWord([]).is_complete()
1045
+ True
1046
+ """
1047
+ return self.number_of_open_symbols() == self.number_of_close_symbols()
1048
+
1049
+ def height(self) -> int:
1050
+ r"""
1051
+ Return the height of ``self``.
1052
+
1053
+ We view the Dyck word as a Dyck path from `(0, 0)` to
1054
+ `(2n, 0)` in the first quadrant by letting ``1``'s represent
1055
+ steps in the direction `(1, 1)` and ``0``'s represent steps in
1056
+ the direction `(1, -1)`.
1057
+
1058
+ The height is the maximum `y`-coordinate reached.
1059
+
1060
+ .. SEEALSO:: :meth:`heights`
1061
+
1062
+ EXAMPLES::
1063
+
1064
+ sage: DyckWord([]).height()
1065
+ 0
1066
+ sage: DyckWord([1,0]).height()
1067
+ 1
1068
+ sage: DyckWord([1, 1, 0, 0]).height()
1069
+ 2
1070
+ sage: DyckWord([1, 1, 0, 1, 0]).height()
1071
+ 2
1072
+ sage: DyckWord([1, 1, 0, 0, 1, 0]).height()
1073
+ 2
1074
+ sage: DyckWord([1, 0, 1, 0]).height()
1075
+ 1
1076
+ sage: DyckWord([1, 1, 0, 0, 1, 1, 1, 0, 0, 0]).height()
1077
+ 3
1078
+ """
1079
+ # calling max(self.heights()) has a significant overhead (20%)
1080
+ height = 0
1081
+ height_max = 0
1082
+ for letter in self:
1083
+ if letter == open_symbol:
1084
+ height += 1
1085
+ height_max = max(height, height_max)
1086
+ elif letter == close_symbol:
1087
+ height -= 1
1088
+ return height_max
1089
+
1090
+ def heights(self) -> tuple:
1091
+ r"""
1092
+ Return the heights of ``self``.
1093
+
1094
+ We view the Dyck word as a Dyck path from `(0,0)` to
1095
+ `(2n,0)` in the first quadrant by letting ``1``'s represent
1096
+ steps in the direction `(1,1)` and ``0``'s represent steps in
1097
+ the direction `(1,-1)`.
1098
+
1099
+ The heights is the sequence of the `y`-coordinates of all
1100
+ `2n+1` lattice points along the path.
1101
+
1102
+ .. SEEALSO:: :meth:`~DyckWords.from_heights`, :meth:`~DyckWords.min_from_heights`
1103
+
1104
+ EXAMPLES::
1105
+
1106
+ sage: DyckWord([]).heights()
1107
+ (0,)
1108
+ sage: DyckWord([1,0]).heights()
1109
+ (0, 1, 0)
1110
+ sage: DyckWord([1, 1, 0, 0]).heights()
1111
+ (0, 1, 2, 1, 0)
1112
+ sage: DyckWord([1, 1, 0, 1, 0]).heights()
1113
+ (0, 1, 2, 1, 2, 1)
1114
+ sage: DyckWord([1, 1, 0, 0, 1, 0]).heights()
1115
+ (0, 1, 2, 1, 0, 1, 0)
1116
+ sage: DyckWord([1, 0, 1, 0]).heights()
1117
+ (0, 1, 0, 1, 0)
1118
+ sage: DyckWord([1, 1, 0, 0, 1, 1, 1, 0, 0, 0]).heights()
1119
+ (0, 1, 2, 1, 0, 1, 2, 3, 2, 1, 0)
1120
+ """
1121
+ height = 0
1122
+ heights = [0] * (len(self) + 1)
1123
+ for i, letter in enumerate(self):
1124
+ if letter == open_symbol:
1125
+ height += 1
1126
+ elif letter == close_symbol:
1127
+ height -= 1
1128
+ heights[i + 1] = height
1129
+ return tuple(heights)
1130
+
1131
+ def associated_parenthesis(self, pos) -> int | None:
1132
+ r"""
1133
+ Report the position for the parenthesis in ``self`` that matches the
1134
+ one at position ``pos``.
1135
+
1136
+ The positions in ``self`` are counted from `0`.
1137
+
1138
+ INPUT:
1139
+
1140
+ - ``pos`` -- the index of the parenthesis in the list
1141
+
1142
+ OUTPUT:
1143
+
1144
+ - Integer representing the index of the matching parenthesis.
1145
+ If no parenthesis matches, return ``None``.
1146
+
1147
+ EXAMPLES::
1148
+
1149
+ sage: DyckWord([1, 0]).associated_parenthesis(0)
1150
+ 1
1151
+ sage: DyckWord([1, 0, 1, 0]).associated_parenthesis(0)
1152
+ 1
1153
+ sage: DyckWord([1, 0, 1, 0]).associated_parenthesis(1)
1154
+ 0
1155
+ sage: DyckWord([1, 0, 1, 0]).associated_parenthesis(2)
1156
+ 3
1157
+ sage: DyckWord([1, 0, 1, 0]).associated_parenthesis(3)
1158
+ 2
1159
+ sage: DyckWord([1, 1, 0, 0]).associated_parenthesis(0)
1160
+ 3
1161
+ sage: DyckWord([1, 1, 0, 0]).associated_parenthesis(2)
1162
+ 1
1163
+ sage: DyckWord([1, 1, 0]).associated_parenthesis(1)
1164
+ 2
1165
+ sage: DyckWord([1, 1]).associated_parenthesis(0)
1166
+ """
1167
+ d = 0
1168
+ height = 0
1169
+ if pos >= len(self):
1170
+ raise ValueError("invalid index")
1171
+
1172
+ if self[pos] == open_symbol:
1173
+ d += 1
1174
+ height += 1
1175
+ elif self[pos] == close_symbol:
1176
+ d -= 1
1177
+ height -= 1
1178
+ else:
1179
+ raise ValueError("unknown symbol %s" % self[pos])
1180
+
1181
+ while height != 0:
1182
+ pos += d
1183
+ if pos < 0 or pos >= len(self):
1184
+ return None
1185
+ if self[pos] == open_symbol:
1186
+ height += 1
1187
+ elif self[pos] == close_symbol:
1188
+ height -= 1
1189
+ return pos
1190
+
1191
+ def ascent_prime_decomposition(self) -> list[DyckWord]:
1192
+ r"""
1193
+ Decompose this Dyck word into a sequence of ascents and prime
1194
+ Dyck paths.
1195
+
1196
+ A Dyck word is *prime* if it is complete and has precisely
1197
+ one return - the final step. In particular, the empty Dyck
1198
+ path is not prime. Thus, the factorization is unique.
1199
+
1200
+ This decomposition yields a sequence of odd length: the words
1201
+ with even indices consist of up steps only, the words with
1202
+ odd indices are prime Dyck paths. The concatenation of the
1203
+ result is the original word.
1204
+
1205
+ EXAMPLES::
1206
+
1207
+ sage: D = DyckWord([1,1,1,0,1,0,1,1,1,1,0,1])
1208
+ sage: D.ascent_prime_decomposition()
1209
+ [[1, 1], [1, 0], [], [1, 0], [1, 1, 1], [1, 0], [1]]
1210
+
1211
+ sage: DyckWord([]).ascent_prime_decomposition()
1212
+ [[]]
1213
+
1214
+ sage: DyckWord([1,1]).ascent_prime_decomposition()
1215
+ [[1, 1]]
1216
+
1217
+ sage: DyckWord([1,0,1,0]).ascent_prime_decomposition()
1218
+ [[], [1, 0], [], [1, 0], []]
1219
+ """
1220
+ n = self.length()
1221
+ H = self.heights()
1222
+ result = []
1223
+ i = 0
1224
+ height = 0
1225
+ up = 0
1226
+ while i < n:
1227
+ j = i + 1
1228
+ while H[j] != height:
1229
+ if j == n:
1230
+ i += 1
1231
+ height += 1
1232
+ up += 1
1233
+ break
1234
+ j += 1
1235
+ else:
1236
+ result.extend([DyckWord([open_symbol] * up), # type:ignore
1237
+ DyckWord(self[i:j])]) # type:ignore
1238
+ i = j
1239
+ up = 0
1240
+
1241
+ result.append(DyckWord([open_symbol] * up)) # type:ignore
1242
+ return result
1243
+
1244
+ def catalan_factorization(self) -> list[DyckWord]:
1245
+ r"""
1246
+ Decompose this Dyck word into a sequence of complete Dyck
1247
+ words.
1248
+
1249
+ Each element of the list returned is a (possibly empty)
1250
+ complete Dyck word. The original word is obtained by placing
1251
+ an up step between each of these complete Dyck words. Thus,
1252
+ the number of words returned is one more than the final height.
1253
+
1254
+ See Section 1.2 of [CC1982]_ or Lemma 9.1.1 of [Lot2005]_.
1255
+
1256
+ EXAMPLES::
1257
+
1258
+ sage: D = DyckWord([1,1,1,0,1,0,1,1,1,1,0,1])
1259
+ sage: D.catalan_factorization()
1260
+ [[], [], [1, 0, 1, 0], [], [], [1, 0], []]
1261
+
1262
+ sage: DyckWord([]).catalan_factorization()
1263
+ [[]]
1264
+
1265
+ sage: DyckWord([1,1]).catalan_factorization()
1266
+ [[], [], []]
1267
+
1268
+ sage: DyckWord([1,0,1,0]).catalan_factorization()
1269
+ [[1, 0, 1, 0]]
1270
+ """
1271
+ H = self.heights()
1272
+ h = 0
1273
+ i = 0
1274
+ j = n = self.length()
1275
+ result = []
1276
+ while i <= n:
1277
+ if H[j] == h or j == i:
1278
+ result.append(DyckWord(self[i:j])) # type:ignore
1279
+ h += 1
1280
+ i = j + 1
1281
+ j = n
1282
+ else:
1283
+ j -= 1
1284
+ return result
1285
+
1286
+ def number_of_initial_rises(self) -> int:
1287
+ r"""
1288
+ Return the length of the initial run of ``self``.
1289
+
1290
+ OUTPUT: nonnegative integer indicating the length of the initial rise
1291
+
1292
+ EXAMPLES::
1293
+
1294
+ sage: DyckWord([1, 0, 1, 0]).number_of_initial_rises()
1295
+ 1
1296
+ sage: DyckWord([1, 1, 0, 0]).number_of_initial_rises()
1297
+ 2
1298
+ sage: DyckWord([1, 1, 0, 0, 1, 0]).number_of_initial_rises()
1299
+ 2
1300
+ sage: DyckWord([1, 0, 1, 1, 0, 0]).number_of_initial_rises()
1301
+ 1
1302
+
1303
+ TESTS::
1304
+
1305
+ sage: DyckWord([]).number_of_initial_rises()
1306
+ 0
1307
+ sage: DyckWord([1, 0]).number_of_initial_rises()
1308
+ 1
1309
+ """
1310
+ if not self:
1311
+ return 0
1312
+ i = 1
1313
+ while self[i] == open_symbol:
1314
+ i += 1
1315
+ return i
1316
+
1317
+ def peaks(self) -> list:
1318
+ r"""
1319
+ Return a list of the positions of the peaks of a Dyck word.
1320
+
1321
+ A peak is `1` followed by a `0`. Note that this does not agree with
1322
+ the definition given in [Hag2008]_.
1323
+
1324
+ .. SEEALSO:: :meth:`valleys`, :meth:`number_of_peaks`
1325
+
1326
+ EXAMPLES::
1327
+
1328
+ sage: DyckWord([1, 0, 1, 0]).peaks()
1329
+ [0, 2]
1330
+ sage: DyckWord([1, 1, 0, 0]).peaks()
1331
+ [1]
1332
+ sage: DyckWord([1,1,0,1,0,1,0,0]).peaks() # Haglund's def gives 2
1333
+ [1, 3, 5]
1334
+ """
1335
+ return [i for i in range(len(self) - 1)
1336
+ if self[i] == open_symbol and self[i + 1] == close_symbol]
1337
+
1338
+ def number_of_peaks(self) -> int:
1339
+ r"""
1340
+ Return the number of peaks of the Dyck path associated to ``self`` .
1341
+
1342
+ .. SEEALSO:: :meth:`peaks`, :meth:`number_of_valleys`
1343
+
1344
+ EXAMPLES::
1345
+
1346
+ sage: DyckWord([1, 0, 1, 0]).number_of_peaks()
1347
+ 2
1348
+ sage: DyckWord([1, 1, 0, 0]).number_of_peaks()
1349
+ 1
1350
+ sage: DyckWord([1,1,0,1,0,1,0,0]).number_of_peaks()
1351
+ 3
1352
+ sage: DyckWord([]).number_of_peaks()
1353
+ 0
1354
+ """
1355
+ return len(self.peaks())
1356
+
1357
+ def valleys(self) -> list:
1358
+ r"""
1359
+ Return a list of the positions of the valleys of a Dyck word.
1360
+
1361
+ A valley is `0` followed by a `1`.
1362
+
1363
+ .. SEEALSO:: :meth:`peaks`, :meth:`number_of_valleys`
1364
+
1365
+ EXAMPLES::
1366
+
1367
+ sage: DyckWord([1, 0, 1, 0]).valleys()
1368
+ [1]
1369
+ sage: DyckWord([1, 1, 0, 0]).valleys()
1370
+ []
1371
+ sage: DyckWord([1,1,0,1,0,1,0,0]).valleys()
1372
+ [2, 4]
1373
+ """
1374
+ return [i for i in range(len(self) - 1)
1375
+ if self[i] == close_symbol and self[i + 1] == open_symbol]
1376
+
1377
+ def number_of_valleys(self) -> int:
1378
+ r"""
1379
+ Return the number of valleys of ``self``.
1380
+
1381
+ .. SEEALSO:: :meth:`number_of_peaks`, :meth:`valleys`
1382
+
1383
+ EXAMPLES::
1384
+
1385
+ sage: DyckWord([1, 0, 1, 0]).number_of_valleys()
1386
+ 1
1387
+ sage: DyckWord([1, 1, 0, 0]).number_of_valleys()
1388
+ 0
1389
+ sage: DyckWord([1, 1, 0, 0, 1, 0]).number_of_valleys()
1390
+ 1
1391
+ sage: DyckWord([1, 0, 1, 1, 0, 0]).number_of_valleys()
1392
+ 1
1393
+
1394
+ TESTS::
1395
+
1396
+ sage: DyckWord([]).number_of_valleys()
1397
+ 0
1398
+ sage: DyckWord([1, 0]).number_of_valleys()
1399
+ 0
1400
+ """
1401
+ return len(self.valleys())
1402
+
1403
+ def position_of_first_return(self) -> int:
1404
+ r"""
1405
+ Return the number of vertical steps before the Dyck path returns to
1406
+ the main diagonal.
1407
+
1408
+ EXAMPLES::
1409
+
1410
+ sage: DyckWord([1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0]).position_of_first_return()
1411
+ 1
1412
+ sage: DyckWord([1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0]).position_of_first_return()
1413
+ 7
1414
+ sage: DyckWord([1, 1, 0, 0]).position_of_first_return()
1415
+ 2
1416
+ sage: DyckWord([1, 0, 1, 0]).position_of_first_return()
1417
+ 1
1418
+ sage: DyckWord([]).position_of_first_return()
1419
+ 0
1420
+ """
1421
+ touches = self.touch_points()
1422
+ if not touches:
1423
+ return 0
1424
+ else:
1425
+ return touches[0]
1426
+
1427
+ def positions_of_double_rises(self) -> list:
1428
+ r"""
1429
+ Return a list of positions in ``self`` where there are two
1430
+ consecutive `1`'s.
1431
+
1432
+ EXAMPLES::
1433
+
1434
+ sage: DyckWord([1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0]).positions_of_double_rises()
1435
+ [2, 5]
1436
+ sage: DyckWord([1, 1, 0, 0]).positions_of_double_rises()
1437
+ [0]
1438
+ sage: DyckWord([1, 0, 1, 0]).positions_of_double_rises()
1439
+ []
1440
+ """
1441
+ return [i for i in range(len(self) - 1)
1442
+ if self[i] == self[i + 1] == open_symbol]
1443
+
1444
+ def number_of_double_rises(self) -> int:
1445
+ r"""
1446
+ Return a the number of positions in ``self`` where there are two
1447
+ consecutive `1`'s.
1448
+
1449
+ EXAMPLES::
1450
+
1451
+ sage: DyckWord([1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0]).number_of_double_rises()
1452
+ 2
1453
+ sage: DyckWord([1, 1, 0, 0]).number_of_double_rises()
1454
+ 1
1455
+ sage: DyckWord([1, 0, 1, 0]).number_of_double_rises()
1456
+ 0
1457
+ """
1458
+ return len(self.positions_of_double_rises())
1459
+
1460
+ def returns_to_zero(self) -> list[int]:
1461
+ r"""
1462
+ Return a list of positions where ``self`` has height `0`,
1463
+ excluding the position `0`.
1464
+
1465
+ EXAMPLES::
1466
+
1467
+ sage: DyckWord([]).returns_to_zero()
1468
+ []
1469
+ sage: DyckWord([1, 0]).returns_to_zero()
1470
+ [2]
1471
+ sage: DyckWord([1, 0, 1, 0]).returns_to_zero()
1472
+ [2, 4]
1473
+ sage: DyckWord([1, 1, 0, 0]).returns_to_zero()
1474
+ [4]
1475
+ """
1476
+ height = 0
1477
+ points = []
1478
+ for i, letter in enumerate(self):
1479
+ if letter == open_symbol:
1480
+ height += 1
1481
+ elif letter == close_symbol:
1482
+ height -= 1
1483
+ if not height:
1484
+ points.append(i + 1)
1485
+ return points
1486
+
1487
+ def touch_points(self) -> list[int]:
1488
+ r"""
1489
+ Return the abscissae (or, equivalently, ordinates) of the
1490
+ points where the Dyck path corresponding to ``self`` (comprising
1491
+ `NE` and `SE` steps) touches the main diagonal. This includes
1492
+ the last point (if it is on the main diagonal) but excludes the
1493
+ beginning point.
1494
+
1495
+ Note that these abscissae are precisely the entries of
1496
+ :meth:`returns_to_zero` divided by `2`.
1497
+
1498
+ OUTPUT: list of integers indicating where the path touches the diagonal
1499
+
1500
+ EXAMPLES::
1501
+
1502
+ sage: DyckWord([1, 0, 1, 0]).touch_points()
1503
+ [1, 2]
1504
+ sage: DyckWord([1, 1, 0, 0]).touch_points()
1505
+ [2]
1506
+ sage: DyckWord([1, 1, 0, 0, 1, 0]).touch_points()
1507
+ [2, 3]
1508
+ sage: DyckWord([1, 0, 1, 1, 0, 0]).touch_points()
1509
+ [1, 3]
1510
+ """
1511
+ return [i // 2 for i in self.returns_to_zero()]
1512
+
1513
+ def touch_composition(self):
1514
+ r"""
1515
+ Return a composition which indicates the positions where ``self``
1516
+ returns to the diagonal.
1517
+
1518
+ This assumes ``self`` to be a complete Dyck word.
1519
+
1520
+ OUTPUT: a composition of length equal to the length of the Dyck word
1521
+
1522
+ EXAMPLES::
1523
+
1524
+ sage: DyckWord([1, 0, 1, 0]).touch_composition()
1525
+ [1, 1]
1526
+ sage: DyckWord([1, 1, 0, 0]).touch_composition()
1527
+ [2]
1528
+ sage: DyckWord([1, 1, 0, 0, 1, 0]).touch_composition()
1529
+ [2, 1]
1530
+ sage: DyckWord([1, 0, 1, 1, 0, 0]).touch_composition()
1531
+ [1, 2]
1532
+ sage: DyckWord([]).touch_composition()
1533
+ []
1534
+ """
1535
+ from sage.combinat.composition import Composition
1536
+ if not self:
1537
+ return Composition([])
1538
+ return Composition(descents=[i - 1 for i in self.touch_points()])
1539
+
1540
+ def number_of_touch_points(self) -> int:
1541
+ r"""
1542
+ Return the number of touches of ``self`` at the main diagonal.
1543
+
1544
+ OUTPUT: nonnegative integer
1545
+
1546
+ EXAMPLES::
1547
+
1548
+ sage: DyckWord([1, 0, 1, 0]).number_of_touch_points()
1549
+ 2
1550
+ sage: DyckWord([1, 1, 0, 0]).number_of_touch_points()
1551
+ 1
1552
+ sage: DyckWord([1, 1, 0, 0, 1, 0]).number_of_touch_points()
1553
+ 2
1554
+ sage: DyckWord([1, 0, 1, 1, 0, 0]).number_of_touch_points()
1555
+ 2
1556
+
1557
+ TESTS::
1558
+
1559
+ sage: DyckWord([]).number_of_touch_points()
1560
+ 0
1561
+ """
1562
+ return len(self.returns_to_zero())
1563
+
1564
+ def rise_composition(self):
1565
+ r"""
1566
+ The sequences of lengths of runs of `1`'s in ``self``. Also equal to
1567
+ the sequence of lengths of vertical segments in the Dyck path.
1568
+
1569
+ EXAMPLES::
1570
+
1571
+ sage: DyckWord([1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0]).pretty_print()
1572
+ ___
1573
+ | x
1574
+ _______| .
1575
+ | x x x . .
1576
+ | x x . . .
1577
+ _| x . . . .
1578
+ | x . . . . .
1579
+ | . . . . . .
1580
+
1581
+ sage: DyckWord([1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0]).rise_composition()
1582
+ [2, 3, 2]
1583
+ sage: DyckWord([1,1,0,0]).rise_composition()
1584
+ [2]
1585
+ sage: DyckWord([1,0,1,0]).rise_composition()
1586
+ [1, 1]
1587
+ """
1588
+ from sage.combinat.composition import Composition
1589
+ L = list(self)
1590
+ rise_comp = []
1591
+ while L:
1592
+ i = L.index(0)
1593
+ L = L[i + 1:]
1594
+ if i:
1595
+ rise_comp.append(i)
1596
+ return Composition(rise_comp)
1597
+
1598
+ @combinatorial_map(name='to two-row standard tableau')
1599
+ def to_standard_tableau(self):
1600
+ r"""
1601
+ Return a standard tableau of shape `(a,b)` where
1602
+ `a` is the number of open symbols and `b` is the number of
1603
+ close symbols in ``self``.
1604
+
1605
+ EXAMPLES::
1606
+
1607
+ sage: DyckWord([]).to_standard_tableau()
1608
+ []
1609
+ sage: DyckWord([1, 0]).to_standard_tableau()
1610
+ [[1], [2]]
1611
+ sage: DyckWord([1, 1, 0, 0]).to_standard_tableau()
1612
+ [[1, 2], [3, 4]]
1613
+ sage: DyckWord([1, 0, 1, 0]).to_standard_tableau()
1614
+ [[1, 3], [2, 4]]
1615
+ sage: DyckWord([1]).to_standard_tableau()
1616
+ [[1]]
1617
+ sage: DyckWord([1, 0, 1]).to_standard_tableau()
1618
+ [[1, 3], [2]]
1619
+ """
1620
+ open_positions = []
1621
+ close_positions = []
1622
+ for i in range(len(self)):
1623
+ if self[i] == open_symbol:
1624
+ open_positions.append(i + 1)
1625
+ else:
1626
+ close_positions.append(i + 1)
1627
+ from sage.combinat.tableau import StandardTableau
1628
+ return StandardTableau([x for x in [open_positions, close_positions] if x])
1629
+
1630
+ def to_tamari_sorting_tuple(self) -> list[int]:
1631
+ """
1632
+ Convert a Dyck word to a Tamari sorting tuple.
1633
+
1634
+ The result is a list of integers, one for every up-step from
1635
+ left to right. To each up-step is associated
1636
+ the distance to the corresponding down step in the Dyck word.
1637
+
1638
+ This is useful for a faster conversion to binary trees.
1639
+
1640
+ EXAMPLES::
1641
+
1642
+ sage: DyckWord([]).to_tamari_sorting_tuple()
1643
+ []
1644
+ sage: DyckWord([1, 0]).to_tamari_sorting_tuple()
1645
+ [0]
1646
+ sage: DyckWord([1, 1, 0, 0]).to_tamari_sorting_tuple()
1647
+ [1, 0]
1648
+ sage: DyckWord([1, 0, 1, 0]).to_tamari_sorting_tuple()
1649
+ [0, 0]
1650
+ sage: DyckWord([1, 1, 0, 1, 0, 0]).to_tamari_sorting_tuple()
1651
+ [2, 0, 0]
1652
+
1653
+ .. SEEALSO:: :meth:`~DyckWord_complete.to_Catalan_code`
1654
+ """
1655
+ position = 0
1656
+ resu = [-i - 1 for i in range(len(self) // 2)]
1657
+ indices_of_active_ups = []
1658
+ for letter in self:
1659
+ if letter == open_symbol:
1660
+ indices_of_active_ups.append(position)
1661
+ position += 1
1662
+ else:
1663
+ previous = indices_of_active_ups.pop()
1664
+ resu[previous] += position
1665
+ return resu
1666
+
1667
+ @combinatorial_map(name="to binary trees: up step, left tree, down step, right tree")
1668
+ def to_binary_tree(self, usemap='1L0R'):
1669
+ r"""
1670
+ Return a binary tree recursively constructed from the Dyck path
1671
+ ``self`` by the map ``usemap``. The default ``usemap`` is ``'1L0R'``
1672
+ which means:
1673
+
1674
+ - an empty Dyck word is a leaf,
1675
+
1676
+ - a non empty Dyck word reads `1 L 0 R` where `L` and `R` correspond
1677
+ to respectively its left and right subtrees.
1678
+
1679
+ INPUT:
1680
+
1681
+ - ``usemap`` -- string, either ``'1L0R'``, ``'1R0L'``, ``'L1R0'``,
1682
+ ``'R1L0'``
1683
+
1684
+ Other valid ``usemap`` are ``'1R0L'``, ``'L1R0'``, and ``'R1L0'``.
1685
+ These correspond to different maps from Dyck paths to binary
1686
+ trees, whose recursive definitions are hopefully clear from the
1687
+ names.
1688
+
1689
+ EXAMPLES::
1690
+
1691
+ sage: # needs sage.graphs
1692
+ sage: dw = DyckWord([1,0])
1693
+ sage: dw.to_binary_tree()
1694
+ [., .]
1695
+ sage: dw = DyckWord([])
1696
+ sage: dw.to_binary_tree()
1697
+ .
1698
+ sage: dw = DyckWord([1,0,1,1,0,0])
1699
+ sage: dw.to_binary_tree()
1700
+ [., [[., .], .]]
1701
+ sage: dw.to_binary_tree("L1R0")
1702
+ [[., .], [., .]]
1703
+ sage: dw = DyckWord([1,0,1,1,0,0,1,1,1,0,1,0,0,0])
1704
+ sage: dw.to_binary_tree() == dw.to_binary_tree("1R0L").left_right_symmetry()
1705
+ True
1706
+ sage: dw.to_binary_tree() == dw.to_binary_tree("L1R0").left_border_symmetry()
1707
+ False
1708
+ sage: dw.to_binary_tree("1R0L") == dw.to_binary_tree("L1R0").left_border_symmetry()
1709
+ True
1710
+ sage: dw.to_binary_tree("R1L0") == dw.to_binary_tree("L1R0").left_right_symmetry()
1711
+ True
1712
+ sage: dw.to_binary_tree("R10L")
1713
+ Traceback (most recent call last):
1714
+ ...
1715
+ ValueError: R10L is not a correct map
1716
+ """
1717
+ if usemap not in ["1L0R", "1R0L", "L1R0", "R1L0"]:
1718
+ raise ValueError("%s is not a correct map" % usemap)
1719
+ from sage.combinat.binary_tree import BinaryTree
1720
+ if not self:
1721
+ return BinaryTree()
1722
+ tp = [0]
1723
+ tp.extend(self.returns_to_zero())
1724
+ l = len(self)
1725
+ if usemap[0] == '1': # we check what kind of reduction we want
1726
+ s0 = 1 # start point for first subtree
1727
+ e0 = tp[1] - 1 # end point for first subtree
1728
+ s1 = e0 + 1 # start point for second subtree
1729
+ e1 = l # end point for second subtree
1730
+ else:
1731
+ s0 = 0
1732
+ e0 = tp[len(tp) - 2]
1733
+ s1 = e0 + 1
1734
+ e1 = l - 1
1735
+ trees = [DyckWord(self[s0:e0]).to_binary_tree(usemap),
1736
+ DyckWord(self[s1:e1]).to_binary_tree(usemap)]
1737
+ if usemap[0] == "R" or usemap[1] == "R":
1738
+ trees.reverse()
1739
+ return BinaryTree(trees)
1740
+
1741
+ @combinatorial_map(name="to the Tamari corresponding Binary tree")
1742
+ def to_binary_tree_tamari(self):
1743
+ r"""
1744
+ Return the binary tree corresponding to ``self`` in a way which
1745
+ is consistent with the Tamari orders on the set of Dyck paths and
1746
+ on the set of binary trees.
1747
+
1748
+ This is the ``'L1R0'`` map documented in :meth:`to_binary_tree`.
1749
+
1750
+ EXAMPLES::
1751
+
1752
+ sage: DyckWord([1,0]).to_binary_tree_tamari() # needs sage.graphs
1753
+ [., .]
1754
+ sage: DyckWord([1,0,1,1,0,0]).to_binary_tree_tamari() # needs sage.graphs
1755
+ [[., .], [., .]]
1756
+ sage: DyckWord([1,0,1,0,1,0]).to_binary_tree_tamari() # needs sage.graphs
1757
+ [[[., .], .], .]
1758
+ """
1759
+ # return self.to_binary_tree("L1R0") # slower and recursive
1760
+ from sage.combinat.binary_tree import from_tamari_sorting_tuple
1761
+ tup = self.to_tamari_sorting_tuple()
1762
+ return from_tamari_sorting_tuple(tup)
1763
+
1764
+ def tamari_interval(self, other):
1765
+ r"""
1766
+ Return the Tamari interval between ``self`` and ``other`` as a
1767
+ :class:`~sage.combinat.interval_posets.TamariIntervalPoset`.
1768
+
1769
+ A "Tamari interval" means an interval in the Tamari order. The
1770
+ Tamari order on the set of Dyck words of size `n` is the
1771
+ partial order obtained from the Tamari order on the set of
1772
+ binary trees of size `n` (see
1773
+ :meth:`~sage.combinat.binary_tree.BinaryTree.tamari_lequal`)
1774
+ by means of the Tamari bijection between Dyck words and binary
1775
+ trees
1776
+ (:meth:`~sage.combinat.binary_tree.BinaryTree.to_dyck_word_tamari`).
1777
+
1778
+ INPUT:
1779
+
1780
+ - ``other`` -- a Dyck word greater or equal to ``self`` in the
1781
+ Tamari order
1782
+
1783
+ EXAMPLES::
1784
+
1785
+ sage: # needs sage.graphs
1786
+ sage: dw = DyckWord([1, 1, 0, 1, 0, 0, 1, 0])
1787
+ sage: ip = dw.tamari_interval(DyckWord([1, 1, 1, 0, 0, 1, 0, 0])); ip
1788
+ The Tamari interval of size 4 induced by relations [(2, 4), (3, 4), (3, 1), (2, 1)]
1789
+ sage: ip.lower_dyck_word()
1790
+ [1, 1, 0, 1, 0, 0, 1, 0]
1791
+ sage: ip.upper_dyck_word()
1792
+ [1, 1, 1, 0, 0, 1, 0, 0]
1793
+ sage: ip.interval_cardinality()
1794
+ 4
1795
+ sage: ip.number_of_tamari_inversions()
1796
+ 2
1797
+ sage: list(ip.dyck_words())
1798
+ [[1, 1, 1, 0, 0, 1, 0, 0],
1799
+ [1, 1, 1, 0, 0, 0, 1, 0],
1800
+ [1, 1, 0, 1, 0, 1, 0, 0],
1801
+ [1, 1, 0, 1, 0, 0, 1, 0]]
1802
+ sage: dw.tamari_interval(DyckWord([1,1,0,0,1,1,0,0]))
1803
+ Traceback (most recent call last):
1804
+ ...
1805
+ ValueError: the two Dyck words are not comparable on the Tamari lattice
1806
+ """
1807
+ from sage.combinat.interval_posets import TamariIntervalPosets
1808
+ return TamariIntervalPosets.from_dyck_words(self, other)
1809
+
1810
+ def _area_sequence_iter(self) -> Iterator[int]:
1811
+ """
1812
+ Return an iterator producing the area sequence.
1813
+
1814
+ .. SEEALSO:: :meth:`to_area_sequence`
1815
+
1816
+ EXAMPLES::
1817
+
1818
+ sage: d = DyckWord([1, 0, 1, 0])
1819
+ sage: [a for a in d._area_sequence_iter()]
1820
+ [0, 0]
1821
+ """
1822
+ a = 0
1823
+ for move in self:
1824
+ if move == open_symbol:
1825
+ yield a
1826
+ a += 1
1827
+ else:
1828
+ a -= 1
1829
+
1830
+ def to_area_sequence(self) -> list[int]:
1831
+ r"""
1832
+ Return the area sequence of the Dyck word ``self``.
1833
+
1834
+ The area sequence of a Dyck word `w` is defined as follows:
1835
+ Representing the Dyck word `w` as a Dyck path from `(0, 0)` to
1836
+ `(n, n)` using `N` and `E` steps (this involves padding `w` by
1837
+ `E` steps until `w` reaches the main diagonal if `w` is not
1838
+ already a complete Dyck path), the area sequence of `w` is the
1839
+ sequence `(a_1, a_2, \ldots, a_n)`, where `a_i` is the number
1840
+ of full cells in the `i`-th row of the rectangle
1841
+ `[0, n] \times [0, n]` which lie completely above the diagonal.
1842
+ (The cells are the regions into which the rectangle is
1843
+ subdivided by the lines `x = i` with `i` integer and the lines
1844
+ `y = j` with `j` integer. The `i`-th row consists of all the
1845
+ cells between the lines `y = i-1` and `y = i`.)
1846
+
1847
+ An alternative definition:
1848
+ Representing the Dyck word `w` as a Dyck path consisting of
1849
+ `NE` and `SE` steps, the area sequence is the sequence of
1850
+ ordinates of all lattice points on the path which are
1851
+ starting points of `NE` steps.
1852
+
1853
+ A list of integers `l` is the area sequence of some Dyck path
1854
+ if and only if it satisfies `l_0 = 0` and
1855
+ `0 \leq l_{i+1} \leq l_i + 1` for `i > 0`.
1856
+
1857
+ EXAMPLES::
1858
+
1859
+ sage: DyckWord([]).to_area_sequence()
1860
+ []
1861
+ sage: DyckWord([1, 0]).to_area_sequence()
1862
+ [0]
1863
+ sage: DyckWord([1, 1, 0, 0]).to_area_sequence()
1864
+ [0, 1]
1865
+ sage: DyckWord([1, 0, 1, 0]).to_area_sequence()
1866
+ [0, 0]
1867
+ sage: all(dw ==
1868
+ ....: DyckWords().from_area_sequence(dw.to_area_sequence())
1869
+ ....: for i in range(6) for dw in DyckWords(i))
1870
+ True
1871
+ sage: DyckWord([1,0,1,0,1,0,1,0,1,0]).to_area_sequence()
1872
+ [0, 0, 0, 0, 0]
1873
+ sage: DyckWord([1,1,1,1,1,0,0,0,0,0]).to_area_sequence()
1874
+ [0, 1, 2, 3, 4]
1875
+ sage: DyckWord([1,1,1,1,0,1,0,0,0,0]).to_area_sequence()
1876
+ [0, 1, 2, 3, 3]
1877
+ sage: DyckWord([1,1,0,1,0,0,1,1,0,1,0,1,0,0]).to_area_sequence()
1878
+ [0, 1, 1, 0, 1, 1, 1]
1879
+ """
1880
+ return list(self._area_sequence_iter())
1881
+
1882
+
1883
+ class DyckWord_complete(DyckWord):
1884
+ r"""
1885
+ The class of complete
1886
+ :class:`Dyck words<sage.combinat.dyck_word.DyckWord>`.
1887
+ A Dyck word is complete, if it contains as many closers as openers.
1888
+
1889
+ For further information on Dyck words, see
1890
+ :class:`DyckWords_class<sage.combinat.dyck_word.DyckWord>`.
1891
+ """
1892
+
1893
+ def semilength(self) -> int:
1894
+ r"""
1895
+ Return the semilength of ``self``.
1896
+
1897
+ The semilength of a complete Dyck word `d` is the number of openers
1898
+ and the number of closers.
1899
+
1900
+ EXAMPLES::
1901
+
1902
+ sage: DyckWord([1, 0, 1, 0]).semilength()
1903
+ 2
1904
+
1905
+ TESTS::
1906
+
1907
+ sage: DyckWord([]).semilength()
1908
+ 0
1909
+ """
1910
+ return len(self) // 2
1911
+
1912
+ @combinatorial_map(name='to partition')
1913
+ def to_partition(self):
1914
+ r"""
1915
+ Return the partition associated to ``self`` .
1916
+
1917
+ This partition is determined by thinking of ``self`` as a lattice path
1918
+ and considering the cells which are above the path but within the
1919
+ `n \times n` grid and the partition is formed by reading the sequence
1920
+ of the number of cells in this collection in each row.
1921
+
1922
+ OUTPUT:
1923
+
1924
+ - a partition representing the rows of cells in the square lattice
1925
+ and above the path
1926
+
1927
+ EXAMPLES::
1928
+
1929
+ sage: DyckWord([]).to_partition()
1930
+ []
1931
+ sage: DyckWord([1,0]).to_partition()
1932
+ []
1933
+ sage: DyckWord([1,1,0,0]).to_partition()
1934
+ []
1935
+ sage: DyckWord([1,0,1,0]).to_partition()
1936
+ [1]
1937
+ sage: DyckWord([1,0,1,0,1,0]).to_partition()
1938
+ [2, 1]
1939
+ sage: DyckWord([1,1,0,0,1,0]).to_partition()
1940
+ [2]
1941
+ sage: DyckWord([1,0,1,1,0,0]).to_partition()
1942
+ [1, 1]
1943
+ """
1944
+ from sage.combinat.partition import Partition
1945
+ n = len(self) // 2
1946
+ res = []
1947
+ for c in reversed(self):
1948
+ if c == close_symbol:
1949
+ n -= 1
1950
+ else:
1951
+ res.append(n)
1952
+ return Partition(res)
1953
+
1954
+ def number_of_parking_functions(self) -> int:
1955
+ r"""
1956
+ Return the number of parking functions with ``self`` as the supporting
1957
+ Dyck path.
1958
+
1959
+ One representation of a parking function is as a pair consisting of a
1960
+ Dyck path and a permutation `\pi` such that if
1961
+ `[a_0, a_1, \ldots, a_{n-1}]` is the area_sequence of the Dyck path
1962
+ (see :meth:`to_area_sequence<DyckWord.to_area_sequence>`) then the
1963
+ permutation `\pi` satisfies `\pi_i < \pi_{i+1}` whenever
1964
+ `a_{i} < a_{i+1}`. This function counts the number of permutations `\pi`
1965
+ which satisfy this condition.
1966
+
1967
+ EXAMPLES::
1968
+
1969
+ sage: DyckWord(area_sequence=[0,1,2]).number_of_parking_functions()
1970
+ 1
1971
+ sage: DyckWord(area_sequence=[0,1,1]).number_of_parking_functions()
1972
+ 3
1973
+ sage: DyckWord(area_sequence=[0,1,0]).number_of_parking_functions()
1974
+ 3
1975
+ sage: DyckWord(area_sequence=[0,0,0]).number_of_parking_functions()
1976
+ 6
1977
+ """
1978
+ from sage.arith.misc import multinomial
1979
+ return multinomial(self.rise_composition())
1980
+
1981
+ def list_parking_functions(self) -> list:
1982
+ r"""
1983
+ Return all parking functions whose supporting Dyck path is ``self``.
1984
+
1985
+ EXAMPLES::
1986
+
1987
+ sage: DyckWord([1,1,0,0,1,0]).list_parking_functions()
1988
+ [[1, 1, 3], [1, 3, 1], [3, 1, 1]]
1989
+ """
1990
+ return list(self.parking_functions())
1991
+
1992
+ def parking_functions(self):
1993
+ r"""
1994
+ Iterate over parking functions whose supporting Dyck path is ``self``.
1995
+
1996
+ EXAMPLES::
1997
+
1998
+ sage: list(DyckWord([1,1,0,1,0,0]).parking_functions())
1999
+ [[1, 1, 2], [1, 2, 1], [2, 1, 1]]
2000
+ """
2001
+ from sage.combinat.parking_functions import ParkingFunction
2002
+ alist = self._area_sequence_iter()
2003
+ for pi in Permutations([i - ai + 1 for i, ai in enumerate(alist)]):
2004
+ yield ParkingFunction(pi)
2005
+
2006
+ def reading_permutation(self) -> Permutation:
2007
+ r"""
2008
+ Return the reading permutation of ``self``.
2009
+
2010
+ This is the permutation formed by taking the reading word of
2011
+ the Dyck path representing ``self`` (with `N` and `E` steps)
2012
+ if the vertical edges of the Dyck path are labeled from bottom
2013
+ to top with `1` through `n` and the diagonals are read from
2014
+ top to bottom starting with the diagonal furthest from the
2015
+ main diagonal.
2016
+
2017
+ EXAMPLES::
2018
+
2019
+ sage: DyckWord([1,0,1,0]).reading_permutation()
2020
+ [2, 1]
2021
+ sage: DyckWord([1,1,0,0]).reading_permutation()
2022
+ [2, 1]
2023
+ sage: DyckWord([1,1,0,1,0,0]).reading_permutation()
2024
+ [3, 2, 1]
2025
+ sage: DyckWord([1,1,0,0,1,0]).reading_permutation()
2026
+ [2, 3, 1]
2027
+ sage: DyckWord([1,0,1,1,0,0,1,0]).reading_permutation()
2028
+ [3, 4, 2, 1]
2029
+ """
2030
+ if not self:
2031
+ return Permutation([]) # type:ignore
2032
+ alist = self.to_area_sequence()
2033
+ m = max(alist)
2034
+ p1 = Word([m - alist[-i - 1]
2035
+ for i in range(len(alist))]).standard_permutation()
2036
+ return p1.inverse().complement()
2037
+
2038
+ def characteristic_symmetric_function(self, q=None,
2039
+ R=QQ['q', 't'].fraction_field()):
2040
+ r"""
2041
+ The characteristic function of ``self`` is the sum of
2042
+ `q^{dinv(D,F)} Q_{ides(read(D,F))}` over all permutation
2043
+ fillings of the Dyck path representing ``self``, where
2044
+ `ides(read(D,F))` is the descent composition of the inverse of the
2045
+ reading word of the filling.
2046
+
2047
+ INPUT:
2048
+
2049
+ - ``q`` -- (default: ``q = R('q')``) a parameter for the generating
2050
+ function power
2051
+
2052
+ - ``R`` -- (default: ``R = QQ['q','t'].fraction_field()``) the base
2053
+ ring to do the calculations over
2054
+
2055
+ OUTPUT:
2056
+
2057
+ - an element of the symmetric functions over the ring ``R``
2058
+ (in the Schur basis).
2059
+
2060
+ EXAMPLES::
2061
+
2062
+ sage: R = QQ['q','t'].fraction_field()
2063
+ sage: (q,t) = R.gens()
2064
+ sage: f = sum(t**D.area() * D.characteristic_symmetric_function() # needs sage.modules
2065
+ ....: for D in DyckWords(3)); f
2066
+ (q^3+q^2*t+q*t^2+t^3+q*t)*s[1, 1, 1] + (q^2+q*t+t^2+q+t)*s[2, 1] + s[3]
2067
+ sage: f.nabla(power=-1) # needs sage.modules
2068
+ s[1, 1, 1]
2069
+ """
2070
+ from sage.combinat.ncsf_qsym.qsym import QuasiSymmetricFunctions
2071
+ from sage.combinat.sf.sf import SymmetricFunctions
2072
+ if q is None:
2073
+ q = R('q')
2074
+ else:
2075
+ if q not in R:
2076
+ raise ValueError("q=%s must be an element of the base ring %s" % (q, R))
2077
+ F = QuasiSymmetricFunctions(R).Fundamental()
2078
+ p = self.reading_permutation().inverse()
2079
+ perms = [Word(perm).standard_permutation()
2080
+ for perm in self.list_parking_functions()]
2081
+ QSexpr = sum(q**self.dinv(pv.inverse()) * F(Permutation([p(i) for i in pv]).descents_composition()) for pv in perms)
2082
+ s = SymmetricFunctions(R).s()
2083
+ return s(QSexpr.to_symmetric_function())
2084
+
2085
+ def to_pair_of_standard_tableaux(self) -> tuple:
2086
+ r"""
2087
+ Convert ``self`` to a pair of standard tableaux of the same shape and
2088
+ of length less than or equal to two.
2089
+
2090
+ EXAMPLES::
2091
+
2092
+ sage: DyckWord([1,0,1,0]).to_pair_of_standard_tableaux()
2093
+ ([[1], [2]], [[1], [2]])
2094
+ sage: DyckWord([1,1,0,0]).to_pair_of_standard_tableaux()
2095
+ ([[1, 2]], [[1, 2]])
2096
+ sage: DyckWord([1,1,0,1,0,0,1,1,0,1,0,1,0,0]).to_pair_of_standard_tableaux()
2097
+ ([[1, 2, 4, 7], [3, 5, 6]], [[1, 2, 4, 6], [3, 5, 7]])
2098
+ """
2099
+ from sage.combinat.tableau import Tableau
2100
+ n = self.semilength()
2101
+ if n == 0:
2102
+ return (Tableau([]), Tableau([])) # type:ignore
2103
+ elif self.height() == n:
2104
+ T = Tableau([list(range(1, n + 1))]) # type:ignore
2105
+ return (T, T)
2106
+ else:
2107
+ left: list[list[int]] = [[], []]
2108
+ right: list[list[int]] = [[], []]
2109
+ for pos in range(n):
2110
+ if self[pos] == open_symbol:
2111
+ left[0].append(pos + 1)
2112
+ else:
2113
+ left[1].append(pos + 1)
2114
+ if self[-pos - 1] == close_symbol:
2115
+ right[0].append(pos + 1)
2116
+ else:
2117
+ right[1].append(pos + 1)
2118
+ return (Tableau(left), Tableau(right)) # type:ignore
2119
+
2120
+ @combinatorial_map(name='to 312 avoiding permutation')
2121
+ def to_312_avoiding_permutation(self) -> Permutation:
2122
+ r"""
2123
+ Convert ``self`` to a `312`-avoiding permutation using the bijection
2124
+ by Bandlow and Killpatrick in [BK2001]_.
2125
+
2126
+ This sends the area to the inversion number.
2127
+
2128
+ EXAMPLES::
2129
+
2130
+ sage: DyckWord([1,1,0,0]).to_312_avoiding_permutation()
2131
+ [2, 1]
2132
+ sage: DyckWord([1,0,1,0]).to_312_avoiding_permutation()
2133
+ [1, 2]
2134
+ sage: p = DyckWord([1,1,0,1,0,0,1,1,0,1,0,1,0,0]).to_312_avoiding_permutation(); p
2135
+ [2, 3, 1, 5, 6, 7, 4]
2136
+ sage: DyckWord([1,1,0,1,0,0,1,1,0,1,0,1,0,0]).area()
2137
+ 5
2138
+ sage: p.length()
2139
+ 5
2140
+
2141
+ TESTS::
2142
+
2143
+ sage: PD = [D.to_312_avoiding_permutation() for D in DyckWords(5)]
2144
+ sage: all(pi.avoids([3,1,2]) for pi in PD)
2145
+ True
2146
+ sage: all(D.area()==D.to_312_avoiding_permutation().length() for D in DyckWords(5))
2147
+ True
2148
+ """
2149
+ n = self.semilength()
2150
+ area = self._area_sequence_iter()
2151
+ pi = Permutations(n).one()
2152
+ for j, aj in enumerate(area):
2153
+ for i in range(aj):
2154
+ pi = pi.apply_simple_reflection(j - i)
2155
+ return pi
2156
+
2157
+ @combinatorial_map(name='to non-crossing permutation')
2158
+ def to_noncrossing_permutation(self) -> Permutation:
2159
+ r"""
2160
+ Use the bijection by C. Stump in [Stu2008]_ to send ``self`` to a
2161
+ non-crossing permutation.
2162
+
2163
+ A non-crossing permutation when written in cyclic notation has cycles
2164
+ which are strictly increasing. Sends the area to the inversion number
2165
+ and ``self.major_index()`` to `n(n-1) - maj(\sigma) - maj(\sigma^{-1})`.
2166
+ Uses the function :func:`~sage.combinat.dyck_word.pealing`
2167
+
2168
+ EXAMPLES::
2169
+
2170
+ sage: DyckWord([1,1,0,0]).to_noncrossing_permutation()
2171
+ [2, 1]
2172
+ sage: DyckWord([1,0,1,0]).to_noncrossing_permutation()
2173
+ [1, 2]
2174
+ sage: p = DyckWord([1,1,0,1,0,0,1,1,0,1,0,1,0,0]).to_noncrossing_permutation(); p
2175
+ [2, 3, 1, 5, 6, 7, 4]
2176
+ sage: DyckWord([1,1,0,1,0,0,1,1,0,1,0,1,0,0]).area()
2177
+ 5
2178
+ sage: p.length()
2179
+ 5
2180
+
2181
+ TESTS::
2182
+
2183
+ sage: all(D.area()==D.to_noncrossing_permutation().length() for D in DyckWords(5))
2184
+ True
2185
+ sage: all(20-D.major_index()==D.to_noncrossing_permutation().major_index()
2186
+ ....: +D.to_noncrossing_permutation().imajor_index() for D in DyckWords(5))
2187
+ True
2188
+ """
2189
+ n = self.semilength()
2190
+ if n == 0:
2191
+ return Permutation([]) # type:ignore
2192
+ D, touch_sequence = pealing(self, return_touches=True)
2193
+ pi = list(range(1, n + 1))
2194
+ while touch_sequence:
2195
+ for touches in touch_sequence:
2196
+ a = pi[touches[0] - 1]
2197
+ for i in range(len(touches) - 1):
2198
+ pi[touches[i] - 1] = pi[touches[i + 1] - 1]
2199
+ pi[touches[-1] - 1] = a
2200
+ D, touch_sequence = pealing(D, return_touches=True)
2201
+ return Permutations()(pi, check=False)
2202
+
2203
+ @combinatorial_map(name='to 321 avoiding permutation')
2204
+ def to_321_avoiding_permutation(self) -> Permutation:
2205
+ r"""
2206
+ Use the bijection (pp. 60-61 of [Knu1973]_ or section 3.1 of [CK2008]_)
2207
+ to send ``self`` to a `321`-avoiding permutation.
2208
+
2209
+ It is shown in [EP2004]_ that it sends the number of centered tunnels
2210
+ to the number of fixed points, the number of right tunnels to the
2211
+ number of excedences, and the semilength plus the height of the middle
2212
+ point to 2 times the length of the longest increasing subsequence.
2213
+
2214
+ EXAMPLES::
2215
+
2216
+ sage: DyckWord([1,0,1,0]).to_321_avoiding_permutation()
2217
+ [2, 1]
2218
+ sage: DyckWord([1,1,0,0]).to_321_avoiding_permutation()
2219
+ [1, 2]
2220
+ sage: D = DyckWord([1,1,0,1,0,0,1,1,0,1,0,1,0,0])
2221
+ sage: p = D.to_321_avoiding_permutation()
2222
+ sage: p
2223
+ [3, 5, 1, 6, 2, 7, 4]
2224
+ sage: D.number_of_tunnels()
2225
+ 0
2226
+ sage: p.number_of_fixed_points()
2227
+ 0
2228
+ sage: D.number_of_tunnels('right')
2229
+ 4
2230
+ sage: len(p.weak_excedences())-p.number_of_fixed_points()
2231
+ 4
2232
+ sage: n = D.semilength()
2233
+ sage: D.heights()[n] + n
2234
+ 8
2235
+ sage: 2*p.longest_increasing_subsequence_length()
2236
+ 8
2237
+
2238
+ TESTS::
2239
+
2240
+ sage: PD = [D.to_321_avoiding_permutation() for D in DyckWords(5)]
2241
+ sage: all(pi.avoids([3,2,1]) for pi in PD)
2242
+ True
2243
+ sage: to_perm = lambda x: x.to_321_avoiding_permutation()
2244
+ sage: all(D.number_of_tunnels() == to_perm(D).number_of_fixed_points()
2245
+ ....: for D in DyckWords(5))
2246
+ True
2247
+ sage: all(D.number_of_tunnels('right') == len(to_perm(D).weak_excedences())
2248
+ ....: -to_perm(D).number_of_fixed_points() for D in DyckWords(5))
2249
+ True
2250
+ sage: all(D.heights()[5]+5 == 2*to_perm(D).longest_increasing_subsequence_length()
2251
+ ....: for D in DyckWords(5))
2252
+ True
2253
+ """
2254
+ from sage.combinat.rsk import RSK_inverse
2255
+ A, B = self.to_pair_of_standard_tableaux()
2256
+ return RSK_inverse(A, B, output='permutation')
2257
+
2258
+ @combinatorial_map(name='to 132 avoiding permutation')
2259
+ def to_132_avoiding_permutation(self) -> Permutation:
2260
+ r"""
2261
+ Use the bijection by C. Krattenthaler in [Kra2001]_ to send ``self``
2262
+ to a `132`-avoiding permutation.
2263
+
2264
+ EXAMPLES::
2265
+
2266
+ sage: DyckWord([1,1,0,0]).to_132_avoiding_permutation()
2267
+ [1, 2]
2268
+ sage: DyckWord([1,0,1,0]).to_132_avoiding_permutation()
2269
+ [2, 1]
2270
+ sage: DyckWord([1,1,0,1,0,0,1,1,0,1,0,1,0,0]).to_132_avoiding_permutation()
2271
+ [6, 5, 4, 7, 2, 1, 3]
2272
+
2273
+ TESTS::
2274
+
2275
+ sage: PD = [D.to_132_avoiding_permutation() for D in DyckWords(5)]
2276
+ sage: all(pi.avoids([1,3,2]) for pi in PD)
2277
+ True
2278
+ """
2279
+ n = self.semilength()
2280
+ area = self.to_area_sequence()
2281
+ area.append(0)
2282
+ pi = []
2283
+ values = list(range(1, n + 1))
2284
+ for i in range(n):
2285
+ if area[n - i - 1] + 1 > area[n - i]:
2286
+ pi.append(n - i - area[n - i - 1])
2287
+ values.remove(n - i - area[n - i - 1])
2288
+ else:
2289
+ v = min(v for v in values if v > n - i - area[n - i - 1])
2290
+ pi.append(v)
2291
+ values.remove(v)
2292
+ return Permutation(pi) # type:ignore
2293
+
2294
+ def to_permutation(self, map) -> Permutation:
2295
+ r"""
2296
+ This is simply a method collecting all implemented maps from Dyck
2297
+ words to permutations.
2298
+
2299
+ INPUT:
2300
+
2301
+ - ``map`` -- defines the map from Dyck words to permutations.
2302
+ These are currently:
2303
+
2304
+ - ``Bandlow-Killpatrick``: :func:`to_312_avoiding_permutation`
2305
+ - ``Knuth``: :func:`to_321_avoiding_permutation`
2306
+ - ``Krattenthaler``: :func:`to_132_avoiding_permutation`
2307
+ - ``Stump``: :func:`to_noncrossing_permutation`
2308
+
2309
+ EXAMPLES::
2310
+
2311
+ sage: D = DyckWord([1,1,1,0,1,0,0,0])
2312
+ sage: D.pretty_print()
2313
+ _____
2314
+ _| x x
2315
+ | x x .
2316
+ | x . .
2317
+ | . . .
2318
+
2319
+ sage: D.to_permutation(map='Bandlow-Killpatrick')
2320
+ [3, 4, 2, 1]
2321
+ sage: D.to_permutation(map='Stump')
2322
+ [4, 2, 3, 1]
2323
+ sage: D.to_permutation(map='Knuth')
2324
+ [1, 2, 4, 3]
2325
+ sage: D.to_permutation(map='Krattenthaler')
2326
+ [2, 1, 3, 4]
2327
+
2328
+ TESTS::
2329
+
2330
+ sage: D = DyckWord([1,0,1,0])
2331
+ sage: D.to_permutation('Banana')
2332
+ Traceback (most recent call last):
2333
+ ...
2334
+ ValueError: the given map is not valid
2335
+ """
2336
+ if map == "Bandlow-Killpatrick":
2337
+ return self.to_312_avoiding_permutation()
2338
+ elif map == "Knuth":
2339
+ return self.to_321_avoiding_permutation()
2340
+ elif map == "Krattenthaler":
2341
+ return self.to_132_avoiding_permutation()
2342
+ elif map == "Stump":
2343
+ return self.to_noncrossing_permutation()
2344
+ else:
2345
+ raise ValueError("the given map is not valid")
2346
+
2347
+ def to_noncrossing_partition(self, bijection=None):
2348
+ r"""
2349
+ Bijection of Biane from ``self`` to a noncrossing partition.
2350
+
2351
+ There is an optional parameter ``bijection`` that indicates if a
2352
+ different bijection from Dyck words to non-crossing partitions
2353
+ should be used (since there are potentially many).
2354
+
2355
+ If the parameter ``bijection`` is "Stump" then the bijection used is
2356
+ from [Stu2008]_, see also the method :meth:`to_noncrossing_permutation`.
2357
+
2358
+ Thanks to Mathieu Dutour for describing the bijection. See also
2359
+ :func:`~CompleteDyckWords.from_noncrossing_partition`.
2360
+
2361
+ EXAMPLES::
2362
+
2363
+ sage: DyckWord([]).to_noncrossing_partition()
2364
+ {}
2365
+ sage: DyckWord([1, 0]).to_noncrossing_partition()
2366
+ {{1}}
2367
+ sage: DyckWord([1, 1, 0, 0]).to_noncrossing_partition()
2368
+ {{1, 2}}
2369
+ sage: DyckWord([1, 1, 1, 0, 0, 0]).to_noncrossing_partition()
2370
+ {{1, 2, 3}}
2371
+ sage: DyckWord([1, 0, 1, 0, 1, 0]).to_noncrossing_partition()
2372
+ {{1}, {2}, {3}}
2373
+ sage: DyckWord([1, 1, 0, 1, 0, 0]).to_noncrossing_partition()
2374
+ {{1, 3}, {2}}
2375
+ sage: DyckWord([]).to_noncrossing_partition("Stump")
2376
+ {}
2377
+ sage: DyckWord([1, 0]).to_noncrossing_partition("Stump")
2378
+ {{1}}
2379
+ sage: DyckWord([1, 1, 0, 0]).to_noncrossing_partition("Stump")
2380
+ {{1, 2}}
2381
+ sage: DyckWord([1, 1, 1, 0, 0, 0]).to_noncrossing_partition("Stump")
2382
+ {{1, 3}, {2}}
2383
+ sage: DyckWord([1, 0, 1, 0, 1, 0]).to_noncrossing_partition("Stump")
2384
+ {{1}, {2}, {3}}
2385
+ sage: DyckWord([1, 1, 0, 1, 0, 0]).to_noncrossing_partition("Stump")
2386
+ {{1, 2, 3}}
2387
+ """
2388
+ P = SetPartitions(len(self) // 2)
2389
+ if bijection == "Stump":
2390
+ return P(self.to_noncrossing_permutation().cycle_tuples(), check=False)
2391
+ partition = []
2392
+ stack = []
2393
+ i = 0
2394
+ p = 1
2395
+
2396
+ # Invariants:
2397
+ # - self[i] = 0
2398
+ # - p is the number of opening parens at position i
2399
+
2400
+ while i < len(self):
2401
+ stack.append(p)
2402
+ j = i + 1
2403
+ while j < len(self) and self[j] == close_symbol:
2404
+ j += 1
2405
+
2406
+ # Now j points to the next 1 or past the end of self
2407
+ nz = j - (i + 1) # the number of )'s between i and j
2408
+ if nz > 0:
2409
+ # Remove the nz last elements of stack and
2410
+ # make a new part in partition
2411
+ if nz > len(stack):
2412
+ raise ValueError("incorrect Dyck word")
2413
+
2414
+ partition.append(stack[-nz:])
2415
+
2416
+ stack = stack[: -nz]
2417
+ i = j
2418
+ p += 1
2419
+
2420
+ if stack:
2421
+ raise ValueError("incorrect Dyck word")
2422
+
2423
+ return P(partition, check=False)
2424
+
2425
+ def to_Catalan_code(self) -> list:
2426
+ r"""
2427
+ Return the Catalan code associated to ``self``.
2428
+
2429
+ A Catalan code of length `n` is a sequence
2430
+ `(a_1, a_2, \ldots, a_n)` of `n` integers `a_i` such that:
2431
+
2432
+ - `0 \leq a_i \leq n-i` for every `i`;
2433
+
2434
+ - if `i < j` and `a_i > 0` and `a_j > 0` and
2435
+ `a_{i+1} = a_{i+2} = \cdots = a_{j-1} = 0`,
2436
+ then `a_i - a_j < j-i`.
2437
+
2438
+ It turns out that the Catalan codes of length `n` are in
2439
+ bijection with Dyck words.
2440
+
2441
+ The Catalan code of a Dyck word is example (x) in Richard Stanley's
2442
+ exercises on combinatorial interpretations for Catalan objects.
2443
+ The code in this example is the reverse of the description provided
2444
+ there. See [Sta-EC2]_ and [StaCat98]_.
2445
+
2446
+ EXAMPLES::
2447
+
2448
+ sage: DyckWord([]).to_Catalan_code()
2449
+ []
2450
+ sage: DyckWord([1, 0]).to_Catalan_code()
2451
+ [0]
2452
+ sage: DyckWord([1, 1, 0, 0]).to_Catalan_code()
2453
+ [0, 1]
2454
+ sage: DyckWord([1, 0, 1, 0]).to_Catalan_code()
2455
+ [0, 0]
2456
+ sage: all(dw ==
2457
+ ....: DyckWords().from_Catalan_code(dw.to_Catalan_code())
2458
+ ....: for i in range(6) for dw in DyckWords(i))
2459
+ True
2460
+
2461
+ .. SEEALSO:: :meth:`to_tamari_sorting_tuple`
2462
+ """
2463
+ if not self:
2464
+ return []
2465
+ cut = self.associated_parenthesis(0)
2466
+ if cut is None:
2467
+ raise ValueError('not valid for incomplete Dyck words')
2468
+ recdw = DyckWord(self[1:cut] + self[cut + 1:]) # type:ignore
2469
+ returns = [0] + recdw.returns_to_zero()
2470
+ res = recdw.to_Catalan_code()
2471
+ res.append(returns.index(cut - 1))
2472
+ return res
2473
+
2474
+ @combinatorial_map(name="To Ordered tree")
2475
+ def to_ordered_tree(self):
2476
+ r"""
2477
+ Return the ordered tree corresponding to ``self`` where the depth
2478
+ of the tree is the maximal height of ``self``.
2479
+
2480
+ EXAMPLES::
2481
+
2482
+ sage: # needs sage.graphs
2483
+ sage: D = DyckWord([1,1,0,0])
2484
+ sage: D.to_ordered_tree()
2485
+ [[[]]]
2486
+ sage: D = DyckWord([1,0,1,0])
2487
+ sage: D.to_ordered_tree()
2488
+ [[], []]
2489
+ sage: D = DyckWord([1, 0, 1, 1, 0, 0])
2490
+ sage: D.to_ordered_tree()
2491
+ [[], [[]]]
2492
+ sage: D = DyckWord([1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0])
2493
+ sage: D.to_ordered_tree()
2494
+ [[], [[], []], [[], [[]]]]
2495
+
2496
+ TESTS::
2497
+
2498
+ sage: D = DyckWord([1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0])
2499
+ sage: D == D.to_ordered_tree().to_dyck_word() # needs sage.graphs
2500
+ True
2501
+ """
2502
+ from sage.combinat.ordered_tree import OrderedTree
2503
+ levels = [OrderedTree().clone()]
2504
+ for u in self:
2505
+ if u == 1:
2506
+ levels.append(OrderedTree().clone())
2507
+ else:
2508
+ tree = levels.pop()
2509
+ tree.set_immutable()
2510
+ root = levels.pop()
2511
+ root.append(tree)
2512
+ levels.append(root)
2513
+ root = levels[0]
2514
+ root.set_immutable()
2515
+ return root
2516
+
2517
+ def to_triangulation(self) -> list:
2518
+ r"""
2519
+ Map ``self`` to a triangulation.
2520
+
2521
+ The map from complete Dyck words of length `2n` to
2522
+ triangulations of `n+2`-gon given by this function is a
2523
+ bijection that can be described as follows.
2524
+
2525
+ Consider the Dyck word as a path from `(0, 0)` to `(n, n)`
2526
+ staying above the diagonal, where `1` is an up step and `0` is
2527
+ a right step. Then each horizontal step has a co-height (`0`
2528
+ at the top and `n-1` at most at the bottom). One reads the
2529
+ Dyck word from left to right. At the beginning, all vertices
2530
+ from `0` to `n+1` are available. For each horizontal step,
2531
+ one creates an edge from the vertex indexed by the co-height
2532
+ to the next available vertex. This chops out a triangle from
2533
+ the polygon and one removes the middle vertex of this triangle
2534
+ from the list of available vertices.
2535
+
2536
+ This bijection has the property that the set of smallest
2537
+ vertices of the edges in a triangulation is an encoding of the
2538
+ co-heights, from which the Dyck word can be easily recovered.
2539
+
2540
+ OUTPUT:
2541
+
2542
+ a list of pairs `(i, j)` that are the edges of the
2543
+ triangulations.
2544
+
2545
+ EXAMPLES::
2546
+
2547
+ sage: DyckWord([1, 1, 0, 0]).to_triangulation()
2548
+ [(0, 2)]
2549
+
2550
+ sage: [t.to_triangulation() for t in DyckWords(3)]
2551
+ [[(2, 4), (1, 4)],
2552
+ [(2, 4), (0, 2)],
2553
+ [(1, 3), (1, 4)],
2554
+ [(1, 3), (0, 3)],
2555
+ [(0, 2), (0, 3)]]
2556
+
2557
+ REFERENCES:
2558
+
2559
+ - [Cha2005]_
2560
+ """
2561
+ n = self.number_of_open_symbols()
2562
+ l = list(range(n + 2)) # from 0 to n + 1
2563
+ edges = []
2564
+ coheight = n - 1
2565
+ for letter in self[1:-1]:
2566
+ if letter == 1:
2567
+ coheight -= 1
2568
+ else:
2569
+ edges.append((coheight, l[coheight + 2]))
2570
+ l.pop(coheight + 1)
2571
+
2572
+ return edges
2573
+
2574
+ def to_triangulation_as_graph(self):
2575
+ r"""
2576
+ Map ``self`` to a triangulation and return the result as a graph.
2577
+
2578
+ See :meth:`to_triangulation` for the bijection used to map
2579
+ complete Dyck words to triangulations.
2580
+
2581
+ OUTPUT:
2582
+
2583
+ - a graph containing both the perimeter edges and the inner
2584
+ edges of a triangulation of a regular polygon.
2585
+
2586
+ EXAMPLES::
2587
+
2588
+ sage: g = DyckWord([1, 1, 0, 0, 1, 0]).to_triangulation_as_graph(); g # needs sage.graphs
2589
+ Graph on 5 vertices
2590
+ sage: g.edges(sort=True, labels=False) # needs sage.graphs
2591
+ [(0, 1), (0, 4), (1, 2), (1, 3), (1, 4), (2, 3), (3, 4)]
2592
+ sage: g.show() # not tested # needs sage.graphs
2593
+ """
2594
+ n = self.number_of_open_symbols()
2595
+ edges = self.to_triangulation()
2596
+ from sage.graphs.graph import Graph
2597
+ peri = [(i, i + 1) for i in range(n + 1)] + [(n + 1, 0)]
2598
+ g = Graph(n + 2)
2599
+ g.add_edges(peri)
2600
+ g.add_edges(edges)
2601
+ g.set_pos(g.layout_circular())
2602
+ return g
2603
+
2604
+ def to_non_decreasing_parking_function(self):
2605
+ r"""
2606
+ Bijection to :class:`non-decreasing parking
2607
+ functions<sage.combinat.non_decreasing_parking_function.NonDecreasingParkingFunctions>`.
2608
+
2609
+ See there the method
2610
+ :meth:`~sage.combinat.non_decreasing_parking_function.NonDecreasingParkingFunction.to_dyck_word`
2611
+ for more information.
2612
+
2613
+ EXAMPLES::
2614
+
2615
+ sage: DyckWord([]).to_non_decreasing_parking_function()
2616
+ []
2617
+ sage: DyckWord([1,0]).to_non_decreasing_parking_function()
2618
+ [1]
2619
+ sage: DyckWord([1,1,0,0]).to_non_decreasing_parking_function()
2620
+ [1, 1]
2621
+ sage: DyckWord([1,0,1,0]).to_non_decreasing_parking_function()
2622
+ [1, 2]
2623
+ sage: DyckWord([1,0,1,1,0,1,0,0,1,0]).to_non_decreasing_parking_function()
2624
+ [1, 2, 2, 3, 5]
2625
+
2626
+ TESTS::
2627
+
2628
+ sage: ld = DyckWords(5)
2629
+ sage: list(ld) == [dw.to_non_decreasing_parking_function().to_dyck_word() for dw in ld]
2630
+ True
2631
+ """
2632
+ from sage.combinat.non_decreasing_parking_function import NonDecreasingParkingFunction
2633
+ return NonDecreasingParkingFunction.from_dyck_word(self)
2634
+
2635
+ def major_index(self) -> int:
2636
+ r"""
2637
+ Return the major index of ``self`` .
2638
+
2639
+ The major index of a Dyck word `D` is the sum of the positions of
2640
+ the valleys of `D` (when started counting at position ``1``).
2641
+
2642
+ EXAMPLES::
2643
+
2644
+ sage: DyckWord([1, 0, 1, 0]).major_index()
2645
+ 2
2646
+ sage: DyckWord([1, 1, 0, 0]).major_index()
2647
+ 0
2648
+ sage: DyckWord([1, 1, 0, 0, 1, 0]).major_index()
2649
+ 4
2650
+ sage: DyckWord([1, 0, 1, 1, 0, 0]).major_index()
2651
+ 2
2652
+
2653
+ TESTS::
2654
+
2655
+ sage: DyckWord([]).major_index()
2656
+ 0
2657
+ sage: DyckWord([1, 0]).major_index()
2658
+ 0
2659
+ """
2660
+ valleys = self.valleys()
2661
+ return sum(valleys) + len(valleys)
2662
+
2663
+ def pyramid_weight(self) -> int:
2664
+ r"""
2665
+ Return the pyramid weight of ``self``.
2666
+
2667
+ A pyramid of ``self`` is a subsequence of the form
2668
+ `1^h 0^h`. A pyramid is maximal if it is neither preceded by a `1`
2669
+ nor followed by a `0`.
2670
+
2671
+ The pyramid weight of a Dyck path is the sum of the lengths of the
2672
+ maximal pyramids and was defined in [DS1992]_.
2673
+
2674
+ EXAMPLES::
2675
+
2676
+ sage: DyckWord([1,1,0,1,1,1,0,0,1,0,0,0,1,1,0,0]).pyramid_weight()
2677
+ 6
2678
+ sage: DyckWord([1,1,1,0,0,0]).pyramid_weight()
2679
+ 3
2680
+ sage: DyckWord([1,0,1,0,1,0]).pyramid_weight()
2681
+ 3
2682
+ sage: DyckWord([1,1,0,1,0,0]).pyramid_weight()
2683
+ 2
2684
+ """
2685
+ aseq = self.to_area_sequence() + [0]
2686
+ bseq = self.reverse().to_area_sequence() + [0]
2687
+ apeak = []
2688
+ bpeak = []
2689
+ for i in range(len(aseq) - 1):
2690
+ if aseq[i + 1] <= aseq[i]:
2691
+ apeak.append(i)
2692
+ if bseq[i + 1] <= bseq[i]:
2693
+ bpeak.append(i)
2694
+ out = 0
2695
+ for i, apeaki in enumerate(apeak):
2696
+ out += min(aseq[apeaki] - aseq[apeaki + 1] + 1,
2697
+ bseq[bpeak[-i - 1]] - bseq[bpeak[-i - 1] + 1] + 1)
2698
+ return out
2699
+
2700
+ def tunnels(self):
2701
+ r"""
2702
+ Return an iterator of ranges of the matching parentheses in the Dyck
2703
+ word ``self``.
2704
+
2705
+ That is, if ``(a,b)`` is in ``self.tunnels()``, then the matching
2706
+ parenthesis to ``self[a]`` is ``self[b-1]``.
2707
+
2708
+ EXAMPLES::
2709
+
2710
+ sage: list(DyckWord([1, 1, 0, 1, 1, 0, 0, 1, 0, 0]).tunnels())
2711
+ [(0, 10), (1, 3), (3, 7), (4, 6), (7, 9)]
2712
+ """
2713
+ heights = self.heights()
2714
+ for i in range(len(heights) - 1):
2715
+ height = heights[i]
2716
+ if height < heights[i + 1]:
2717
+ yield (i, i + 1 + heights[i + 1:].index(height))
2718
+
2719
+ def number_of_tunnels(self, tunnel_type='centered') -> int:
2720
+ r"""
2721
+ Return the number of tunnels of ``self`` of type ``tunnel_type``.
2722
+
2723
+ A tunnel is a pair `(a,b)` where ``a`` is the position of an open
2724
+ parenthesis and ``b`` is the position of the matching close
2725
+ parenthesis. If `a + b = n` then the tunnel is called *centered* . If
2726
+ `a + b < n` then the tunnel is called *left* and if `a + b > n`, then
2727
+ the tunnel is called *right*.
2728
+
2729
+ INPUT:
2730
+
2731
+ - ``tunnel_type`` -- (default: ``'centered'``) can be one of the
2732
+ following: ``'left'``, ``'right'``, ``'centered'``, or ``'all'``
2733
+
2734
+ EXAMPLES::
2735
+
2736
+ sage: DyckWord([1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0]).number_of_tunnels()
2737
+ 0
2738
+ sage: DyckWord([1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0]).number_of_tunnels('left')
2739
+ 5
2740
+ sage: DyckWord([1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0]).number_of_tunnels('right')
2741
+ 2
2742
+ sage: DyckWord([1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0]).number_of_tunnels('all')
2743
+ 7
2744
+ sage: DyckWord([1, 1, 0, 0]).number_of_tunnels('centered')
2745
+ 2
2746
+ """
2747
+ n = len(self)
2748
+ tunnels = self.tunnels()
2749
+ if tunnel_type == 'left':
2750
+ return len([1 for (i, j) in tunnels if i + j < n])
2751
+ elif tunnel_type == 'centered':
2752
+ return len([1 for (i, j) in tunnels if i + j == n])
2753
+ elif tunnel_type == 'right':
2754
+ return len([1 for (i, j) in tunnels if i + j > n])
2755
+ elif tunnel_type == 'all':
2756
+ return len(list(tunnels))
2757
+ else:
2758
+ raise ValueError("the given tunnel_type is not valid")
2759
+
2760
+ @combinatorial_map(order=2, name="Reverse path")
2761
+ def reverse(self) -> DyckWord:
2762
+ r"""
2763
+ Return the reverse and complement of ``self``.
2764
+
2765
+ This operation corresponds to flipping the Dyck path across the
2766
+ `y=-x` line.
2767
+
2768
+ EXAMPLES::
2769
+
2770
+ sage: DyckWord([1,1,0,0,1,0]).reverse()
2771
+ [1, 0, 1, 1, 0, 0]
2772
+ sage: DyckWord([1,1,1,0,0,0]).reverse()
2773
+ [1, 1, 1, 0, 0, 0]
2774
+ sage: len([D for D in DyckWords(5) if D.reverse() == D])
2775
+ 10
2776
+
2777
+ TESTS::
2778
+
2779
+ sage: DyckWord([]).reverse()
2780
+ []
2781
+ """
2782
+ alist = []
2783
+ for i in range(len(self)):
2784
+ if self[i] == open_symbol:
2785
+ alist.append(close_symbol)
2786
+ else:
2787
+ alist.append(open_symbol)
2788
+ alist.reverse()
2789
+ return DyckWord(alist) # type:ignore
2790
+
2791
+ def first_return_decomposition(self) -> tuple:
2792
+ r"""
2793
+ Decompose a Dyck word into a pair of Dyck words (potentially empty)
2794
+ where the first word consists of the word after the first up step and
2795
+ the corresponding matching closing parenthesis.
2796
+
2797
+ EXAMPLES::
2798
+
2799
+ sage: DyckWord([1,1,0,1,0,0,1,1,0,1,0,1,0,0]).first_return_decomposition()
2800
+ ([1, 0, 1, 0], [1, 1, 0, 1, 0, 1, 0, 0])
2801
+ sage: DyckWord([1,1,0,0]).first_return_decomposition()
2802
+ ([1, 0], [])
2803
+ sage: DyckWord([1,0,1,0]).first_return_decomposition()
2804
+ ([], [1, 0])
2805
+ """
2806
+ k = self.position_of_first_return() * 2
2807
+ return DyckWord(self[1:k - 1]), DyckWord(self[k:]) # type:ignore
2808
+
2809
+ def decomposition_reverse(self) -> DyckWord:
2810
+ r"""
2811
+ Return the involution of ``self`` with a recursive definition.
2812
+
2813
+ If a Dyck word `D` decomposes as `1 D_1 0 D_2` where `D_1` and
2814
+ `D_2` are complete Dyck words then the decomposition reverse is
2815
+ `1 \phi(D_2) 0 \phi(D_1)`.
2816
+
2817
+ EXAMPLES::
2818
+
2819
+ sage: DyckWord([1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0]).decomposition_reverse()
2820
+ [1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0]
2821
+ sage: DyckWord([1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0]).decomposition_reverse()
2822
+ [1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0]
2823
+ sage: DyckWord([1,1,0,0]).decomposition_reverse()
2824
+ [1, 0, 1, 0]
2825
+ sage: DyckWord([1,0,1,0]).decomposition_reverse()
2826
+ [1, 1, 0, 0]
2827
+ """
2828
+ if not self:
2829
+ return self
2830
+ D1, D2 = self.first_return_decomposition()
2831
+ D = [1] + list(D2.decomposition_reverse())
2832
+ D += [0] + list(D1.decomposition_reverse())
2833
+ return DyckWord(D) # type:ignore
2834
+
2835
+ @combinatorial_map(name="Area-dinv to bounce-area")
2836
+ def area_dinv_to_bounce_area_map(self) -> DyckWord:
2837
+ r"""
2838
+ Return the image of ``self`` under the map which sends a
2839
+ Dyck word with ``area`` equal to `r` and ``dinv`` equal to `s` to a
2840
+ Dyck word with ``bounce`` equal to `r` and ``area`` equal to `s` .
2841
+
2842
+ The inverse of this map is :meth:`bounce_area_to_area_dinv_map`.
2843
+
2844
+ For a definition of this map, see [Hag2008]_ p. 50 where it is called
2845
+ `\zeta`. However, this map differs from Haglund's map by an application
2846
+ of :meth:`reverse` (as does the definition of the :meth:`bounce`
2847
+ statistic).
2848
+
2849
+ EXAMPLES::
2850
+
2851
+ sage: DyckWord([1,1,0,1,0,0,1,1,0,1,0,1,0,0]).area_dinv_to_bounce_area_map()
2852
+ [1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0]
2853
+ sage: DyckWord([1,1,0,1,0,0,1,1,0,1,0,1,0,0]).area()
2854
+ 5
2855
+ sage: DyckWord([1,1,0,1,0,0,1,1,0,1,0,1,0,0]).dinv()
2856
+ 13
2857
+ sage: DyckWord([1,1,1,1,1,0,0,0,1,0,0,1,0,0]).area()
2858
+ 13
2859
+ sage: DyckWord([1,1,1,1,1,0,0,0,1,0,0,1,0,0]).bounce()
2860
+ 5
2861
+ sage: DyckWord([1,1,1,1,1,0,0,0,1,0,0,1,0,0]).area_dinv_to_bounce_area_map()
2862
+ [1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0]
2863
+ sage: DyckWord([1,1,0,0]).area_dinv_to_bounce_area_map()
2864
+ [1, 0, 1, 0]
2865
+ sage: DyckWord([1,0,1,0]).area_dinv_to_bounce_area_map()
2866
+ [1, 1, 0, 0]
2867
+ """
2868
+ if not self:
2869
+ return self
2870
+ a = self.to_area_sequence()
2871
+ a.reverse()
2872
+ image = []
2873
+ for i in range(max(a), -2, -1):
2874
+ for j in a:
2875
+ if j == i:
2876
+ image.append(1)
2877
+ elif j == i + 1:
2878
+ image.append(0)
2879
+ return DyckWord(image) # type:ignore
2880
+
2881
+ @combinatorial_map(name="Bounce-area to area-dinv")
2882
+ def bounce_area_to_area_dinv_map(self) -> DyckWord:
2883
+ r"""
2884
+ Return the image of the Dyck word under the map which sends a
2885
+ Dyck word with ``bounce`` equal to `r` and ``area`` equal to `s` to a
2886
+ Dyck word with ``area`` equal to `r` and ``dinv`` equal to `s` .
2887
+
2888
+ This implementation uses a recursive method by saying that the
2889
+ last entry in the area sequence of the Dyck word ``self`` is
2890
+ equal to the number of touch points of the Dyck path minus 1
2891
+ of the image of this map.
2892
+
2893
+ The inverse of this map is :meth:`area_dinv_to_bounce_area_map`.
2894
+
2895
+ For a definition of this map, see [Hag2008]_ p. 50 where it is called
2896
+ `\zeta^{-1}`. However, this map differs from Haglund's map by an
2897
+ application of :meth:`reverse` (as does the definition of the
2898
+ :meth:`bounce` statistic).
2899
+
2900
+ EXAMPLES::
2901
+
2902
+ sage: DyckWord([1,1,0,1,0,0,1,1,0,1,0,1,0,0]).bounce_area_to_area_dinv_map()
2903
+ [1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0]
2904
+ sage: DyckWord([1,1,0,1,0,0,1,1,0,1,0,1,0,0]).area()
2905
+ 5
2906
+ sage: DyckWord([1,1,0,1,0,0,1,1,0,1,0,1,0,0]).bounce()
2907
+ 9
2908
+ sage: DyckWord([1,1,0,0,1,1,1,1,0,0,1,0,0,0]).area()
2909
+ 9
2910
+ sage: DyckWord([1,1,0,0,1,1,1,1,0,0,1,0,0,0]).dinv()
2911
+ 5
2912
+ sage: all(D==D.bounce_area_to_area_dinv_map().area_dinv_to_bounce_area_map() for D in DyckWords(6))
2913
+ True
2914
+ sage: DyckWord([1,1,0,0]).bounce_area_to_area_dinv_map()
2915
+ [1, 0, 1, 0]
2916
+ sage: DyckWord([1,0,1,0]).bounce_area_to_area_dinv_map()
2917
+ [1, 1, 0, 0]
2918
+ """
2919
+ aseq = self._area_sequence_iter()
2920
+ out: list[int] = []
2921
+ zeros: list[int] = []
2922
+ for ai in aseq:
2923
+ p = (zeros + [len(out)])[ai]
2924
+ out = [1] + out[p:] + [0] + out[:p]
2925
+ zeros = [0] + [j + len(out) - p for j in zeros[:ai]]
2926
+ return DyckWord(out) # type:ignore
2927
+
2928
+ def area(self) -> int:
2929
+ r"""
2930
+ Return the area for ``self`` corresponding to the area
2931
+ of the Dyck path.
2932
+
2933
+ One can view a balanced Dyck word as a lattice path from
2934
+ `(0,0)` to `(n,n)` in the first quadrant by letting
2935
+ '1's represent steps in the direction `(1,0)` and '0's
2936
+ represent steps in the direction `(0,1)`. The resulting
2937
+ path will remain weakly above the diagonal `y = x`.
2938
+
2939
+ The area statistic is the number of complete
2940
+ squares in the integer lattice which are below the path and above
2941
+ the line `y = x`. The 'half-squares' directly above the
2942
+ line `y = x` do not contribute to this statistic.
2943
+
2944
+ EXAMPLES::
2945
+
2946
+ sage: dw = DyckWord([1,0,1,0])
2947
+ sage: dw.area() # 2 half-squares, 0 complete squares
2948
+ 0
2949
+
2950
+ ::
2951
+
2952
+ sage: dw = DyckWord([1,1,1,0,1,1,1,0,0,0,1,1,0,0,1,0,0,0])
2953
+ sage: dw.area()
2954
+ 19
2955
+
2956
+ ::
2957
+
2958
+ sage: DyckWord([1,1,1,1,0,0,0,0]).area()
2959
+ 6
2960
+ sage: DyckWord([1,1,1,0,1,0,0,0]).area()
2961
+ 5
2962
+ sage: DyckWord([1,1,1,0,0,1,0,0]).area()
2963
+ 4
2964
+ sage: DyckWord([1,1,1,0,0,0,1,0]).area()
2965
+ 3
2966
+ sage: DyckWord([1,0,1,1,0,1,0,0]).area()
2967
+ 2
2968
+ sage: DyckWord([1,1,0,1,1,0,0,0]).area()
2969
+ 4
2970
+ sage: DyckWord([1,1,0,0,1,1,0,0]).area()
2971
+ 2
2972
+ sage: DyckWord([1,0,1,1,1,0,0,0]).area()
2973
+ 3
2974
+ sage: DyckWord([1,0,1,1,0,0,1,0]).area()
2975
+ 1
2976
+ sage: DyckWord([1,0,1,0,1,1,0,0]).area()
2977
+ 1
2978
+ sage: DyckWord([1,1,0,0,1,0,1,0]).area()
2979
+ 1
2980
+ sage: DyckWord([1,1,0,1,0,0,1,0]).area()
2981
+ 2
2982
+ sage: DyckWord([1,1,0,1,0,1,0,0]).area()
2983
+ 3
2984
+ sage: DyckWord([1,0,1,0,1,0,1,0]).area()
2985
+ 0
2986
+ """
2987
+ return sum(self._area_sequence_iter())
2988
+
2989
+ def bounce_path(self) -> DyckWord:
2990
+ r"""
2991
+ Return the bounce path of ``self`` formed by starting at `(n,n)` and
2992
+ traveling West until encountering the first vertical step of ``self``,
2993
+ then South until encountering the diagonal, then West again to hit
2994
+ the path, etc. until the `(0,0)` point is reached. The path followed
2995
+ by this walk is the bounce path.
2996
+
2997
+ .. SEEALSO:: :meth:`bounce`
2998
+
2999
+ EXAMPLES::
3000
+
3001
+ sage: DyckWord([1,1,0,1,0,0]).bounce_path()
3002
+ [1, 0, 1, 1, 0, 0]
3003
+ sage: DyckWord([1,1,1,0,0,0]).bounce_path()
3004
+ [1, 1, 1, 0, 0, 0]
3005
+ sage: DyckWord([1,0,1,0,1,0]).bounce_path()
3006
+ [1, 0, 1, 0, 1, 0]
3007
+ sage: DyckWord([1,1,1,1,0,0,1,0,0,0]).bounce_path()
3008
+ [1, 1, 0, 0, 1, 1, 1, 0, 0, 0]
3009
+
3010
+ TESTS::
3011
+
3012
+ sage: DyckWord([]).bounce_path()
3013
+ []
3014
+ sage: DyckWord([1,0]).bounce_path()
3015
+ [1, 0]
3016
+ """
3017
+ area_seq = self.to_area_sequence()
3018
+ i = len(area_seq) - 1
3019
+ n = 5
3020
+ while i > 0:
3021
+ n -= 1
3022
+ a = area_seq[i]
3023
+ i_new = i - a
3024
+ while i > i_new:
3025
+ i -= 1
3026
+ area_seq[i] = area_seq[i + 1] - 1
3027
+ i -= 1
3028
+ return DyckWord(area_sequence=area_seq) # type:ignore
3029
+
3030
+ def bounce(self) -> int:
3031
+ r"""
3032
+ Return the bounce statistic of ``self`` due to J. Haglund,
3033
+ see [Hag2008]_.
3034
+
3035
+ One can view a balanced Dyck word as a lattice path from `(0,0)` to
3036
+ `(n,n)` in the first quadrant by letting '1's represent steps in
3037
+ the direction `(0,1)` and '0's represent steps in the direction
3038
+ `(1,0)`. The resulting path will remain weakly above the diagonal
3039
+ `y = x`.
3040
+
3041
+ We describe the bounce statistic of such a path in terms of what is
3042
+ known as the "bounce path".
3043
+
3044
+ We can think of our bounce path as describing the trail of a billiard
3045
+ ball shot West from `(n, n)`, which "bounces" down whenever it
3046
+ encounters a vertical step and "bounces" left when it encounters the
3047
+ line `y = x`.
3048
+
3049
+ The bouncing ball will strike the diagonal at the places
3050
+
3051
+ .. MATH::
3052
+
3053
+ (0, 0), (j_1, j_1), (j_2, j_2), \ldots, (j_r-1, j_r-1), (j_r, j_r)
3054
+ = (n, n).
3055
+
3056
+ We define the bounce to be the sum `\sum_{i=1}^{r-1} j_i`.
3057
+
3058
+ EXAMPLES::
3059
+
3060
+ sage: DyckWord([1,1,1,0,1,1,1,0,0,0,1,1,0,0,1,0,0,0]).bounce()
3061
+ 7
3062
+ sage: DyckWord([1,1,1,1,0,0,0,0]).bounce()
3063
+ 0
3064
+ sage: DyckWord([1,1,1,0,1,0,0,0]).bounce()
3065
+ 1
3066
+ sage: DyckWord([1,1,1,0,0,1,0,0]).bounce()
3067
+ 2
3068
+ sage: DyckWord([1,1,1,0,0,0,1,0]).bounce()
3069
+ 3
3070
+ sage: DyckWord([1,0,1,1,0,1,0,0]).bounce()
3071
+ 3
3072
+ sage: DyckWord([1,1,0,1,1,0,0,0]).bounce()
3073
+ 1
3074
+ sage: DyckWord([1,1,0,0,1,1,0,0]).bounce()
3075
+ 2
3076
+ sage: DyckWord([1,0,1,1,1,0,0,0]).bounce()
3077
+ 1
3078
+ sage: DyckWord([1,0,1,1,0,0,1,0]).bounce()
3079
+ 4
3080
+ sage: DyckWord([1,0,1,0,1,1,0,0]).bounce()
3081
+ 3
3082
+ sage: DyckWord([1,1,0,0,1,0,1,0]).bounce()
3083
+ 5
3084
+ sage: DyckWord([1,1,0,1,0,0,1,0]).bounce()
3085
+ 4
3086
+ sage: DyckWord([1,1,0,1,0,1,0,0]).bounce()
3087
+ 2
3088
+ sage: DyckWord([1,0,1,0,1,0,1,0]).bounce()
3089
+ 6
3090
+ """
3091
+ x_pos = len(self) // 2
3092
+ y_pos = len(self) // 2
3093
+
3094
+ b = 0
3095
+
3096
+ mode = "left"
3097
+ makeup_steps = 0
3098
+ l = self._list[:]
3099
+ l.reverse()
3100
+
3101
+ for move in l:
3102
+ if mode == "left":
3103
+ if move == close_symbol:
3104
+ x_pos -= 1
3105
+ elif move == open_symbol:
3106
+ y_pos -= 1
3107
+ if x_pos == y_pos:
3108
+ b += x_pos
3109
+ else:
3110
+ mode = "drop"
3111
+ elif mode == "drop":
3112
+ if move == close_symbol:
3113
+ makeup_steps += 1
3114
+ elif move == open_symbol:
3115
+ y_pos -= 1
3116
+ if x_pos == y_pos:
3117
+ b += x_pos
3118
+ mode = "left"
3119
+ x_pos -= makeup_steps
3120
+ makeup_steps = 0
3121
+
3122
+ return b
3123
+
3124
+ def dinv(self, labeling=None) -> int:
3125
+ r"""
3126
+ Return the dinv statistic of ``self`` due to M. Haiman, see [Hag2008]_.
3127
+
3128
+ If a labeling is provided then this function returns the dinv of the
3129
+ labeled Dyck word.
3130
+
3131
+ INPUT:
3132
+
3133
+ - ``labeling`` -- an optional argument to be viewed as the labelings
3134
+ of the vertical edges of the Dyck path
3135
+
3136
+ OUTPUT:
3137
+
3138
+ - an integer representing the ``dinv`` statistic of the Dyck path
3139
+ or the labelled Dyck path.
3140
+
3141
+ EXAMPLES::
3142
+
3143
+ sage: DyckWord([1,0,1,0,1,0,1,0,1,0]).dinv()
3144
+ 10
3145
+ sage: DyckWord([1,1,1,1,1,0,0,0,0,0]).dinv()
3146
+ 0
3147
+ sage: DyckWord([1,1,1,1,0,1,0,0,0,0]).dinv()
3148
+ 1
3149
+ sage: DyckWord([1,1,0,1,0,0,1,1,0,1,0,1,0,0]).dinv()
3150
+ 13
3151
+ sage: DyckWord([1,1,0,1,0,0,1,1,0,1,0,1,0,0]).dinv([1,2,3,4,5,6,7])
3152
+ 11
3153
+ sage: DyckWord([1,1,0,1,0,0,1,1,0,1,0,1,0,0]).dinv([6,7,5,3,4,2,1])
3154
+ 2
3155
+ """
3156
+ alist = self.to_area_sequence()
3157
+ cnt = 0
3158
+ for j, aj in enumerate(alist):
3159
+ if labeling is not None:
3160
+ lj = labeling[j]
3161
+ for i in range(j):
3162
+ if (alist[i] == aj and (labeling is None or labeling[i] < lj)) or (alist[i] - aj == 1 and (labeling is None or labeling[i] > lj)):
3163
+ cnt += 1
3164
+ return cnt
3165
+
3166
+ @combinatorial_map(name='to alternating sign matrix')
3167
+ def to_alternating_sign_matrix(self):
3168
+ r"""
3169
+ Return ``self`` as an alternating sign matrix.
3170
+
3171
+ This is an inclusion map from Dyck words of length `2n` to certain
3172
+ `n \times n` alternating sign matrices.
3173
+
3174
+ EXAMPLES::
3175
+
3176
+ sage: DyckWord([1,1,1,0,1,0,0,0]).to_alternating_sign_matrix() # needs sage.modules
3177
+ [ 0 0 1 0]
3178
+ [ 1 0 -1 1]
3179
+ [ 0 1 0 0]
3180
+ [ 0 0 1 0]
3181
+ sage: DyckWord([1,0,1,0,1,1,0,0]).to_alternating_sign_matrix() # needs sage.modules
3182
+ [1 0 0 0]
3183
+ [0 1 0 0]
3184
+ [0 0 0 1]
3185
+ [0 0 1 0]
3186
+ """
3187
+ parkfn = self.reverse().to_non_decreasing_parking_function()
3188
+ parkfn2 = [len(parkfn) + 1 - parkfn[i] for i in range(len(parkfn))]
3189
+ monotone_triangle = [[0] * (len(parkfn2) - j)
3190
+ for j in range(len(parkfn2))]
3191
+ for i in range(len(monotone_triangle)):
3192
+ for j in range(len(monotone_triangle[i])):
3193
+ monotone_triangle[i][j] = len(monotone_triangle[i]) - j
3194
+ monotone_triangle[i][0] = parkfn2[i]
3195
+ A = AlternatingSignMatrices(len(parkfn))
3196
+ return A.from_monotone_triangle(monotone_triangle)
3197
+
3198
+
3199
+ class DyckWords(UniqueRepresentation, Parent):
3200
+ r"""
3201
+ Dyck words.
3202
+
3203
+ A Dyck word is a sequence `(w_1, \ldots, w_n)` consisting of 1 s and 0 s,
3204
+ with the property that for any `i` with `1 \leq i \leq n`, the sequence
3205
+ `(w_1, \ldots, w_i)` contains at least as many 1 s as 0 s.
3206
+
3207
+ A Dyck word is balanced if the total number of 1 s is equal to the total
3208
+ number of 0 s. The number of balanced Dyck words of length `2k` is given
3209
+ by the :func:`Catalan number<sage.combinat.combinat.catalan_number>` `C_k`.
3210
+
3211
+ EXAMPLES:
3212
+
3213
+ This class can be called with three keyword parameters ``k1``, ``k2``
3214
+ and ``complete``.
3215
+
3216
+ If neither ``k1`` nor ``k2`` are specified, then :class:`DyckWords`
3217
+ returns the combinatorial class of all balanced (=complete) Dyck words,
3218
+ unless the keyword ``complete`` is set to False (in which case it
3219
+ returns the class of all Dyck words).
3220
+
3221
+ ::
3222
+
3223
+ sage: DW = DyckWords(); DW
3224
+ Complete Dyck words
3225
+ sage: [] in DW
3226
+ True
3227
+ sage: [1, 0, 1, 0] in DW
3228
+ True
3229
+ sage: [1, 1, 0] in DW
3230
+ False
3231
+ sage: ADW = DyckWords(complete=False); ADW
3232
+ Dyck words
3233
+ sage: [] in ADW
3234
+ True
3235
+ sage: [1, 0, 1, 0] in ADW
3236
+ True
3237
+ sage: [1, 1, 0] in ADW
3238
+ True
3239
+ sage: [1, 0, 0] in ADW
3240
+ False
3241
+
3242
+ If just ``k1`` is specified, then it returns the balanced Dyck words with
3243
+ ``k1`` opening parentheses and ``k1`` closing parentheses.
3244
+
3245
+ ::
3246
+
3247
+ sage: DW2 = DyckWords(2); DW2
3248
+ Dyck words with 2 opening parentheses and 2 closing parentheses
3249
+ sage: DW2.first()
3250
+ [1, 0, 1, 0]
3251
+ sage: DW2.last()
3252
+ [1, 1, 0, 0]
3253
+ sage: DW2.cardinality()
3254
+ 2
3255
+ sage: DyckWords(100).cardinality() == catalan_number(100)
3256
+ True
3257
+
3258
+ If ``k2`` is specified in addition to ``k1``, then it returns the
3259
+ Dyck words with ``k1`` opening parentheses and ``k2`` closing parentheses.
3260
+
3261
+ ::
3262
+
3263
+ sage: DW32 = DyckWords(3,2); DW32
3264
+ Dyck words with 3 opening parentheses and 2 closing parentheses
3265
+ sage: DW32.list()
3266
+ [[1, 0, 1, 0, 1],
3267
+ [1, 0, 1, 1, 0],
3268
+ [1, 1, 0, 0, 1],
3269
+ [1, 1, 0, 1, 0],
3270
+ [1, 1, 1, 0, 0]]
3271
+ """
3272
+ @staticmethod
3273
+ def __classcall_private__(cls, k1=None, k2=None, complete=True):
3274
+ """
3275
+ Choose the correct parent based upon input.
3276
+
3277
+ EXAMPLES::
3278
+
3279
+ sage: DW1 = DyckWords(3,3)
3280
+ sage: DW2 = DyckWords(3)
3281
+ sage: DW1 is DW2
3282
+ True
3283
+ """
3284
+ if k2 is None:
3285
+ if k1 is None:
3286
+ if complete:
3287
+ return CompleteDyckWords_all()
3288
+ return DyckWords_all()
3289
+
3290
+ k1 = Integer(k1)
3291
+ if k1 < 0:
3292
+ raise ValueError("k1 (= %s) must be nonnegative" % k1)
3293
+ return CompleteDyckWords_size(k1)
3294
+ else:
3295
+ k1 = Integer(k1)
3296
+
3297
+ k2 = Integer(k2)
3298
+ if k1 < 0 or (k2 is not None and k2 < 0):
3299
+ raise ValueError("k1 (= %s) and k2 (= %s) must be nonnegative, with k1 >= k2" % (k1, k2))
3300
+ if k1 < k2:
3301
+ raise ValueError("k1 (= %s) must be >= k2 (= %s)" % (k1, k2))
3302
+
3303
+ if k1 == k2:
3304
+ return CompleteDyckWords_size(k1)
3305
+ return DyckWords_size(k1, k2)
3306
+
3307
+ Element = DyckWord
3308
+
3309
+ # add options to class
3310
+ class options(GlobalOptions):
3311
+ r"""
3312
+ Set and display the options for Dyck words. If no parameters
3313
+ are set, then the function returns a copy of the options dictionary.
3314
+
3315
+ The ``options`` to Dyck words can be accessed as the method
3316
+ :meth:`DyckWords.options` of :class:`DyckWords` and
3317
+ related parent classes.
3318
+
3319
+ @OPTIONS
3320
+
3321
+ EXAMPLES::
3322
+
3323
+ sage: D = DyckWord([1, 1, 0, 1, 0, 0])
3324
+ sage: D
3325
+ [1, 1, 0, 1, 0, 0]
3326
+ sage: DyckWords.options.display="lattice"
3327
+ sage: D
3328
+ ___
3329
+ _| x
3330
+ | x .
3331
+ | . .
3332
+ sage: DyckWords.options(diagram_style='line')
3333
+ sage: D
3334
+ /\/\
3335
+ / \
3336
+ sage: DyckWords.options._reset()
3337
+ """
3338
+ NAME = 'DyckWords'
3339
+ module = 'sage.combinat.dyck_word'
3340
+ display = dict(default='list',
3341
+ description='Specifies how Dyck words should be printed',
3342
+ values=dict(list='displayed as a list',
3343
+ lattice='displayed on the lattice defined by ``diagram_style``'),
3344
+ case_sensitive=False)
3345
+ ascii_art = dict(default='path',
3346
+ description='Specifies how the ascii art of Dyck words should be printed',
3347
+ values=dict(path="Using the path string",
3348
+ pretty_output="Using pretty printing"),
3349
+ alias=dict(pretty_print='pretty_output', path_string='path'),
3350
+ case_sensitive=False)
3351
+ diagram_style = dict(default='grid',
3352
+ values=dict(grid='printing as paths on a grid using N and E steps',
3353
+ line='printing as paths on a line using NE and SE steps',),
3354
+ alias={'N-E': 'grid', 'NE-SE': 'line'},
3355
+ case_sensitive=False)
3356
+ latex_tikz_scale = dict(default=1,
3357
+ description='The default value for the tikz scale when latexed',
3358
+ checker=lambda x: True) # More trouble than it's worth to check
3359
+ latex_diagonal = dict(default=False,
3360
+ description='The default value for displaying the diagonal when latexed',
3361
+ checker=lambda x: isinstance(x, bool))
3362
+ latex_line_width_scalar = dict(default=2,
3363
+ description='The default value for the line width as a '
3364
+ 'multiple of the tikz scale when latexed',
3365
+ checker=lambda x: True) # More trouble than it's worth to check
3366
+ latex_color = dict(default='black',
3367
+ description='The default value for the color when latexed',
3368
+ checker=lambda x: isinstance(x, str))
3369
+ latex_bounce_path = dict(default=False,
3370
+ description='The default value for displaying the bounce path when latexed',
3371
+ checker=lambda x: isinstance(x, bool))
3372
+ latex_peaks = dict(default=False,
3373
+ description='The default value for displaying the peaks when latexed',
3374
+ checker=lambda x: isinstance(x, bool))
3375
+ latex_valleys = dict(default=False,
3376
+ description='The default value for displaying the valleys when latexed',
3377
+ checker=lambda x: isinstance(x, bool))
3378
+
3379
+ def _element_constructor_(self, word):
3380
+ """
3381
+ Construct an element of ``self``.
3382
+
3383
+ EXAMPLES::
3384
+
3385
+ sage: D = DyckWords()
3386
+ sage: elt = D([1, 1, 0, 1, 0, 0]); elt
3387
+ [1, 1, 0, 1, 0, 0]
3388
+ sage: elt.parent() is D
3389
+ True
3390
+ """
3391
+ if isinstance(word, DyckWord) and word.parent() is self:
3392
+ return word
3393
+ return self.element_class(self, list(word))
3394
+
3395
+ def __contains__(self, x) -> bool:
3396
+ r"""
3397
+ TESTS::
3398
+
3399
+ sage: D = DyckWords(complete=False)
3400
+ sage: [] in D
3401
+ True
3402
+ sage: [1] in D
3403
+ True
3404
+ sage: [0] in D
3405
+ False
3406
+ sage: [1, 0] in D
3407
+ True
3408
+ """
3409
+ if isinstance(x, DyckWord):
3410
+ return True
3411
+
3412
+ if not isinstance(x, list):
3413
+ return False
3414
+
3415
+ return is_a(x)
3416
+
3417
+ def from_heights(self, heights) -> DyckWord:
3418
+ r"""
3419
+ Compute a Dyck word knowing its heights.
3420
+
3421
+ We view the Dyck word as a Dyck path from `(0, 0)` to
3422
+ `(2n, 0)` in the first quadrant by letting ``1``'s represent
3423
+ steps in the direction `(1, 1)` and ``0``'s represent steps in
3424
+ the direction `(1, -1)`.
3425
+
3426
+ The :meth:`~DyckWord.heights` is the sequence of the `y`-coordinates of
3427
+ the `2n+1` lattice points along this path.
3428
+
3429
+ EXAMPLES::
3430
+
3431
+ sage: from sage.combinat.dyck_word import DyckWord
3432
+ sage: D = DyckWords(complete=False)
3433
+ sage: D.from_heights((0,))
3434
+ []
3435
+ sage: D.from_heights((0, 1, 0))
3436
+ [1, 0]
3437
+ sage: D.from_heights((0, 1, 2, 1, 0))
3438
+ [1, 1, 0, 0]
3439
+
3440
+ This also works for incomplete Dyck words::
3441
+
3442
+ sage: D.from_heights((0, 1, 2, 1, 2, 1))
3443
+ [1, 1, 0, 1, 0]
3444
+ sage: D.from_heights((0, 1, 2, 1))
3445
+ [1, 1, 0]
3446
+
3447
+ .. SEEALSO:: :meth:`~DyckWord.heights`, :meth:`min_from_heights`
3448
+
3449
+ TESTS::
3450
+
3451
+ sage: all(dw == D.from_heights(dw.heights())
3452
+ ....: for i in range(7) for dw in DyckWords(i))
3453
+ True
3454
+
3455
+ sage: D.from_heights((1, 2, 1))
3456
+ Traceback (most recent call last):
3457
+ ...
3458
+ ValueError: heights must start with 0: (1, 2, 1)
3459
+ sage: D.from_heights((0, 1, 4, 1))
3460
+ Traceback (most recent call last):
3461
+ ...
3462
+ ValueError: consecutive heights must differ by exactly 1: (0, 1, 4, 1)
3463
+ sage: D.from_heights(())
3464
+ Traceback (most recent call last):
3465
+ ...
3466
+ ValueError: heights must start with 0: ()
3467
+ """
3468
+ l1 = len(heights) - 1
3469
+ res = [0] * (l1)
3470
+ if not heights or heights[0] != 0:
3471
+ raise ValueError("heights must start with 0: %s" % (heights,))
3472
+ for i in range(l1):
3473
+ if heights[i] == heights[i + 1] - 1:
3474
+ res[i] = 1
3475
+ elif heights[i] != heights[i + 1] + 1:
3476
+ raise ValueError("consecutive heights must differ by exactly 1: %s" % (heights,))
3477
+ return self.element_class(self, res)
3478
+
3479
+ def min_from_heights(self, heights) -> DyckWord:
3480
+ r"""
3481
+ Compute the smallest Dyck word which achieves or surpasses
3482
+ a given sequence of heights.
3483
+
3484
+ INPUT:
3485
+
3486
+ - ``heights`` -- a nonempty list or iterable consisting of
3487
+ nonnegative integers, the first of which is `0`
3488
+
3489
+ OUTPUT:
3490
+
3491
+ - The smallest Dyck word whose sequence of heights is
3492
+ componentwise higher-or-equal to ``heights``. Here,
3493
+ "smaller" can be understood both in the sense of
3494
+ lexicographic order on the Dyck words, and in the sense
3495
+ of every vertex of the path having the smallest possible
3496
+ height.
3497
+
3498
+ .. SEEALSO::
3499
+
3500
+ - :meth:`~DyckWord.heights`
3501
+ - :meth:`from_heights`
3502
+
3503
+ EXAMPLES::
3504
+
3505
+ sage: D = DyckWords(complete=False)
3506
+ sage: D.min_from_heights((0,))
3507
+ []
3508
+ sage: D.min_from_heights((0, 1, 0))
3509
+ [1, 0]
3510
+ sage: D.min_from_heights((0, 0, 2, 0, 0))
3511
+ [1, 1, 0, 0]
3512
+ sage: D.min_from_heights((0, 0, 2, 0, 2, 0))
3513
+ [1, 1, 0, 1, 0]
3514
+ sage: D.min_from_heights((0, 0, 1, 0, 1, 0))
3515
+ [1, 1, 0, 1, 0]
3516
+
3517
+ TESTS::
3518
+
3519
+ sage: D.min_from_heights(())
3520
+ Traceback (most recent call last):
3521
+ ...
3522
+ ValueError: heights must start with 0: ()
3523
+ """
3524
+ if not heights or heights[0] != 0:
3525
+ raise ValueError("heights must start with 0: %s" % (heights,))
3526
+ # round heights to the smallest even-odd integer
3527
+ heights = list(heights)
3528
+ for i in range(0, len(heights), 2):
3529
+ if heights[i] % 2:
3530
+ heights[i] += 1
3531
+ for i in range(1, len(heights), 2):
3532
+ if heights[i] % 2 == 0:
3533
+ heights[i] += 1
3534
+
3535
+ # smooth heights
3536
+ for i in range(len(heights) - 1):
3537
+ if heights[i + 1] < heights[i]:
3538
+ heights[i + 1] = heights[i] - 1
3539
+ for i in range(len(heights) - 1, 0, -1):
3540
+ if heights[i] > heights[i - 1]:
3541
+ heights[i - 1] = heights[i] - 1
3542
+ return self.from_heights(heights)
3543
+
3544
+
3545
+ class DyckWords_all(DyckWords):
3546
+ """
3547
+ All Dyck words.
3548
+ """
3549
+
3550
+ def __init__(self):
3551
+ """
3552
+ Initialize ``self``.
3553
+
3554
+ EXAMPLES::
3555
+
3556
+ sage: TestSuite(DyckWords(complete=False)).run()
3557
+ """
3558
+ DyckWords.__init__(self, category=InfiniteEnumeratedSets())
3559
+
3560
+ def _repr_(self) -> str:
3561
+ r"""
3562
+ TESTS::
3563
+
3564
+ sage: DyckWords(complete=False)
3565
+ Dyck words
3566
+ """
3567
+ return "Dyck words"
3568
+
3569
+ def _an_element_(self) -> DyckWord:
3570
+ r"""
3571
+ TESTS::
3572
+
3573
+ sage: DyckWords(complete=False).an_element()
3574
+ [1, 0, 1, 0, 1, 0, 1, 0, 1, 0]
3575
+ """
3576
+ return DyckWords(5).an_element()
3577
+
3578
+ def __iter__(self):
3579
+ """
3580
+ Iterate over ``self``.
3581
+
3582
+ EXAMPLES::
3583
+
3584
+ sage: it = DyckWords(complete=False).__iter__()
3585
+ sage: [next(it) for x in range(10)]
3586
+ [[],
3587
+ [1],
3588
+ [1, 0],
3589
+ [1, 1],
3590
+ [1, 0, 0],
3591
+ [1, 0, 1],
3592
+ [1, 1, 0],
3593
+ [1, 1, 1],
3594
+ [1, 0, 1, 0],
3595
+ [1, 1, 0, 0]]
3596
+ """
3597
+ n = 0
3598
+ yield self.element_class(self, [])
3599
+ while True:
3600
+ for k in range(1, n + 1):
3601
+ for x in DyckWords_size(k, n - k):
3602
+ yield self.element_class(self, list(x))
3603
+ n += 1
3604
+
3605
+
3606
+ class DyckWordBacktracker(GenericBacktracker):
3607
+ r"""
3608
+ This class is an iterator for all Dyck words
3609
+ with `n` opening parentheses and `n - k` closing parentheses using
3610
+ the backtracker class. It is used by the :class:`DyckWords_size` class.
3611
+
3612
+ This is not really meant to be called directly, partially because
3613
+ it fails in a couple corner cases: ``DWB(0)`` yields ``[0]``, not the
3614
+ empty word, and ``DWB(k, k+1)`` yields something (it shouldn't yield
3615
+ anything). This could be fixed with a sanity check in ``_rec()``, but
3616
+ then we'd be doing the sanity check *every time* we generate new
3617
+ objects; instead, we do *one* sanity check in :class:`DyckWords` and
3618
+ assume here that the sanity check has already been made.
3619
+
3620
+ AUTHOR:
3621
+
3622
+ - Dan Drake (2008-05-30)
3623
+ """
3624
+
3625
+ def __init__(self, k1, k2):
3626
+ r"""
3627
+ TESTS::
3628
+
3629
+ sage: from sage.combinat.dyck_word import DyckWordBacktracker
3630
+ sage: len(list(DyckWordBacktracker(5, 5)))
3631
+ 42
3632
+ sage: len(list(DyckWordBacktracker(6,4)))
3633
+ 90
3634
+ sage: len(list(DyckWordBacktracker(7,0)))
3635
+ 1
3636
+ """
3637
+ GenericBacktracker.__init__(self, [], (0, 0))
3638
+ # note that the comments in this class think of our objects as
3639
+ # Dyck paths, not words; having k1 opening parens and k2 closing
3640
+ # parens corresponds to paths of length k1 + k2 ending at height
3641
+ # k1 - k2.
3642
+ k1 = Integer(k1)
3643
+ k2 = Integer(k2)
3644
+ self.n = k1 + k2
3645
+ self.endht = k1 - k2
3646
+
3647
+ def _rec(self, path, state):
3648
+ r"""
3649
+ TESTS::
3650
+
3651
+ sage: from sage.combinat.dyck_word import DyckWordBacktracker
3652
+ sage: dwb = DyckWordBacktracker(3, 3)
3653
+ sage: list(dwb._rec([1,1,0],(3, 2)))
3654
+ [([1, 1, 0, 0], (4, 1), False), ([1, 1, 0, 1], (4, 3), False)]
3655
+ sage: list(dwb._rec([1,1,0,0],(4, 0)))
3656
+ [([1, 1, 0, 0, 1], (5, 1), False)]
3657
+ sage: list(DyckWordBacktracker(4, 4)._rec([1,1,1,1],(4, 4)))
3658
+ [([1, 1, 1, 1, 0], (5, 3), False)]
3659
+ """
3660
+ len, ht = state
3661
+
3662
+ if len < self.n - 1:
3663
+ # if length is less than n-1, new path won't have length n, so
3664
+ # don't yield it, and keep building paths
3665
+
3666
+ # if the path isn't too low and is not touching the x-axis, we can
3667
+ # yield a path with a downstep at the end
3668
+ if ht > (self.endht - (self.n - len)) and ht > 0:
3669
+ yield path + [0], (len + 1, ht - 1), False
3670
+
3671
+ # if the path isn't too high, it can also take an upstep
3672
+ if ht < (self.endht + (self.n - len)):
3673
+ yield path + [1], (len + 1, ht + 1), False
3674
+ else:
3675
+ # length is n - 1, so add a single step (up or down,
3676
+ # according to current height and endht), don't try to
3677
+ # construct more paths, and yield the path
3678
+ if ht < self.endht:
3679
+ yield path + [1], None, True
3680
+ else:
3681
+ yield path + [0], None, True
3682
+
3683
+
3684
+ class DyckWords_size(DyckWords):
3685
+ """
3686
+ Dyck words with `k_1` openers and `k_2` closers.
3687
+ """
3688
+
3689
+ def __init__(self, k1, k2):
3690
+ r"""
3691
+ TESTS:
3692
+
3693
+ Check that :issue:`18244` is fixed::
3694
+
3695
+ sage: DyckWords(13r, 8r).cardinality()
3696
+ 87210
3697
+ sage: parent(_)
3698
+ Integer Ring
3699
+ sage: TestSuite(DyckWords(4,2)).run()
3700
+ """
3701
+ self.k1 = Integer(k1)
3702
+ self.k2 = Integer(k2)
3703
+ DyckWords.__init__(self, category=FiniteEnumeratedSets())
3704
+
3705
+ def _repr_(self) -> str:
3706
+ r"""
3707
+ TESTS::
3708
+
3709
+ sage: DyckWords(4)
3710
+ Dyck words with 4 opening parentheses and 4 closing parentheses
3711
+ """
3712
+ return "Dyck words with %s opening parentheses and %s closing parentheses" % (self.k1, self.k2)
3713
+
3714
+ def __contains__(self, x) -> bool:
3715
+ r"""
3716
+ EXAMPLES::
3717
+
3718
+ sage: [1, 0, 0, 1] in DyckWords(2,2)
3719
+ False
3720
+ sage: [1, 0, 1, 0] in DyckWords(2,2)
3721
+ True
3722
+ sage: [1, 0, 1, 0, 1] in DyckWords(3,2)
3723
+ True
3724
+ sage: [1, 0, 1, 1, 0] in DyckWords(3,2)
3725
+ True
3726
+ sage: [1, 0, 1, 1] in DyckWords(3,1)
3727
+ True
3728
+ """
3729
+ return is_a(x, self.k1, self.k2)
3730
+
3731
+ def __iter__(self):
3732
+ r"""
3733
+ Return an iterator for Dyck words with ``k1`` opening and ``k2``
3734
+ closing parentheses.
3735
+
3736
+ EXAMPLES::
3737
+
3738
+ sage: list(DyckWords(0))
3739
+ [[]]
3740
+ sage: list(DyckWords(1))
3741
+ [[1, 0]]
3742
+ sage: list(DyckWords(2))
3743
+ [[1, 0, 1, 0], [1, 1, 0, 0]]
3744
+ sage: len(DyckWords(5))
3745
+ 42
3746
+ sage: list(DyckWords(3,2))
3747
+ [[1, 0, 1, 0, 1],
3748
+ [1, 0, 1, 1, 0],
3749
+ [1, 1, 0, 0, 1],
3750
+ [1, 1, 0, 1, 0],
3751
+ [1, 1, 1, 0, 0]]
3752
+ """
3753
+ if self.k1 == 0:
3754
+ yield self.element_class(self, [])
3755
+ elif self.k2 == 0:
3756
+ yield self.element_class(self, [open_symbol] * self.k1)
3757
+ else:
3758
+ for w in DyckWordBacktracker(self.k1, self.k2):
3759
+ yield self.element_class(self, w)
3760
+
3761
+ def cardinality(self) -> int:
3762
+ r"""
3763
+ Return the number of Dyck words with `k_1` openers and `k_2` closers.
3764
+
3765
+ This number is
3766
+
3767
+ .. MATH::
3768
+
3769
+ \frac{k_1 - k_2 + 1}{k_1 + 1} \binom{k_1 + k_2}{k_2}
3770
+ = \binom{k_1 + k_2}{k_2} - \binom{k_1 + k_2}{k_2 - 1}
3771
+
3772
+ if `k_2 \leq k_1 + 1`, and `0` if `k_2 > k_1` (these numbers are the
3773
+ same if `k_2 = k_1 + 1`).
3774
+
3775
+ EXAMPLES::
3776
+
3777
+ sage: DyckWords(3, 2).cardinality()
3778
+ 5
3779
+ sage: all(all(DyckWords(p, q).cardinality()
3780
+ ....: == len(DyckWords(p, q).list()) for q in range(p + 1))
3781
+ ....: for p in range(7))
3782
+ True
3783
+ """
3784
+ return (self.k1 - self.k2 + 1) * (self.k1 + self.k2).binomial(self.k2) // (self.k1 + 1)
3785
+
3786
+ ################################################################
3787
+ # Complete Dyck words
3788
+
3789
+
3790
+ class CompleteDyckWords(DyckWords):
3791
+ """
3792
+ Abstract base class for all complete Dyck words.
3793
+ """
3794
+ Element = DyckWord_complete
3795
+
3796
+ def __contains__(self, x) -> bool:
3797
+ r"""
3798
+ TESTS::
3799
+
3800
+ sage: [] in DyckWords()
3801
+ True
3802
+ sage: [1] in DyckWords()
3803
+ False
3804
+ sage: [0] in DyckWords()
3805
+ False
3806
+ """
3807
+ if isinstance(x, DyckWord_complete):
3808
+ return True
3809
+
3810
+ if not isinstance(x, list):
3811
+ return False
3812
+
3813
+ if len(x) % 2:
3814
+ return False
3815
+
3816
+ return is_a(x, len(x) // 2)
3817
+
3818
+ def from_Catalan_code(self, code) -> DyckWord:
3819
+ r"""
3820
+ Return the Dyck word associated to the given Catalan code
3821
+ ``code``.
3822
+
3823
+ A Catalan code of length `n` is a sequence
3824
+ `(a_1, a_2, \ldots, a_n)` of `n` integers `a_i` such that:
3825
+
3826
+ - `0 \leq a_i \leq n-i` for every `i`;
3827
+
3828
+ - if `i < j` and `a_i > 0` and `a_j > 0` and
3829
+ `a_{i+1} = a_{i+2} = \cdots = a_{j-1} = 0`,
3830
+ then `a_i - a_j < j-i`.
3831
+
3832
+ It turns out that the Catalan codes of length `n` are in
3833
+ bijection with Dyck words.
3834
+
3835
+ The Catalan code of a Dyck word is example (x) in Richard Stanley's
3836
+ exercises on combinatorial interpretations for Catalan objects.
3837
+ The code in this example is the reverse of the description provided
3838
+ there. See [Sta-EC2]_ and [StaCat98]_.
3839
+
3840
+ EXAMPLES::
3841
+
3842
+ sage: DyckWords().from_Catalan_code([])
3843
+ []
3844
+ sage: DyckWords().from_Catalan_code([0])
3845
+ [1, 0]
3846
+ sage: DyckWords().from_Catalan_code([0, 1])
3847
+ [1, 1, 0, 0]
3848
+ sage: DyckWords().from_Catalan_code([0, 0])
3849
+ [1, 0, 1, 0]
3850
+ """
3851
+ code = list(code)
3852
+ if not code:
3853
+ return self.element_class(self, [])
3854
+ res = self.from_Catalan_code(code[:-1])
3855
+ cuts = [0] + res.returns_to_zero()
3856
+ lst = [1] + res[:cuts[code[-1]]] + [0] + res[cuts[code[-1]]:]
3857
+ return self.element_class(self, lst)
3858
+
3859
+ def from_area_sequence(self, code) -> DyckWord:
3860
+ r"""
3861
+ Return the Dyck word associated to the given area sequence
3862
+ ``code``.
3863
+
3864
+ See :meth:`~DyckWord.to_area_sequence` for a definition of the area
3865
+ sequence of a Dyck word.
3866
+
3867
+ .. SEEALSO:: :meth:`~DyckWord_complete.area`, :meth:`~DyckWord.to_area_sequence`.
3868
+
3869
+ INPUT:
3870
+
3871
+ - ``code`` -- list of integers satisfying ``code[0] == 0``
3872
+ and ``0 <= code[i+1] <= code[i]+1``
3873
+
3874
+ EXAMPLES::
3875
+
3876
+ sage: DyckWords().from_area_sequence([])
3877
+ []
3878
+ sage: DyckWords().from_area_sequence([0])
3879
+ [1, 0]
3880
+ sage: DyckWords().from_area_sequence([0, 1])
3881
+ [1, 1, 0, 0]
3882
+ sage: DyckWords().from_area_sequence([0, 0])
3883
+ [1, 0, 1, 0]
3884
+ """
3885
+ if not is_area_sequence(code):
3886
+ raise ValueError("the given sequence is not a sequence giving "
3887
+ "the number of cells between the Dyck path "
3888
+ "and the diagonal")
3889
+ dyck_word = []
3890
+ for i in range(len(code)):
3891
+ if i:
3892
+ dyck_word.extend([close_symbol] * (code[i - 1] - code[i] + 1))
3893
+ dyck_word.append(open_symbol)
3894
+ dyck_word.extend([close_symbol] * (2 * len(code) - len(dyck_word)))
3895
+ return self.element_class(self, dyck_word)
3896
+
3897
+ def from_noncrossing_partition(self, ncp) -> DyckWord:
3898
+ r"""
3899
+ Convert a noncrossing partition ``ncp`` to a Dyck word.
3900
+
3901
+ EXAMPLES::
3902
+
3903
+ sage: DyckWord(noncrossing_partition=[[1,2]]) # indirect doctest
3904
+ [1, 1, 0, 0]
3905
+ sage: DyckWord(noncrossing_partition=[[1],[2]])
3906
+ [1, 0, 1, 0]
3907
+
3908
+ sage: dws = DyckWords(5).list()
3909
+ sage: ncps = [x.to_noncrossing_partition() for x in dws]
3910
+ sage: dws2 = [DyckWord(noncrossing_partition=x) for x in ncps]
3911
+ sage: dws == dws2
3912
+ True
3913
+ """
3914
+ l = [0] * sum(len(v) for v in ncp)
3915
+ for v in ncp:
3916
+ l[max(v) - 1] = len(v)
3917
+
3918
+ res = []
3919
+ for i in l:
3920
+ res += [open_symbol] + [close_symbol] * i
3921
+ return self.element_class(self, res)
3922
+
3923
+ def from_non_decreasing_parking_function(self, pf) -> DyckWord:
3924
+ r"""
3925
+ Bijection from :class:`non-decreasing parking
3926
+ functions<sage.combinat.non_decreasing_parking_function.NonDecreasingParkingFunctions>`.
3927
+
3928
+ See there the method
3929
+ :meth:`~sage.combinat.non_decreasing_parking_function.NonDecreasingParkingFunction.to_dyck_word`
3930
+ for more information.
3931
+
3932
+ EXAMPLES::
3933
+
3934
+ sage: D = DyckWords()
3935
+ sage: D.from_non_decreasing_parking_function([])
3936
+ []
3937
+ sage: D.from_non_decreasing_parking_function([1])
3938
+ [1, 0]
3939
+ sage: D.from_non_decreasing_parking_function([1,1])
3940
+ [1, 1, 0, 0]
3941
+ sage: D.from_non_decreasing_parking_function([1,2])
3942
+ [1, 0, 1, 0]
3943
+ sage: D.from_non_decreasing_parking_function([1,1,1])
3944
+ [1, 1, 1, 0, 0, 0]
3945
+ sage: D.from_non_decreasing_parking_function([1,2,3])
3946
+ [1, 0, 1, 0, 1, 0]
3947
+ sage: D.from_non_decreasing_parking_function([1,1,3,3,4,6,6])
3948
+ [1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0]
3949
+
3950
+ TESTS::
3951
+
3952
+ sage: D.from_non_decreasing_parking_function(NonDecreasingParkingFunction([]))
3953
+ []
3954
+ sage: D.from_non_decreasing_parking_function(NonDecreasingParkingFunction([1,1,3,3,4,6,6]))
3955
+ [1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0]
3956
+ """
3957
+ return self.from_area_sequence([i - pf[i] + 1 for i in range(len(pf))])
3958
+
3959
+
3960
+ class CompleteDyckWords_all(CompleteDyckWords, DyckWords_all):
3961
+ """
3962
+ All complete Dyck words.
3963
+ """
3964
+
3965
+ def _repr_(self) -> str:
3966
+ r"""
3967
+ TESTS::
3968
+
3969
+ sage: DyckWords()
3970
+ Complete Dyck words
3971
+ """
3972
+ return "Complete Dyck words"
3973
+
3974
+ def __iter__(self):
3975
+ """
3976
+ Iterate over ``self``.
3977
+
3978
+ EXAMPLES::
3979
+
3980
+ sage: it = DyckWords().__iter__()
3981
+ sage: [next(it) for x in range(10)]
3982
+ [[],
3983
+ [1, 0],
3984
+ [1, 0, 1, 0],
3985
+ [1, 1, 0, 0],
3986
+ [1, 0, 1, 0, 1, 0],
3987
+ [1, 0, 1, 1, 0, 0],
3988
+ [1, 1, 0, 0, 1, 0],
3989
+ [1, 1, 0, 1, 0, 0],
3990
+ [1, 1, 1, 0, 0, 0],
3991
+ [1, 0, 1, 0, 1, 0, 1, 0]]
3992
+ """
3993
+ n = 0
3994
+ while True:
3995
+ for x in CompleteDyckWords_size(n):
3996
+ yield self.element_class(self, list(x))
3997
+ n += 1
3998
+
3999
+ class height_poset(UniqueRepresentation, Parent):
4000
+ r"""
4001
+ The poset of complete Dyck words compared componentwise by ``heights``.
4002
+
4003
+ This is, ``D`` is smaller than or equal to ``D'`` if it is
4004
+ weakly below ``D'``.
4005
+
4006
+ This is implemented by comparison of area sequences.
4007
+ """
4008
+
4009
+ def __init__(self):
4010
+ r"""
4011
+ TESTS::
4012
+
4013
+ sage: poset = DyckWords().height_poset()
4014
+ sage: TestSuite(poset).run()
4015
+ """
4016
+ Parent.__init__(self, facade=DyckWords_all(), category=Posets())
4017
+
4018
+ def _an_element_(self):
4019
+ r"""
4020
+ TESTS::
4021
+
4022
+ sage: DyckWords().height_poset().an_element() # indirect doctest
4023
+ [1, 0, 1, 0, 1, 0, 1, 0, 1, 0]
4024
+ """
4025
+ return DyckWords_all().an_element()
4026
+
4027
+ def __call__(self, obj):
4028
+ r"""
4029
+ TESTS::
4030
+
4031
+ sage: poset = DyckWords().height_poset()
4032
+ sage: poset([1,0,1,0])
4033
+ [1, 0, 1, 0]
4034
+ """
4035
+ return DyckWords_all()(obj)
4036
+
4037
+ def le(self, dw1, dw2):
4038
+ r"""
4039
+ Compare two Dyck words of equal size, and return ``True`` if
4040
+ all of the heights of ``dw1`` are less than or equal to the
4041
+ respective heights of ``dw2`` .
4042
+
4043
+ .. SEEALSO::
4044
+
4045
+ :meth:`~sage.combinat.dyck_word.DyckWord.to_area_sequence`
4046
+
4047
+ EXAMPLES::
4048
+
4049
+ sage: poset = DyckWords().height_poset()
4050
+ sage: poset.le(DyckWord([]), DyckWord([]))
4051
+ True
4052
+ sage: poset.le(DyckWord([1,0]), DyckWord([1,0]))
4053
+ True
4054
+ sage: poset.le(DyckWord([1,0,1,0]), DyckWord([1,1,0,0]))
4055
+ True
4056
+ sage: poset.le(DyckWord([1,1,0,0]), DyckWord([1,0,1,0]))
4057
+ False
4058
+ sage: [poset.le(dw1, dw2)
4059
+ ....: for dw1 in DyckWords(3) for dw2 in DyckWords(3)]
4060
+ [True, True, True, True, True, False, True, False, True, True,
4061
+ False, False, True, True, True, False, False, False, True,
4062
+ True, False, False, False, False, True]
4063
+ """
4064
+ if len(dw1) != len(dw2):
4065
+ raise ValueError(f"length mismatch: {dw1} and {dw2}")
4066
+ ar1 = dw1._area_sequence_iter()
4067
+ ar2 = dw2._area_sequence_iter()
4068
+ return all(a1 <= a2 for a1, a2 in zip(ar1, ar2))
4069
+
4070
+
4071
+ class CompleteDyckWords_size(CompleteDyckWords, DyckWords_size):
4072
+ """
4073
+ All complete Dyck words of a given size.
4074
+ """
4075
+
4076
+ def __init__(self, k):
4077
+ """
4078
+ Initialize ``self``.
4079
+
4080
+ TESTS::
4081
+
4082
+ sage: TestSuite(DyckWords(4)).run()
4083
+ """
4084
+ CompleteDyckWords.__init__(self, category=FiniteEnumeratedSets())
4085
+ DyckWords_size.__init__(self, k, k)
4086
+
4087
+ def __contains__(self, x) -> bool:
4088
+ r"""
4089
+ TESTS::
4090
+
4091
+ sage: [1, 0] in DyckWords(1)
4092
+ True
4093
+ sage: [1, 0] in DyckWords(2)
4094
+ False
4095
+ sage: [1, 1, 0, 0] in DyckWords(2)
4096
+ True
4097
+ sage: [1, 0, 0, 1] in DyckWords(2)
4098
+ False
4099
+ """
4100
+ return CompleteDyckWords.__contains__(self, x) and len(x) // 2 == self.k1
4101
+
4102
+ def cardinality(self) -> int:
4103
+ r"""
4104
+ Return the number of complete Dyck words of semilength `n`, i.e. the
4105
+ `n`-th :func:`Catalan number<sage.combinat.combinat.catalan_number>`.
4106
+
4107
+ EXAMPLES::
4108
+
4109
+ sage: DyckWords(4).cardinality()
4110
+ 14
4111
+ sage: ns = list(range(9))
4112
+ sage: dws = [DyckWords(n) for n in ns]
4113
+ sage: all(dw.cardinality() == len(dw.list()) for dw in dws)
4114
+ True
4115
+ """
4116
+ return catalan_number(self.k1)
4117
+
4118
+ def random_element(self) -> DyckWord:
4119
+ """
4120
+ Return a random complete Dyck word of semilength `n`.
4121
+
4122
+ The algorithm is based on a classical combinatorial fact. One
4123
+ chooses at random a word with `n` 0s and `n+1` 1s. One then
4124
+ considers every 1 as an ascending step and every 0 as a
4125
+ descending step, and one finds the lowest point of the path
4126
+ (with respect to a slightly tilted slope). One then cuts the
4127
+ path at this point and builds a Dyck word by exchanging the
4128
+ two parts of the word and removing the initial step.
4129
+
4130
+ .. TODO::
4131
+
4132
+ extend this to m-Dyck words
4133
+
4134
+ EXAMPLES::
4135
+
4136
+ sage: dw = DyckWords(8)
4137
+ sage: dw.random_element() # random
4138
+ [1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0]
4139
+
4140
+ sage: D = DyckWords(8)
4141
+ sage: D.random_element() in D
4142
+ True
4143
+ """
4144
+ from sage.misc.prandom import shuffle
4145
+ n = self.k1
4146
+ w = [0] * n + [1] * (n + 1)
4147
+ shuffle(w)
4148
+ idx = 0
4149
+ height = 0
4150
+ height_min = 0
4151
+ for i in range(2 * n):
4152
+ if w[i] == 1:
4153
+ height += n
4154
+ else:
4155
+ height -= n + 1
4156
+ if height < height_min:
4157
+ height_min = height
4158
+ idx = i + 1
4159
+ w = w[idx:] + w[:idx]
4160
+ return self.element_class(self, w[1:])
4161
+
4162
+ def _iter_by_recursion(self):
4163
+ """
4164
+ Iterate over ``self`` by recursively using the position of
4165
+ the first return to 0.
4166
+
4167
+ EXAMPLES::
4168
+
4169
+ sage: DW = DyckWords(4)
4170
+ sage: L = [d for d in DW._iter_by_recursion()]; L
4171
+ [[1, 0, 1, 0, 1, 0, 1, 0],
4172
+ [1, 0, 1, 0, 1, 1, 0, 0],
4173
+ [1, 0, 1, 1, 0, 0, 1, 0],
4174
+ [1, 0, 1, 1, 0, 1, 0, 0],
4175
+ [1, 0, 1, 1, 1, 0, 0, 0],
4176
+ [1, 1, 0, 0, 1, 0, 1, 0],
4177
+ [1, 1, 0, 0, 1, 1, 0, 0],
4178
+ [1, 1, 0, 1, 0, 0, 1, 0],
4179
+ [1, 1, 1, 0, 0, 0, 1, 0],
4180
+ [1, 1, 0, 1, 0, 1, 0, 0],
4181
+ [1, 1, 0, 1, 1, 0, 0, 0],
4182
+ [1, 1, 1, 0, 0, 1, 0, 0],
4183
+ [1, 1, 1, 0, 1, 0, 0, 0],
4184
+ [1, 1, 1, 1, 0, 0, 0, 0]]
4185
+ sage: len(L) == DW.cardinality()
4186
+ True
4187
+ """
4188
+ # Do a couple of small cases first
4189
+ if self.k1 <= 2:
4190
+ if self.k1 == 0:
4191
+ yield self.element_class(self, [])
4192
+ elif self.k1 == 1:
4193
+ yield self.element_class(self, [1, 0])
4194
+ elif self.k1 == 2:
4195
+ yield self.element_class(self, [1, 0, 1, 0])
4196
+ yield self.element_class(self, [1, 1, 0, 0])
4197
+ return
4198
+
4199
+ # Create all necessary parents
4200
+ P = [CompleteDyckWords_size(i) for i in range(self.k1)]
4201
+
4202
+ for i in range(self.k1):
4203
+ for p in P[i]._iter_by_recursion():
4204
+ list_1p0 = [1] + list(p) + [0]
4205
+ for s in P[-i - 1]._iter_by_recursion():
4206
+ yield self.element_class(self, list_1p0 + list(s))
4207
+
4208
+
4209
+ def is_area_sequence(seq) -> bool:
4210
+ r"""
4211
+ Test if a sequence `l` of integers satisfies `l_0 = 0` and
4212
+ `0 \leq l_{i+1} \leq l_i + 1` for `i > 0`.
4213
+
4214
+ EXAMPLES::
4215
+
4216
+ sage: from sage.combinat.dyck_word import is_area_sequence
4217
+ sage: is_area_sequence([0,2,0])
4218
+ False
4219
+ sage: is_area_sequence([1,2,3])
4220
+ False
4221
+ sage: is_area_sequence([0,1,0])
4222
+ True
4223
+ sage: is_area_sequence([0,1,2])
4224
+ True
4225
+ sage: is_area_sequence([])
4226
+ True
4227
+ """
4228
+ if not seq:
4229
+ return True
4230
+ return seq[0] == 0 and all(0 <= seq[i + 1] <= seq[i] + 1
4231
+ for i in range(len(seq) - 1))
4232
+
4233
+
4234
+ def is_a(obj, k1=None, k2=None) -> bool:
4235
+ r"""
4236
+ Test if ``obj`` is a Dyck word with exactly ``k1`` open symbols and
4237
+ exactly ``k2`` close symbols.
4238
+
4239
+ If ``k1`` is not specified, then the number of open symbols can be
4240
+ arbitrary. If ``k1`` is specified but ``k2`` is not, then ``k2`` is
4241
+ set to be ``k1``.
4242
+
4243
+ EXAMPLES::
4244
+
4245
+ sage: from sage.combinat.dyck_word import is_a
4246
+ sage: is_a([1,1,0,0])
4247
+ True
4248
+ sage: is_a([1,0,1,0])
4249
+ True
4250
+ sage: is_a([1,1,0,0], 2)
4251
+ True
4252
+ sage: is_a([1,1,0,0], 3)
4253
+ False
4254
+ sage: is_a([1,1,0,0], 3, 2)
4255
+ False
4256
+ sage: is_a([1,1,0])
4257
+ True
4258
+ sage: is_a([0,1,0])
4259
+ False
4260
+ sage: is_a([1,0,0])
4261
+ False
4262
+ sage: is_a([1,1,0],2,1)
4263
+ True
4264
+ sage: is_a([1,1,0],2)
4265
+ False
4266
+ sage: is_a([1,1,0],1,1)
4267
+ False
4268
+ """
4269
+ if k1 is not None:
4270
+ if k2 is None:
4271
+ k2 = k1
4272
+ elif k1 < k2:
4273
+ raise ValueError("k1 (= %s) must be >= k2 (= %s)" % (k1, k2))
4274
+
4275
+ n_opens = 0
4276
+ n_closes = 0
4277
+
4278
+ for p in obj:
4279
+ if p == open_symbol:
4280
+ n_opens += 1
4281
+ elif p == close_symbol:
4282
+ if n_opens == n_closes:
4283
+ return False
4284
+ n_closes += 1
4285
+ else:
4286
+ return False
4287
+
4288
+ return (k1 is None and k2 is None) or (n_opens == k1 and n_closes == k2)
4289
+
4290
+
4291
+ def pealing(D, return_touches=False):
4292
+ r"""
4293
+ A helper function for computing the bijection from a Dyck word to a
4294
+ `231`-avoiding permutation using the bijection "Stump". For details
4295
+ see [Stu2008]_.
4296
+
4297
+ .. SEEALSO::
4298
+
4299
+ :meth:`~sage.combinat.dyck_word.DyckWord_complete.to_noncrossing_partition`
4300
+
4301
+ EXAMPLES::
4302
+
4303
+ sage: from sage.combinat.dyck_word import pealing
4304
+ sage: pealing(DyckWord([1,1,0,0]))
4305
+ [1, 0, 1, 0]
4306
+ sage: pealing(DyckWord([1,0,1,0]))
4307
+ [1, 0, 1, 0]
4308
+ sage: pealing(DyckWord([1, 1, 0, 0, 1, 1, 1, 0, 0, 0]))
4309
+ [1, 0, 1, 0, 1, 0, 1, 0, 1, 0]
4310
+ sage: pealing(DyckWord([1,1,0,0]),return_touches=True)
4311
+ ([1, 0, 1, 0], [[1, 2]])
4312
+ sage: pealing(DyckWord([1,0,1,0]),return_touches=True)
4313
+ ([1, 0, 1, 0], [])
4314
+ sage: pealing(DyckWord([1, 1, 0, 0, 1, 1, 1, 0, 0, 0]),return_touches=True)
4315
+ ([1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [[1, 2], [3, 5]])
4316
+ """
4317
+ n = D.semilength()
4318
+ area = D.to_area_sequence()
4319
+ new_area = []
4320
+ touch_sequences = []
4321
+ touches = []
4322
+ for i in range(n - 1):
4323
+ if area[i + 1] == 0:
4324
+ touches.append(i + 1)
4325
+ if len(touches) > 1:
4326
+ touch_sequences.append(touches)
4327
+ touches = []
4328
+ elif area[i] == 0:
4329
+ touches = []
4330
+ new_area.append(0)
4331
+ elif area[i + 1] == 1:
4332
+ new_area.append(0)
4333
+ touches.append(i + 1)
4334
+ else:
4335
+ new_area.append(area[i + 1] - 2)
4336
+ new_area.append(0)
4337
+ if area[n - 1]:
4338
+ touches.append(n)
4339
+ if len(touches) > 1:
4340
+ touch_sequences.append(touches)
4341
+ D = DyckWords().from_area_sequence(new_area)
4342
+ if return_touches:
4343
+ return (D, touch_sequences)
4344
+ else:
4345
+ return D
4346
+
4347
+
4348
+ from sage.misc.persist import register_unpickle_override
4349
+ register_unpickle_override('sage.combinat.dyck_word', 'DyckWord', DyckWord)