Mercurial > projects > dwt-addons
comparison dwtx/draw2d/graph/CompoundBreakCycles.d @ 98:95307ad235d9
Added Draw2d code, still work in progress
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sun, 03 Aug 2008 00:52:14 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
96:b492ba44e44d | 98:95307ad235d9 |
---|---|
1 /******************************************************************************* | |
2 * Copyright (c) 2003, 2005 IBM Corporation and others. | |
3 * All rights reserved. This program and the accompanying materials | |
4 * are made available under the terms of the Eclipse Public License v1.0 | |
5 * which accompanies this distribution, and is available at | |
6 * http://www.eclipse.org/legal/epl-v10.html | |
7 * | |
8 * Contributors: | |
9 * IBM Corporation - initial API and implementation | |
10 * Port to the D programming language: | |
11 * Frank Benoit <benoit@tionex.de> | |
12 *******************************************************************************/ | |
13 module dwtx.draw2d.graph.CompoundBreakCycles; | |
14 | |
15 import dwt.dwthelper.utils; | |
16 import dwtx.draw2d.graph.GraphVisitor; | |
17 import dwtx.draw2d.graph.NodeList; | |
18 import dwtx.draw2d.graph.Node; | |
19 import dwtx.draw2d.graph.DirectedGraph; | |
20 import dwtx.draw2d.graph.Subgraph; | |
21 import dwtx.draw2d.graph.Edge; | |
22 | |
23 | |
24 /** | |
25 * This visitor eliminates cycles in the graph via a modified implementation of the | |
26 * greedy cycle removal algorithm for directed graphs. The algorithm has been modified to | |
27 * handle the presence of Subgraphs and compound cycles which may result. This algorithm | |
28 * determines a set of edges which can be inverted and result in a graph without compound | |
29 * cycles. | |
30 * | |
31 * @author Daniel Lee | |
32 * @author Randy Hudson | |
33 * @since 2.1.2 | |
34 */ | |
35 class CompoundBreakCycles : GraphVisitor { | |
36 | |
37 /* | |
38 * Caches all nodes in the graph. Used in identifying cycles and in cycle removal. | |
39 * Flag field indicates "presence". If true, the node has been removed from the list. | |
40 */ | |
41 private NodeList graphNodes; | |
42 private NodeList sL; | |
43 | |
44 public this(){ | |
45 sL = new NodeList(); | |
46 } | |
47 | |
48 private bool allFlagged(NodeList nodes) { | |
49 for (int i = 0; i < nodes.size(); i++) { | |
50 if (nodes.getNode(i).flag is false) | |
51 return false; | |
52 } | |
53 return true; | |
54 } | |
55 | |
56 private int buildNestingTreeIndices(NodeList nodes, int base) { | |
57 for (int i = 0; i < nodes.size(); i++) { | |
58 Node node = cast(Node)nodes.get(i); | |
59 if (auto s = cast(Subgraph) node ) { | |
60 s.nestingTreeMin = base; | |
61 base = buildNestingTreeIndices(s.members, base); | |
62 } | |
63 node.nestingIndex = base++; | |
64 } | |
65 return base++; | |
66 } | |
67 | |
68 private bool canBeRemoved(Node n) { | |
69 return !n.flag && getChildCount(n) is 0; | |
70 } | |
71 | |
72 private bool changeInDegree(Node n, int delta) { | |
73 return (n.workingInts[1] += delta) is 0; | |
74 } | |
75 | |
76 private bool changeOutDegree(Node n, int delta) { | |
77 return (n.workingInts[2] += delta) is 0; | |
78 } | |
79 | |
80 /* | |
81 * Execution of the modified greedy cycle removal algorithm. | |
82 */ | |
83 private void cycleRemove(NodeList children) { | |
84 NodeList sR = new NodeList(); | |
85 do { | |
86 findSinks(children, sR); | |
87 findSources(children); | |
88 | |
89 // all sinks and sources added, find node with highest | |
90 // outDegree - inDegree | |
91 Node max = findNodeWithMaxDegree(children); | |
92 if (max !is null) { | |
93 for (int i = 0; i < children.size(); i++) { | |
94 Node child = cast(Node)children.get(i); | |
95 if (child.flag) | |
96 continue; | |
97 if (child is max) | |
98 restoreSinks(max, sR); | |
99 else | |
100 restoreSources(child); | |
101 } | |
102 remove(max); | |
103 } | |
104 } while (!allFlagged(children)); | |
105 while (!sR.isEmpty()) | |
106 sL.add(sR.remove(sR.size() - 1)); | |
107 } | |
108 | |
109 private void findInitialSinks(NodeList children, NodeList sinks) { | |
110 for (int i = 0; i < children.size(); i++) { | |
111 Node node = children.getNode(i); | |
112 if (node.flag) | |
113 continue; | |
114 if (isSink(node) && canBeRemoved(node)) { | |
115 sinks.add(node); | |
116 node.flag = true; | |
117 } | |
118 if (null !is cast(Subgraph)node ) | |
119 findInitialSinks((cast(Subgraph)node).members, sinks); | |
120 } | |
121 } | |
122 | |
123 private void findInitialSources(NodeList children, NodeList sources) { | |
124 for (int i = 0; i < children.size(); i++) { | |
125 Node node = children.getNode(i); | |
126 if (isSource(node) && canBeRemoved(node)) { | |
127 sources.add(node); | |
128 node.flag = true; | |
129 } | |
130 if (null !is cast(Subgraph)node ) | |
131 findInitialSources((cast(Subgraph)node).members, sources); | |
132 } | |
133 } | |
134 | |
135 private Node findNodeWithMaxDegree(NodeList nodes) { | |
136 int max = Integer.MIN_VALUE; | |
137 Node maxNode = null; | |
138 | |
139 for (int i = 0; i < nodes.size(); i++) { | |
140 Node node = nodes.getNode(i); | |
141 if (node.flag) | |
142 continue; | |
143 int degree = getNestedOutDegree(node) - getNestedInDegree(node); | |
144 if (degree >= max && node.flag is false) { | |
145 max = degree; | |
146 maxNode = node; | |
147 } | |
148 } | |
149 return maxNode; | |
150 } | |
151 | |
152 /* | |
153 * Finds all sinks in graphNodes and adds them to the passed NodeList | |
154 */ | |
155 private void findSinks(NodeList children, NodeList rightList) { | |
156 // NodeList rightList = new NodeList(); | |
157 NodeList sinks = new NodeList(); | |
158 findInitialSinks(children, sinks); | |
159 while (!sinks.isEmpty()) { | |
160 Node sink = sinks.getNode(sinks.size() - 1); | |
161 rightList.add(sink); | |
162 sinks.remove(sink); | |
163 removeSink(sink, sinks); | |
164 | |
165 // Check to see if the removal has made the parent node a sink | |
166 if (sink.getParent() !is null) { | |
167 Node parent = sink.getParent(); | |
168 setChildCount(parent, getChildCount(parent) - 1); | |
169 if (isSink(parent) && canBeRemoved(parent)) { | |
170 sinks.add(parent); | |
171 parent.flag = true; | |
172 } | |
173 } | |
174 } | |
175 } | |
176 | |
177 /* | |
178 * Finds all sources in graphNodes and adds them to the sL NodeList. | |
179 */ | |
180 private void findSources(NodeList children) { | |
181 NodeList sources = new NodeList(); | |
182 findInitialSources(children, sources); | |
183 while (!sources.isEmpty()) { | |
184 Node source = sources.getNode(sources.size() - 1); | |
185 sL.add(source); | |
186 sources.remove(source); | |
187 removeSource(source, sources); | |
188 | |
189 // Check to see if the removal has made the parent node a source | |
190 if (source.getParent() !is null) { | |
191 Node parent = source.getParent(); | |
192 setChildCount(parent, getChildCount(parent) - 1); | |
193 if (isSource(parent) && canBeRemoved(parent)) { | |
194 sources.add(parent); | |
195 parent.flag = true; | |
196 } | |
197 } | |
198 } | |
199 } | |
200 | |
201 private int getChildCount(Node n) { | |
202 return n.workingInts[3]; | |
203 } | |
204 | |
205 private int getInDegree(Node n) { | |
206 return n.workingInts[1]; | |
207 } | |
208 | |
209 private int getNestedInDegree(Node n) { | |
210 int result = getInDegree(n); | |
211 if ( auto s = cast(Subgraph)n ) { | |
212 for (int i = 0; i < s.members.size(); i++) | |
213 if (!s.members.getNode(i).flag) | |
214 result += getInDegree(s.members.getNode(i)); | |
215 } | |
216 return result; | |
217 } | |
218 | |
219 private int getNestedOutDegree(Node n) { | |
220 int result = getOutDegree(n); | |
221 if ( auto s = cast(Subgraph)n ) { | |
222 for (int i = 0; i < s.members.size(); i++) | |
223 if (!s.members.getNode(i).flag) | |
224 result += getOutDegree(s.members.getNode(i)); | |
225 } | |
226 return result; | |
227 } | |
228 | |
229 private int getOrderIndex(Node n) { | |
230 return n.workingInts[0]; | |
231 } | |
232 | |
233 private int getOutDegree(Node n) { | |
234 return n.workingInts[2]; | |
235 } | |
236 | |
237 private void initializeDegrees(DirectedGraph g) { | |
238 g.nodes.resetFlags(); | |
239 g.edges.resetFlags(false); | |
240 for (int i = 0; i < g.nodes.size(); i++) { | |
241 Node n = g.nodes.getNode(i); | |
242 setInDegree(n, n.incoming.size()); | |
243 setOutDegree(n, n.outgoing.size()); | |
244 if ( auto s = cast(Subgraph)n ) | |
245 setChildCount(n, s.members.size()); | |
246 else | |
247 setChildCount(n, 0); | |
248 } | |
249 } | |
250 | |
251 private void invertEdges(DirectedGraph g) { | |
252 // Assign order indices | |
253 int orderIndex = 0; | |
254 for (int i = 0; i < sL.size(); i++) { | |
255 setOrderIndex(sL.getNode(i), orderIndex++); | |
256 } | |
257 // Invert edges that are causing a cycle | |
258 for (int i = 0; i < g.edges.size(); i++) { | |
259 Edge e = g.edges.getEdge(i); | |
260 if (getOrderIndex(e.source) > getOrderIndex(e.target) | |
261 && !e.source.isNested(e.target) | |
262 && !e.target.isNested(e.source)) { | |
263 e.invert(); | |
264 e.isFeedback_ = true; | |
265 } | |
266 } | |
267 } | |
268 | |
269 /** | |
270 * Removes all edges connecting the given subgraph to other nodes outside of it. | |
271 * @param s | |
272 * @param n | |
273 */ | |
274 private void isolateSubgraph(Subgraph subgraph, Node member) { | |
275 Edge edge = null; | |
276 for (int i = 0; i < member.incoming.size(); i++) { | |
277 edge = member.incoming.getEdge(i); | |
278 if (!subgraph.isNested(edge.source) && !edge.flag) | |
279 removeEdge(edge); | |
280 } | |
281 for (int i = 0; i < member.outgoing.size(); i++) { | |
282 edge = member.outgoing.getEdge(i); | |
283 if (!subgraph.isNested(edge.target) && !edge.flag) | |
284 removeEdge(edge); | |
285 } | |
286 if ( auto s = cast(Subgraph)member ) { | |
287 NodeList members = s.members; | |
288 for (int i = 0; i < members.size(); i++) | |
289 isolateSubgraph(subgraph, members.getNode(i)); | |
290 } | |
291 } | |
292 | |
293 private bool isSink(Node n) { | |
294 return getOutDegree(n) is 0 | |
295 && (n.getParent() is null | |
296 || isSink(n.getParent())); | |
297 } | |
298 | |
299 private bool isSource(Node n) { | |
300 return getInDegree(n) is 0 | |
301 && (n.getParent() is null | |
302 || isSource(n.getParent())); | |
303 } | |
304 | |
305 private void remove(Node n) { | |
306 n.flag = true; | |
307 if (n.getParent() !is null) | |
308 setChildCount(n.getParent(), getChildCount(n.getParent()) - 1); | |
309 removeSink(n, null); | |
310 removeSource(n, null); | |
311 sL.add(n); | |
312 if ( auto s = cast(Subgraph)n ) { | |
313 isolateSubgraph(s, s); | |
314 cycleRemove(s.members); | |
315 } | |
316 } | |
317 | |
318 private bool removeEdge(Edge e) { | |
319 if (e.flag) | |
320 return false; | |
321 e.flag = true; | |
322 changeOutDegree(e.source, -1); | |
323 changeInDegree(e.target, -1); | |
324 return true; | |
325 } | |
326 | |
327 /** | |
328 * Removes all edges between a parent and any of its children or descendants. | |
329 */ | |
330 private void removeParentChildEdges(DirectedGraph g) { | |
331 for (int i = 0; i < g.edges.size(); i++) { | |
332 Edge e = g.edges.getEdge(i); | |
333 if (e.source.isNested(e.target) || e.target.isNested(e.source)) | |
334 removeEdge(e); | |
335 } | |
336 } | |
337 | |
338 private void removeSink(Node sink, NodeList allSinks) { | |
339 for (int i = 0; i < sink.incoming.size(); i++) { | |
340 Edge e = sink.incoming.getEdge(i); | |
341 if (!e.flag) { | |
342 removeEdge(e); | |
343 Node source = e.source; | |
344 if (allSinks !is null && isSink(source) && canBeRemoved(source)) { | |
345 allSinks.add(source); | |
346 source.flag = true; | |
347 } | |
348 } | |
349 } | |
350 } | |
351 | |
352 private void removeSource(Node n, NodeList allSources) { | |
353 for (int i = 0; i < n.outgoing.size(); i++) { | |
354 Edge e = n.outgoing.getEdge(i); | |
355 if (!e.flag) { | |
356 e.flag = true; | |
357 changeInDegree(e.target, -1); | |
358 changeOutDegree(e.source, -1); | |
359 | |
360 Node target = e.target; | |
361 if (allSources !is null && isSource(target) && canBeRemoved(target)) { | |
362 allSources.add(target); | |
363 target.flag = true; | |
364 } | |
365 } | |
366 } | |
367 } | |
368 | |
369 /** | |
370 * Restores an edge if it has been removed, and both of its nodes are not removed. | |
371 * @param e the edge | |
372 * @return <code>true</code> if the edge was restored | |
373 */ | |
374 private bool restoreEdge(Edge e) { | |
375 if (!e.flag || e.source.flag || e.target.flag) | |
376 return false; | |
377 e.flag = false; | |
378 changeOutDegree(e.source, 1); | |
379 changeInDegree(e.target, 1); | |
380 return true; | |
381 } | |
382 | |
383 /** | |
384 * Brings back all nodes nested in the given node. | |
385 * @param node the node to restore | |
386 * @param sr current sinks | |
387 */ | |
388 private void restoreSinks(Node node, NodeList sR) { | |
389 if (node.flag && sR.contains(node)) { | |
390 node.flag = false; | |
391 if (node.getParent() !is null) | |
392 setChildCount(node.getParent(), getChildCount(node.getParent()) + 1); | |
393 sR.remove(node); | |
394 for (int i = 0; i < node.incoming.size(); i++) { | |
395 Edge e = node.incoming.getEdge(i); | |
396 restoreEdge(e); | |
397 } | |
398 for (int i = 0; i < node.outgoing.size(); i++) { | |
399 Edge e = node.outgoing.getEdge(i); | |
400 restoreEdge(e); | |
401 } | |
402 } | |
403 if ( auto s = cast(Subgraph)node ) { | |
404 for (int i = 0; i < s.members.size(); i++) { | |
405 Node member = s.members.getNode(i); | |
406 restoreSinks(member, sR); | |
407 } | |
408 } | |
409 } | |
410 | |
411 /** | |
412 * Brings back all nodes nested in the given node. | |
413 * @param node the node to restore | |
414 * @param sr current sinks | |
415 */ | |
416 private void restoreSources(Node node) { | |
417 if (node.flag && sL.contains(node)) { | |
418 node.flag = false; | |
419 if (node.getParent() !is null) | |
420 setChildCount(node.getParent(), getChildCount(node.getParent()) + 1); | |
421 sL.remove(node); | |
422 for (int i = 0; i < node.incoming.size(); i++) { | |
423 Edge e = node.incoming.getEdge(i); | |
424 restoreEdge(e); | |
425 } | |
426 for (int i = 0; i < node.outgoing.size(); i++) { | |
427 Edge e = node.outgoing.getEdge(i); | |
428 restoreEdge(e); | |
429 } | |
430 } | |
431 if ( auto s = cast(Subgraph)node ) { | |
432 for (int i = 0; i < s.members.size(); i++) { | |
433 Node member = s.members.getNode(i); | |
434 restoreSources(member); | |
435 } | |
436 } | |
437 } | |
438 | |
439 public void revisit(DirectedGraph g) { | |
440 for (int i = 0; i < g.edges.size(); i++) { | |
441 Edge e = g.edges.getEdge(i); | |
442 if (e.isFeedback()) | |
443 e.invert(); | |
444 } | |
445 } | |
446 | |
447 private void setChildCount(Node n, int count) { | |
448 n.workingInts[3] = count; | |
449 } | |
450 | |
451 private void setInDegree(Node n, int deg) { | |
452 n.workingInts[1] = deg; | |
453 } | |
454 | |
455 private void setOrderIndex(Node n, int index) { | |
456 n.workingInts[0] = index; | |
457 } | |
458 | |
459 private void setOutDegree(Node n, int deg) { | |
460 n.workingInts[2] = deg; | |
461 } | |
462 | |
463 /** | |
464 * @see GraphVisitor#visit(dwtx.draw2d.graph.DirectedGraph) | |
465 */ | |
466 public void visit(DirectedGraph g) { | |
467 initializeDegrees(g); | |
468 graphNodes = g.nodes; | |
469 | |
470 NodeList roots = new NodeList(); | |
471 for (int i = 0; i < graphNodes.size(); i++) { | |
472 if (graphNodes.getNode(i).getParent() is null) | |
473 roots.add(graphNodes.getNode(i)); | |
474 } | |
475 buildNestingTreeIndices(roots, 0); | |
476 removeParentChildEdges(g); | |
477 cycleRemove(roots); | |
478 invertEdges(g); | |
479 } | |
480 | |
481 } |