Prefer to use VS2013 for compiling and testing on AppVeyor
[TortoiseGit.git] / ext / OGDF / src / cluster / CconnectClusterPlanar.cpp
blobe812571d832e6e9801814fa439e5459f83a09d4d
1 /*
2 * $Revision: 2599 $
4 * last checkin:
5 * $Author: chimani $
6 * $Date: 2012-07-15 22:39:24 +0200 (So, 15. Jul 2012) $
7 ***************************************************************/
9 /** \file
10 * \brief Implementation of cluster planarity tests and cluster
11 * planar embedding for c-connected clustered graphs. Based on
12 * the algorithm by Cohen, Feng and Eades which uses PQ-trees.
14 * \author Sebastian Leipert
16 * \par License:
17 * This file is part of the Open Graph Drawing Framework (OGDF).
19 * \par
20 * Copyright (C)<br>
21 * See README.txt in the root directory of the OGDF installation for details.
23 * \par
24 * This program is free software; you can redistribute it and/or
25 * modify it under the terms of the GNU General Public License
26 * Version 2 or 3 as published by the Free Software Foundation;
27 * see the file LICENSE.txt included in the packaging of this file
28 * for details.
30 * \par
31 * This program is distributed in the hope that it will be useful,
32 * but WITHOUT ANY WARRANTY; without even the implied warranty of
33 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34 * GNU General Public License for more details.
36 * \par
37 * You should have received a copy of the GNU General Public
38 * License along with this program; if not, write to the Free
39 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
40 * Boston, MA 02110-1301, USA.
42 * \see http://www.gnu.org/copyleft/gpl.html
43 ***************************************************************/
46 #include <ogdf/cluster/CconnectClusterPlanar.h>
47 #include <ogdf/basic/simple_graph_alg.h>
48 #include <ogdf/basic/extended_graph_alg.h>
50 namespace ogdf {
52 // Constructor
53 CconnectClusterPlanar::CconnectClusterPlanar()
55 ogdf::strcpy(errorCode,124,"\0");
56 m_errorCode = none;
59 // Destructor
60 CconnectClusterPlanar::~CconnectClusterPlanar()
64 // Tests if a ClusterGraph is C-planar
65 // Specifies reason for non planarity
66 bool CconnectClusterPlanar::call(ClusterGraph &C, char (&code)[124])
68 bool cPlanar = call(C);
69 strcpy(code,124,errorCode);
70 return cPlanar;
73 // Tests if a ClusterGraph is C-planar
74 bool CconnectClusterPlanar::call(ClusterGraph &C)
76 Graph G;
77 ClusterGraph Cp(C,G);
78 OGDF_ASSERT(Cp.consistencyCheck());
81 m_clusterPQTree.init(Cp,0);
83 bool cPlanar = preProcess(Cp,G);
85 m_parallelEdges.init();
86 m_isParallel.init();
87 m_clusterPQTree.init();
89 return cPlanar;
94 // Tests if a ClusterGraph is C-planar
95 bool CconnectClusterPlanar::call(const ClusterGraph &C)
97 Graph G;
98 ClusterGraph Cp(C,G);
99 OGDF_ASSERT(Cp.consistencyCheck());
100 OGDF_ASSERT(&G == &(Graph&) Cp);
102 m_clusterPQTree.init(Cp,0);
105 bool cPlanar = preProcess(Cp,G);
107 m_parallelEdges.init();
108 m_isParallel.init();
109 m_clusterPQTree.init();
111 return cPlanar;
117 // CallTree:
119 // call(ClusterGraph &C)
121 // preProcess(ClusterGraph &C,Graph &G)
123 // planarityTest(ClusterGraph &C,cluster &act,Graph &G)
125 // foreach ChildCluster
126 // planarityTest(ClusterGraph &C,cluster &act,Graph &G)
128 // preparation(Graph &G,cluster &cl)
130 // foreach biconnected Component
131 // doTest(Graph &G,NodeArray<int> &numbering,cluster &cl)
139 bool CconnectClusterPlanar::preProcess(ClusterGraph &C,Graph &G)
141 if (!isCConnected(C))
143 ogdf::sprintf(errorCode,124,"Graph is not C-connected \n");
144 m_errorCode = nonCConnected;
145 return false;
148 if (!isPlanar(C))
150 ogdf::sprintf(errorCode,124,"Graph is not planar\n");
151 m_errorCode = nonPlanar;
152 return false;
155 cluster c;
157 SListPure<node> selfLoops;
158 makeLoopFree(G,selfLoops);
160 c = C.rootCluster();
162 bool cPlanar = planarityTest(C,c,G);
164 return cPlanar;
169 // Recursive call for testing c-planarity of the clustered graph
170 // that is induced by cluster act
171 bool CconnectClusterPlanar::planarityTest(
172 ClusterGraph &C,
173 cluster &act,
174 Graph &G)
176 // Test children first
177 ListConstIterator<cluster> it;
178 for (it = act->cBegin(); it.valid();)
180 ListConstIterator<cluster> succ = it.succ();
181 cluster next = (*it);
182 if (!planarityTest(C,next,G))
183 return false;
184 it = succ;
187 // Get induced subgraph of cluster act and test it for planarity
189 List<node> subGraphNodes;
190 ListIterator<node> its;
191 for (its = act->nBegin(); its.valid(); its++)
192 subGraphNodes.pushBack(*its);
194 Graph subGraph;
195 NodeArray<node> table;
196 inducedSubGraph(G,subGraphNodes.begin(),subGraph,table);
199 // Introduce super sink and add edges corresponding
200 // to outgoing edges of the cluster
202 node superSink = subGraph.newNode();
203 EdgeArray<node> outgoingTable(subGraph,0);
207 for (its = act->nBegin(); its.valid(); its++)
209 node w = (*its);
210 adjEntry adj = w->firstAdj();
211 forall_adj(adj,w)
213 edge e = adj->theEdge();
214 edge cor = 0;
215 if (table[e->source()] == 0) // edge is connected to a node outside the cluster
217 cor = subGraph.newEdge(table[e->target()],superSink);
218 outgoingTable[cor] = e->source();
220 else if (table[e->target()] == 0) // dito
222 cor = subGraph.newEdge(table[e->source()],superSink);
223 outgoingTable[cor] = e->target();
226 // else edge connects two nodes of the cluster
229 if (superSink->degree() == 0) // root cluster is not connected to outside clusters
231 subGraph.delNode(superSink);
232 superSink = 0;
236 bool cPlanar = preparation(subGraph,act,superSink);
239 if (cPlanar && act != C.rootCluster())
241 // Remove induced subgraph and the cluster act.
242 // Replace it by a wheel graph
243 while (!subGraphNodes.empty())
245 node w = subGraphNodes.popFrontRet();
246 // C.unassignNode(w);
247 G.delNode(w);
250 cluster parent = act->parent();
252 if (superSink && m_clusterPQTree[act])
253 constructWheelGraph(C,G,parent,m_clusterPQTree[act],outgoingTable);
255 C.delCluster(act);
256 if (m_clusterPQTree[act] != 0) // if query necessary for clusters with just one child
258 m_clusterPQTree[act]->emptyAllPertinentNodes();
259 delete m_clusterPQTree[act];
263 else if (!cPlanar)
265 ogdf::sprintf(errorCode,124,"Graph is not planar at cluster %d.\n",act->index());
266 m_errorCode = nonCPlanar;
267 }//if not cplanar
269 return cPlanar;
276 void CconnectClusterPlanar::constructWheelGraph(ClusterGraph &C,
277 Graph &G,
278 cluster &parent,
279 PlanarPQTree* T,
280 EdgeArray<node> &outgoingTable)
283 const PQNode<edge,IndInfo*,bool>* root = T->root();
284 const PQNode<edge,IndInfo*,bool>* checkNode = 0;
286 Queue<const PQNode<edge,IndInfo*,bool>*> treeNodes;
287 treeNodes.append(root);
289 node correspond = G.newNode(); // Corresponds to the root node.
290 // root node is either a leaf or a P-node
291 C.reassignNode(correspond,parent);
293 Queue<node> graphNodes;
294 graphNodes.append(correspond);
296 node hub;
297 node next = 0;
298 node pre;
299 node newNode; // corresponds to anchor of a hub or a cut node
303 while (!treeNodes.empty())
305 checkNode = treeNodes.pop();
306 correspond = graphNodes.pop();
308 PQNode<edge,IndInfo*,bool>* firstSon = 0;
309 PQNode<edge,IndInfo*,bool>* nextSon = 0;
310 PQNode<edge,IndInfo*,bool>* oldSib = 0;
311 PQNode<edge,IndInfo*,bool>* holdSib = 0;
314 if (checkNode->type() == PQNodeRoot::PNode)
316 // correspond is a cut node
318 OGDF_ASSERT(checkNode->referenceChild())
319 firstSon = checkNode->referenceChild();
321 if (firstSon->type() != PQNodeRoot::leaf)
323 treeNodes.append(firstSon);
324 newNode = G.newNode();
325 C.reassignNode(newNode,parent);
326 graphNodes.append(newNode);
327 G.newEdge(correspond,newNode);
329 else
331 // insert Edge to the outside
332 PQLeaf<edge,IndInfo*,bool>* leaf =
333 (PQLeaf<edge,IndInfo*,bool>*) firstSon;
334 edge f = leaf->getKey()->m_userStructKey;
335 //node x = outgoingTable[f];
336 G.newEdge(correspond,outgoingTable[f]);
337 delete leaf->getKey();
340 nextSon = firstSon->getNextSib(oldSib);
341 oldSib = firstSon;
342 pre = next;
343 while (nextSon && nextSon != firstSon)
345 if (nextSon->type() != PQNodeRoot::leaf)
347 treeNodes.append(nextSon);
348 newNode = G.newNode(); // new node corresponding to anchor
349 // or cutnode
350 C.reassignNode(newNode,parent);
351 graphNodes.append(newNode);
352 G.newEdge(correspond,newNode);
354 else
356 // insert Edge to the outside
357 PQLeaf<edge,IndInfo*,bool>* leaf =
358 (PQLeaf<edge,IndInfo*,bool>*) nextSon;
359 edge f = leaf->getKey()->m_userStructKey;
360 //node x = outgoingTable[f];
361 G.newEdge(correspond,outgoingTable[f]);
362 delete leaf->getKey();
364 holdSib = nextSon->getNextSib(oldSib);
365 oldSib = nextSon;
366 nextSon = holdSib;
370 else if (checkNode->type() == PQNodeRoot::QNode)
372 // correspond is the anchor of a hub
373 OGDF_ASSERT(checkNode->getEndmost(PQNodeRoot::LEFT))
374 firstSon = checkNode->getEndmost(PQNodeRoot::LEFT);
376 hub = G.newNode();
377 C.reassignNode(hub,parent);
378 G.newEdge(hub,correspond); // link anchor and hub
379 next = G.newNode(); // for first son
380 C.reassignNode(next,parent);
381 G.newEdge(hub,next);
382 G.newEdge(correspond,next);
384 if (firstSon->type() != PQNodeRoot::leaf)
386 treeNodes.append(firstSon);
387 newNode = G.newNode();
388 C.reassignNode(newNode,parent);
389 graphNodes.append(newNode);
390 G.newEdge(next,newNode);
392 else
394 // insert Edge to the outside
395 PQLeaf<edge,IndInfo*,bool>* leaf =
396 (PQLeaf<edge,IndInfo*,bool>*) firstSon;
397 edge f = leaf->getKey()->m_userStructKey;
398 //node x = outgoingTable[f];
399 G.newEdge(next,outgoingTable[f]);
400 delete leaf->getKey();
403 nextSon = firstSon->getNextSib(oldSib);
404 oldSib = firstSon;
405 pre = next;
406 while (nextSon)
408 next = G.newNode();
409 C.reassignNode(next,parent);
410 G.newEdge(hub,next);
411 G.newEdge(pre,next);
412 if (nextSon->type() != PQNodeRoot::leaf)
414 treeNodes.append(nextSon);
415 newNode = G.newNode(); // new node corresponding to anchor
416 // or cutnode
417 C.reassignNode(newNode,parent);
418 graphNodes.append(newNode);
420 G.newEdge(next,newNode);
422 else
424 // insert Edge to the outside
425 PQLeaf<edge,IndInfo*,bool>* leaf =
426 (PQLeaf<edge,IndInfo*,bool>*) nextSon;
427 edge f = leaf->getKey()->m_userStructKey;
428 G.newEdge(next,outgoingTable[f]);
429 delete leaf->getKey();
431 holdSib = nextSon->getNextSib(oldSib);
432 oldSib = nextSon;
433 nextSon = holdSib;
434 pre = next;
437 G.newEdge(next,correspond);
441 OGDF_ASSERT(C.consistencyCheck());
449 // Prepare planarity test for one cluster
451 bool CconnectClusterPlanar::preparation(Graph &G,
452 cluster &cl,
453 node superSink)
456 node v;
457 edge e;
458 int bcIdSuperSink = -1; // ID of biconnected component that contains superSink
459 // Initialization with -1 necessary for assertion
460 bool cPlanar = true;
463 NodeArray<node> tableNodes(G,0);
464 EdgeArray<edge> tableEdges(G,0);
465 NodeArray<bool> mark(G,0);
467 EdgeArray<int> componentID(G);
470 // Determine Biconnected Components
471 int bcCount = biconnectedComponents(G,componentID);
473 // Determine edges per biconnected component
474 Array<SList<edge> > blockEdges(0,bcCount-1);
475 forall_edges(e,G)
477 blockEdges[componentID[e]].pushFront(e);
480 // Determine nodes per biconnected component.
481 Array<SList<node> > blockNodes(0,bcCount-1);
482 for (int i = 0; i < bcCount; i++)
484 SListIterator<edge> it;
485 for (it = blockEdges[i].begin(); it.valid(); ++it)
487 e = *it;
488 if (!mark[e->source()])
490 blockNodes[i].pushBack(e->source());
491 mark[e->source()] = true;
493 if (!mark[e->target()])
495 blockNodes[i].pushBack(e->target());
496 mark[e->target()] = true;
499 if (superSink && mark[superSink])
501 OGDF_ASSERT(bcIdSuperSink == -1);
502 bcIdSuperSink = i;
504 SListIterator<node> itn;
505 for (itn = blockNodes[i].begin(); itn.valid(); ++itn)
507 v = *itn;
508 if (mark[v])
509 mark[v] = false;
510 else
512 OGDF_ASSERT(mark[v]); // v has been placed two times on the list.
519 // Perform planarity test for every biconnected component
521 if (bcCount == 1)
523 // Compute st-numbering
524 NodeArray<int> numbering(G,0);
525 int n;
526 if (superSink)
527 n = stNumber(G,numbering,0,superSink);
528 else
529 n = stNumber(G,numbering);
530 OGDF_ASSERT_IF(dlConsistencyChecks,testSTnumber(G,numbering,n))
533 EdgeArray<edge> backTableEdges(G,0);
534 forall_edges(e,G)
535 backTableEdges[e] = e;
537 cPlanar = doTest(G,numbering,cl,superSink,backTableEdges);
539 else
541 for (int i = 0; i < bcCount; i++)
543 #ifdef OGDF_DEBUG
544 if(int(ogdf::debugLevel)>=int(dlHeavyChecks)){
545 cout<<endl<<endl<<"-----------------------------------";
546 cout<<endl<<endl<<"Component "<<i<<endl;}
547 #endif
549 Graph C;
551 SListIterator<node> itn;
552 for (itn = blockNodes[i].begin(); itn.valid(); ++ itn)
554 v = *itn;
555 node w = C.newNode();
556 tableNodes[v] = w;
558 #ifdef OGDF_DEBUG
559 if(int(ogdf::debugLevel)>=int(dlHeavyChecks)){
560 cout <<"Original: " << v << " New: " << w<< endl;}
561 #endif
565 NodeArray<node> backTableNodes(C,0);
567 SListIterator<edge> it;
568 for (it = blockEdges[i].begin(); it.valid(); ++it)
570 e = *it;
571 edge f = C.newEdge(tableNodes[e->source()],tableNodes[e->target()]);
572 tableEdges[e] = f;
575 EdgeArray<edge> backTableEdges(C,0);
576 for (it = blockEdges[i].begin(); it.valid(); ++it)
577 backTableEdges[tableEdges[*it]] = *it;
579 // Compute st-numbering
580 NodeArray<int> numbering(C,0);
581 int n;
582 if (bcIdSuperSink == i)
584 n = stNumber(C,numbering,0,tableNodes[superSink]);
585 OGDF_ASSERT_IF(dlConsistencyChecks,testSTnumber(C,numbering,n))
586 cPlanar = doTest(C,numbering,cl,tableNodes[superSink],backTableEdges);
588 else
590 n = stNumber(C,numbering);
591 OGDF_ASSERT_IF(dlConsistencyChecks,testSTnumber(C,numbering,n))
592 cPlanar = doTest(C,numbering,cl,0,backTableEdges);
595 if (!cPlanar)
596 break;
604 return cPlanar;
608 // Performs a planarity test on a biconnected component
609 // of G. numbering contains an st-numbering of the component.
610 bool CconnectClusterPlanar::doTest(
611 Graph &G,
612 NodeArray<int> &numbering,
613 cluster &cl,
614 node superSink,
615 EdgeArray<edge> &edgeTable)
617 node v;
618 bool cPlanar = true;
620 NodeArray<SListPure<PlanarLeafKey<IndInfo*>* > > inLeaves(G);
621 NodeArray<SListPure<PlanarLeafKey<IndInfo*>* > > outLeaves(G);
622 Array<node> table(G.numberOfNodes()+1);
624 forall_nodes(v,G)
626 edge e;
627 forall_adj_edges(e,v)
629 if (numbering[e->opposite(v)] > numbering[v])
630 //sideeffect: loops are ignored
632 PlanarLeafKey<IndInfo*>* L = OGDF_NEW PlanarLeafKey<IndInfo*>(e);
633 inLeaves[v].pushFront(L);
636 table[numbering[v]] = v;
639 forall_nodes(v,G)
641 SListIterator<PlanarLeafKey<IndInfo*>* > it;
642 for (it = inLeaves[v].begin(); it.valid(); ++it)
644 PlanarLeafKey<IndInfo*>* L = *it;
645 outLeaves[L->userStructKey()->opposite(v)].pushFront(L);
649 PlanarPQTree* T = new PlanarPQTree();
651 T->Initialize(inLeaves[table[1]]);
652 for (int i = 2; i < G.numberOfNodes(); i++)
654 if (T->Reduction(outLeaves[table[i]]))
656 T->ReplaceRoot(inLeaves[table[i]]);
657 T->emptyAllPertinentNodes();
660 else
662 cPlanar = false;
663 break;
666 if (cPlanar && cl && superSink)
668 // Keep the PQTree to construct a wheelgraph
669 // Replace the edge stored in the keys of T
670 // by the original edges.
671 // Necessary, since the edges currently in T
672 // correspond to a graph that mirrors a biconnected
673 // component and thus is deallocated
675 SListIterator<PlanarLeafKey<IndInfo*>* > it;
676 int n = G.numberOfNodes();
678 for (it = outLeaves[table[n]].begin(); it.valid(); ++it)
680 PQLeafKey<edge,IndInfo*,bool>* key = (PQLeafKey<edge,IndInfo*,bool>*) *it;
681 key->m_userStructKey = edgeTable[key->m_userStructKey];
684 m_clusterPQTree[cl] = T;
687 else //if (cPlanar)
688 delete T;
690 // Cleanup
691 forall_nodes(v,G)
693 if (v != superSink || !cPlanar)
695 while (!outLeaves[v].empty())
697 PlanarLeafKey<IndInfo*>* L = outLeaves[v].popFrontRet();
698 delete L;
703 return cPlanar;
708 void CconnectClusterPlanar::prepareParallelEdges(Graph &G)
711 edge e;
713 // Stores for one reference edge all parallel edges.
714 m_parallelEdges.init(G);
715 // Is true for any multiedge, except for the reference edge.
716 m_isParallel.init(G,false);
717 getParallelFreeUndirected(G,m_parallelEdges);
718 m_parallelCount = 0;
719 forall_edges(e,G)
721 if (!m_parallelEdges[e].empty())
723 ListIterator<edge> it;
724 for (it = m_parallelEdges[e].begin(); it.valid(); it++)
726 m_isParallel[*it] = true;
727 m_parallelCount++;
736 } // end namespace ogdf