passagemath-combinat 10.6.42__cp314-cp314t-win_amd64.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.
- passagemath_combinat/__init__.py +3 -0
- passagemath_combinat-10.6.42.dist-info/DELVEWHEEL +2 -0
- passagemath_combinat-10.6.42.dist-info/METADATA +160 -0
- passagemath_combinat-10.6.42.dist-info/RECORD +401 -0
- passagemath_combinat-10.6.42.dist-info/WHEEL +5 -0
- passagemath_combinat-10.6.42.dist-info/top_level.txt +3 -0
- passagemath_combinat.libs/libgmp-10-3a5f019e2510aeaad918cab2b57a689d.dll +0 -0
- passagemath_combinat.libs/libsymmetrica-3-7dcf900932804d0df5fd0919b4668720.dll +0 -0
- sage/algebras/affine_nil_temperley_lieb.py +263 -0
- sage/algebras/all.py +24 -0
- sage/algebras/all__sagemath_combinat.py +35 -0
- sage/algebras/askey_wilson.py +935 -0
- sage/algebras/associated_graded.py +345 -0
- sage/algebras/cellular_basis.py +350 -0
- sage/algebras/cluster_algebra.py +2766 -0
- sage/algebras/down_up_algebra.py +860 -0
- sage/algebras/free_algebra.py +1698 -0
- sage/algebras/free_algebra_element.py +345 -0
- sage/algebras/free_algebra_quotient.py +405 -0
- sage/algebras/free_algebra_quotient_element.py +295 -0
- sage/algebras/free_zinbiel_algebra.py +885 -0
- sage/algebras/hall_algebra.py +783 -0
- sage/algebras/hecke_algebras/all.py +4 -0
- sage/algebras/hecke_algebras/ariki_koike_algebra.py +1796 -0
- sage/algebras/hecke_algebras/ariki_koike_specht_modules.py +475 -0
- sage/algebras/hecke_algebras/cubic_hecke_algebra.py +3520 -0
- sage/algebras/hecke_algebras/cubic_hecke_base_ring.py +1473 -0
- sage/algebras/hecke_algebras/cubic_hecke_matrix_rep.py +1079 -0
- sage/algebras/iwahori_hecke_algebra.py +3095 -0
- sage/algebras/jordan_algebra.py +1773 -0
- sage/algebras/lie_conformal_algebras/abelian_lie_conformal_algebra.py +113 -0
- sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra.py +156 -0
- sage/algebras/lie_conformal_algebras/all.py +18 -0
- sage/algebras/lie_conformal_algebras/bosonic_ghosts_lie_conformal_algebra.py +134 -0
- sage/algebras/lie_conformal_algebras/examples.py +43 -0
- sage/algebras/lie_conformal_algebras/fermionic_ghosts_lie_conformal_algebra.py +131 -0
- sage/algebras/lie_conformal_algebras/finitely_freely_generated_lca.py +139 -0
- sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py +174 -0
- sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py +167 -0
- sage/algebras/lie_conformal_algebras/freely_generated_lie_conformal_algebra.py +107 -0
- sage/algebras/lie_conformal_algebras/graded_lie_conformal_algebra.py +135 -0
- sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py +353 -0
- sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py +236 -0
- sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_basis.py +78 -0
- sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py +328 -0
- sage/algebras/lie_conformal_algebras/n2_lie_conformal_algebra.py +117 -0
- sage/algebras/lie_conformal_algebras/neveu_schwarz_lie_conformal_algebra.py +86 -0
- sage/algebras/lie_conformal_algebras/virasoro_lie_conformal_algebra.py +82 -0
- sage/algebras/lie_conformal_algebras/weyl_lie_conformal_algebra.py +205 -0
- sage/algebras/nil_coxeter_algebra.py +191 -0
- sage/algebras/q_commuting_polynomials.py +673 -0
- sage/algebras/q_system.py +608 -0
- sage/algebras/quantum_clifford.py +959 -0
- sage/algebras/quantum_groups/ace_quantum_onsager.py +693 -0
- sage/algebras/quantum_groups/all.py +9 -0
- sage/algebras/quantum_groups/fock_space.py +2219 -0
- sage/algebras/quantum_groups/q_numbers.py +207 -0
- sage/algebras/quantum_groups/quantum_group_gap.py +2695 -0
- sage/algebras/quantum_groups/representations.py +591 -0
- sage/algebras/quantum_matrix_coordinate_algebra.py +1006 -0
- sage/algebras/quantum_oscillator.py +623 -0
- sage/algebras/quaternion_algebra.py +20 -0
- sage/algebras/quaternion_algebra_element.py +55 -0
- sage/algebras/rational_cherednik_algebra.py +525 -0
- sage/algebras/schur_algebra.py +670 -0
- sage/algebras/shuffle_algebra.py +1011 -0
- sage/algebras/splitting_algebra.py +779 -0
- sage/algebras/tensor_algebra.py +709 -0
- sage/algebras/yangian.py +1082 -0
- sage/algebras/yokonuma_hecke_algebra.py +1018 -0
- sage/all__sagemath_combinat.py +44 -0
- sage/combinat/SJT.py +255 -0
- sage/combinat/affine_permutation.py +2405 -0
- sage/combinat/algebraic_combinatorics.py +55 -0
- sage/combinat/all.py +53 -0
- sage/combinat/all__sagemath_combinat.py +195 -0
- sage/combinat/alternating_sign_matrix.py +2063 -0
- sage/combinat/baxter_permutations.py +346 -0
- sage/combinat/bijectionist.py +3220 -0
- sage/combinat/binary_recurrence_sequences.py +1180 -0
- sage/combinat/blob_algebra.py +685 -0
- sage/combinat/catalog_partitions.py +27 -0
- sage/combinat/chas/all.py +23 -0
- sage/combinat/chas/fsym.py +1180 -0
- sage/combinat/chas/wqsym.py +2601 -0
- sage/combinat/cluster_complex.py +326 -0
- sage/combinat/colored_permutations.py +2039 -0
- sage/combinat/colored_permutations_representations.py +964 -0
- sage/combinat/composition_signed.py +142 -0
- sage/combinat/composition_tableau.py +855 -0
- sage/combinat/constellation.py +1729 -0
- sage/combinat/core.py +751 -0
- sage/combinat/counting.py +12 -0
- sage/combinat/crystals/affine.py +742 -0
- sage/combinat/crystals/affine_factorization.py +518 -0
- sage/combinat/crystals/affinization.py +331 -0
- sage/combinat/crystals/alcove_path.py +2013 -0
- sage/combinat/crystals/all.py +22 -0
- sage/combinat/crystals/bkk_crystals.py +141 -0
- sage/combinat/crystals/catalog.py +115 -0
- sage/combinat/crystals/catalog_elementary_crystals.py +18 -0
- sage/combinat/crystals/catalog_infinity_crystals.py +33 -0
- sage/combinat/crystals/catalog_kirillov_reshetikhin.py +18 -0
- sage/combinat/crystals/crystals.py +257 -0
- sage/combinat/crystals/direct_sum.py +260 -0
- sage/combinat/crystals/elementary_crystals.py +1251 -0
- sage/combinat/crystals/fast_crystals.py +441 -0
- sage/combinat/crystals/fully_commutative_stable_grothendieck.py +1205 -0
- sage/combinat/crystals/generalized_young_walls.py +1076 -0
- sage/combinat/crystals/highest_weight_crystals.py +436 -0
- sage/combinat/crystals/induced_structure.py +695 -0
- sage/combinat/crystals/infinity_crystals.py +730 -0
- sage/combinat/crystals/kac_modules.py +863 -0
- sage/combinat/crystals/kirillov_reshetikhin.py +4196 -0
- sage/combinat/crystals/kyoto_path_model.py +497 -0
- sage/combinat/crystals/letters.cp314t-win_amd64.pyd +0 -0
- sage/combinat/crystals/letters.pxd +79 -0
- sage/combinat/crystals/letters.pyx +3056 -0
- sage/combinat/crystals/littelmann_path.py +1518 -0
- sage/combinat/crystals/monomial_crystals.py +1262 -0
- sage/combinat/crystals/multisegments.py +462 -0
- sage/combinat/crystals/mv_polytopes.py +467 -0
- sage/combinat/crystals/pbw_crystal.py +511 -0
- sage/combinat/crystals/pbw_datum.cp314t-win_amd64.pyd +0 -0
- sage/combinat/crystals/pbw_datum.pxd +4 -0
- sage/combinat/crystals/pbw_datum.pyx +487 -0
- sage/combinat/crystals/polyhedral_realization.py +372 -0
- sage/combinat/crystals/spins.cp314t-win_amd64.pyd +0 -0
- sage/combinat/crystals/spins.pxd +21 -0
- sage/combinat/crystals/spins.pyx +756 -0
- sage/combinat/crystals/star_crystal.py +290 -0
- sage/combinat/crystals/subcrystal.py +464 -0
- sage/combinat/crystals/tensor_product.py +1177 -0
- sage/combinat/crystals/tensor_product_element.cp314t-win_amd64.pyd +0 -0
- sage/combinat/crystals/tensor_product_element.pxd +35 -0
- sage/combinat/crystals/tensor_product_element.pyx +1870 -0
- sage/combinat/crystals/virtual_crystal.py +420 -0
- sage/combinat/cyclic_sieving_phenomenon.py +204 -0
- sage/combinat/debruijn_sequence.cp314t-win_amd64.pyd +0 -0
- sage/combinat/debruijn_sequence.pyx +355 -0
- sage/combinat/decorated_permutation.py +270 -0
- sage/combinat/degree_sequences.cp314t-win_amd64.pyd +0 -0
- sage/combinat/degree_sequences.pyx +588 -0
- sage/combinat/derangements.py +527 -0
- sage/combinat/descent_algebra.py +1008 -0
- sage/combinat/diagram.py +1551 -0
- sage/combinat/diagram_algebras.py +5886 -0
- sage/combinat/dyck_word.py +4349 -0
- sage/combinat/e_one_star.py +1623 -0
- sage/combinat/enumerated_sets.py +123 -0
- sage/combinat/expnums.cp314t-win_amd64.pyd +0 -0
- sage/combinat/expnums.pyx +148 -0
- sage/combinat/fast_vector_partitions.cp314t-win_amd64.pyd +0 -0
- sage/combinat/fast_vector_partitions.pyx +346 -0
- sage/combinat/fqsym.py +1977 -0
- sage/combinat/free_dendriform_algebra.py +954 -0
- sage/combinat/free_prelie_algebra.py +1141 -0
- sage/combinat/fully_commutative_elements.py +1077 -0
- sage/combinat/fully_packed_loop.py +1523 -0
- sage/combinat/gelfand_tsetlin_patterns.py +1409 -0
- sage/combinat/gray_codes.py +311 -0
- sage/combinat/grossman_larson_algebras.py +667 -0
- sage/combinat/growth.py +4352 -0
- sage/combinat/hall_polynomial.py +188 -0
- sage/combinat/hillman_grassl.py +866 -0
- sage/combinat/integer_matrices.py +329 -0
- sage/combinat/integer_vectors_mod_permgroup.py +1238 -0
- sage/combinat/k_tableau.py +4564 -0
- sage/combinat/kazhdan_lusztig.py +215 -0
- sage/combinat/key_polynomial.py +885 -0
- sage/combinat/knutson_tao_puzzles.py +2286 -0
- sage/combinat/lr_tableau.py +311 -0
- sage/combinat/matrices/all.py +24 -0
- sage/combinat/matrices/hadamard_matrix.py +3790 -0
- sage/combinat/matrices/latin.py +2912 -0
- sage/combinat/misc.py +401 -0
- sage/combinat/multiset_partition_into_sets_ordered.py +3541 -0
- sage/combinat/ncsf_qsym/all.py +21 -0
- sage/combinat/ncsf_qsym/combinatorics.py +317 -0
- sage/combinat/ncsf_qsym/generic_basis_code.py +1427 -0
- sage/combinat/ncsf_qsym/ncsf.py +5637 -0
- sage/combinat/ncsf_qsym/qsym.py +4053 -0
- sage/combinat/ncsf_qsym/tutorial.py +447 -0
- sage/combinat/ncsym/all.py +21 -0
- sage/combinat/ncsym/bases.py +855 -0
- sage/combinat/ncsym/dual.py +593 -0
- sage/combinat/ncsym/ncsym.py +2076 -0
- sage/combinat/necklace.py +551 -0
- sage/combinat/non_decreasing_parking_function.py +634 -0
- sage/combinat/nu_dyck_word.py +1474 -0
- sage/combinat/output.py +861 -0
- sage/combinat/parallelogram_polyomino.py +4326 -0
- sage/combinat/parking_functions.py +1602 -0
- sage/combinat/partition_algebra.py +1998 -0
- sage/combinat/partition_kleshchev.py +1982 -0
- sage/combinat/partition_shifting_algebras.py +584 -0
- sage/combinat/partition_tuple.py +3114 -0
- sage/combinat/path_tableaux/all.py +13 -0
- sage/combinat/path_tableaux/catalog.py +29 -0
- sage/combinat/path_tableaux/dyck_path.py +380 -0
- sage/combinat/path_tableaux/frieze.py +476 -0
- sage/combinat/path_tableaux/path_tableau.py +728 -0
- sage/combinat/path_tableaux/semistandard.py +510 -0
- sage/combinat/perfect_matching.py +779 -0
- sage/combinat/plane_partition.py +3300 -0
- sage/combinat/q_bernoulli.cp314t-win_amd64.pyd +0 -0
- sage/combinat/q_bernoulli.pyx +128 -0
- sage/combinat/quickref.py +81 -0
- sage/combinat/recognizable_series.py +2051 -0
- sage/combinat/regular_sequence.py +4316 -0
- sage/combinat/regular_sequence_bounded.py +543 -0
- sage/combinat/restricted_growth.py +81 -0
- sage/combinat/ribbon.py +20 -0
- sage/combinat/ribbon_shaped_tableau.py +489 -0
- sage/combinat/ribbon_tableau.py +1180 -0
- sage/combinat/rigged_configurations/all.py +46 -0
- sage/combinat/rigged_configurations/bij_abstract_class.py +548 -0
- sage/combinat/rigged_configurations/bij_infinity.py +370 -0
- sage/combinat/rigged_configurations/bij_type_A.py +163 -0
- sage/combinat/rigged_configurations/bij_type_A2_dual.py +338 -0
- sage/combinat/rigged_configurations/bij_type_A2_even.py +218 -0
- sage/combinat/rigged_configurations/bij_type_A2_odd.py +199 -0
- sage/combinat/rigged_configurations/bij_type_B.py +900 -0
- sage/combinat/rigged_configurations/bij_type_C.py +267 -0
- sage/combinat/rigged_configurations/bij_type_D.py +771 -0
- sage/combinat/rigged_configurations/bij_type_D_tri.py +392 -0
- sage/combinat/rigged_configurations/bij_type_D_twisted.py +576 -0
- sage/combinat/rigged_configurations/bij_type_E67.py +402 -0
- sage/combinat/rigged_configurations/bijection.py +143 -0
- sage/combinat/rigged_configurations/kleber_tree.py +1475 -0
- sage/combinat/rigged_configurations/kr_tableaux.py +1898 -0
- sage/combinat/rigged_configurations/rc_crystal.py +461 -0
- sage/combinat/rigged_configurations/rc_infinity.py +540 -0
- sage/combinat/rigged_configurations/rigged_configuration_element.py +2403 -0
- sage/combinat/rigged_configurations/rigged_configurations.py +1918 -0
- sage/combinat/rigged_configurations/rigged_partition.cp314t-win_amd64.pyd +0 -0
- sage/combinat/rigged_configurations/rigged_partition.pxd +15 -0
- sage/combinat/rigged_configurations/rigged_partition.pyx +680 -0
- sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py +499 -0
- sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py +428 -0
- sage/combinat/rsk.py +3438 -0
- sage/combinat/schubert_polynomial.py +508 -0
- sage/combinat/set_partition.py +3318 -0
- sage/combinat/set_partition_iterator.cp314t-win_amd64.pyd +0 -0
- sage/combinat/set_partition_iterator.pyx +136 -0
- sage/combinat/set_partition_ordered.py +1590 -0
- sage/combinat/sf/abreu_nigro.py +346 -0
- sage/combinat/sf/all.py +52 -0
- sage/combinat/sf/character.py +576 -0
- sage/combinat/sf/classical.py +319 -0
- sage/combinat/sf/dual.py +996 -0
- sage/combinat/sf/elementary.py +549 -0
- sage/combinat/sf/hall_littlewood.py +1028 -0
- sage/combinat/sf/hecke.py +336 -0
- sage/combinat/sf/homogeneous.py +464 -0
- sage/combinat/sf/jack.py +1428 -0
- sage/combinat/sf/k_dual.py +1458 -0
- sage/combinat/sf/kfpoly.py +447 -0
- sage/combinat/sf/llt.py +789 -0
- sage/combinat/sf/macdonald.py +2019 -0
- sage/combinat/sf/monomial.py +525 -0
- sage/combinat/sf/multiplicative.py +113 -0
- sage/combinat/sf/new_kschur.py +1786 -0
- sage/combinat/sf/ns_macdonald.py +964 -0
- sage/combinat/sf/orthogonal.py +246 -0
- sage/combinat/sf/orthotriang.py +355 -0
- sage/combinat/sf/powersum.py +963 -0
- sage/combinat/sf/schur.py +880 -0
- sage/combinat/sf/sf.py +1653 -0
- sage/combinat/sf/sfa.py +7053 -0
- sage/combinat/sf/symplectic.py +253 -0
- sage/combinat/sf/witt.py +721 -0
- sage/combinat/shifted_primed_tableau.py +2735 -0
- sage/combinat/shuffle.py +830 -0
- sage/combinat/sidon_sets.py +146 -0
- sage/combinat/similarity_class_type.py +1721 -0
- sage/combinat/sine_gordon.py +618 -0
- sage/combinat/six_vertex_model.py +784 -0
- sage/combinat/skew_partition.py +2053 -0
- sage/combinat/skew_tableau.py +2989 -0
- sage/combinat/sloane_functions.py +8935 -0
- sage/combinat/specht_module.py +1403 -0
- sage/combinat/species/all.py +48 -0
- sage/combinat/species/characteristic_species.py +321 -0
- sage/combinat/species/composition_species.py +273 -0
- sage/combinat/species/cycle_species.py +284 -0
- sage/combinat/species/empty_species.py +155 -0
- sage/combinat/species/functorial_composition_species.py +148 -0
- sage/combinat/species/generating_series.py +673 -0
- sage/combinat/species/library.py +148 -0
- sage/combinat/species/linear_order_species.py +169 -0
- sage/combinat/species/misc.py +83 -0
- sage/combinat/species/partition_species.py +290 -0
- sage/combinat/species/permutation_species.py +268 -0
- sage/combinat/species/product_species.py +423 -0
- sage/combinat/species/recursive_species.py +476 -0
- sage/combinat/species/set_species.py +192 -0
- sage/combinat/species/species.py +820 -0
- sage/combinat/species/structure.py +539 -0
- sage/combinat/species/subset_species.py +243 -0
- sage/combinat/species/sum_species.py +225 -0
- sage/combinat/subword.py +564 -0
- sage/combinat/subword_complex.py +2122 -0
- sage/combinat/subword_complex_c.cp314t-win_amd64.pyd +0 -0
- sage/combinat/subword_complex_c.pyx +119 -0
- sage/combinat/super_tableau.py +821 -0
- sage/combinat/superpartition.py +1154 -0
- sage/combinat/symmetric_group_algebra.py +3774 -0
- sage/combinat/symmetric_group_representations.py +1830 -0
- sage/combinat/t_sequences.py +877 -0
- sage/combinat/tableau.py +9506 -0
- sage/combinat/tableau_residues.py +860 -0
- sage/combinat/tableau_tuple.py +5353 -0
- sage/combinat/tiling.py +2432 -0
- sage/combinat/triangles_FHM.py +777 -0
- sage/combinat/tutorial.py +1857 -0
- sage/combinat/vector_partition.py +337 -0
- sage/combinat/words/abstract_word.py +1722 -0
- sage/combinat/words/all.py +59 -0
- sage/combinat/words/alphabet.py +268 -0
- sage/combinat/words/finite_word.py +7201 -0
- sage/combinat/words/infinite_word.py +113 -0
- sage/combinat/words/lyndon_word.py +652 -0
- sage/combinat/words/morphic.py +351 -0
- sage/combinat/words/morphism.py +3878 -0
- sage/combinat/words/paths.py +2932 -0
- sage/combinat/words/shuffle_product.py +278 -0
- sage/combinat/words/suffix_trees.py +1873 -0
- sage/combinat/words/word.py +769 -0
- sage/combinat/words/word_char.cp314t-win_amd64.pyd +0 -0
- sage/combinat/words/word_char.pyx +847 -0
- sage/combinat/words/word_datatypes.cp314t-win_amd64.pyd +0 -0
- sage/combinat/words/word_datatypes.pxd +4 -0
- sage/combinat/words/word_datatypes.pyx +1067 -0
- sage/combinat/words/word_generators.py +2026 -0
- sage/combinat/words/word_infinite_datatypes.py +1218 -0
- sage/combinat/words/word_options.py +99 -0
- sage/combinat/words/words.py +2396 -0
- sage/data_structures/all__sagemath_combinat.py +1 -0
- sage/databases/all__sagemath_combinat.py +13 -0
- sage/databases/findstat.py +4897 -0
- sage/databases/oeis.py +2058 -0
- sage/databases/sloane.py +393 -0
- sage/dynamics/all__sagemath_combinat.py +14 -0
- sage/dynamics/cellular_automata/all.py +7 -0
- sage/dynamics/cellular_automata/catalog.py +34 -0
- sage/dynamics/cellular_automata/elementary.py +612 -0
- sage/dynamics/cellular_automata/glca.py +477 -0
- sage/dynamics/cellular_automata/solitons.py +1463 -0
- sage/dynamics/finite_dynamical_system.py +1249 -0
- sage/dynamics/finite_dynamical_system_catalog.py +382 -0
- sage/games/all.py +7 -0
- sage/games/hexad.py +704 -0
- sage/games/quantumino.py +591 -0
- sage/games/sudoku.py +889 -0
- sage/games/sudoku_backtrack.cp314t-win_amd64.pyd +0 -0
- sage/games/sudoku_backtrack.pyx +189 -0
- sage/groups/all__sagemath_combinat.py +1 -0
- sage/groups/indexed_free_group.py +489 -0
- sage/libs/all__sagemath_combinat.py +6 -0
- sage/libs/lrcalc/__init__.py +1 -0
- sage/libs/lrcalc/lrcalc.py +525 -0
- sage/libs/symmetrica/__init__.py +7 -0
- sage/libs/symmetrica/all.py +101 -0
- sage/libs/symmetrica/kostka.pxi +168 -0
- sage/libs/symmetrica/part.pxi +193 -0
- sage/libs/symmetrica/plet.pxi +42 -0
- sage/libs/symmetrica/sab.pxi +196 -0
- sage/libs/symmetrica/sb.pxi +332 -0
- sage/libs/symmetrica/sc.pxi +192 -0
- sage/libs/symmetrica/schur.pxi +956 -0
- sage/libs/symmetrica/symmetrica.cp314t-win_amd64.pyd +0 -0
- sage/libs/symmetrica/symmetrica.pxi +1172 -0
- sage/libs/symmetrica/symmetrica.pyx +39 -0
- sage/monoids/all.py +13 -0
- sage/monoids/automatic_semigroup.py +1054 -0
- sage/monoids/free_abelian_monoid.py +315 -0
- sage/monoids/free_abelian_monoid_element.cp314t-win_amd64.pyd +0 -0
- sage/monoids/free_abelian_monoid_element.pxd +16 -0
- sage/monoids/free_abelian_monoid_element.pyx +397 -0
- sage/monoids/free_monoid.py +335 -0
- sage/monoids/free_monoid_element.py +431 -0
- sage/monoids/hecke_monoid.py +65 -0
- sage/monoids/string_monoid.py +817 -0
- sage/monoids/string_monoid_element.py +547 -0
- sage/monoids/string_ops.py +143 -0
- sage/monoids/trace_monoid.py +972 -0
- sage/rings/all__sagemath_combinat.py +2 -0
- sage/sat/all.py +4 -0
- sage/sat/boolean_polynomials.py +405 -0
- sage/sat/converters/__init__.py +6 -0
- sage/sat/converters/anf2cnf.py +14 -0
- sage/sat/converters/polybori.py +611 -0
- sage/sat/solvers/__init__.py +5 -0
- sage/sat/solvers/cryptominisat.py +287 -0
- sage/sat/solvers/dimacs.py +783 -0
- sage/sat/solvers/picosat.py +228 -0
- sage/sat/solvers/sat_lp.py +156 -0
- sage/sat/solvers/satsolver.cp314t-win_amd64.pyd +0 -0
- sage/sat/solvers/satsolver.pxd +3 -0
- 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
|
+
"""
|