@@ -276,8 +276,7 @@ def action(x):
276
276
while stack :
277
277
node = stack .pop ()
278
278
action (node )
279
- for i in range (len (node )):
280
- subtree = node [- i - 1 ]
279
+ for subtree in reversed (node ):
281
280
if not subtree .is_empty ():
282
281
stack .append (subtree )
283
282
@@ -639,15 +638,122 @@ def action(x):
639
638
# subtrees, and should not be exploded again, but instead
640
639
# should be manipulated and removed from the stack.
641
640
stack .append (None )
642
- for i in range (len (node )):
643
- subtree = node [- i - 1 ]
641
+ for subtree in reversed (node ):
644
642
if not subtree .is_empty ():
645
643
stack .append (subtree )
646
644
else :
647
645
stack .pop ()
648
646
node = stack .pop ()
649
647
action (node )
650
648
649
+ def contour_traversal (self , first_action = None , middle_action = None , final_action = None , leaf_action = None ):
650
+ r"""
651
+ Run the counterclockwise countour traversal algorithm (iterative
652
+ implementation) and subject every node encountered
653
+ to some procedure ``first_action``, ``middle_action`` or ``final_action`` each time it reaches it.
654
+
655
+ ALGORITHM:
656
+
657
+ - if the root is a leaf, apply `leaf_action`
658
+ - else
659
+ - apply `first_action` to the root
660
+ - iteratively apply `middle_action` to the root and traverse each subtree
661
+ from the leftmost one to the rightmost one
662
+ - apply `final_action` to the root
663
+
664
+ INPUT:
665
+
666
+ - ``first_action`` -- (optional) a function which takes a node as
667
+ input, and does something the first time it is reached during exploration
668
+
669
+ - ``middle_action`` -- (optional) a function which takes a node as
670
+ input, and does something each time it explore one of its children
671
+
672
+ - ``final_action`` -- (optional) a function which takes a node as
673
+ input, and does something the last time it is reached during exploration
674
+
675
+ - ``leaf_action`` -- (optional) a function which takes a leaf as
676
+ input, and does something when it is reached during exploration.
677
+
678
+ OUTPUT:
679
+
680
+ ``None``. (This is *not* an iterator.)
681
+
682
+ TESTS::
683
+
684
+ sage: l = []
685
+ sage: t = OrderedTree([[],[[],[],]]).canonical_labelling()
686
+ sage: t
687
+ 1[2[], 3[4[], 5[]]]
688
+ sage: t.contour_traversal(lambda node: (l.append(node.label()),l.append('a')),
689
+ ....: lambda node: (l.append(node.label()),l.append('b')),
690
+ ....: lambda node: (l.append(node.label()),l.append('c')),
691
+ ....: lambda node: (l.append(node.label())))
692
+ sage: l
693
+ [1, 'a', 1, 'b', 2, 1, 'b', 3, 'a', 3, 'b', 4, 3, 'b', 5, 3, 'c', 1, 'c']
694
+
695
+ sage: l = []
696
+ sage: b = BinaryTree([[None,[]],[[[],[]],[]]]).canonical_labelling()
697
+ sage: b
698
+ 3[1[., 2[., .]], 7[5[4[., .], 6[., .]], 8[., .]]]
699
+ sage: b.contour_traversal(lambda node: l.append(node.label()),
700
+ ....: lambda node: l.append(node.label()),
701
+ ....: lambda node: l.append(node.label()),
702
+ ....: None)
703
+ sage: l
704
+ [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]
705
+
706
+ The following test checks that things do not go wrong if some among
707
+ the descendants of the tree are equal or even identical::
708
+
709
+ sage: u = BinaryTree(None)
710
+ sage: v = BinaryTree([u, u])
711
+ sage: w = BinaryTree([v, v])
712
+ sage: t = BinaryTree([w, w])
713
+ sage: t.node_number()
714
+ 7
715
+ sage: l = []
716
+ sage: t.contour_traversal(first_action = lambda node: l.append(0))
717
+ sage: len(l)
718
+ 7
719
+ """
720
+ if first_action is None :
721
+ def first_action (x ):
722
+ return
723
+ if middle_action is None :
724
+ def middle_action (x ):
725
+ return
726
+ if final_action is None :
727
+ def final_action (x ):
728
+ return
729
+ if leaf_action is None :
730
+ def leaf_action (x ):
731
+ return
732
+ stack = []
733
+ stack .append (self )
734
+ corners = [0 , 0 ]
735
+ while stack :
736
+ node = stack .pop ()
737
+ if not node :
738
+ leaf_action (node )
739
+ corners .pop ()
740
+ corners [- 1 ] += 1
741
+ elif not corners [- 1 ]:
742
+ first_action (node )
743
+ middle_action (node )
744
+ stack .append (node )
745
+ stack .append (node [0 ])
746
+ corners .append (0 )
747
+ elif corners [- 1 ] == len (node ):
748
+ final_action (node )
749
+ corners .pop ()
750
+ corners [- 1 ] += 1
751
+ else :
752
+ middle_action (node )
753
+ stack .append (node )
754
+ stack .append (node [corners [- 1 ]])
755
+ corners .append (0 )
756
+
651
757
def breadth_first_order_traversal (self , action = None ):
652
758
r"""
653
759
Run the breadth-first post-order traversal algorithm
@@ -823,12 +929,41 @@ def node_number_at_depth(self, depth):
823
929
True
824
930
sage: [T.node_number_at_depth(i) for i in range(3)]
825
931
[0, 0, 0]
932
+
933
+ Check that we do not hit a recursion limit::
934
+
935
+ sage: T = OrderedTree([])
936
+ sage: for _ in range(9999):
937
+ ....: T = OrderedTree([T])
938
+ sage: T.node_number_at_depth(2000)
939
+ 1
826
940
"""
827
941
if self .is_empty ():
828
- return Integer (0 )
829
- if depth == 0 :
830
- return Integer (1 )
831
- return sum (son .node_number_at_depth (depth - 1 ) for son in self )
942
+ return 0
943
+ m = 0
944
+
945
+ def fr_action (node ):
946
+ nonlocal m , depths , depth
947
+ if depths [- 1 ] == depth :
948
+ m += 1
949
+
950
+ def m_action (node ):
951
+ nonlocal depths
952
+ depths .append (depths [- 1 ] + 1 )
953
+
954
+ def fn_action (node ):
955
+ nonlocal depths
956
+ depths .pop ()
957
+
958
+ def lf_action (node ):
959
+ nonlocal m , depths , depth
960
+ if depths [- 1 ] == depth :
961
+ m += 1
962
+ depths .pop ()
963
+
964
+ depths = [0 ]
965
+ self .contour_traversal (fr_action , m_action , fn_action , lf_action )
966
+ return Integer (m )
832
967
833
968
def paths_to_the_right (self , path ):
834
969
r"""
@@ -1052,11 +1187,25 @@ def node_number(self):
1052
1187
2
1053
1188
sage: BinaryTree([[None, [[], []]], None]).node_number()
1054
1189
5
1190
+
1191
+ TESTS:
1192
+
1193
+ Check that we do not hit a recursion limit::
1194
+
1195
+ sage: T = OrderedTree([])
1196
+ sage: for _ in range(9999):
1197
+ ....: T = OrderedTree([T])
1198
+ sage: T.node_number()
1199
+ 10000
1055
1200
"""
1056
- if self .is_empty ():
1057
- return Integer (0 )
1058
- else :
1059
- return sum ((i .node_number () for i in self ), Integer (1 ))
1201
+ count = 0
1202
+
1203
+ def incr (node ):
1204
+ nonlocal count
1205
+ count += 1
1206
+
1207
+ self .iterative_pre_order_traversal (incr )
1208
+ return Integer (count )
1060
1209
1061
1210
def depth (self ):
1062
1211
"""
@@ -1079,11 +1228,33 @@ def depth(self):
1079
1228
0
1080
1229
sage: BinaryTree([[],[[],[]]]).depth()
1081
1230
3
1231
+
1232
+ TESTS:
1233
+
1234
+ Check that we do not hit a recursion limit::
1235
+
1236
+ sage: T = OrderedTree([])
1237
+ sage: for _ in range(9999):
1238
+ ....: T = OrderedTree([T])
1239
+ sage: T.depth()
1240
+ 10000
1082
1241
"""
1083
- if self :
1084
- return Integer (1 + max (i .depth () for i in self ))
1085
- else :
1086
- return Integer (0 if self .is_empty () else 1 )
1242
+ if self .is_empty ():
1243
+ return 0
1244
+ m = []
1245
+
1246
+ def action (node ):
1247
+ nonlocal m
1248
+ if node .is_empty ():
1249
+ m .append (- 1 )
1250
+ elif not bool (node ):
1251
+ m .append (0 )
1252
+ else :
1253
+ mx = max (m .pop () for _ in node )
1254
+ m .append (mx + 1 )
1255
+
1256
+ self .contour_traversal (final_action = action , leaf_action = action )
1257
+ return Integer (m [0 ] + 1 )
1087
1258
1088
1259
def _ascii_art_ (self ):
1089
1260
r"""
0 commit comments