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,4326 @@
1
+ # sage_setup: distribution = sagemath-combinat
2
+ # sage.doctest: needs sage.combinat sage.modules
3
+ r"""
4
+ Parallelogram polyominoes
5
+
6
+ The goal of this module is to give some tools to manipulate the
7
+ parallelogram polyominoes.
8
+ """
9
+ # *****************************************************************************
10
+ # Copyright (C) 2014,2015 Adrien Boussicault (boussica@labri.fr),
11
+ # Copyright (C) 2016 Patxi Laborde-Zubieta (plaborde@labri.fr),
12
+ # Copyright (C) 2019 Henri Derycke (henri.derycke@ens-lyon.org),
13
+ #
14
+ # Distributed under the terms of the GNU General Public License (GPL)
15
+ # as published by the Free Software Foundation; either version 2 of
16
+ # the License, or (at your option) any later version.
17
+ # https://www.gnu.org/licenses/
18
+ # *****************************************************************************
19
+ from __future__ import annotations
20
+
21
+ from sage.structure.list_clone import ClonableList
22
+ from sage.structure.unique_representation import UniqueRepresentation
23
+ from sage.structure.set_factories import (SetFactory, ParentWithSetFactory,
24
+ TopMostParentPolicy)
25
+ from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass
26
+ from sage.sets.set import Set
27
+ from sage.misc.lazy_attribute import lazy_class_attribute
28
+ from sage.misc.lazy_attribute import lazy_attribute
29
+ from sage.sets.disjoint_union_enumerated_sets import (
30
+ DisjointUnionEnumeratedSets
31
+ )
32
+ from sage.rings.integer import Integer
33
+ from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
34
+ from sage.sets.family import Family
35
+ from sage.sets.positive_integers import PositiveIntegers
36
+ from sage.misc.cachefunc import cached_method
37
+ from sage.misc.latex import latex
38
+ from copy import deepcopy
39
+ from sage.matrix.constructor import matrix
40
+ from sage.combinat.combinat import catalan_number
41
+ from sage.combinat.combinatorial_map import combinatorial_map
42
+ from sage.functions.trig import cos, sin
43
+ from sage.misc.functional import sqrt
44
+
45
+ from sage.misc.lazy_import import lazy_import
46
+ lazy_import("sage.plot.graphics", "Graphics")
47
+ lazy_import("sage.plot.line", "line")
48
+ lazy_import("sage.plot.text", "text")
49
+ lazy_import("sage.plot.point", "point")
50
+
51
+ import pprint
52
+
53
+
54
+ class LocalOptions:
55
+ r"""
56
+ This class allow to add local options to an object.
57
+ LocalOptions is like a dictionary, it has keys and values that represent
58
+ options and the values associated to the option. This is useful to
59
+ decorate an object with some optional informations.
60
+
61
+ :class:`LocalOptions` should be used as follow.
62
+
63
+ INPUT:
64
+
65
+ - ``name`` -- the name of the LocalOptions
66
+
67
+ - ``<options>=dict(...)`` -- dictionary specifying an option
68
+
69
+ The options are specified by keyword arguments with their values
70
+ being a dictionary which describes the option. The
71
+ allowed/expected keys in the dictionary are:
72
+
73
+ - ``checker`` -- a function for checking whether a particular value for
74
+ the option is valid
75
+ - ``default`` -- the default value of the option
76
+ - ``values`` -- dictionary of the legal values for this option (this
77
+ automatically defines the corresponding ``checker``); this dictionary
78
+ gives the possible options, as keys, together with a brief description
79
+ of them
80
+
81
+ ::
82
+
83
+ sage: from sage.combinat.parallelogram_polyomino import LocalOptions
84
+ sage: o = LocalOptions(
85
+ ....: 'Name Example',
86
+ ....: delim=dict(
87
+ ....: default='b',
88
+ ....: values={'b':'the option b', 'p':'the option p'}
89
+ ....: )
90
+ ....: )
91
+ sage: class Ex:
92
+ ....: options=o
93
+ ....: def _repr_b(self): return "b"
94
+ ....: def _repr_p(self): return "p"
95
+ ....: def __repr__(self): return self.options._dispatch(
96
+ ....: self, '_repr_','delim'
97
+ ....: )
98
+ sage: e = Ex(); e
99
+ b
100
+ sage: e.options(delim='p'); e
101
+ p
102
+
103
+ This class is temporary, in the future, this class should be integrated in
104
+ sage.structure.global_options.py. We should split global_option in two
105
+ classes LocalOptions and GlobalOptions.
106
+ """
107
+
108
+ def __init__(self, name='', **options):
109
+ r"""
110
+ Construct a new LocalOptions.
111
+
112
+ INPUT:
113
+
114
+ - ``name`` -- the name of the LocalOptions
115
+
116
+ - ``<options>=dict(...)`` -- dictionary specifying an option
117
+
118
+ The options are specified by keyword arguments with their values
119
+ being a dictionary which describes the option. The
120
+ allowed/expected keys in the dictionary are:
121
+
122
+ - ``checker`` -- a function for checking whether a particular value for
123
+ the option is valid
124
+ - ``default`` -- the default value of the option
125
+ - ``values`` -- dictionary of the legal values for this option (this
126
+ automatically defines the corresponding ``checker``); this dictionary
127
+ gives the possible options, as keys, together with a brief
128
+ description of them.
129
+
130
+ EXAMPLES::
131
+
132
+ sage: from sage.combinat.parallelogram_polyomino import (
133
+ ....: LocalOptions
134
+ ....: )
135
+ sage: o = LocalOptions(
136
+ ....: "Name Example",
137
+ ....: tikz_options=dict(
138
+ ....: default='toto',
139
+ ....: values=dict(
140
+ ....: toto='name',
141
+ ....: x="3"
142
+ ....: )
143
+ ....: ),
144
+ ....: display=dict(
145
+ ....: default='list',
146
+ ....: values=dict(
147
+ ....: list="list representation",
148
+ ....: diagram="diagram representation"
149
+ ....: )
150
+ ....: )
151
+ ....: )
152
+ """
153
+ self._name = name
154
+ self._available_options = options
155
+ self._options = {}
156
+ for key in self._available_options:
157
+ self._options[key] = self._available_options[key]["default"]
158
+
159
+ def __repr__(self) -> str:
160
+ r"""
161
+ Return a string representation of ``self``.
162
+
163
+ EXAMPLES::
164
+
165
+ sage: from sage.combinat.parallelogram_polyomino import (
166
+ ....: LocalOptions
167
+ ....: )
168
+ sage: o = LocalOptions(
169
+ ....: "Name Example",
170
+ ....: tikz_options=dict(
171
+ ....: default='toto',
172
+ ....: values=dict(
173
+ ....: toto='name',
174
+ ....: x="3"
175
+ ....: )
176
+ ....: ),
177
+ ....: display=dict(
178
+ ....: default='list',
179
+ ....: values=dict(
180
+ ....: list="list representation",
181
+ ....: diagram="diagram representation"
182
+ ....: )
183
+ ....: )
184
+ ....: )
185
+ sage: o
186
+ Current options for Name Example
187
+ - display: 'list'
188
+ - tikz_options: 'toto'
189
+ """
190
+ options = list(self._options)
191
+ if options == []:
192
+ return 'Current options for {}'.format(self._name)
193
+
194
+ options.sort()
195
+ width = 1 + max(len(key) for key in options)
196
+ txt = '\n'.join(' - {:{}} {}'.format(key + ':', width, pprint.pformat(self[key]))
197
+ for key in options)
198
+ return 'Current options for {}\n{}'.format(self._name, txt)
199
+
200
+ def __setitem__(self, key, value):
201
+ r"""
202
+ The ``__setitem__`` method is used to change the current values of the
203
+ options. It also checks that the supplied options are valid and changes
204
+ any alias to its generic value.
205
+
206
+ INPUT:
207
+
208
+ - ``key`` -- an option
209
+
210
+ - ``value`` -- the value
211
+
212
+ EXAMPLES::
213
+
214
+ sage: from sage.combinat.parallelogram_polyomino import (
215
+ ....: LocalOptions
216
+ ....: )
217
+ sage: o = LocalOptions(
218
+ ....: "Name Example",
219
+ ....: tikz_options=dict(
220
+ ....: default='toto',
221
+ ....: values=dict(
222
+ ....: toto='name',
223
+ ....: x="3"
224
+ ....: )
225
+ ....: ),
226
+ ....: display=dict(
227
+ ....: default='list',
228
+ ....: values=dict(
229
+ ....: list="list representation",
230
+ ....: diagram="diagram representation"
231
+ ....: )
232
+ ....: ),
233
+ ....: size=dict(
234
+ ....: default=1,
235
+ ....: checker=lambda x : x in NN
236
+ ....: )
237
+ ....: )
238
+ sage: o("display")
239
+ 'list'
240
+ sage: o["display"]="diagram"
241
+ sage: o("display")
242
+ 'diagram'
243
+ sage: o["display"]="?"
244
+ Current value : diagram
245
+ {'default': 'list', 'values':
246
+ {'diagram': 'diagram representation',
247
+ 'list': 'list representation'}}
248
+ sage: o("size")
249
+ 1
250
+ sage: o["size"]=3
251
+ sage: o("size")
252
+ 3
253
+ sage: o["size"]=-6
254
+ """
255
+ assert (key in self._available_options)
256
+ if value == "?":
257
+ res = "Current value : " + str(self._options[key])
258
+ option_key = self._available_options[key]
259
+ if "values" in option_key:
260
+ res += "\n" + pprint.pformat(self._available_options[key])
261
+ print(res)
262
+ else:
263
+ available_options = self._available_options
264
+ if "values" in available_options:
265
+ assert (value in self._available_options[key]["values"])
266
+ if "checker" in available_options:
267
+ assert (available_options["checker"](value))
268
+ self._options[key] = value
269
+
270
+ def __call__(self, *get_values, **options):
271
+ r"""
272
+ Get or set value of the option ``option``.
273
+
274
+ INPUT:
275
+
276
+ - ``get_values`` -- the options to be printed
277
+
278
+ - ``<options>=dict(...)`` -- dictionary specifying an option see
279
+ :class:`LocalOptions` for more details
280
+
281
+ EXAMPLES::
282
+
283
+ sage: from sage.combinat.parallelogram_polyomino import (
284
+ ....: LocalOptions
285
+ ....: )
286
+ sage: o = LocalOptions(
287
+ ....: "Name Example",
288
+ ....: tikz_options=dict(
289
+ ....: default='toto',
290
+ ....: values=dict(
291
+ ....: toto='name',
292
+ ....: x="3"
293
+ ....: )
294
+ ....: ),
295
+ ....: display=dict(
296
+ ....: default='list',
297
+ ....: values=dict(
298
+ ....: list="list representation",
299
+ ....: diagram="diagram representation"
300
+ ....: )
301
+ ....: )
302
+ ....: )
303
+ sage: o("display")
304
+ 'list'
305
+ sage: o(display='diagram')
306
+ sage: o("display")
307
+ 'diagram'
308
+ sage: o(display='?')
309
+ Current value : diagram
310
+ {'default': 'list', 'values':
311
+ {'diagram': 'diagram representation',
312
+ 'list': 'list representation'}}
313
+ """
314
+ for key, value in options.items():
315
+ self.__setitem__(key, value)
316
+ for key in get_values:
317
+ return self.__getitem__(key)
318
+
319
+ def __getitem__(self, key):
320
+ r"""
321
+ Return the current value of the option ``key``.
322
+
323
+ INPUT:
324
+
325
+ - ``key`` -- an option
326
+
327
+ EXAMPLES::
328
+
329
+ sage: from sage.combinat.parallelogram_polyomino import (
330
+ ....: LocalOptions
331
+ ....: )
332
+ sage: o = LocalOptions(
333
+ ....: "Name Example",
334
+ ....: tikz_options=dict(
335
+ ....: default='toto',
336
+ ....: values=dict(
337
+ ....: toto='name',
338
+ ....: x="3"
339
+ ....: )
340
+ ....: ),
341
+ ....: display=dict(
342
+ ....: default='list',
343
+ ....: values=dict(
344
+ ....: list="list representation",
345
+ ....: diagram="diagram representation"
346
+ ....: )
347
+ ....: )
348
+ ....: )
349
+ sage: o["display"]
350
+ 'list'
351
+ """
352
+ return self._options[key]
353
+
354
+ def __iter__(self):
355
+ r"""
356
+ A generator for the options in ``self``.
357
+
358
+ EXAMPLES::
359
+
360
+ sage: from sage.combinat.parallelogram_polyomino import (
361
+ ....: LocalOptions
362
+ ....: )
363
+ sage: o = LocalOptions(
364
+ ....: "Name Example",
365
+ ....: tikz_options=dict(
366
+ ....: default='toto',
367
+ ....: values=dict(
368
+ ....: toto='name',
369
+ ....: x="3"
370
+ ....: )
371
+ ....: ),
372
+ ....: display=dict(
373
+ ....: default='list',
374
+ ....: values=dict(
375
+ ....: list="list representation",
376
+ ....: diagram="diagram representation"
377
+ ....: )
378
+ ....: )
379
+ ....: )
380
+ sage: all(key in ['tikz_options','display'] for key in o)
381
+ True
382
+ """
383
+ return self._available_options.__iter__()
384
+
385
+ def keys(self) -> list[str]:
386
+ r"""
387
+ Return the list of the options in ``self``.
388
+
389
+ EXAMPLES::
390
+
391
+ sage: from sage.combinat.parallelogram_polyomino import (
392
+ ....: LocalOptions
393
+ ....: )
394
+ sage: o = LocalOptions(
395
+ ....: "Name Example",
396
+ ....: tikz_options=dict(
397
+ ....: default='toto',
398
+ ....: values=dict(
399
+ ....: toto='name',
400
+ ....: x="3"
401
+ ....: )
402
+ ....: ),
403
+ ....: display=dict(
404
+ ....: default='list',
405
+ ....: values=dict(
406
+ ....: list="list representation",
407
+ ....: diagram="diagram representation"
408
+ ....: )
409
+ ....: )
410
+ ....: )
411
+ sage: keys=o.keys()
412
+ sage: keys.sort()
413
+ sage: keys
414
+ ['display', 'tikz_options']
415
+ """
416
+ return list(self)
417
+
418
+ def _dispatch(self, obj, dispatch_to, option, *get_values, **set_values):
419
+ r"""
420
+ The *dispatchable* options are options which dispatch related methods
421
+ of the corresponding class. The format for specifying a dispatchable
422
+ option is to include ``dispatch_to = <option name>`` in the
423
+ specifications for the options and then to add the options to the
424
+ class.
425
+
426
+ The ``_dispatch`` method will then call the method named
427
+ ``<option name> + '_' + <current value of option>`` of ``obj``
428
+ with arguments ``*get_values, **set_values``.
429
+
430
+ Note that the argument ``self`` is necessary here because the
431
+ dispatcher is a method of the options class and not of ``self``.
432
+
433
+ EXAMPLES::
434
+
435
+ sage: from sage.combinat.parallelogram_polyomino import LocalOptions
436
+ sage: delim = {'default': 'b',
437
+ ....: 'values': {'b': 'option b', 'p': 'option p'}}
438
+ sage: o = LocalOptions('Name example', delim=delim)
439
+ sage: class Ex:
440
+ ....: options=o
441
+ ....: def _repr_b(self): return "b"
442
+ ....: def _repr_p(self): return "p"
443
+ ....: def __repr__(self): return self.options._dispatch(
444
+ ....: self, '_repr_','delim')
445
+ sage: e = Ex(); e
446
+ b
447
+ sage: e.options(delim='p'); e
448
+ p
449
+ """
450
+ assert (option in self._available_options)
451
+ if dispatch_to[-1] == "_":
452
+ dispatch_to = dispatch_to[:-1]
453
+ f = getattr(obj, dispatch_to + "_" + str(self._options[option]))
454
+ return f(*get_values, **set_values)
455
+
456
+
457
+ default_tikz_options = dict(
458
+ scale=1, line_size=1, point_size=3.5, color_line='black',
459
+ color_point='black', color_bounce_0='red', color_bounce_1='blue',
460
+ translation=[0, 0], rotation=0, mirror=None
461
+ )
462
+ r"""
463
+ This is the default TIKZ options.
464
+
465
+ This option is used to configurate element of a drawing to allow
466
+ TIKZ code generation.
467
+ """
468
+
469
+ ParallelogramPolyominoesOptions = LocalOptions(
470
+ 'ParallelogramPolyominoes_size',
471
+ # module='sage.combinat.parallelogram_polyomino',
472
+ # doc=r"""
473
+ # """,
474
+ # end_doc=r"""
475
+ # """,
476
+ tikz_options=dict(
477
+ default=default_tikz_options,
478
+ description='the tikz options',
479
+ checker=lambda x: Set(x.keys()).issubset(
480
+ Set(
481
+ [
482
+ 'scale', 'line_size', 'point_size',
483
+ 'color_line', 'color_point', 'translation', 'mirror',
484
+ 'rotation', 'color_bounce_0', 'color_bounce_1',
485
+ ]
486
+ )
487
+ )
488
+ ),
489
+ drawing_components=dict(
490
+ default=dict(diagram=True, tree=False, bounce_0=False, bounce_1=False, bounce_values=False),
491
+ description='Different tree-like tableaux components to draw',
492
+ checker=lambda x: Set(x.keys()).issubset(
493
+ Set(['diagram', 'tree', 'bounce_0', 'bounce_1', 'bounce_values', ])
494
+ )
495
+ ),
496
+ display=dict(
497
+ default='list',
498
+ values=dict(
499
+ list='displayed as list',
500
+ drawing='as a drawing',
501
+ )
502
+ ),
503
+ latex=dict(
504
+ default='drawing',
505
+ values=dict(
506
+ list='displayed as list',
507
+ drawing='as a drawing',
508
+ )
509
+ )
510
+ )
511
+ r"""
512
+ This global option contains all the data needed by the Parallelogram classes
513
+ to draw, display in ASCII, compile in latex a parallelogram polyomino.
514
+
515
+ The available options are:
516
+
517
+ - tikz_options : this option configurate all the information useful to
518
+ generate TIKZ code. For example, color, line size, etc ...
519
+
520
+ - drawing_components : this option is used to explain to the system
521
+ which component of the drawing you want to draw. For example,
522
+ you can ask to draw some elements of the following list:
523
+ - the diagram,
524
+ - the tree inside the parallelogram polyomino,
525
+ - the bounce paths inside the parallelogram polyomino,
526
+ - the value of the bounce on each square of a bounce path.
527
+
528
+ - display : this option is used to configurate the ASCII display.
529
+ The available options are:
530
+ - list : (this is the default value) is used to represent PP as a list
531
+ containing the upper and lower path.
532
+ - drawing : this value is used to explain we want to display an array with
533
+ the PP drawn inside (with connected 1).
534
+
535
+ - latex : Same as display. The default is "drawing".
536
+
537
+ See :meth:`ParallelogramPolyomino.get_options` for more details and for an
538
+ user use of options.
539
+
540
+ EXAMPLES::
541
+
542
+ sage: from sage.combinat.parallelogram_polyomino import (
543
+ ....: ParallelogramPolyominoesOptions
544
+ ....: )
545
+ sage: opt = ParallelogramPolyominoesOptions['tikz_options']
546
+ sage: opt
547
+ {'color_bounce_0': 'red',
548
+ 'color_bounce_1': 'blue',
549
+ 'color_line': 'black',
550
+ 'color_point': 'black',
551
+ 'line_size': 1,
552
+ 'mirror': None,
553
+ 'point_size': 3.5,
554
+ 'rotation': 0,
555
+ 'scale': 1,
556
+ 'translation': [0, 0]}
557
+ """
558
+
559
+
560
+ class _drawing_tool:
561
+ r"""
562
+ Technical class to produce TIKZ drawing.
563
+
564
+ This class contains some 2D geometric tools to produce some TIKZ
565
+ drawings.
566
+
567
+ With that classes you can use options to set up drawing informations.
568
+ Then the class will produce a drawing by using those informations.
569
+
570
+ EXAMPLES::
571
+
572
+ sage: # needs sage.plot sage.symbolic
573
+ sage: from sage.combinat.parallelogram_polyomino import (
574
+ ....: _drawing_tool, default_tikz_options,
575
+ ....: ParallelogramPolyominoesOptions
576
+ ....: )
577
+ sage: opt = ParallelogramPolyominoesOptions['tikz_options']
578
+ sage: dt = _drawing_tool(opt)
579
+ sage: dt.draw_line([1, 1], [-1, -1])
580
+ '\n \\draw[color=black, line width=1] (1.000000, 1.000000) --
581
+ (-1.000000, -1.000000);'
582
+ sage: fct = lambda vec: [2*vec[0], vec[1]]
583
+ sage: dt = _drawing_tool(opt, fct)
584
+ sage: dt.draw_line([1, 1], [-1, -1])
585
+ '\n \\draw[color=black, line width=1] (2.000000, 1.000000) --
586
+ (-2.000000, -1.000000);'
587
+ sage: import copy
588
+ sage: opt = copy.deepcopy(opt)
589
+ sage: opt['mirror'] = [0,1]
590
+ sage: dt = _drawing_tool(opt)
591
+ sage: dt.draw_line([1, 1], [-1, -1])
592
+ '\n \\draw[color=black, line width=1] (-1.000000, 1.000000) --
593
+ (1.000000, -1.000000);'
594
+ """
595
+
596
+ def __init__(self, options, XY=lambda v: v):
597
+ r"""
598
+ Construct a drawing tools to produce some TIKZ drawing.
599
+
600
+ INPUT:
601
+
602
+ - ``options`` -- drawing options
603
+
604
+ - ``XY`` -- a user function to convert vector in other vector
605
+ (default: identity function)
606
+
607
+ EXAMPLES::
608
+
609
+ sage: # needs sage.plot sage.symbolic
610
+ sage: from sage.combinat.parallelogram_polyomino import (
611
+ ....: _drawing_tool, default_tikz_options,
612
+ ....: ParallelogramPolyominoesOptions
613
+ ....: )
614
+ sage: opt = ParallelogramPolyominoesOptions['tikz_options']
615
+ sage: dt = _drawing_tool(opt)
616
+ sage: dt.draw_line([1, 1], [-1, -1])
617
+ '\n \\draw[color=black, line width=1] (1.000000, 1.000000) --
618
+ (-1.000000, -1.000000);'
619
+ """
620
+ self._XY = lambda v: XY([float(v[0]), float(v[1])])
621
+ self._translation = options['translation']
622
+ self._mirror = options['mirror']
623
+ self._rotation = options['rotation']
624
+ self._color_line = options['color_line']
625
+ self._line_size = options['line_size']
626
+ self._point_size = options['point_size']
627
+ self._color_point = options['color_point']
628
+
629
+ def XY(self, v):
630
+ r"""
631
+ This function give the image of v by some transformation given by the
632
+ drawing option of ``_drawing_tool``.
633
+
634
+ The transformation is the composition of rotation, mirror, translation
635
+ and XY user function.
636
+
637
+ First we apply XY function, then the translation, then the mirror and
638
+ finally the rotation.
639
+
640
+ INPUT:
641
+
642
+ - ``v`` -- the vector to transform
643
+
644
+ OUTPUT: list of 2 floats encoding a vector
645
+
646
+ EXAMPLES::
647
+
648
+ sage: # needs sage.plot sage.symbolic
649
+ sage: from sage.combinat.parallelogram_polyomino import (
650
+ ....: _drawing_tool, ParallelogramPolyominoesOptions
651
+ ....: )
652
+ sage: opt = ParallelogramPolyominoesOptions['tikz_options']
653
+ sage: dt = _drawing_tool(opt)
654
+ sage: dt.XY([1, 1])
655
+ [1.0, 1.0]
656
+ sage: fct = lambda vec: [2*vec[0], vec[1]]
657
+ sage: dt = _drawing_tool(opt, fct)
658
+ sage: dt.XY([1, 1])
659
+ [2.0, 1.0]
660
+ sage: import copy
661
+ sage: opt = copy.deepcopy(opt)
662
+ sage: opt['mirror'] = [0, 1]
663
+ sage: dt = _drawing_tool(opt)
664
+ sage: dt.XY([1, 1])
665
+ [-1.0, 1.0]
666
+ """
667
+ def translate(pos, v):
668
+ r"""
669
+ Translate a position with a vector.
670
+
671
+ INPUT:
672
+
673
+ - ``pos`` -- the position to translate
674
+
675
+ - ``v`` -- the translation vector
676
+
677
+ OUTPUT:
678
+
679
+ The translated position.
680
+ """
681
+ return [pos[0] + v[0], pos[1] + v[1]]
682
+
683
+ def rotate(pos, angle):
684
+ r"""
685
+ Rotate by `angle` a position around the origin.
686
+
687
+ INPUT:
688
+
689
+ - ``pos`` -- the position to rotate
690
+
691
+ - ``angle`` -- the angle of rotation
692
+
693
+ OUTPUT:
694
+
695
+ The rotated position.
696
+ """
697
+ x, y = pos
698
+ return [x*cos(angle) - y*sin(angle), x*sin(angle) + y*cos(angle)]
699
+
700
+ def mirror(pos, axe):
701
+ r"""
702
+ Return the mirror of a position according to a given axe.
703
+
704
+ INPUT:
705
+
706
+ - ``pos`` -- the position to mirror
707
+
708
+ - ``axe`` -- the axe vector
709
+
710
+ OUTPUT:
711
+
712
+ The mirrored position.
713
+ """
714
+ if axe is None:
715
+ return pos
716
+ if not isinstance(axe, (list, tuple)):
717
+ raise ValueError(
718
+ "mirror option should be None or a list of two real" +
719
+ " encoding a 2D vector."
720
+ )
721
+ n = float(sqrt(axe[0]**2 + axe[1]**2))
722
+ axe[0] = float(axe[0]/n)
723
+ axe[1] = float(axe[1]/n)
724
+ sp = (pos[0]*axe[0] + pos[1]*axe[1])
725
+ sn = (- pos[0]*axe[1] + pos[1]*axe[0])
726
+ return [
727
+ sp*axe[0] + sn*axe[1],
728
+ sp*axe[1] - sn*axe[0]
729
+ ]
730
+ return rotate(
731
+ mirror(
732
+ translate(self._XY(v), self._translation),
733
+ self._mirror
734
+ ), self._rotation
735
+ )
736
+
737
+ def draw_line(self, v1, v2, color=None, size=None):
738
+ r"""
739
+ Return the TIKZ code for a line.
740
+
741
+ INPUT:
742
+
743
+ - ``v1`` -- point, The first point of the line
744
+
745
+ - ``v2`` -- point, The second point of the line
746
+
747
+ - ``color`` -- string (default: ``None``); the color of the line.
748
+ If set to ``None``, the color is chosen according the
749
+ drawing option given by ``_drawing_tool``.
750
+
751
+ - ``size`` -- integer (default: ``None``); the size of the line.
752
+ If set to ``None``, the size is chosen according the
753
+ drawing option given by ``_drawing_tool``.
754
+
755
+ OUTPUT: the code of a line in TIKZ
756
+
757
+ EXAMPLES::
758
+
759
+ sage: # needs sage.plot sage.symbolic
760
+ sage: from sage.combinat.parallelogram_polyomino import (
761
+ ....: _drawing_tool, ParallelogramPolyominoesOptions
762
+ ....: )
763
+ sage: opt = ParallelogramPolyominoesOptions['tikz_options']
764
+ sage: dt = _drawing_tool(opt)
765
+ sage: dt.draw_line([1, 1], [-1, -1])
766
+ '\n \\draw[color=black, line width=1] (1.000000, 1.000000) --
767
+ (-1.000000, -1.000000);'
768
+ """
769
+ if color is None:
770
+ color = self._color_line
771
+ if size is None:
772
+ size = self._line_size
773
+ x1, y1 = self.XY(v1)
774
+ x2, y2 = self.XY(v2)
775
+ return "\n \\draw[color=%s, line width=%s] (%f, %f) -- (%f, %f);" % (
776
+ color, size, float(x1), float(y1), float(x2), float(y2)
777
+ )
778
+
779
+ def draw_polyline(self, list_of_vertices, color=None, size=None):
780
+ r"""
781
+ Return the TIKZ code for a polyline.
782
+
783
+ INPUT:
784
+
785
+ - ``list_of_vertices`` -- list of points
786
+
787
+ - ``color`` -- string (default: ``None``); the color of the line.
788
+ If set to ``None``, the color is chosen according the
789
+ drawing option given by ``_drawing_tool``.
790
+
791
+ - ``size`` -- integer (default: ``None``); the size of the line.
792
+ If set to ``None``, the size is chosen according the
793
+ drawing option given by ``_drawing_tool``.
794
+
795
+ OUTPUT: the code of a polyline in TIKZ
796
+
797
+ EXAMPLES::
798
+
799
+ sage: # needs sage.plot sage.symbolic
800
+ sage: from sage.combinat.parallelogram_polyomino import (
801
+ ....: _drawing_tool, ParallelogramPolyominoesOptions
802
+ ....: )
803
+ sage: opt = ParallelogramPolyominoesOptions['tikz_options']
804
+ sage: dt = _drawing_tool(opt)
805
+ sage: dt.draw_polyline([[1, 1], [-1, -1], [0,0]])
806
+ '\n \\draw[color=black, line width=1] (1.000000, 1.000000) --
807
+ (-1.000000, -1.000000);\n \\draw[color=black, line width=1]
808
+ (-1.000000, -1.000000) -- (0.000000, 0.000000);'
809
+ """
810
+ res = ""
811
+ for i in range(len(list_of_vertices)-1):
812
+ res += self.draw_line(
813
+ list_of_vertices[i], list_of_vertices[i+1], color, size)
814
+ return res
815
+
816
+ def draw_point(self, p1, color=None, size=None):
817
+ r"""
818
+ Return the TIKZ code for a point.
819
+
820
+ INPUT:
821
+
822
+ - ``p1`` -- a point
823
+
824
+ - ``color`` -- string (default: ``None``); the color of the line.
825
+ If set to ``None``, the color is chosen according the
826
+ drawing option given by ``_drawing_tool``.
827
+
828
+ - ``size`` -- integer (default: ``None``); the size of the line.
829
+ If set to ``None``, the size is chosen according the
830
+ drawing option given by ``_drawing_tool``.
831
+
832
+ OUTPUT: the code of a point in TIKZ
833
+
834
+ EXAMPLES::
835
+
836
+ sage: # needs sage.plot sage.symbolic
837
+ sage: from sage.combinat.parallelogram_polyomino import (
838
+ ....: _drawing_tool, ParallelogramPolyominoesOptions
839
+ ....: )
840
+ sage: opt = ParallelogramPolyominoesOptions['tikz_options']
841
+ sage: dt = _drawing_tool(opt)
842
+ sage: dt.draw_point([1, 1])
843
+ '\n \\filldraw[color=black] (1.000000, 1.000000) circle (3.5pt);'
844
+ """
845
+ if color is None:
846
+ color = self._color_point
847
+ if size is None:
848
+ size = self._point_size
849
+ x1, y1 = self.XY(p1)
850
+ return "\n \\filldraw[color=%s] (%f, %f) circle (%spt);" % (
851
+ color, float(x1), float(y1), size
852
+ )
853
+
854
+
855
+ class ParallelogramPolyomino(ClonableList,
856
+ metaclass=InheritComparisonClasscallMetaclass):
857
+ r"""
858
+ Parallelogram Polyominoes.
859
+
860
+ A parallelogram polyomino is a finite connected union of cells
861
+ whose boundary can be decomposed in two paths, the upper and the lower
862
+ paths, which are comprised of north and east unit steps and meet only at
863
+ their starting and final points.
864
+
865
+ Parallelogram Polyominoes can be defined with those two paths.
866
+
867
+ EXAMPLES::
868
+
869
+ sage: pp = ParallelogramPolyomino([[0, 1], [1, 0]])
870
+ sage: pp
871
+ [[0, 1], [1, 0]]
872
+ """
873
+ @staticmethod
874
+ def __classcall_private__(cls, *args, **opts):
875
+ r"""
876
+ Ensure that parallelogram polyominoes created by the enumerated sets
877
+ are instances of :class:`ParallelogramPolyomino` and have the same
878
+ parent.
879
+
880
+ TESTS::
881
+
882
+ sage: issubclass(
883
+ ....: ParallelogramPolyominoes().element_class,
884
+ ....: ParallelogramPolyomino
885
+ ....: )
886
+ True
887
+ sage: pp = ParallelogramPolyomino([[0, 1], [1, 0]])
888
+ sage: pp.parent()
889
+ Parallelogram polyominoes
890
+ sage: str(type(pp)) == (
891
+ ....: "<class 'sage.combinat.parallelogram_polyomino" +
892
+ ....: ".ParallelogramPolyominoes_all_with_category" +
893
+ ....: ".element_class'>"
894
+ ....: )
895
+ True
896
+
897
+ sage: pp1 = ParallelogramPolyominoes()([[0, 1], [1, 0]])
898
+ sage: pp1.parent() is pp.parent()
899
+ True
900
+ sage: type(pp1) is type(pp)
901
+ True
902
+
903
+ sage: pp1 = ParallelogramPolyominoes(2)([[0, 1], [1, 0]])
904
+ sage: pp1.parent() is pp.parent()
905
+ True
906
+ sage: type(pp1) is type(pp)
907
+ True
908
+ """
909
+ return cls._auto_parent._element_constructor_(*args, **opts)
910
+
911
+ @lazy_class_attribute
912
+ def _auto_parent(cls):
913
+ r"""
914
+ The automatic parent of the elements of this class.
915
+
916
+ When calling the constructor of an element of this class, one needs a
917
+ parent. This class attribute specifies which parent is used.
918
+
919
+ EXAMPLES::
920
+
921
+ sage: ParallelogramPolyomino._auto_parent
922
+ Parallelogram polyominoes
923
+ sage: ParallelogramPolyominoes()([[0, 1], [1, 0]]).parent()
924
+ Parallelogram polyominoes
925
+ """
926
+ return ParallelogramPolyominoes()
927
+
928
+ def _ascii_art_(self):
929
+ """
930
+ TESTS::
931
+
932
+ sage: ascii_art(ParallelogramPolyomino([[0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], [1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0]]))
933
+ ***
934
+ ****
935
+ ***
936
+ ***
937
+ **
938
+ *
939
+ sage: ascii_art(ParallelogramPolyomino([[0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1], [1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0]]))
940
+ **
941
+ ***
942
+ ***
943
+ ***
944
+ **
945
+ **
946
+ """
947
+ from sage.typeset.ascii_art import AsciiArt
948
+
949
+ data = zip(self.lower_widths(), self.upper_widths())
950
+ txt = []
951
+ for x,y in data:
952
+ txt += [' ' * x + '*' * (y - x)]
953
+
954
+ return AsciiArt(txt)
955
+
956
+ def _unicode_art_(self):
957
+ """
958
+ TESTS::
959
+
960
+ sage: unicode_art(ParallelogramPolyomino([[0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], [1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0]]))
961
+ ┌┬┬┐
962
+ ├┼┼┼┐
963
+ └┼┼┼┤
964
+ └┼┼┼┐
965
+ └┼┼┤
966
+ └┼┤
967
+ └┘
968
+ sage: unicode_art(ParallelogramPolyomino([[0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1], [1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0]]))
969
+ ┌┬┐
970
+ ├┼┼┐
971
+ ├┼┼┤
972
+ ├┼┼┤
973
+ └┴┼┼┐
974
+ └┼┼┐
975
+ └┴┘
976
+ """
977
+ from sage.typeset.unicode_art import UnicodeArt
978
+
979
+ data = list(zip(self.lower_widths(), self.upper_widths()))
980
+
981
+ txt = ['┌' + '┬' * (data[0][1] - 1) + '┐']
982
+ for i in range(1, len(data)):
983
+ x1, y1 = data[i-1]
984
+ x2, y2 = data[i]
985
+ line = [' ' * x1]
986
+ if x1 == x2:
987
+ line += ['├']
988
+ else:
989
+ line += ['└' + '┴' * (x2 - x1 - 1) + '┼']
990
+ line += ['┼' * (y1 - x2 - 1)]
991
+ if y1 == y2:
992
+ line += ['┤']
993
+ else:
994
+ line += ['┼' + '┬' * (y2 - y1 - 1) + '┐']
995
+ txt += [''.join(line)]
996
+ txt += [' ' * data[-1][0] + '└' + '┴' * (data[-1][1] - data[-1][0] - 1) + '┘']
997
+
998
+ return UnicodeArt(txt, baseline=0)
999
+
1000
+ def check(self):
1001
+ r"""
1002
+ This method raises an error if the internal data of the class does not
1003
+ represent a parallelogram polyomino.
1004
+
1005
+ EXAMPLES::
1006
+
1007
+ sage: pp = ParallelogramPolyomino(
1008
+ ....: [
1009
+ ....: [0, 0, 0, 1, 0, 1, 0, 1, 1],
1010
+ ....: [1, 0, 1, 1, 0, 0, 1, 0, 0]
1011
+ ....: ]
1012
+ ....: )
1013
+ sage: pp = ParallelogramPolyomino([[0, 1], [1, 0]])
1014
+ sage: pp = ParallelogramPolyomino([[1], [1]])
1015
+
1016
+ sage: pp = ParallelogramPolyomino( # indirect doctest
1017
+ ....: [[1, 0], [0, 1]]
1018
+ ....: )
1019
+ Traceback (most recent call last):
1020
+ ...
1021
+ ValueError: the lower and upper paths are crossing
1022
+
1023
+ sage: pp = ParallelogramPolyomino([[1], [0, 1]]) # indirect doctest
1024
+ Traceback (most recent call last):
1025
+ ...
1026
+ ValueError: the lower and upper paths have different sizes (2 != 1)
1027
+
1028
+ sage: pp = ParallelogramPolyomino([[1], [0]]) # indirect doctest
1029
+ Traceback (most recent call last):
1030
+ ...
1031
+ ValueError: the two paths have distinct ends
1032
+
1033
+ sage: pp = ParallelogramPolyomino([[0], [1]]) # indirect doctest
1034
+ Traceback (most recent call last):
1035
+ ...
1036
+ ValueError: the two paths have distinct ends
1037
+
1038
+ sage: pp = ParallelogramPolyomino([[0], [0]]) # indirect doctest
1039
+ Traceback (most recent call last):
1040
+ ...
1041
+ ValueError: the lower or the upper path can...t be equal to [0]
1042
+
1043
+ sage: pp = ParallelogramPolyomino([[], [0]]) # indirect doctest
1044
+ Traceback (most recent call last):
1045
+ ...
1046
+ ValueError: the lower or the upper path can...t be equal to []
1047
+
1048
+ sage: pp = ParallelogramPolyomino([[0], []]) # indirect doctest
1049
+ Traceback (most recent call last):
1050
+ ...
1051
+ ValueError: the lower or the upper path can...t be equal to []
1052
+
1053
+ sage: pp = ParallelogramPolyomino([[], []]) # indirect doctest
1054
+ Traceback (most recent call last):
1055
+ ...
1056
+ ValueError: the lower or the upper path can...t be equal to []
1057
+ """
1058
+ lower_path = self.lower_path()
1059
+ upper_path = self.upper_path()
1060
+ if lower_path == [0] and upper_path == [0]:
1061
+ raise ValueError(
1062
+ "the lower or the upper path can't be equal to [0]"
1063
+ )
1064
+ if lower_path == [] or upper_path == []:
1065
+ raise ValueError(
1066
+ "the lower or the upper path can't be equal to []"
1067
+ )
1068
+ if len(upper_path) != len(lower_path):
1069
+ raise ValueError(
1070
+ "the lower and upper paths have different sizes (%s != %s)" % (
1071
+ len(upper_path), len(lower_path)
1072
+ )
1073
+ )
1074
+ p_up = [0, 0]
1075
+ p_down = [0, 0]
1076
+ for i in range(len(upper_path)-1):
1077
+ p_up[1-upper_path[i]] += 1
1078
+ p_down[1-lower_path[i]] += 1
1079
+ if (p_up[0] <= p_down[0] or p_down[1] <= p_up[1]):
1080
+ raise ValueError("the lower and upper paths are crossing")
1081
+ p_up[1 - upper_path[-1]] += 1
1082
+ p_down[1 - lower_path[-1]] += 1
1083
+ if (p_up[0] != p_down[0] or p_up[1] != p_down[1]):
1084
+ raise ValueError("the two paths have distinct ends")
1085
+
1086
+ def __hash__(self):
1087
+ r"""
1088
+ Return the hash code of the parallelogram polyomino.
1089
+
1090
+ EXAMPLES::
1091
+
1092
+ sage: pp = ParallelogramPolyomino(
1093
+ ....: [
1094
+ ....: [0, 0, 0, 1, 0, 1, 0, 1, 1],
1095
+ ....: [1, 0, 1, 1, 0, 0, 1, 0, 0]
1096
+ ....: ]
1097
+ ....: )
1098
+ sage: hash(pp) == hash((
1099
+ ....: (0, 0, 0, 1, 0, 1, 0, 1, 1), (1, 0, 1, 1, 0, 0, 1, 0, 0)
1100
+ ....: ))
1101
+ True
1102
+
1103
+ sage: PPS = ParallelogramPolyominoes(8)
1104
+ sage: D = { PPS[0]: True, PPS[1]: True }
1105
+ sage: D[PPS[0]] = False
1106
+ sage: import pprint
1107
+ sage: pp = pprint.PrettyPrinter()
1108
+ sage: pp.pprint(D)
1109
+ {[[0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0]]: False,
1110
+ [[0, 0, 0, 0, 0, 0, 1, 1], [1, 0, 0, 0, 0, 0, 1, 0]]: True}
1111
+ """
1112
+ return hash(tuple(map(tuple, list(self))))
1113
+
1114
+ def __copy__(self):
1115
+ r"""
1116
+ Copy a parallelogram Polyomino.
1117
+
1118
+ EXAMPLES::
1119
+
1120
+ sage: pp = ParallelogramPolyomino(
1121
+ ....: [
1122
+ ....: [0, 0, 0, 1, 0, 1, 0, 1, 1],
1123
+ ....: [1, 0, 1, 1, 0, 0, 1, 0, 0]
1124
+ ....: ]
1125
+ ....: )
1126
+ sage: pp1 = copy(pp)
1127
+ sage: pp1 is pp
1128
+ False
1129
+ sage: pp1 == pp
1130
+ True
1131
+ sage: pp1
1132
+ [[0, 0, 0, 1, 0, 1, 0, 1, 1], [1, 0, 1, 1, 0, 0, 1, 0, 0]]
1133
+ """
1134
+ return ParallelogramPolyomino([self.lower_path(), self.upper_path()])
1135
+
1136
+ def __init__(self, parent, value, check=True):
1137
+ r"""
1138
+ Construct a parallelogram polyomino.
1139
+
1140
+ The input is a pair of lower path and upper path.
1141
+
1142
+ The lower and upper paths of the empty parallelogram polyomino are
1143
+ [1] and [1].
1144
+
1145
+ EXAMPLES::
1146
+
1147
+ sage: lower_path = [0, 0, 1, 0, 1, 1]
1148
+ sage: upper_path = [1, 1, 0, 1, 0, 0]
1149
+ sage: pp = ParallelogramPolyomino([lower_path, upper_path])
1150
+ sage: pp
1151
+ [[0, 0, 1, 0, 1, 1], [1, 1, 0, 1, 0, 0]]
1152
+
1153
+ sage: pp = ParallelogramPolyomino([[0, 1], [1, 0]])
1154
+ sage: pp
1155
+ [[0, 1], [1, 0]]
1156
+
1157
+ sage: pp = ParallelogramPolyomino([[1], [1]])
1158
+ sage: pp
1159
+ [[1], [1]]
1160
+ """
1161
+ ClonableList.__init__(self, parent, value)
1162
+ if check:
1163
+ if not isinstance(value, (list, tuple)):
1164
+ raise ValueError(
1165
+ "value %s must be a list or a tuple" % value)
1166
+ self.check()
1167
+ self._options = None
1168
+
1169
+ def reflect(self) -> ParallelogramPolyomino:
1170
+ r"""
1171
+ Return the parallelogram polyomino obtained by switching rows and
1172
+ columns.
1173
+
1174
+ EXAMPLES::
1175
+
1176
+ sage: pp = ParallelogramPolyomino([[0,0,0,0,1,1,0,1,0,1], [1,0,1,0,0,1,1,0,0,0]])
1177
+ sage: pp.heights(), pp.upper_heights()
1178
+ ([4, 3, 2, 3], [0, 1, 3, 3])
1179
+ sage: pp = pp.reflect()
1180
+ sage: pp.widths(), pp.lower_widths()
1181
+ ([4, 3, 2, 3], [0, 1, 3, 3])
1182
+
1183
+ sage: pp = ParallelogramPolyomino([[0,0,0,1,1], [1,0,0,1,0]])
1184
+ sage: ascii_art(pp)
1185
+ *
1186
+ *
1187
+ **
1188
+ sage: ascii_art(pp.reflect())
1189
+ ***
1190
+ *
1191
+
1192
+ TESTS::
1193
+
1194
+ sage: pp = ParallelogramPolyomino([[1], [1]])
1195
+ sage: pp.reflect()
1196
+ [[1], [1]]
1197
+ """
1198
+ if self.size() == 1:
1199
+ return self
1200
+ a, b = self
1201
+ return ParallelogramPolyomino([[1 - v for v in b],
1202
+ [1 - v for v in a]])
1203
+
1204
+ def rotate(self) -> ParallelogramPolyomino:
1205
+ r"""
1206
+ Return the parallelogram polyomino obtained by rotation of 180 degrees.
1207
+
1208
+ EXAMPLES::
1209
+
1210
+ sage: pp = ParallelogramPolyomino([[0,0,0,1,1], [1,0,0,1,0]])
1211
+ sage: ascii_art(pp)
1212
+ *
1213
+ *
1214
+ **
1215
+ sage: ascii_art(pp.rotate())
1216
+ **
1217
+ *
1218
+ *
1219
+ """
1220
+ a, b = self
1221
+ return ParallelogramPolyomino([b[::-1], a[::-1]])
1222
+
1223
+ def _to_dyck_delest_viennot(self):
1224
+ r"""
1225
+ Convert to a Dyck word using the Delest-Viennot bijection.
1226
+
1227
+ This bijection is described on page 179 and page 180 Figure 6
1228
+ in the article [DeVi1984]_, where it is called the classical
1229
+ bijection `\gamma`.
1230
+
1231
+ EXAMPLES::
1232
+
1233
+ sage: pp = ParallelogramPolyomino([[0, 1, 0, 0, 1, 1], [1, 1, 1, 0, 0, 0]])
1234
+ sage: pp._to_dyck_delest_viennot()
1235
+ [1, 1, 0, 1, 1, 0, 1, 0, 0, 0]
1236
+
1237
+ TESTS::
1238
+
1239
+ sage: pp = ParallelogramPolyomino([[1], [1]])
1240
+ sage: pp._to_dyck_delest_viennot()
1241
+ []
1242
+ """
1243
+ from sage.combinat.dyck_word import DyckWord
1244
+ dyck = []
1245
+ dick_size = self.size() - 1
1246
+ if not dick_size:
1247
+ return DyckWord([])
1248
+ upper_path = self.upper_path()
1249
+ lower_path = self.lower_path()
1250
+ dyck.append(1 - lower_path[0])
1251
+ for i in range(1, dick_size):
1252
+ dyck.append(upper_path[i])
1253
+ dyck.append(1 - lower_path[i])
1254
+ dyck.append(upper_path[dick_size])
1255
+ return DyckWord(dyck)
1256
+
1257
+ def _to_dyck_delest_viennot_peaks_valleys(self):
1258
+ r"""
1259
+ Convert to a Dyck word using the Delest-Viennot bijection `\beta`.
1260
+
1261
+ This bijection is described on page 182 and Figure 8 in the
1262
+ article [DeVi1984]_. It returns the unique Dyck path whose
1263
+ peak heights are the column heights and whose valley heights
1264
+ are the overlaps between adjacent columns.
1265
+
1266
+ EXAMPLES:
1267
+
1268
+ This is the example in Figure 8 of [DeVi1984]_::
1269
+
1270
+ sage: pp = ParallelogramPolyomino([[0,0,0,0,1,1,0,1,0,1], [1,0,1,0,0,1,1,0,0,0]])
1271
+ sage: pp._to_dyck_delest_viennot_peaks_valleys()
1272
+ [1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0]
1273
+
1274
+ TESTS::
1275
+
1276
+ sage: pp = ParallelogramPolyomino([[1], [1]])
1277
+ sage: pp._to_dyck_delest_viennot_peaks_valleys()
1278
+ []
1279
+ """
1280
+ from sage.combinat.dyck_word import DyckWord
1281
+ a = self.heights()
1282
+ u = self.upper_heights()
1283
+ b = [0] + [a[i]-u[i+1]+u[i]-1 for i in range(len(a)-1)] + [0]
1284
+ dyck = []
1285
+ for i in range(len(a)):
1286
+ dyck.extend([1] * (a[i] - b[i]))
1287
+ dyck.extend([0] * (a[i] - b[i + 1]))
1288
+ return DyckWord(dyck)
1289
+
1290
+ @combinatorial_map(name="To Dyck word")
1291
+ def to_dyck_word(self, bijection=None):
1292
+ r"""
1293
+ Convert to a Dyck word.
1294
+
1295
+ INPUT:
1296
+
1297
+ - ``bijection`` -- string or ``None`` (default: ``None``); the name of
1298
+ the bijection. If it is set to ``None`` then the ``'Delest-Viennot'``
1299
+ bijection is used.
1300
+ Expected values are ``None``, ``'Delest-Viennot'``, or ``'Delest-Viennot-beta'``.
1301
+
1302
+ OUTPUT: a Dyck word
1303
+
1304
+ EXAMPLES::
1305
+
1306
+ sage: pp = ParallelogramPolyomino([[0, 1, 0, 0, 1, 1], [1, 1, 1, 0, 0, 0]])
1307
+ sage: pp.to_dyck_word()
1308
+ [1, 1, 0, 1, 1, 0, 1, 0, 0, 0]
1309
+ sage: pp.to_dyck_word(bijection='Delest-Viennot')
1310
+ [1, 1, 0, 1, 1, 0, 1, 0, 0, 0]
1311
+
1312
+ sage: pp.to_dyck_word(bijection='Delest-Viennot-beta')
1313
+ [1, 0, 1, 1, 1, 0, 1, 0, 0, 0]
1314
+ """
1315
+ if bijection is None or bijection == 'Delest-Viennot':
1316
+ return self._to_dyck_delest_viennot()
1317
+ if bijection == 'Delest-Viennot-beta':
1318
+ return self._to_dyck_delest_viennot_peaks_valleys()
1319
+ raise ValueError("the given bijection is not valid")
1320
+
1321
+ @staticmethod
1322
+ def _from_dyck_word_delest_viennot(dyck):
1323
+ r"""
1324
+ Convert a Dyck word to a parallelogram polyomino using the Delest
1325
+ Viennot bijection.
1326
+
1327
+ This bijection is described on page 179 and page 180 Figure 6 in
1328
+ the article [DeVi1984]_, where it is called the classical
1329
+ bijection `\gamma`.
1330
+
1331
+ INPUT:
1332
+
1333
+ - ``dyck`` -- a Dyck word
1334
+
1335
+ OUTPUT: a parallelogram polyomino
1336
+
1337
+ EXAMPLES::
1338
+
1339
+ sage: dyck = DyckWord([1, 1, 0, 1, 1, 0, 1, 0, 0, 0])
1340
+ sage: ParallelogramPolyomino._from_dyck_word_delest_viennot(dyck)
1341
+ [[0, 1, 0, 0, 1, 1], [1, 1, 1, 0, 0, 0]]
1342
+
1343
+ TESTS::
1344
+
1345
+ sage: gamma = ParallelogramPolyomino._to_dyck_delest_viennot
1346
+ sage: gamma_inv = ParallelogramPolyomino._from_dyck_word_delest_viennot
1347
+ sage: all(all(D == gamma(gamma_inv(D)) for D in DyckWords(n)) for n in range(7))
1348
+ True
1349
+ """
1350
+ l = [1] + list(dyck) + [0]
1351
+ word_up = []
1352
+ word_down = []
1353
+ for i in range(0, len(l), 2):
1354
+ word_up.append(l[i])
1355
+ word_down.append(1 - l[i + 1])
1356
+ return ParallelogramPolyomino([word_down, word_up])
1357
+
1358
+ @staticmethod
1359
+ def _from_dyck_word_delest_viennot_peaks_valleys(dyck):
1360
+ r"""
1361
+ Convert a Dyck word to a parallelogram polyomino using the Delest
1362
+ Viennot bijection `\beta`.
1363
+
1364
+ This bijection is described on page 182 and Figure 8 in
1365
+ the article [DeVi1984]_.
1366
+
1367
+ INPUT:
1368
+
1369
+ - ``dyck`` -- a Dyck word
1370
+
1371
+ OUTPUT: a parallelogram polyomino
1372
+
1373
+ EXAMPLES::
1374
+
1375
+ sage: dyck = DyckWord([1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0])
1376
+ sage: ParallelogramPolyomino._from_dyck_word_delest_viennot_peaks_valleys(dyck)
1377
+ [[0, 0, 0, 0, 1, 1, 0, 1, 0, 1], [1, 0, 1, 0, 0, 1, 1, 0, 0, 0]]
1378
+
1379
+ sage: dyck = DyckWord([1,1,0,1,1,1,1,1,0,0,1,0,0,0,0,0,1,1,1,0,0,1,0,0])
1380
+ sage: ParallelogramPolyomino._from_dyck_word_delest_viennot_peaks_valleys(dyck)
1381
+ [[0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1], [1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0]]
1382
+
1383
+ TESTS::
1384
+
1385
+ sage: beta = ParallelogramPolyomino._to_dyck_delest_viennot_peaks_valleys
1386
+ sage: beta_inv = ParallelogramPolyomino._from_dyck_word_delest_viennot_peaks_valleys
1387
+ sage: all(all(D == beta(beta_inv(D)) for D in DyckWords(n)) for n in range(7))
1388
+ True
1389
+ """
1390
+ if not dyck:
1391
+ return ParallelogramPolyomino([[1], [1]])
1392
+ a = []
1393
+ b = [0]
1394
+ h = 0
1395
+ for i in range(len(dyck)-1):
1396
+ if dyck[i] == 1:
1397
+ h += 1
1398
+ if dyck[i+1] == 0:
1399
+ a.append(h)
1400
+ else:
1401
+ if dyck[i+1] == 1:
1402
+ b.append(h)
1403
+ h -= 1
1404
+ b.append(0)
1405
+ word_down = []
1406
+ word_up = []
1407
+ for i in range(len(a)):
1408
+ word_down.extend([0]*(a[i]-b[i]) + [1])
1409
+ word_up.extend([1]+[0]*(a[i]-b[i+1]))
1410
+ return ParallelogramPolyomino([word_down, word_up])
1411
+
1412
+ @staticmethod
1413
+ def from_dyck_word(dyck, bijection=None):
1414
+ r"""
1415
+ Convert a Dyck word to parallelogram polyomino.
1416
+
1417
+ INPUT:
1418
+
1419
+ - ``dyck`` -- a Dyck word
1420
+
1421
+ - ``bijection`` -- string or ``None`` (default: ``None``); the
1422
+ bijection to use. See :meth:`to_dyck_word` for more details.
1423
+
1424
+ OUTPUT: a parallelogram polyomino
1425
+
1426
+ EXAMPLES::
1427
+
1428
+ sage: dyck = DyckWord([1, 1, 0, 1, 1, 0, 1, 0, 0, 0])
1429
+ sage: ParallelogramPolyomino.from_dyck_word(dyck)
1430
+ [[0, 1, 0, 0, 1, 1], [1, 1, 1, 0, 0, 0]]
1431
+ sage: ParallelogramPolyomino.from_dyck_word(dyck, bijection='Delest-Viennot')
1432
+ [[0, 1, 0, 0, 1, 1], [1, 1, 1, 0, 0, 0]]
1433
+ sage: ParallelogramPolyomino.from_dyck_word(dyck, bijection='Delest-Viennot-beta')
1434
+ [[0, 0, 1, 0, 1, 1], [1, 1, 1, 0, 0, 0]]
1435
+ """
1436
+ if bijection is None or bijection == 'Delest-Viennot':
1437
+ return ParallelogramPolyomino._from_dyck_word_delest_viennot(dyck)
1438
+ if bijection == 'Delest-Viennot-beta':
1439
+ return ParallelogramPolyomino._from_dyck_word_delest_viennot_peaks_valleys(dyck)
1440
+ raise ValueError("the given bijection is not valid")
1441
+
1442
+ def _to_binary_tree_Aval_Boussicault(self, position=None):
1443
+ r"""
1444
+ Convert to a binary tree using the Aval-Boussicault algorithm.
1445
+
1446
+ You can use the parameter ``position`` to use the bijection on
1447
+ a new parallelogram polyomino (PP). This PP is obtained by cutting the
1448
+ PP in such a way the cell at position ``position`` becomes the
1449
+ top-left most corner of the PP.
1450
+
1451
+ Reference: [ABBS2013]_
1452
+
1453
+ INPUT:
1454
+
1455
+ - ``position`` -- the cell position; this is a recursive parameter
1456
+ It should not be used directly
1457
+
1458
+ EXAMPLES::
1459
+
1460
+ sage: pp = ParallelogramPolyomino(
1461
+ ....: [
1462
+ ....: [0, 0, 1, 0, 1, 0, 1, 0, 1, 1],
1463
+ ....: [1, 1, 0, 1, 1, 0, 0, 0, 1, 0]
1464
+ ....: ]
1465
+ ....: )
1466
+ sage: pp._to_binary_tree_Aval_Boussicault() # needs sage.graphs
1467
+ [[., [[., .], [[., [., .]], .]]], [[., .], .]]
1468
+
1469
+ sage: pp = ParallelogramPolyomino([[0, 1], [1, 0]])
1470
+ sage: pp._to_binary_tree_Aval_Boussicault() # needs sage.graphs
1471
+ [., .]
1472
+
1473
+ sage: pp = ParallelogramPolyomino([[1], [1]])
1474
+ sage: pp._to_binary_tree_Aval_Boussicault() # needs sage.graphs
1475
+ .
1476
+ """
1477
+ from sage.combinat.binary_tree import BinaryTree
1478
+ if position is None:
1479
+ position = [0, 0]
1480
+ if self.size() == 1:
1481
+ return BinaryTree()
1482
+ result = [BinaryTree(), BinaryTree()]
1483
+ right_son = list(position)
1484
+ left_son = list(position)
1485
+ w = right_son[1] + 1
1486
+ h = left_son[0] + 1
1487
+ while w < self.width():
1488
+ if self[right_son[0]][w] == 1:
1489
+ if self[right_son[0]-1][w] == 0:
1490
+ right_son[1] = w
1491
+ result[1] = self._to_binary_tree_Aval_Boussicault(
1492
+ right_son
1493
+ )
1494
+ break
1495
+ w += 1
1496
+ while h < self.height():
1497
+ if self[h][left_son[1]] == 1:
1498
+ if self[h][left_son[1]-1] == 0:
1499
+ left_son[0] = h
1500
+ result[0] = self._to_binary_tree_Aval_Boussicault(left_son)
1501
+ break
1502
+ h += 1
1503
+ return BinaryTree(result)
1504
+
1505
+ @combinatorial_map(name="To binary tree")
1506
+ def to_binary_tree(self, bijection=None):
1507
+ r"""
1508
+ Convert to a binary tree.
1509
+
1510
+ INPUT:
1511
+
1512
+ - ``bijection`` -- string or ``None`` (default: ``None``); the name of
1513
+ bijection to use for the conversion. The possible values are ``None``
1514
+ or ``'Aval-Boussicault'``. The ``None`` value is equivalent to
1515
+ ``'Aval-Boussicault'``.
1516
+
1517
+ EXAMPLES::
1518
+
1519
+ sage: pp = ParallelogramPolyomino(
1520
+ ....: [
1521
+ ....: [0, 0, 1, 0, 1, 0, 1, 0, 1, 1],
1522
+ ....: [1, 1, 0, 1, 1, 0, 0, 0, 1, 0]
1523
+ ....: ]
1524
+ ....: )
1525
+ sage: pp.to_binary_tree() # needs sage.graphs
1526
+ [[., [[., .], [[., [., .]], .]]], [[., .], .]]
1527
+
1528
+ sage: pp = ParallelogramPolyomino([[0, 1], [1, 0]])
1529
+ sage: pp.to_binary_tree() # needs sage.graphs
1530
+ [., .]
1531
+
1532
+ sage: pp = ParallelogramPolyomino([[1], [1]])
1533
+ sage: pp.to_binary_tree() # needs sage.graphs
1534
+ .
1535
+ """
1536
+ if bijection is None or bijection == 'Aval-Boussicault':
1537
+ return self._to_binary_tree_Aval_Boussicault([0, 0])
1538
+
1539
+ def _to_ordered_tree_via_dyck(self):
1540
+ r"""
1541
+ Convert the parallelogram polyominoe (PP) by using first the
1542
+ Delest-Viennot bijection between PP and Dyck paths, and then
1543
+ by using the classical bijection between Dyck paths and
1544
+ ordered trees.
1545
+
1546
+ This last bijection is described in [DerZak1980]_ (see page 12 and
1547
+ Figure 3.1 of page 13).
1548
+
1549
+ See :meth:`_to_dyck_delest_viennot` for the exact references.
1550
+ See also :meth:`to_ordered_tree()`.
1551
+
1552
+ EXAMPLES::
1553
+
1554
+ sage: pp = ParallelogramPolyomino([[0, 1], [1, 0]])
1555
+ sage: pp._to_ordered_tree_via_dyck() # needs sage.graphs
1556
+ [[]]
1557
+
1558
+ sage: pp = ParallelogramPolyomino([[0, 1, 1], [1, 1, 0]])
1559
+ sage: pp._to_ordered_tree_via_dyck() # needs sage.graphs
1560
+ [[[]]]
1561
+
1562
+ sage: pp = ParallelogramPolyomino([[0, 0, 1], [1, 0, 0]])
1563
+ sage: pp._to_ordered_tree_via_dyck() # needs sage.graphs
1564
+ [[], []]
1565
+
1566
+ sage: pp = ParallelogramPolyomino(
1567
+ ....: [
1568
+ ....: [0, 0, 1, 0, 1, 0, 1, 0, 1, 1],
1569
+ ....: [1, 1, 0, 1, 1, 0, 0, 0, 1, 0]
1570
+ ....: ]
1571
+ ....: )
1572
+ sage: pp._to_ordered_tree_via_dyck() # needs sage.graphs
1573
+ [[[[]], [[[]], []]], [[]]]
1574
+ """
1575
+ return self._to_dyck_delest_viennot().to_ordered_tree()
1576
+
1577
+ def _to_ordered_tree_Bou_Socci(self):
1578
+ r"""
1579
+ Return the ordered tree using the Boussicault-Socci bijection.
1580
+
1581
+ This bijection is described in the article [BRS2015]_.
1582
+
1583
+ EXAMPLES::
1584
+
1585
+ sage: pp = ParallelogramPolyomino(
1586
+ ....: [
1587
+ ....: [0, 0, 1, 0, 1, 0, 1, 0, 1, 1],
1588
+ ....: [1, 1, 0, 1, 1, 0, 0, 0, 1, 0]
1589
+ ....: ]
1590
+ ....: )
1591
+ sage: pp._to_ordered_tree_Bou_Socci() # needs sage.graphs
1592
+ [[[[[]], [[[]]]]], [[]]]
1593
+ sage: pp.to_ordered_tree(bijection='Boussicault-Socci') # needs sage.graphs
1594
+ [[[[[]], [[[]]]]], [[]]]
1595
+
1596
+ sage: pp = ParallelogramPolyomino([[0, 1], [1, 0]])
1597
+ sage: pp._to_ordered_tree_Bou_Socci() # needs sage.graphs
1598
+ [[]]
1599
+
1600
+ sage: pp = ParallelogramPolyomino([[1], [1]])
1601
+ sage: pp._to_ordered_tree_Bou_Socci() # needs sage.graphs
1602
+ []
1603
+ """
1604
+ from sage.combinat.ordered_tree import OrderedTree
1605
+ from sage.combinat.binary_tree import BinaryTree
1606
+
1607
+ def make_tree(b_tree, d):
1608
+ r"""
1609
+ This is a technical function that converts binary tree to ordered
1610
+ tree with the following construction.
1611
+
1612
+ Add a virtual root v such that the root become:
1613
+
1614
+ - the left son of v if ``d`` is equal to 0;
1615
+
1616
+ - the right son of v if ``d`` is equal to 1;
1617
+
1618
+ Then now the vertices of the ordered tree are the vertices of
1619
+ the binary tree and the virtual root.
1620
+
1621
+ The edges are defined as follow:
1622
+
1623
+ - if v1 is a left (resp. right) son of v2 and v2 is a right
1624
+ (resp. left) son of v3, then, in the ordered tree, v2 is the
1625
+ father of v1;
1626
+ - if v1 is a left (resp. right) son of v2 and v2 is a left (resp.
1627
+ right) son of v3, then, in the ordered tree, v2 and v3 are
1628
+ brother and v2 are on the left of v3.
1629
+
1630
+ For example, if d is equal to 1
1631
+
1632
+ ::
1633
+
1634
+ 1
1635
+ / \
1636
+ 2 3
1637
+ /
1638
+ 7
1639
+ / \
1640
+ 8 4
1641
+ \
1642
+ 5
1643
+ \
1644
+ 6
1645
+
1646
+ becomes
1647
+
1648
+ ::
1649
+
1650
+ _____v_____
1651
+ | |
1652
+ ___1___ ____ 3
1653
+ | | |
1654
+ 2 ___7__ 8
1655
+ | | |
1656
+ 4 5 6
1657
+
1658
+ and if d = 0, it becomes
1659
+
1660
+ ::
1661
+
1662
+ _________v________
1663
+ | | | |
1664
+ 1 2 ____7___ 8
1665
+ | | | |
1666
+ 3 4 5 6
1667
+
1668
+ INPUT:
1669
+
1670
+ - ``b_tree`` -- a binary tree
1671
+
1672
+ - ``d`` -- 0 or 1
1673
+
1674
+ OUTPUT:
1675
+
1676
+ An ordered tree.
1677
+ """
1678
+ if b_tree == BinaryTree():
1679
+ return OrderedTree([])
1680
+ res = []
1681
+ res.append(make_tree(b_tree[1 - d], 1 - d))
1682
+ res += make_tree(b_tree[d], d)
1683
+ return OrderedTree(res)
1684
+ return make_tree(
1685
+ self.to_binary_tree(bijection='Aval-Boussicault'), 1)
1686
+
1687
+ @combinatorial_map(name="To ordered tree")
1688
+ def to_ordered_tree(self, bijection=None):
1689
+ r"""
1690
+ Return an ordered tree from the parallelogram polyomino.
1691
+
1692
+ Different bijections can be specified.
1693
+
1694
+ The bijection 'via dyck and Delest-Viennot' is the composition of
1695
+ :meth:`_to_dyck_delest_viennot` and the classical bijection between
1696
+ dyck paths and ordered trees.
1697
+
1698
+ The bijection between Dyck Word and ordered trees is described
1699
+ in [DerZak1980]_ (See page 12 and 13 and Figure 3.1).
1700
+
1701
+ The bijection 'Boussicault-Socci' is described in [BRS2015]_.
1702
+
1703
+ INPUT:
1704
+
1705
+ - ``bijection`` -- string or ``None`` (default: ``None``); the name of
1706
+ bijection to use for the conversion. The possible value are ``None``,
1707
+ ``'Boussicault-Socci'`` or ``'via dyck and Delest-Viennot'``.
1708
+ The ``None`` value is equivalent to the ``'Boussicault-Socci'``
1709
+ value.
1710
+
1711
+ EXAMPLES::
1712
+
1713
+ sage: pp = ParallelogramPolyomino(
1714
+ ....: [
1715
+ ....: [0, 0, 1, 0, 1, 0, 1, 0, 1, 1],
1716
+ ....: [1, 1, 0, 1, 1, 0, 0, 0, 1, 0]
1717
+ ....: ]
1718
+ ....: )
1719
+ sage: pp.to_ordered_tree() # needs sage.graphs
1720
+ [[[[[]], [[[]]]]], [[]]]
1721
+
1722
+ sage: pp = ParallelogramPolyomino([[0, 1], [1, 0]])
1723
+ sage: pp.to_ordered_tree() # needs sage.graphs
1724
+ [[]]
1725
+
1726
+ sage: pp = ParallelogramPolyomino([[1], [1]])
1727
+ sage: pp.to_ordered_tree() # needs sage.graphs
1728
+ []
1729
+
1730
+ sage: pp = ParallelogramPolyomino(
1731
+ ....: [
1732
+ ....: [0, 0, 1, 0, 1, 0, 1, 0, 1, 1],
1733
+ ....: [1, 1, 0, 1, 1, 0, 0, 0, 1, 0]
1734
+ ....: ]
1735
+ ....: )
1736
+ sage: pp.to_ordered_tree('via dyck and Delest-Viennot') # needs sage.graphs
1737
+ [[[[]], [[[]], []]], [[]]]
1738
+ """
1739
+ if bijection is None or bijection == 'Boussicault-Socci':
1740
+ return self._to_ordered_tree_Bou_Socci()
1741
+ if bijection == 'via dyck and Delest-Viennot':
1742
+ return self._to_ordered_tree_via_dyck()
1743
+
1744
+ def get_options(self):
1745
+ r"""
1746
+ Return all the options of the object.
1747
+
1748
+ EXAMPLES::
1749
+
1750
+ sage: pp = ParallelogramPolyomino([[0, 1], [1, 0]])
1751
+ sage: pp.get_options()
1752
+ Current options for ParallelogramPolyominoes_size
1753
+ - display: 'list'
1754
+ - drawing_components: {'bounce_0': False,
1755
+ 'bounce_1': False,
1756
+ 'bounce_values': False,
1757
+ 'diagram': True,
1758
+ 'tree': False}
1759
+ - latex: 'drawing'
1760
+ - tikz_options: {'color_bounce_0': 'red',
1761
+ 'color_bounce_1': 'blue',
1762
+ 'color_line': 'black',
1763
+ 'color_point': 'black',
1764
+ 'line_size': 1,
1765
+ 'mirror': None,
1766
+ 'point_size': 3.5,
1767
+ 'rotation': 0,
1768
+ 'scale': 1,
1769
+ 'translation': [0, 0]}
1770
+ """
1771
+ if self._options is None:
1772
+ return self.parent().get_options()
1773
+ return self._options
1774
+
1775
+ def set_options(self, *get_value, **set_value):
1776
+ r"""
1777
+ Set new options to the object.
1778
+ See :class:`LocalOptions` for more info.
1779
+
1780
+ EXAMPLES::
1781
+
1782
+ sage: pp = ParallelogramPolyomino(
1783
+ ....: [
1784
+ ....: [0, 0, 0, 0, 1, 0, 1, 0, 1],
1785
+ ....: [1, 0, 0, 0, 1, 1, 0, 0, 0]
1786
+ ....: ]
1787
+ ....: )
1788
+ sage: pp
1789
+ [[0, 0, 0, 0, 1, 0, 1, 0, 1], [1, 0, 0, 0, 1, 1, 0, 0, 0]]
1790
+ sage: pp.set_options(display='drawing')
1791
+ sage: pp
1792
+ [1 0 0]
1793
+ [1 0 0]
1794
+ [1 0 0]
1795
+ [1 1 1]
1796
+ [0 1 1]
1797
+ [0 0 1]
1798
+
1799
+ sage: pp = ParallelogramPolyomino([[0, 1], [1, 0]])
1800
+ sage: view(PP) # not tested
1801
+ sage: pp.set_options(
1802
+ ....: drawing_components=dict(
1803
+ ....: diagram = True,
1804
+ ....: bounce_0 = True,
1805
+ ....: bounce_1 = True,
1806
+ ....: )
1807
+ ....: )
1808
+ sage: view(PP) # not tested
1809
+ """
1810
+ if self._options is None:
1811
+ self._options = deepcopy(self.get_options())
1812
+ self._options(*get_value, **set_value)
1813
+
1814
+ def upper_path(self) -> list:
1815
+ r"""
1816
+ Get the upper path of the parallelogram polyomino.
1817
+
1818
+ EXAMPLES::
1819
+
1820
+ sage: lower_path = [0, 0, 1, 0, 1, 1]
1821
+ sage: upper_path = [1, 1, 0, 1, 0, 0]
1822
+ sage: pp = ParallelogramPolyomino([lower_path, upper_path])
1823
+ sage: pp.upper_path()
1824
+ [1, 1, 0, 1, 0, 0]
1825
+ """
1826
+ return list(ClonableList.__getitem__(self, 1))
1827
+
1828
+ def lower_path(self) -> list:
1829
+ r"""
1830
+ Get the lower path of the parallelogram polyomino.
1831
+
1832
+ EXAMPLES::
1833
+
1834
+ sage: lower_path = [0, 0, 1, 0, 1, 1]
1835
+ sage: upper_path = [1, 1, 0, 1, 0, 0]
1836
+ sage: pp = ParallelogramPolyomino([lower_path, upper_path])
1837
+ sage: pp.lower_path()
1838
+ [0, 0, 1, 0, 1, 1]
1839
+ """
1840
+ return list(ClonableList.__getitem__(self, 0))
1841
+
1842
+ @staticmethod
1843
+ def _prefix_lengths(word, up):
1844
+ r"""
1845
+ Convert a word to a list of lengths using the following algorithm:
1846
+
1847
+ 1) convert each 1-``up`` letter of the word by the number of ``up``
1848
+ located on the left in the word;
1849
+ 2) remove all the ``up`` letters and return the resulting list of
1850
+ integers.
1851
+
1852
+ INPUT:
1853
+
1854
+ - ``word`` -- a word of 0 and 1
1855
+
1856
+ - ``up`` -- 0 or 1 (a letter of the word)
1857
+
1858
+ OUTPUT: list of integers
1859
+
1860
+ EXAMPLES::
1861
+
1862
+ sage: ParallelogramPolyomino._prefix_lengths([], 1)
1863
+ []
1864
+ sage: ParallelogramPolyomino._prefix_lengths([], 0)
1865
+ []
1866
+ sage: ParallelogramPolyomino._prefix_lengths([1,1,0,1,0,0,1], 1)
1867
+ [2, 3, 3]
1868
+ sage: ParallelogramPolyomino._prefix_lengths([1,1,0,1,0,0,1], 0)
1869
+ [0, 0, 1, 3]
1870
+ """
1871
+ res = []
1872
+ h = 0
1873
+ for e in word:
1874
+ if e == up:
1875
+ h += 1
1876
+ else:
1877
+ res.append(h)
1878
+ return res
1879
+
1880
+ def upper_heights(self):
1881
+ r"""
1882
+ Return the list of heights associated to each vertical step of the
1883
+ parallelogram polyomino's upper path.
1884
+
1885
+ OUTPUT: list of integers
1886
+
1887
+ EXAMPLES::
1888
+
1889
+ sage: ParallelogramPolyomino([[0, 1], [1, 0]]).upper_heights()
1890
+ [0]
1891
+ sage: ParallelogramPolyomino(
1892
+ ....: [[0, 0, 1, 1, 0, 1, 1, 1], [1, 0, 1, 1, 0, 1, 1, 0]]
1893
+ ....: ).upper_heights()
1894
+ [0, 1, 1, 2, 2]
1895
+ """
1896
+ return ParallelogramPolyomino._prefix_lengths(self.upper_path(), 0)
1897
+
1898
+ def lower_heights(self):
1899
+ r"""
1900
+ Return the list of heights associated to each vertical step of the
1901
+ parallelogram polyomino's lower path.
1902
+
1903
+ OUTPUT: list of integers
1904
+
1905
+ EXAMPLES::
1906
+
1907
+ sage: ParallelogramPolyomino([[0, 1], [1, 0]]).lower_heights()
1908
+ [1]
1909
+ sage: ParallelogramPolyomino(
1910
+ ....: [[0, 0, 1, 1, 0, 1, 1, 1], [1, 0, 1, 1, 0, 1, 1, 0]]
1911
+ ....: ).lower_heights()
1912
+ [2, 2, 3, 3, 3]
1913
+ """
1914
+ return ParallelogramPolyomino._prefix_lengths(self.lower_path(), 0)
1915
+
1916
+ def upper_widths(self):
1917
+ r"""
1918
+ Return the list of widths associated to each horizontal step of the
1919
+ parallelogram polyomino's upper path.
1920
+
1921
+ OUTPUT: list of integers
1922
+
1923
+ EXAMPLES::
1924
+
1925
+ sage: ParallelogramPolyomino([[0, 1], [1, 0]]).upper_widths()
1926
+ [1]
1927
+ sage: ParallelogramPolyomino(
1928
+ ....: [[0, 0, 1, 1, 0, 1, 1, 1], [1, 0, 1, 1, 0, 1, 1, 0]]
1929
+ ....: ).upper_widths()
1930
+ [1, 3, 5]
1931
+ """
1932
+ return ParallelogramPolyomino._prefix_lengths(self.upper_path(), 1)
1933
+
1934
+ def lower_widths(self):
1935
+ r"""
1936
+ Return the list of widths associated to each horizontal step of the
1937
+ parallelogram polyomino's lower path.
1938
+
1939
+ OUTPUT: list of integers
1940
+
1941
+ EXAMPLES::
1942
+
1943
+ sage: ParallelogramPolyomino([[0, 1], [1, 0]]).lower_widths()
1944
+ [0]
1945
+ sage: ParallelogramPolyomino(
1946
+ ....: [[0, 0, 1, 1, 0, 1, 1, 1], [1, 0, 1, 1, 0, 1, 1, 0]]
1947
+ ....: ).lower_widths()
1948
+ [0, 0, 2]
1949
+ """
1950
+ return ParallelogramPolyomino._prefix_lengths(self.lower_path(), 1)
1951
+
1952
+ def widths(self) -> list:
1953
+ r"""
1954
+ Return a list of the widths of the parallelogram polyomino.
1955
+
1956
+ Namely, the parallelogram polyomino is split row by row and the
1957
+ method returns the list containing the sizes of the rows.
1958
+
1959
+ EXAMPLES::
1960
+
1961
+ sage: pp = ParallelogramPolyomino(
1962
+ ....: [
1963
+ ....: [0, 0, 0, 1, 0, 1, 0, 1, 1],
1964
+ ....: [1, 0, 1, 1, 0, 0, 1, 0, 0]
1965
+ ....: ]
1966
+ ....: )
1967
+ sage: pp.widths()
1968
+ [1, 3, 3, 3, 2]
1969
+
1970
+ sage: pp = ParallelogramPolyomino([[0, 1], [1, 0]])
1971
+ sage: pp.widths()
1972
+ [1]
1973
+
1974
+ sage: pp = ParallelogramPolyomino([[1], [1]])
1975
+ sage: pp.widths()
1976
+ []
1977
+ """
1978
+ uw = self.upper_widths()
1979
+ lw = self.lower_widths()
1980
+ return [up - lo for up, lo in zip(uw, lw)]
1981
+
1982
+ def degree_convexity(self) -> int:
1983
+ r"""
1984
+ Return the degree convexity of a parallelogram polyomino.
1985
+
1986
+ A convex polyomino is said to be k-convex if every pair of its cells
1987
+ can be connected by a monotone path (path with south and east steps)
1988
+ with at most k changes of direction.
1989
+ The degree of convexity of a convex polyomino P is the smallest integer
1990
+ k such that P is k-convex.
1991
+
1992
+ If the parallelogram polyomino is empty, the function return -1.
1993
+
1994
+ EXAMPLES::
1995
+
1996
+ sage: pp = ParallelogramPolyomino(
1997
+ ....: [
1998
+ ....: [0, 0, 0, 1, 0, 1, 0, 1, 1],
1999
+ ....: [1, 0, 1, 1, 0, 0, 1, 0, 0]
2000
+ ....: ]
2001
+ ....: )
2002
+ sage: pp.degree_convexity()
2003
+ 3
2004
+
2005
+ sage: pp = ParallelogramPolyomino([[0, 1], [1, 0]])
2006
+ sage: pp.degree_convexity()
2007
+ 0
2008
+
2009
+ sage: pp = ParallelogramPolyomino([[1], [1]])
2010
+ sage: pp.degree_convexity()
2011
+ -1
2012
+ """
2013
+ l0 = len(self.bounce_path(direction=0))
2014
+ l1 = len(self.bounce_path(direction=1))
2015
+ return min(l0, l1) - 1
2016
+
2017
+ def is_flat(self) -> bool:
2018
+ r"""
2019
+ Return whether the two bounce paths join together in the rightmost cell
2020
+ of the bottom row of P.
2021
+
2022
+ EXAMPLES::
2023
+
2024
+ sage: pp = ParallelogramPolyomino(
2025
+ ....: [
2026
+ ....: [0, 0, 0, 1, 0, 1, 0, 1, 1],
2027
+ ....: [1, 0, 1, 1, 0, 0, 1, 0, 0]
2028
+ ....: ]
2029
+ ....: )
2030
+ sage: pp.is_flat()
2031
+ False
2032
+
2033
+ sage: pp = ParallelogramPolyomino([[0, 1], [1, 0]])
2034
+ sage: pp.is_flat()
2035
+ True
2036
+
2037
+ sage: pp = ParallelogramPolyomino([[1], [1]])
2038
+ sage: pp.is_flat()
2039
+ True
2040
+ """
2041
+ l0 = len(self.bounce_path(direction=0))
2042
+ l1 = len(self.bounce_path(direction=1))
2043
+ return l0 == l1
2044
+
2045
+ def is_k_directed(self, k) -> bool:
2046
+ r"""
2047
+ Return whether the Polyomino Parallelogram is k-directed.
2048
+
2049
+ A convex polyomino is said to be k-convex if every pair of its cells
2050
+ can be connected by a monotone path (path with south and east steps)
2051
+ with at most k changes of direction.
2052
+
2053
+ The degree of convexity of a convex polyomino P is the smallest integer
2054
+ k such that P is k-convex.
2055
+
2056
+ INPUT:
2057
+
2058
+ - ``k`` -- nonnegative integer
2059
+
2060
+ EXAMPLES::
2061
+
2062
+ sage: pp = ParallelogramPolyomino(
2063
+ ....: [
2064
+ ....: [0, 0, 0, 1, 0, 1, 0, 1, 1],
2065
+ ....: [1, 0, 1, 1, 0, 0, 1, 0, 0]
2066
+ ....: ]
2067
+ ....: )
2068
+ sage: pp.is_k_directed(3)
2069
+ True
2070
+ sage: pp.is_k_directed(4)
2071
+ True
2072
+ sage: pp.is_k_directed(5)
2073
+ True
2074
+ sage: pp.is_k_directed(0)
2075
+ False
2076
+ sage: pp.is_k_directed(1)
2077
+ False
2078
+ sage: pp.is_k_directed(2)
2079
+ False
2080
+
2081
+ sage: pp = ParallelogramPolyomino([[0, 1], [1, 0]])
2082
+ sage: pp.is_k_directed(0)
2083
+ True
2084
+ sage: pp.is_k_directed(1)
2085
+ True
2086
+
2087
+ sage: pp = ParallelogramPolyomino([[1], [1]])
2088
+ sage: pp.is_k_directed(0)
2089
+ True
2090
+ sage: pp.is_k_directed(1)
2091
+ True
2092
+ """
2093
+ return self.degree_convexity() <= k
2094
+
2095
+ def heights(self) -> list:
2096
+ r"""
2097
+ Return a list of heights of the parallelogram polyomino.
2098
+
2099
+ Namely, the parallelogram polyomino is split column by column and
2100
+ the method returns the list containing the sizes of the columns.
2101
+
2102
+ EXAMPLES::
2103
+
2104
+ sage: pp = ParallelogramPolyomino(
2105
+ ....: [
2106
+ ....: [0, 0, 0, 1, 0, 1, 0, 1, 1],
2107
+ ....: [1, 0, 1, 1, 0, 0, 1, 0, 0]
2108
+ ....: ]
2109
+ ....: )
2110
+ sage: pp.heights()
2111
+ [3, 3, 4, 2]
2112
+
2113
+ sage: pp = ParallelogramPolyomino([[0, 1], [1, 0]])
2114
+ sage: pp.heights()
2115
+ [1]
2116
+
2117
+ sage: pp = ParallelogramPolyomino([[1], [1]])
2118
+ sage: pp.heights()
2119
+ [0]
2120
+ """
2121
+ uh = self.upper_heights()
2122
+ lh = self.lower_heights()
2123
+ return [a - b for a, b in zip(lh, uh)]
2124
+
2125
+ def width(self):
2126
+ r"""
2127
+ Return the width of the parallelogram polyomino.
2128
+
2129
+ EXAMPLES::
2130
+
2131
+ sage: pp = ParallelogramPolyomino(
2132
+ ....: [
2133
+ ....: [0, 1, 0, 0, 1, 1, 0, 1, 1, 1],
2134
+ ....: [1, 1, 1, 0, 1, 0, 0, 1, 1, 0]
2135
+ ....: ]
2136
+ ....: )
2137
+ sage: pp.width()
2138
+ 6
2139
+
2140
+ sage: pp = ParallelogramPolyomino([[0, 1], [1, 0]])
2141
+ sage: pp.width()
2142
+ 1
2143
+
2144
+ sage: pp = ParallelogramPolyomino([[1], [1]])
2145
+ sage: pp.width()
2146
+ 1
2147
+ """
2148
+ if self.size() == 1:
2149
+ return 1
2150
+ return self.upper_widths()[-1]
2151
+
2152
+ def height(self):
2153
+ r"""
2154
+ Return the height of the parallelogram polyomino.
2155
+
2156
+ EXAMPLES::
2157
+
2158
+ sage: pp = ParallelogramPolyomino(
2159
+ ....: [
2160
+ ....: [0, 1, 0, 0, 1, 1, 0, 1, 1, 1],
2161
+ ....: [1, 1, 1, 0, 1, 0, 0, 1, 1, 0]
2162
+ ....: ]
2163
+ ....: )
2164
+ sage: pp.height()
2165
+ 4
2166
+
2167
+ sage: pp = ParallelogramPolyomino([[0, 1], [1, 0]])
2168
+ sage: pp.height()
2169
+ 1
2170
+
2171
+ sage: pp = ParallelogramPolyomino([[1], [1]])
2172
+ sage: pp.height()
2173
+ 0
2174
+ """
2175
+ if self.size() == 1:
2176
+ return 0
2177
+ return self.lower_heights()[-1]
2178
+
2179
+ def cell_is_inside(self, w, h):
2180
+ r"""
2181
+ Determine whether the cell at a given position
2182
+ is inside the parallelogram polyomino.
2183
+
2184
+ INPUT:
2185
+
2186
+ - ``w`` -- the x coordinate of the box position
2187
+
2188
+ - ``h`` -- the y coordinate of the box position
2189
+
2190
+ OUTPUT:
2191
+
2192
+ Return 0 if there is no cell at the given position,
2193
+ return 1 if there is a cell.
2194
+
2195
+ EXAMPLES::
2196
+
2197
+ sage: pp = ParallelogramPolyomino(
2198
+ ....: [
2199
+ ....: [0, 1, 0, 0, 1, 1, 0, 1, 1, 1],
2200
+ ....: [1, 1, 1, 0, 1, 0, 0, 1, 1, 0]
2201
+ ....: ]
2202
+ ....: )
2203
+ sage: pp.cell_is_inside(0, 0)
2204
+ 1
2205
+ sage: pp.cell_is_inside(1, 0)
2206
+ 1
2207
+ sage: pp.cell_is_inside(0, 1)
2208
+ 0
2209
+ sage: pp.cell_is_inside(3, 0)
2210
+ 0
2211
+ sage: pp.cell_is_inside(pp.width()-1,pp.height()-1)
2212
+ 1
2213
+ sage: pp.cell_is_inside(pp.width(),pp.height()-1)
2214
+ 0
2215
+ sage: pp.cell_is_inside(pp.width()-1,pp.height())
2216
+ 0
2217
+ """
2218
+ lower_widths = self.lower_widths()
2219
+ widths = self.widths()
2220
+
2221
+ if h >= len(widths) or h < 0:
2222
+ return 0
2223
+ if lower_widths[h] <= w and w < lower_widths[h] + widths[h]:
2224
+ return 1
2225
+ return 0
2226
+
2227
+ @cached_method
2228
+ def get_array(self):
2229
+ r"""
2230
+ Return an array of 0s and 1s such that the 1s represent the boxes of
2231
+ the parallelogram polyomino.
2232
+
2233
+ EXAMPLES::
2234
+
2235
+ sage: pp = ParallelogramPolyomino(
2236
+ ....: [
2237
+ ....: [0, 0, 0, 0, 1, 0, 1, 0, 1],
2238
+ ....: [1, 0, 0, 0, 1, 1, 0, 0, 0]
2239
+ ....: ]
2240
+ ....: )
2241
+ sage: matrix(pp.get_array())
2242
+ [1 0 0]
2243
+ [1 0 0]
2244
+ [1 0 0]
2245
+ [1 1 1]
2246
+ [0 1 1]
2247
+ [0 0 1]
2248
+
2249
+ sage: pp = ParallelogramPolyomino([[0, 1], [1, 0]])
2250
+ sage: pp.get_array()
2251
+ [[1]]
2252
+
2253
+ sage: pp = ParallelogramPolyomino([[1], [1]])
2254
+ sage: pp.get_array()
2255
+ []
2256
+ """
2257
+ width = self.width()
2258
+ height = self.height()
2259
+ return [
2260
+ [self.cell_is_inside(w, h) for w in range(width)]
2261
+ for h in range(height)
2262
+ ]
2263
+
2264
+ class _polyomino_row:
2265
+ r"""
2266
+ This is an internal class representing a single row of
2267
+ a parallelogram polyomino.
2268
+
2269
+ EXAMPLES::
2270
+
2271
+ sage: pp = ParallelogramPolyomino(
2272
+ ....: [
2273
+ ....: [0, 0, 0, 0, 1, 0, 1, 0, 1],
2274
+ ....: [1, 0, 0, 0, 1, 1, 0, 0, 0]
2275
+ ....: ]
2276
+ ....: )
2277
+ sage: row = ParallelogramPolyomino._polyomino_row(pp, 4)
2278
+ sage: row
2279
+ [0, 1, 1]
2280
+ """
2281
+
2282
+ def __init__(self, polyomino, row):
2283
+ r"""
2284
+ The constructor of the class.
2285
+
2286
+ EXAMPLES::
2287
+
2288
+ sage: pp = ParallelogramPolyomino(
2289
+ ....: [
2290
+ ....: [0, 0, 0, 0, 1, 0, 1, 0, 1],
2291
+ ....: [1, 0, 0, 0, 1, 1, 0, 0, 0]
2292
+ ....: ]
2293
+ ....: )
2294
+ sage: row = ParallelogramPolyomino._polyomino_row(pp, 4)
2295
+ """
2296
+ self.polyomino = polyomino
2297
+ self.row = row
2298
+
2299
+ def __getitem__(self, column):
2300
+ r"""
2301
+ Return 0 or 1 if the is a cell inside the specific column inside the
2302
+ row.
2303
+
2304
+ EXAMPLES::
2305
+
2306
+ sage: pp = ParallelogramPolyomino(
2307
+ ....: [
2308
+ ....: [0, 0, 0, 0, 1, 0, 1, 0, 1],
2309
+ ....: [1, 0, 0, 0, 1, 1, 0, 0, 0]
2310
+ ....: ]
2311
+ ....: )
2312
+ sage: matrix(pp.get_array())
2313
+ [1 0 0]
2314
+ [1 0 0]
2315
+ [1 0 0]
2316
+ [1 1 1]
2317
+ [0 1 1]
2318
+ [0 0 1]
2319
+
2320
+ sage: row = ParallelogramPolyomino._polyomino_row(pp, 4)
2321
+ sage: [row[-1], row[0], row[1], row[2], row[3]]
2322
+ [0, 0, 1, 1, 0]
2323
+ """
2324
+ if (self.is_inside() and
2325
+ 0 <= column and column < self.polyomino.width()):
2326
+ return self.polyomino.get_array()[self.row][column]
2327
+ return 0
2328
+
2329
+ def is_inside(self) -> bool:
2330
+ r"""
2331
+ Return ``True`` if the row is inside the parallelogram polyomino,
2332
+ return ``False`` otherwise.
2333
+
2334
+ EXAMPLES::
2335
+
2336
+ sage: PP = ParallelogramPolyomino
2337
+ sage: pp = PP(
2338
+ ....: [
2339
+ ....: [0, 0, 0, 0, 1, 0, 1, 0, 1],
2340
+ ....: [1, 0, 0, 0, 1, 1, 0, 0, 0]
2341
+ ....: ]
2342
+ ....: )
2343
+ sage: matrix(pp.get_array())
2344
+ [1 0 0]
2345
+ [1 0 0]
2346
+ [1 0 0]
2347
+ [1 1 1]
2348
+ [0 1 1]
2349
+ [0 0 1]
2350
+
2351
+ sage: [
2352
+ ....: PP._polyomino_row(pp, i).is_inside()
2353
+ ....: for i in [-1,0,3,5,6]
2354
+ ....: ]
2355
+ [False, True, True, True, False]
2356
+ """
2357
+ return 0 <= self.row and self.row < self.polyomino.height()
2358
+
2359
+ def is_outside(self) -> bool:
2360
+ r"""
2361
+ Return ``True`` if the row is outside the parallelogram polyomino,
2362
+ return ``False`` otherwise.
2363
+
2364
+ EXAMPLES::
2365
+
2366
+ sage: PP = ParallelogramPolyomino
2367
+ sage: pp = PP(
2368
+ ....: [
2369
+ ....: [0, 0, 0, 0, 1, 0, 1, 0, 1],
2370
+ ....: [1, 0, 0, 0, 1, 1, 0, 0, 0]
2371
+ ....: ]
2372
+ ....: )
2373
+ sage: matrix(pp.get_array())
2374
+ [1 0 0]
2375
+ [1 0 0]
2376
+ [1 0 0]
2377
+ [1 1 1]
2378
+ [0 1 1]
2379
+ [0 0 1]
2380
+
2381
+ sage: [
2382
+ ....: PP._polyomino_row(pp, i).is_outside()
2383
+ ....: for i in [-1,0,3,5,6]
2384
+ ....: ]
2385
+ [True, False, False, False, True]
2386
+ """
2387
+ return not self.is_inside()
2388
+
2389
+ def __repr__(self) -> str:
2390
+ r"""
2391
+ Return a string representation of ``self``.
2392
+
2393
+ EXAMPLES::
2394
+
2395
+ sage: PP = ParallelogramPolyomino
2396
+ sage: pp = PP(
2397
+ ....: [
2398
+ ....: [0, 0, 0, 0, 1, 0, 1, 0, 1],
2399
+ ....: [1, 0, 0, 0, 1, 1, 0, 0, 0]
2400
+ ....: ]
2401
+ ....: )
2402
+ sage: pp[-1]
2403
+ The (outside) row -1 of the parallelogram
2404
+ sage: pp[0]
2405
+ [1, 0, 0]
2406
+ sage: pp[5]
2407
+ [0, 0, 1]
2408
+ sage: pp[6]
2409
+ The (outside) row 6 of the parallelogram
2410
+ """
2411
+ if self.is_outside():
2412
+ return "The (outside) row %s of the parallelogram" % (self.row)
2413
+ else:
2414
+ return str(self.polyomino.get_array()[self.row])
2415
+
2416
+ def __getitem__(self, row):
2417
+ r"""
2418
+ Return the row of the parallelogram polyomino.
2419
+
2420
+ The index of the row can be out of range of the height of
2421
+ the parallelogram polyomino.
2422
+ In that case, the row returned is outside the parallelogram
2423
+ polyomino.
2424
+
2425
+ EXAMPLES::
2426
+
2427
+ sage: pp = ParallelogramPolyomino(
2428
+ ....: [[0, 0, 1, 0, 1, 1], [1, 1, 0, 0, 1, 0]]
2429
+ ....: )
2430
+ sage: pp[0].is_inside()
2431
+ True
2432
+ sage: pp[3].is_outside()
2433
+ True
2434
+ sage: pp[-1].is_outside()
2435
+ True
2436
+ sage: pp[0][0]
2437
+ 1
2438
+ sage: pp[[0, 1]]
2439
+ 1
2440
+ sage: pp[2][0]
2441
+ 0
2442
+ sage: pp[-1][0]
2443
+ 0
2444
+ sage: pp[[4, 4]]
2445
+ 0
2446
+ """
2447
+ if isinstance(row, list):
2448
+ return self._polyomino_row(self, row[0])[row[1]]
2449
+ return self._polyomino_row(self, row)
2450
+
2451
+ def bounce_path(self, direction=1):
2452
+ r"""
2453
+ Return the bounce path of the parallelogram polyomino.
2454
+
2455
+ The bounce path is a path with two steps (1, 0) and (0, 1).
2456
+
2457
+ If 'direction' is 1 (resp. 0), the bounce path is the path
2458
+ starting at position (h=1, w=0) (resp. (h=0, w=1)) with
2459
+ initial direction, the vector (0, 1) (resp. (1, 0)), and turning
2460
+ each time the path crosses the perimeter of the parallelogram
2461
+ polyomino.
2462
+
2463
+ The path is coded by a list of integers. Each integer represents
2464
+ the size of the path between two turnings.
2465
+
2466
+ You can visualize the two bounce paths by using the following
2467
+ commands.
2468
+
2469
+ INPUT:
2470
+
2471
+ - ``direction`` -- the initial direction of the bounce path (see above
2472
+ for the definition)
2473
+
2474
+ EXAMPLES::
2475
+
2476
+ sage: PP = ParallelogramPolyomino(
2477
+ ....: [[0, 0, 1, 0, 1, 1], [1, 1, 0, 0, 1, 0]]
2478
+ ....: )
2479
+ sage: PP.bounce_path(direction=1)
2480
+ [2, 2, 1]
2481
+ sage: PP.bounce_path(direction=0)
2482
+ [2, 1, 1, 1]
2483
+
2484
+ sage: PP = ParallelogramPolyomino(
2485
+ ....: [
2486
+ ....: [0, 0, 1, 1, 1, 0, 0, 1, 1],
2487
+ ....: [1, 1, 1, 0, 1, 1, 0, 0, 0]
2488
+ ....: ]
2489
+ ....: )
2490
+ sage: PP.bounce_path(direction=1)
2491
+ [3, 1, 2, 2]
2492
+ sage: PP.bounce_path(direction=0)
2493
+ [2, 4, 2]
2494
+
2495
+ sage: PP = ParallelogramPolyomino(
2496
+ ....: [[0, 0, 1, 0, 1, 1], [1, 1, 0, 0, 1, 0]]
2497
+ ....: )
2498
+ sage: PP.set_options(
2499
+ ....: drawing_components=dict(
2500
+ ....: diagram = True
2501
+ ....: , bounce_0 = True
2502
+ ....: , bounce_1 = True
2503
+ ....: )
2504
+ ....: )
2505
+ sage: view(PP) # not tested
2506
+
2507
+ sage: PP = ParallelogramPolyomino([[0, 1], [1, 0]])
2508
+ sage: PP.bounce_path(direction=1)
2509
+ [1]
2510
+ sage: PP.bounce_path(direction=0)
2511
+ [1]
2512
+
2513
+ sage: PP = ParallelogramPolyomino([[1], [1]])
2514
+ sage: PP.bounce_path(direction=1)
2515
+ []
2516
+ sage: PP.bounce_path(direction=0)
2517
+ []
2518
+
2519
+ TESTS::
2520
+
2521
+ sage: PP = ParallelogramPolyomino(
2522
+ ....: [[0, 0, 1, 0, 1, 1], [1, 1, 0, 0, 1, 0]]
2523
+ ....: )
2524
+ sage: PP.bounce_path(direction=1)
2525
+ [2, 2, 1]
2526
+ sage: PP.bounce_path(direction=0)
2527
+ [2, 1, 1, 1]
2528
+ """
2529
+ result = []
2530
+ pos = [0, 0]
2531
+ pos[direction] -= 1
2532
+ old = list(pos)
2533
+ ne = list(pos)
2534
+ ne[direction] += 1
2535
+ while self[ne] == 1:
2536
+ pos[direction] += 1
2537
+ while self[pos] == 1:
2538
+ pos[direction] += 1
2539
+ pos[direction] -= 1
2540
+ result.append(pos[direction]-old[direction])
2541
+ direction = 1 - direction
2542
+ old[0], old[1] = pos
2543
+ ne[0], ne[1] = pos
2544
+ ne[direction] += 1
2545
+ return result
2546
+
2547
+ def bounce(self, direction=1):
2548
+ r"""
2549
+ Return the bounce of the parallelogram polyomino.
2550
+
2551
+ Let ``p`` be the bounce path of the parallelogram
2552
+ polyomino (:meth:`bounce_path`). The bounce is defined by:
2553
+
2554
+ ``sum([(1+ floor(i/2))*p[i] for i in range(len(p))])``
2555
+
2556
+ INPUT:
2557
+
2558
+ - ``direction`` -- the initial direction of the bounce path
2559
+ (see :meth:`bounce_path` for the definition)
2560
+
2561
+ EXAMPLES::
2562
+
2563
+ sage: PP = ParallelogramPolyomino(
2564
+ ....: [[0, 0, 1, 0, 1, 1], [1, 1, 0, 0, 1, 0]]
2565
+ ....: )
2566
+ sage: PP.bounce(direction=1)
2567
+ 6
2568
+ sage: PP.bounce(direction=0)
2569
+ 7
2570
+
2571
+ sage: PP = ParallelogramPolyomino(
2572
+ ....: [
2573
+ ....: [0, 0, 1, 1, 1, 0, 0, 1, 1],
2574
+ ....: [1, 1, 1, 0, 1, 1, 0, 0, 0]
2575
+ ....: ]
2576
+ ....: )
2577
+ sage: PP.bounce(direction=1)
2578
+ 12
2579
+ sage: PP.bounce(direction=0)
2580
+ 10
2581
+
2582
+ sage: PP = ParallelogramPolyomino([[0, 1], [1, 0]])
2583
+ sage: PP.bounce(direction=1)
2584
+ 1
2585
+ sage: PP.bounce(direction=0)
2586
+ 1
2587
+
2588
+ sage: PP = ParallelogramPolyomino([[1], [1]])
2589
+ sage: PP.bounce(direction=1)
2590
+ 0
2591
+ sage: PP.bounce(direction=0)
2592
+ 0
2593
+ """
2594
+ return sum((1 + i//2) * pi
2595
+ for i, pi in enumerate(self.bounce_path(direction)))
2596
+
2597
+ def area(self):
2598
+ r"""
2599
+ Return the area of the parallelogram polyomino. The area of a
2600
+ parallelogram polyomino is the number of cells of the parallelogram
2601
+ polyomino.
2602
+
2603
+ EXAMPLES::
2604
+
2605
+ sage: pp = ParallelogramPolyomino(
2606
+ ....: [
2607
+ ....: [0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1],
2608
+ ....: [1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0]
2609
+ ....: ]
2610
+ ....: )
2611
+ sage: pp.area()
2612
+ 13
2613
+
2614
+ sage: pp = ParallelogramPolyomino([[0, 1], [1, 0]])
2615
+ sage: pp.area()
2616
+ 1
2617
+
2618
+ sage: pp = ParallelogramPolyomino([[1], [1]])
2619
+ sage: pp.area()
2620
+ 0
2621
+ """
2622
+ return sum(self.heights())
2623
+
2624
+ def _repr_(self) -> str:
2625
+ r"""
2626
+ Return a string representation of the parallelogram polyomino.
2627
+
2628
+ EXAMPLES::
2629
+
2630
+ sage: pp = ParallelogramPolyomino(
2631
+ ....: [[0, 0, 1, 0, 1, 1], [1, 1, 0, 0, 1, 0]]
2632
+ ....: )
2633
+ sage: pp # indirect doctest
2634
+ [[0, 0, 1, 0, 1, 1], [1, 1, 0, 0, 1, 0]]
2635
+ sage: pp.set_options(display='drawing')
2636
+ sage: pp # indirect doctest
2637
+ [1 1 0]
2638
+ [1 1 0]
2639
+ [0 1 1]
2640
+ """
2641
+ return self.get_options()._dispatch(self, '_repr_', 'display')
2642
+
2643
+ def _repr_list(self) -> str:
2644
+ r"""
2645
+ Return a string representation with list style.
2646
+
2647
+ EXAMPLES::
2648
+
2649
+ sage: pp = ParallelogramPolyomino(
2650
+ ....: [[0, 0, 1, 0, 1, 1], [1, 1, 0, 0, 1, 0]]
2651
+ ....: )
2652
+ sage: pp._repr_() == pp._repr_list()
2653
+ True
2654
+ sage: pp._repr_list()
2655
+ '[[0, 0, 1, 0, 1, 1], [1, 1, 0, 0, 1, 0]]'
2656
+ """
2657
+ return ClonableList._repr_(self)
2658
+
2659
+ def _repr_drawing(self) -> str:
2660
+ r"""
2661
+ Return a string representing a drawing of the parallelogram polyomino.
2662
+
2663
+ EXAMPLES::
2664
+
2665
+ sage: pp = ParallelogramPolyomino(
2666
+ ....: [[0, 0, 1, 0, 1, 1], [1, 1, 0, 0, 1, 0]]
2667
+ ....: )
2668
+ sage: pp._repr_() == pp._repr_drawing()
2669
+ False
2670
+ sage: pp._repr_drawing()
2671
+ '[1 1 0]\n[1 1 0]\n[0 1 1]'
2672
+ """
2673
+ return str(matrix(self.get_array()))
2674
+
2675
+ def get_tikz_options(self):
2676
+ r"""
2677
+ Return all the tikz options permitting to draw the parallelogram
2678
+ polyomino.
2679
+
2680
+ See :class:`LocalOption` to have more informations about the
2681
+ modification of those options.
2682
+
2683
+ EXAMPLES::
2684
+
2685
+ sage: pp = ParallelogramPolyomino([[0, 1], [1, 0]])
2686
+ sage: pp.get_tikz_options()
2687
+ {'color_bounce_0': 'red',
2688
+ 'color_bounce_1': 'blue',
2689
+ 'color_line': 'black',
2690
+ 'color_point': 'black',
2691
+ 'line_size': 1,
2692
+ 'mirror': None,
2693
+ 'point_size': 3.5,
2694
+ 'rotation': 0,
2695
+ 'scale': 1,
2696
+ 'translation': [0, 0]}
2697
+ """
2698
+ return self.get_options()['tikz_options']
2699
+
2700
+ def _to_tikz_diagram(self):
2701
+ r"""
2702
+ Return the tikz code of the diagram representing ``self``.
2703
+
2704
+ TESTS::
2705
+
2706
+ sage: # needs sage.plot sage.symbolic
2707
+ sage: pp = ParallelogramPolyomino(
2708
+ ....: [[0, 0, 1, 0, 0, 0, 1, 1], [1, 0, 1, 1, 0, 0, 0, 0]]
2709
+ ....: )
2710
+ sage: pp.to_tikz() == pp._to_tikz_diagram()
2711
+ True
2712
+ sage: print(pp.to_tikz())
2713
+ <BLANKLINE>
2714
+ \draw[color=black, line width=1] (0.000000, 5.000000) --
2715
+ (0.000000, 3.000000);
2716
+ \draw[color=black, line width=1] (3.000000, 4.000000) --
2717
+ (3.000000, 0.000000);
2718
+ \draw[color=black, line width=1] (0.000000, 5.000000) --
2719
+ (1.000000, 5.000000);
2720
+ \draw[color=black, line width=1] (1.000000, 0.000000) --
2721
+ (3.000000, 0.000000);
2722
+ \draw[color=black, line width=1] (1.000000, 5.000000) --
2723
+ (1.000000, 0.000000);
2724
+ \draw[color=black, line width=1] (2.000000, 4.000000) --
2725
+ (2.000000, 0.000000);
2726
+ \draw[color=black, line width=1] (0.000000, 4.000000) --
2727
+ (3.000000, 4.000000);
2728
+ \draw[color=black, line width=1] (0.000000, 3.000000) --
2729
+ (3.000000, 3.000000);
2730
+ \draw[color=black, line width=1] (1.000000, 2.000000) --
2731
+ (3.000000, 2.000000);
2732
+ \draw[color=black, line width=1] (1.000000, 1.000000) --
2733
+ (3.000000, 1.000000);
2734
+ """
2735
+ tikz_options = self.get_tikz_options()
2736
+ grid_width = self.width() + 1
2737
+ grid_height = self.height() + 1
2738
+ drawing_tool = _drawing_tool(
2739
+ tikz_options,
2740
+ XY=lambda v: [v[0], grid_height-1-v[1]]
2741
+ )
2742
+ res = ""
2743
+ if self.size() == 1:
2744
+ res += drawing_tool.draw_line([0, 0], [1, 0])
2745
+ return res
2746
+ res += drawing_tool.draw_line([0, 0], [0, self.lower_heights()[0]])
2747
+ res += drawing_tool.draw_line(
2748
+ [grid_width-1, self.upper_heights()[grid_width-2]],
2749
+ [grid_width-1, self.lower_heights()[grid_width-2]]
2750
+ )
2751
+ res += drawing_tool.draw_line([0, 0], [self.upper_widths()[0], 0])
2752
+ res += drawing_tool.draw_line(
2753
+ [self.lower_widths()[grid_height-2], grid_height-1],
2754
+ [self.upper_widths()[grid_height-2], grid_height-1]
2755
+ )
2756
+ for w in range(1, grid_width-1):
2757
+ h1 = self.upper_heights()[w-1]
2758
+ h2 = self.lower_heights()[w]
2759
+ res += drawing_tool.draw_line([w, h1], [w, h2])
2760
+ for h in range(1, grid_height-1):
2761
+ w1 = self.lower_widths()[h-1]
2762
+ w2 = self.upper_widths()[h]
2763
+ res += drawing_tool.draw_line([w1, h], [w2, h])
2764
+ return res
2765
+
2766
+ def _to_tikz_bounce(self, directions=None):
2767
+ r"""
2768
+ Return the tikz code to display one or both bounces of ``self``.
2769
+
2770
+ See :meth:`ParallelogramPolyomino.bounce_path` for more information
2771
+ about the bounce.
2772
+
2773
+ TESTS::
2774
+
2775
+ sage: # needs sage.plot sage.symbolic
2776
+ sage: pp = ParallelogramPolyomino(
2777
+ ....: [[0, 0, 0, 1, 1, 0, 1, 1], [1, 0, 1, 1, 0, 1, 0, 0]]
2778
+ ....: )
2779
+ sage: pp.to_tikz() == pp._to_tikz_bounce()
2780
+ False
2781
+ sage: pp.set_options(drawing_components=dict(
2782
+ ....: diagram=False, bounce_0=True)
2783
+ ....: )
2784
+ sage: pp.to_tikz() == pp._to_tikz_bounce([0])
2785
+ True
2786
+ sage: pp.set_options(
2787
+ ....: drawing_components=dict(diagram=False, bounce_1=True)
2788
+ ....: )
2789
+ sage: pp.to_tikz() == pp._to_tikz_bounce([1])
2790
+ True
2791
+ sage: pp.set_options(
2792
+ ....: drawing_components=dict(
2793
+ ....: diagram=False, bounce_0= True, bounce_1=True
2794
+ ....: )
2795
+ ....: )
2796
+ sage: pp.to_tikz() == pp._to_tikz_bounce([0,1])
2797
+ True
2798
+ sage: pp.to_tikz() == pp._to_tikz_bounce()
2799
+ True
2800
+ sage: pp.set_options(
2801
+ ....: drawing_components=dict(diagram=True, bounce_0=True)
2802
+ ....: )
2803
+ sage: print(pp.to_tikz()) # indirect doctest
2804
+ <BLANKLINE>
2805
+ \draw[color=black, line width=1] (0.000000, 4.000000) --
2806
+ (0.000000, 1.000000);
2807
+ \draw[color=black, line width=1] (4.000000, 2.000000) --
2808
+ (4.000000, 0.000000);
2809
+ \draw[color=black, line width=1] (0.000000, 4.000000) --
2810
+ (1.000000, 4.000000);
2811
+ \draw[color=black, line width=1] (2.000000, 0.000000) --
2812
+ (4.000000, 0.000000);
2813
+ \draw[color=black, line width=1] (1.000000, 4.000000) --
2814
+ (1.000000, 1.000000);
2815
+ \draw[color=black, line width=1] (2.000000, 3.000000) --
2816
+ (2.000000, 0.000000);
2817
+ \draw[color=black, line width=1] (3.000000, 3.000000) --
2818
+ (3.000000, 0.000000);
2819
+ \draw[color=black, line width=1] (0.000000, 3.000000) --
2820
+ (3.000000, 3.000000);
2821
+ \draw[color=black, line width=1] (0.000000, 2.000000) --
2822
+ (4.000000, 2.000000);
2823
+ \draw[color=black, line width=1] (0.000000, 1.000000) --
2824
+ (4.000000, 1.000000);
2825
+ \draw[color=red, line width=2] (1.000000, 4.000000) --
2826
+ (1.000000, 1.000000);
2827
+ \draw[color=red, line width=2] (1.000000, 1.000000) --
2828
+ (4.000000, 1.000000);
2829
+ \draw[color=red, line width=2] (4.000000, 1.000000) --
2830
+ (4.000000, 0.000000);
2831
+ """
2832
+ if directions is None:
2833
+ directions = [0, 1]
2834
+ res = ""
2835
+ tikz_options = self.get_tikz_options()
2836
+ grid_height = self.height() + 1
2837
+ drawing_tool = _drawing_tool(
2838
+ tikz_options,
2839
+ XY=lambda v: [v[0], grid_height-1-v[1]]
2840
+ )
2841
+
2842
+ def draw_bounce(direction, color):
2843
+ r"""
2844
+ Return the TIKZ code of the bounce path of ``self``.
2845
+
2846
+ See :meth:`ParallelogramPolyomino.bounce_path` for more information
2847
+ about the bounce.
2848
+ """
2849
+ if (len(self.bounce_path(direction)) >
2850
+ len(self.bounce_path(1 - direction))):
2851
+ increase_size_line = 1
2852
+ else:
2853
+ increase_size_line = 0
2854
+ res = ""
2855
+ bp = self.bounce_path(direction)
2856
+ pos = [0, 0]
2857
+ pos[1-direction] += 1
2858
+ old = list(pos)
2859
+ for e in bp:
2860
+ pos[direction] += e
2861
+ res += drawing_tool.draw_line(
2862
+ [old[1], old[0]], [pos[1], pos[0]],
2863
+ color=color,
2864
+ size=2*tikz_options['line_size'] + increase_size_line,
2865
+ )
2866
+ old[0], old[1] = pos
2867
+ direction = 1-direction
2868
+ return res
2869
+ if len(self.bounce_path(0)) > len(self.bounce_path(1)):
2870
+ if 0 in directions:
2871
+ res += draw_bounce(0, tikz_options['color_bounce_0'])
2872
+ if 1 in directions:
2873
+ res += draw_bounce(1, tikz_options['color_bounce_1'])
2874
+ else:
2875
+ if 1 in directions:
2876
+ res += draw_bounce(1, tikz_options['color_bounce_1'])
2877
+ if 0 in directions:
2878
+ res += draw_bounce(0, tikz_options['color_bounce_0'])
2879
+ return res
2880
+
2881
+ def _to_tikz_tree(self):
2882
+ r"""
2883
+ Return the tikz code to display a node inside the boxes which are
2884
+ nodes.
2885
+ See :meth:`ParallelogramPolyomino.box_is_node` for more information.
2886
+
2887
+ TESTS::
2888
+
2889
+ sage: # needs sage.plot sage.symbolic
2890
+ sage: pp = ParallelogramPolyomino(
2891
+ ....: [[0, 0, 0, 1, 1, 0, 1, 1], [1, 0, 1, 1, 0, 1, 0, 0]]
2892
+ ....: )
2893
+ sage: pp.to_tikz() == pp._to_tikz_tree()
2894
+ False
2895
+ sage: pp.set_options(
2896
+ ....: drawing_components=dict(diagram=False, tree=True)
2897
+ ....: )
2898
+ sage: pp.to_tikz() == pp._to_tikz_tree()
2899
+ True
2900
+ sage: pp.set_options(
2901
+ ....: drawing_components=dict(diagram=True, tree=True)
2902
+ ....: )
2903
+ sage: print(pp.to_tikz()) # indirect doctest
2904
+ <BLANKLINE>
2905
+ \draw[color=black, line width=1] (0.000000, 4.000000) --
2906
+ (0.000000, 1.000000);
2907
+ \draw[color=black, line width=1] (4.000000, 2.000000) --
2908
+ (4.000000, 0.000000);
2909
+ \draw[color=black, line width=1] (0.000000, 4.000000) --
2910
+ (1.000000, 4.000000);
2911
+ \draw[color=black, line width=1] (2.000000, 0.000000) --
2912
+ (4.000000, 0.000000);
2913
+ \draw[color=black, line width=1] (1.000000, 4.000000) --
2914
+ (1.000000, 1.000000);
2915
+ \draw[color=black, line width=1] (2.000000, 3.000000) --
2916
+ (2.000000, 0.000000);
2917
+ \draw[color=black, line width=1] (3.000000, 3.000000) --
2918
+ (3.000000, 0.000000);
2919
+ \draw[color=black, line width=1] (0.000000, 3.000000) --
2920
+ (3.000000, 3.000000);
2921
+ \draw[color=black, line width=1] (0.000000, 2.000000) --
2922
+ (4.000000, 2.000000);
2923
+ \draw[color=black, line width=1] (0.000000, 1.000000) --
2924
+ (4.000000, 1.000000);
2925
+ \filldraw[color=black] (0.500000, 2.500000) circle (3.5pt);
2926
+ \filldraw[color=black] (0.500000, 1.500000) circle (3.5pt);
2927
+ \filldraw[color=black] (2.500000, 0.500000) circle (3.5pt);
2928
+ \filldraw[color=black] (1.500000, 2.500000) circle (3.5pt);
2929
+ \filldraw[color=black] (2.500000, 2.500000) circle (3.5pt);
2930
+ \filldraw[color=black] (3.500000, 1.500000) circle (3.5pt);
2931
+ \filldraw[color=black] (0.500000, 3.500000) circle (3.5pt);
2932
+ """
2933
+ res = ""
2934
+ tikz_options = self.get_tikz_options()
2935
+ if self.size() == 1:
2936
+ return res
2937
+ grid_height = self.height() + 1
2938
+ drawing_tool = _drawing_tool(
2939
+ tikz_options,
2940
+ XY=lambda v: [v[0] + .5, grid_height-1-v[1] - .5]
2941
+ )
2942
+ for node in self.get_BS_nodes():
2943
+ res += drawing_tool.draw_point([node[1], node[0]])
2944
+ res += drawing_tool.draw_point([0, 0])
2945
+ return res
2946
+
2947
+ def _get_node_position_at_row(self, row):
2948
+ r"""
2949
+ Return the position of the leftmost cell in the row indexed by ``row``
2950
+ of the array obtained with ``get_array``.
2951
+
2952
+ INPUT:
2953
+
2954
+ - ``row`` -- the index of the row
2955
+
2956
+ OUTPUT: a [row,column] position of the cell
2957
+
2958
+ EXAMPLES::
2959
+
2960
+ sage: # needs sage.plot
2961
+ sage: pp = ParallelogramPolyomino(
2962
+ ....: [
2963
+ ....: [0, 0, 0, 0, 1, 0, 1, 0, 1],
2964
+ ....: [1, 0, 0, 0, 1, 1, 0, 0, 0]
2965
+ ....: ]
2966
+ ....: )
2967
+ sage: matrix(pp.get_array())
2968
+ [1 0 0]
2969
+ [1 0 0]
2970
+ [1 0 0]
2971
+ [1 1 1]
2972
+ [0 1 1]
2973
+ [0 0 1]
2974
+ sage: pp._get_node_position_at_row(0)
2975
+ [0, 0]
2976
+ sage: pp._get_node_position_at_row(1)
2977
+ [1, 0]
2978
+ sage: pp._get_node_position_at_row(2)
2979
+ [2, 0]
2980
+ sage: pp._get_node_position_at_row(3)
2981
+ [3, 0]
2982
+ sage: pp._get_node_position_at_row(4)
2983
+ [4, 1]
2984
+ sage: pp._get_node_position_at_row(5)
2985
+ [5, 2]
2986
+ """
2987
+ h = row
2988
+ for w in range(self.width()):
2989
+ if self[h][w] == 1:
2990
+ return [h, w]
2991
+ return None
2992
+
2993
+ def _get_node_position_at_column(self, column):
2994
+ r"""
2995
+ Return the position of the topmost cell in the column indexed by
2996
+ ``column`` of the array obtained with ``get_array``.
2997
+
2998
+ INPUT:
2999
+
3000
+ - ``column`` -- the index of the column
3001
+
3002
+ OUTPUT: a [row,column] position of the cell
3003
+
3004
+ EXAMPLES::
3005
+
3006
+ sage: pp = ParallelogramPolyomino(
3007
+ ....: [[0, 0, 1, 0, 0, 0, 1, 1], [1, 0, 1, 1, 0, 0, 0, 0]]
3008
+ ....: )
3009
+ sage: matrix(pp.get_array())
3010
+ [1 0 0]
3011
+ [1 1 1]
3012
+ [0 1 1]
3013
+ [0 1 1]
3014
+ [0 1 1]
3015
+ sage: pp._get_node_position_at_column(0)
3016
+ [0, 0]
3017
+ sage: pp._get_node_position_at_column(1)
3018
+ [1, 1]
3019
+ sage: pp._get_node_position_at_column(2)
3020
+ [1, 2]
3021
+ """
3022
+ w = column
3023
+ for h in range(self.height()):
3024
+ if self[h][w] == 1:
3025
+ return [h, w]
3026
+ return None
3027
+
3028
+ def get_node_position_from_box(self, box_position, direction, nb_crossed_nodes=None):
3029
+ r"""
3030
+ This function starts from a cell inside a parallelogram polyomino and
3031
+ a direction.
3032
+
3033
+ If ``direction`` is equal to 0, the function selects the column
3034
+ associated with the y-coordinate of ``box_position`` and then returns
3035
+ the topmost cell of the column that is on the top of ``box_position``
3036
+ (the cell of ``box_position`` is included).
3037
+
3038
+ If ``direction`` is equal to 1, the function selects the row
3039
+ associated with the x-coordinate of ``box_position`` and then returns
3040
+ the leftmost cell of the row that is on the left of ``box_position``.
3041
+ (the cell of ``box_position`` is included).
3042
+
3043
+ This function updates the entry of ``nb_crossed_nodes``. The function
3044
+ increases the entry of ``nb_crossed_nodes`` by the number of boxes that
3045
+ is a node (see ``box_is_node``) located on the top if ``direction``
3046
+ is 0 (resp. on the left if ``direction`` is 1) of ``box_position``
3047
+ (cell at ``box_position`` is excluded).
3048
+
3049
+ INPUT:
3050
+
3051
+ - ``box_position`` -- the position of the starting cell
3052
+
3053
+ - ``direction`` -- the direction (0 or 1)
3054
+
3055
+ - ``nb_crossed_nodes`` -- ``[0]`` (default) a list containing just one
3056
+ integer
3057
+
3058
+ OUTPUT: a [row,column] position of the cell
3059
+
3060
+ EXAMPLES::
3061
+
3062
+ sage: pp = ParallelogramPolyomino(
3063
+ ....: [[0, 0, 1, 0, 0, 0, 1, 1], [1, 0, 1, 1, 0, 0, 0, 0]]
3064
+ ....: )
3065
+ sage: matrix(pp.get_array())
3066
+ [1 0 0]
3067
+ [1 1 1]
3068
+ [0 1 1]
3069
+ [0 1 1]
3070
+ [0 1 1]
3071
+ sage: l = [0]
3072
+ sage: pp.get_node_position_from_box([3, 2], 0, l)
3073
+ [1, 2]
3074
+ sage: l
3075
+ [1]
3076
+ sage: l = [0]
3077
+ sage: pp.get_node_position_from_box([3, 2], 1, l)
3078
+ [3, 1]
3079
+ sage: l
3080
+ [1]
3081
+ sage: l = [0]
3082
+ sage: pp.get_node_position_from_box([1, 2], 0, l)
3083
+ [1, 2]
3084
+ sage: l
3085
+ [0]
3086
+ sage: l = [0]
3087
+ sage: pp.get_node_position_from_box([1, 2], 1, l)
3088
+ [1, 0]
3089
+ sage: l
3090
+ [2]
3091
+ sage: l = [0]
3092
+ sage: pp.get_node_position_from_box([3, 1], 0, l)
3093
+ [1, 1]
3094
+ sage: l
3095
+ [2]
3096
+ sage: l = [0]
3097
+ sage: pp.get_node_position_from_box([3, 1], 1, l)
3098
+ [3, 1]
3099
+ sage: l
3100
+ [0]
3101
+ """
3102
+ if nb_crossed_nodes is None:
3103
+ nb_crossed_nodes = [0]
3104
+ pos = list(box_position)
3105
+ if self[pos[0]][pos[1]] == 0:
3106
+ return None
3107
+ while self[pos[0]][pos[1]] != 0:
3108
+ pos[direction] -= 1
3109
+ if self.box_is_node(pos):
3110
+ nb_crossed_nodes[0] += 1
3111
+ pos[direction] += 1
3112
+ return pos
3113
+
3114
+ def box_is_node(self, pos) -> bool:
3115
+ r"""
3116
+ Return ``True`` if the box contains a node in the context of the
3117
+ Aval-Boussicault bijection between parallelogram polyomino and binary
3118
+ tree.
3119
+
3120
+ A box is a node if there is no cell on the top of the box in the
3121
+ same column or on the left of the box.in the same row.
3122
+
3123
+ INPUT:
3124
+
3125
+ - ``pos`` -- the [x,y] coordinate of the box
3126
+
3127
+ OUTPUT: boolean
3128
+
3129
+ EXAMPLES::
3130
+
3131
+ sage: pp = ParallelogramPolyomino(
3132
+ ....: [[0, 0, 1, 0, 0, 0, 1, 1], [1, 1, 0, 1, 0, 0, 0, 0]]
3133
+ ....: )
3134
+ sage: pp.set_options(display='drawing')
3135
+ sage: pp
3136
+ [1 1 0]
3137
+ [1 1 1]
3138
+ [0 1 1]
3139
+ [0 1 1]
3140
+ [0 1 1]
3141
+ sage: pp.box_is_node([2,1])
3142
+ True
3143
+ sage: pp.box_is_node([2,0])
3144
+ False
3145
+ sage: pp.box_is_node([1,1])
3146
+ False
3147
+ """
3148
+ if self[pos[0]][pos[1]] == 0:
3149
+ return False
3150
+ if self[pos[0] - 1][pos[1]] == 0:
3151
+ return True
3152
+ if self[pos[0]][pos[1] - 1] == 0:
3153
+ return True
3154
+ return False
3155
+
3156
+ def box_is_root(self, box) -> bool:
3157
+ r"""
3158
+ Return ``True`` if the box contains the root of the tree : it
3159
+ is the top-left box of the parallelogram polyomino.
3160
+
3161
+ INPUT:
3162
+
3163
+ - ``box`` -- the x,y coordinate of the cell
3164
+
3165
+ EXAMPLES::
3166
+
3167
+ sage: pp = ParallelogramPolyomino(
3168
+ ....: [[0, 0, 1, 0, 0, 0, 1, 1], [1, 1, 0, 1, 0, 0, 0, 0]]
3169
+ ....: )
3170
+ sage: pp.box_is_root([0, 0])
3171
+ True
3172
+ sage: pp.box_is_root([0, 1])
3173
+ False
3174
+ """
3175
+ return box[0] == 0 and box[1] == 0
3176
+
3177
+ def _get_number_of_nodes_in_the_bounding_path(self, box, direction):
3178
+ r"""
3179
+ When we draw the bounding path from ``box`` to the top-left cell of
3180
+ ``self``, the path is crossing some cells containing some nodes
3181
+ defined by the Boussicault-Socci bijection
3182
+ (see :meth:`_to_ordered_tree_Bou_Socci`).
3183
+
3184
+ This function returns a list of numbers that represent the number of
3185
+ nodes minus 1 that the path is crossing between each bounding.
3186
+ The starting box is excluded from the count of nodes.
3187
+
3188
+ This function is a specialized tool for
3189
+ :meth:`_get_path_in_pair_of_tree_from_row()` and
3190
+ :meth:`_get_path_in_pair_of_tree_from_column()`
3191
+ each number is reduced by one to compute the path in the ordered tree
3192
+ of those functions.
3193
+
3194
+ INPUT:
3195
+
3196
+ - ``box`` -- the x,y coordinate of the starting point of the bounding
3197
+ path
3198
+ - ``direction`` -- the initial direction of the bounding path (1 or 0,
3199
+ 1 for left and 0 for top)
3200
+
3201
+ EXAMPLES::
3202
+
3203
+ sage: pp = ParallelogramPolyomino(
3204
+ ....: [[0, 0, 1, 0, 0, 0, 1, 1], [1, 0, 1, 1, 0, 0, 0, 0]]
3205
+ ....: )
3206
+ sage: pp._get_number_of_nodes_in_the_bounding_path([4, 2], 1)
3207
+ [0, 0, 2, 0]
3208
+ sage: pp._get_number_of_nodes_in_the_bounding_path([3, 2], 1)
3209
+ [0, 0, 1, 0]
3210
+ sage: pp._get_number_of_nodes_in_the_bounding_path([2, 2], 1)
3211
+ [0, 0, 0, 0]
3212
+ sage: pp._get_number_of_nodes_in_the_bounding_path([1, 2], 1)
3213
+ [0, 1]
3214
+ sage: pp._get_number_of_nodes_in_the_bounding_path([4, 2], 0)
3215
+ [0, 1, 0]
3216
+ sage: pp._get_number_of_nodes_in_the_bounding_path([3, 2], 0)
3217
+ [0, 1, 0]
3218
+ sage: pp._get_number_of_nodes_in_the_bounding_path([2, 2], 0)
3219
+ [0, 1, 0]
3220
+ sage: pp._get_number_of_nodes_in_the_bounding_path([1, 2], 0)
3221
+ [0, 1, -1]
3222
+
3223
+ sage: pp._get_number_of_nodes_in_the_bounding_path([4, 1], 1)
3224
+ [0, 0, 2, -1]
3225
+ sage: pp._get_number_of_nodes_in_the_bounding_path([3, 1], 1)
3226
+ [0, 0, 1, -1]
3227
+ sage: pp._get_number_of_nodes_in_the_bounding_path([2, 1], 1)
3228
+ [0, 0, 0, -1]
3229
+ sage: pp._get_number_of_nodes_in_the_bounding_path([1, 1], 1)
3230
+ [0, 0]
3231
+ sage: pp._get_number_of_nodes_in_the_bounding_path([4, 1], 0)
3232
+ [0, 0, 2]
3233
+ sage: pp._get_number_of_nodes_in_the_bounding_path([3, 1], 0)
3234
+ [0, 0, 1]
3235
+ sage: pp._get_number_of_nodes_in_the_bounding_path([2, 1], 0)
3236
+ [0, 0, 0]
3237
+ sage: pp._get_number_of_nodes_in_the_bounding_path([1, 1], 0)
3238
+ [0, 0, -1]
3239
+
3240
+ sage: pp._get_number_of_nodes_in_the_bounding_path([1, 0], 1)
3241
+ [0, -1]
3242
+ sage: pp._get_number_of_nodes_in_the_bounding_path([0, 0], 1)
3243
+ []
3244
+ sage: pp._get_number_of_nodes_in_the_bounding_path([1, 0], 0)
3245
+ [0]
3246
+ sage: pp._get_number_of_nodes_in_the_bounding_path([0, 0], 0)
3247
+ []
3248
+ """
3249
+ path = []
3250
+ while not self.box_is_root(box):
3251
+ nb_sons = [0]
3252
+ box = self.get_node_position_from_box(box, direction, nb_sons)
3253
+ direction = 1 - direction
3254
+ path.append(nb_sons[0] - 1)
3255
+ path.reverse()
3256
+ return path
3257
+
3258
+ def _get_path_in_pair_of_tree_from_row(self, line):
3259
+ r"""
3260
+ When we draw the bounding path from the left-most cell of ``line`` to
3261
+ the top-left cell of ``self``, the path is bounding in some cells that
3262
+ are nodes in the ordered tree of the Boussicault-Socci bijection.
3263
+ This function returns the path of the bounding path inside the ordered
3264
+ tree.
3265
+
3266
+ The path in the ordered tree is encoded as a list of integers.
3267
+ The first integer represents the son choice between the sons of the
3268
+ root.
3269
+ Recursively, an integer represent the son choice between the sons of
3270
+ the current father.
3271
+
3272
+ The bijection is described in the paper [BRS2015]_
3273
+ at page 7, the first (resp. second) ordered tree is obtained by
3274
+ gluing all roots of the ordered forest F_e (resp. F_s) to a virtual
3275
+ root. An example can be read, page 8, Figure 6.
3276
+
3277
+ INPUT:
3278
+
3279
+ - ``line`` -- the x coordinate of the line
3280
+
3281
+ OUTPUT: list of integers
3282
+
3283
+ EXAMPLES::
3284
+
3285
+ sage: pp = ParallelogramPolyomino(
3286
+ ....: [[0, 0, 1, 0, 0, 0, 1, 1], [1, 0, 1, 1, 0, 0, 0, 0]]
3287
+ ....: )
3288
+ sage: pp._get_path_in_pair_of_tree_from_row(4)
3289
+ [0, 0, 2]
3290
+ sage: pp._get_path_in_pair_of_tree_from_row(3)
3291
+ [0, 0, 1]
3292
+ sage: pp._get_path_in_pair_of_tree_from_row(2)
3293
+ [0, 0, 0]
3294
+ sage: pp._get_path_in_pair_of_tree_from_row(1)
3295
+ [0]
3296
+ sage: pp._get_path_in_pair_of_tree_from_row(0)
3297
+ []
3298
+
3299
+ sage: pp = ParallelogramPolyomino(
3300
+ ....: [[0, 0, 1, 0, 0, 0, 1, 1], [1, 1, 0, 1, 0, 0, 0, 0]]
3301
+ ....: )
3302
+ sage: pp._get_path_in_pair_of_tree_from_row(4)
3303
+ [0, 2]
3304
+ sage: pp._get_path_in_pair_of_tree_from_row(3)
3305
+ [0, 1]
3306
+ sage: pp._get_path_in_pair_of_tree_from_row(2)
3307
+ [0, 0]
3308
+ sage: pp._get_path_in_pair_of_tree_from_row(1)
3309
+ [0]
3310
+ sage: pp._get_path_in_pair_of_tree_from_row(0)
3311
+ []
3312
+ """
3313
+ pos = self._get_node_position_at_row(line)
3314
+ return self._get_number_of_nodes_in_the_bounding_path(pos, 0)
3315
+
3316
+ def _get_path_in_pair_of_tree_from_column(self, column):
3317
+ r"""
3318
+ When we draw the bounding path from the top-most cell of ``column``
3319
+ to the top-left cell of ``self``, the path is bounding in some cells
3320
+ that are nodes in the ordered tree of the Boussicault-Socci bijection.
3321
+ This function returns the path of the bounding path inside the ordered
3322
+ tree.
3323
+
3324
+ The path in the ordered tree is encoded as a list of integers.
3325
+ The first integer represents the son choice between the sons of the
3326
+ root.
3327
+ Recursively, an integer represent the son choice between the sons of
3328
+ the current father.
3329
+
3330
+ The bijection is described in the paper [BRS2015]_
3331
+ at page 7, the first (resp. second) ordered tree is obtained by
3332
+ gluing all roots of the ordered forest F_e (resp. F_s) to a virtual
3333
+ root. An example can be read, page 8, Figure 6.
3334
+
3335
+ INPUT:
3336
+
3337
+ - ``column`` -- the y coordinate of the column
3338
+
3339
+ OUTPUT: list of integers
3340
+
3341
+ EXAMPLES::
3342
+
3343
+ sage: pp = ParallelogramPolyomino(
3344
+ ....: [[0, 0, 1, 0, 0, 0, 1, 1], [1, 0, 1, 1, 0, 0, 0, 0]]
3345
+ ....: )
3346
+ sage: pp._get_path_in_pair_of_tree_from_column(2)
3347
+ [0, 1]
3348
+ sage: pp._get_path_in_pair_of_tree_from_column(1)
3349
+ [0, 0]
3350
+ sage: pp._get_path_in_pair_of_tree_from_column(0)
3351
+ []
3352
+
3353
+ sage: pp = ParallelogramPolyomino(
3354
+ ....: [[0, 0, 1, 0, 0, 0, 1, 1], [1, 1, 0, 1, 0, 0, 0, 0]]
3355
+ ....: )
3356
+ sage: pp._get_path_in_pair_of_tree_from_column(2)
3357
+ [0, 0]
3358
+ sage: pp._get_path_in_pair_of_tree_from_row(1)
3359
+ [0]
3360
+ sage: pp._get_path_in_pair_of_tree_from_row(0)
3361
+ []
3362
+ """
3363
+ pos = self._get_node_position_at_column(column)
3364
+ return self._get_number_of_nodes_in_the_bounding_path(pos, 1)
3365
+
3366
+ def get_BS_nodes(self):
3367
+ r"""
3368
+ Return the list of cells containing node of the left and right planar
3369
+ tree in the Boussicault-Socci bijection.
3370
+
3371
+ EXAMPLES::
3372
+
3373
+ sage: pp = ParallelogramPolyomino(
3374
+ ....: [[0, 0, 1, 0, 0, 0, 1, 1], [1, 1, 0, 1, 0, 0, 0, 0]]
3375
+ ....: )
3376
+ sage: pp.set_options(display='drawing')
3377
+ sage: pp
3378
+ [1 1 0]
3379
+ [1 1 1]
3380
+ [0 1 1]
3381
+ [0 1 1]
3382
+ [0 1 1]
3383
+ sage: sorted(pp.get_BS_nodes())
3384
+ [[0, 1], [1, 0], [1, 2], [2, 1], [3, 1], [4, 1]]
3385
+
3386
+ You can draw the point inside the parallelogram polyomino by typing
3387
+ (the left nodes are in blue, and the right node are in red) ::
3388
+
3389
+ sage: pp.set_options(drawing_components=dict(tree=True))
3390
+ sage: view(pp) # not tested
3391
+ """
3392
+ result = [self._get_node_position_at_row(h)
3393
+ for h in range(1, self.height())]
3394
+ result.extend(self._get_node_position_at_column(w)
3395
+ for w in range(1, self.width()))
3396
+ return result
3397
+
3398
+ def get_right_BS_nodes(self):
3399
+ r"""
3400
+ Return the list of cells containing node of the right planar tree in
3401
+ the Boussicault-Socci bijection between parallelogram polyominoes
3402
+ and pair of ordered trees.
3403
+
3404
+ EXAMPLES::
3405
+
3406
+ sage: pp = ParallelogramPolyomino(
3407
+ ....: [[0, 0, 1, 0, 0, 0, 1, 1], [1, 1, 0, 1, 0, 0, 0, 0]]
3408
+ ....: )
3409
+ sage: pp.set_options(display='drawing')
3410
+ sage: pp
3411
+ [1 1 0]
3412
+ [1 1 1]
3413
+ [0 1 1]
3414
+ [0 1 1]
3415
+ [0 1 1]
3416
+ sage: sorted(pp.get_right_BS_nodes())
3417
+ [[1, 0], [1, 2]]
3418
+
3419
+ sage: pp = ParallelogramPolyomino(
3420
+ ....: [[0, 0, 1, 0, 0, 0, 1, 1], [1, 0, 1, 1, 0, 0, 0, 0]]
3421
+ ....: )
3422
+ sage: pp.set_options(display='drawing')
3423
+ sage: pp
3424
+ [1 0 0]
3425
+ [1 1 1]
3426
+ [0 1 1]
3427
+ [0 1 1]
3428
+ [0 1 1]
3429
+ sage: sorted(pp.get_right_BS_nodes())
3430
+ [[1, 0], [1, 1], [1, 2], [2, 1], [3, 1], [4, 1]]
3431
+
3432
+ You can draw the point inside the parallelogram polyomino by typing,
3433
+ (the left nodes are in blue, and the right node are in red) ::
3434
+
3435
+ sage: pp.set_options(drawing_components=dict(tree=True))
3436
+ sage: view(pp) # not tested
3437
+ """
3438
+ result = []
3439
+ for h in range(1, self.height()):
3440
+ path2 = self._get_path_in_pair_of_tree_from_row(h)
3441
+ if len(path2) % 2 == 1:
3442
+ result.append(self._get_node_position_at_row(h))
3443
+ for w in range(1, self.width()):
3444
+ path2 = self._get_path_in_pair_of_tree_from_column(w)
3445
+ if len(path2) % 2 == 0:
3446
+ result.append(self._get_node_position_at_column(w))
3447
+ return result
3448
+
3449
+ def get_left_BS_nodes(self):
3450
+ r"""
3451
+ Return the list of cells containing node of the left planar tree in
3452
+ the Boussicault-Socci bijection between parallelogram polyominoes
3453
+ and pair of ordered trees.
3454
+
3455
+ OUTPUT: list of [row,column] position of cells
3456
+
3457
+ EXAMPLES::
3458
+
3459
+ sage: pp = ParallelogramPolyomino(
3460
+ ....: [[0, 0, 1, 0, 0, 0, 1, 1], [1, 1, 0, 1, 0, 0, 0, 0]]
3461
+ ....: )
3462
+ sage: pp.set_options(display='drawing')
3463
+ sage: pp
3464
+ [1 1 0]
3465
+ [1 1 1]
3466
+ [0 1 1]
3467
+ [0 1 1]
3468
+ [0 1 1]
3469
+ sage: sorted(pp.get_left_BS_nodes())
3470
+ [[0, 1], [2, 1], [3, 1], [4, 1]]
3471
+
3472
+ sage: pp = ParallelogramPolyomino(
3473
+ ....: [[0, 0, 1, 0, 0, 0, 1, 1], [1, 0, 1, 1, 0, 0, 0, 0]]
3474
+ ....: )
3475
+ sage: pp.set_options(display='drawing')
3476
+ sage: pp
3477
+ [1 0 0]
3478
+ [1 1 1]
3479
+ [0 1 1]
3480
+ [0 1 1]
3481
+ [0 1 1]
3482
+ sage: sorted(pp.get_left_BS_nodes())
3483
+ []
3484
+
3485
+ You can draw the point inside the parallelogram polyomino by typing
3486
+ (the left nodes are in blue, and the right node are in red) ::
3487
+
3488
+ sage: pp.set_options(drawing_components=dict(tree=True))
3489
+ sage: view(pp) # not tested
3490
+ """
3491
+ result = []
3492
+ for h in range(1, self.height()):
3493
+ path2 = self._get_path_in_pair_of_tree_from_row(h)
3494
+ if len(path2) % 2 == 0:
3495
+ result.append(self._get_node_position_at_row(h))
3496
+ for w in range(1, self.width()):
3497
+ path2 = self._get_path_in_pair_of_tree_from_column(w)
3498
+ if len(path2) % 2 == 1:
3499
+ result.append(self._get_node_position_at_column(w))
3500
+ return result
3501
+
3502
+ def to_tikz(self):
3503
+ r"""
3504
+ Return the tikz code of the parallelogram polyomino.
3505
+
3506
+ This code is the code present inside a tikz latex environment.
3507
+
3508
+ We can modify the output with the options.
3509
+
3510
+ EXAMPLES::
3511
+
3512
+ sage: # needs sage.plot sage.symbolic
3513
+ sage: pp = ParallelogramPolyomino(
3514
+ ....: [[0,0,0,1,1,0,1,0,0,1,1,1],[1,1,1,0,0,1,1,0,0,1,0,0]]
3515
+ ....: )
3516
+ sage: print(pp.to_tikz())
3517
+ <BLANKLINE>
3518
+ \draw[color=black, line width=1] (0.000000, 6.000000) --
3519
+ (0.000000, 3.000000);
3520
+ \draw[color=black, line width=1] (6.000000, 2.000000) --
3521
+ (6.000000, 0.000000);
3522
+ \draw[color=black, line width=1] (0.000000, 6.000000) --
3523
+ (3.000000, 6.000000);
3524
+ \draw[color=black, line width=1] (3.000000, 0.000000) --
3525
+ (6.000000, 0.000000);
3526
+ \draw[color=black, line width=1] (1.000000, 6.000000) --
3527
+ (1.000000, 3.000000);
3528
+ \draw[color=black, line width=1] (2.000000, 6.000000) --
3529
+ (2.000000, 2.000000);
3530
+ \draw[color=black, line width=1] (3.000000, 6.000000) --
3531
+ (3.000000, 0.000000);
3532
+ \draw[color=black, line width=1] (4.000000, 4.000000) --
3533
+ (4.000000, 0.000000);
3534
+ \draw[color=black, line width=1] (5.000000, 4.000000) --
3535
+ (5.000000, 0.000000);
3536
+ \draw[color=black, line width=1] (0.000000, 5.000000) --
3537
+ (3.000000, 5.000000);
3538
+ \draw[color=black, line width=1] (0.000000, 4.000000) --
3539
+ (5.000000, 4.000000);
3540
+ \draw[color=black, line width=1] (0.000000, 3.000000) --
3541
+ (5.000000, 3.000000);
3542
+ \draw[color=black, line width=1] (2.000000, 2.000000) --
3543
+ (6.000000, 2.000000);
3544
+ \draw[color=black, line width=1] (3.000000, 1.000000) --
3545
+ (6.000000, 1.000000);
3546
+ sage: pp.set_options(
3547
+ ....: drawing_components=dict(
3548
+ ....: diagram=True,
3549
+ ....: tree=True,
3550
+ ....: bounce_0=True,
3551
+ ....: bounce_1=True
3552
+ ....: )
3553
+ ....: )
3554
+ sage: print(pp.to_tikz())
3555
+ <BLANKLINE>
3556
+ \draw[color=black, line width=1] (0.000000, 6.000000) --
3557
+ (0.000000, 3.000000);
3558
+ \draw[color=black, line width=1] (6.000000, 2.000000) --
3559
+ (6.000000, 0.000000);
3560
+ \draw[color=black, line width=1] (0.000000, 6.000000) --
3561
+ (3.000000, 6.000000);
3562
+ \draw[color=black, line width=1] (3.000000, 0.000000) --
3563
+ (6.000000, 0.000000);
3564
+ \draw[color=black, line width=1] (1.000000, 6.000000) --
3565
+ (1.000000, 3.000000);
3566
+ \draw[color=black, line width=1] (2.000000, 6.000000) --
3567
+ (2.000000, 2.000000);
3568
+ \draw[color=black, line width=1] (3.000000, 6.000000) --
3569
+ (3.000000, 0.000000);
3570
+ \draw[color=black, line width=1] (4.000000, 4.000000) --
3571
+ (4.000000, 0.000000);
3572
+ \draw[color=black, line width=1] (5.000000, 4.000000) --
3573
+ (5.000000, 0.000000);
3574
+ \draw[color=black, line width=1] (0.000000, 5.000000) --
3575
+ (3.000000, 5.000000);
3576
+ \draw[color=black, line width=1] (0.000000, 4.000000) --
3577
+ (5.000000, 4.000000);
3578
+ \draw[color=black, line width=1] (0.000000, 3.000000) --
3579
+ (5.000000, 3.000000);
3580
+ \draw[color=black, line width=1] (2.000000, 2.000000) --
3581
+ (6.000000, 2.000000);
3582
+ \draw[color=black, line width=1] (3.000000, 1.000000) --
3583
+ (6.000000, 1.000000);
3584
+ \draw[color=blue, line width=3] (0.000000, 5.000000) --
3585
+ (3.000000, 5.000000);
3586
+ \draw[color=blue, line width=3] (3.000000, 5.000000) --
3587
+ (3.000000, 2.000000);
3588
+ \draw[color=blue, line width=3] (3.000000, 2.000000) --
3589
+ (5.000000, 2.000000);
3590
+ \draw[color=blue, line width=3] (5.000000, 2.000000) --
3591
+ (5.000000, 0.000000);
3592
+ \draw[color=blue, line width=3] (5.000000, 0.000000) --
3593
+ (6.000000, 0.000000);
3594
+ \draw[color=red, line width=2] (1.000000, 6.000000) --
3595
+ (1.000000, 3.000000);
3596
+ \draw[color=red, line width=2] (1.000000, 3.000000) --
3597
+ (5.000000, 3.000000);
3598
+ \draw[color=red, line width=2] (5.000000, 3.000000) --
3599
+ (5.000000, 0.000000);
3600
+ \draw[color=red, line width=2] (5.000000, 0.000000) --
3601
+ (6.000000, 0.000000);
3602
+ \filldraw[color=black] (0.500000, 4.500000) circle (3.5pt);
3603
+ \filldraw[color=black] (0.500000, 3.500000) circle (3.5pt);
3604
+ \filldraw[color=black] (2.500000, 2.500000) circle (3.5pt);
3605
+ \filldraw[color=black] (3.500000, 1.500000) circle (3.5pt);
3606
+ \filldraw[color=black] (3.500000, 0.500000) circle (3.5pt);
3607
+ \filldraw[color=black] (1.500000, 5.500000) circle (3.5pt);
3608
+ \filldraw[color=black] (2.500000, 5.500000) circle (3.5pt);
3609
+ \filldraw[color=black] (3.500000, 3.500000) circle (3.5pt);
3610
+ \filldraw[color=black] (4.500000, 3.500000) circle (3.5pt);
3611
+ \filldraw[color=black] (5.500000, 1.500000) circle (3.5pt);
3612
+ \filldraw[color=black] (0.500000, 5.500000) circle (3.5pt);
3613
+ """
3614
+ res = ""
3615
+ drawing_components = self.get_options()['drawing_components']
3616
+ if 'diagram' in drawing_components and drawing_components["diagram"]:
3617
+ res += self._to_tikz_diagram()
3618
+ directions = []
3619
+ if 'bounce_0' in drawing_components and drawing_components["bounce_0"]:
3620
+ directions.append(0)
3621
+ if 'bounce_1' in drawing_components and drawing_components["bounce_1"]:
3622
+ directions.append(1)
3623
+ if len(directions) != 0:
3624
+ res += self._to_tikz_bounce(directions)
3625
+ if 'tree' in drawing_components and drawing_components["tree"]:
3626
+ res += self._to_tikz_tree()
3627
+ return res
3628
+
3629
+ def geometry(self) -> list:
3630
+ r"""
3631
+ Return a pair [h, w] containing the height and the width of the
3632
+ parallelogram polyomino.
3633
+
3634
+ EXAMPLES::
3635
+
3636
+ sage: pp = ParallelogramPolyomino(
3637
+ ....: [[0, 1, 1, 1, 1], [1, 1, 1, 1, 0]]
3638
+ ....: )
3639
+ sage: pp.geometry()
3640
+ [1, 4]
3641
+
3642
+ sage: pp = ParallelogramPolyomino([[0, 1], [1, 0]])
3643
+ sage: pp.geometry()
3644
+ [1, 1]
3645
+
3646
+ sage: pp = ParallelogramPolyomino([[1], [1]])
3647
+ sage: pp.geometry()
3648
+ [0, 1]
3649
+ """
3650
+ return [self.height(), self.width()]
3651
+
3652
+ def _plot_diagram(self):
3653
+ r"""
3654
+ Return a plot of the diagram representing ``self``.
3655
+
3656
+ TESTS::
3657
+
3658
+ sage: pp = ParallelogramPolyomino(
3659
+ ....: [[0, 1, 1, 1, 1], [1, 1, 1, 1, 0]]
3660
+ ....: )
3661
+ sage: pp._plot_diagram() # needs sage.plot
3662
+ Graphics object consisting of 7 graphics primitives
3663
+
3664
+ sage: pp = ParallelogramPolyomino([
3665
+ ....: [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
3666
+ ....: [1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0]
3667
+ ....: ])
3668
+ sage: pp._plot_diagram() # needs sage.plot
3669
+ Graphics object consisting of 25 graphics primitives
3670
+ """
3671
+ G = Graphics()
3672
+
3673
+ # Draw the inner grid
3674
+ for i,u,v in zip(range(self.height()-1), self.upper_widths()[1:], self.lower_widths()):
3675
+ G += line([(u,-i-1),(v,-i-1)],rgbcolor=(0,0,0))
3676
+ for i,u,v in zip(range(self.width()-1), self.upper_heights()[1:], self.lower_heights()):
3677
+ G += line([(i+1,-u),(i+1,-v)],rgbcolor=(0,0,0))
3678
+
3679
+ # Draw the outer border
3680
+ lower_heights = [0] + self.lower_heights()
3681
+ for i in range(self.width()):
3682
+ if lower_heights[i] != lower_heights[i+1]:
3683
+ G += line([(i,-lower_heights[i]),(i,-lower_heights[i+1])],rgbcolor=(0,0,0),thickness=2)
3684
+ upper_heights = self.upper_heights() + [self.height()]
3685
+ for i in range(self.width()):
3686
+ if upper_heights[i] != upper_heights[i+1]:
3687
+ G += line([(i+1,-upper_heights[i]),(i+1,-upper_heights[i+1])],rgbcolor=(0,0,0),thickness=2)
3688
+
3689
+ lower_widths = self.lower_widths() + [self.width()]
3690
+ for i in range(self.height()):
3691
+ if lower_widths[i] != lower_widths[i+1]:
3692
+ G += line([(lower_widths[i],-i-1),(lower_widths[i+1],-i-1)],rgbcolor=(0,0,0),thickness=2)
3693
+ upper_widths = [0] + self.upper_widths()
3694
+ for i in range(self.height()):
3695
+ if upper_widths[i] != upper_widths[i+1]:
3696
+ G += line([(upper_widths[i],-i),(upper_widths[i+1],-i)],rgbcolor=(0,0,0),thickness=2)
3697
+
3698
+ return G
3699
+
3700
+ def _plot_bounce(self, directions=None):
3701
+ r"""
3702
+ Return a plot of the bounce paths of ``self``.
3703
+
3704
+ INPUT:
3705
+
3706
+ - ``directions`` -- direction(s) `0` and/or `1` of the bounce paths
3707
+
3708
+ TESTS::
3709
+
3710
+ sage: pp = ParallelogramPolyomino(
3711
+ ....: [[0, 1, 1, 1, 1], [1, 1, 1, 1, 0]]
3712
+ ....: )
3713
+ sage: pp._plot_bounce(directions=[1]) # needs sage.plot
3714
+ Graphics object consisting of 1 graphics primitive
3715
+
3716
+ sage: pp = ParallelogramPolyomino([
3717
+ ....: [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
3718
+ ....: [1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0]
3719
+ ....: ])
3720
+ sage: pp._plot_bounce(directions=[0,1]) # needs sage.plot
3721
+ Graphics object consisting of 9 graphics primitives
3722
+ """
3723
+ if directions is None:
3724
+ directions = [0, 1]
3725
+ G = Graphics()
3726
+ if 0 in directions:
3727
+ a,b = (1,0)
3728
+ for bounce,u in enumerate(self.bounce_path(direction=0)):
3729
+ if bounce & 1:
3730
+ u,v = a+u,b
3731
+ else:
3732
+ u,v = a,b+u
3733
+ G += line([(a-.1,-b),(u-.1,-v)], rgbcolor=(1,0,0), thickness=1.5)
3734
+ a,b = u,v
3735
+ if 1 in directions:
3736
+ a,b = (0,1)
3737
+ for bounce,u in enumerate(self.bounce_path(direction=1)):
3738
+ if bounce & 1:
3739
+ u,v = a,b+u
3740
+ else:
3741
+ u,v = a+u,b
3742
+ G += line([(a,-b+.1),(u,-v+.1)], rgbcolor=(0,0,1), thickness=1.5)
3743
+ a,b = u,v
3744
+ return G
3745
+
3746
+ def _plot_bounce_values(self, bounce=0):
3747
+ r"""
3748
+ Return a plot containing the value of bounce along the specified bounce path.
3749
+
3750
+ TESTS::
3751
+
3752
+ sage: pp = ParallelogramPolyomino(
3753
+ ....: [[0, 1, 1, 1, 1], [1, 1, 1, 1, 0]]
3754
+ ....: )
3755
+ sage: pp._plot_bounce_values() # needs sage.plot
3756
+ Graphics object consisting of 4 graphics primitives
3757
+
3758
+ sage: pp = ParallelogramPolyomino([
3759
+ ....: [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
3760
+ ....: [1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0]
3761
+ ....: ])
3762
+ sage: pp._plot_bounce_values(bounce=1) # needs sage.plot
3763
+ Graphics object consisting of 10 graphics primitives
3764
+ """
3765
+ G = Graphics()
3766
+
3767
+ # Bounce path from the top
3768
+ if bounce == 0:
3769
+ a,b = (0,-1)
3770
+ for bounce,u in enumerate(self.bounce_path(direction=0)):
3771
+ if bounce & 1:
3772
+ u,v = a+u,b
3773
+ else:
3774
+ u,v = a,b+u
3775
+ for i in range(a,u+1):
3776
+ for j in range(b,v+1):
3777
+ if (i,j) != (a,b):
3778
+ G += text(str(bounce//2 + 1), (i+.5,-j-.5),rgbcolor=(0,0,0))
3779
+ a,b = u,v
3780
+ #Bounce path from the left
3781
+ else:
3782
+ a,b = (-1,0)
3783
+ for bounce,u in enumerate(self.bounce_path(direction=1)):
3784
+ if bounce & 1:
3785
+ u,v = a,b+u
3786
+ else:
3787
+ u,v = a+u,b
3788
+ for i in range(a,u+1):
3789
+ for j in range(b,v+1):
3790
+ if (i,j) != (a,b):
3791
+ G += text(str(bounce//2 + 1), (i+.5,-j-.5),rgbcolor=(0,0,0))
3792
+ a,b = u,v
3793
+ return G
3794
+
3795
+ def _plot_tree(self):
3796
+ r"""
3797
+ Return a plot of the nodes of the tree.
3798
+
3799
+ TESTS::
3800
+
3801
+ sage: pp = ParallelogramPolyomino(
3802
+ ....: [[0, 1, 1, 1, 1], [1, 1, 1, 1, 0]]
3803
+ ....: )
3804
+ sage: pp._plot_tree() # needs sage.plot
3805
+ Graphics object consisting of 2 graphics primitives
3806
+
3807
+ sage: pp = ParallelogramPolyomino([
3808
+ ....: [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
3809
+ ....: [1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0]
3810
+ ....: ])
3811
+ sage: pp._plot_tree() # needs sage.plot
3812
+ Graphics object consisting of 2 graphics primitives
3813
+ """
3814
+ G = Graphics()
3815
+ G += point(points=((v+.5,-u-.5) for u,v in self.get_BS_nodes()),size=20)
3816
+ G += point([.5, -.5],size=20)
3817
+ return G
3818
+
3819
+ def plot(self):
3820
+ r"""
3821
+ Return a plot of ``self``.
3822
+
3823
+ EXAMPLES::
3824
+
3825
+ sage: pp = ParallelogramPolyomino([[0,1],[1,0]])
3826
+ sage: pp.plot() # needs sage.plot
3827
+ Graphics object consisting of 4 graphics primitives
3828
+ sage: pp.set_options(
3829
+ ....: drawing_components=dict(
3830
+ ....: diagram=True,
3831
+ ....: bounce_0=True,
3832
+ ....: bounce_1=True,
3833
+ ....: bounce_values=0,
3834
+ ....: )
3835
+ ....: )
3836
+ sage: pp.plot() # needs sage.plot
3837
+ Graphics object consisting of 7 graphics primitives
3838
+ """
3839
+ G = Graphics()
3840
+
3841
+ drawing_components = self.get_options()['drawing_components']
3842
+ if 'diagram' in drawing_components and drawing_components["diagram"]:
3843
+ G += self._plot_diagram()
3844
+ directions = []
3845
+ if 'bounce_0' in drawing_components and drawing_components["bounce_0"]:
3846
+ directions.append(0)
3847
+ if 'bounce_1' in drawing_components and drawing_components["bounce_1"]:
3848
+ directions.append(1)
3849
+ if len(directions) != 0:
3850
+ G += self._plot_bounce(directions)
3851
+ if 'bounce_values' in drawing_components and drawing_components["bounce_values"] is not False:
3852
+ G += self._plot_bounce_values()
3853
+ if 'tree' in drawing_components and drawing_components["tree"]:
3854
+ G += self._plot_tree()
3855
+
3856
+ G.set_aspect_ratio(1)
3857
+ G.axes(False)
3858
+ return G
3859
+
3860
+ def size(self) -> int:
3861
+ r"""
3862
+ Return the size of the parallelogram polyomino.
3863
+
3864
+ The size of a parallelogram polyomino is its half-perimeter.
3865
+
3866
+ EXAMPLES::
3867
+
3868
+ sage: pp = ParallelogramPolyomino(
3869
+ ....: [[0, 0, 0, 0, 1, 0, 1, 1], [1, 0, 0, 0, 1, 1, 0, 0]]
3870
+ ....: )
3871
+ sage: pp.size()
3872
+ 8
3873
+
3874
+ sage: pp = ParallelogramPolyomino([[0, 1], [1, 0]])
3875
+ sage: pp.size()
3876
+ 2
3877
+
3878
+ sage: pp = ParallelogramPolyomino([[1], [1]])
3879
+ sage: pp.size()
3880
+ 1
3881
+ """
3882
+ return len(self.upper_path())
3883
+
3884
+ def _latex_(self):
3885
+ r"""
3886
+ Return a LaTeX version of ``self``.
3887
+
3888
+ EXAMPLES::
3889
+
3890
+ sage: # needs sage.plot sage.symbolic
3891
+ sage: pp = ParallelogramPolyomino([[0,1],[1,0]])
3892
+ sage: latex(pp)
3893
+ <BLANKLINE>
3894
+ \begin{tikzpicture}[scale=1]
3895
+ ...
3896
+ \end{tikzpicture}
3897
+
3898
+ For more on the latex options, see
3899
+ :meth:`ParallelogramPolyominoes.options`.
3900
+ """
3901
+ return self.get_options()._dispatch(self, '_latex_', 'latex')
3902
+
3903
+ def _latex_drawing(self):
3904
+ r"""
3905
+ Return a LaTeX version of ``self`` in a drawing style.
3906
+
3907
+ EXAMPLES::
3908
+
3909
+ sage: # needs sage.plot sage.symbolic
3910
+ sage: pp = ParallelogramPolyomino([[0,1],[1,0]])
3911
+ sage: print(pp._latex_drawing())
3912
+ <BLANKLINE>
3913
+ \begin{tikzpicture}[scale=1]
3914
+ ...
3915
+ \end{tikzpicture}
3916
+ """
3917
+ latex.add_package_to_preamble_if_available("tikz")
3918
+ tikz_options = self.get_tikz_options()
3919
+ res = "\n\\begin{tikzpicture}[scale=%s]" % (tikz_options['scale'])
3920
+ res += self.to_tikz()
3921
+ res += "\n\\end{tikzpicture}"
3922
+ return res
3923
+
3924
+ def _latex_list(self):
3925
+ r"""
3926
+ Return a LaTeX version of ``self`` in a list style.
3927
+
3928
+ EXAMPLES::
3929
+
3930
+ sage: pp = ParallelogramPolyomino([[0,1],[1,0]])
3931
+ sage: pp._latex_list()
3932
+ '\\[[[0, 1], [1, 0]]\\]'
3933
+ """
3934
+ return "\\[%s\\]" % self._repr_list()
3935
+
3936
+
3937
+ class ParallelogramPolyominoesFactory(SetFactory):
3938
+ r"""
3939
+ The parallelogram polyominoes factory.
3940
+
3941
+ EXAMPLES::
3942
+
3943
+ sage: PPS = ParallelogramPolyominoes(size=4)
3944
+ sage: PPS
3945
+ Parallelogram polyominoes of size 4
3946
+
3947
+ sage: sorted(PPS)
3948
+ [[[0, 0, 0, 1], [1, 0, 0, 0]],
3949
+ [[0, 0, 1, 1], [1, 0, 1, 0]],
3950
+ [[0, 0, 1, 1], [1, 1, 0, 0]],
3951
+ [[0, 1, 0, 1], [1, 1, 0, 0]],
3952
+ [[0, 1, 1, 1], [1, 1, 1, 0]]]
3953
+
3954
+ sage: PPS = ParallelogramPolyominoes()
3955
+ sage: PPS
3956
+ Parallelogram polyominoes
3957
+ sage: PPS.cardinality()
3958
+ +Infinity
3959
+ """
3960
+
3961
+ def __call__(self, size=None, policy=None):
3962
+ r"""
3963
+ Return a family of parallelogram polyominoes enumerated with the
3964
+ parameter constraints.
3965
+
3966
+ INPUT:
3967
+
3968
+ - ``size`` -- integer (default: ``None``); the size of the parallelogram
3969
+ polyominoes contained in the family.
3970
+ If set to ``None``, the family returned contains all
3971
+ the parallelogram polyominoes.
3972
+
3973
+ EXAMPLES::
3974
+
3975
+ sage: PPS = ParallelogramPolyominoes(size=4)
3976
+ sage: PPS
3977
+ Parallelogram polyominoes of size 4
3978
+ sage: sorted(PPS)
3979
+ [[[0, 0, 0, 1], [1, 0, 0, 0]],
3980
+ [[0, 0, 1, 1], [1, 0, 1, 0]],
3981
+ [[0, 0, 1, 1], [1, 1, 0, 0]],
3982
+ [[0, 1, 0, 1], [1, 1, 0, 0]],
3983
+ [[0, 1, 1, 1], [1, 1, 1, 0]]]
3984
+
3985
+ sage: PPS = ParallelogramPolyominoes()
3986
+ sage: PPS
3987
+ Parallelogram polyominoes
3988
+ sage: PPS.cardinality()
3989
+ +Infinity
3990
+
3991
+ sage: PPS = ParallelogramPolyominoes(size=None)
3992
+ sage: PPS
3993
+ Parallelogram polyominoes
3994
+ sage: PPS.cardinality()
3995
+ +Infinity
3996
+ """
3997
+ if policy is None:
3998
+ policy = self._default_policy
3999
+
4000
+ if isinstance(size, (Integer, int)):
4001
+ return ParallelogramPolyominoes_size(size, policy)
4002
+ if size is None:
4003
+ return ParallelogramPolyominoes_all(policy)
4004
+ raise ValueError("invalid argument for Parallelogram Polyominoes "
4005
+ "Factory")
4006
+
4007
+ @lazy_attribute
4008
+ def _default_policy(self):
4009
+ r"""
4010
+ Return a default policy.
4011
+
4012
+ EXAMPLES::
4013
+
4014
+ sage: str(ParallelogramPolyominoes._default_policy) == (
4015
+ ....: "Set factory policy for " +
4016
+ ....: "<class 'sage.combinat.parallelogram_polyomino" +
4017
+ ....: ".ParallelogramPolyomino'> " +
4018
+ ....: "with parent Parallelogram polyominoes" +
4019
+ ....: "[=Factory for parallelogram polyominoes(())]"
4020
+ ....: )
4021
+ True
4022
+ """
4023
+ return TopMostParentPolicy(self, (), ParallelogramPolyomino)
4024
+
4025
+ def _repr_(self) -> str:
4026
+ r"""
4027
+ Return the string representation of the parallelogram polyominoes
4028
+ factory.
4029
+
4030
+ EXAMPLES::
4031
+
4032
+ sage: ParallelogramPolyominoes
4033
+ Factory for parallelogram polyominoes
4034
+ """
4035
+ return "Factory for parallelogram polyominoes"
4036
+
4037
+
4038
+ ParallelogramPolyominoes = ParallelogramPolyominoesFactory()
4039
+ ParallelogramPolyominoes.__doc__ = \
4040
+ ParallelogramPolyominoesFactory.__call__.__doc__
4041
+
4042
+
4043
+ class ParallelogramPolyominoes_size(
4044
+ ParentWithSetFactory, UniqueRepresentation
4045
+ ):
4046
+ r"""
4047
+ The parallelogram polyominoes of size `n`.
4048
+
4049
+ EXAMPLES::
4050
+
4051
+ sage: PPS = ParallelogramPolyominoes(4)
4052
+ sage: PPS
4053
+ Parallelogram polyominoes of size 4
4054
+ sage: sorted(PPS)
4055
+ [[[0, 0, 0, 1], [1, 0, 0, 0]],
4056
+ [[0, 0, 1, 1], [1, 0, 1, 0]],
4057
+ [[0, 0, 1, 1], [1, 1, 0, 0]],
4058
+ [[0, 1, 0, 1], [1, 1, 0, 0]],
4059
+ [[0, 1, 1, 1], [1, 1, 1, 0]]]
4060
+ """
4061
+
4062
+ def __init__(self, size, policy):
4063
+ r"""
4064
+ Construct a set of Parallelogram Polyominoes of a given size.
4065
+
4066
+ EXAMPLES::
4067
+
4068
+ sage: ParallelogramPolyominoes(4)
4069
+ Parallelogram polyominoes of size 4
4070
+ """
4071
+ self._size = size
4072
+ ParentWithSetFactory.__init__(
4073
+ self, (size, ), policy, category=FiniteEnumeratedSets()
4074
+ )
4075
+
4076
+ def _repr_(self) -> str:
4077
+ r"""
4078
+ Return the string representation of the set of
4079
+ parallelogram polyominoes
4080
+
4081
+ EXAMPLES::
4082
+
4083
+ sage: ParallelogramPolyominoes(4)
4084
+ Parallelogram polyominoes of size 4
4085
+ """
4086
+ return "Parallelogram polyominoes of size %s" % (self._size)
4087
+
4088
+ def _an_element_(self):
4089
+ r"""
4090
+ Return an element of a parallelogram polyomino of a given size.
4091
+
4092
+ EXAMPLES::
4093
+
4094
+ sage: PPS = ParallelogramPolyominoes(4)
4095
+ sage: PPS.an_element() in PPS
4096
+ True
4097
+ """
4098
+ return next(self.__iter__())
4099
+
4100
+ def check_element(self, el, check):
4101
+ r"""
4102
+ Check is a given element `el` is in the set of parallelogram
4103
+ polyominoes of a fixed size.
4104
+
4105
+ EXAMPLES::
4106
+
4107
+ sage: PPS = ParallelogramPolyominoes(3)
4108
+ sage: ParallelogramPolyomino( # indirect doctest
4109
+ ....: [[0, 1, 1], [1, 1, 0]]
4110
+ ....: ) in PPS
4111
+ True
4112
+ """
4113
+ if el.size() != self.size():
4114
+ raise ValueError(
4115
+ "the parallelogram polyomino has a wrong size: %s" % el.size())
4116
+
4117
+ def cardinality(self):
4118
+ r"""
4119
+ Return the number of parallelogram polyominoes.
4120
+
4121
+ The number of parallelogram polyominoes of size n is given by
4122
+ the Catalan number `c_{n-1}`.
4123
+
4124
+ EXAMPLES::
4125
+
4126
+ sage: ParallelogramPolyominoes(1).cardinality()
4127
+ 1
4128
+ sage: ParallelogramPolyominoes(2).cardinality()
4129
+ 1
4130
+ sage: ParallelogramPolyominoes(3).cardinality()
4131
+ 2
4132
+ sage: ParallelogramPolyominoes(4).cardinality()
4133
+ 5
4134
+
4135
+ sage: all(
4136
+ ....: ParallelogramPolyominoes(i).cardinality()
4137
+ ....: == len(list(ParallelogramPolyominoes(i)))
4138
+ ....: for i in range(1,7)
4139
+ ....: )
4140
+ True
4141
+ """
4142
+ return catalan_number(self.size() - 1)
4143
+
4144
+ def __iter__(self):
4145
+ r"""
4146
+ Return a parallelogram polyomino generator.
4147
+
4148
+ EXAMPLES::
4149
+
4150
+ sage: len(list(ParallelogramPolyominoes(4))) == 5
4151
+ True
4152
+ sage: all(
4153
+ ....: pp in ParallelogramPolyominoes()
4154
+ ....: for pp in ParallelogramPolyominoes(4)
4155
+ ....: )
4156
+ True
4157
+ """
4158
+ from sage.combinat.dyck_word import DyckWords
4159
+ for dyck in DyckWords(self.size() - 1):
4160
+ yield ParallelogramPolyomino.from_dyck_word(dyck)
4161
+
4162
+ def get_options(self):
4163
+ r"""
4164
+ Return all the options associated with all the elements of
4165
+ the set of parallelogram polyominoes with a fixed size.
4166
+
4167
+ EXAMPLES::
4168
+
4169
+ sage: pps = ParallelogramPolyominoes(5)
4170
+ sage: pps.get_options()
4171
+ Current options for ParallelogramPolyominoes_size
4172
+ - display: 'list'
4173
+ ...
4174
+ """
4175
+ return self.options
4176
+
4177
+ def size(self):
4178
+ r"""
4179
+ Return the size of the parallelogram polyominoes generated by this
4180
+ parent.
4181
+
4182
+ EXAMPLES::
4183
+
4184
+ sage: ParallelogramPolyominoes(0).size()
4185
+ 0
4186
+ sage: ParallelogramPolyominoes(1).size()
4187
+ 1
4188
+ sage: ParallelogramPolyominoes(5).size()
4189
+ 5
4190
+ """
4191
+ return self._size
4192
+
4193
+ def set_options(self, *get_value, **set_value):
4194
+ r"""
4195
+ Set new options to the object.
4196
+
4197
+ EXAMPLES::
4198
+
4199
+ sage: PPS = ParallelogramPolyominoes(3)
4200
+ sage: PPS.set_options(
4201
+ ....: drawing_components=dict(
4202
+ ....: diagram = True,
4203
+ ....: bounce_0 = True,
4204
+ ....: bounce_1 = True,
4205
+ ....: )
4206
+ ....: )
4207
+ sage: pp = PPS[0]
4208
+ sage: view(pp) # not tested
4209
+ """
4210
+ self.options(*get_value, **set_value)
4211
+
4212
+ options = ParallelogramPolyominoesOptions
4213
+ r"""
4214
+ The options for ParallelogramPolyominoes.
4215
+ """
4216
+
4217
+
4218
+ class ParallelogramPolyominoes_all(
4219
+ ParentWithSetFactory, DisjointUnionEnumeratedSets
4220
+ ):
4221
+ r"""
4222
+ This class enumerates all the parallelogram polyominoes.
4223
+
4224
+ EXAMPLES::
4225
+
4226
+ sage: PPS = ParallelogramPolyominoes()
4227
+ sage: PPS
4228
+ Parallelogram polyominoes
4229
+ """
4230
+
4231
+ def __init__(self, policy):
4232
+ r"""
4233
+ Construct the set of all parallelogram polyominoes.
4234
+
4235
+ EXAMPLES::
4236
+
4237
+ sage: PPS = ParallelogramPolyominoes()
4238
+ sage: PPS
4239
+ Parallelogram polyominoes
4240
+
4241
+ sage: ParallelogramPolyomino([[0, 1, 1], [1, 1, 0]]) in PPS
4242
+ True
4243
+
4244
+ sage: PPS = ParallelogramPolyominoes()
4245
+ sage: next(PPS.__iter__()) in PPS
4246
+ True
4247
+ """
4248
+ ParentWithSetFactory.__init__(
4249
+ self, (), policy, category=FiniteEnumeratedSets()
4250
+ )
4251
+ DisjointUnionEnumeratedSets.__init__(
4252
+ self, Family(
4253
+ PositiveIntegers(),
4254
+ lambda n: ParallelogramPolyominoes_size(
4255
+ n, policy=self.facade_policy()
4256
+ )
4257
+ ),
4258
+ facade=True, keepkey=False, category=self.category()
4259
+ )
4260
+
4261
+ def _repr_(self) -> str:
4262
+ r"""
4263
+ Return a string representation of the set of parallelogram polyominoes.
4264
+
4265
+ EXAMPLES::
4266
+
4267
+ sage: PPS = ParallelogramPolyominoes()
4268
+ sage: PPS
4269
+ Parallelogram polyominoes
4270
+ """
4271
+ return "Parallelogram polyominoes"
4272
+
4273
+ def check_element(self, el, check):
4274
+ r"""
4275
+ Check is a given element `el` is in the set of parallelogram
4276
+ polyominoes.
4277
+
4278
+ EXAMPLES::
4279
+
4280
+ sage: PPS = ParallelogramPolyominoes()
4281
+ sage: ParallelogramPolyomino( # indirect doctest
4282
+ ....: [[0, 1, 1], [1, 1, 0]]
4283
+ ....: ) in PPS
4284
+ True
4285
+ """
4286
+ pass
4287
+
4288
+ def get_options(self):
4289
+ r"""
4290
+ Return all the options associated with the set of
4291
+ parallelogram polyominoes.
4292
+
4293
+ EXAMPLES::
4294
+
4295
+ sage: PPS = ParallelogramPolyominoes()
4296
+ sage: options = PPS.get_options()
4297
+ sage: options
4298
+ Current options for ParallelogramPolyominoes_size
4299
+ - display: 'list'
4300
+ ...
4301
+ """
4302
+ return self.options
4303
+
4304
+ def set_options(self, *get_value, **set_value):
4305
+ r"""
4306
+ Set new options to the object.
4307
+
4308
+ EXAMPLES::
4309
+
4310
+ sage: PPS = ParallelogramPolyominoes()
4311
+ sage: PPS.set_options(
4312
+ ....: drawing_components=dict(
4313
+ ....: diagram = True,
4314
+ ....: bounce_0 = True,
4315
+ ....: bounce_1 = True,
4316
+ ....: )
4317
+ ....: )
4318
+ sage: pp = next(iter(PPS))
4319
+ sage: view(pp) # not tested
4320
+ """
4321
+ self.options(*get_value, **set_value)
4322
+
4323
+ options = ParallelogramPolyominoesOptions
4324
+ r"""
4325
+ The options for ParallelogramPolyominoes.
4326
+ """