passagemath-combinat 10.6.42__cp314-cp314-musllinux_1_2_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_combinat/__init__.py +3 -0
- passagemath_combinat-10.6.42.dist-info/METADATA +160 -0
- passagemath_combinat-10.6.42.dist-info/RECORD +400 -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-0e7fc84e.so.10.5.0 +0 -0
- passagemath_combinat.libs/libsymmetrica-81fe8739.so.3.0.0 +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 +35 -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.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +0 -0
- sage/combinat/debruijn_sequence.pyx +355 -0
- sage/combinat/decorated_permutation.py +270 -0
- sage/combinat/degree_sequences.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +0 -0
- sage/combinat/expnums.pyx +148 -0
- sage/combinat/fast_vector_partitions.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +0 -0
- sage/combinat/words/word_char.pyx +847 -0
- sage/combinat/words/word_datatypes.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +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.cpython-314-x86_64-linux-musl.so +0 -0
- sage/sat/solvers/satsolver.pxd +3 -0
- sage/sat/solvers/satsolver.pyx +405 -0
|
@@ -0,0 +1,1722 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-combinat
|
|
2
|
+
r"""
|
|
3
|
+
Abstract word (finite or infinite)
|
|
4
|
+
|
|
5
|
+
This module gathers functions that works for both finite and infinite
|
|
6
|
+
words.
|
|
7
|
+
|
|
8
|
+
AUTHORS:
|
|
9
|
+
|
|
10
|
+
- Sébastien Labbé
|
|
11
|
+
- Franco Saliola
|
|
12
|
+
|
|
13
|
+
EXAMPLES::
|
|
14
|
+
|
|
15
|
+
sage: a = 0.618
|
|
16
|
+
sage: g = words.CodingOfRotationWord(alpha=a, beta=1-a, x=a)
|
|
17
|
+
sage: f = words.FibonacciWord()
|
|
18
|
+
sage: p = f.longest_common_prefix(g, length='finite')
|
|
19
|
+
sage: p
|
|
20
|
+
word: 0100101001001010010100100101001001010010...
|
|
21
|
+
sage: p.length()
|
|
22
|
+
231
|
|
23
|
+
"""
|
|
24
|
+
# ****************************************************************************
|
|
25
|
+
# Copyright (C) 2008-2010 Sebastien Labbe <slabqc@gmail.com>,
|
|
26
|
+
# 2008-2010 Franco Saliola <saliola@gmail.com>
|
|
27
|
+
#
|
|
28
|
+
# This program is free software: you can redistribute it and/or modify
|
|
29
|
+
# it under the terms of the GNU General Public License as published by
|
|
30
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
31
|
+
# (at your option) any later version.
|
|
32
|
+
# https://www.gnu.org/licenses/
|
|
33
|
+
# ****************************************************************************
|
|
34
|
+
from itertools import islice, groupby
|
|
35
|
+
|
|
36
|
+
from sage.structure.sage_object import SageObject
|
|
37
|
+
from sage.combinat.words.word_options import word_options
|
|
38
|
+
from sage.rings.finite_rings.integer_mod_ring import Integers
|
|
39
|
+
from sage.rings.infinity import Infinity
|
|
40
|
+
from sage.rings.integer_ring import ZZ
|
|
41
|
+
from sage.structure.richcmp import richcmp_method, rich_to_bool, richcmp_item
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@richcmp_method
|
|
45
|
+
class Word_class(SageObject):
|
|
46
|
+
def parent(self):
|
|
47
|
+
r"""
|
|
48
|
+
Return the parent of ``self``.
|
|
49
|
+
|
|
50
|
+
TESTS::
|
|
51
|
+
|
|
52
|
+
sage: Word(iter([1,2,3]), length='unknown').parent()
|
|
53
|
+
Finite words over Set of Python objects of class 'object'
|
|
54
|
+
sage: Word(range(12)).parent()
|
|
55
|
+
Finite words over Set of Python objects of class 'object'
|
|
56
|
+
sage: Word(range(4), alphabet=list(range(6))).parent()
|
|
57
|
+
Finite words over {0, 1, 2, 3, 4, 5}
|
|
58
|
+
sage: Word(iter('abac'), alphabet='abc').parent()
|
|
59
|
+
Finite words over {'a', 'b', 'c'}
|
|
60
|
+
"""
|
|
61
|
+
return self._parent
|
|
62
|
+
|
|
63
|
+
def _repr_(self):
|
|
64
|
+
r"""
|
|
65
|
+
Return a string representation of ``self``.
|
|
66
|
+
|
|
67
|
+
TESTS::
|
|
68
|
+
|
|
69
|
+
sage: Word(iter([1,2,3]), length='unknown')._repr_()
|
|
70
|
+
'word: 123'
|
|
71
|
+
sage: Word(range(100), length='unknown')._repr_()
|
|
72
|
+
'word: 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,...'
|
|
73
|
+
sage: Word(lambda x:x%3)._repr_()
|
|
74
|
+
'word: 0120120120120120120120120120120120120120...'
|
|
75
|
+
"""
|
|
76
|
+
if word_options['old_repr']:
|
|
77
|
+
return "Word over %s" % (str(self.parent().alphabet())[17:])
|
|
78
|
+
return word_options['identifier'] + self.string_rep()
|
|
79
|
+
|
|
80
|
+
def string_rep(self):
|
|
81
|
+
r"""
|
|
82
|
+
Return the (truncated) raw sequence of letters as a string.
|
|
83
|
+
|
|
84
|
+
EXAMPLES::
|
|
85
|
+
|
|
86
|
+
sage: Word('abbabaab').string_rep()
|
|
87
|
+
'abbabaab'
|
|
88
|
+
sage: Word([0, 1, 0, 0, 1]).string_rep()
|
|
89
|
+
'01001'
|
|
90
|
+
sage: Word([0,1,10,101]).string_rep()
|
|
91
|
+
'0,1,10,101'
|
|
92
|
+
sage: WordOptions(letter_separator='-')
|
|
93
|
+
sage: Word([0,1,10,101]).string_rep()
|
|
94
|
+
'0-1-10-101'
|
|
95
|
+
sage: WordOptions(letter_separator=',')
|
|
96
|
+
|
|
97
|
+
TESTS:
|
|
98
|
+
|
|
99
|
+
Insertion in a str::
|
|
100
|
+
|
|
101
|
+
sage: from itertools import count
|
|
102
|
+
sage: w = Word((i % 5 for i in count()), length='unknown')
|
|
103
|
+
sage: "w = %s in this string." % w
|
|
104
|
+
'w = 0123401234012340123401234012340123401234... in this string.'
|
|
105
|
+
|
|
106
|
+
Using LatexExpr::
|
|
107
|
+
|
|
108
|
+
sage: from sage.misc.latex import LatexExpr
|
|
109
|
+
sage: LatexExpr(w)
|
|
110
|
+
0123401234012340123401234012340123401234...
|
|
111
|
+
|
|
112
|
+
With the print statement::
|
|
113
|
+
|
|
114
|
+
sage: print(w)
|
|
115
|
+
0123401234012340123401234012340123401234...
|
|
116
|
+
|
|
117
|
+
Truncation is done for possibly infinite words::
|
|
118
|
+
|
|
119
|
+
sage: print(w)
|
|
120
|
+
0123401234012340123401234012340123401234...
|
|
121
|
+
"""
|
|
122
|
+
l = word_options['truncate_length']
|
|
123
|
+
letters = list(islice(self, int(l + 1)))
|
|
124
|
+
if len(letters) == l + 1:
|
|
125
|
+
letters.pop()
|
|
126
|
+
suffix = "..."
|
|
127
|
+
else:
|
|
128
|
+
suffix = ""
|
|
129
|
+
if word_options['display'] == 'string':
|
|
130
|
+
ls = word_options['letter_separator']
|
|
131
|
+
letters = [str(a) for a in letters]
|
|
132
|
+
if all(len(a) == 1 for a in letters):
|
|
133
|
+
return ''.join(letters) + suffix
|
|
134
|
+
elif suffix == "...":
|
|
135
|
+
return ls.join(letters) + ls + suffix
|
|
136
|
+
else:
|
|
137
|
+
return ls.join(letters)
|
|
138
|
+
elif word_options['display'] == 'list':
|
|
139
|
+
if suffix == "...":
|
|
140
|
+
return "[%s, %s]" % (str(letters)[1:-1], suffix)
|
|
141
|
+
else:
|
|
142
|
+
return str(letters)
|
|
143
|
+
|
|
144
|
+
__str__ = string_rep
|
|
145
|
+
|
|
146
|
+
def __iter__(self):
|
|
147
|
+
r"""
|
|
148
|
+
EXAMPLES::
|
|
149
|
+
|
|
150
|
+
sage: from sage.combinat.words.word import Word_class
|
|
151
|
+
sage: w = Word_class()
|
|
152
|
+
sage: w.__iter__()
|
|
153
|
+
Traceback (most recent call last):
|
|
154
|
+
...
|
|
155
|
+
NotImplementedError: you need to define an iterator in __iter__
|
|
156
|
+
"""
|
|
157
|
+
raise NotImplementedError("you need to define an iterator in __iter__")
|
|
158
|
+
|
|
159
|
+
def length(self):
|
|
160
|
+
r"""
|
|
161
|
+
Return the length of ``self``.
|
|
162
|
+
|
|
163
|
+
TESTS::
|
|
164
|
+
|
|
165
|
+
sage: from sage.combinat.words.word import Word_class
|
|
166
|
+
sage: w = Word(iter('abba'*100), length='unknown')
|
|
167
|
+
sage: w.length() is None
|
|
168
|
+
True
|
|
169
|
+
sage: w = Word(iter('abba'), length='finite')
|
|
170
|
+
sage: w.length()
|
|
171
|
+
4
|
|
172
|
+
sage: w = Word(iter([0,1,1,0,1,0,0,1]*100), length='unknown')
|
|
173
|
+
sage: w.length() is None
|
|
174
|
+
True
|
|
175
|
+
sage: w = Word(iter([0,1,1,0,1,0,0,1]), length='finite')
|
|
176
|
+
sage: w.length()
|
|
177
|
+
8
|
|
178
|
+
"""
|
|
179
|
+
return self._len
|
|
180
|
+
|
|
181
|
+
def is_finite(self):
|
|
182
|
+
r"""
|
|
183
|
+
Return whether this word is known to be finite.
|
|
184
|
+
|
|
185
|
+
.. WARNING::
|
|
186
|
+
|
|
187
|
+
A word defined by an iterator such that its end has
|
|
188
|
+
never been reached will returns False.
|
|
189
|
+
|
|
190
|
+
EXAMPLES::
|
|
191
|
+
|
|
192
|
+
sage: Word([]).is_finite()
|
|
193
|
+
True
|
|
194
|
+
sage: Word('a').is_finite()
|
|
195
|
+
True
|
|
196
|
+
sage: TM = words.ThueMorseWord()
|
|
197
|
+
sage: TM.is_finite()
|
|
198
|
+
False
|
|
199
|
+
|
|
200
|
+
::
|
|
201
|
+
|
|
202
|
+
sage: w = Word(iter('a'*100))
|
|
203
|
+
sage: w.is_finite()
|
|
204
|
+
False
|
|
205
|
+
"""
|
|
206
|
+
return False
|
|
207
|
+
|
|
208
|
+
def __len__(self):
|
|
209
|
+
r"""
|
|
210
|
+
Return the length of ``self`` (as a Python integer).
|
|
211
|
+
|
|
212
|
+
.. NOTE::
|
|
213
|
+
|
|
214
|
+
For infinite words or words of unknown length,
|
|
215
|
+
use `length()` method instead.
|
|
216
|
+
|
|
217
|
+
OUTPUT: positive integer
|
|
218
|
+
|
|
219
|
+
EXAMPLES::
|
|
220
|
+
|
|
221
|
+
sage: len(Word(lambda n:n, length=1000))
|
|
222
|
+
1000
|
|
223
|
+
sage: len(Word(iter('a'*200), length='finite'))
|
|
224
|
+
200
|
|
225
|
+
|
|
226
|
+
We make sure :issue:`8574` is fixed::
|
|
227
|
+
|
|
228
|
+
sage: s = WordMorphism('0->000,1->%s'%('1'*100))
|
|
229
|
+
sage: len(s('1'))
|
|
230
|
+
100
|
|
231
|
+
|
|
232
|
+
For infinite words::
|
|
233
|
+
|
|
234
|
+
sage: len(Word(lambda n:n))
|
|
235
|
+
Traceback (most recent call last):
|
|
236
|
+
...
|
|
237
|
+
TypeError: Python len method cannot return a non integer value (=+Infinity): use length method instead.
|
|
238
|
+
sage: len(Word(iter('a'*200)))
|
|
239
|
+
Traceback (most recent call last):
|
|
240
|
+
...
|
|
241
|
+
TypeError: Python len method cannot return a non integer value (=None): use length method instead.
|
|
242
|
+
|
|
243
|
+
For words of unknown length::
|
|
244
|
+
|
|
245
|
+
sage: len(Word(iter('a'*200), length='unknown'))
|
|
246
|
+
Traceback (most recent call last):
|
|
247
|
+
...
|
|
248
|
+
TypeError: Python len method cannot return a non integer value (=None): use length method instead.
|
|
249
|
+
"""
|
|
250
|
+
L = self.length()
|
|
251
|
+
if L is None or L is Infinity:
|
|
252
|
+
msg = "Python len method cannot return a non integer value (=%s): " % L
|
|
253
|
+
msg += "use length method instead."
|
|
254
|
+
raise TypeError(msg)
|
|
255
|
+
return int(L)
|
|
256
|
+
|
|
257
|
+
def __richcmp__(self, other, op):
|
|
258
|
+
r"""
|
|
259
|
+
Compare two words lexicographically according to the ordering
|
|
260
|
+
defined by the parent of ``self``.
|
|
261
|
+
|
|
262
|
+
This corresponds to Python's built-in
|
|
263
|
+
ordering when no parent nor alphabet was used to defined the word.
|
|
264
|
+
|
|
265
|
+
Provides for all normal comparison operators.
|
|
266
|
+
|
|
267
|
+
.. NOTE::
|
|
268
|
+
|
|
269
|
+
This function will not terminate if ``self`` and ``other``
|
|
270
|
+
are equal infinite words!
|
|
271
|
+
|
|
272
|
+
EXAMPLES::
|
|
273
|
+
|
|
274
|
+
sage: W = Word
|
|
275
|
+
sage: from itertools import count
|
|
276
|
+
sage: W(range(1,10)) > W(range(10))
|
|
277
|
+
True
|
|
278
|
+
sage: W(range(10)) < W(range(1,10))
|
|
279
|
+
True
|
|
280
|
+
sage: W(range(10)) == W(range(10))
|
|
281
|
+
True
|
|
282
|
+
sage: W(range(10)) < W(count())
|
|
283
|
+
True
|
|
284
|
+
sage: W(count()) > W(range(10))
|
|
285
|
+
True
|
|
286
|
+
|
|
287
|
+
::
|
|
288
|
+
|
|
289
|
+
sage: W = Words(['a', 'b', 'c'])
|
|
290
|
+
sage: W('a') > W([])
|
|
291
|
+
True
|
|
292
|
+
sage: W([]) < W('a')
|
|
293
|
+
True
|
|
294
|
+
|
|
295
|
+
sage: Word('abc') == Word(['a','b','c'])
|
|
296
|
+
True
|
|
297
|
+
sage: Words([0,1])([0,1,0,1]) == Words([0,1])([0,1,0,1])
|
|
298
|
+
True
|
|
299
|
+
sage: Words([0,1])([0,1,0,1]) == Words([0,1])([0,1,0,0])
|
|
300
|
+
False
|
|
301
|
+
|
|
302
|
+
It works even when parents are not the same::
|
|
303
|
+
|
|
304
|
+
sage: Words([0,1,2])([0,1,0,1]) == Words([0,1])([0,1,0,1])
|
|
305
|
+
True
|
|
306
|
+
sage: Word('ababa') == Words('abcd')('ababa')
|
|
307
|
+
True
|
|
308
|
+
|
|
309
|
+
Or when one word is finite while the other is infinite::
|
|
310
|
+
|
|
311
|
+
sage: Word(range(20)) == Word(lambda n:n)
|
|
312
|
+
False
|
|
313
|
+
sage: Word(lambda n:n) == Word(range(20))
|
|
314
|
+
False
|
|
315
|
+
|
|
316
|
+
Beware the following does not halt! ::
|
|
317
|
+
|
|
318
|
+
sage: from itertools import count
|
|
319
|
+
sage: Word(lambda n:n) == Word(count()) #not tested
|
|
320
|
+
|
|
321
|
+
Examples for unequality::
|
|
322
|
+
|
|
323
|
+
sage: w = Word(range(10))
|
|
324
|
+
sage: z = Word(range(10))
|
|
325
|
+
sage: w != z
|
|
326
|
+
False
|
|
327
|
+
sage: u = Word(range(12))
|
|
328
|
+
sage: u != w
|
|
329
|
+
True
|
|
330
|
+
|
|
331
|
+
TESTS::
|
|
332
|
+
|
|
333
|
+
sage: Word(count())[:20] == Word(range(20))
|
|
334
|
+
True
|
|
335
|
+
sage: Word(range(20)) == Word(count())[:20]
|
|
336
|
+
True
|
|
337
|
+
sage: Word(range(20)) == Word(lambda n:n)[:20]
|
|
338
|
+
True
|
|
339
|
+
sage: Word(range(20)) == Word(lambda n:n,length=20)
|
|
340
|
+
True
|
|
341
|
+
"""
|
|
342
|
+
if not isinstance(other, Word_class):
|
|
343
|
+
return NotImplemented
|
|
344
|
+
self_it, other_it = iter(self), iter(other)
|
|
345
|
+
cmp_key = self._parent.sortkey_letters
|
|
346
|
+
while True:
|
|
347
|
+
try:
|
|
348
|
+
cs = next(self_it)
|
|
349
|
+
except StopIteration:
|
|
350
|
+
try:
|
|
351
|
+
next(other_it)
|
|
352
|
+
except StopIteration:
|
|
353
|
+
# If both self_it and other_it are exhausted then
|
|
354
|
+
# self == other. Return 0.
|
|
355
|
+
return rich_to_bool(op, 0)
|
|
356
|
+
else:
|
|
357
|
+
# If self_it is exhausted, but not other_it, then
|
|
358
|
+
# self is a proper prefix of other: return -1
|
|
359
|
+
return rich_to_bool(op, -1)
|
|
360
|
+
else:
|
|
361
|
+
try:
|
|
362
|
+
co = next(other_it)
|
|
363
|
+
except StopIteration:
|
|
364
|
+
# If self_it is not exhausted but other_it is, then
|
|
365
|
+
# other is a proper prefix of self: return 1.
|
|
366
|
+
return rich_to_bool(op, 1)
|
|
367
|
+
else:
|
|
368
|
+
key_cs = cmp_key(cs)
|
|
369
|
+
key_co = cmp_key(co)
|
|
370
|
+
res = richcmp_item(key_cs, key_co, op)
|
|
371
|
+
if res is not NotImplemented:
|
|
372
|
+
return res
|
|
373
|
+
|
|
374
|
+
def _longest_common_prefix_iterator(self, other):
|
|
375
|
+
r"""
|
|
376
|
+
Return an iterator of the longest common prefix of ``self`` and ``other``.
|
|
377
|
+
|
|
378
|
+
INPUT:
|
|
379
|
+
|
|
380
|
+
- ``other`` -- word
|
|
381
|
+
|
|
382
|
+
OUTPUT: iterator
|
|
383
|
+
|
|
384
|
+
EXAMPLES::
|
|
385
|
+
|
|
386
|
+
sage: f = words.FibonacciWord()
|
|
387
|
+
sage: it = f._longest_common_prefix_iterator(f)
|
|
388
|
+
sage: w = Word(it, length='unknown'); w
|
|
389
|
+
word: 0100101001001010010100100101001001010010...
|
|
390
|
+
sage: w[:6]
|
|
391
|
+
word: 010010
|
|
392
|
+
sage: it = w._longest_common_prefix_iterator(w[:10])
|
|
393
|
+
sage: w = Word(it, length='finite'); w
|
|
394
|
+
word: 0100101001
|
|
395
|
+
"""
|
|
396
|
+
for (b, c) in zip(self, other):
|
|
397
|
+
if b == c:
|
|
398
|
+
yield b
|
|
399
|
+
else:
|
|
400
|
+
break
|
|
401
|
+
|
|
402
|
+
def longest_common_prefix(self, other, length='unknown'):
|
|
403
|
+
r"""
|
|
404
|
+
Return the longest common prefix of ``self`` and ``other``.
|
|
405
|
+
|
|
406
|
+
INPUT:
|
|
407
|
+
|
|
408
|
+
- ``other`` -- word
|
|
409
|
+
|
|
410
|
+
- ``length`` -- string (default: ``'unknown'``)
|
|
411
|
+
the length type of the resulting word if known. It may be one of
|
|
412
|
+
the following:
|
|
413
|
+
|
|
414
|
+
- ``'unknown'``
|
|
415
|
+
- ``'finite'``
|
|
416
|
+
- ``'infinite'``
|
|
417
|
+
|
|
418
|
+
EXAMPLES::
|
|
419
|
+
|
|
420
|
+
sage: f = lambda n : add(Integer(n).digits(2)) % 2
|
|
421
|
+
sage: t = Word(f)
|
|
422
|
+
sage: u = t[:10]
|
|
423
|
+
sage: t.longest_common_prefix(u)
|
|
424
|
+
word: 0110100110
|
|
425
|
+
|
|
426
|
+
The longest common prefix of two equal infinite words::
|
|
427
|
+
|
|
428
|
+
sage: t1 = Word(f)
|
|
429
|
+
sage: t2 = Word(f)
|
|
430
|
+
sage: t1.longest_common_prefix(t2)
|
|
431
|
+
word: 0110100110010110100101100110100110010110...
|
|
432
|
+
|
|
433
|
+
Useful to study the approximation of an infinite word::
|
|
434
|
+
|
|
435
|
+
sage: a = 0.618
|
|
436
|
+
sage: g = words.CodingOfRotationWord(alpha=a, beta=1-a, x=a)
|
|
437
|
+
sage: f = words.FibonacciWord()
|
|
438
|
+
sage: p = f.longest_common_prefix(g, length='finite')
|
|
439
|
+
sage: p.length()
|
|
440
|
+
231
|
|
441
|
+
|
|
442
|
+
TESTS::
|
|
443
|
+
|
|
444
|
+
sage: w = Word('12345')
|
|
445
|
+
sage: y = Word('1236777')
|
|
446
|
+
sage: w.longest_common_prefix(y)
|
|
447
|
+
word: 123
|
|
448
|
+
sage: w.longest_common_prefix(w)
|
|
449
|
+
word: 12345
|
|
450
|
+
sage: y.longest_common_prefix(w)
|
|
451
|
+
word: 123
|
|
452
|
+
sage: y.longest_common_prefix(y)
|
|
453
|
+
word: 1236777
|
|
454
|
+
sage: Word().longest_common_prefix(w)
|
|
455
|
+
word:
|
|
456
|
+
sage: w.longest_common_prefix(Word())
|
|
457
|
+
word:
|
|
458
|
+
sage: w.longest_common_prefix(w[:3])
|
|
459
|
+
word: 123
|
|
460
|
+
sage: Word("11").longest_common_prefix(Word("1"))
|
|
461
|
+
word: 1
|
|
462
|
+
sage: Word("1").longest_common_prefix(Word("11"))
|
|
463
|
+
word: 1
|
|
464
|
+
|
|
465
|
+
With infinite words::
|
|
466
|
+
|
|
467
|
+
sage: t = words.ThueMorseWord('ab')
|
|
468
|
+
sage: u = t[:10]
|
|
469
|
+
sage: u.longest_common_prefix(t)
|
|
470
|
+
word: abbabaabba
|
|
471
|
+
sage: u.longest_common_prefix(u)
|
|
472
|
+
word: abbabaabba
|
|
473
|
+
|
|
474
|
+
Check length::
|
|
475
|
+
|
|
476
|
+
sage: w1 = Word(iter('ab'*200))
|
|
477
|
+
sage: w2 = Word(iter('bcd'*200))
|
|
478
|
+
sage: w1.longest_common_prefix(w2, length=19)
|
|
479
|
+
Traceback (most recent call last):
|
|
480
|
+
...
|
|
481
|
+
ValueError: invalid argument length (=19)
|
|
482
|
+
"""
|
|
483
|
+
it = self._longest_common_prefix_iterator(other)
|
|
484
|
+
|
|
485
|
+
if length == "finite" or \
|
|
486
|
+
(length == "unknown" and (self.is_finite() or other.is_finite())):
|
|
487
|
+
parent = self._parent.factors()
|
|
488
|
+
elif length == "infinite":
|
|
489
|
+
parent = self._parent.shift()
|
|
490
|
+
elif length == "unknown":
|
|
491
|
+
from sage.combinat.words.words import FiniteOrInfiniteWords
|
|
492
|
+
parent = FiniteOrInfiniteWords(self._parent.alphabet())
|
|
493
|
+
else:
|
|
494
|
+
raise ValueError("invalid argument length (={})".format(length))
|
|
495
|
+
|
|
496
|
+
return parent(it)
|
|
497
|
+
|
|
498
|
+
def _longest_periodic_prefix_iterator(self, period=1):
|
|
499
|
+
r"""
|
|
500
|
+
Return an iterator of the longest prefix of ``self`` having the given
|
|
501
|
+
period.
|
|
502
|
+
|
|
503
|
+
INPUT:
|
|
504
|
+
|
|
505
|
+
- ``period`` -- positive integer (default: 1)
|
|
506
|
+
|
|
507
|
+
OUTPUT: iterator
|
|
508
|
+
|
|
509
|
+
EXAMPLES::
|
|
510
|
+
|
|
511
|
+
sage: list(Word([])._longest_periodic_prefix_iterator())
|
|
512
|
+
[]
|
|
513
|
+
sage: list(Word([1])._longest_periodic_prefix_iterator())
|
|
514
|
+
[1]
|
|
515
|
+
sage: list(Word([1,2])._longest_periodic_prefix_iterator())
|
|
516
|
+
[1]
|
|
517
|
+
sage: list(Word([1,1,2])._longest_periodic_prefix_iterator())
|
|
518
|
+
[1, 1]
|
|
519
|
+
sage: list(Word([1,1,1,2])._longest_periodic_prefix_iterator())
|
|
520
|
+
[1, 1, 1]
|
|
521
|
+
sage: Word(Word(lambda n:0)._longest_periodic_prefix_iterator())
|
|
522
|
+
word: 0000000000000000000000000000000000000000...
|
|
523
|
+
sage: list(Word([1,2,1,2,1,3])._longest_periodic_prefix_iterator(2))
|
|
524
|
+
[1, 2, 1, 2, 1]
|
|
525
|
+
"""
|
|
526
|
+
for i, l in enumerate(self):
|
|
527
|
+
if self[i % period] == l:
|
|
528
|
+
yield l
|
|
529
|
+
else:
|
|
530
|
+
break
|
|
531
|
+
|
|
532
|
+
def longest_periodic_prefix(self, period=1):
|
|
533
|
+
r"""
|
|
534
|
+
Return the longest prefix of ``self`` having the given period.
|
|
535
|
+
|
|
536
|
+
INPUT:
|
|
537
|
+
|
|
538
|
+
- ``period`` -- positive integer (default: 1)
|
|
539
|
+
|
|
540
|
+
OUTPUT: word
|
|
541
|
+
|
|
542
|
+
EXAMPLES::
|
|
543
|
+
|
|
544
|
+
sage: Word([]).longest_periodic_prefix()
|
|
545
|
+
word:
|
|
546
|
+
sage: Word([1]).longest_periodic_prefix()
|
|
547
|
+
word: 1
|
|
548
|
+
sage: Word([1,2]).longest_periodic_prefix()
|
|
549
|
+
word: 1
|
|
550
|
+
sage: Word([1,1,2]).longest_periodic_prefix()
|
|
551
|
+
word: 11
|
|
552
|
+
sage: Word([1,2,1,2,1,3]).longest_periodic_prefix(2)
|
|
553
|
+
word: 12121
|
|
554
|
+
sage: type(_)
|
|
555
|
+
<class 'sage.combinat.words.word.FiniteWord_iter_with_caching'>
|
|
556
|
+
sage: Word(lambda n:0).longest_periodic_prefix()
|
|
557
|
+
word: 0000000000000000000000000000000000000000...
|
|
558
|
+
"""
|
|
559
|
+
if self.is_finite():
|
|
560
|
+
parent = self._parent.factors()
|
|
561
|
+
else:
|
|
562
|
+
from sage.combinat.words.words import FiniteOrInfiniteWords
|
|
563
|
+
parent = FiniteOrInfiniteWords(self._parent.alphabet())
|
|
564
|
+
return parent(self._longest_periodic_prefix_iterator(period))
|
|
565
|
+
|
|
566
|
+
def is_empty(self):
|
|
567
|
+
r"""
|
|
568
|
+
Return ``True`` if the length of ``self`` is zero, and ``False`` otherwise.
|
|
569
|
+
|
|
570
|
+
EXAMPLES::
|
|
571
|
+
|
|
572
|
+
sage: it = iter([])
|
|
573
|
+
sage: Word(it).is_empty()
|
|
574
|
+
True
|
|
575
|
+
sage: it = iter([1,2,3])
|
|
576
|
+
sage: Word(it).is_empty()
|
|
577
|
+
False
|
|
578
|
+
sage: from itertools import count
|
|
579
|
+
sage: Word(count()).is_empty()
|
|
580
|
+
False
|
|
581
|
+
"""
|
|
582
|
+
try:
|
|
583
|
+
next(iter(self))
|
|
584
|
+
return False
|
|
585
|
+
except StopIteration:
|
|
586
|
+
return True
|
|
587
|
+
|
|
588
|
+
def _to_integer_iterator(self, use_parent_alphabet=False):
|
|
589
|
+
r"""
|
|
590
|
+
Return an iterator over the letters of an integer representation of
|
|
591
|
+
``self``.
|
|
592
|
+
|
|
593
|
+
INPUT:
|
|
594
|
+
|
|
595
|
+
- ``use_parent_alphabet`` -- boolean (default: ``False``); when ``True``
|
|
596
|
+
and if the ``self`` parent's alphabet is finite, it uses the index of
|
|
597
|
+
the letters in the alphabet. Otherwise, the first letter occurring in
|
|
598
|
+
``self`` is mapped to zero, and every letter that hasn't yet occurred in
|
|
599
|
+
the word is mapped to the next available integer.
|
|
600
|
+
|
|
601
|
+
EXAMPLES::
|
|
602
|
+
|
|
603
|
+
sage: from itertools import count
|
|
604
|
+
sage: w = Word(count())
|
|
605
|
+
sage: ir = w._to_integer_iterator()
|
|
606
|
+
sage: [next(ir) for _ in range(10)]
|
|
607
|
+
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
608
|
+
sage: w = Word(iter("abbacabba"))
|
|
609
|
+
sage: ir = w._to_integer_iterator()
|
|
610
|
+
sage: list(ir)
|
|
611
|
+
[0, 1, 1, 0, 2, 0, 1, 1, 0]
|
|
612
|
+
|
|
613
|
+
::
|
|
614
|
+
|
|
615
|
+
sage: w = Words('abc')('abbccc')
|
|
616
|
+
sage: list(w._to_integer_iterator(True))
|
|
617
|
+
[0, 1, 1, 2, 2, 2]
|
|
618
|
+
sage: w = Words('acb')('abbccc')
|
|
619
|
+
sage: list(w._to_integer_iterator(True))
|
|
620
|
+
[0, 2, 2, 1, 1, 1]
|
|
621
|
+
sage: w = Words('xabc')('abbccc')
|
|
622
|
+
sage: list(w._to_integer_iterator(True))
|
|
623
|
+
[1, 2, 2, 3, 3, 3]
|
|
624
|
+
"""
|
|
625
|
+
from sage.combinat.words.words import FiniteWords, InfiniteWords
|
|
626
|
+
if use_parent_alphabet and\
|
|
627
|
+
isinstance(self.parent(), (FiniteWords, InfiniteWords)):
|
|
628
|
+
A = self.parent().alphabet()
|
|
629
|
+
for letter in self:
|
|
630
|
+
yield A.rank(letter)
|
|
631
|
+
|
|
632
|
+
else:
|
|
633
|
+
mapping = {}
|
|
634
|
+
next_value = 0
|
|
635
|
+
for letter in self:
|
|
636
|
+
if letter not in mapping:
|
|
637
|
+
mapping[letter] = next_value
|
|
638
|
+
next_value += 1
|
|
639
|
+
yield mapping[letter]
|
|
640
|
+
|
|
641
|
+
def to_integer_word(self):
|
|
642
|
+
r"""
|
|
643
|
+
Return a word over the integers whose letters are those output by
|
|
644
|
+
self._to_integer_iterator()
|
|
645
|
+
|
|
646
|
+
EXAMPLES::
|
|
647
|
+
|
|
648
|
+
sage: from itertools import count
|
|
649
|
+
sage: w = Word(count()); w
|
|
650
|
+
word: 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,...
|
|
651
|
+
sage: w.to_integer_word()
|
|
652
|
+
word: 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,...
|
|
653
|
+
sage: w = Word(iter("abbacabba"), length='finite'); w
|
|
654
|
+
word: abbacabba
|
|
655
|
+
sage: w.to_integer_word()
|
|
656
|
+
word: 011020110
|
|
657
|
+
sage: w = Word(iter("abbacabba"), length='unknown'); w
|
|
658
|
+
word: abbacabba
|
|
659
|
+
sage: w.to_integer_word()
|
|
660
|
+
word: 011020110
|
|
661
|
+
"""
|
|
662
|
+
length = "unknown" if self._len is None else self._len
|
|
663
|
+
from sage.combinat.words.word import Word
|
|
664
|
+
return Word(self._to_integer_iterator(), length=length)
|
|
665
|
+
|
|
666
|
+
def lex_less(self, other):
|
|
667
|
+
r"""
|
|
668
|
+
Return ``True`` if ``self`` is lexicographically less than ``other``.
|
|
669
|
+
|
|
670
|
+
EXAMPLES::
|
|
671
|
+
|
|
672
|
+
sage: w = Word([1,2,3])
|
|
673
|
+
sage: u = Word([1,3,2])
|
|
674
|
+
sage: v = Word([3,2,1])
|
|
675
|
+
sage: w.lex_less(u)
|
|
676
|
+
True
|
|
677
|
+
sage: v.lex_less(w)
|
|
678
|
+
False
|
|
679
|
+
sage: a = Word("abba")
|
|
680
|
+
sage: b = Word("abbb")
|
|
681
|
+
sage: a.lex_less(b)
|
|
682
|
+
True
|
|
683
|
+
sage: b.lex_less(a)
|
|
684
|
+
False
|
|
685
|
+
|
|
686
|
+
For infinite words::
|
|
687
|
+
|
|
688
|
+
sage: t = words.ThueMorseWord()
|
|
689
|
+
sage: t.lex_less(t[:10])
|
|
690
|
+
False
|
|
691
|
+
sage: t[:10].lex_less(t)
|
|
692
|
+
True
|
|
693
|
+
"""
|
|
694
|
+
return self < other
|
|
695
|
+
|
|
696
|
+
def lex_greater(self, other):
|
|
697
|
+
r"""
|
|
698
|
+
Return ``True`` if ``self`` is lexicographically greater than ``other``.
|
|
699
|
+
|
|
700
|
+
EXAMPLES::
|
|
701
|
+
|
|
702
|
+
sage: w = Word([1,2,3])
|
|
703
|
+
sage: u = Word([1,3,2])
|
|
704
|
+
sage: v = Word([3,2,1])
|
|
705
|
+
sage: w.lex_greater(u)
|
|
706
|
+
False
|
|
707
|
+
sage: v.lex_greater(w)
|
|
708
|
+
True
|
|
709
|
+
sage: a = Word("abba")
|
|
710
|
+
sage: b = Word("abbb")
|
|
711
|
+
sage: a.lex_greater(b)
|
|
712
|
+
False
|
|
713
|
+
sage: b.lex_greater(a)
|
|
714
|
+
True
|
|
715
|
+
|
|
716
|
+
For infinite words::
|
|
717
|
+
|
|
718
|
+
sage: t = words.ThueMorseWord()
|
|
719
|
+
sage: t[:10].lex_greater(t)
|
|
720
|
+
False
|
|
721
|
+
sage: t.lex_greater(t[:10])
|
|
722
|
+
True
|
|
723
|
+
"""
|
|
724
|
+
return self > other
|
|
725
|
+
|
|
726
|
+
def apply_morphism(self, morphism):
|
|
727
|
+
r"""
|
|
728
|
+
Return the word obtained by applying the morphism to ``self``.
|
|
729
|
+
|
|
730
|
+
INPUT:
|
|
731
|
+
|
|
732
|
+
- ``morphism`` -- can be an instance of WordMorphism, or
|
|
733
|
+
anything that can be used to construct one
|
|
734
|
+
|
|
735
|
+
EXAMPLES::
|
|
736
|
+
|
|
737
|
+
sage: w = Word("ab")
|
|
738
|
+
sage: d = {'a':'ab', 'b':'ba'}
|
|
739
|
+
sage: w.apply_morphism(d)
|
|
740
|
+
word: abba
|
|
741
|
+
sage: w.apply_morphism(WordMorphism(d))
|
|
742
|
+
word: abba
|
|
743
|
+
|
|
744
|
+
::
|
|
745
|
+
|
|
746
|
+
sage: w = Word('ababa')
|
|
747
|
+
sage: d = dict(a='ab', b='ba')
|
|
748
|
+
sage: d
|
|
749
|
+
{'a': 'ab', 'b': 'ba'}
|
|
750
|
+
sage: w.apply_morphism(d)
|
|
751
|
+
word: abbaabbaab
|
|
752
|
+
|
|
753
|
+
For infinite words::
|
|
754
|
+
|
|
755
|
+
sage: t = words.ThueMorseWord([0,1]); t
|
|
756
|
+
word: 0110100110010110100101100110100110010110...
|
|
757
|
+
sage: t.apply_morphism({0:8,1:9})
|
|
758
|
+
word: 8998988998898998988989988998988998898998...
|
|
759
|
+
"""
|
|
760
|
+
from sage.combinat.words.morphism import WordMorphism
|
|
761
|
+
if not isinstance(morphism, WordMorphism):
|
|
762
|
+
morphism = WordMorphism(morphism)
|
|
763
|
+
return morphism(self)
|
|
764
|
+
|
|
765
|
+
def _delta_iterator(self):
|
|
766
|
+
r"""
|
|
767
|
+
Return an iterator of the image of ``self`` under the delta morphism.
|
|
768
|
+
This is the word composed of the length of consecutive runs of the
|
|
769
|
+
same letter in a given word.
|
|
770
|
+
|
|
771
|
+
OUTPUT: generator object
|
|
772
|
+
|
|
773
|
+
EXAMPLES::
|
|
774
|
+
|
|
775
|
+
sage: W = Words('0123456789')
|
|
776
|
+
sage: it=W('22112122')._delta_iterator()
|
|
777
|
+
sage: Word(it)
|
|
778
|
+
word: 22112
|
|
779
|
+
sage: Word(W('555008')._delta_iterator())
|
|
780
|
+
word: 321
|
|
781
|
+
sage: Word(W()._delta_iterator())
|
|
782
|
+
word:
|
|
783
|
+
|
|
784
|
+
For infinite words::
|
|
785
|
+
|
|
786
|
+
sage: t = words.ThueMorseWord()
|
|
787
|
+
sage: it = t._delta_iterator()
|
|
788
|
+
sage: Word(it)
|
|
789
|
+
word: 1211222112112112221122211222112112112221...
|
|
790
|
+
"""
|
|
791
|
+
for letter, run in groupby(self):
|
|
792
|
+
yield len(list(run))
|
|
793
|
+
|
|
794
|
+
def delta(self):
|
|
795
|
+
r"""
|
|
796
|
+
Return the image of ``self`` under the delta morphism.
|
|
797
|
+
|
|
798
|
+
This is the word composed of the length of consecutive runs of
|
|
799
|
+
the same letter in a given word.
|
|
800
|
+
|
|
801
|
+
OUTPUT: word over integers
|
|
802
|
+
|
|
803
|
+
EXAMPLES:
|
|
804
|
+
|
|
805
|
+
For finite words::
|
|
806
|
+
|
|
807
|
+
sage: W = Words('0123456789')
|
|
808
|
+
sage: W('22112122').delta()
|
|
809
|
+
word: 22112
|
|
810
|
+
sage: W('555008').delta()
|
|
811
|
+
word: 321
|
|
812
|
+
sage: W().delta()
|
|
813
|
+
word:
|
|
814
|
+
sage: Word('aabbabaa').delta()
|
|
815
|
+
word: 22112
|
|
816
|
+
|
|
817
|
+
For infinite words::
|
|
818
|
+
|
|
819
|
+
sage: t = words.ThueMorseWord()
|
|
820
|
+
sage: t.delta()
|
|
821
|
+
word: 1211222112112112221122211222112112112221...
|
|
822
|
+
"""
|
|
823
|
+
from sage.combinat.words.word import Word
|
|
824
|
+
from sage.rings.semirings.non_negative_integer_semiring import NN
|
|
825
|
+
return Word(self._delta_iterator(), alphabet=NN)
|
|
826
|
+
|
|
827
|
+
def _iterated_right_palindromic_closure_iterator(self, f=None):
|
|
828
|
+
r"""
|
|
829
|
+
Return an iterator over the iterated (`f`-)palindromic closure of ``self``.
|
|
830
|
+
|
|
831
|
+
INPUT:
|
|
832
|
+
|
|
833
|
+
- ``f`` -- involution on the alphabet of ``self`` (default: ``None``);
|
|
834
|
+
it must be callable on letters as well as words (e.g. WordMorphism)
|
|
835
|
+
|
|
836
|
+
OUTPUT: iterator; the iterated (`f`-)palindromic closure of ``self``
|
|
837
|
+
|
|
838
|
+
EXAMPLES::
|
|
839
|
+
|
|
840
|
+
sage: w = Word('abc')
|
|
841
|
+
sage: it = w._iterated_right_palindromic_closure_iterator()
|
|
842
|
+
sage: Word(it)
|
|
843
|
+
word: abacaba
|
|
844
|
+
|
|
845
|
+
::
|
|
846
|
+
|
|
847
|
+
sage: w = Word('aaa')
|
|
848
|
+
sage: it = w._iterated_right_palindromic_closure_iterator()
|
|
849
|
+
sage: Word(it)
|
|
850
|
+
word: aaa
|
|
851
|
+
|
|
852
|
+
::
|
|
853
|
+
|
|
854
|
+
sage: w = Word('abbab')
|
|
855
|
+
sage: it = w._iterated_right_palindromic_closure_iterator()
|
|
856
|
+
sage: Word(it)
|
|
857
|
+
word: ababaabababaababa
|
|
858
|
+
|
|
859
|
+
An infinite word::
|
|
860
|
+
|
|
861
|
+
sage: t = words.ThueMorseWord('ab')
|
|
862
|
+
sage: it = t._iterated_right_palindromic_closure_iterator()
|
|
863
|
+
sage: Word(it)
|
|
864
|
+
word: ababaabababaababaabababaababaabababaabab...
|
|
865
|
+
|
|
866
|
+
TESTS:
|
|
867
|
+
|
|
868
|
+
The empty word::
|
|
869
|
+
|
|
870
|
+
sage: w = Word()
|
|
871
|
+
sage: it = w._iterated_right_palindromic_closure_iterator()
|
|
872
|
+
sage: next(it)
|
|
873
|
+
Traceback (most recent call last):
|
|
874
|
+
...
|
|
875
|
+
StopIteration
|
|
876
|
+
|
|
877
|
+
REFERENCES:
|
|
878
|
+
|
|
879
|
+
- [1] A. de Luca, A. De Luca, Pseudopalindrome closure operators
|
|
880
|
+
in free monoids, Theoret. Comput. Sci. 362 (2006) 282--300.
|
|
881
|
+
"""
|
|
882
|
+
par = self.parent().factors()
|
|
883
|
+
w = self[:0]
|
|
884
|
+
for letter in self:
|
|
885
|
+
length_before = w.length()
|
|
886
|
+
w = (w*par([letter])).palindromic_closure(f=f)
|
|
887
|
+
length_after = w.length()
|
|
888
|
+
d = length_after - length_before
|
|
889
|
+
yield from w[-d:]
|
|
890
|
+
|
|
891
|
+
def _iterated_right_palindromic_closure_recursive_iterator(self, f=None):
|
|
892
|
+
r"""
|
|
893
|
+
Return an iterator over the iterated (`f`-)palindromic closure of ``self``.
|
|
894
|
+
|
|
895
|
+
INPUT:
|
|
896
|
+
|
|
897
|
+
- ``f`` -- involution (default: ``None``) on the alphabet of ``self``;
|
|
898
|
+
it must be callable on letters as well as words (e.g. WordMorphism)
|
|
899
|
+
|
|
900
|
+
OUTPUT: iterator; the iterated (`f`-)palindromic closure of ``self``
|
|
901
|
+
|
|
902
|
+
ALGORITHM:
|
|
903
|
+
|
|
904
|
+
For the case of palindromes only, it has been shown in [2] that
|
|
905
|
+
the iterated right palindromic closure of a given word `w`,
|
|
906
|
+
denoted by `IRPC(w)`, may be obtained as follows.
|
|
907
|
+
Let `w` be any word and `x` be a letter. Then
|
|
908
|
+
|
|
909
|
+
#. If `x` does not occur in `w`,
|
|
910
|
+
`IRPC(wx) = IRPC(w) \cdot x \cdot IRPC(w)`
|
|
911
|
+
#. Otherwise, write `w = w_1xw_2` such that `x` does not
|
|
912
|
+
occur in `w_2`. Then `IRPC(wx) = IRPC(w) \cdot IRPC(w_1)^{-1}
|
|
913
|
+
\cdot IRPC(w)`
|
|
914
|
+
|
|
915
|
+
This formula is directly generalized to the case of `f`-palindromes.
|
|
916
|
+
See [1] for more details.
|
|
917
|
+
|
|
918
|
+
EXAMPLES::
|
|
919
|
+
|
|
920
|
+
sage: w = Word('abc')
|
|
921
|
+
sage: it = w._iterated_right_palindromic_closure_recursive_iterator()
|
|
922
|
+
sage: Word(it)
|
|
923
|
+
word: abacaba
|
|
924
|
+
|
|
925
|
+
::
|
|
926
|
+
|
|
927
|
+
sage: w = Word('aaa')
|
|
928
|
+
sage: it = w._iterated_right_palindromic_closure_recursive_iterator()
|
|
929
|
+
sage: Word(it)
|
|
930
|
+
word: aaa
|
|
931
|
+
|
|
932
|
+
::
|
|
933
|
+
|
|
934
|
+
sage: w = Word('abbab')
|
|
935
|
+
sage: it = w._iterated_right_palindromic_closure_recursive_iterator()
|
|
936
|
+
sage: Word(it)
|
|
937
|
+
word: ababaabababaababa
|
|
938
|
+
|
|
939
|
+
An infinite word::
|
|
940
|
+
|
|
941
|
+
sage: t = words.ThueMorseWord('ab')
|
|
942
|
+
sage: it = t._iterated_right_palindromic_closure_recursive_iterator()
|
|
943
|
+
sage: Word(it)
|
|
944
|
+
word: ababaabababaababaabababaababaabababaabab...
|
|
945
|
+
|
|
946
|
+
TESTS:
|
|
947
|
+
|
|
948
|
+
The empty word::
|
|
949
|
+
|
|
950
|
+
sage: w = Word()
|
|
951
|
+
sage: it = w._iterated_right_palindromic_closure_recursive_iterator()
|
|
952
|
+
sage: next(it)
|
|
953
|
+
Traceback (most recent call last):
|
|
954
|
+
...
|
|
955
|
+
StopIteration
|
|
956
|
+
|
|
957
|
+
REFERENCES:
|
|
958
|
+
|
|
959
|
+
- [1] A. de Luca, A. De Luca, Pseudopalindrome closure operators
|
|
960
|
+
in free monoids, Theoret. Comput. Sci. 362 (2006) 282--300.
|
|
961
|
+
- [2] J. Justin, Episturmian morphisms and a Galois theorem on
|
|
962
|
+
continued fractions, RAIRO Theoret. Informatics Appl. 39 (2005)
|
|
963
|
+
207-215.
|
|
964
|
+
"""
|
|
965
|
+
parent = self.parent().factors()
|
|
966
|
+
ipcw = self[:0]
|
|
967
|
+
lengths = []
|
|
968
|
+
for i, letter in enumerate(self):
|
|
969
|
+
lengths.append(ipcw.length())
|
|
970
|
+
w = self[:i]
|
|
971
|
+
pos = w.rfind(parent([letter]))
|
|
972
|
+
if pos == -1:
|
|
973
|
+
to_append = parent([letter]).palindromic_closure(f=f) + ipcw
|
|
974
|
+
else:
|
|
975
|
+
to_append = ipcw[lengths[pos]:]
|
|
976
|
+
ipcw += to_append
|
|
977
|
+
yield from to_append
|
|
978
|
+
|
|
979
|
+
def iterated_right_palindromic_closure(self, f=None, algorithm='recursive'):
|
|
980
|
+
r"""
|
|
981
|
+
Return the iterated (`f`-)palindromic closure of ``self``.
|
|
982
|
+
|
|
983
|
+
INPUT:
|
|
984
|
+
|
|
985
|
+
- ``f`` -- involution (default: ``None``) on the alphabet of ``self``;
|
|
986
|
+
it must be callable on letters as well as words (e.g. WordMorphism)
|
|
987
|
+
|
|
988
|
+
- ``algorithm`` -- string (default: ``'recursive'``); specifying which
|
|
989
|
+
algorithm to be used when computing the iterated palindromic closure.
|
|
990
|
+
It must be one of the two following values:
|
|
991
|
+
|
|
992
|
+
- ``'definition'`` -- computed using the definition
|
|
993
|
+
- ``'recursive'`` -- computation based on an efficient formula
|
|
994
|
+
that recursively computes the iterated right palindromic closure
|
|
995
|
+
without having to recompute the longest `f`-palindromic suffix
|
|
996
|
+
at each iteration [2].
|
|
997
|
+
|
|
998
|
+
OUTPUT: word; the iterated (`f`-)palindromic closure of ``self``
|
|
999
|
+
|
|
1000
|
+
EXAMPLES::
|
|
1001
|
+
|
|
1002
|
+
sage: Word('123').iterated_right_palindromic_closure()
|
|
1003
|
+
word: 1213121
|
|
1004
|
+
|
|
1005
|
+
::
|
|
1006
|
+
|
|
1007
|
+
sage: w = Word('abc')
|
|
1008
|
+
sage: w.iterated_right_palindromic_closure()
|
|
1009
|
+
word: abacaba
|
|
1010
|
+
|
|
1011
|
+
::
|
|
1012
|
+
|
|
1013
|
+
sage: w = Word('aaa')
|
|
1014
|
+
sage: w.iterated_right_palindromic_closure()
|
|
1015
|
+
word: aaa
|
|
1016
|
+
|
|
1017
|
+
::
|
|
1018
|
+
|
|
1019
|
+
sage: w = Word('abbab')
|
|
1020
|
+
sage: w.iterated_right_palindromic_closure()
|
|
1021
|
+
word: ababaabababaababa
|
|
1022
|
+
|
|
1023
|
+
A right `f`-palindromic closure::
|
|
1024
|
+
|
|
1025
|
+
sage: f = WordMorphism('a->b,b->a')
|
|
1026
|
+
sage: w = Word('abbab')
|
|
1027
|
+
sage: w.iterated_right_palindromic_closure(f=f)
|
|
1028
|
+
word: abbaabbaababbaabbaabbaababbaabbaab
|
|
1029
|
+
|
|
1030
|
+
An infinite word::
|
|
1031
|
+
|
|
1032
|
+
sage: t = words.ThueMorseWord('ab')
|
|
1033
|
+
sage: t.iterated_right_palindromic_closure()
|
|
1034
|
+
word: ababaabababaababaabababaababaabababaabab...
|
|
1035
|
+
|
|
1036
|
+
There are two implementations computing the iterated right
|
|
1037
|
+
`f`-palindromic closure, the latter being much more efficient::
|
|
1038
|
+
|
|
1039
|
+
sage: w = Word('abaab')
|
|
1040
|
+
sage: u = w.iterated_right_palindromic_closure(algorithm='definition')
|
|
1041
|
+
sage: v = w.iterated_right_palindromic_closure(algorithm='recursive')
|
|
1042
|
+
sage: u
|
|
1043
|
+
word: abaabaababaabaaba
|
|
1044
|
+
sage: u == v
|
|
1045
|
+
True
|
|
1046
|
+
sage: w = words.RandomWord(8)
|
|
1047
|
+
sage: u = w.iterated_right_palindromic_closure(algorithm='definition')
|
|
1048
|
+
sage: v = w.iterated_right_palindromic_closure(algorithm='recursive')
|
|
1049
|
+
sage: u == v
|
|
1050
|
+
True
|
|
1051
|
+
|
|
1052
|
+
TESTS:
|
|
1053
|
+
|
|
1054
|
+
The empty word::
|
|
1055
|
+
|
|
1056
|
+
sage: w = Word()
|
|
1057
|
+
sage: w.iterated_right_palindromic_closure()
|
|
1058
|
+
word:
|
|
1059
|
+
|
|
1060
|
+
The length-`1` word::
|
|
1061
|
+
|
|
1062
|
+
sage: Word('1').iterated_right_palindromic_closure()
|
|
1063
|
+
word: 1
|
|
1064
|
+
|
|
1065
|
+
If the word is finite, so is the result::
|
|
1066
|
+
|
|
1067
|
+
sage: w = Word([0,1]*7)
|
|
1068
|
+
sage: c = w.iterated_right_palindromic_closure()
|
|
1069
|
+
sage: type(c)
|
|
1070
|
+
<class 'sage.combinat.words.word.FiniteWord_iter_with_caching'>
|
|
1071
|
+
|
|
1072
|
+
REFERENCES:
|
|
1073
|
+
|
|
1074
|
+
- [1] A. de Luca, A. De Luca, Pseudopalindrome closure operators
|
|
1075
|
+
in free monoids, Theoret. Comput. Sci. 362 (2006) 282--300.
|
|
1076
|
+
- [2] J. Justin, Episturmian morphisms and a Galois theorem on
|
|
1077
|
+
continued fractions, RAIRO Theoret. Informatics Appl. 39 (2005)
|
|
1078
|
+
207-215.
|
|
1079
|
+
"""
|
|
1080
|
+
if algorithm == 'definition':
|
|
1081
|
+
it = self._iterated_right_palindromic_closure_iterator(f=f)
|
|
1082
|
+
elif algorithm == 'recursive':
|
|
1083
|
+
it = self._iterated_right_palindromic_closure_recursive_iterator(f=f)
|
|
1084
|
+
else:
|
|
1085
|
+
raise ValueError("algorithm (=%s) must be either 'definition' or 'recursive'")
|
|
1086
|
+
|
|
1087
|
+
if self.is_finite():
|
|
1088
|
+
return self._parent(it)
|
|
1089
|
+
else:
|
|
1090
|
+
from sage.combinat.words.words import Words
|
|
1091
|
+
parent = Words(self._parent.alphabet())
|
|
1092
|
+
return parent(it)
|
|
1093
|
+
|
|
1094
|
+
def prefixes_iterator(self, max_length=None):
|
|
1095
|
+
r"""
|
|
1096
|
+
Return an iterator over the prefixes of ``self``.
|
|
1097
|
+
|
|
1098
|
+
INPUT:
|
|
1099
|
+
|
|
1100
|
+
- ``max_length`` -- nonnegative integer or ``None`` (default); the
|
|
1101
|
+
maximum length of the prefixes
|
|
1102
|
+
|
|
1103
|
+
OUTPUT: iterator
|
|
1104
|
+
|
|
1105
|
+
EXAMPLES::
|
|
1106
|
+
|
|
1107
|
+
sage: w = Word('abaaba')
|
|
1108
|
+
sage: for p in w.prefixes_iterator(): p
|
|
1109
|
+
word:
|
|
1110
|
+
word: a
|
|
1111
|
+
word: ab
|
|
1112
|
+
word: aba
|
|
1113
|
+
word: abaa
|
|
1114
|
+
word: abaab
|
|
1115
|
+
word: abaaba
|
|
1116
|
+
sage: for p in w.prefixes_iterator(max_length=3): p
|
|
1117
|
+
word:
|
|
1118
|
+
word: a
|
|
1119
|
+
word: ab
|
|
1120
|
+
word: aba
|
|
1121
|
+
|
|
1122
|
+
You can iterate over the prefixes of an infinite word::
|
|
1123
|
+
|
|
1124
|
+
sage: f = words.FibonacciWord()
|
|
1125
|
+
sage: for p in f.prefixes_iterator(max_length=8): p
|
|
1126
|
+
word:
|
|
1127
|
+
word: 0
|
|
1128
|
+
word: 01
|
|
1129
|
+
word: 010
|
|
1130
|
+
word: 0100
|
|
1131
|
+
word: 01001
|
|
1132
|
+
word: 010010
|
|
1133
|
+
word: 0100101
|
|
1134
|
+
word: 01001010
|
|
1135
|
+
|
|
1136
|
+
TESTS::
|
|
1137
|
+
|
|
1138
|
+
sage: list(f.prefixes_iterator(max_length=0))
|
|
1139
|
+
[word: ]
|
|
1140
|
+
"""
|
|
1141
|
+
to_consider = self if max_length is None else self[:max_length]
|
|
1142
|
+
yield self[:0]
|
|
1143
|
+
for (i, a) in enumerate(to_consider):
|
|
1144
|
+
yield self[:i + 1]
|
|
1145
|
+
|
|
1146
|
+
def palindrome_prefixes_iterator(self, max_length=None):
|
|
1147
|
+
r"""
|
|
1148
|
+
Return an iterator over the palindrome prefixes of ``self``.
|
|
1149
|
+
|
|
1150
|
+
INPUT:
|
|
1151
|
+
|
|
1152
|
+
- ``max_length`` -- nonnegative integer or ``None`` (default); the
|
|
1153
|
+
maximum length of the prefixes
|
|
1154
|
+
|
|
1155
|
+
OUTPUT: iterator
|
|
1156
|
+
|
|
1157
|
+
EXAMPLES::
|
|
1158
|
+
|
|
1159
|
+
sage: w = Word('abaaba')
|
|
1160
|
+
sage: for pp in w.palindrome_prefixes_iterator(): pp
|
|
1161
|
+
word:
|
|
1162
|
+
word: a
|
|
1163
|
+
word: aba
|
|
1164
|
+
word: abaaba
|
|
1165
|
+
sage: for pp in w.palindrome_prefixes_iterator(max_length=4): pp
|
|
1166
|
+
word:
|
|
1167
|
+
word: a
|
|
1168
|
+
word: aba
|
|
1169
|
+
|
|
1170
|
+
You can iterate over the palindrome prefixes of an infinite word::
|
|
1171
|
+
|
|
1172
|
+
sage: f = words.FibonacciWord()
|
|
1173
|
+
sage: for pp in f.palindrome_prefixes_iterator(max_length=20): pp
|
|
1174
|
+
word:
|
|
1175
|
+
word: 0
|
|
1176
|
+
word: 010
|
|
1177
|
+
word: 010010
|
|
1178
|
+
word: 01001010010
|
|
1179
|
+
word: 0100101001001010010
|
|
1180
|
+
"""
|
|
1181
|
+
for p in self.prefixes_iterator(max_length):
|
|
1182
|
+
if p.is_palindrome():
|
|
1183
|
+
yield p
|
|
1184
|
+
|
|
1185
|
+
def _partial_sums_iterator(self, start, mod=None):
|
|
1186
|
+
r"""
|
|
1187
|
+
Iterator over the partial sums of the prefixes of ``self``.
|
|
1188
|
+
|
|
1189
|
+
INPUT:
|
|
1190
|
+
|
|
1191
|
+
- ``self`` -- a word over the integers
|
|
1192
|
+
- ``start`` -- integer; the first letter of the resulting word
|
|
1193
|
+
- ``mod`` -- (default: ``None``) it can be one of the following:
|
|
1194
|
+
- None or 0 : result is over the integers
|
|
1195
|
+
- integer : result is over the integers modulo ``mod``.
|
|
1196
|
+
|
|
1197
|
+
EXAMPLES::
|
|
1198
|
+
|
|
1199
|
+
sage: w = Word(range(8))
|
|
1200
|
+
sage: list(w._partial_sums_iterator(0, mod=10))
|
|
1201
|
+
[0, 0, 1, 3, 6, 0, 5, 1, 8]
|
|
1202
|
+
|
|
1203
|
+
::
|
|
1204
|
+
|
|
1205
|
+
sage: w = Word([1,1,1,1,1,1,1,1,1,1,1,1])
|
|
1206
|
+
sage: list(w._partial_sums_iterator(0, mod=10))
|
|
1207
|
+
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2]
|
|
1208
|
+
|
|
1209
|
+
::
|
|
1210
|
+
|
|
1211
|
+
sage: w = Word([1,1,1,1,1,1,1,1,1,1,1,1])
|
|
1212
|
+
sage: list(w._partial_sums_iterator(0))
|
|
1213
|
+
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
|
|
1214
|
+
"""
|
|
1215
|
+
if mod in (None, 0):
|
|
1216
|
+
sum = start
|
|
1217
|
+
|
|
1218
|
+
elif mod in ZZ:
|
|
1219
|
+
Zn = Integers(mod)
|
|
1220
|
+
sum = Zn(start)
|
|
1221
|
+
|
|
1222
|
+
else:
|
|
1223
|
+
raise TypeError('mod(=%s) must be None or an integer' % mod)
|
|
1224
|
+
|
|
1225
|
+
yield sum
|
|
1226
|
+
for letter in self:
|
|
1227
|
+
sum += letter
|
|
1228
|
+
yield sum
|
|
1229
|
+
|
|
1230
|
+
def partial_sums(self, start, mod=None):
|
|
1231
|
+
r"""
|
|
1232
|
+
Return the word defined by the partial sums of its prefixes.
|
|
1233
|
+
|
|
1234
|
+
INPUT:
|
|
1235
|
+
|
|
1236
|
+
- ``self`` -- a word over the integers
|
|
1237
|
+
- ``start`` -- integer; the first letter of the resulting word
|
|
1238
|
+
- ``mod`` -- (default: ``None``) it can be one of the following:
|
|
1239
|
+
- None or 0 : result is over the integers
|
|
1240
|
+
- integer : result is over the integers modulo ``mod``.
|
|
1241
|
+
|
|
1242
|
+
EXAMPLES::
|
|
1243
|
+
|
|
1244
|
+
sage: w = Word(range(10))
|
|
1245
|
+
sage: w.partial_sums(0)
|
|
1246
|
+
word: 0,0,1,3,6,10,15,21,28,36,45
|
|
1247
|
+
sage: w.partial_sums(1)
|
|
1248
|
+
word: 1,1,2,4,7,11,16,22,29,37,46
|
|
1249
|
+
|
|
1250
|
+
::
|
|
1251
|
+
|
|
1252
|
+
sage: w = Word([1,2,3,1,2,3,2,2,2,2])
|
|
1253
|
+
sage: w.partial_sums(0, mod=None)
|
|
1254
|
+
word: 0,1,3,6,7,9,12,14,16,18,20
|
|
1255
|
+
sage: w.partial_sums(0, mod=0)
|
|
1256
|
+
word: 0,1,3,6,7,9,12,14,16,18,20
|
|
1257
|
+
sage: w.partial_sums(0, mod=8)
|
|
1258
|
+
word: 01367146024
|
|
1259
|
+
sage: w.partial_sums(0, mod=4)
|
|
1260
|
+
word: 01323102020
|
|
1261
|
+
sage: w.partial_sums(0, mod=2)
|
|
1262
|
+
word: 01101100000
|
|
1263
|
+
sage: w.partial_sums(0, mod=1)
|
|
1264
|
+
word: 00000000000
|
|
1265
|
+
|
|
1266
|
+
TESTS:
|
|
1267
|
+
|
|
1268
|
+
If the word is infinite, so is the result::
|
|
1269
|
+
|
|
1270
|
+
sage: w = Word(lambda n:1)
|
|
1271
|
+
sage: u = w.partial_sums(0)
|
|
1272
|
+
sage: type(u)
|
|
1273
|
+
<class 'sage.combinat.words.word.InfiniteWord_iter_with_caching'>
|
|
1274
|
+
"""
|
|
1275
|
+
it = self._partial_sums_iterator(start=start, mod=mod)
|
|
1276
|
+
|
|
1277
|
+
if mod in (None, 0):
|
|
1278
|
+
alphabet = None
|
|
1279
|
+
elif mod in ZZ:
|
|
1280
|
+
alphabet = Integers(mod)
|
|
1281
|
+
|
|
1282
|
+
if self.is_finite():
|
|
1283
|
+
length = "finite"
|
|
1284
|
+
elif self.length() == Infinity:
|
|
1285
|
+
length = "infinite"
|
|
1286
|
+
else:
|
|
1287
|
+
length = "unknown"
|
|
1288
|
+
from sage.combinat.words.word import Word
|
|
1289
|
+
return Word(it, alphabet=alphabet, length=length)
|
|
1290
|
+
|
|
1291
|
+
def _finite_differences_iterator(self, mod=None):
|
|
1292
|
+
r"""
|
|
1293
|
+
Iterator over the differences of consecutive letters of ``self``.
|
|
1294
|
+
|
|
1295
|
+
INPUT:
|
|
1296
|
+
|
|
1297
|
+
- ``self`` -- a word over the integers
|
|
1298
|
+
- ``mod`` -- (default: ``None``) it can be one of the following:
|
|
1299
|
+
- None or 0 : result is over the integers
|
|
1300
|
+
- integer : result is over the integers modulo ``mod``.
|
|
1301
|
+
|
|
1302
|
+
EXAMPLES::
|
|
1303
|
+
|
|
1304
|
+
sage: w = Word(x^2 for x in range(10))
|
|
1305
|
+
sage: list(w._finite_differences_iterator())
|
|
1306
|
+
[1, 3, 5, 7, 9, 11, 13, 15, 17]
|
|
1307
|
+
|
|
1308
|
+
::
|
|
1309
|
+
|
|
1310
|
+
sage: w = Word([1,6,8,4,2,6,8,2,3])
|
|
1311
|
+
sage: list(w._finite_differences_iterator())
|
|
1312
|
+
[5, 2, -4, -2, 4, 2, -6, 1]
|
|
1313
|
+
sage: list(w._finite_differences_iterator(4))
|
|
1314
|
+
[1, 2, 0, 2, 0, 2, 2, 1]
|
|
1315
|
+
sage: list(w._finite_differences_iterator(5))
|
|
1316
|
+
[0, 2, 1, 3, 4, 2, 4, 1]
|
|
1317
|
+
|
|
1318
|
+
TESTS::
|
|
1319
|
+
|
|
1320
|
+
sage: w = Word([2,3,6])
|
|
1321
|
+
sage: list(w._finite_differences_iterator())
|
|
1322
|
+
[1, 3]
|
|
1323
|
+
sage: w = Word([2,6])
|
|
1324
|
+
sage: list(w._finite_differences_iterator())
|
|
1325
|
+
[4]
|
|
1326
|
+
sage: w = Word([2])
|
|
1327
|
+
sage: list(w._finite_differences_iterator())
|
|
1328
|
+
[]
|
|
1329
|
+
sage: w = Word()
|
|
1330
|
+
sage: list(w._finite_differences_iterator())
|
|
1331
|
+
[]
|
|
1332
|
+
|
|
1333
|
+
::
|
|
1334
|
+
|
|
1335
|
+
sage: list(w._finite_differences_iterator('a'))
|
|
1336
|
+
Traceback (most recent call last):
|
|
1337
|
+
...
|
|
1338
|
+
TypeError: mod(=a) must be None or an integer
|
|
1339
|
+
"""
|
|
1340
|
+
if mod in (None, 0):
|
|
1341
|
+
i = iter(self)
|
|
1342
|
+
j = iter(self)
|
|
1343
|
+
|
|
1344
|
+
try:
|
|
1345
|
+
next(j)
|
|
1346
|
+
while True:
|
|
1347
|
+
yield next(j) - next(i)
|
|
1348
|
+
except StopIteration:
|
|
1349
|
+
return
|
|
1350
|
+
|
|
1351
|
+
elif mod in ZZ:
|
|
1352
|
+
Zn = Integers(mod)
|
|
1353
|
+
i = iter(self)
|
|
1354
|
+
j = iter(self)
|
|
1355
|
+
|
|
1356
|
+
try:
|
|
1357
|
+
next(j)
|
|
1358
|
+
while True:
|
|
1359
|
+
yield Zn(next(j) - next(i))
|
|
1360
|
+
except StopIteration:
|
|
1361
|
+
return
|
|
1362
|
+
|
|
1363
|
+
else:
|
|
1364
|
+
raise TypeError('mod(=%s) must be None or an integer' % mod)
|
|
1365
|
+
|
|
1366
|
+
def finite_differences(self, mod=None):
|
|
1367
|
+
r"""
|
|
1368
|
+
Return the word obtained by the differences of consecutive letters
|
|
1369
|
+
of ``self``.
|
|
1370
|
+
|
|
1371
|
+
INPUT:
|
|
1372
|
+
|
|
1373
|
+
- ``self`` -- a word over the integers
|
|
1374
|
+
- ``mod`` -- (default: ``None``) it can be one of the following:
|
|
1375
|
+
- None or 0 : result is over the integers
|
|
1376
|
+
- integer : result is over the integers modulo ``mod``.
|
|
1377
|
+
|
|
1378
|
+
EXAMPLES::
|
|
1379
|
+
|
|
1380
|
+
sage: w = Word([x^2 for x in range(10)])
|
|
1381
|
+
sage: w.finite_differences()
|
|
1382
|
+
word: 1,3,5,7,9,11,13,15,17
|
|
1383
|
+
sage: w.finite_differences(mod=4)
|
|
1384
|
+
word: 131313131
|
|
1385
|
+
sage: w.finite_differences(mod=0)
|
|
1386
|
+
word: 1,3,5,7,9,11,13,15,17
|
|
1387
|
+
|
|
1388
|
+
TESTS::
|
|
1389
|
+
|
|
1390
|
+
sage: w = Word([2,3,6])
|
|
1391
|
+
sage: w.finite_differences()
|
|
1392
|
+
word: 13
|
|
1393
|
+
sage: w = Word([2,6])
|
|
1394
|
+
sage: w.finite_differences()
|
|
1395
|
+
word: 4
|
|
1396
|
+
sage: w = Word([2])
|
|
1397
|
+
sage: w.finite_differences()
|
|
1398
|
+
word:
|
|
1399
|
+
sage: w = Word()
|
|
1400
|
+
sage: w.finite_differences()
|
|
1401
|
+
word:
|
|
1402
|
+
|
|
1403
|
+
If the word is infinite, so is the result::
|
|
1404
|
+
|
|
1405
|
+
sage: w = Word(lambda n:n)
|
|
1406
|
+
sage: u = w.finite_differences()
|
|
1407
|
+
sage: u
|
|
1408
|
+
word: 1111111111111111111111111111111111111111...
|
|
1409
|
+
sage: type(u)
|
|
1410
|
+
<class 'sage.combinat.words.word.InfiniteWord_iter_with_caching'>
|
|
1411
|
+
"""
|
|
1412
|
+
it = self._finite_differences_iterator(mod=mod)
|
|
1413
|
+
|
|
1414
|
+
if mod in (None, 0):
|
|
1415
|
+
alphabet = None
|
|
1416
|
+
elif mod in ZZ:
|
|
1417
|
+
alphabet = Integers(mod)
|
|
1418
|
+
|
|
1419
|
+
if self.is_finite():
|
|
1420
|
+
length = "finite"
|
|
1421
|
+
elif self.length() == Infinity:
|
|
1422
|
+
length = "infinite"
|
|
1423
|
+
else:
|
|
1424
|
+
length = "unknown"
|
|
1425
|
+
from sage.combinat.words.word import Word
|
|
1426
|
+
return Word(it, alphabet=alphabet, length=length)
|
|
1427
|
+
|
|
1428
|
+
def sum_digits(self, base=2, mod=None):
|
|
1429
|
+
r"""
|
|
1430
|
+
Return the sequence of the sum modulo ``mod`` of the digits written
|
|
1431
|
+
in base ``base`` of ``self``.
|
|
1432
|
+
|
|
1433
|
+
INPUT:
|
|
1434
|
+
|
|
1435
|
+
- ``self`` -- word over natural numbers
|
|
1436
|
+
|
|
1437
|
+
- ``base`` -- integer (default: 2) greater or equal to 2
|
|
1438
|
+
|
|
1439
|
+
- ``mod`` -- modulo (default: ``None``); can take the following
|
|
1440
|
+
values:
|
|
1441
|
+
- ``integer`` -- the modulo
|
|
1442
|
+
- ``None`` -- the value ``base`` is considered for the modulo
|
|
1443
|
+
|
|
1444
|
+
EXAMPLES:
|
|
1445
|
+
|
|
1446
|
+
The Thue-Morse word::
|
|
1447
|
+
|
|
1448
|
+
sage: from itertools import count
|
|
1449
|
+
sage: Word(count()).sum_digits()
|
|
1450
|
+
word: 0110100110010110100101100110100110010110...
|
|
1451
|
+
|
|
1452
|
+
Sum of digits modulo 2 of the prime numbers written in base 2::
|
|
1453
|
+
|
|
1454
|
+
sage: Word(primes(1000)).sum_digits() # needs sage.libs.pari
|
|
1455
|
+
word: 1001110100111010111011001011101110011011...
|
|
1456
|
+
|
|
1457
|
+
Sum of digits modulo 3 of the prime numbers written in base 3::
|
|
1458
|
+
|
|
1459
|
+
sage: Word(primes(1000)).sum_digits(base=3) # needs sage.libs.pari
|
|
1460
|
+
word: 2100002020002221222121022221022122111022...
|
|
1461
|
+
sage: Word(primes(1000)).sum_digits(base=3, mod=3) # needs sage.libs.pari
|
|
1462
|
+
word: 2100002020002221222121022221022122111022...
|
|
1463
|
+
|
|
1464
|
+
Sum of digits modulo 2 of the prime numbers written in base 3::
|
|
1465
|
+
|
|
1466
|
+
sage: Word(primes(1000)).sum_digits(base=3, mod=2) # needs sage.libs.pari
|
|
1467
|
+
word: 0111111111111111111111111111111111111111...
|
|
1468
|
+
|
|
1469
|
+
Sum of digits modulo 7 of the prime numbers written in base 10::
|
|
1470
|
+
|
|
1471
|
+
sage: Word(primes(1000)).sum_digits(base=10, mod=7) # needs sage.libs.pari
|
|
1472
|
+
word: 2350241354435041006132432241353546006304...
|
|
1473
|
+
|
|
1474
|
+
Negative entries::
|
|
1475
|
+
|
|
1476
|
+
sage: w = Word([-1,0,1,2,3,4,5])
|
|
1477
|
+
sage: w.sum_digits()
|
|
1478
|
+
Traceback (most recent call last):
|
|
1479
|
+
...
|
|
1480
|
+
NotImplementedError: nth digit of Thue-Morse word is not implemented for negative value of n
|
|
1481
|
+
|
|
1482
|
+
TESTS:
|
|
1483
|
+
|
|
1484
|
+
The Thue-Morse word::
|
|
1485
|
+
|
|
1486
|
+
sage: from itertools import count
|
|
1487
|
+
sage: w = Word(count()).sum_digits()
|
|
1488
|
+
sage: t = words.ThueMorseWord()
|
|
1489
|
+
sage: w[:100] == t[:100]
|
|
1490
|
+
True
|
|
1491
|
+
|
|
1492
|
+
::
|
|
1493
|
+
|
|
1494
|
+
sage: type(Word(range(10)).sum_digits())
|
|
1495
|
+
<class 'sage.combinat.words.word.FiniteWord_iter_with_caching'>
|
|
1496
|
+
"""
|
|
1497
|
+
from functools import partial
|
|
1498
|
+
from sage.combinat.words.word_generators import words
|
|
1499
|
+
|
|
1500
|
+
# The alphabet
|
|
1501
|
+
if mod is None and base >= 2:
|
|
1502
|
+
alphabet = list(range(base))
|
|
1503
|
+
elif mod in ZZ and mod >= 2:
|
|
1504
|
+
alphabet = list(range(mod))
|
|
1505
|
+
else:
|
|
1506
|
+
raise ValueError("base (=%s) and mod (=%s) must be integers greater or equal to 2" % (base, mod))
|
|
1507
|
+
|
|
1508
|
+
# The iterator
|
|
1509
|
+
f = partial(words._ThueMorseWord_nth_digit, alphabet=alphabet, base=base)
|
|
1510
|
+
it = (f(a) for a in self)
|
|
1511
|
+
|
|
1512
|
+
# The length
|
|
1513
|
+
if self.is_finite():
|
|
1514
|
+
length = "finite"
|
|
1515
|
+
elif self.length() == Infinity:
|
|
1516
|
+
length = None
|
|
1517
|
+
else:
|
|
1518
|
+
length = "unknown"
|
|
1519
|
+
|
|
1520
|
+
from sage.combinat.words.word import Word
|
|
1521
|
+
return Word(it, alphabet=alphabet, length=length, datatype='iter')
|
|
1522
|
+
|
|
1523
|
+
def first_occurrence(self, other, start=0):
|
|
1524
|
+
r"""
|
|
1525
|
+
Return the position of the first occurrence of ``other`` in ``self``.
|
|
1526
|
+
|
|
1527
|
+
If ``other`` is not a factor of ``self``, it returns ``None``
|
|
1528
|
+
or loops forever when ``self`` is an infinite word.
|
|
1529
|
+
|
|
1530
|
+
INPUT:
|
|
1531
|
+
|
|
1532
|
+
- ``other`` -- a finite word
|
|
1533
|
+
- ``start`` -- integer (default: `0`) where the search starts
|
|
1534
|
+
|
|
1535
|
+
OUTPUT: integer or ``None``
|
|
1536
|
+
|
|
1537
|
+
EXAMPLES::
|
|
1538
|
+
|
|
1539
|
+
sage: w = Word('01234567890123456789')
|
|
1540
|
+
sage: w.first_occurrence(Word('3456'))
|
|
1541
|
+
3
|
|
1542
|
+
sage: w.first_occurrence(Word('3456'), start=7)
|
|
1543
|
+
13
|
|
1544
|
+
|
|
1545
|
+
When the factor is not present, ``None`` is returned::
|
|
1546
|
+
|
|
1547
|
+
sage: w.first_occurrence(Word('3456'), start=17) is None
|
|
1548
|
+
True
|
|
1549
|
+
sage: w.first_occurrence(Word('3333')) is None
|
|
1550
|
+
True
|
|
1551
|
+
|
|
1552
|
+
Also works for searching a finite word in an infinite word::
|
|
1553
|
+
|
|
1554
|
+
sage: w = Word('0123456789')^oo
|
|
1555
|
+
sage: w.first_occurrence(Word('3456'))
|
|
1556
|
+
3
|
|
1557
|
+
sage: w.first_occurrence(Word('3456'), start=1000)
|
|
1558
|
+
1003
|
|
1559
|
+
|
|
1560
|
+
But it will loop for ever if the factor is not found::
|
|
1561
|
+
|
|
1562
|
+
sage: w.first_occurrence(Word('3333')) # not tested -- infinite loop
|
|
1563
|
+
|
|
1564
|
+
The empty word occurs in a word::
|
|
1565
|
+
|
|
1566
|
+
sage: Word('123').first_occurrence(Word(''), 0)
|
|
1567
|
+
0
|
|
1568
|
+
sage: Word('').first_occurrence(Word(''), 0)
|
|
1569
|
+
0
|
|
1570
|
+
"""
|
|
1571
|
+
lf = other.length()
|
|
1572
|
+
lm = self.length()
|
|
1573
|
+
if lf == 0:
|
|
1574
|
+
return start
|
|
1575
|
+
elif lm == 0:
|
|
1576
|
+
return None
|
|
1577
|
+
occ = other.last_position_dict()
|
|
1578
|
+
suff = other.good_suffix_table()
|
|
1579
|
+
s = start
|
|
1580
|
+
while s <= lm - lf:
|
|
1581
|
+
for j in range(lf-1, -1, -1):
|
|
1582
|
+
a = self[s+j]
|
|
1583
|
+
if other[j] != a:
|
|
1584
|
+
s += max(suff[j + 1], j - occ.get(a, -1))
|
|
1585
|
+
break
|
|
1586
|
+
else:
|
|
1587
|
+
return s
|
|
1588
|
+
return None
|
|
1589
|
+
|
|
1590
|
+
def factor_occurrences_iterator(self, fact):
|
|
1591
|
+
r"""
|
|
1592
|
+
Return an iterator over all occurrences (including overlapping ones)
|
|
1593
|
+
of fact in ``self`` in their order of appearance.
|
|
1594
|
+
|
|
1595
|
+
INPUT:
|
|
1596
|
+
|
|
1597
|
+
- ``fact`` -- a non empty finite word
|
|
1598
|
+
|
|
1599
|
+
OUTPUT: iterator
|
|
1600
|
+
|
|
1601
|
+
EXAMPLES::
|
|
1602
|
+
|
|
1603
|
+
sage: TM = words.ThueMorseWord()
|
|
1604
|
+
sage: fact = Word([0,1,1,0,1])
|
|
1605
|
+
sage: it = TM.factor_occurrences_iterator(fact)
|
|
1606
|
+
sage: next(it)
|
|
1607
|
+
0
|
|
1608
|
+
sage: next(it)
|
|
1609
|
+
12
|
|
1610
|
+
sage: next(it)
|
|
1611
|
+
24
|
|
1612
|
+
|
|
1613
|
+
::
|
|
1614
|
+
|
|
1615
|
+
sage: u = Word('121')
|
|
1616
|
+
sage: w = Word('121213211213')
|
|
1617
|
+
sage: list(w.factor_occurrences_iterator(u))
|
|
1618
|
+
[0, 2, 8]
|
|
1619
|
+
"""
|
|
1620
|
+
if fact.is_empty():
|
|
1621
|
+
raise NotImplementedError("The factor must be non empty")
|
|
1622
|
+
if not fact.is_finite():
|
|
1623
|
+
raise ValueError("The factor must be finite")
|
|
1624
|
+
p = self.first_occurrence(fact, start=0)
|
|
1625
|
+
while p is not None:
|
|
1626
|
+
yield p
|
|
1627
|
+
p = self.first_occurrence(fact, start=p+1)
|
|
1628
|
+
|
|
1629
|
+
def return_words_iterator(self, fact):
|
|
1630
|
+
r"""
|
|
1631
|
+
Return an iterator over all the return words of fact in self
|
|
1632
|
+
(without unicity).
|
|
1633
|
+
|
|
1634
|
+
INPUT:
|
|
1635
|
+
|
|
1636
|
+
- ``fact`` -- a non empty finite word
|
|
1637
|
+
|
|
1638
|
+
OUTPUT: iterator
|
|
1639
|
+
|
|
1640
|
+
EXAMPLES::
|
|
1641
|
+
|
|
1642
|
+
sage: w = Word('baccabccbacbca')
|
|
1643
|
+
sage: b = Word('b')
|
|
1644
|
+
sage: list(w.return_words_iterator(b))
|
|
1645
|
+
[word: bacca, word: bcc, word: bac]
|
|
1646
|
+
|
|
1647
|
+
::
|
|
1648
|
+
|
|
1649
|
+
sage: TM = words.ThueMorseWord()
|
|
1650
|
+
sage: fact = Word([0,1,1,0,1])
|
|
1651
|
+
sage: it = TM.return_words_iterator(fact)
|
|
1652
|
+
sage: next(it)
|
|
1653
|
+
word: 011010011001
|
|
1654
|
+
sage: next(it)
|
|
1655
|
+
word: 011010010110
|
|
1656
|
+
sage: next(it)
|
|
1657
|
+
word: 0110100110010110
|
|
1658
|
+
sage: next(it)
|
|
1659
|
+
word: 01101001
|
|
1660
|
+
sage: next(it)
|
|
1661
|
+
word: 011010011001
|
|
1662
|
+
sage: next(it)
|
|
1663
|
+
word: 011010010110
|
|
1664
|
+
"""
|
|
1665
|
+
it = self.factor_occurrences_iterator(fact)
|
|
1666
|
+
try:
|
|
1667
|
+
i = next(it)
|
|
1668
|
+
while True:
|
|
1669
|
+
j = next(it)
|
|
1670
|
+
yield self[i:j]
|
|
1671
|
+
i = j
|
|
1672
|
+
except StopIteration:
|
|
1673
|
+
return
|
|
1674
|
+
|
|
1675
|
+
def complete_return_words_iterator(self, fact):
|
|
1676
|
+
r"""
|
|
1677
|
+
Return an iterator over all the complete return words of fact in
|
|
1678
|
+
``self`` (without unicity).
|
|
1679
|
+
|
|
1680
|
+
A complete return words `u` of a factor `v` is a factor starting
|
|
1681
|
+
by the given factor `v` and ending just after the next occurrence
|
|
1682
|
+
of this factor `v`. See for instance [1].
|
|
1683
|
+
|
|
1684
|
+
INPUT:
|
|
1685
|
+
|
|
1686
|
+
- ``fact`` -- a non empty finite word
|
|
1687
|
+
|
|
1688
|
+
OUTPUT: iterator
|
|
1689
|
+
|
|
1690
|
+
EXAMPLES::
|
|
1691
|
+
|
|
1692
|
+
sage: TM = words.ThueMorseWord()
|
|
1693
|
+
sage: fact = Word([0,1,1,0,1])
|
|
1694
|
+
sage: it = TM.complete_return_words_iterator(fact)
|
|
1695
|
+
sage: next(it)
|
|
1696
|
+
word: 01101001100101101
|
|
1697
|
+
sage: next(it)
|
|
1698
|
+
word: 01101001011001101
|
|
1699
|
+
sage: next(it)
|
|
1700
|
+
word: 011010011001011001101
|
|
1701
|
+
sage: next(it)
|
|
1702
|
+
word: 0110100101101
|
|
1703
|
+
sage: next(it)
|
|
1704
|
+
word: 01101001100101101
|
|
1705
|
+
sage: next(it)
|
|
1706
|
+
word: 01101001011001101
|
|
1707
|
+
|
|
1708
|
+
REFERENCES:
|
|
1709
|
+
|
|
1710
|
+
- [1] J. Justin, L. Vuillon, Return words in Sturmian and
|
|
1711
|
+
episturmian words, Theor. Inform. Appl. 34 (2000) 343--356.
|
|
1712
|
+
"""
|
|
1713
|
+
it = self.factor_occurrences_iterator(fact)
|
|
1714
|
+
L = fact.length()
|
|
1715
|
+
try:
|
|
1716
|
+
i = next(it)
|
|
1717
|
+
while True:
|
|
1718
|
+
j = next(it)
|
|
1719
|
+
yield self[i:j+L]
|
|
1720
|
+
i = j
|
|
1721
|
+
except StopIteration:
|
|
1722
|
+
return
|