passagemath-graphs 10.6.1rc1__cp310-cp310-musllinux_1_2_aarch64.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_graphs-10.6.1rc1.dist-info/METADATA +292 -0
- passagemath_graphs-10.6.1rc1.dist-info/RECORD +260 -0
- passagemath_graphs-10.6.1rc1.dist-info/WHEEL +5 -0
- passagemath_graphs-10.6.1rc1.dist-info/top_level.txt +2 -0
- passagemath_graphs.libs/libgcc_s-69c45f16.so.1 +0 -0
- passagemath_graphs.libs/libgmp-8e78bd9b.so.10.5.0 +0 -0
- passagemath_graphs.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
- sage/all__sagemath_graphs.py +39 -0
- sage/combinat/abstract_tree.py +2723 -0
- sage/combinat/all__sagemath_graphs.py +34 -0
- sage/combinat/binary_tree.py +5306 -0
- sage/combinat/cluster_algebra_quiver/all.py +22 -0
- sage/combinat/cluster_algebra_quiver/cluster_seed.py +5208 -0
- sage/combinat/cluster_algebra_quiver/interact.py +124 -0
- sage/combinat/cluster_algebra_quiver/mutation_class.py +625 -0
- sage/combinat/cluster_algebra_quiver/mutation_type.py +1555 -0
- sage/combinat/cluster_algebra_quiver/quiver.py +2290 -0
- sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +2468 -0
- sage/combinat/designs/MOLS_handbook_data.py +570 -0
- sage/combinat/designs/all.py +58 -0
- sage/combinat/designs/bibd.py +1655 -0
- sage/combinat/designs/block_design.py +1071 -0
- sage/combinat/designs/covering_array.py +269 -0
- sage/combinat/designs/covering_design.py +530 -0
- sage/combinat/designs/database.py +5615 -0
- sage/combinat/designs/design_catalog.py +122 -0
- sage/combinat/designs/designs_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/designs_pyx.pxd +21 -0
- sage/combinat/designs/designs_pyx.pyx +993 -0
- sage/combinat/designs/difference_family.py +3951 -0
- sage/combinat/designs/difference_matrices.py +279 -0
- sage/combinat/designs/evenly_distributed_sets.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/evenly_distributed_sets.pyx +661 -0
- sage/combinat/designs/ext_rep.py +1064 -0
- sage/combinat/designs/gen_quadrangles_with_spread.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/gen_quadrangles_with_spread.pyx +339 -0
- sage/combinat/designs/group_divisible_designs.py +361 -0
- sage/combinat/designs/incidence_structures.py +2357 -0
- sage/combinat/designs/latin_squares.py +581 -0
- sage/combinat/designs/orthogonal_arrays.py +2244 -0
- sage/combinat/designs/orthogonal_arrays_build_recursive.py +1780 -0
- sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +967 -0
- sage/combinat/designs/resolvable_bibd.py +815 -0
- sage/combinat/designs/steiner_quadruple_systems.py +1306 -0
- sage/combinat/designs/subhypergraph_search.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/subhypergraph_search.pyx +530 -0
- sage/combinat/designs/twographs.py +306 -0
- sage/combinat/finite_state_machine.py +14874 -0
- sage/combinat/finite_state_machine_generators.py +2006 -0
- sage/combinat/graph_path.py +448 -0
- sage/combinat/interval_posets.py +3908 -0
- sage/combinat/nu_tamari_lattice.py +269 -0
- sage/combinat/ordered_tree.py +1446 -0
- sage/combinat/posets/all.py +46 -0
- sage/combinat/posets/bubble_shuffle.py +247 -0
- sage/combinat/posets/cartesian_product.py +493 -0
- sage/combinat/posets/d_complete.py +182 -0
- sage/combinat/posets/elements.py +273 -0
- sage/combinat/posets/forest.py +30 -0
- sage/combinat/posets/hasse_cython.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/posets/hasse_cython.pyx +174 -0
- sage/combinat/posets/hasse_diagram.py +3672 -0
- sage/combinat/posets/hochschild_lattice.py +158 -0
- sage/combinat/posets/incidence_algebras.py +794 -0
- sage/combinat/posets/lattices.py +5117 -0
- sage/combinat/posets/linear_extension_iterator.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/posets/linear_extension_iterator.pyx +292 -0
- sage/combinat/posets/linear_extensions.py +1037 -0
- sage/combinat/posets/mobile.py +275 -0
- sage/combinat/posets/moebius_algebra.py +776 -0
- sage/combinat/posets/poset_examples.py +2178 -0
- sage/combinat/posets/posets.py +9360 -0
- sage/combinat/rooted_tree.py +1070 -0
- sage/combinat/shard_order.py +239 -0
- sage/combinat/tamari_lattices.py +384 -0
- sage/combinat/yang_baxter_graph.py +923 -0
- sage/databases/all__sagemath_graphs.py +1 -0
- sage/databases/knotinfo_db.py +1231 -0
- sage/ext_data/all__sagemath_graphs.py +1 -0
- sage/ext_data/graphs/graph_plot_js.html +330 -0
- sage/ext_data/kenzo/CP2.txt +45 -0
- sage/ext_data/kenzo/CP3.txt +349 -0
- sage/ext_data/kenzo/CP4.txt +4774 -0
- sage/ext_data/kenzo/README.txt +49 -0
- sage/ext_data/kenzo/S4.txt +20 -0
- sage/graphs/all.py +42 -0
- sage/graphs/asteroidal_triples.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/asteroidal_triples.pyx +320 -0
- sage/graphs/base/all.py +1 -0
- sage/graphs/base/boost_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/boost_graph.pxd +106 -0
- sage/graphs/base/boost_graph.pyx +3045 -0
- sage/graphs/base/c_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/c_graph.pxd +106 -0
- sage/graphs/base/c_graph.pyx +5096 -0
- sage/graphs/base/dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/dense_graph.pxd +28 -0
- sage/graphs/base/dense_graph.pyx +801 -0
- sage/graphs/base/graph_backends.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/graph_backends.pxd +5 -0
- sage/graphs/base/graph_backends.pyx +797 -0
- sage/graphs/base/overview.py +85 -0
- sage/graphs/base/sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/sparse_graph.pxd +90 -0
- sage/graphs/base/sparse_graph.pyx +1653 -0
- sage/graphs/base/static_dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_dense_graph.pxd +5 -0
- sage/graphs/base/static_dense_graph.pyx +1032 -0
- sage/graphs/base/static_sparse_backend.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_sparse_backend.pxd +27 -0
- sage/graphs/base/static_sparse_backend.pyx +1583 -0
- sage/graphs/base/static_sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_sparse_graph.pxd +37 -0
- sage/graphs/base/static_sparse_graph.pyx +1375 -0
- sage/graphs/bipartite_graph.py +2732 -0
- sage/graphs/centrality.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/centrality.pyx +1038 -0
- sage/graphs/cographs.py +519 -0
- sage/graphs/comparability.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/comparability.pyx +851 -0
- sage/graphs/connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/connectivity.pxd +157 -0
- sage/graphs/connectivity.pyx +4813 -0
- sage/graphs/convexity_properties.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/convexity_properties.pxd +16 -0
- sage/graphs/convexity_properties.pyx +870 -0
- sage/graphs/digraph.py +4754 -0
- sage/graphs/digraph_generators.py +1993 -0
- sage/graphs/distances_all_pairs.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/distances_all_pairs.pxd +12 -0
- sage/graphs/distances_all_pairs.pyx +2938 -0
- sage/graphs/domination.py +1363 -0
- sage/graphs/dot2tex_utils.py +100 -0
- sage/graphs/edge_connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/edge_connectivity.pyx +1215 -0
- sage/graphs/generators/all.py +1 -0
- sage/graphs/generators/basic.py +1769 -0
- sage/graphs/generators/chessboard.py +538 -0
- sage/graphs/generators/classical_geometries.py +1611 -0
- sage/graphs/generators/degree_sequence.py +235 -0
- sage/graphs/generators/distance_regular.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/generators/distance_regular.pyx +2846 -0
- sage/graphs/generators/families.py +4759 -0
- sage/graphs/generators/intersection.py +565 -0
- sage/graphs/generators/platonic_solids.py +262 -0
- sage/graphs/generators/random.py +2623 -0
- sage/graphs/generators/smallgraphs.py +5741 -0
- sage/graphs/generators/world_map.py +724 -0
- sage/graphs/generic_graph.py +26867 -0
- sage/graphs/generic_graph_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/generic_graph_pyx.pxd +34 -0
- sage/graphs/generic_graph_pyx.pyx +1673 -0
- sage/graphs/genus.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/genus.pyx +622 -0
- sage/graphs/graph.py +9645 -0
- sage/graphs/graph_coloring.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_coloring.pyx +2284 -0
- sage/graphs/graph_database.py +1177 -0
- sage/graphs/graph_decompositions/all.py +1 -0
- sage/graphs/graph_decompositions/bandwidth.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/bandwidth.pyx +428 -0
- sage/graphs/graph_decompositions/clique_separators.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/clique_separators.pyx +616 -0
- sage/graphs/graph_decompositions/cutwidth.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/cutwidth.pyx +753 -0
- sage/graphs/graph_decompositions/fast_digraph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/fast_digraph.pxd +13 -0
- sage/graphs/graph_decompositions/fast_digraph.pyx +212 -0
- sage/graphs/graph_decompositions/graph_products.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/graph_products.pyx +508 -0
- sage/graphs/graph_decompositions/modular_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/modular_decomposition.pxd +27 -0
- sage/graphs/graph_decompositions/modular_decomposition.pyx +1536 -0
- sage/graphs/graph_decompositions/slice_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/slice_decomposition.pxd +18 -0
- sage/graphs/graph_decompositions/slice_decomposition.pyx +1106 -0
- sage/graphs/graph_decompositions/tree_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/tree_decomposition.pxd +17 -0
- sage/graphs/graph_decompositions/tree_decomposition.pyx +1996 -0
- sage/graphs/graph_decompositions/vertex_separation.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/vertex_separation.pxd +5 -0
- sage/graphs/graph_decompositions/vertex_separation.pyx +1963 -0
- sage/graphs/graph_editor.py +82 -0
- sage/graphs/graph_generators.py +3314 -0
- sage/graphs/graph_generators_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_generators_pyx.pyx +95 -0
- sage/graphs/graph_input.py +812 -0
- sage/graphs/graph_latex.py +2064 -0
- sage/graphs/graph_list.py +410 -0
- sage/graphs/graph_plot.py +1756 -0
- sage/graphs/graph_plot_js.py +338 -0
- sage/graphs/hyperbolicity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/hyperbolicity.pyx +1704 -0
- sage/graphs/hypergraph_generators.py +364 -0
- sage/graphs/independent_sets.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/independent_sets.pxd +13 -0
- sage/graphs/independent_sets.pyx +402 -0
- sage/graphs/isgci.py +1033 -0
- sage/graphs/isoperimetric_inequalities.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/isoperimetric_inequalities.pyx +489 -0
- sage/graphs/line_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/line_graph.pyx +743 -0
- sage/graphs/lovasz_theta.py +77 -0
- sage/graphs/matching.py +1633 -0
- sage/graphs/matching_covered_graph.py +3590 -0
- sage/graphs/orientations.py +1489 -0
- sage/graphs/partial_cube.py +459 -0
- sage/graphs/path_enumeration.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/path_enumeration.pyx +2040 -0
- sage/graphs/pq_trees.py +1129 -0
- sage/graphs/print_graphs.py +201 -0
- sage/graphs/schnyder.py +865 -0
- sage/graphs/spanning_tree.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/spanning_tree.pyx +1457 -0
- sage/graphs/strongly_regular_db.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/strongly_regular_db.pyx +3340 -0
- sage/graphs/traversals.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/traversals.pxd +9 -0
- sage/graphs/traversals.pyx +1872 -0
- sage/graphs/trees.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/trees.pxd +15 -0
- sage/graphs/trees.pyx +310 -0
- sage/graphs/tutte_polynomial.py +713 -0
- sage/graphs/views.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/views.pyx +794 -0
- sage/graphs/weakly_chordal.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/weakly_chordal.pyx +604 -0
- sage/groups/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/partn_ref/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.pxd +38 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +1666 -0
- sage/knots/all.py +6 -0
- sage/knots/free_knotinfo_monoid.py +507 -0
- sage/knots/gauss_code.py +291 -0
- sage/knots/knot.py +682 -0
- sage/knots/knot_table.py +284 -0
- sage/knots/knotinfo.py +2900 -0
- sage/knots/link.py +4715 -0
- sage/sandpiles/all.py +13 -0
- sage/sandpiles/examples.py +225 -0
- sage/sandpiles/sandpile.py +6365 -0
- sage/topology/all.py +22 -0
- sage/topology/cell_complex.py +1214 -0
- sage/topology/cubical_complex.py +1976 -0
- sage/topology/delta_complex.py +1806 -0
- sage/topology/filtered_simplicial_complex.py +744 -0
- sage/topology/moment_angle_complex.py +823 -0
- sage/topology/simplicial_complex.py +5160 -0
- sage/topology/simplicial_complex_catalog.py +92 -0
- sage/topology/simplicial_complex_examples.py +1680 -0
- sage/topology/simplicial_complex_homset.py +205 -0
- sage/topology/simplicial_complex_morphism.py +836 -0
- sage/topology/simplicial_set.py +4102 -0
- sage/topology/simplicial_set_catalog.py +55 -0
- sage/topology/simplicial_set_constructions.py +2954 -0
- sage/topology/simplicial_set_examples.py +865 -0
- sage/topology/simplicial_set_morphism.py +1464 -0
@@ -0,0 +1,2723 @@
|
|
1
|
+
# sage_setup: distribution = sagemath-graphs
|
2
|
+
r"""
|
3
|
+
Abstract recursive trees
|
4
|
+
|
5
|
+
The purpose of this class is to help implement trees with a specific structure
|
6
|
+
on the children of each node. For instance, one could want to define a tree in
|
7
|
+
which each node sees its children as linearly (see the :mod:`Ordered Trees
|
8
|
+
<sage.combinat.ordered_tree>` module) or cyclically ordered.
|
9
|
+
|
10
|
+
**Tree structures**
|
11
|
+
|
12
|
+
Conceptually, one can define a tree structure from any object that can contain
|
13
|
+
others. Indeed, a list can contain lists which contain lists which contain
|
14
|
+
lists, and thus define a tree ... The same can be done with sets, or any kind
|
15
|
+
of iterable objects.
|
16
|
+
|
17
|
+
While any iterable is sufficient to encode trees, it can prove useful to have
|
18
|
+
other methods available like isomorphism tests (see next section), conversions
|
19
|
+
to DiGraphs objects (see :meth:`~.AbstractLabelledTree.as_digraph`) or
|
20
|
+
computation of the number of automorphisms constrained by the structure on
|
21
|
+
children. Providing such methods is the whole purpose of the
|
22
|
+
:class:`AbstractTree` class.
|
23
|
+
|
24
|
+
As a result, the :class:`AbstractTree` class is not meant to be
|
25
|
+
instantiated, but extended. It is expected that classes extending this one may
|
26
|
+
also inherit from classes representing iterables, for instance
|
27
|
+
:class:`~sage.structure.list_clone.ClonableArray` or :class:`~sage.structure.list_clone.ClonableList`
|
28
|
+
|
29
|
+
**Constrained Trees**
|
30
|
+
|
31
|
+
The tree built from a specific container will reflect the properties of the
|
32
|
+
container. Indeed, if ``A`` is an iterable class whose elements are linearly
|
33
|
+
ordered, a class ``B`` extending both of :class:`AbstractTree` and ``A`` will
|
34
|
+
be such that the children of a node will be linearly ordered. If ``A`` behaves
|
35
|
+
like a set (i.e. if there is no order on the elements it contains), then two
|
36
|
+
trees will be considered as equal if one can be obtained from the other
|
37
|
+
through permutations between the children of a same node (see next section).
|
38
|
+
|
39
|
+
**Paths and ID**
|
40
|
+
|
41
|
+
It is expected that each element of a set of children should be identified by
|
42
|
+
its index in the container. This way, any node of the tree can be identified
|
43
|
+
by a word describing a path from the root node.
|
44
|
+
|
45
|
+
**Canonical labellings**
|
46
|
+
|
47
|
+
Equality between instances of classes extending both :class:`AbstractTree`
|
48
|
+
and ``A`` is entirely defined by the equality defined on the elements of
|
49
|
+
``A``. A canonical labelling of such a tree, however, should be such that
|
50
|
+
two trees ``a`` and ``b`` satisfying ``a == b`` have the same canonical
|
51
|
+
labellings. On the other hand, the canonical labellings of trees ``a`` and
|
52
|
+
``b`` satisfying ``a != b`` are expected to be different.
|
53
|
+
|
54
|
+
For this reason, the values returned by the :meth:`canonical_labelling
|
55
|
+
<AbstractTree.canonical_labelling>` method heavily
|
56
|
+
depend on the data structure used for a node's children and **should be**
|
57
|
+
**overridden** by most of the classes extending :class:`AbstractTree` if it is
|
58
|
+
incoherent with the data structure.
|
59
|
+
|
60
|
+
**Authors**
|
61
|
+
|
62
|
+
- Florent Hivert (2010-2011): initial revision
|
63
|
+
- Frédéric Chapoton (2011): contributed some methods
|
64
|
+
"""
|
65
|
+
import itertools
|
66
|
+
|
67
|
+
from sage.structure.list_clone import ClonableArray
|
68
|
+
from sage.rings.integer import Integer
|
69
|
+
from sage.misc.misc_c import prod
|
70
|
+
|
71
|
+
# Unfortunately Cython forbids multiple inheritance. Therefore, we do not
|
72
|
+
# inherit from SageObject to be able to inherit from Element or a subclass
|
73
|
+
# of it later.
|
74
|
+
|
75
|
+
|
76
|
+
class AbstractTree:
|
77
|
+
"""
|
78
|
+
Abstract Tree.
|
79
|
+
|
80
|
+
There is no data structure defined here, as this class is meant to be
|
81
|
+
extended, not instantiated.
|
82
|
+
|
83
|
+
.. rubric:: How should this class be extended?
|
84
|
+
|
85
|
+
A class extending :class:`AbstractTree
|
86
|
+
<sage.combinat.abstract_tree.AbstractTree>` should respect several
|
87
|
+
assumptions:
|
88
|
+
|
89
|
+
* For a tree ``T``, the call ``iter(T)`` should return an iterator on the
|
90
|
+
children of the root ``T``.
|
91
|
+
|
92
|
+
* The :meth:`canonical_labelling
|
93
|
+
<AbstractTree.canonical_labelling>` method
|
94
|
+
should return the same value for trees that are considered equal (see the
|
95
|
+
"canonical labellings" section in the documentation of the
|
96
|
+
:class:`AbstractTree <sage.combinat.abstract_tree.AbstractTree>` class).
|
97
|
+
|
98
|
+
* For a tree ``T`` the call ``T.parent().labelled_trees()`` should return
|
99
|
+
a parent for labelled trees of the same kind: for example,
|
100
|
+
|
101
|
+
- if ``T`` is a binary tree, ``T.parent()`` is ``BinaryTrees()`` and
|
102
|
+
``T.parent().labelled_trees()`` is ``LabelledBinaryTrees()``
|
103
|
+
|
104
|
+
- if ``T`` is an ordered tree, ``T.parent()`` is ``OrderedTrees()`` and
|
105
|
+
``T.parent().labelled_trees()`` is ``LabelledOrderedTrees()``
|
106
|
+
|
107
|
+
TESTS::
|
108
|
+
|
109
|
+
sage: TestSuite(OrderedTree()).run()
|
110
|
+
sage: TestSuite(BinaryTree()).run()
|
111
|
+
"""
|
112
|
+
|
113
|
+
def pre_order_traversal_iter(self):
|
114
|
+
r"""
|
115
|
+
The depth-first pre-order traversal iterator.
|
116
|
+
|
117
|
+
This method iters each node following the depth-first pre-order
|
118
|
+
traversal algorithm (recursive implementation). The algorithm
|
119
|
+
is::
|
120
|
+
|
121
|
+
yield the root (in the case of binary trees, if it is not
|
122
|
+
a leaf);
|
123
|
+
then explore each subtree (by the algorithm) from the
|
124
|
+
leftmost one to the rightmost one.
|
125
|
+
|
126
|
+
EXAMPLES:
|
127
|
+
|
128
|
+
For example, on the following binary tree `b`::
|
129
|
+
|
130
|
+
| ___3____ |
|
131
|
+
| / \ |
|
132
|
+
| 1 _7_ |
|
133
|
+
| \ / \ |
|
134
|
+
| 2 5 8 |
|
135
|
+
| / \ |
|
136
|
+
| 4 6 |
|
137
|
+
|
138
|
+
(only the nodes shown), the depth-first pre-order traversal
|
139
|
+
algorithm explores `b` in the following order of nodes:
|
140
|
+
`3,1,2,7,5,4,6,8`.
|
141
|
+
|
142
|
+
Another example::
|
143
|
+
|
144
|
+
| __1____ |
|
145
|
+
| / / / |
|
146
|
+
| 2 6 8_ |
|
147
|
+
| | | / / |
|
148
|
+
| 3_ 7 9 10 |
|
149
|
+
| / / |
|
150
|
+
| 4 5 |
|
151
|
+
|
152
|
+
The algorithm explores this labelled tree in the following
|
153
|
+
order: `1,2,3,4,5,6,7,8,9,10`.
|
154
|
+
|
155
|
+
TESTS::
|
156
|
+
|
157
|
+
sage: b = BinaryTree([[None,[]],[[[],[]],[]]]).canonical_labelling()
|
158
|
+
sage: ascii_art([b])
|
159
|
+
[ ___3____ ]
|
160
|
+
[ / \ ]
|
161
|
+
[ 1 _7_ ]
|
162
|
+
[ \ / \ ]
|
163
|
+
[ 2 5 8 ]
|
164
|
+
[ / \ ]
|
165
|
+
[ 4 6 ]
|
166
|
+
sage: [n.label() for n in b.pre_order_traversal_iter()]
|
167
|
+
[3, 1, 2, 7, 5, 4, 6, 8]
|
168
|
+
|
169
|
+
sage: t = OrderedTree([[[[],[]]],[[]],[[],[]]]).canonical_labelling()
|
170
|
+
sage: ascii_art([t])
|
171
|
+
[ __1____ ]
|
172
|
+
[ / / / ]
|
173
|
+
[ 2 6 8_ ]
|
174
|
+
[ | | / / ]
|
175
|
+
[ 3_ 7 9 10 ]
|
176
|
+
[ / / ]
|
177
|
+
[ 4 5 ]
|
178
|
+
sage: [n.label() for n in t.pre_order_traversal_iter()]
|
179
|
+
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
180
|
+
|
181
|
+
sage: [n for n in BinaryTree(None).pre_order_traversal_iter()]
|
182
|
+
[]
|
183
|
+
|
184
|
+
The following test checks that things do not go wrong if some among
|
185
|
+
the descendants of the tree are equal or even identical::
|
186
|
+
|
187
|
+
sage: u = BinaryTree(None)
|
188
|
+
sage: v = BinaryTree([u, u])
|
189
|
+
sage: w = BinaryTree([v, v])
|
190
|
+
sage: t = BinaryTree([w, w])
|
191
|
+
sage: t.node_number()
|
192
|
+
7
|
193
|
+
sage: l = [1 for i in t.pre_order_traversal_iter()]
|
194
|
+
sage: len(l)
|
195
|
+
7
|
196
|
+
"""
|
197
|
+
if self.is_empty():
|
198
|
+
return
|
199
|
+
yield self
|
200
|
+
yield from itertools.chain(*[c.pre_order_traversal_iter()
|
201
|
+
for c in self])
|
202
|
+
|
203
|
+
def iterative_pre_order_traversal(self, action=None):
|
204
|
+
r"""
|
205
|
+
Run the depth-first pre-order traversal algorithm (iterative
|
206
|
+
implementation) and subject every node encountered
|
207
|
+
to some procedure ``action``. The algorithm is::
|
208
|
+
|
209
|
+
manipulate the root with function `action` (in the case
|
210
|
+
of a binary tree, only if the root is not a leaf);
|
211
|
+
then explore each subtree (by the algorithm) from the
|
212
|
+
leftmost one to the rightmost one.
|
213
|
+
|
214
|
+
INPUT:
|
215
|
+
|
216
|
+
- ``action`` -- (optional) a function which takes a node as
|
217
|
+
input, and does something during the exploration
|
218
|
+
|
219
|
+
OUTPUT:
|
220
|
+
|
221
|
+
``None``. (This is *not* an iterator.)
|
222
|
+
|
223
|
+
.. SEEALSO::
|
224
|
+
|
225
|
+
- :meth:`~sage.combinat.abstract_tree.AbstractTree.pre_order_traversal_iter()`
|
226
|
+
- :meth:`~sage.combinat.abstract_tree.AbstractTree.pre_order_traversal()`
|
227
|
+
|
228
|
+
TESTS::
|
229
|
+
|
230
|
+
sage: l = []
|
231
|
+
sage: b = BinaryTree([[None,[]],[[[],[]],[]]]).canonical_labelling()
|
232
|
+
sage: b
|
233
|
+
3[1[., 2[., .]], 7[5[4[., .], 6[., .]], 8[., .]]]
|
234
|
+
sage: b.iterative_pre_order_traversal(lambda node: l.append(node.label()))
|
235
|
+
sage: l
|
236
|
+
[3, 1, 2, 7, 5, 4, 6, 8]
|
237
|
+
|
238
|
+
sage: t = OrderedTree([[[[],[]]],[[]],[[],[]]]).canonical_labelling()
|
239
|
+
sage: t
|
240
|
+
1[2[3[4[], 5[]]], 6[7[]], 8[9[], 10[]]]
|
241
|
+
sage: l = []
|
242
|
+
sage: t.iterative_pre_order_traversal(lambda node: l.append(node.label()))
|
243
|
+
sage: l
|
244
|
+
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
245
|
+
sage: l = []
|
246
|
+
|
247
|
+
sage: BinaryTree().canonical_labelling().\
|
248
|
+
....: pre_order_traversal(lambda node: l.append(node.label()))
|
249
|
+
sage: l
|
250
|
+
[]
|
251
|
+
sage: OrderedTree([]).canonical_labelling().\
|
252
|
+
....: iterative_pre_order_traversal(lambda node: l.append(node.label()))
|
253
|
+
sage: l
|
254
|
+
[1]
|
255
|
+
|
256
|
+
The following test checks that things do not go wrong if some among
|
257
|
+
the descendants of the tree are equal or even identical::
|
258
|
+
|
259
|
+
sage: u = BinaryTree(None)
|
260
|
+
sage: v = BinaryTree([u, u])
|
261
|
+
sage: w = BinaryTree([v, v])
|
262
|
+
sage: t = BinaryTree([w, w])
|
263
|
+
sage: t.node_number()
|
264
|
+
7
|
265
|
+
sage: l = []
|
266
|
+
sage: t.iterative_pre_order_traversal(lambda node: l.append(1))
|
267
|
+
sage: len(l)
|
268
|
+
7
|
269
|
+
"""
|
270
|
+
if self.is_empty():
|
271
|
+
return
|
272
|
+
if action is None:
|
273
|
+
def action(x):
|
274
|
+
return None
|
275
|
+
stack = []
|
276
|
+
stack.append(self)
|
277
|
+
while stack:
|
278
|
+
node = stack.pop()
|
279
|
+
action(node)
|
280
|
+
for subtree in reversed(node):
|
281
|
+
if not subtree.is_empty():
|
282
|
+
stack.append(subtree)
|
283
|
+
|
284
|
+
def pre_order_traversal(self, action=None):
|
285
|
+
r"""
|
286
|
+
Run the depth-first pre-order traversal algorithm (recursive
|
287
|
+
implementation) and subject every node encountered
|
288
|
+
to some procedure ``action``. The algorithm is::
|
289
|
+
|
290
|
+
manipulate the root with function `action` (in the case
|
291
|
+
of a binary tree, only if the root is not a leaf);
|
292
|
+
then explore each subtree (by the algorithm) from the
|
293
|
+
leftmost one to the rightmost one.
|
294
|
+
|
295
|
+
INPUT:
|
296
|
+
|
297
|
+
- ``action`` -- (optional) a function which takes a node as
|
298
|
+
input, and does something during the exploration
|
299
|
+
|
300
|
+
OUTPUT:
|
301
|
+
|
302
|
+
``None``. (This is *not* an iterator.)
|
303
|
+
|
304
|
+
EXAMPLES:
|
305
|
+
|
306
|
+
For example, on the following binary tree `b`::
|
307
|
+
|
308
|
+
| ___3____ |
|
309
|
+
| / \ |
|
310
|
+
| 1 _7_ |
|
311
|
+
| \ / \ |
|
312
|
+
| 2 5 8 |
|
313
|
+
| / \ |
|
314
|
+
| 4 6 |
|
315
|
+
|
316
|
+
the depth-first pre-order traversal algorithm explores `b` in the
|
317
|
+
following order of nodes: `3,1,2,7,5,4,6,8`.
|
318
|
+
|
319
|
+
Another example::
|
320
|
+
|
321
|
+
| __1____ |
|
322
|
+
| / / / |
|
323
|
+
| 2 6 8_ |
|
324
|
+
| | | / / |
|
325
|
+
| 3_ 7 9 10 |
|
326
|
+
| / / |
|
327
|
+
| 4 5 |
|
328
|
+
|
329
|
+
The algorithm explores this tree in the following order:
|
330
|
+
`1,2,3,4,5,6,7,8,9,10`.
|
331
|
+
|
332
|
+
.. SEEALSO::
|
333
|
+
|
334
|
+
- :meth:`~sage.combinat.abstract_tree.AbstractTree.pre_order_traversal_iter()`
|
335
|
+
- :meth:`~sage.combinat.abstract_tree.AbstractTree.iterative_pre_order_traversal()`
|
336
|
+
|
337
|
+
TESTS::
|
338
|
+
|
339
|
+
sage: l = []
|
340
|
+
sage: b = BinaryTree([[None,[]],[[[],[]],[]]]).canonical_labelling()
|
341
|
+
sage: b
|
342
|
+
3[1[., 2[., .]], 7[5[4[., .], 6[., .]], 8[., .]]]
|
343
|
+
sage: b.pre_order_traversal(lambda node: l.append(node.label()))
|
344
|
+
sage: l
|
345
|
+
[3, 1, 2, 7, 5, 4, 6, 8]
|
346
|
+
sage: li = []
|
347
|
+
sage: b.iterative_pre_order_traversal(lambda node: li.append(node.label()))
|
348
|
+
sage: l == li
|
349
|
+
True
|
350
|
+
|
351
|
+
sage: t = OrderedTree([[[[],[]]],[[]],[[],[]]]).canonical_labelling()
|
352
|
+
sage: t
|
353
|
+
1[2[3[4[], 5[]]], 6[7[]], 8[9[], 10[]]]
|
354
|
+
sage: l = []
|
355
|
+
sage: t.pre_order_traversal(lambda node: l.append(node.label()))
|
356
|
+
sage: l
|
357
|
+
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
358
|
+
sage: li = []
|
359
|
+
sage: t.iterative_pre_order_traversal(lambda node: li.append(node.label()))
|
360
|
+
sage: l == li
|
361
|
+
True
|
362
|
+
|
363
|
+
sage: l = []
|
364
|
+
sage: BinaryTree().canonical_labelling().\
|
365
|
+
....: pre_order_traversal(lambda node: l.append(node.label()))
|
366
|
+
sage: l
|
367
|
+
[]
|
368
|
+
sage: OrderedTree([]).canonical_labelling().\
|
369
|
+
....: pre_order_traversal(lambda node: l.append(node.label()))
|
370
|
+
sage: l
|
371
|
+
[1]
|
372
|
+
|
373
|
+
The following test checks that things do not go wrong if some among
|
374
|
+
the descendants of the tree are equal or even identical::
|
375
|
+
|
376
|
+
sage: u = BinaryTree(None)
|
377
|
+
sage: v = BinaryTree([u, u])
|
378
|
+
sage: w = BinaryTree([v, v])
|
379
|
+
sage: t = BinaryTree([w, w])
|
380
|
+
sage: t.node_number()
|
381
|
+
7
|
382
|
+
sage: l = []
|
383
|
+
sage: t.pre_order_traversal(lambda node: l.append(1))
|
384
|
+
sage: len(l)
|
385
|
+
7
|
386
|
+
"""
|
387
|
+
if action is None:
|
388
|
+
def action(x):
|
389
|
+
return None
|
390
|
+
for node in self.pre_order_traversal_iter():
|
391
|
+
action(node)
|
392
|
+
|
393
|
+
def post_order_traversal_iter(self):
|
394
|
+
r"""
|
395
|
+
The depth-first post-order traversal iterator.
|
396
|
+
|
397
|
+
This method iters each node following the depth-first post-order
|
398
|
+
traversal algorithm (recursive implementation). The algorithm
|
399
|
+
is::
|
400
|
+
|
401
|
+
explore each subtree (by the algorithm) from the
|
402
|
+
leftmost one to the rightmost one;
|
403
|
+
then yield the root (in the case of binary trees, only if
|
404
|
+
it is not a leaf).
|
405
|
+
|
406
|
+
EXAMPLES:
|
407
|
+
|
408
|
+
For example on the following binary tree `b`::
|
409
|
+
|
410
|
+
| ___3____ |
|
411
|
+
| / \ |
|
412
|
+
| 1 _7_ |
|
413
|
+
| \ / \ |
|
414
|
+
| 2 5 8 |
|
415
|
+
| / \ |
|
416
|
+
| 4 6 |
|
417
|
+
|
418
|
+
(only the nodes are shown), the depth-first post-order traversal
|
419
|
+
algorithm explores `b` in the following order of nodes:
|
420
|
+
`2,1,4,6,5,8,7,3`.
|
421
|
+
|
422
|
+
For another example, consider the labelled tree::
|
423
|
+
|
424
|
+
| __1____ |
|
425
|
+
| / / / |
|
426
|
+
| 2 6 8_ |
|
427
|
+
| | | / / |
|
428
|
+
| 3_ 7 9 10 |
|
429
|
+
| / / |
|
430
|
+
| 4 5 |
|
431
|
+
|
432
|
+
The algorithm explores this tree in the following order:
|
433
|
+
`4,5,3,2,7,6,9,10,8,1`.
|
434
|
+
|
435
|
+
TESTS::
|
436
|
+
|
437
|
+
sage: b = BinaryTree([[None,[]],[[[],[]],[]]]).canonical_labelling()
|
438
|
+
sage: ascii_art([b])
|
439
|
+
[ ___3____ ]
|
440
|
+
[ / \ ]
|
441
|
+
[ 1 _7_ ]
|
442
|
+
[ \ / \ ]
|
443
|
+
[ 2 5 8 ]
|
444
|
+
[ / \ ]
|
445
|
+
[ 4 6 ]
|
446
|
+
sage: [node.label() for node in b.post_order_traversal_iter()]
|
447
|
+
[2, 1, 4, 6, 5, 8, 7, 3]
|
448
|
+
|
449
|
+
sage: t = OrderedTree([[[[],[]]],[[]],[[],[]]]).canonical_labelling()
|
450
|
+
sage: ascii_art([t])
|
451
|
+
[ __1____ ]
|
452
|
+
[ / / / ]
|
453
|
+
[ 2 6 8_ ]
|
454
|
+
[ | | / / ]
|
455
|
+
[ 3_ 7 9 10 ]
|
456
|
+
[ / / ]
|
457
|
+
[ 4 5 ]
|
458
|
+
sage: [node.label() for node in t.post_order_traversal_iter()]
|
459
|
+
[4, 5, 3, 2, 7, 6, 9, 10, 8, 1]
|
460
|
+
|
461
|
+
sage: [node.label() for node in BinaryTree().canonical_labelling().\
|
462
|
+
....: post_order_traversal_iter()]
|
463
|
+
[]
|
464
|
+
sage: [node.label() for node in OrderedTree([]).\
|
465
|
+
....: canonical_labelling().post_order_traversal_iter()]
|
466
|
+
[1]
|
467
|
+
|
468
|
+
The following test checks that things do not go wrong if some among
|
469
|
+
the descendants of the tree are equal or even identical::
|
470
|
+
|
471
|
+
sage: u = BinaryTree(None)
|
472
|
+
sage: v = BinaryTree([u, u])
|
473
|
+
sage: w = BinaryTree([v, v])
|
474
|
+
sage: t = BinaryTree([w, w])
|
475
|
+
sage: t.node_number()
|
476
|
+
7
|
477
|
+
sage: l = [1 for i in t.post_order_traversal_iter()]
|
478
|
+
sage: len(l)
|
479
|
+
7
|
480
|
+
"""
|
481
|
+
if self.is_empty():
|
482
|
+
return
|
483
|
+
yield from itertools.chain(*[c.post_order_traversal_iter()
|
484
|
+
for c in self])
|
485
|
+
yield self
|
486
|
+
|
487
|
+
def post_order_traversal(self, action=None):
|
488
|
+
r"""
|
489
|
+
Run the depth-first post-order traversal algorithm (recursive
|
490
|
+
implementation) and subject every node encountered
|
491
|
+
to some procedure ``action``. The algorithm is::
|
492
|
+
|
493
|
+
explore each subtree (by the algorithm) from the
|
494
|
+
leftmost one to the rightmost one;
|
495
|
+
then manipulate the root with function `action` (in the
|
496
|
+
case of a binary tree, only if the root is not a leaf).
|
497
|
+
|
498
|
+
INPUT:
|
499
|
+
|
500
|
+
- ``action`` -- (optional) a function which takes a node as
|
501
|
+
input, and does something during the exploration
|
502
|
+
|
503
|
+
OUTPUT:
|
504
|
+
|
505
|
+
``None``. (This is *not* an iterator.)
|
506
|
+
|
507
|
+
.. SEEALSO::
|
508
|
+
|
509
|
+
- :meth:`~sage.combinat.abstract_tree.AbstractTree.post_order_traversal_iter()`
|
510
|
+
- :meth:`~sage.combinat.abstract_tree.AbstractTree.iterative_post_order_traversal()`
|
511
|
+
|
512
|
+
TESTS::
|
513
|
+
|
514
|
+
sage: l = []
|
515
|
+
sage: b = BinaryTree([[None,[]],[[[],[]],[]]]).canonical_labelling()
|
516
|
+
sage: b
|
517
|
+
3[1[., 2[., .]], 7[5[4[., .], 6[., .]], 8[., .]]]
|
518
|
+
sage: b.post_order_traversal(lambda node: l.append(node.label()))
|
519
|
+
sage: l
|
520
|
+
[2, 1, 4, 6, 5, 8, 7, 3]
|
521
|
+
|
522
|
+
sage: t = OrderedTree([[[[],[]]],[[]],[[],[]]]).\
|
523
|
+
....: canonical_labelling(); t
|
524
|
+
1[2[3[4[], 5[]]], 6[7[]], 8[9[], 10[]]]
|
525
|
+
sage: l = []
|
526
|
+
sage: t.post_order_traversal(lambda node: l.append(node.label()))
|
527
|
+
sage: l
|
528
|
+
[4, 5, 3, 2, 7, 6, 9, 10, 8, 1]
|
529
|
+
|
530
|
+
sage: l = []
|
531
|
+
sage: BinaryTree().canonical_labelling().\
|
532
|
+
....: post_order_traversal(lambda node: l.append(node.label()))
|
533
|
+
sage: l
|
534
|
+
[]
|
535
|
+
sage: OrderedTree([]).canonical_labelling().\
|
536
|
+
....: post_order_traversal(lambda node: l.append(node.label()))
|
537
|
+
sage: l
|
538
|
+
[1]
|
539
|
+
|
540
|
+
The following test checks that things do not go wrong if some among
|
541
|
+
the descendants of the tree are equal or even identical::
|
542
|
+
|
543
|
+
sage: u = BinaryTree(None)
|
544
|
+
sage: v = BinaryTree([u, u])
|
545
|
+
sage: w = BinaryTree([v, v])
|
546
|
+
sage: t = BinaryTree([w, w])
|
547
|
+
sage: t.node_number()
|
548
|
+
7
|
549
|
+
sage: l = []
|
550
|
+
sage: t.post_order_traversal(lambda node: l.append(1))
|
551
|
+
sage: len(l)
|
552
|
+
7
|
553
|
+
"""
|
554
|
+
if action is None:
|
555
|
+
def action(x):
|
556
|
+
return None
|
557
|
+
for node in self.post_order_traversal_iter():
|
558
|
+
action(node)
|
559
|
+
|
560
|
+
def iterative_post_order_traversal(self, action=None):
|
561
|
+
r"""
|
562
|
+
Run the depth-first post-order traversal algorithm (iterative
|
563
|
+
implementation) and subject every node encountered
|
564
|
+
to some procedure ``action``. The algorithm is::
|
565
|
+
|
566
|
+
explore each subtree (by the algorithm) from the
|
567
|
+
leftmost one to the rightmost one;
|
568
|
+
then manipulate the root with function `action` (in the
|
569
|
+
case of a binary tree, only if the root is not a leaf).
|
570
|
+
|
571
|
+
INPUT:
|
572
|
+
|
573
|
+
- ``action`` -- (optional) a function which takes a node as
|
574
|
+
input, and does something during the exploration
|
575
|
+
|
576
|
+
OUTPUT:
|
577
|
+
|
578
|
+
``None``. (This is *not* an iterator.)
|
579
|
+
|
580
|
+
.. SEEALSO::
|
581
|
+
|
582
|
+
- :meth:`~sage.combinat.abstract_tree.AbstractTree.post_order_traversal_iter()`
|
583
|
+
|
584
|
+
TESTS::
|
585
|
+
|
586
|
+
sage: l = []
|
587
|
+
sage: b = BinaryTree([[None,[]],[[[],[]],[]]]).canonical_labelling()
|
588
|
+
sage: b
|
589
|
+
3[1[., 2[., .]], 7[5[4[., .], 6[., .]], 8[., .]]]
|
590
|
+
sage: b.iterative_post_order_traversal(lambda node: l.append(node.label()))
|
591
|
+
sage: l
|
592
|
+
[2, 1, 4, 6, 5, 8, 7, 3]
|
593
|
+
|
594
|
+
sage: t = OrderedTree([[[[],[]]],[[]],[[],[]]]).canonical_labelling()
|
595
|
+
sage: t
|
596
|
+
1[2[3[4[], 5[]]], 6[7[]], 8[9[], 10[]]]
|
597
|
+
sage: l = []
|
598
|
+
sage: t.iterative_post_order_traversal(lambda node: l.append(node.label()))
|
599
|
+
sage: l
|
600
|
+
[4, 5, 3, 2, 7, 6, 9, 10, 8, 1]
|
601
|
+
|
602
|
+
sage: l = []
|
603
|
+
sage: BinaryTree().canonical_labelling().\
|
604
|
+
....: iterative_post_order_traversal(
|
605
|
+
....: lambda node: l.append(node.label()))
|
606
|
+
sage: l
|
607
|
+
[]
|
608
|
+
sage: OrderedTree([]).canonical_labelling().\
|
609
|
+
....: iterative_post_order_traversal(
|
610
|
+
....: lambda node: l.append(node.label()))
|
611
|
+
sage: l
|
612
|
+
[1]
|
613
|
+
|
614
|
+
The following test checks that things do not go wrong if some among
|
615
|
+
the descendants of the tree are equal or even identical::
|
616
|
+
|
617
|
+
sage: u = BinaryTree(None)
|
618
|
+
sage: v = BinaryTree([u, u])
|
619
|
+
sage: w = BinaryTree([v, v])
|
620
|
+
sage: t = BinaryTree([w, w])
|
621
|
+
sage: t.node_number()
|
622
|
+
7
|
623
|
+
sage: l = []
|
624
|
+
sage: t.iterative_post_order_traversal(lambda node: l.append(1))
|
625
|
+
sage: len(l)
|
626
|
+
7
|
627
|
+
"""
|
628
|
+
if self.is_empty():
|
629
|
+
return
|
630
|
+
if action is None:
|
631
|
+
def action(x):
|
632
|
+
return None
|
633
|
+
stack = [self]
|
634
|
+
while stack:
|
635
|
+
node = stack[-1]
|
636
|
+
if node is not None:
|
637
|
+
# A "None" on the stack means that the node right before
|
638
|
+
# it on the stack has already been "exploded" into
|
639
|
+
# subtrees, and should not be exploded again, but instead
|
640
|
+
# should be manipulated and removed from the stack.
|
641
|
+
stack.append(None)
|
642
|
+
for subtree in reversed(node):
|
643
|
+
if not subtree.is_empty():
|
644
|
+
stack.append(subtree)
|
645
|
+
else:
|
646
|
+
stack.pop()
|
647
|
+
node = stack.pop()
|
648
|
+
action(node)
|
649
|
+
|
650
|
+
def contour_traversal(self, first_action=None, middle_action=None, final_action=None, leaf_action=None):
|
651
|
+
r"""
|
652
|
+
Run the counterclockwise contour traversal algorithm (iterative
|
653
|
+
implementation) and subject every node encountered
|
654
|
+
to some procedure ``first_action``, ``middle_action`` or ``final_action`` each time it reaches it.
|
655
|
+
|
656
|
+
ALGORITHM:
|
657
|
+
|
658
|
+
- if the root is a leaf, apply `leaf_action`
|
659
|
+
- else
|
660
|
+
- apply `first_action` to the root
|
661
|
+
- iteratively apply `middle_action` to the root and traverse each subtree
|
662
|
+
from the leftmost one to the rightmost one
|
663
|
+
- apply `final_action` to the root
|
664
|
+
|
665
|
+
INPUT:
|
666
|
+
|
667
|
+
- ``first_action`` -- (optional) a function which takes a node as
|
668
|
+
input, and does something the first time it is reached during exploration
|
669
|
+
|
670
|
+
- ``middle_action`` -- (optional) a function which takes a node as
|
671
|
+
input, and does something each time it explore one of its children
|
672
|
+
|
673
|
+
- ``final_action`` -- (optional) a function which takes a node as
|
674
|
+
input, and does something the last time it is reached during exploration
|
675
|
+
|
676
|
+
- ``leaf_action`` -- (optional) a function which takes a leaf as
|
677
|
+
input, and does something when it is reached during exploration.
|
678
|
+
|
679
|
+
OUTPUT:
|
680
|
+
|
681
|
+
``None``. (This is *not* an iterator.)
|
682
|
+
|
683
|
+
TESTS::
|
684
|
+
|
685
|
+
sage: l = []
|
686
|
+
sage: t = OrderedTree([[],[[],[],]]).canonical_labelling()
|
687
|
+
sage: t
|
688
|
+
1[2[], 3[4[], 5[]]]
|
689
|
+
sage: t.contour_traversal(lambda node: (l.append(node.label()),l.append('a')),
|
690
|
+
....: lambda node: (l.append(node.label()),l.append('b')),
|
691
|
+
....: lambda node: (l.append(node.label()),l.append('c')),
|
692
|
+
....: lambda node: (l.append(node.label())))
|
693
|
+
sage: l
|
694
|
+
[1, 'a', 1, 'b', 2, 1, 'b', 3, 'a', 3, 'b', 4, 3, 'b', 5, 3, 'c', 1, 'c']
|
695
|
+
|
696
|
+
sage: l = []
|
697
|
+
sage: b = BinaryTree([[None,[]],[[[],[]],[]]]).canonical_labelling()
|
698
|
+
sage: b
|
699
|
+
3[1[., 2[., .]], 7[5[4[., .], 6[., .]], 8[., .]]]
|
700
|
+
sage: b.contour_traversal(lambda node: l.append(node.label()),
|
701
|
+
....: lambda node: l.append(node.label()),
|
702
|
+
....: lambda node: l.append(node.label()),
|
703
|
+
....: None)
|
704
|
+
sage: l
|
705
|
+
[3, 3, 1, 1, 1, 2, 2, 2, 2, 1, 3, 7, 7, 5, 5, 4, 4, 4, 4, 5, 6, 6, 6, 6, 5, 7, 8, 8, 8, 8, 7, 3]
|
706
|
+
|
707
|
+
The following test checks that things do not go wrong if some among
|
708
|
+
the descendants of the tree are equal or even identical::
|
709
|
+
|
710
|
+
sage: u = BinaryTree(None)
|
711
|
+
sage: v = BinaryTree([u, u])
|
712
|
+
sage: w = BinaryTree([v, v])
|
713
|
+
sage: t = BinaryTree([w, w])
|
714
|
+
sage: t.node_number()
|
715
|
+
7
|
716
|
+
sage: l = []
|
717
|
+
sage: t.contour_traversal(first_action = lambda node: l.append(0))
|
718
|
+
sage: len(l)
|
719
|
+
7
|
720
|
+
"""
|
721
|
+
if first_action is None:
|
722
|
+
def first_action(x):
|
723
|
+
return
|
724
|
+
if middle_action is None:
|
725
|
+
def middle_action(x):
|
726
|
+
return
|
727
|
+
if final_action is None:
|
728
|
+
def final_action(x):
|
729
|
+
return
|
730
|
+
if leaf_action is None:
|
731
|
+
def leaf_action(x):
|
732
|
+
return
|
733
|
+
stack = []
|
734
|
+
stack.append(self)
|
735
|
+
corners = [0, 0]
|
736
|
+
while stack:
|
737
|
+
node = stack.pop()
|
738
|
+
if not node:
|
739
|
+
leaf_action(node)
|
740
|
+
corners.pop()
|
741
|
+
corners[-1] += 1
|
742
|
+
elif not corners[-1]:
|
743
|
+
first_action(node)
|
744
|
+
middle_action(node)
|
745
|
+
stack.append(node)
|
746
|
+
stack.append(node[0])
|
747
|
+
corners.append(0)
|
748
|
+
elif corners[-1] == len(node):
|
749
|
+
final_action(node)
|
750
|
+
corners.pop()
|
751
|
+
corners[-1] += 1
|
752
|
+
else:
|
753
|
+
middle_action(node)
|
754
|
+
stack.append(node)
|
755
|
+
stack.append(node[corners[-1]])
|
756
|
+
corners.append(0)
|
757
|
+
|
758
|
+
def breadth_first_order_traversal(self, action=None):
|
759
|
+
r"""
|
760
|
+
Run the breadth-first post-order traversal algorithm
|
761
|
+
and subject every node encountered to some procedure
|
762
|
+
``action``. The algorithm is::
|
763
|
+
|
764
|
+
queue <- [ root ];
|
765
|
+
while the queue is not empty:
|
766
|
+
node <- pop( queue );
|
767
|
+
manipulate the node;
|
768
|
+
prepend to the queue the list of all subtrees of
|
769
|
+
the node (from the rightmost to the leftmost).
|
770
|
+
|
771
|
+
INPUT:
|
772
|
+
|
773
|
+
- ``action`` -- (optional) a function which takes a node as
|
774
|
+
input, and does something during the exploration
|
775
|
+
|
776
|
+
OUTPUT:
|
777
|
+
|
778
|
+
``None``. (This is *not* an iterator.)
|
779
|
+
|
780
|
+
EXAMPLES:
|
781
|
+
|
782
|
+
For example, on the following binary tree `b`::
|
783
|
+
|
784
|
+
| ___3____ |
|
785
|
+
| / \ |
|
786
|
+
| 1 _7_ |
|
787
|
+
| \ / \ |
|
788
|
+
| 2 5 8 |
|
789
|
+
| / \ |
|
790
|
+
| 4 6 |
|
791
|
+
|
792
|
+
the breadth-first order traversal algorithm explores `b` in the
|
793
|
+
following order of nodes: `3,1,7,2,5,8,4,6`.
|
794
|
+
|
795
|
+
TESTS::
|
796
|
+
|
797
|
+
sage: b = BinaryTree([[None,[]],[[[],[]],[]]]).canonical_labelling()
|
798
|
+
sage: l = []
|
799
|
+
sage: b.breadth_first_order_traversal(lambda node: l.append(node.label()))
|
800
|
+
sage: l
|
801
|
+
[3, 1, 7, 2, 5, 8, 4, 6]
|
802
|
+
|
803
|
+
sage: t = OrderedTree([[[[],[]]],[[]],[[],[]]]).canonical_labelling()
|
804
|
+
sage: t
|
805
|
+
1[2[3[4[], 5[]]], 6[7[]], 8[9[], 10[]]]
|
806
|
+
sage: l = []
|
807
|
+
sage: t.breadth_first_order_traversal(lambda node: l.append(node.label()))
|
808
|
+
sage: l
|
809
|
+
[1, 2, 6, 8, 3, 7, 9, 10, 4, 5]
|
810
|
+
|
811
|
+
sage: l = []
|
812
|
+
sage: BinaryTree().canonical_labelling().\
|
813
|
+
....: breadth_first_order_traversal(
|
814
|
+
....: lambda node: l.append(node.label()))
|
815
|
+
sage: l
|
816
|
+
[]
|
817
|
+
sage: OrderedTree([]).canonical_labelling().\
|
818
|
+
....: breadth_first_order_traversal(
|
819
|
+
....: lambda node: l.append(node.label()))
|
820
|
+
sage: l
|
821
|
+
[1]
|
822
|
+
"""
|
823
|
+
if self.is_empty():
|
824
|
+
return
|
825
|
+
if action is None:
|
826
|
+
def action(x):
|
827
|
+
return None
|
828
|
+
queue = []
|
829
|
+
queue.append(self)
|
830
|
+
while queue:
|
831
|
+
node = queue.pop()
|
832
|
+
action(node)
|
833
|
+
for subtree in node:
|
834
|
+
if not subtree.is_empty():
|
835
|
+
queue.insert(0, subtree)
|
836
|
+
|
837
|
+
def paths_at_depth(self, depth, path=[]):
|
838
|
+
r"""
|
839
|
+
Return a generator for all paths at a fixed depth.
|
840
|
+
|
841
|
+
This iterates over all paths for nodes that are at the given depth.
|
842
|
+
|
843
|
+
Here the root is considered to have depth 0.
|
844
|
+
|
845
|
+
INPUT:
|
846
|
+
|
847
|
+
- ``depth`` -- integer
|
848
|
+
- ``path`` -- (optional) list; given path used in the recursion
|
849
|
+
|
850
|
+
.. WARNING::
|
851
|
+
|
852
|
+
The ``path`` option should not be used directly.
|
853
|
+
|
854
|
+
.. SEEALSO::
|
855
|
+
|
856
|
+
:meth:`paths`, :meth:`paths_to_the_right`, :meth:`node_number_at_depth`
|
857
|
+
|
858
|
+
EXAMPLES::
|
859
|
+
|
860
|
+
sage: T = OrderedTree([[[], [[], [[]]]], [], [[[],[]]], [], []])
|
861
|
+
sage: ascii_art(T)
|
862
|
+
______o_______
|
863
|
+
/ / / / /
|
864
|
+
_o__ o o o o
|
865
|
+
/ / |
|
866
|
+
o o_ o_
|
867
|
+
/ / / /
|
868
|
+
o o o o
|
869
|
+
|
|
870
|
+
o
|
871
|
+
sage: list(T.paths_at_depth(0))
|
872
|
+
[()]
|
873
|
+
sage: list(T.paths_at_depth(2))
|
874
|
+
[(0, 0), (0, 1), (2, 0)]
|
875
|
+
sage: list(T.paths_at_depth(4))
|
876
|
+
[(0, 1, 1, 0)]
|
877
|
+
sage: list(T.paths_at_depth(5))
|
878
|
+
[]
|
879
|
+
|
880
|
+
sage: T2 = OrderedTree([])
|
881
|
+
sage: list(T2.paths_at_depth(0))
|
882
|
+
[()]
|
883
|
+
"""
|
884
|
+
if not depth:
|
885
|
+
yield tuple(path)
|
886
|
+
else:
|
887
|
+
for i in range(len(self)):
|
888
|
+
yield from self[i].paths_at_depth(depth - 1, path + [i])
|
889
|
+
|
890
|
+
def node_number_at_depth(self, depth):
|
891
|
+
r"""
|
892
|
+
Return the number of nodes at a given depth.
|
893
|
+
|
894
|
+
This counts all nodes that are at the given depth.
|
895
|
+
|
896
|
+
Here the root is considered to have depth 0.
|
897
|
+
|
898
|
+
INPUT:
|
899
|
+
|
900
|
+
- ``depth`` -- integer
|
901
|
+
|
902
|
+
.. SEEALSO::
|
903
|
+
|
904
|
+
:meth:`node_number`, :meth:`node_number_to_the_right`, :meth:`paths_at_depth`
|
905
|
+
|
906
|
+
EXAMPLES::
|
907
|
+
|
908
|
+
sage: T = OrderedTree([[[], [[]]], [[], [[[]]]], []])
|
909
|
+
sage: ascii_art(T)
|
910
|
+
___o____
|
911
|
+
/ / /
|
912
|
+
o_ o_ o
|
913
|
+
/ / / /
|
914
|
+
o o o o
|
915
|
+
| |
|
916
|
+
o o
|
917
|
+
|
|
918
|
+
o
|
919
|
+
sage: [T.node_number_at_depth(i) for i in range(6)]
|
920
|
+
[1, 3, 4, 2, 1, 0]
|
921
|
+
|
922
|
+
TESTS:
|
923
|
+
|
924
|
+
Check that the empty tree has no nodes (:issue:`29134`)::
|
925
|
+
|
926
|
+
sage: T = BinaryTree()
|
927
|
+
sage: T
|
928
|
+
.
|
929
|
+
sage: T.is_empty()
|
930
|
+
True
|
931
|
+
sage: [T.node_number_at_depth(i) for i in range(3)]
|
932
|
+
[0, 0, 0]
|
933
|
+
|
934
|
+
Check that we do not hit a recursion limit::
|
935
|
+
|
936
|
+
sage: T = OrderedTree([])
|
937
|
+
sage: for _ in range(9999):
|
938
|
+
....: T = OrderedTree([T])
|
939
|
+
sage: T.node_number_at_depth(2000)
|
940
|
+
1
|
941
|
+
"""
|
942
|
+
if self.is_empty():
|
943
|
+
return 0
|
944
|
+
m = 0
|
945
|
+
|
946
|
+
def fr_action(node):
|
947
|
+
nonlocal m, depths, depth
|
948
|
+
if depths[-1] == depth:
|
949
|
+
m += 1
|
950
|
+
|
951
|
+
def m_action(node):
|
952
|
+
nonlocal depths
|
953
|
+
depths.append(depths[-1] + 1)
|
954
|
+
|
955
|
+
def fn_action(node):
|
956
|
+
nonlocal depths
|
957
|
+
depths.pop()
|
958
|
+
|
959
|
+
def lf_action(node):
|
960
|
+
nonlocal m, depths, depth
|
961
|
+
if depths[-1] == depth:
|
962
|
+
m += 1
|
963
|
+
depths.pop()
|
964
|
+
|
965
|
+
depths = [0]
|
966
|
+
self.contour_traversal(fr_action, m_action, fn_action, lf_action)
|
967
|
+
return Integer(m)
|
968
|
+
|
969
|
+
def paths_to_the_right(self, path):
|
970
|
+
r"""
|
971
|
+
Return a generator of paths for all nodes at the same
|
972
|
+
depth and to the right of the node identified by ``path``.
|
973
|
+
|
974
|
+
This iterates over the paths for nodes that are at the same
|
975
|
+
depth as the given one, and strictly to its right.
|
976
|
+
|
977
|
+
INPUT:
|
978
|
+
|
979
|
+
- ``path`` -- any path in the tree
|
980
|
+
|
981
|
+
.. SEEALSO::
|
982
|
+
|
983
|
+
:meth:`paths`, :meth:`paths_at_depth`, :meth:`node_number_to_the_right`
|
984
|
+
|
985
|
+
EXAMPLES::
|
986
|
+
|
987
|
+
sage: T = OrderedTree([[[], [[]]], [[], [[[]]]], []])
|
988
|
+
sage: ascii_art(T)
|
989
|
+
___o____
|
990
|
+
/ / /
|
991
|
+
o_ o_ o
|
992
|
+
/ / / /
|
993
|
+
o o o o
|
994
|
+
| |
|
995
|
+
o o
|
996
|
+
|
|
997
|
+
o
|
998
|
+
sage: g = T.paths_to_the_right(())
|
999
|
+
sage: list(g)
|
1000
|
+
[]
|
1001
|
+
|
1002
|
+
sage: g = T.paths_to_the_right((0,))
|
1003
|
+
sage: list(g)
|
1004
|
+
[(1,), (2,)]
|
1005
|
+
|
1006
|
+
sage: g = T.paths_to_the_right((0,1))
|
1007
|
+
sage: list(g)
|
1008
|
+
[(1, 0), (1, 1)]
|
1009
|
+
|
1010
|
+
sage: g = T.paths_to_the_right((0,1,0))
|
1011
|
+
sage: list(g)
|
1012
|
+
[(1, 1, 0)]
|
1013
|
+
|
1014
|
+
sage: g = T.paths_to_the_right((1,2))
|
1015
|
+
sage: list(g)
|
1016
|
+
[]
|
1017
|
+
"""
|
1018
|
+
depth = len(path)
|
1019
|
+
if (not depth) or path[0] >= len(self):
|
1020
|
+
return
|
1021
|
+
for i in range(path[0] + 1, len(self)):
|
1022
|
+
for p in self[i].paths_at_depth(depth - 1, path=[i]):
|
1023
|
+
yield p
|
1024
|
+
for p in self[path[0]].paths_to_the_right(path[1:]):
|
1025
|
+
yield tuple([path[0]] + list(p))
|
1026
|
+
|
1027
|
+
def node_number_to_the_right(self, path):
|
1028
|
+
r"""
|
1029
|
+
Return the number of nodes at the same depth and to the right of
|
1030
|
+
the node identified by ``path``.
|
1031
|
+
|
1032
|
+
This counts the nodes that are at the same depth as the given
|
1033
|
+
one, and strictly to its right.
|
1034
|
+
|
1035
|
+
.. SEEALSO::
|
1036
|
+
|
1037
|
+
:meth:`node_number`, :meth:`node_number_at_depth`, :meth:`paths_to_the_right`
|
1038
|
+
|
1039
|
+
EXAMPLES::
|
1040
|
+
|
1041
|
+
sage: T = OrderedTree([[[], [[]]], [[], [[[]]]], []])
|
1042
|
+
sage: ascii_art(T)
|
1043
|
+
___o____
|
1044
|
+
/ / /
|
1045
|
+
o_ o_ o
|
1046
|
+
/ / / /
|
1047
|
+
o o o o
|
1048
|
+
| |
|
1049
|
+
o o
|
1050
|
+
|
|
1051
|
+
o
|
1052
|
+
sage: T.node_number_to_the_right(())
|
1053
|
+
0
|
1054
|
+
sage: T.node_number_to_the_right((0,))
|
1055
|
+
2
|
1056
|
+
sage: T.node_number_to_the_right((0,1))
|
1057
|
+
2
|
1058
|
+
sage: T.node_number_to_the_right((0,1,0))
|
1059
|
+
1
|
1060
|
+
|
1061
|
+
sage: T = OrderedTree([])
|
1062
|
+
sage: T.node_number_to_the_right(())
|
1063
|
+
0
|
1064
|
+
"""
|
1065
|
+
depth = len(path)
|
1066
|
+
if depth == 0:
|
1067
|
+
return Integer(0)
|
1068
|
+
result = sum(son.node_number_at_depth(depth - 1)
|
1069
|
+
for son in self[path[0] + 1:])
|
1070
|
+
if path[0] < len(self) and path[0] >= 0:
|
1071
|
+
result += self[path[0]].node_number_to_the_right(path[1:])
|
1072
|
+
return result
|
1073
|
+
|
1074
|
+
def subtrees(self):
|
1075
|
+
"""
|
1076
|
+
Return a generator for all nonempty subtrees of ``self``.
|
1077
|
+
|
1078
|
+
The number of nonempty subtrees of a tree is its number of
|
1079
|
+
nodes. (The word "nonempty" makes a difference only in the
|
1080
|
+
case of binary trees. For ordered trees, for example, all
|
1081
|
+
trees are nonempty.)
|
1082
|
+
|
1083
|
+
EXAMPLES::
|
1084
|
+
|
1085
|
+
sage: list(OrderedTree([]).subtrees())
|
1086
|
+
[[]]
|
1087
|
+
sage: list(OrderedTree([[],[[]]]).subtrees())
|
1088
|
+
[[[], [[]]], [], [[]], []]
|
1089
|
+
|
1090
|
+
sage: list(OrderedTree([[],[[]]]).canonical_labelling().subtrees())
|
1091
|
+
[1[2[], 3[4[]]], 2[], 3[4[]], 4[]]
|
1092
|
+
|
1093
|
+
sage: list(BinaryTree([[],[[],[]]]).subtrees())
|
1094
|
+
[[[., .], [[., .], [., .]]], [., .], [[., .], [., .]], [., .], [., .]]
|
1095
|
+
|
1096
|
+
sage: v = BinaryTree([[],[]])
|
1097
|
+
sage: list(v.canonical_labelling().subtrees())
|
1098
|
+
[2[1[., .], 3[., .]], 1[., .], 3[., .]]
|
1099
|
+
|
1100
|
+
TESTS::
|
1101
|
+
|
1102
|
+
sage: t = OrderedTree([[], [[], [[], []], [[], []]], [[], []]])
|
1103
|
+
sage: t.node_number() == len(list(t.subtrees()))
|
1104
|
+
True
|
1105
|
+
sage: list(BinaryTree().subtrees())
|
1106
|
+
[]
|
1107
|
+
sage: bt = BinaryTree([[],[[],[]]])
|
1108
|
+
sage: bt.node_number() == len(list(bt.subtrees()))
|
1109
|
+
True
|
1110
|
+
"""
|
1111
|
+
return self.pre_order_traversal_iter()
|
1112
|
+
|
1113
|
+
def paths(self):
|
1114
|
+
"""
|
1115
|
+
Return a generator for all paths to nodes of ``self``.
|
1116
|
+
|
1117
|
+
OUTPUT:
|
1118
|
+
|
1119
|
+
This method returns a list of sequences of integers. Each of these
|
1120
|
+
sequences represents a path from the root node to some node. For
|
1121
|
+
instance, `(1, 3, 2, 5, 0, 3)` represents the node obtained by
|
1122
|
+
choosing the 1st child of the root node (in the ordering returned
|
1123
|
+
by ``iter``), then the 3rd child of its child, then the 2nd child
|
1124
|
+
of the latter, etc. (where the labelling of the children is
|
1125
|
+
zero-based).
|
1126
|
+
|
1127
|
+
The root element is represented by the empty tuple ``()``.
|
1128
|
+
|
1129
|
+
.. SEEALSO::
|
1130
|
+
|
1131
|
+
:meth:`paths_at_depth`, :meth:`paths_to_the_right`
|
1132
|
+
|
1133
|
+
EXAMPLES::
|
1134
|
+
|
1135
|
+
sage: list(OrderedTree([]).paths())
|
1136
|
+
[()]
|
1137
|
+
sage: list(OrderedTree([[],[[]]]).paths())
|
1138
|
+
[(), (0,), (1,), (1, 0)]
|
1139
|
+
|
1140
|
+
sage: list(BinaryTree([[],[[],[]]]).paths())
|
1141
|
+
[(), (0,), (1,), (1, 0), (1, 1)]
|
1142
|
+
|
1143
|
+
TESTS::
|
1144
|
+
|
1145
|
+
sage: t = OrderedTree([[], [[], [[], []], [[], []]], [[], []]])
|
1146
|
+
sage: t.node_number() == len(list(t.paths()))
|
1147
|
+
True
|
1148
|
+
sage: list(BinaryTree().paths())
|
1149
|
+
[]
|
1150
|
+
sage: bt = BinaryTree([[],[[],[]]])
|
1151
|
+
sage: bt.node_number() == len(list(bt.paths()))
|
1152
|
+
True
|
1153
|
+
"""
|
1154
|
+
if not self.is_empty():
|
1155
|
+
yield ()
|
1156
|
+
for i, t in enumerate(self):
|
1157
|
+
for p in t.paths():
|
1158
|
+
yield (i,) + p
|
1159
|
+
|
1160
|
+
def node_number(self):
|
1161
|
+
"""
|
1162
|
+
Return the number of nodes of ``self``.
|
1163
|
+
|
1164
|
+
.. SEEALSO::
|
1165
|
+
|
1166
|
+
:meth:`node_number_at_depth`, :meth:`node_number_to_the_right`
|
1167
|
+
|
1168
|
+
EXAMPLES::
|
1169
|
+
|
1170
|
+
sage: OrderedTree().node_number()
|
1171
|
+
1
|
1172
|
+
sage: OrderedTree([]).node_number()
|
1173
|
+
1
|
1174
|
+
sage: OrderedTree([[],[]]).node_number()
|
1175
|
+
3
|
1176
|
+
sage: OrderedTree([[],[[]]]).node_number()
|
1177
|
+
4
|
1178
|
+
sage: OrderedTree([[], [[], [[], []], [[], []]], [[], []]]).node_number()
|
1179
|
+
13
|
1180
|
+
|
1181
|
+
EXAMPLES::
|
1182
|
+
|
1183
|
+
sage: BinaryTree(None).node_number()
|
1184
|
+
0
|
1185
|
+
sage: BinaryTree([]).node_number()
|
1186
|
+
1
|
1187
|
+
sage: BinaryTree([[], None]).node_number()
|
1188
|
+
2
|
1189
|
+
sage: BinaryTree([[None, [[], []]], None]).node_number()
|
1190
|
+
5
|
1191
|
+
|
1192
|
+
TESTS:
|
1193
|
+
|
1194
|
+
Check that we do not hit a recursion limit::
|
1195
|
+
|
1196
|
+
sage: T = OrderedTree([])
|
1197
|
+
sage: for _ in range(9999):
|
1198
|
+
....: T = OrderedTree([T])
|
1199
|
+
sage: T.node_number()
|
1200
|
+
10000
|
1201
|
+
"""
|
1202
|
+
count = 0
|
1203
|
+
|
1204
|
+
def incr(node):
|
1205
|
+
nonlocal count
|
1206
|
+
count += 1
|
1207
|
+
|
1208
|
+
self.iterative_pre_order_traversal(incr)
|
1209
|
+
return Integer(count)
|
1210
|
+
|
1211
|
+
def depth(self):
|
1212
|
+
"""
|
1213
|
+
Return the depth of ``self``.
|
1214
|
+
|
1215
|
+
EXAMPLES::
|
1216
|
+
|
1217
|
+
sage: OrderedTree().depth()
|
1218
|
+
1
|
1219
|
+
sage: OrderedTree([]).depth()
|
1220
|
+
1
|
1221
|
+
sage: OrderedTree([[],[]]).depth()
|
1222
|
+
2
|
1223
|
+
sage: OrderedTree([[],[[]]]).depth()
|
1224
|
+
3
|
1225
|
+
sage: OrderedTree([[], [[], [[], []], [[], []]], [[], []]]).depth()
|
1226
|
+
4
|
1227
|
+
|
1228
|
+
sage: BinaryTree().depth()
|
1229
|
+
0
|
1230
|
+
sage: BinaryTree([[],[[],[]]]).depth()
|
1231
|
+
3
|
1232
|
+
|
1233
|
+
TESTS:
|
1234
|
+
|
1235
|
+
Check that we do not hit a recursion limit::
|
1236
|
+
|
1237
|
+
sage: T = OrderedTree([])
|
1238
|
+
sage: for _ in range(9999):
|
1239
|
+
....: T = OrderedTree([T])
|
1240
|
+
sage: T.depth()
|
1241
|
+
10000
|
1242
|
+
"""
|
1243
|
+
if self.is_empty():
|
1244
|
+
return 0
|
1245
|
+
m = []
|
1246
|
+
|
1247
|
+
def action(node):
|
1248
|
+
nonlocal m
|
1249
|
+
if node.is_empty():
|
1250
|
+
m.append(-1)
|
1251
|
+
elif not bool(node):
|
1252
|
+
m.append(0)
|
1253
|
+
else:
|
1254
|
+
mx = max(m.pop() for _ in node)
|
1255
|
+
m.append(mx + 1)
|
1256
|
+
|
1257
|
+
self.contour_traversal(final_action=action, leaf_action=action)
|
1258
|
+
return Integer(m[0] + 1)
|
1259
|
+
|
1260
|
+
def _ascii_art_(self):
|
1261
|
+
r"""
|
1262
|
+
TESTS::
|
1263
|
+
|
1264
|
+
sage: t = OrderedTree([])
|
1265
|
+
sage: ascii_art(t)
|
1266
|
+
o
|
1267
|
+
sage: t = OrderedTree([[]])
|
1268
|
+
sage: aa = ascii_art(t);aa
|
1269
|
+
o
|
1270
|
+
|
|
1271
|
+
o
|
1272
|
+
sage: aa.get_baseline()
|
1273
|
+
2
|
1274
|
+
sage: tt1 = OrderedTree([[],[[],[],[[[[]]]]],[[[],[],[],[]]]])
|
1275
|
+
sage: ascii_art(tt1)
|
1276
|
+
_____o_______
|
1277
|
+
/ / /
|
1278
|
+
o _o__ o
|
1279
|
+
/ / / |
|
1280
|
+
o o o __o___
|
1281
|
+
| / / / /
|
1282
|
+
o o o o o
|
1283
|
+
|
|
1284
|
+
o
|
1285
|
+
|
|
1286
|
+
o
|
1287
|
+
sage: ascii_art(tt1.canonical_labelling())
|
1288
|
+
______1_______
|
1289
|
+
/ / /
|
1290
|
+
2 _3__ 10
|
1291
|
+
/ / / |
|
1292
|
+
4 5 6 ___11____
|
1293
|
+
| / / / /
|
1294
|
+
7 12 13 14 15
|
1295
|
+
|
|
1296
|
+
8
|
1297
|
+
|
|
1298
|
+
9
|
1299
|
+
sage: ascii_art(OrderedTree([[],[[]]]))
|
1300
|
+
o_
|
1301
|
+
/ /
|
1302
|
+
o o
|
1303
|
+
|
|
1304
|
+
o
|
1305
|
+
sage: t = OrderedTree([[[],[[[],[]]],[[]]],[[[[[],[]]]]],[[],[]]])
|
1306
|
+
sage: ascii_art(t)
|
1307
|
+
_____o_______
|
1308
|
+
/ / /
|
1309
|
+
__o____ o o_
|
1310
|
+
/ / / | / /
|
1311
|
+
o o o o o o
|
1312
|
+
| | |
|
1313
|
+
o_ o o
|
1314
|
+
/ / |
|
1315
|
+
o o o_
|
1316
|
+
/ /
|
1317
|
+
o o
|
1318
|
+
sage: ascii_art(t.canonical_labelling())
|
1319
|
+
______1________
|
1320
|
+
/ / /
|
1321
|
+
__2____ 10 16_
|
1322
|
+
/ / / | / /
|
1323
|
+
3 4 8 11 17 18
|
1324
|
+
| | |
|
1325
|
+
5_ 9 12
|
1326
|
+
/ / |
|
1327
|
+
6 7 13_
|
1328
|
+
/ /
|
1329
|
+
14 15
|
1330
|
+
"""
|
1331
|
+
def node_to_str(t):
|
1332
|
+
return str(t.label()) if hasattr(t, "label") else "o"
|
1333
|
+
|
1334
|
+
if self.is_empty():
|
1335
|
+
from sage.typeset.ascii_art import empty_ascii_art
|
1336
|
+
return empty_ascii_art
|
1337
|
+
|
1338
|
+
from sage.typeset.ascii_art import AsciiArt
|
1339
|
+
if len(self) == 0:
|
1340
|
+
t_repr = AsciiArt([node_to_str(self)])
|
1341
|
+
t_repr._root = 1
|
1342
|
+
return t_repr
|
1343
|
+
if len(self) == 1:
|
1344
|
+
repr_child = self[0]._ascii_art_()
|
1345
|
+
sep = AsciiArt([" " * (repr_child._root - 1)])
|
1346
|
+
t_repr = AsciiArt([node_to_str(self)])
|
1347
|
+
t_repr._root = 1
|
1348
|
+
repr_root = (sep + t_repr) * (sep + AsciiArt(["|"]))
|
1349
|
+
t_repr = repr_root * repr_child
|
1350
|
+
t_repr._root = repr_child._root
|
1351
|
+
t_repr._baseline = t_repr._h - 1
|
1352
|
+
return t_repr
|
1353
|
+
# General case
|
1354
|
+
l_repr = [subtree._ascii_art_() for subtree in self]
|
1355
|
+
acc = l_repr.pop(0)
|
1356
|
+
whitesep = acc._root + 1
|
1357
|
+
lf_sep = " " * (acc._root + 1) + "_" * (acc._l - acc._root)
|
1358
|
+
ls_sep = " " * (acc._root) + "/" + " " * (acc._l - acc._root)
|
1359
|
+
while l_repr:
|
1360
|
+
t_repr = l_repr.pop(0)
|
1361
|
+
acc += AsciiArt([" "]) + t_repr
|
1362
|
+
if len(l_repr) == 0:
|
1363
|
+
lf_sep += "_" * (t_repr._root + 1)
|
1364
|
+
else:
|
1365
|
+
lf_sep += "_" * (t_repr._l + 1)
|
1366
|
+
ls_sep += " " * (t_repr._root) + "/" + " " * (t_repr._l - t_repr._root)
|
1367
|
+
mid = whitesep + (len(lf_sep) - whitesep) // 2
|
1368
|
+
node = node_to_str(self)
|
1369
|
+
t_repr = AsciiArt([lf_sep[:mid - 1] + node + lf_sep[mid + len(node) - 1:], ls_sep]) * acc
|
1370
|
+
t_repr._root = mid
|
1371
|
+
t_repr._baseline = t_repr._h - 1
|
1372
|
+
return t_repr
|
1373
|
+
|
1374
|
+
def _unicode_art_(self):
|
1375
|
+
r"""
|
1376
|
+
TESTS::
|
1377
|
+
|
1378
|
+
sage: t = OrderedTree([])
|
1379
|
+
sage: unicode_art(t)
|
1380
|
+
o
|
1381
|
+
sage: t = OrderedTree([[]])
|
1382
|
+
sage: aa = unicode_art(t);aa
|
1383
|
+
o
|
1384
|
+
│
|
1385
|
+
o
|
1386
|
+
sage: aa.get_baseline()
|
1387
|
+
2
|
1388
|
+
sage: tt1 = OrderedTree([[],[[],[],[[[[]]]]],[[[],[],[],[]]]])
|
1389
|
+
sage: unicode_art(tt1)
|
1390
|
+
╭───┬─o────╮
|
1391
|
+
│ │ │
|
1392
|
+
o ╭─o─╮ o
|
1393
|
+
│ │ │ │
|
1394
|
+
o o o ╭─┬o┬─╮
|
1395
|
+
│ │ │ │ │
|
1396
|
+
o o o o o
|
1397
|
+
│
|
1398
|
+
o
|
1399
|
+
│
|
1400
|
+
o
|
1401
|
+
sage: unicode_art(tt1.canonical_labelling())
|
1402
|
+
╭───┬──1─────╮
|
1403
|
+
│ │ │
|
1404
|
+
2 ╭─3─╮ 10
|
1405
|
+
│ │ │ │
|
1406
|
+
4 5 6 ╭──┬11┬──╮
|
1407
|
+
│ │ │ │ │
|
1408
|
+
7 12 13 14 15
|
1409
|
+
│
|
1410
|
+
8
|
1411
|
+
│
|
1412
|
+
9
|
1413
|
+
sage: unicode_art(OrderedTree([[],[[]]]))
|
1414
|
+
╭o╮
|
1415
|
+
│ │
|
1416
|
+
o o
|
1417
|
+
│
|
1418
|
+
o
|
1419
|
+
sage: t = OrderedTree([[[],[[[],[]]],[[]]],[[[[[],[]]]]],[[],[]]])
|
1420
|
+
sage: unicode_art(t)
|
1421
|
+
╭────o┬───╮
|
1422
|
+
│ │ │
|
1423
|
+
╭──o──╮ o ╭o╮
|
1424
|
+
│ │ │ │ │ │
|
1425
|
+
o o o o o o
|
1426
|
+
│ │ │
|
1427
|
+
╭o╮ o o
|
1428
|
+
│ │ │
|
1429
|
+
o o ╭o╮
|
1430
|
+
│ │
|
1431
|
+
o o
|
1432
|
+
sage: unicode_art(t.canonical_labelling())
|
1433
|
+
╭──────1─────╮
|
1434
|
+
│ │ │
|
1435
|
+
╭──2──╮ 10 ╭16╮
|
1436
|
+
│ │ │ │ │ │
|
1437
|
+
3 4 8 11 17 18
|
1438
|
+
│ │ │
|
1439
|
+
╭5╮ 9 12
|
1440
|
+
│ │ │
|
1441
|
+
6 7 ╭13╮
|
1442
|
+
│ │
|
1443
|
+
14 15
|
1444
|
+
"""
|
1445
|
+
|
1446
|
+
def node_to_str(t):
|
1447
|
+
if hasattr(t, "label"):
|
1448
|
+
return str(t.label())
|
1449
|
+
else:
|
1450
|
+
return "o"
|
1451
|
+
# other possible choices for nodes would be u"█ ▓ ░ ╋ ╬"
|
1452
|
+
|
1453
|
+
if self.is_empty():
|
1454
|
+
from sage.typeset.unicode_art import empty_unicode_art
|
1455
|
+
return empty_unicode_art
|
1456
|
+
|
1457
|
+
from sage.typeset.unicode_art import UnicodeArt
|
1458
|
+
if not len(self):
|
1459
|
+
t_repr = UnicodeArt([node_to_str(self)])
|
1460
|
+
t_repr._root = 0
|
1461
|
+
return t_repr
|
1462
|
+
|
1463
|
+
if len(self) == 1:
|
1464
|
+
repr_child = self[0]._unicode_art_()
|
1465
|
+
sep = UnicodeArt([" " * repr_child._root])
|
1466
|
+
t_repr = UnicodeArt([node_to_str(self)])
|
1467
|
+
repr_root = (sep + t_repr) * (sep + UnicodeArt(["│"]))
|
1468
|
+
t_repr = repr_root * repr_child
|
1469
|
+
t_repr._root = repr_child._root
|
1470
|
+
t_repr._baseline = t_repr._h - 1
|
1471
|
+
return t_repr
|
1472
|
+
|
1473
|
+
# General case
|
1474
|
+
l_repr = [subtree._unicode_art_() for subtree in self]
|
1475
|
+
acc = l_repr.pop(0)
|
1476
|
+
whitesep = acc._root
|
1477
|
+
lf_sep = " " * whitesep + "╭" + "─" * (acc._l - acc._root)
|
1478
|
+
ls_sep = " " * whitesep + "│" + " " * (acc._l - acc._root)
|
1479
|
+
while l_repr:
|
1480
|
+
tr = l_repr.pop(0)
|
1481
|
+
acc += UnicodeArt([" "]) + tr
|
1482
|
+
if not l_repr:
|
1483
|
+
lf_sep += "─" * (tr._root) + "╮"
|
1484
|
+
ls_sep += " " * (tr._root) + "│"
|
1485
|
+
else:
|
1486
|
+
lf_sep += "─" * (tr._root) + "┬" + "─" * (tr._l - tr._root)
|
1487
|
+
ls_sep += " " * (tr._root) + "│" + " " * (tr._l - tr._root)
|
1488
|
+
mid = whitesep + (len(lf_sep) - whitesep) // 2
|
1489
|
+
node = node_to_str(self)
|
1490
|
+
lf_sep = (lf_sep[:mid - len(node) // 2] + node +
|
1491
|
+
lf_sep[mid + len(node) - len(node) // 2:])
|
1492
|
+
t_repr = UnicodeArt([lf_sep, ls_sep]) * acc
|
1493
|
+
t_repr._root = mid
|
1494
|
+
t_repr._baseline = t_repr._h - 1
|
1495
|
+
return t_repr
|
1496
|
+
|
1497
|
+
def canonical_labelling(self, shift=1):
|
1498
|
+
"""
|
1499
|
+
Return a labelled version of ``self``.
|
1500
|
+
|
1501
|
+
The actual canonical labelling is currently unspecified. However, it
|
1502
|
+
is guaranteed to have labels in `1...n` where `n` is the number of
|
1503
|
+
nodes of the tree. Moreover, two (unlabelled) trees compare as equal if
|
1504
|
+
and only if their canonical labelled trees compare as equal.
|
1505
|
+
|
1506
|
+
EXAMPLES::
|
1507
|
+
|
1508
|
+
sage: t = OrderedTree([[], [[], [[], []], [[], []]], [[], []]])
|
1509
|
+
sage: t.canonical_labelling()
|
1510
|
+
1[2[], 3[4[], 5[6[], 7[]], 8[9[], 10[]]], 11[12[], 13[]]]
|
1511
|
+
|
1512
|
+
sage: BinaryTree([]).canonical_labelling()
|
1513
|
+
1[., .]
|
1514
|
+
sage: BinaryTree([[],[[],[]]]).canonical_labelling()
|
1515
|
+
2[1[., .], 4[3[., .], 5[., .]]]
|
1516
|
+
|
1517
|
+
TESTS::
|
1518
|
+
|
1519
|
+
sage: BinaryTree().canonical_labelling()
|
1520
|
+
.
|
1521
|
+
"""
|
1522
|
+
LTR = self.parent().labelled_trees()
|
1523
|
+
liste = []
|
1524
|
+
deca = 1
|
1525
|
+
for subtree in self:
|
1526
|
+
liste += [subtree.canonical_labelling(shift + deca)]
|
1527
|
+
deca += subtree.node_number()
|
1528
|
+
return LTR._element_constructor_(liste, label=shift)
|
1529
|
+
|
1530
|
+
def to_hexacode(self):
|
1531
|
+
r"""
|
1532
|
+
Transform a tree into a hexadecimal string.
|
1533
|
+
|
1534
|
+
The definition of the hexacode is recursive. The first letter is
|
1535
|
+
the valence of the root as a hexadecimal (up to 15), followed by
|
1536
|
+
the concatenation of the hexacodes of the subtrees.
|
1537
|
+
|
1538
|
+
This method only works for trees where every vertex has
|
1539
|
+
valency at most 15.
|
1540
|
+
|
1541
|
+
See :func:`from_hexacode` for the reverse transformation.
|
1542
|
+
|
1543
|
+
EXAMPLES::
|
1544
|
+
|
1545
|
+
sage: from sage.combinat.abstract_tree import from_hexacode
|
1546
|
+
sage: LT = LabelledOrderedTrees()
|
1547
|
+
sage: from_hexacode('2010', LT).to_hexacode()
|
1548
|
+
'2010'
|
1549
|
+
sage: LT.an_element().to_hexacode()
|
1550
|
+
'3020010'
|
1551
|
+
sage: t = from_hexacode('a0000000000000000', LT)
|
1552
|
+
sage: t.to_hexacode()
|
1553
|
+
'a0000000000'
|
1554
|
+
|
1555
|
+
sage: OrderedTrees(6).an_element().to_hexacode()
|
1556
|
+
'500000'
|
1557
|
+
|
1558
|
+
TESTS::
|
1559
|
+
|
1560
|
+
sage: one = LT([], label='@')
|
1561
|
+
sage: LT([one for _ in range(15)], label='@').to_hexacode()
|
1562
|
+
'f000000000000000'
|
1563
|
+
sage: LT([one for _ in range(16)], label='@').to_hexacode()
|
1564
|
+
Traceback (most recent call last):
|
1565
|
+
...
|
1566
|
+
ValueError: the width of the tree is too large
|
1567
|
+
"""
|
1568
|
+
if len(self) > 15:
|
1569
|
+
raise ValueError("the width of the tree is too large")
|
1570
|
+
if self.node_number() == 1:
|
1571
|
+
return "0"
|
1572
|
+
return ("%x" % len(self)) + "".join(u.to_hexacode() for u in self)
|
1573
|
+
|
1574
|
+
def tree_factorial(self):
|
1575
|
+
r"""
|
1576
|
+
Return the tree-factorial of ``self``.
|
1577
|
+
|
1578
|
+
Definition:
|
1579
|
+
|
1580
|
+
The tree-factorial `T!` of a tree `T` is the product `\prod_{v\in
|
1581
|
+
T}\#\mbox{children}(v)`.
|
1582
|
+
|
1583
|
+
EXAMPLES::
|
1584
|
+
|
1585
|
+
sage: LT = LabelledOrderedTrees()
|
1586
|
+
sage: t = LT([LT([],label=6),LT([],label=1)],label=9)
|
1587
|
+
sage: t.tree_factorial()
|
1588
|
+
3
|
1589
|
+
|
1590
|
+
sage: BinaryTree([[],[[],[]]]).tree_factorial()
|
1591
|
+
15
|
1592
|
+
|
1593
|
+
TESTS::
|
1594
|
+
|
1595
|
+
sage: BinaryTree().tree_factorial()
|
1596
|
+
1
|
1597
|
+
"""
|
1598
|
+
nb = self.node_number()
|
1599
|
+
if nb <= 1:
|
1600
|
+
return Integer(1)
|
1601
|
+
return nb * prod(s.tree_factorial() for s in self)
|
1602
|
+
|
1603
|
+
def _latex_(self):
|
1604
|
+
r"""
|
1605
|
+
Generate `\LaTeX` output which can be easily modified.
|
1606
|
+
|
1607
|
+
TESTS::
|
1608
|
+
|
1609
|
+
sage: latex(BinaryTree([[[],[]],[[],None]]))
|
1610
|
+
{ \newcommand{\nodea}{\node[draw,circle] (a) {$$}
|
1611
|
+
;}\newcommand{\nodeb}{\node[draw,circle] (b) {$$}
|
1612
|
+
;}\newcommand{\nodec}{\node[draw,circle] (c) {$$}
|
1613
|
+
;}\newcommand{\noded}{\node[draw,circle] (d) {$$}
|
1614
|
+
;}\newcommand{\nodee}{\node[draw,circle] (e) {$$}
|
1615
|
+
;}\newcommand{\nodef}{\node[draw,circle] (f) {$$}
|
1616
|
+
;}\begin{tikzpicture}[auto]
|
1617
|
+
\matrix[column sep=.3cm, row sep=.3cm,ampersand replacement=\&]{
|
1618
|
+
\& \& \& \nodea \& \& \& \\
|
1619
|
+
\& \nodeb \& \& \& \& \nodee \& \\
|
1620
|
+
\nodec \& \& \noded \& \& \nodef \& \& \\
|
1621
|
+
};
|
1622
|
+
<BLANKLINE>
|
1623
|
+
\path[ultra thick, red] (b) edge (c) edge (d)
|
1624
|
+
(e) edge (f)
|
1625
|
+
(a) edge (b) edge (e);
|
1626
|
+
\end{tikzpicture}}
|
1627
|
+
"""
|
1628
|
+
#######################################################################
|
1629
|
+
# load tikz in the preamble for *view*
|
1630
|
+
from sage.misc.latex import latex
|
1631
|
+
latex.add_package_to_preamble_if_available("tikz")
|
1632
|
+
#######################################################################
|
1633
|
+
# latex environment : TikZ
|
1634
|
+
begin_env = "\\begin{tikzpicture}[auto]\n"
|
1635
|
+
end_env = "\\end{tikzpicture}"
|
1636
|
+
# it uses matrix trick to place each node
|
1637
|
+
matrix_begin = "\\matrix[column sep=.3cm, row sep=.3cm,ampersand replacement=\\&]{\n"
|
1638
|
+
matrix_end = "\\\\\n};\n"
|
1639
|
+
# a basic path to each edges
|
1640
|
+
path_begin = "\\path[ultra thick, red] "
|
1641
|
+
path_end = ";\n"
|
1642
|
+
# to make a pretty output, it creates one LaTeX command for
|
1643
|
+
# each node
|
1644
|
+
cmd = "\\node"
|
1645
|
+
new_cmd1 = "\\newcommand{" + cmd
|
1646
|
+
new_cmd2 = "}{\\node[draw,circle] ("
|
1647
|
+
new_cmd3 = ") {$"
|
1648
|
+
new_cmd4 = "$}\n;}"
|
1649
|
+
# some variables to simplify code
|
1650
|
+
sep = "\\&"
|
1651
|
+
space = " " * 9
|
1652
|
+
sepspace = sep + space
|
1653
|
+
spacesep = space + sep
|
1654
|
+
|
1655
|
+
def node_to_str(node):
|
1656
|
+
return " " + node + " " * (len(space) - 1 - len(node))
|
1657
|
+
# # TODO:: modify how to create nodes --> new_cmd : \\node[...] in create_node
|
1658
|
+
num = [0]
|
1659
|
+
|
1660
|
+
def resolve(self):
|
1661
|
+
nodes = []
|
1662
|
+
matrix = []
|
1663
|
+
edges = []
|
1664
|
+
|
1665
|
+
def create_node(self):
|
1666
|
+
r"""
|
1667
|
+
create a name (infixe reading)
|
1668
|
+
-> ex: b
|
1669
|
+
create a new command:
|
1670
|
+
-> ex: \newcommand{\nodeb}{\node[draw,circle] (b) {$$};
|
1671
|
+
return the name and the command to build:
|
1672
|
+
. the matrix
|
1673
|
+
. and the edges
|
1674
|
+
"""
|
1675
|
+
name = "".join(chr(ord(x) + 49) for x in str(num[0]))
|
1676
|
+
node = cmd + name
|
1677
|
+
nodes.append((name,
|
1678
|
+
(str(self.label()) if hasattr(self, "label") else ""))
|
1679
|
+
)
|
1680
|
+
num[0] += 1
|
1681
|
+
return node, name
|
1682
|
+
|
1683
|
+
def empty_tree():
|
1684
|
+
r"""
|
1685
|
+
TESTS::
|
1686
|
+
|
1687
|
+
sage: t = BinaryTree()
|
1688
|
+
sage: print(latex(t))
|
1689
|
+
{ \begin{tikzpicture}[auto]
|
1690
|
+
\matrix[column sep=.3cm, row sep=.3cm,ampersand replacement=\&]{
|
1691
|
+
\\
|
1692
|
+
};
|
1693
|
+
\end{tikzpicture}}
|
1694
|
+
"""
|
1695
|
+
matrix.append(space)
|
1696
|
+
|
1697
|
+
def one_node_tree(self):
|
1698
|
+
r"""
|
1699
|
+
TESTS::
|
1700
|
+
|
1701
|
+
sage: t = BinaryTree([]); print(latex(t))
|
1702
|
+
{ \newcommand{\nodea}{\node[draw,circle] (a) {$$}
|
1703
|
+
;}\begin{tikzpicture}[auto]
|
1704
|
+
\matrix[column sep=.3cm, row sep=.3cm,ampersand replacement=\&]{
|
1705
|
+
\nodea \\
|
1706
|
+
};
|
1707
|
+
\end{tikzpicture}}
|
1708
|
+
sage: t = OrderedTree([]); print(latex(t))
|
1709
|
+
{ \newcommand{\nodea}{\node[draw,circle] (a) {$$}
|
1710
|
+
;}\begin{tikzpicture}[auto]
|
1711
|
+
\matrix[column sep=.3cm, row sep=.3cm,ampersand replacement=\&]{
|
1712
|
+
\nodea \\
|
1713
|
+
};
|
1714
|
+
\end{tikzpicture}}
|
1715
|
+
"""
|
1716
|
+
node, _ = create_node(self)
|
1717
|
+
matrix.append(node_to_str(node))
|
1718
|
+
|
1719
|
+
def concat_matrix(mat, mat2):
|
1720
|
+
lmat = len(mat)
|
1721
|
+
lmat2 = len(mat2)
|
1722
|
+
for i in range(max(lmat, lmat2)):
|
1723
|
+
# mat[i] --> n & n & ...
|
1724
|
+
# mat2[i] -> n' & n' & ...
|
1725
|
+
# ==> n & n & ... & n' & n' & ...
|
1726
|
+
try:
|
1727
|
+
mat[i] += sep + mat2[i]
|
1728
|
+
except IndexError:
|
1729
|
+
if i >= lmat:
|
1730
|
+
if i != 0:
|
1731
|
+
# mat[i] does not exist but
|
1732
|
+
# mat[0] has k "&"
|
1733
|
+
# mat2[i] -> n' & n' & ...
|
1734
|
+
# ==> (_ &)*k+1 n' & n' & ...
|
1735
|
+
nb_of_and = mat[0].count(sep) - mat2[0].count(sep)
|
1736
|
+
mat.append(spacesep * (nb_of_and) + mat2[i])
|
1737
|
+
else:
|
1738
|
+
# mat is empty
|
1739
|
+
# mat2[i] -> n' & n' & ...
|
1740
|
+
# ==> mat2
|
1741
|
+
mat.extend(mat2)
|
1742
|
+
return
|
1743
|
+
else:
|
1744
|
+
# mat[i] -> n & n & ...
|
1745
|
+
# mat2[i] does not exist but mat2[0] exists
|
1746
|
+
# # and has k "&"
|
1747
|
+
# NOTE:: i != 0 because that is a no-empty subtree.
|
1748
|
+
# ==> n & n & ... (& _)*k+1
|
1749
|
+
nb_of_and = mat2[0].count(sep)
|
1750
|
+
mat[i] += sepspace * (nb_of_and + 1)
|
1751
|
+
|
1752
|
+
def tmp(subtree, edge, nodes, edges, matrix):
|
1753
|
+
if not subtree.is_empty():
|
1754
|
+
# # create representation of the subtree
|
1755
|
+
nodes_st, matrix_st, edges_st = resolve(subtree)
|
1756
|
+
# # add its nodes to the "global" nodes set
|
1757
|
+
nodes.extend(nodes_st)
|
1758
|
+
# # create a new edge between the root and the subtree
|
1759
|
+
edge.append(nodes_st[0][0])
|
1760
|
+
# # add the subtree edges to the "global" edges set
|
1761
|
+
edges.extend(edges_st)
|
1762
|
+
# # build a new matrix by concatenation
|
1763
|
+
concat_matrix(matrix, matrix_st)
|
1764
|
+
else:
|
1765
|
+
concat_matrix(matrix, [space])
|
1766
|
+
|
1767
|
+
def pair_nodes_tree(self, nodes, edges, matrix):
|
1768
|
+
r"""
|
1769
|
+
TESTS::
|
1770
|
+
|
1771
|
+
sage: t = OrderedTree([[[],[]],[[],[]]]).\
|
1772
|
+
....: canonical_labelling(); print(latex(t))
|
1773
|
+
{ \newcommand{\nodea}{\node[draw,circle] (a) {$1$}
|
1774
|
+
;}\newcommand{\nodeb}{\node[draw,circle] (b) {$2$}
|
1775
|
+
;}\newcommand{\nodec}{\node[draw,circle] (c) {$3$}
|
1776
|
+
;}\newcommand{\noded}{\node[draw,circle] (d) {$4$}
|
1777
|
+
;}\newcommand{\nodee}{\node[draw,circle] (e) {$5$}
|
1778
|
+
;}\newcommand{\nodef}{\node[draw,circle] (f) {$6$}
|
1779
|
+
;}\newcommand{\nodeg}{\node[draw,circle] (g) {$7$}
|
1780
|
+
;}\begin{tikzpicture}[auto]
|
1781
|
+
\matrix[column sep=.3cm, row sep=.3cm,ampersand replacement=\&]{
|
1782
|
+
\& \& \& \nodea \& \& \& \\
|
1783
|
+
\& \nodeb \& \& \& \& \nodee \& \\
|
1784
|
+
\nodec \& \& \noded \& \& \nodef \& \& \nodeg \\
|
1785
|
+
};
|
1786
|
+
<BLANKLINE>
|
1787
|
+
\path[ultra thick, red] (b) edge (c) edge (d)
|
1788
|
+
(e) edge (f) edge (g)
|
1789
|
+
(a) edge (b) edge (e);
|
1790
|
+
\end{tikzpicture}}
|
1791
|
+
sage: t = BinaryTree([[],[[],[]]]); print(latex(t))
|
1792
|
+
{ \newcommand{\nodea}{\node[draw,circle] (a) {$$}
|
1793
|
+
;}\newcommand{\nodeb}{\node[draw,circle] (b) {$$}
|
1794
|
+
;}\newcommand{\nodec}{\node[draw,circle] (c) {$$}
|
1795
|
+
;}\newcommand{\noded}{\node[draw,circle] (d) {$$}
|
1796
|
+
;}\newcommand{\nodee}{\node[draw,circle] (e) {$$}
|
1797
|
+
;}\begin{tikzpicture}[auto]
|
1798
|
+
\matrix[column sep=.3cm, row sep=.3cm,ampersand replacement=\&]{
|
1799
|
+
\& \nodea \& \& \& \\
|
1800
|
+
\nodeb \& \& \& \nodec \& \\
|
1801
|
+
\& \& \noded \& \& \nodee \\
|
1802
|
+
};
|
1803
|
+
<BLANKLINE>
|
1804
|
+
\path[ultra thick, red] (c) edge (d) edge (e)
|
1805
|
+
(a) edge (b) edge (c);
|
1806
|
+
\end{tikzpicture}}
|
1807
|
+
"""
|
1808
|
+
# build all subtree matrices.
|
1809
|
+
node, name = create_node(self)
|
1810
|
+
edge = [name]
|
1811
|
+
split = len(self) // 2
|
1812
|
+
# the left part
|
1813
|
+
for i in range(split):
|
1814
|
+
tmp(self[i], edge, nodes, edges, matrix)
|
1815
|
+
# # prepare the root line
|
1816
|
+
nb_of_and = matrix[0].count(sep)
|
1817
|
+
# the middle
|
1818
|
+
for i in range(len(matrix)):
|
1819
|
+
matrix[i] += sepspace
|
1820
|
+
# the right part
|
1821
|
+
for i in range(split, len(self)):
|
1822
|
+
tmp(self[i], edge, nodes, edges, matrix)
|
1823
|
+
|
1824
|
+
# # create the root line
|
1825
|
+
root_line = (spacesep * (nb_of_and + 1) + node_to_str(node) +
|
1826
|
+
sepspace * (matrix[0].count(sep) - nb_of_and - 1))
|
1827
|
+
matrix.insert(0, root_line)
|
1828
|
+
# add edges from the root
|
1829
|
+
edges.append(edge)
|
1830
|
+
|
1831
|
+
def odd_nodes_tree(self, nodes, edges, matrix):
|
1832
|
+
r"""
|
1833
|
+
TESTS::
|
1834
|
+
|
1835
|
+
sage: t = OrderedTree([[]]).canonical_labelling()
|
1836
|
+
sage: print(latex(t))
|
1837
|
+
{ \newcommand{\nodea}{\node[draw,circle] (a) {$1$}
|
1838
|
+
;}\newcommand{\nodeb}{\node[draw,circle] (b) {$2$}
|
1839
|
+
;}\begin{tikzpicture}[auto]
|
1840
|
+
\matrix[column sep=.3cm, row sep=.3cm,ampersand replacement=\&]{
|
1841
|
+
\nodea \\
|
1842
|
+
\nodeb \\
|
1843
|
+
};
|
1844
|
+
<BLANKLINE>
|
1845
|
+
\path[ultra thick, red] (a) edge (b);
|
1846
|
+
\end{tikzpicture}}
|
1847
|
+
sage: t = OrderedTree([[[],[]]]).canonical_labelling(); print(latex(t))
|
1848
|
+
{ \newcommand{\nodea}{\node[draw,circle] (a) {$1$}
|
1849
|
+
;}\newcommand{\nodeb}{\node[draw,circle] (b) {$2$}
|
1850
|
+
;}\newcommand{\nodec}{\node[draw,circle] (c) {$3$}
|
1851
|
+
;}\newcommand{\noded}{\node[draw,circle] (d) {$4$}
|
1852
|
+
;}\begin{tikzpicture}[auto]
|
1853
|
+
\matrix[column sep=.3cm, row sep=.3cm,ampersand replacement=\&]{
|
1854
|
+
\& \nodea \& \\
|
1855
|
+
\& \nodeb \& \\
|
1856
|
+
\nodec \& \& \noded \\
|
1857
|
+
};
|
1858
|
+
<BLANKLINE>
|
1859
|
+
\path[ultra thick, red] (b) edge (c) edge (d)
|
1860
|
+
(a) edge (b);
|
1861
|
+
\end{tikzpicture}}
|
1862
|
+
sage: t = OrderedTree([[[],[],[]]]).canonical_labelling(); print(latex(t))
|
1863
|
+
{ \newcommand{\nodea}{\node[draw,circle] (a) {$1$}
|
1864
|
+
;}\newcommand{\nodeb}{\node[draw,circle] (b) {$2$}
|
1865
|
+
;}\newcommand{\nodec}{\node[draw,circle] (c) {$3$}
|
1866
|
+
;}\newcommand{\noded}{\node[draw,circle] (d) {$4$}
|
1867
|
+
;}\newcommand{\nodee}{\node[draw,circle] (e) {$5$}
|
1868
|
+
;}\begin{tikzpicture}[auto]
|
1869
|
+
\matrix[column sep=.3cm, row sep=.3cm,ampersand replacement=\&]{
|
1870
|
+
\& \nodea \& \\
|
1871
|
+
\& \nodeb \& \\
|
1872
|
+
\nodec \& \noded \& \nodee \\
|
1873
|
+
};
|
1874
|
+
<BLANKLINE>
|
1875
|
+
\path[ultra thick, red] (b) edge (c) edge (d) edge (e)
|
1876
|
+
(a) edge (b);
|
1877
|
+
\end{tikzpicture}}
|
1878
|
+
sage: t = OrderedTree([[[],[],[]],[],[]]).canonical_labelling(); print(latex(t))
|
1879
|
+
{ \newcommand{\nodea}{\node[draw,circle] (a) {$1$}
|
1880
|
+
;}\newcommand{\nodeb}{\node[draw,circle] (b) {$2$}
|
1881
|
+
;}\newcommand{\nodec}{\node[draw,circle] (c) {$3$}
|
1882
|
+
;}\newcommand{\noded}{\node[draw,circle] (d) {$4$}
|
1883
|
+
;}\newcommand{\nodee}{\node[draw,circle] (e) {$5$}
|
1884
|
+
;}\newcommand{\nodef}{\node[draw,circle] (f) {$6$}
|
1885
|
+
;}\newcommand{\nodeg}{\node[draw,circle] (g) {$7$}
|
1886
|
+
;}\begin{tikzpicture}[auto]
|
1887
|
+
\matrix[column sep=.3cm, row sep=.3cm,ampersand replacement=\&]{
|
1888
|
+
\& \& \& \nodea \& \\
|
1889
|
+
\& \nodeb \& \& \nodef \& \nodeg \\
|
1890
|
+
\nodec \& \noded \& \nodee \& \& \\
|
1891
|
+
};
|
1892
|
+
<BLANKLINE>
|
1893
|
+
\path[ultra thick, red] (b) edge (c) edge (d) edge (e)
|
1894
|
+
(a) edge (b) edge (f) edge (g);
|
1895
|
+
\end{tikzpicture}}
|
1896
|
+
"""
|
1897
|
+
# build all subtree matrices.
|
1898
|
+
node, name = create_node(self)
|
1899
|
+
edge = [name]
|
1900
|
+
split = len(self) // 2
|
1901
|
+
# the left part
|
1902
|
+
for i in range(split):
|
1903
|
+
tmp(self[i], edge, nodes, edges, matrix)
|
1904
|
+
# # prepare the root line
|
1905
|
+
if matrix:
|
1906
|
+
nb_of_and = matrix[0].count(sep)
|
1907
|
+
sizetmp = len(matrix[0])
|
1908
|
+
else:
|
1909
|
+
nb_of_and = 0
|
1910
|
+
sizetmp = 0
|
1911
|
+
# the middle
|
1912
|
+
tmp(self[split], edge, nodes, edges, matrix)
|
1913
|
+
nb_of_and += matrix[0][sizetmp:].split("node")[0].count(sep)
|
1914
|
+
|
1915
|
+
# the right part
|
1916
|
+
for i in range(split + 1, len(self)):
|
1917
|
+
tmp(self[i], edge, nodes, edges, matrix)
|
1918
|
+
|
1919
|
+
# # create the root line
|
1920
|
+
root_line = (spacesep * (nb_of_and) + node_to_str(node) +
|
1921
|
+
sepspace * (matrix[0].count(sep) - nb_of_and))
|
1922
|
+
matrix.insert(0, root_line)
|
1923
|
+
# add edges from the root
|
1924
|
+
edges.append(edge)
|
1925
|
+
if self.is_empty():
|
1926
|
+
empty_tree()
|
1927
|
+
elif len(self) == 0 or all(subtree.is_empty()
|
1928
|
+
for subtree in self):
|
1929
|
+
one_node_tree(self)
|
1930
|
+
elif not len(self) % 2:
|
1931
|
+
pair_nodes_tree(self, nodes, edges, matrix)
|
1932
|
+
else:
|
1933
|
+
odd_nodes_tree(self, nodes, edges, matrix)
|
1934
|
+
return nodes, matrix, edges
|
1935
|
+
|
1936
|
+
nodes, matrix, edges = resolve(self)
|
1937
|
+
|
1938
|
+
def make_cmd(nodes):
|
1939
|
+
cmds = []
|
1940
|
+
for name, label in nodes:
|
1941
|
+
cmds.append(new_cmd1 + name + new_cmd2 +
|
1942
|
+
name + new_cmd3 +
|
1943
|
+
label + new_cmd4)
|
1944
|
+
return cmds
|
1945
|
+
|
1946
|
+
def make_edges(edges):
|
1947
|
+
all_paths = []
|
1948
|
+
for edge in edges:
|
1949
|
+
path = "(" + edge[0] + ")"
|
1950
|
+
for i in range(1, len(edge)):
|
1951
|
+
path += " edge (%s)" % edge[i]
|
1952
|
+
all_paths.append(path)
|
1953
|
+
return all_paths
|
1954
|
+
return ("{ " +
|
1955
|
+
"".join(make_cmd(nodes)) +
|
1956
|
+
begin_env +
|
1957
|
+
(matrix_begin +
|
1958
|
+
"\\\\ \n".join(matrix) +
|
1959
|
+
matrix_end +
|
1960
|
+
("\n" +
|
1961
|
+
path_begin +
|
1962
|
+
"\n\t".join(make_edges(edges)) +
|
1963
|
+
path_end if edges else "")
|
1964
|
+
if matrix else "") +
|
1965
|
+
end_env +
|
1966
|
+
"}")
|
1967
|
+
|
1968
|
+
|
1969
|
+
class AbstractClonableTree(AbstractTree):
|
1970
|
+
"""
|
1971
|
+
Abstract Clonable Tree.
|
1972
|
+
|
1973
|
+
An abstract class for trees with clone protocol (see
|
1974
|
+
:mod:`~sage.structure.list_clone`). It is expected that classes extending
|
1975
|
+
this one may also inherit from classes like :class:`~sage.structure.list_clone.ClonableArray` or
|
1976
|
+
:class:`~sage.structure.list_clone.ClonableList` depending whether one
|
1977
|
+
wants to build trees where adding a child is allowed.
|
1978
|
+
|
1979
|
+
.. NOTE:: Due to the limitation of Cython inheritance, one cannot inherit
|
1980
|
+
here from :class:`~sage.structure.list_clone.ClonableElement`, because
|
1981
|
+
it would prevent us from later inheriting from
|
1982
|
+
:class:`~sage.structure.list_clone.ClonableArray` or
|
1983
|
+
:class:`~sage.structure.list_clone.ClonableList`.
|
1984
|
+
|
1985
|
+
.. rubric:: How should this class be extended ?
|
1986
|
+
|
1987
|
+
A class extending :class:`AbstractClonableTree
|
1988
|
+
<sage.combinat.abstract_tree.AbstractClonableTree>` should satisfy the
|
1989
|
+
following assumptions:
|
1990
|
+
|
1991
|
+
* An instantiable class extending :class:`AbstractClonableTree
|
1992
|
+
<sage.combinat.abstract_tree.AbstractClonableTree>` should also extend
|
1993
|
+
the :class:`ClonableElement <sage.structure.list_clone.ClonableElement>`
|
1994
|
+
class or one of its subclasses generally, at least :class:`ClonableArray
|
1995
|
+
<sage.structure.list_clone.ClonableArray>`.
|
1996
|
+
|
1997
|
+
* To respect the Clone protocol, the :meth:`AbstractClonableTree.check`
|
1998
|
+
method should be overridden by the new class.
|
1999
|
+
|
2000
|
+
See also the assumptions in :class:`AbstractTree`.
|
2001
|
+
"""
|
2002
|
+
|
2003
|
+
def check(self):
|
2004
|
+
"""
|
2005
|
+
Check that ``self`` is a correct tree.
|
2006
|
+
|
2007
|
+
This method does nothing. It is implemented here because many
|
2008
|
+
extensions of :class:`AbstractClonableTree
|
2009
|
+
<sage.combinat.abstract_tree.AbstractClonableTree>` also extend
|
2010
|
+
:class:`sage.structure.list_clone.ClonableElement`, which requires it.
|
2011
|
+
|
2012
|
+
It should be overridden in subclasses in order to check that the
|
2013
|
+
characterizing property of the respective kind of tree holds (eg: two
|
2014
|
+
children for binary trees).
|
2015
|
+
|
2016
|
+
EXAMPLES::
|
2017
|
+
|
2018
|
+
sage: OrderedTree([[],[[]]]).check()
|
2019
|
+
sage: BinaryTree([[],[[],[]]]).check()
|
2020
|
+
"""
|
2021
|
+
pass
|
2022
|
+
|
2023
|
+
def __setitem__(self, idx, value):
|
2024
|
+
"""
|
2025
|
+
Substitute a subtree.
|
2026
|
+
|
2027
|
+
.. NOTE::
|
2028
|
+
|
2029
|
+
The tree ``self`` must be in a mutable state. See
|
2030
|
+
:mod:`sage.structure.list_clone` for more details about
|
2031
|
+
mutability. The default implementation here assume that the
|
2032
|
+
container of the node implement a method ``_setitem`` with signature
|
2033
|
+
`self._setitem(idx, value)`. It is usually provided by inheriting
|
2034
|
+
from :class:`~sage.structure.list_clone.ClonableArray`.
|
2035
|
+
|
2036
|
+
INPUT:
|
2037
|
+
|
2038
|
+
- ``idx`` -- a valid path in ``self`` identifying a node
|
2039
|
+
|
2040
|
+
- ``value`` -- the tree to be substituted
|
2041
|
+
|
2042
|
+
EXAMPLES:
|
2043
|
+
|
2044
|
+
Trying to modify a non mutable tree raises an error::
|
2045
|
+
|
2046
|
+
sage: x = OrderedTree([])
|
2047
|
+
sage: x[0] = OrderedTree([[]])
|
2048
|
+
Traceback (most recent call last):
|
2049
|
+
...
|
2050
|
+
ValueError: object is immutable; please change a copy instead.
|
2051
|
+
|
2052
|
+
Here is the correct way to do it::
|
2053
|
+
|
2054
|
+
sage: x = OrderedTree([[],[[]]])
|
2055
|
+
sage: with x.clone() as x:
|
2056
|
+
....: x[0] = OrderedTree([[]])
|
2057
|
+
sage: x
|
2058
|
+
[[[]], [[]]]
|
2059
|
+
|
2060
|
+
One can also substitute at any depth::
|
2061
|
+
|
2062
|
+
sage: y = OrderedTree(x)
|
2063
|
+
sage: with x.clone() as x:
|
2064
|
+
....: x[0,0] = OrderedTree([[]])
|
2065
|
+
sage: x
|
2066
|
+
[[[[]]], [[]]]
|
2067
|
+
sage: y
|
2068
|
+
[[[]], [[]]]
|
2069
|
+
sage: with y.clone() as y:
|
2070
|
+
....: y[(0,)] = OrderedTree([])
|
2071
|
+
sage: y
|
2072
|
+
[[], [[]]]
|
2073
|
+
|
2074
|
+
This works for binary trees as well::
|
2075
|
+
|
2076
|
+
sage: bt = BinaryTree([[],[[],[]]]); bt
|
2077
|
+
[[., .], [[., .], [., .]]]
|
2078
|
+
sage: with bt.clone() as bt1:
|
2079
|
+
....: bt1[0,0] = BinaryTree([[[], []], None])
|
2080
|
+
sage: bt1
|
2081
|
+
[[[[[., .], [., .]], .], .], [[., .], [., .]]]
|
2082
|
+
|
2083
|
+
TESTS::
|
2084
|
+
|
2085
|
+
sage: x = OrderedTree([])
|
2086
|
+
sage: with x.clone() as x:
|
2087
|
+
....: x[0] = OrderedTree([[]])
|
2088
|
+
Traceback (most recent call last):
|
2089
|
+
...
|
2090
|
+
IndexError: list assignment index out of range
|
2091
|
+
|
2092
|
+
sage: x = OrderedTree([]); x = OrderedTree([x,x]); x = OrderedTree([x,x]); x = OrderedTree([x,x])
|
2093
|
+
sage: with x.clone() as x:
|
2094
|
+
....: x[0,0] = OrderedTree()
|
2095
|
+
sage: x
|
2096
|
+
[[[], [[], []]], [[[], []], [[], []]]]
|
2097
|
+
"""
|
2098
|
+
if not isinstance(value, self.__class__):
|
2099
|
+
raise TypeError('the given value is not a tree')
|
2100
|
+
if isinstance(idx, tuple):
|
2101
|
+
self.__setitem_rec__(idx, 0, value)
|
2102
|
+
else:
|
2103
|
+
self._setitem(idx, value)
|
2104
|
+
|
2105
|
+
def __setitem_rec__(self, idx, i, value):
|
2106
|
+
"""
|
2107
|
+
TESTS::
|
2108
|
+
|
2109
|
+
sage: x = OrderedTree([[[], []],[[]]])
|
2110
|
+
sage: with x.clone() as x: # indirect doctest
|
2111
|
+
....: x[0,1] = OrderedTree([[[]]])
|
2112
|
+
sage: x
|
2113
|
+
[[[], [[[]]]], [[]]]
|
2114
|
+
"""
|
2115
|
+
if i == len(idx) - 1:
|
2116
|
+
self._setitem(idx[-1], value)
|
2117
|
+
else:
|
2118
|
+
with self[idx[i]].clone() as child:
|
2119
|
+
child.__setitem_rec__(idx, i + 1, value)
|
2120
|
+
self[idx[i]] = child
|
2121
|
+
|
2122
|
+
def __getitem__(self, idx):
|
2123
|
+
"""
|
2124
|
+
Return the ``idx``-th child of ``self`` (which is a subtree) if
|
2125
|
+
``idx`` is an integer, or the ``idx[n-1]``-th child of the
|
2126
|
+
``idx[n-2]``-th child of the ... of the ``idx[0]``-th child of
|
2127
|
+
``self`` if ``idx`` is a list (or iterable) of length `n`.
|
2128
|
+
|
2129
|
+
The indexing of the children is zero-based.
|
2130
|
+
|
2131
|
+
INPUT:
|
2132
|
+
|
2133
|
+
- ``idx`` -- integer; or a valid path in ``self`` identifying a node
|
2134
|
+
|
2135
|
+
.. NOTE::
|
2136
|
+
|
2137
|
+
The default implementation here assumes that the container of the
|
2138
|
+
node inherits from
|
2139
|
+
:class:`~sage.structure.list_clone.ClonableArray`.
|
2140
|
+
|
2141
|
+
EXAMPLES::
|
2142
|
+
|
2143
|
+
sage: x = OrderedTree([[],[[]]])
|
2144
|
+
sage: x[1,0]
|
2145
|
+
[]
|
2146
|
+
sage: x = OrderedTree([[],[[]]])
|
2147
|
+
sage: x[()]
|
2148
|
+
[[], [[]]]
|
2149
|
+
sage: x[(0,)]
|
2150
|
+
[]
|
2151
|
+
sage: x[0,0]
|
2152
|
+
Traceback (most recent call last):
|
2153
|
+
...
|
2154
|
+
IndexError: list index out of range
|
2155
|
+
|
2156
|
+
sage: u = BinaryTree(None)
|
2157
|
+
sage: v = BinaryTree([u, u])
|
2158
|
+
sage: w = BinaryTree([u, v])
|
2159
|
+
sage: t = BinaryTree([v, w])
|
2160
|
+
sage: z = BinaryTree([w, t])
|
2161
|
+
sage: z[0,1]
|
2162
|
+
[., .]
|
2163
|
+
sage: z[0,0]
|
2164
|
+
.
|
2165
|
+
sage: z[1]
|
2166
|
+
[[., .], [., [., .]]]
|
2167
|
+
sage: z[1,1]
|
2168
|
+
[., [., .]]
|
2169
|
+
sage: z[1][1,1]
|
2170
|
+
[., .]
|
2171
|
+
"""
|
2172
|
+
if isinstance(idx, slice):
|
2173
|
+
return ClonableArray.__getitem__(self, idx)
|
2174
|
+
try:
|
2175
|
+
i = int(idx)
|
2176
|
+
except TypeError:
|
2177
|
+
res = self
|
2178
|
+
# idx is supposed to be an iterable of ints
|
2179
|
+
for i in idx:
|
2180
|
+
res = ClonableArray._getitem(res, i)
|
2181
|
+
return res
|
2182
|
+
else:
|
2183
|
+
return ClonableArray._getitem(self, i)
|
2184
|
+
|
2185
|
+
|
2186
|
+
class AbstractLabelledTree(AbstractTree):
|
2187
|
+
"""
|
2188
|
+
Abstract Labelled Tree.
|
2189
|
+
|
2190
|
+
Typically a class for labelled trees is constructed by inheriting from
|
2191
|
+
a class for unlabelled trees and :class:`AbstractLabelledTree`.
|
2192
|
+
|
2193
|
+
.. rubric:: How should this class be extended ?
|
2194
|
+
|
2195
|
+
A class extending :class:`AbstractLabelledTree
|
2196
|
+
<sage.combinat.abstract_tree.AbstractLabelledTree>` should respect the
|
2197
|
+
following assumptions:
|
2198
|
+
|
2199
|
+
* For a labelled tree ``T`` the call ``T.parent().unlabelled_trees()``
|
2200
|
+
should return a parent for unlabelled trees of the same kind: for
|
2201
|
+
example,
|
2202
|
+
|
2203
|
+
- if ``T`` is a binary labelled tree, ``T.parent()`` is
|
2204
|
+
``LabelledBinaryTrees()`` and ``T.parent().unlabelled_trees()`` is
|
2205
|
+
``BinaryTrees()``
|
2206
|
+
|
2207
|
+
- if ``T`` is an ordered labelled tree, ``T.parent()`` is
|
2208
|
+
``LabelledOrderedTrees()`` and ``T.parent().unlabelled_trees()`` is
|
2209
|
+
``OrderedTrees()``
|
2210
|
+
|
2211
|
+
* In the same vein, the class of ``T`` should contain an attribute
|
2212
|
+
``_UnLabelled`` which should be the class for the corresponding
|
2213
|
+
unlabelled trees.
|
2214
|
+
|
2215
|
+
See also the assumptions in :class:`AbstractTree`.
|
2216
|
+
|
2217
|
+
.. SEEALSO:: :class:`AbstractTree`
|
2218
|
+
"""
|
2219
|
+
|
2220
|
+
def __init__(self, parent, children, label=None, check=True):
|
2221
|
+
"""
|
2222
|
+
TESTS::
|
2223
|
+
|
2224
|
+
sage: LabelledOrderedTree([])
|
2225
|
+
None[]
|
2226
|
+
sage: LabelledOrderedTree([], 3)
|
2227
|
+
3[]
|
2228
|
+
sage: LT = LabelledOrderedTree
|
2229
|
+
sage: t = LT([LT([LT([], label=42), LT([], 21)])], label=1)
|
2230
|
+
sage: t
|
2231
|
+
1[None[42[], 21[]]]
|
2232
|
+
sage: LabelledOrderedTree(OrderedTree([[],[[],[]],[]]))
|
2233
|
+
None[None[], None[None[], None[]], None[]]
|
2234
|
+
|
2235
|
+
We test that inheriting from `LabelledOrderedTree` allows construction from a
|
2236
|
+
`LabelledOrderedTree` (:issue:`16314`)::
|
2237
|
+
|
2238
|
+
sage: LBTS = LabelledOrderedTrees()
|
2239
|
+
sage: class Foo(LabelledOrderedTree):
|
2240
|
+
....: def bar(self):
|
2241
|
+
....: print("bar called")
|
2242
|
+
sage: foo = Foo(LBTS, [], label=1); foo
|
2243
|
+
1[]
|
2244
|
+
sage: foo1 = LBTS([LBTS([], label=21)], label=42); foo1
|
2245
|
+
42[21[]]
|
2246
|
+
sage: foo2 = Foo(LBTS, foo1); foo2
|
2247
|
+
42[21[]]
|
2248
|
+
sage: foo2[0]
|
2249
|
+
21[]
|
2250
|
+
sage: foo2.__class__
|
2251
|
+
<class '__main__.Foo'>
|
2252
|
+
sage: foo2[0].__class__
|
2253
|
+
<class '__main__.Foo'>
|
2254
|
+
sage: foo2.bar()
|
2255
|
+
bar called
|
2256
|
+
sage: foo2.label()
|
2257
|
+
42
|
2258
|
+
"""
|
2259
|
+
# We must initialize the label before the subtrees to allows rooted
|
2260
|
+
# trees canonization. Indeed it needs that ``self``._hash_() is working
|
2261
|
+
# at the end of the call super().__init__(...)
|
2262
|
+
if isinstance(children, AbstractLabelledTree):
|
2263
|
+
if label is None:
|
2264
|
+
self._label = children._label
|
2265
|
+
else:
|
2266
|
+
self._label = label
|
2267
|
+
else:
|
2268
|
+
self._label = label
|
2269
|
+
super().__init__(parent, children, check=check)
|
2270
|
+
|
2271
|
+
def _repr_(self):
|
2272
|
+
"""
|
2273
|
+
Return the string representation of ``self``.
|
2274
|
+
|
2275
|
+
TESTS::
|
2276
|
+
|
2277
|
+
sage: LabelledOrderedTree([]) # indirect doctest
|
2278
|
+
None[]
|
2279
|
+
sage: LabelledOrderedTree([], label=3) # indirect doctest
|
2280
|
+
3[]
|
2281
|
+
sage: LabelledOrderedTree([[],[[]]]) # indirect doctest
|
2282
|
+
None[None[], None[None[]]]
|
2283
|
+
sage: LabelledOrderedTree([[],LabelledOrderedTree([[]], label=2)], label=3)
|
2284
|
+
3[None[], 2[None[]]]
|
2285
|
+
"""
|
2286
|
+
return "%s%s" % (self._label, self[:])
|
2287
|
+
|
2288
|
+
def label(self, path=None):
|
2289
|
+
"""
|
2290
|
+
Return the label of ``self``.
|
2291
|
+
|
2292
|
+
INPUT:
|
2293
|
+
|
2294
|
+
- ``path`` -- ``None`` (default) or a path (list or tuple of
|
2295
|
+
children index in the tree)
|
2296
|
+
|
2297
|
+
OUTPUT: the label of the subtree indexed by ``path``
|
2298
|
+
|
2299
|
+
EXAMPLES::
|
2300
|
+
|
2301
|
+
sage: t = LabelledOrderedTree([[],[]], label = 3)
|
2302
|
+
sage: t.label()
|
2303
|
+
3
|
2304
|
+
sage: t[0].label()
|
2305
|
+
sage: t = LabelledOrderedTree([LabelledOrderedTree([], 5),[]], label = 3)
|
2306
|
+
sage: t.label()
|
2307
|
+
3
|
2308
|
+
sage: t[0].label()
|
2309
|
+
5
|
2310
|
+
sage: t[1].label()
|
2311
|
+
sage: t.label([0])
|
2312
|
+
5
|
2313
|
+
"""
|
2314
|
+
if path is None:
|
2315
|
+
return self._label
|
2316
|
+
else:
|
2317
|
+
tr = self
|
2318
|
+
for i in path:
|
2319
|
+
tr = tr[i]
|
2320
|
+
return tr._label
|
2321
|
+
|
2322
|
+
def labels(self):
|
2323
|
+
"""
|
2324
|
+
Return the list of labels of ``self``.
|
2325
|
+
|
2326
|
+
EXAMPLES::
|
2327
|
+
|
2328
|
+
sage: LT = LabelledOrderedTree
|
2329
|
+
sage: t = LT([LT([],label='b'),LT([],label='c')],label='a')
|
2330
|
+
sage: t.labels()
|
2331
|
+
['a', 'b', 'c']
|
2332
|
+
|
2333
|
+
sage: LBT = LabelledBinaryTree
|
2334
|
+
sage: LBT([LBT([],label=1),LBT([],label=4)],label=2).labels()
|
2335
|
+
[2, 1, 4]
|
2336
|
+
"""
|
2337
|
+
return [t.label() for t in self.subtrees()]
|
2338
|
+
|
2339
|
+
def leaf_labels(self):
|
2340
|
+
"""
|
2341
|
+
Return the list of labels of the leaves of ``self``.
|
2342
|
+
|
2343
|
+
In case of a labelled binary tree, these "leaves" are not actually
|
2344
|
+
the leaves of the binary trees, but the nodes whose both children
|
2345
|
+
are leaves!
|
2346
|
+
|
2347
|
+
EXAMPLES::
|
2348
|
+
|
2349
|
+
sage: LT = LabelledOrderedTree
|
2350
|
+
sage: t = LT([LT([],label='b'),LT([],label='c')],label='a')
|
2351
|
+
sage: t.leaf_labels()
|
2352
|
+
['b', 'c']
|
2353
|
+
|
2354
|
+
sage: LBT = LabelledBinaryTree
|
2355
|
+
sage: bt = LBT([LBT([],label='b'),LBT([],label='c')],label='a')
|
2356
|
+
sage: bt.leaf_labels()
|
2357
|
+
['b', 'c']
|
2358
|
+
sage: LBT([], label='1').leaf_labels()
|
2359
|
+
['1']
|
2360
|
+
sage: LBT(None).leaf_labels()
|
2361
|
+
[]
|
2362
|
+
"""
|
2363
|
+
return [t.label() for t in self.subtrees() if t.node_number() == 1]
|
2364
|
+
|
2365
|
+
def __eq__(self, other):
|
2366
|
+
"""
|
2367
|
+
Test if ``self`` is equal to ``other``.
|
2368
|
+
|
2369
|
+
TESTS::
|
2370
|
+
|
2371
|
+
sage LabelledOrderedTree() == LabelledOrderedTree()
|
2372
|
+
True
|
2373
|
+
sage LabelledOrderedTree([]) == LabelledOrderedTree()
|
2374
|
+
False
|
2375
|
+
sage: t1 = LabelledOrderedTree([[],[[]]])
|
2376
|
+
sage: t2 = LabelledOrderedTree([[],[[]]])
|
2377
|
+
sage: t1 == t2
|
2378
|
+
True
|
2379
|
+
sage: t2 = LabelledOrderedTree(t1)
|
2380
|
+
sage: t1 == t2
|
2381
|
+
True
|
2382
|
+
sage: t1 = LabelledOrderedTree([[],[[]]])
|
2383
|
+
sage: t2 = LabelledOrderedTree([[[]],[]])
|
2384
|
+
sage: t1 == t2
|
2385
|
+
False
|
2386
|
+
"""
|
2387
|
+
return super().__eq__(other) and self._label == other._label
|
2388
|
+
|
2389
|
+
def _hash_(self):
|
2390
|
+
"""
|
2391
|
+
Return the hash value for ``self``.
|
2392
|
+
|
2393
|
+
TESTS::
|
2394
|
+
|
2395
|
+
sage: t1 = LabelledOrderedTree([[],[[]]], label = 1); t1hash = t1.__hash__()
|
2396
|
+
sage: LabelledOrderedTree([[],[[]]], label = 1).__hash__() == t1hash
|
2397
|
+
True
|
2398
|
+
sage: LabelledOrderedTree([[[]],[]], label = 1).__hash__() == t1hash
|
2399
|
+
False
|
2400
|
+
sage: LabelledOrderedTree(t1, label = 1).__hash__() == t1hash
|
2401
|
+
True
|
2402
|
+
sage: LabelledOrderedTree([[],[[]]], label = 25).__hash__() == t1hash
|
2403
|
+
False
|
2404
|
+
sage: LabelledOrderedTree(t1, label = 25).__hash__() == t1hash
|
2405
|
+
False
|
2406
|
+
|
2407
|
+
sage: LabelledBinaryTree([[],[[],[]]], label = 25).__hash__() #random
|
2408
|
+
8544617749928727644
|
2409
|
+
|
2410
|
+
We check that the hash value depends on the value of the labels of the
|
2411
|
+
subtrees::
|
2412
|
+
|
2413
|
+
sage: LBT = LabelledBinaryTree
|
2414
|
+
sage: t1 = LBT([], label = 1)
|
2415
|
+
sage: t2 = LBT([], label = 2)
|
2416
|
+
sage: t3 = LBT([], label = 3)
|
2417
|
+
sage: t12 = LBT([t1, t2], label = "a")
|
2418
|
+
sage: t13 = LBT([t1, t3], label = "a")
|
2419
|
+
sage: t12.__hash__() != t13.__hash__()
|
2420
|
+
True
|
2421
|
+
"""
|
2422
|
+
return self._UnLabelled._hash_(self) ^ hash(self._label)
|
2423
|
+
|
2424
|
+
def shape(self):
|
2425
|
+
"""
|
2426
|
+
Return the unlabelled tree associated to ``self``.
|
2427
|
+
|
2428
|
+
EXAMPLES::
|
2429
|
+
|
2430
|
+
sage: t = LabelledOrderedTree([[],[[]]], label = 25).shape(); t
|
2431
|
+
[[], [[]]]
|
2432
|
+
|
2433
|
+
sage: LabelledBinaryTree([[],[[],[]]], label = 25).shape()
|
2434
|
+
[[., .], [[., .], [., .]]]
|
2435
|
+
|
2436
|
+
sage: LRT = LabelledRootedTree
|
2437
|
+
sage: tb = LRT([],label='b')
|
2438
|
+
sage: LRT([tb, tb], label='a').shape()
|
2439
|
+
[[], []]
|
2440
|
+
|
2441
|
+
TESTS::
|
2442
|
+
|
2443
|
+
sage: t.parent()
|
2444
|
+
Ordered trees
|
2445
|
+
sage: type(t)
|
2446
|
+
<class 'sage.combinat.ordered_tree.OrderedTrees_all_with_category.element_class'>
|
2447
|
+
"""
|
2448
|
+
TR = self.parent().unlabelled_trees()
|
2449
|
+
if not self:
|
2450
|
+
return TR.leaf()
|
2451
|
+
else:
|
2452
|
+
return TR._element_constructor_([i.shape() for i in self])
|
2453
|
+
|
2454
|
+
def as_digraph(self):
|
2455
|
+
"""
|
2456
|
+
Return a directed graph version of ``self``.
|
2457
|
+
|
2458
|
+
.. WARNING::
|
2459
|
+
|
2460
|
+
At this time, the output makes sense only if ``self`` is a
|
2461
|
+
labelled binary tree with no repeated labels and no ``None``
|
2462
|
+
labels.
|
2463
|
+
|
2464
|
+
EXAMPLES::
|
2465
|
+
|
2466
|
+
sage: LT = LabelledOrderedTrees()
|
2467
|
+
sage: t1 = LT([LT([],label=6),LT([],label=1)],label=9)
|
2468
|
+
sage: t1.as_digraph()
|
2469
|
+
Digraph on 3 vertices
|
2470
|
+
|
2471
|
+
sage: t = BinaryTree([[None, None],[[],None]])
|
2472
|
+
sage: lt = t.canonical_labelling()
|
2473
|
+
sage: lt.as_digraph()
|
2474
|
+
Digraph on 4 vertices
|
2475
|
+
"""
|
2476
|
+
from sage.graphs.digraph import DiGraph
|
2477
|
+
resu = {self.label():
|
2478
|
+
[t.label() for t in self if not t.is_empty()]}
|
2479
|
+
resu = DiGraph(resu, format='dict_of_lists')
|
2480
|
+
for t in self:
|
2481
|
+
if not t.is_empty():
|
2482
|
+
resu = resu.union(t.as_digraph())
|
2483
|
+
return resu
|
2484
|
+
|
2485
|
+
|
2486
|
+
class AbstractLabelledClonableTree(AbstractLabelledTree,
|
2487
|
+
AbstractClonableTree):
|
2488
|
+
"""
|
2489
|
+
Abstract Labelled Clonable Tree.
|
2490
|
+
|
2491
|
+
This class takes care of modification for the label by the clone protocol.
|
2492
|
+
|
2493
|
+
.. NOTE:: Due to the limitation of Cython inheritance, one cannot inherit
|
2494
|
+
here from :class:`~sage.structure.list_clone.ClonableArray`, because it would prevent us to
|
2495
|
+
inherit later from :class:`~sage.structure.list_clone.ClonableList`.
|
2496
|
+
"""
|
2497
|
+
|
2498
|
+
def set_root_label(self, label):
|
2499
|
+
"""
|
2500
|
+
Set the label of the root of ``self``.
|
2501
|
+
|
2502
|
+
INPUT:
|
2503
|
+
|
2504
|
+
- ``label`` -- any Sage object
|
2505
|
+
|
2506
|
+
OUTPUT: none, ``self`` is modified in place
|
2507
|
+
|
2508
|
+
.. NOTE::
|
2509
|
+
|
2510
|
+
``self`` must be in a mutable state. See
|
2511
|
+
:mod:`sage.structure.list_clone` for more details about
|
2512
|
+
mutability.
|
2513
|
+
|
2514
|
+
EXAMPLES::
|
2515
|
+
|
2516
|
+
sage: t = LabelledOrderedTree([[],[[],[]]])
|
2517
|
+
sage: t.set_root_label(3)
|
2518
|
+
Traceback (most recent call last):
|
2519
|
+
...
|
2520
|
+
ValueError: object is immutable; please change a copy instead.
|
2521
|
+
sage: with t.clone() as t:
|
2522
|
+
....: t.set_root_label(3)
|
2523
|
+
sage: t.label()
|
2524
|
+
3
|
2525
|
+
sage: t
|
2526
|
+
3[None[], None[None[], None[]]]
|
2527
|
+
|
2528
|
+
This also works for binary trees::
|
2529
|
+
|
2530
|
+
sage: bt = LabelledBinaryTree([[],[]])
|
2531
|
+
sage: bt.set_root_label(3)
|
2532
|
+
Traceback (most recent call last):
|
2533
|
+
...
|
2534
|
+
ValueError: object is immutable; please change a copy instead.
|
2535
|
+
sage: with bt.clone() as bt:
|
2536
|
+
....: bt.set_root_label(3)
|
2537
|
+
sage: bt.label()
|
2538
|
+
3
|
2539
|
+
sage: bt
|
2540
|
+
3[None[., .], None[., .]]
|
2541
|
+
|
2542
|
+
TESTS::
|
2543
|
+
|
2544
|
+
sage: with t.clone() as t:
|
2545
|
+
....: t[0] = LabelledOrderedTree(t[0], label = 4)
|
2546
|
+
sage: t
|
2547
|
+
3[4[], None[None[], None[]]]
|
2548
|
+
sage: with t.clone() as t:
|
2549
|
+
....: t[1,0] = LabelledOrderedTree(t[1,0], label = 42)
|
2550
|
+
sage: t
|
2551
|
+
3[4[], None[42[], None[]]]
|
2552
|
+
"""
|
2553
|
+
self._require_mutable()
|
2554
|
+
self._label = label
|
2555
|
+
|
2556
|
+
def set_label(self, path, label):
|
2557
|
+
"""
|
2558
|
+
Change the label of subtree indexed by ``path`` to ``label``.
|
2559
|
+
|
2560
|
+
INPUT:
|
2561
|
+
|
2562
|
+
- ``path`` -- ``None`` (default) or a path (list or tuple of children
|
2563
|
+
index in the tree)
|
2564
|
+
|
2565
|
+
- ``label`` -- any sage object
|
2566
|
+
|
2567
|
+
OUTPUT: nothing, ``self`` is modified in place
|
2568
|
+
|
2569
|
+
.. NOTE::
|
2570
|
+
|
2571
|
+
``self`` must be in a mutable state. See
|
2572
|
+
:mod:`sage.structure.list_clone` for more details about
|
2573
|
+
mutability.
|
2574
|
+
|
2575
|
+
EXAMPLES::
|
2576
|
+
|
2577
|
+
sage: t = LabelledOrderedTree([[],[[],[]]])
|
2578
|
+
sage: t.set_label((0,), 4)
|
2579
|
+
Traceback (most recent call last):
|
2580
|
+
...
|
2581
|
+
ValueError: object is immutable; please change a copy instead.
|
2582
|
+
sage: with t.clone() as t:
|
2583
|
+
....: t.set_label((0,), 4)
|
2584
|
+
sage: t
|
2585
|
+
None[4[], None[None[], None[]]]
|
2586
|
+
sage: with t.clone() as t:
|
2587
|
+
....: t.set_label((1,0), label = 42)
|
2588
|
+
sage: t
|
2589
|
+
None[4[], None[42[], None[]]]
|
2590
|
+
|
2591
|
+
.. TODO::
|
2592
|
+
|
2593
|
+
Do we want to implement the following syntactic sugar::
|
2594
|
+
|
2595
|
+
with t.clone() as tt:
|
2596
|
+
tt.labels[1,2] = 3 ?
|
2597
|
+
"""
|
2598
|
+
self._require_mutable()
|
2599
|
+
path = tuple(path)
|
2600
|
+
if path == ():
|
2601
|
+
self._label = label
|
2602
|
+
else:
|
2603
|
+
with self[path[0]].clone() as child:
|
2604
|
+
child.set_label(path[1:], label)
|
2605
|
+
self[path[0]] = child
|
2606
|
+
|
2607
|
+
def map_labels(self, f):
|
2608
|
+
"""
|
2609
|
+
Apply the function `f` to the labels of ``self``.
|
2610
|
+
|
2611
|
+
This method returns a copy of ``self`` on which the function `f` has
|
2612
|
+
been applied on all labels (a label `x` is replaced by `f(x)`).
|
2613
|
+
|
2614
|
+
EXAMPLES::
|
2615
|
+
|
2616
|
+
sage: LT = LabelledOrderedTree
|
2617
|
+
sage: t = LT([LT([],label=1),LT([],label=7)],label=3); t
|
2618
|
+
3[1[], 7[]]
|
2619
|
+
sage: t.map_labels(lambda z:z+1)
|
2620
|
+
4[2[], 8[]]
|
2621
|
+
|
2622
|
+
sage: LBT = LabelledBinaryTree
|
2623
|
+
sage: bt = LBT([LBT([],label=1),LBT([],label=4)],label=2); bt
|
2624
|
+
2[1[., .], 4[., .]]
|
2625
|
+
sage: bt.map_labels(lambda z:z+1)
|
2626
|
+
3[2[., .], 5[., .]]
|
2627
|
+
"""
|
2628
|
+
if self.is_empty():
|
2629
|
+
return self
|
2630
|
+
return self.parent()([t.map_labels(f) for t in self],
|
2631
|
+
label=f(self.label()))
|
2632
|
+
|
2633
|
+
|
2634
|
+
def from_hexacode(ch, parent=None, label='@'):
|
2635
|
+
r"""
|
2636
|
+
Transform a hexadecimal string into a tree.
|
2637
|
+
|
2638
|
+
INPUT:
|
2639
|
+
|
2640
|
+
- ``ch`` -- a hexadecimal string
|
2641
|
+
|
2642
|
+
- ``parent`` -- kind of trees to be produced. If ``None``, this will
|
2643
|
+
be ``LabelledOrderedTrees``
|
2644
|
+
|
2645
|
+
- ``label`` -- a label (default: ``'@'``) to be used for every vertex
|
2646
|
+
of the tree
|
2647
|
+
|
2648
|
+
See :meth:`AbstractTree.to_hexacode` for the description of the encoding
|
2649
|
+
|
2650
|
+
See :func:`_from_hexacode_aux` for the actual code
|
2651
|
+
|
2652
|
+
EXAMPLES::
|
2653
|
+
|
2654
|
+
sage: from sage.combinat.abstract_tree import from_hexacode
|
2655
|
+
sage: from_hexacode('12000', LabelledOrderedTrees())
|
2656
|
+
@[@[@[], @[]]]
|
2657
|
+
sage: from_hexacode('12000')
|
2658
|
+
@[@[@[], @[]]]
|
2659
|
+
|
2660
|
+
sage: from_hexacode('1200', LabelledOrderedTrees())
|
2661
|
+
@[@[@[], @[]]]
|
2662
|
+
|
2663
|
+
It can happen that only a prefix of the word is used::
|
2664
|
+
|
2665
|
+
sage: from_hexacode('a'+14*'0', LabelledOrderedTrees())
|
2666
|
+
@[@[], @[], @[], @[], @[], @[], @[], @[], @[], @[]]
|
2667
|
+
|
2668
|
+
One can choose the label::
|
2669
|
+
|
2670
|
+
sage: from_hexacode('1200', LabelledOrderedTrees(), label='o')
|
2671
|
+
o[o[o[], o[]]]
|
2672
|
+
|
2673
|
+
One can also create other kinds of trees::
|
2674
|
+
|
2675
|
+
sage: from_hexacode('1200', OrderedTrees())
|
2676
|
+
[[[], []]]
|
2677
|
+
"""
|
2678
|
+
if parent is None:
|
2679
|
+
from sage.combinat.ordered_tree import LabelledOrderedTrees
|
2680
|
+
parent = LabelledOrderedTrees()
|
2681
|
+
return _from_hexacode_aux(ch, parent, label)[0]
|
2682
|
+
|
2683
|
+
|
2684
|
+
def _from_hexacode_aux(ch, parent, label='@'):
|
2685
|
+
r"""
|
2686
|
+
Transform a hexadecimal string into a tree and a remainder string.
|
2687
|
+
|
2688
|
+
INPUT:
|
2689
|
+
|
2690
|
+
- ``ch`` -- a hexadecimal string
|
2691
|
+
|
2692
|
+
- ``parent`` -- kind of trees to be produced
|
2693
|
+
|
2694
|
+
- ``label`` -- a label (default: ``'@'``) to be used for every vertex
|
2695
|
+
of the tree
|
2696
|
+
|
2697
|
+
This method is used in :func:`from_hexacode`
|
2698
|
+
|
2699
|
+
EXAMPLES::
|
2700
|
+
|
2701
|
+
sage: from sage.combinat.abstract_tree import _from_hexacode_aux
|
2702
|
+
sage: _from_hexacode_aux('12000', LabelledOrderedTrees())
|
2703
|
+
(@[@[@[], @[]]], '0')
|
2704
|
+
|
2705
|
+
sage: _from_hexacode_aux('1200', LabelledOrderedTrees())
|
2706
|
+
(@[@[@[], @[]]], '')
|
2707
|
+
|
2708
|
+
sage: _from_hexacode_aux('1200', OrderedTrees())
|
2709
|
+
([[[], []]], '')
|
2710
|
+
|
2711
|
+
sage: _from_hexacode_aux('a00000000000000', LabelledOrderedTrees())
|
2712
|
+
(@[@[], @[], @[], @[], @[], @[], @[], @[], @[], @[]], '0000')
|
2713
|
+
"""
|
2714
|
+
Trees = parent
|
2715
|
+
width = int(ch[0], 16) # hexadecimal input
|
2716
|
+
remainder = ch[1:]
|
2717
|
+
if width == 0:
|
2718
|
+
return (Trees([], label), remainder)
|
2719
|
+
branches = {}
|
2720
|
+
for i in range(width):
|
2721
|
+
tree, remainder = _from_hexacode_aux(remainder, parent, label)
|
2722
|
+
branches[i] = tree
|
2723
|
+
return (Trees(branches.values(), label), remainder)
|