1 /** \file
2 * \brief Implementation of class ClusterAnalysis that calculates
3 * the bag and inner/outer activity structures for a clustered graph
4 * as described in Chimani, Klein:Shrinking the Search Space for Clustered
5 * Planarity. GD 2012.
6 *
7 * \author Karsten Klein
8 *
9 * \par License:
10 * This file is part of the Open Graph Drawing Framework (OGDF).
11 *
12 * \par
13 * Copyright (C)<br>
14 * See README.md in the OGDF root directory for details.
15 *
16 * \par
17 * This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU General Public License
19 * Version 2 or 3 as published by the Free Software Foundation;
20 * see the file LICENSE.txt included in the packaging of this file
21 * for details.
22 *
23 * \par
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * \par
30 * You should have received a copy of the GNU General Public
31 * License along with this program; if not, see
32 * http://www.gnu.org/copyleft/gpl.html
33 */
34
35 #include <ogdf/cluster/ClusterAnalysis.h>
36 #include <ogdf/cluster/ClusterArray.h>
37 #include <ogdf/basic/Queue.h>
38 #include <ogdf/basic/DisjointSets.h>
39
40 // Comment on use of ClusterArrays:
41 // We would like to save some space by only reserving one slot
42 // per existing cluster instead of maxClusterIndex() slots,
43 // which might be larger. However, we then would
44 // need to store an index with each cluster in a struct here,
45 // minimizing the effect again. Note ClusterArrays are static
46 // here, a change in the ClusterGraph won't be detected
47 // by ClusterAnalysis after initialization.
48 // Where multiple indexing is done, the static version
49 // of ClusterArrays is used with successive cluster index
50 // numbers computed locally, to save memory. When only a single
51 // indexed structure is needed this is clearly an overhead.
52 // Therefore the ClusterArrays are then created with size maxClusterIndex().
53
54
55 namespace ogdf {
56
57 //Needs to be the largest int allowed, as it is used as default,
58 //and an update is done for smaller values
59 const int ClusterAnalysis::IsNotActiveBound = std::numeric_limits<int>::max();
60 const int ClusterAnalysis::DefaultIndex = -1;
61
62 //Constructor
ClusterAnalysis(const ClusterGraph & C,bool oalists,bool indyBags)63 ClusterAnalysis::ClusterAnalysis(const ClusterGraph& C, bool oalists, bool indyBags) : m_C(&C), m_oanum(nullptr), m_ianum(nullptr),
64 m_bags(nullptr), m_storeoalists(oalists), m_lcaEdges(nullptr), m_indyBags(indyBags), m_numIndyBags(-1), m_indyBagRoots(nullptr)
65 {
66 init();
67 computeBags();
68 if (m_indyBags)
69 {
70 computeIndyBags();
71 }
72 }
ClusterAnalysis(const ClusterGraph & C,bool indyBags)73 ClusterAnalysis::ClusterAnalysis(const ClusterGraph& C, bool indyBags): m_C(&C), m_oanum(nullptr), m_ianum(nullptr),
74 m_bags(nullptr), m_storeoalists(true), m_lcaEdges(nullptr), m_indyBags(indyBags),m_numIndyBags(-1), m_indyBagRoots(nullptr)
75 {
76 init();
77 computeBags();
78 // Even though it looks like we could compute bags and
79 // indyBags in one pass, we do not look at each vertex
80 // in each cluster during bag computation, as this would
81 // be inefficient (use Union-Find instead). However, for
82 // independent bags, we need to identify bags wo outeractive
83 // vertices per cluster (TODO there may be shortcuts however, e.g.
84 // if number of outeractive vertices is zero for a cluster, then
85 // every bag is independent).
86 if (m_indyBags)
87 {
88 computeIndyBags();
89 }
90 }
~ClusterAnalysis()91 ClusterAnalysis::~ClusterAnalysis()
92 {
93 cleanUp();
94 }
95
cleanUp()96 void ClusterAnalysis::cleanUp()
97 {
98 delete m_oanum;
99 delete m_ianum;
100 delete m_bags;
101 delete m_lcaEdges;
102 if (m_storeoalists) delete m_oalists;
103 for(node v : m_C->constGraph().nodes)
104 {
105 delete m_bagindex[v];
106 }
107 if (m_indyBags) delete m_indyBagRoots;
108 }
109
outerActive(cluster c) const110 int ClusterAnalysis::outerActive(cluster c) const {return (*m_oanum)[c];}
innerActive(cluster c) const111 int ClusterAnalysis::innerActive(cluster c) const {return (*m_ianum)[c];}
numberOfBags(cluster c) const112 int ClusterAnalysis::numberOfBags(cluster c) const {return (*m_bags)[c];}
oaNodes(cluster c)113 List<node>& ClusterAnalysis::oaNodes(cluster c) {
114 OGDF_ASSERT(m_storeoalists);
115 return (*m_oalists)[c];
116 }
117
isOuterActive(node v,cluster c) const118 bool ClusterAnalysis::isOuterActive(node v, cluster c) const
119 {
120 return (*m_oactive[v])[c] > 0;
121 }
isInnerActive(node v,cluster c) const122 bool ClusterAnalysis::isInnerActive(node v, cluster c) const
123 {
124 return (*m_iactive[v])[c] > 0;
125 }
lcaEdges(cluster c)126 List<edge>& ClusterAnalysis::lcaEdges(cluster c)
127 {
128 return (*m_lcaEdges)[c];
129 }
bagIndex(node v,cluster c)130 int ClusterAnalysis::bagIndex(node v, cluster c) {return (*m_bagindex[v])[c];}
131
indyBagIndex(node v)132 int ClusterAnalysis::indyBagIndex(node v) {
133 if (!m_indyBags)
134 {
135 OGDF_THROW_PARAM(AlgorithmFailureException,AlgorithmFailureCode::IllegalParameter);
136 }
137 return m_indyBagNumber[v];
138
139 }
140
indyBagRoot(int i)141 cluster ClusterAnalysis::indyBagRoot(int i) {
142 if (!m_indyBags)
143 {
144 OGDF_THROW_PARAM(AlgorithmFailureException,AlgorithmFailureCode::IllegalParameter);
145 }
146 return m_indyBagRoots[i];
147 }
148 //we fill all arrays that store the inner/outer activity status
init()149 void ClusterAnalysis::init() {
150 //does it make sense to split into detecting inner / outer activity?
151 //is it faster to run once over the edges, with bidirectional detection
152 //and a check if the edge is processed already, or is it at least almost
153 //always as fast when testing from both directions?
154
155 //first outactive vertices
156 const Graph &G = m_C->constGraph();
157 m_iactive.init(G);
158 m_oactive.init(G);
159 m_ialevel.init(G, IsNotActiveBound);
160 m_oalevel.init(G, IsNotActiveBound);
161
162 delete m_oanum;
163 delete m_ianum;
164 delete m_bags;
165 m_oanum = new ClusterArray<int>(*m_C, 0);
166 m_ianum = new ClusterArray<int>(*m_C, 0);
167 m_bags = new ClusterArray<int>(*m_C, 0);
168 m_lcaEdges = new ClusterArray<List<edge> >(*m_C);
169 if (m_storeoalists)
170 m_oalists = new ClusterArray<List<node> >(*m_C);
171
172 //We don't want to set dynamic depths update for clusters in m_C,
173 //therefore we just compute the values here
174 //top-down run through the cluster tree, depth 0 for the root
175 ClusterArray<int> cdepth(*m_C);
176 cdepth[m_C->rootCluster()] = 0;
177 Queue<cluster> cq;
178 for (cluster ci : m_C->rootCluster()->children) {
179 cq.append(ci);
180 }
181
182 while (!cq.empty())
183 {
184 cluster cc = cq.pop();
185 cdepth[cc] = cdepth[cc->parent()]+1;
186 for(cluster ci : cc->children) {
187 cq.append(ci);
188 }
189 }
190
191 //store that we already visited e, as we don't have a static lookup
192 //for the paths, running the search from both directions is slower.
193 EdgeArray<bool> visited(G, false);
194 for(node v : G.nodes)
195 {
196 // See comment on use of ClusterArrays above
197 m_iactive[v] = new ClusterArray<int>(*m_C,0,m_C->maxClusterIndex()+1);
198 m_oactive[v] = new ClusterArray<int>(*m_C,0,m_C->maxClusterIndex()+1);
199 }
200 for(node v : G.nodes)
201 {
202 for(adjEntry adj : v->adjEntries) {
203 edge e = adj->theEdge();
204
205 if (!visited[e])
206 {
207 node w = e->opposite(v);
208 List<cluster> el; // result cluster list of path in cluster tree T between v,w
209 cluster c1,c2; // ancestors of lca(v,w) on path, we don't really need them here
210
211 cluster lca = m_C->commonClusterAncestorsPath(v, w, c1, c2, el);
212 OGDF_ASSERT(el.size() > 0);
213 ListIterator<cluster> ctit = el.begin();//m_C->clusterOf(v);
214
215 //run over the path, set activity status (vertices are
216 //active for a cluster if adjacent edge crosses the border
217
218 //clusters before lca are left, i.e. v is outer active
219 //clusters behind lca are entered, i.e. v is inner active
220 while (ctit.valid() && (*ctit) != lca)
221 {
222 (*m_oactive[v])[*ctit]++;
223 (*m_iactive[w])[*ctit]++;
224
225 //only count vertices a single time
226 if ((*m_oactive[v])[*ctit] == 1) (*m_oanum)[*ctit]++;
227 if ((*m_iactive[w])[*ctit] == 1) (*m_ianum)[*ctit]++;
228
229 //update the activity levels
230 //could do this just for the last in the line...
231 int clevel = cdepth[*ctit];
232 if (m_ialevel[w] > clevel)
233 m_ialevel[w] = clevel;
234 if (m_oalevel[v] > clevel)
235 m_oalevel[v] = clevel;
236
237 ++ctit;
238 }
239
240 OGDF_ASSERT((*ctit) == lca);
241 //vertices are never active wrt lca
242 //we store however the corresponding edges
243 //for later use in bag detection
244 (*m_lcaEdges)[lca].pushBack(e);
245 ++ctit;
246
247 while (ctit.valid())
248 {
249 (*m_iactive[v])[*ctit]++;
250 (*m_oactive[w])[*ctit]++;
251
252 if ((*m_iactive[v])[*ctit] == 1) (*m_ianum)[*ctit]++;
253 if ((*m_oactive[w])[*ctit] == 1) (*m_oanum)[*ctit]++;
254
255 //update the activity levels
256 int clevel = cdepth[*ctit];
257 //could do this just for the last in the line...
258 if (m_ialevel[v] > clevel)
259 m_ialevel[v] = clevel;
260 if (m_oalevel[w] > clevel)
261 m_oalevel[w] = clevel;
262
263 ++ctit;
264 }
265
266 visited[e] = true;
267
268 #ifdef OGDF_DEBUG
269 std::cout << "Edge "<< v << " " << w <<"\n";
270 #endif
271 }
272 }
273
274 }
275 #ifdef OGDF_DEBUG
276 for(node v : G.nodes)
277 {
278 std::cout << "Knoten "<<v<<" ist";
279 List<cluster> ol;
280 List<cluster> il;
281 for(cluster c : m_C->clusters)
282 {
283 if ((*m_iactive[v])[c]>0) il.pushBack(c);
284 if ((*m_oactive[v])[c]>0) ol.pushBack(c);
285 }
286 std::cout << " inneractive for ";
287 for (cluster ci : il) {
288 std::cout << ci << ", ";
289 }
290 std::cout << "\n";
291 std::cout << " outeractive for ";
292 for (cluster ci : ol) {
293 std::cout << ci << ", ";
294 }
295 std::cout << "\n";
296 }
297
298 #endif
299 }
300
301 // Runs through a list of vertices (starting with the one \p nodeIT points to)
302 // which is expected to be a full list of cluster vertices in \p c. Depending on
303 // outer activity and bag index number of the vertices, independent bags
304 // are detected and a corresponding index is assigned accordingly for each vertex.
partitionCluster(ListConstIterator<node> & nodeIt,cluster c,HashArray<int,List<node>> & bagNodes,HashArray<int,bool> & indyBag,Skiplist<int * > & indexNumbers,Array<cluster> & bagRoots)305 void ClusterAnalysis::partitionCluster(ListConstIterator<node> & nodeIt, cluster c,
306 HashArray<int, List<node> > & bagNodes, HashArray<int, bool> & indyBag,
307 Skiplist<int*> & indexNumbers, Array<cluster> & bagRoots) {
308 // Run through all vertices in c
309 while (nodeIt.valid())
310 {
311 node v = *nodeIt;
312 //if vertex is outeractive, the containing bag loses its status
313 // Nodes that are already processed can never be outeractive
314 // as we traverse bottom up. They are skipped as they are part
315 // of an independent bag in an already processed cluster.
316 if (m_indyBagNumber[v] == DefaultIndex)
317 {
318 int ind = bagIndex(v,c);
319 if (isOuterActive(v,c))
320 {
321 indyBag[ind]= false;
322 } else //don't need to add the index if vertex is oactive
323 {
324 if (!indexNumbers.isElement(&ind))
325 {
326 indexNumbers.add(new int(ind));
327 }
328 bagNodes[ind].pushBack(v); //store vertex in index' list
329 }
330 }
331 #ifdef OGDF_DEBUG
332 if (m_indyBagNumber[v] != DefaultIndex)
333 OGDF_ASSERT(!isOuterActive(v,c));
334 #endif
335
336 ++nodeIt;
337 }
338 // Now we have all indexes of bags that don't solely contain oactive vertices.
339 // For each index we check if the bag still has independency status,
340 // in this case we have found an independent bag and can remove all its
341 // vertices (mark them).
342
343 #if 0
344 std::cout << "Checking skiplist\n";
345 #endif
346 SkiplistIterator<int*> its = indexNumbers.begin();
347 while (its.valid())
348 {
349 int bind = *(*its);
350 #if 0
351 std::cout << "Found index "<< bind <<"\n";
352 #endif
353 if (indyBag[bind])
354 {
355 #ifdef OGDF_DEBUG
356 Logger::slout() << "Found independent bag with "<< bagNodes[bind].size() << "vertices\n";
357 #endif
358
359 for (node v : bagNodes[bind]) {
360 // Assign the final index number
361 m_indyBagNumber[v] = m_numIndyBags;
362 }
363 bagRoots[m_numIndyBags] = c;
364 m_numIndyBags++;
365 }
366
367 delete *its;
368 ++its;
369 }
370 }
371
372 // For each cluster we check if we can identify an independent
373 // bag, which might be useful for clustered planarity testing.
374 // compute independent bag affiliation for all vertices,
375 // store result in m_indyBagNumber, and set m_numIndyBags accordingly.
376 // We could interweave this with computeBags to be more efficient but
377 // this would it make the code more complex and in case we don't need
378 // the IndyBags we would do additional work in vain.
computeIndyBags()379 void ClusterAnalysis::computeIndyBags() {
380 m_numIndyBags = 0; //used both to count the bags and to store current
381 //indyBag index number for vertex assignment (i.e., starts with 1)
382 const Graph &G = m_C->constGraph();
383
384 // Store the root cluster of each indyBag
385 delete m_indyBagRoots;
386 // Intermediate storage during computation, maximum of #vertices possible
387 #ifdef OGDF_DEBUG
388 Array<cluster> bagRoots(0,G.numberOfNodes(),nullptr);
389 #else
390 Array<cluster> bagRoots(G.numberOfNodes());
391 #endif
392
393 // Store indyBag affiliation. Every vertex will get a number != -1 (DefaultIndex,
394 // as in the worst case the whole graph is an indyBag (in root cluster).
395 // Once assigned, the number won't change during the processing.
396 m_indyBagNumber.init(G, DefaultIndex);
397
398 // We run bottom up over all clusters (to find the minimum inclusion).
399 // For each vertex, we use the outer activity and bag index information.
400 // In case we find a bag without outeractive vertices it is a IndyBag.
401 // Already processed vertices are simply marked by an indyBag index entry different to -1.
402 #if 0
403 NodeArray<bool> processed(G, false); //mark vertices when IndyBag detected
404 #endif
405
406 // We do not have the sets of vertices for all bags, as only the bag index
407 // has been stored for a cluster.
408 // Detect the current leaf clusters for bottom up traversal.
409 List<cluster> ccleafs;
410 ClusterArray<int> unprocessedChildren(*m_C); //processing below: compute bags
411 for(cluster c : m_C->clusters)
412 {
413 if (c->cCount() == 0) ccleafs.pushBack(c);
414 unprocessedChildren[c] = c->cCount();
415 }
416 OGDF_ASSERT(!ccleafs.empty());
417
418 // Run through all clusters, leaves first.
419 while (!ccleafs.empty()){
420 // We cannot store the following information over the whole
421 // graph even though the bag index (which is one out of the
422 // set of vertex set ids in union find) would allow this.
423 // The bag index is defined per cluster.
424 // However, when moving up in the cluster tree indy information
425 // is not monotone, we therefore would need to update it accordingly.
426 // (Bag which is not outeractive will not become outeractive, but
427 // may get a part of an outeractive bag with the same id, and an
428 // outeractive bag might become enclosed).
429 HashArray<int, bool> indyBag(true); // true if bag with index i does not have outeractive vertices
430 // We want to store all vertices for each index that may be a
431 // potential indyBag index. We could add these in the entry stored
432 // in our index Skiplist, but then we need a comparison of the
433 // respecting class objects, slowing down the processing.
434 HashArray<int, List<node> > bagNodes; //holds vertices for index i
435
436 // We need a data structure that holds all indexes used, allows
437 // to search for them and to iterate through them. Could use
438 // insertion sorted dynamically allocated array here, but as
439 // SkipLists are implemented in OGDF, which provide comparable
440 // efficiency, we use them.
441 Skiplist<int*> indexNumbers; //holds all index numbers in c
442
443 cluster c = ccleafs.popFrontRet();
444
445 List<node> nodes;
446 ListConstIterator<node> it;
447 // Process leaves separately. As long as we don't do something
448 // special for non-leaves, we could just use getClusterNodes instead.
449 if (c->cCount() == 0) {
450 it = c->nBegin();
451 }
452 else {
453 // At this point all child clusters of c have been processed.
454 // This is somewhat slow, as we repeatedly collect the vertices
455 // Todo: Instead, we could clone the clusternodes code
456 // here and process them as we see them.
457
458 c->getClusterNodes(nodes);
459
460 #if 0
461 std::cout <<"Processing cluster with "<<nodes.size()<<"\n";
462 #endif
463
464 it = nodes.begin();
465 }
466 // Run through all vertices in c
467 partitionCluster(it, c, bagNodes, indyBag, indexNumbers, bagRoots);
468 // Now we update the status of the parent cluster and,
469 // in case all its children are processed, add it to
470 // the process queue.
471 if (c != m_C->rootCluster())
472 {
473 OGDF_ASSERT(unprocessedChildren[c->parent()] > 0);
474 unprocessedChildren[c->parent()]--;
475 if (unprocessedChildren[c->parent()] == 0) ccleafs.pushBack(c->parent());
476 }
477 }
478 // Copying into smaller array is a bit slower than just reserving the whole #v array,
479 // but we do it anyway.
480 m_indyBagRoots = new cluster[m_numIndyBags];
481 for (int k = 0; k < m_numIndyBags; k++)
482 {
483 OGDF_ASSERT(bagRoots[k] != nullptr);
484 m_indyBagRoots[k] = bagRoots[k];
485 }
486 #ifdef OGDF_DEBUG
487 #if 0
488 List<node> rnodes;
489 m_C->rootCluster()->getClusterNodes(rnodes);
490
491 for(node v : rnodes) {
492 std::cout << "Root bag index: "<<bagIndex(v, m_C->rootCluster())<<"\n";
493 std::cout << "Indy bag index: "<<m_indyBagNumber[v]<<"\n";
494 }
495 #endif
496
497 Skiplist<int*> ibind;
498 for(node v : G.nodes)
499 {
500 int i = m_indyBagNumber[v];
501 if (!ibind.isElement(&i))
502 {
503 ibind.add(new int(i));
504 }
505 std::cout << "numIndyBags: "<<m_numIndyBags<<" i: "<<i<<"\n";
506 OGDF_ASSERT(i != DefaultIndex);
507 OGDF_ASSERT(i >= 0);
508 OGDF_ASSERT(i < m_numIndyBags);
509
510 }
511 int storedBags = 0;
512 SkiplistIterator<int*> its = ibind.begin();
513 while (its.valid())
514 {
515 storedBags++;
516 delete *its;
517 ++its;
518 }
519 Logger::slout() << m_numIndyBags<< " independent bags detected, "<<storedBags<<" stored\n";
520 OGDF_ASSERT(m_numIndyBags==storedBags);
521 #endif
522 }
523
524 //compute bag affiliation for all vertices
525 //store result in m_bagindex
computeBags()526 void ClusterAnalysis::computeBags() {
527 const Graph &G = m_C->constGraph();
528
529 // Storage structure for results
530 m_bagindex.init(G);
531 // We use Union-Find for chunks and bags
532 DisjointSets<> uf;
533 NodeArray<int> setid(G); // Index mapping for union-find
534 #if 0
535 node* nn = new node[G.numberOfNodes()]; // dito
536 #endif
537
538 // Every cluster gets its index
539 ClusterArray<int> cind(*m_C);
540 // We store the lists of cluster vertices
541 List<node>* clists = new List<node>[m_C->numberOfClusters()];
542 int i = 0;
543
544 // Store index and detect the current leaf clusters
545 List<cluster> ccleafs;
546 ClusterArray<int> unprocessedChildren(*m_C); //processing below: compute bags
547 for(cluster c : m_C->clusters)
548 {
549 cind[c] = i++;
550 if (c->cCount() == 0) ccleafs.pushBack(c);
551 unprocessedChildren[c] = c->cCount();
552 }
553
554
555 // Now we run through all vertices, storing them in the parent lists,
556 // at the same time, we initialize m_bagindex
557 for(node v : G.nodes)
558 {
559 // setid is constant in the following
560 setid[v] = uf.makeSet();
561 // Each vertex v gets its own ClusterArray that stores v's bag index per cluster.
562 // See comment on use of ClusterArrays above
563 m_bagindex[v] = new ClusterArray<int>(*m_C,DefaultIndex, m_C->maxClusterIndex()+1);//m_C->numberOfClusters());
564 cluster c = m_C->clusterOf(v);
565 // Push vertices in parent list
566 clists[cind[c]].pushBack(v);
567 }
568
569 // Now each clist contains the direct vertex descendants
570 // We process the clusters bottom-up, compute the chunks
571 // of the leafs first. At each level, for a cluster the
572 // vertex lists of all children are concatenated
573 // (could improve this by having an array of size(#leafs)
574 // and concatenating only at child1), then the bags are
575 // updated as follows: chunks may be linked by exactly
576 // the edges with lca(c) ie the ones in m_lcaEdges[c],
577 // and bags may be built by direct child clusters that join chunks.
578 // While concatenating the vertex lists, we can check
579 // for the vertices in each child if the uf number is the same
580 // as the one of a first initial vertex, otherwise we join.
581
582 // First, lowest level clusters are processed: All chunks are bags
583
584
585 OGDF_ASSERT(!ccleafs.empty());
586
587 while (!ccleafs.empty()){
588 const cluster c = ccleafs.popFrontRet();
589 Skiplist<int*> cbags; //Stores bag indexes ocurring in c
590
591 auto storeResult = [&] {
592 for (node v : clists[cind[c]]) {
593 int theid = uf.find(setid[v]);
594 (*m_bagindex[v])[c] = theid;
595 if (!cbags.isElement(&theid)) {
596 cbags.add(new int(theid));
597 }
598 // push into list of outer active vertices
599 if (m_storeoalists && isOuterActive(v, c)) {
600 (*m_oalists)[c].pushBack(v);
601 }
602 }
603 (*m_bags)[c] = cbags.size(); // store number of bags of c
604 };
605
606 if (m_storeoalists){
607 //no outeractive vertices detected so far
608 (*m_oalists)[c].clear();
609 }
610
611 //process leafs separately
612 if (c->cCount() == 0) {
613
614
615 //Todo could use lcaEdges list here too, see below
616 for (node u : c->nodes)
617 {
618 for(adjEntry adj : u->adjEntries) {
619 node w = adj->twinNode();
620 if (m_C->clusterOf(w) == c)
621 {
622 uf.link(uf.find(setid[u]),uf.find(setid[w]));
623 }
624 }
625 }
626 // Now all chunks in the leaf cluster are computed
627 // update for parent is done in the else case
628
629 storeResult();
630 }
631 else {
632 // ?We construct the vertex list by concatenating
633 // ?the lists of the children to the current list.
634 // We need the lists for storing the results efficiently.
635 // (Should be slightly faster than to call clusterNodes each time)
636 // Bags are either links of chunks by edges with lca==c
637 // or links of chunk by child clusters.
638 // Edge links
639 for(edge e : (*m_lcaEdges)[c]) {
640 uf.link(uf.find(setid[e->source()]),uf.find(setid[e->target()]));
641 }
642
643 // Cluster links
644 for(cluster cc : c->children)
645 {
646 //Initial id per child cluster cc: Use value of first
647 //vertex, each time we encounter a different value in cc,
648 //we link the chunks
649
650 //add (*itcc)'s vertices to c's list
651 ListConstIterator<node> itvc = clists[cind[cc]].begin();
652 int inid;
653 if (itvc.valid()) inid = uf.find(setid[*itvc]);
654 while (itvc.valid())
655 {
656 int theid = uf.find(setid[*itvc]);
657
658 if (theid != inid)
659 uf.link(inid,theid);
660 clists[cind[c]].pushBack(*itvc);
661 ++itvc;
662 }
663 }
664
665 storeResult();
666 }
667 // Now we update the status of the parent cluster and,
668 // in case all its children are processed, add it to
669 // the process queue.
670 if (c != m_C->rootCluster())
671 {
672 OGDF_ASSERT(unprocessedChildren[c->parent()] > 0);
673 unprocessedChildren[c->parent()]--;
674 if (unprocessedChildren[c->parent()] == 0) ccleafs.pushBack(c->parent());
675 }
676 }
677
678 // clean up
679 delete[] clists;
680 }
681
682
683 }
684