File: non-recursive-graphlib

package info (click to toggle)
pypy 5.6.0%2Bdfsg-4
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 97,040 kB
  • ctags: 185,069
  • sloc: python: 1,147,862; ansic: 49,642; cpp: 5,245; asm: 5,169; makefile: 529; sh: 481; xml: 232; lisp: 45
file content (123 lines) | stat: -rw-r--r-- 3,987 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
Description: Make graphlib less recursive
 Fixes FTBFS on armel, mipsel, mips64el
Author: Armin Rigo <arigo@tunes.org>
Origin: upstream,
 https://bitbucket.org/pypy/pypy/commits/198fd97491aa047542f85ee0069ae2d82c832df9
 https://bitbucket.org/pypy/pypy/commits/c49289d2da9650a94976de2a4489d1f9d7f42940
 https://bitbucket.org/pypy/pypy/commits/924339af3d65f367d2b7a6d06276dbd230c50439
 https://bitbucket.org/pypy/pypy/commits/da7ce96af92c1537a3bf15f92f5f50bd6dc419a3

diff --git a/rpython/tool/algo/graphlib.py b/rpython/tool/algo/graphlib.py
--- a/rpython/tool/algo/graphlib.py
+++ b/rpython/tool/algo/graphlib.py
@@ -25,18 +25,27 @@
     return edges
 
 def depth_first_search(root, vertices, edges):
-    seen = {}
+    seen = set([root])
     result = []
-    def visit(vertex):
-        result.append(('start', vertex))
-        seen[vertex] = True
-        for edge in edges[vertex]:
-            w = edge.target
-            if w in vertices and w not in seen:
-                visit(w)
-        result.append(('stop', vertex))
-    visit(root)
-    return result
+    stack = []
+    while True:
+        result.append(('start', root))
+        stack.append((root, iter(edges[root])))
+        while True:
+            vertex, iterator = stack[-1]
+            try:
+                edge = next(iterator)
+            except StopIteration:
+                stack.pop()
+                result.append(('stop', vertex))
+                if not stack:
+                    return result
+            else:
+                w = edge.target
+                if w in vertices and w not in seen:
+                    seen.add(w)
+                    root = w
+                    break
 
 def vertices_reachable_from(root, vertices, edges):
     for event, v in depth_first_search(root, vertices, edges):
@@ -97,13 +106,20 @@
             for edge in edges[v]:
                 if edge.target in vertices:
                     edgestack.append(edge)
-                    visit(edge.target)
+                    yield visit(edge.target)
                     edgestack.pop()
             stackpos[v] = None
         else:
             if stackpos[v] is not None:   # back-edge
                 result.append(edgestack[stackpos[v]:])
-    visit(root)
+
+    pending = [visit(root)]
+    while pending:
+        generator = pending[-1]
+        try:
+            pending.append(next(generator))
+        except StopIteration:
+            pending.pop()
     return result        
 
 
@@ -164,14 +180,20 @@
                 raise CycleFound
             if w in unvisited:
                 del unvisited[w]
-                visit(w)
+                yield visit(w)
         del visiting[vertex]
     try:
         unvisited = vertices.copy()
         while unvisited:
             visiting = {}
             root = unvisited.popitem()[0]
-            visit(root)
+            pending = [visit(root)]
+            while pending:
+                generator = pending[-1]
+                try:
+                    pending.append(next(generator))
+                except StopIteration:
+                    pending.pop()
     except CycleFound:
         return False
     else:
diff --git a/rpython/tool/algo/test/test_graphlib.py b/rpython/tool/algo/test/test_graphlib.py
--- a/rpython/tool/algo/test/test_graphlib.py
+++ b/rpython/tool/algo/test/test_graphlib.py
@@ -20,6 +20,22 @@
         'G': [],
         }
 
+    def test_depth_first_search(self):
+        # 'D' missing from the list of vertices
+        lst = depth_first_search('A', list('ABCEFG'), self.edges)
+        assert lst == [
+            ('start', 'A'),
+            ('start', 'B'),
+            ('start', 'E'),
+            ('start', 'C'),
+            ('start', 'F'),
+            ('stop', 'F'),
+            ('stop', 'C'),
+            ('stop', 'E'),
+            ('stop', 'B'),
+            ('stop', 'A'),
+        ]
+
     def test_strong_components(self):
         edges = self.edges
         saved = copy_edges(edges)