1 /** \file
2  * \brief implementation of the class BoyerMyrvoldPlanar
3  *
4  * \author Jens Schmidt
5  *
6  * \par License:
7  * This file is part of the Open Graph Drawing Framework (OGDF).
8  *
9  * \par
10  * Copyright (C)<br>
11  * See README.md in the OGDF root directory for details.
12  *
13  * \par
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * Version 2 or 3 as published by the Free Software Foundation;
17  * see the file LICENSE.txt included in the packaging of this file
18  * for details.
19  *
20  * \par
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * \par
27  * You should have received a copy of the GNU General Public
28  * License along with this program; if not, see
29  * http://www.gnu.org/copyleft/gpl.html
30  */
31 
32 
33 #include <ogdf/planarity/boyer_myrvold/BoyerMyrvoldPlanar.h>
34 #include <ogdf/planarity/boyer_myrvold/BoyerMyrvoldInit.h>
35 #include <ogdf/planarity/boyer_myrvold/FindKuratowskis.h>
36 
37 
38 namespace ogdf {
39 
40 const int BoyerMyrvoldPlanar::DirectionCCW = 0;
41 const int BoyerMyrvoldPlanar::DirectionCW  = 1;
42 
43 // constructor
BoyerMyrvoldPlanar(Graph & g,bool bundles,int embeddingGrade,bool limitStructures,SListPure<KuratowskiStructure> & output,double randomness,bool avoidE2Minors,bool extractSubgraph,const EdgeArray<int> * edgeCosts)44 BoyerMyrvoldPlanar::BoyerMyrvoldPlanar(
45 	Graph& g,
46 	bool bundles,
47 	int embeddingGrade, // see enumeration EmbeddingGrade for options
48 	bool limitStructures, // limits number of structures to embeddingGrade
49 	SListPure<KuratowskiStructure>& output,
50 	double randomness, // creates a random DFS-Tree, if in [0,1[
51 	                   // a value of 1 will always chose the edges with highest cost not be deleted
52 	bool avoidE2Minors, // avoids multiple identical minors (type AE2/E2)
53 	bool extractSubgraph, // will extract planar subgraph
54 	const EdgeArray<int> *edgeCosts) // costs for removing each edge
55 :
56 m_g(g),
57 	m_bundles(bundles),
58 	m_embeddingGrade(embeddingGrade),
59 	m_limitStructures(limitStructures),
60 	m_randomness(randomness),
61 	m_avoidE2Minors(avoidE2Minors),
62 	m_edgeCosts(edgeCosts),
63 	m_extractSubgraph(extractSubgraph),
64 
65 	// BoyerMyrvoldInit members
66 	m_realVertex(g,nullptr),
67 	m_dfi(g,0),
68 	m_nodeFromDFI(-g.numberOfNodes(),g.numberOfNodes(),nullptr),
69 	m_adjParent(g,nullptr),
70 	m_leastAncestor(g), // doesn't need initialization
71 	m_edgeType(g,BoyerMyrvoldEdgeType::Undefined),
72 	m_lowPoint(g), // doesn't need initialization
73 	m_separatedDFSChildList(g),
74 	m_pNodeInParent(g), // doesn't need initialization
75 
76 	// Walkup & Walkdown members
77 	m_visited(g,0),
78 	m_flipped(g,false),
79 	m_backedgeFlags(g),
80 	m_pertinentRoots(g),
81 	m_output(output)
82 {
83 	m_rand.seed(rand());
84 	m_link[BoyerMyrvoldPlanar::DirectionCCW].init(g,nullptr);
85 	m_link[BoyerMyrvoldPlanar::DirectionCW].init(g,nullptr);
86 	m_beforeSCE[BoyerMyrvoldPlanar::DirectionCCW].init(g,nullptr);
87 	m_beforeSCE[BoyerMyrvoldPlanar::DirectionCW].init(g,nullptr);
88 	m_output.clear();
89 	// apply this only, if FIND-procedure will be called
90 	if (m_embeddingGrade > EmbeddingGrade::doNotFind) {
91 		m_pointsToRoot.init(g,nullptr);
92 		m_visitedWithBackedge.init(g,nullptr);
93 		m_numUnembeddedBackedgesInBicomp.init(g,0);
94 		m_highestSubtreeDFI.init(g); // doesn't need initialization
95 	}
96 	m_flippedNodes = 0;
97 }
98 
99 
100 // walk upon external face in the given direction, see getSucessorOnExternalFace
101 // the difference is, that all inactive vertices are skipped, i.e. the returned node
102 // is active in relation to the node with dfi v
103 // in the special case of degree-one nodes the direction is not changed
104 // info returns the dynamic nodetype of the endnode
activeSuccessor(node w,int & direction,int v,int & info) const105 node BoyerMyrvoldPlanar::activeSuccessor(node w, int& direction, int v, int& info) const
106 {
107 	OGDF_ASSERT(w!=nullptr);
108 	OGDF_ASSERT(w->degree()>0);
109 	OGDF_ASSERT(m_link[BoyerMyrvoldPlanar::DirectionCW][w]!=nullptr);
110 	OGDF_ASSERT(m_link[BoyerMyrvoldPlanar::DirectionCCW][w]!=nullptr);
111 	node next;
112 	adjEntry adj;
113 
114 	do {
115 		adj = m_link[direction][w];
116 		next = adj->theNode();
117 		OGDF_ASSERT(next!=nullptr);
118 		OGDF_ASSERT(next->degree()>0);
119 		OGDF_ASSERT(m_link[BoyerMyrvoldPlanar::DirectionCW][next]!=nullptr);
120 		OGDF_ASSERT(m_link[BoyerMyrvoldPlanar::DirectionCCW][next]!=nullptr);
121 
122 		if (w->degree() > 1)
123 			direction = adj==beforeShortCircuitEdge(next,BoyerMyrvoldPlanar::DirectionCCW)->twin();
124 		w=next;
125 		info = infoAboutNode(next,v);
126 
127 	} while (info==0); // until not inactive
128 	return next;
129 }
130 
131 // merges adjEntries of virtual node w and associated real vertex x according to
132 // given outgoing directions x_dir and w_dir.
mergeBiconnectedComponent(ArrayBuffer<int> & stack)133 void BoyerMyrvoldPlanar::mergeBiconnectedComponent(ArrayBuffer<int>& stack) {
134 	bool doEmbed = m_embeddingGrade != EmbeddingGrade::doNotEmbed;
135 
136 	const int w_dir = stack.popRet(); // outgoing direction of w
137 	const int x_dir = stack.popRet(); // outgoing direction of x
138 	int tmp = stack.popRet();
139 	const node w = m_nodeFromDFI[tmp]; // virtual DFS-Successor of x
140 	const node w_child = m_nodeFromDFI[-tmp]; // real unique DFS-Child of bicomp with root w
141 	const node x = m_realVertex[w];
142 
143 	// set new external face neighbors and save adjEntry, where edges will be merged
144 	adjEntry mergeEntry = nullptr;
145 	Direction dir;
146 	if (doEmbed) {
147 		dir = (x_dir == BoyerMyrvoldPlanar::DirectionCCW) ? Direction::before : Direction::after;
148 		mergeEntry = beforeShortCircuitEdge(x, !x_dir)->twin();
149 	}
150 	m_link[!x_dir][x] = m_link[!w_dir][w];
151 	m_beforeSCE[!x_dir][x] = m_beforeSCE[!w_dir][w];
152 
153 	// merge real and virtual nodes, flip biconnected component root if neccesary
154 	// not neccesary if onlyPlanar
155 	OGDF_ASSERT(!m_flipped[w_child]);
156 	adjEntry adj = w->firstAdj();
157 	if (doEmbed) {
158 		if (x_dir == w_dir) {
159 			// if not flipped
160 			if (dir == Direction::after) {
161 				mergeEntry = mergeEntry->cyclicSucc();
162 				dir = Direction::before;
163 			}
164 		} else {
165 			// if flipped:
166 			// set unique DFS-child of associated bicomp root node to "flipped"
167 			m_flipped[w_child] = true;
168 			++m_flippedNodes;
169 			if (dir == Direction::before) {
170 				mergeEntry = mergeEntry->cyclicPred();
171 				dir = Direction::after;
172 			}
173 		}
174 	}
175 
176 	// merge adjEntries
177 	adjEntry temp;
178 	while (adj != nullptr) {
179 		temp = adj->succ();
180 		edge e = adj->theEdge();
181 		OGDF_ASSERT(e->source() != x);
182 		OGDF_ASSERT(e->target() != x);
183 		// this allows also self-loops when moving adjacency entries
184 		if (e->source() == w) {
185 			if (!doEmbed) { m_g.moveSource(e, x); }
186 			else { m_g.moveSource(e, mergeEntry, dir); }
187 		} else {
188 			if (!doEmbed) { m_g.moveTarget(e, x); }
189 			else { m_g.moveTarget(e, mergeEntry, dir); }
190 		}
191 		adj = temp;
192 	}
193 
194 	// remove w from pertinent roots of x
195 	OGDF_ASSERT( !doEmbed || !m_pertinentRoots[x].empty() );
196 	OGDF_ASSERT( m_pertinentRoots[x].front() == w );
197 	m_pertinentRoots[x].popFront();
198 
199 	// consider x's unique dfs-successor in pertinent bicomp:
200 	// remove this successor from separatedChildList of x using
201 	// saved pointer pNodeInParent in constant time
202 	OGDF_ASSERT( !doEmbed || !m_separatedDFSChildList[x].empty() );
203 	OGDF_ASSERT( m_pNodeInParent[w_child].valid() );
204 	m_separatedDFSChildList[x].del(m_pNodeInParent[w_child]);
205 
206 	// delete virtual vertex, it must not contain any edges any more
207 	OGDF_ASSERT( w->firstAdj() == nullptr );
208 	m_nodeFromDFI[m_dfi[w]] = nullptr;
209 	m_g.delNode(w);
210 }
211 
embedBackedges(const node v,const int v_dir,const node w,const int w_dir)212 void BoyerMyrvoldPlanar::embedBackedges(
213 	const node v,
214 	const int v_dir,
215 	const node w,
216 	const int w_dir)
217 {
218 	OGDF_ASSERT( v != nullptr );
219 	OGDF_ASSERT( w != nullptr );
220 	OGDF_ASSERT( !m_backedgeFlags[w].empty() );
221 	OGDF_ASSERT( m_link[BoyerMyrvoldPlanar::DirectionCCW][v] != nullptr );
222 	OGDF_ASSERT( m_link[BoyerMyrvoldPlanar::DirectionCW][v] != nullptr );
223 	OGDF_ASSERT( m_link[BoyerMyrvoldPlanar::DirectionCCW][w] != nullptr );
224 	OGDF_ASSERT( m_link[BoyerMyrvoldPlanar::DirectionCW][w] != nullptr );
225 
226 	bool doEmbed = m_embeddingGrade != EmbeddingGrade::doNotEmbed;
227 
228 	adjEntry mergeEntryV, mergeEntryW;
229 	Direction insertv, insertw;
230 	if (doEmbed) {
231 		// if one edge is a short circuit edge, compute the former underlying adjEntry
232 		// the adjEntry of v, used for inserting backedges
233 		mergeEntryV = beforeShortCircuitEdge(v, v_dir)->twin();
234 		insertv = (v_dir == BoyerMyrvoldPlanar::DirectionCCW) ? Direction::after : Direction::before;
235 		// the adjEntry of w, used for inserting backedges
236 		mergeEntryW = beforeShortCircuitEdge(w, !w_dir)->twin();
237 		insertw = (w_dir == BoyerMyrvoldPlanar::DirectionCCW) ? Direction::before : Direction::after;
238 
239 	}
240 
241 	// the first/last (last iff !doEmbed) backedge in the backedgeFlags-list will be the new external face adjEntry
242 	adjEntry saveBack = doEmbed ? m_backedgeFlags[w].front() : m_backedgeFlags[w].back();
243 	for (adjEntry adj: m_backedgeFlags[w]) {
244 		// embed backedge
245 		edge e = adj->theEdge();
246 		OGDF_ASSERT( e->isIncident(w) );
247 
248 		// insert backedge to v (and backedge to w iff doEmbed)
249 		if (e->source() == w) {
250 			if (doEmbed) {
251 				m_g.moveTarget(e, mergeEntryV, insertv);
252 				m_g.moveSource(e, mergeEntryW, insertw);
253 			} else {
254 				m_g.moveTarget(e, v);
255 			}
256 		} else {
257 			if (doEmbed) {
258 				m_g.moveSource(e, mergeEntryV, insertv);
259 				m_g.moveTarget(e, mergeEntryW, insertw);
260 			} else {
261 				m_g.moveSource(e, v);
262 			}
263 		}
264 	}
265 
266 	// set external face link for this backedge and delete out-dated short
267 	// circuit links
268 	m_link[v_dir][v] = saveBack->twin();
269 	m_beforeSCE[v_dir][v] = nullptr;
270 	m_link[!w_dir][w] = saveBack;
271 	m_beforeSCE[!w_dir][w] = nullptr;
272 
273 	// decrease counter of backedges per bicomp
274 	if (m_embeddingGrade > EmbeddingGrade::doNotFind) {
275 		node bicompRoot = m_pointsToRoot[m_backedgeFlags[w].front()->theEdge()];
276 		m_numUnembeddedBackedgesInBicomp[bicompRoot] -= m_backedgeFlags[w].size();
277 		OGDF_ASSERT( m_extractSubgraph || m_numUnembeddedBackedgesInBicomp[bicompRoot] >= 0 );
278 	}
279 
280 	// delete BackedgeFlags
281 	m_backedgeFlags[w].clear();
282 }
283 
284 
285 // create short circuit edge from node v with direction v_dir to node w with outgoing
286 // direction w_dir.
createShortCircuitEdge(const node v,const int v_dir,const node w,const int w_dir)287 void BoyerMyrvoldPlanar::createShortCircuitEdge(
288 	const node v,
289 	const int v_dir,
290 	const node w,
291 	const int w_dir)
292 {
293 	// save former neighbors
294 	if (m_beforeSCE[v_dir][v]==nullptr) m_beforeSCE[v_dir][v]=m_link[v_dir][v];
295 	if (m_beforeSCE[!w_dir][w]==nullptr) m_beforeSCE[!w_dir][w]=m_link[!w_dir][w];
296 	// set new short circuit edge
297 	adjEntry temp = m_beforeSCE[!w_dir][w]->twin();
298 	m_link[!w_dir][w] = m_beforeSCE[v_dir][v]->twin();
299 	m_link[v_dir][v] = temp;
300 }
301 
302 
303 // Walkup
304 // finds pertinent subgraph for descendant w of v.
305 // marks visited nodes with marker and returns the last traversed node.
walkup(const node v,const node w,const int marker,const edge back)306 node BoyerMyrvoldPlanar::walkup(
307 	const node v,
308 	const node w,
309 	const int marker,
310 	const edge back)
311 {
312 	const int i = m_dfi[v];
313 	node x = w;
314 	node y = w;
315 	node temp;
316 	int x_dir = BoyerMyrvoldPlanar::DirectionCW;
317 	int y_dir = BoyerMyrvoldPlanar::DirectionCCW;
318 
319 	while (m_visited[x]!=marker && m_visited[y]!=marker)
320 	{
321 		m_visited[x] = marker;
322 		m_visited[y] = marker;
323 		if (m_embeddingGrade > EmbeddingGrade::doNotFind) {
324 			m_visitedWithBackedge[x] = back;
325 			m_visitedWithBackedge[y] = back;
326 		}
327 
328 		// is x or y root vertex?
329 		if (m_realVertex[x] != nullptr) {
330 			temp=x;
331 		} else if (m_realVertex[y] != nullptr) {
332 			temp=y;
333 		} else temp=nullptr;
334 
335 		if (temp != nullptr) {
336 			// put pertinent root into the list of its non-virtual vertex.
337 			// the insert-position is either front or back of the list, this
338 			// depends on the external activity of the pertinent root's
339 			// biconnected component.
340 
341 			x = m_realVertex[temp];
342 			y = x;
343 
344 			OGDF_ASSERT(m_extractSubgraph || m_visited[x]==marker || m_pertinentRoots[x].empty());
345 			// push pertinent root
346 			if (m_lowPoint[m_nodeFromDFI[-m_dfi[temp]]] < i) {
347 				m_pertinentRoots[x].pushBack(temp);
348 			} else m_pertinentRoots[x].pushFront(temp);
349 			// found v, finish walkup and return last traversed node
350 			if (x==v) {
351 				m_visited[x] = marker;
352 				return temp;
353 			}
354 		} else {
355 			// traverse to external face successors
356 			x = successorOnExternalFace(x,x_dir);
357 			y = successorOnExternalFace(y,y_dir);
358 		}
359 	}
360 
361 	// return last traversed node
362 	return (m_visited[x] == marker) ? x : y;
363 }
364 
365 
366 // Walkdown
367 // for DFS-child w of the current processed vertex v': embed all backedges
368 // to the virtual node v of v'
369 // returns 1, iff the embedding process found a stopping configuration
walkdown(const int i,const node v,FindKuratowskis * findKuratowskis)370 int BoyerMyrvoldPlanar::walkdown(
371 	const int i, // dfi of rootvertex v'
372 	const node v, // v is virtual node of v'
373 	FindKuratowskis *findKuratowskis)
374 {
375 	ArrayBuffer<int> stack;
376 	node stopX = nullptr;
377 
378 	bool stoppingNodesFound = 0; // 0=false,1=true,2=break
379 
380 	// in both directions
381 	// j=current outgoing direction of current embedded node v
382 	for (int j: {BoyerMyrvoldPlanar::DirectionCCW, BoyerMyrvoldPlanar::DirectionCW}) {
383 		int w_dir = j; // direction of traversal of node w
384 
385 		node w = successorOnExternalFace(v,w_dir); // current node
386 
387 		while (w != v) {
388 			// assert, that CCW[] and CW[] return that adjEntry of the neighbor
389 			OGDF_ASSERT(beforeShortCircuitEdge(w,w_dir)->twinNode()==w);
390 
391 			// if backedgeFlag is set
392 			if (!m_backedgeFlags[w].empty()) {
393 				while (!stack.empty()) {
394 					mergeBiconnectedComponent(stack);
395 				}
396 				embedBackedges(v, j, w, w_dir);
397 			}
398 
399 			// if pertinentRoots of w is not empty
400 			if (!m_pertinentRoots[w].empty()) {
401 				// append pertinent root of w and direction of entry in w to stack
402 				// y is root of pertinent child bicomp
403 				node root = m_pertinentRoots[w].front();
404 
405 				if(m_extractSubgraph && root->degree() == 0) {
406 					m_pertinentRoots[w].popFront();
407 					continue;
408 				}
409 
410 				stack.push(m_dfi[root]);
411 
412 				// append outgoing direction of entry in w to stack
413 				OGDF_ASSERT(w->degree() > 0);
414 				stack.push(w_dir);
415 
416 				// get active successor in pertinent bicomp
417 				// variables for recognizing the right direction after descending to a bicomp
418 				int x_dir = BoyerMyrvoldPlanar::DirectionCCW;
419 				int y_dir = BoyerMyrvoldPlanar::DirectionCW;
420 				int infoX, infoY; // gives information about the type of endnode in that direction
421 				node x = activeSuccessor(root,x_dir,i,infoX);
422 				node y = activeSuccessor(root,y_dir,i,infoY);
423 
424 				OGDF_ASSERT(x != root);
425 				OGDF_ASSERT(y != root);
426 				createShortCircuitEdge(root,BoyerMyrvoldPlanar::DirectionCCW,x,x_dir);
427 				createShortCircuitEdge(root,BoyerMyrvoldPlanar::DirectionCW,y,y_dir);
428 
429 				// push counterclockwise resp. clockwise active successor
430 				// in pertinent bicomp
431 				if (infoX == infoY) {
432 					// if both attributes are externally active and non-pertinent,
433 					// save stopping nodes
434 					if (infoX==3) {
435 						if(!m_extractSubgraph) {
436 							OGDF_ASSERT(x != y);
437 							if (m_embeddingGrade <= EmbeddingGrade::doNotFind) return true;
438 						}
439 
440 						// extract Kuratowskis
441 						stoppingNodesFound = 1;
442 						if(!m_extractSubgraph) {
443 							// check, if we have found enough kuratowski structures
444 							if (m_embeddingGrade > 0 &&
445 									findKuratowskis->getAllKuratowskis().size() >= m_embeddingGrade) {
446 								return 2;
447 							}
448 							findKuratowskis->addKuratowskiStructure(m_nodeFromDFI[i],root,x,y);
449 						}
450 
451 						// go to the pertinent starting node on father bicomp
452 						stack.pop(); // delete new w_dir from stack
453 						w = m_realVertex[m_nodeFromDFI[stack.popRet()]]; // x itself
454 						// refresh pertinentRoots information
455 						m_pertinentRoots[w].popFront();
456 
457 						// if more pertinent child bicomps exist on the same root,
458 						// let the walkdown either embed it or find a new kuratowski structure
459 						while (!stack.empty() && !pertinent(w)) {
460 							// last real root
461 							node lastActiveNode = w;
462 
463 							// not in V-bicomp:
464 							// go to the unvisited active node on father bicomp
465 							w_dir = stack.popRet(); // outgoing direction of w
466 							x_dir = stack.popRet(); // outgoing direction of x
467 							w = m_nodeFromDFI[stack.top()]; // w, virtual node
468 
469 							node otherActiveNode = m_link[!w_dir][w]->theNode();
470 
471 							OGDF_ASSERT(otherActiveNode == constActiveSuccessor(w,!w_dir,i,infoX));
472 							OGDF_ASSERT(externallyActive(otherActiveNode,i));
473 							OGDF_ASSERT(lastActiveNode == m_link[w_dir][w]->theNode());
474 							if (pertinent(otherActiveNode)) {
475 								// push adapted information about actual bicomp in stack
476 								stack.push(x_dir);
477 								stack.push(!w_dir);
478 								// go on with walkdown on unvisited active node
479 								w_dir = !w_dir;
480 								break;
481 							} else {
482 								// delete old root
483 								stack.pop();
484 								// if there are two stopping vertices, that are not pertinent
485 								// there could be another kuratowski structure
486 								if (!m_extractSubgraph && lastActiveNode != otherActiveNode &&
487 										wNodesExist(w,lastActiveNode,otherActiveNode)) {
488 									// check, if we have found enough kuratowski structures
489 									if (m_embeddingGrade > 0 &&
490 											findKuratowskis->getAllKuratowskis().size() >= m_embeddingGrade) {
491 										return 2;
492 									}
493 									// different stopping nodes:
494 									// try to extract kuratowski structure and put the two
495 									// stopping nodes in the right traversal order
496 									if (w_dir==BoyerMyrvoldPlanar::DirectionCCW) {
497 										findKuratowskis->addKuratowskiStructure(m_nodeFromDFI[i],
498 														w,lastActiveNode,otherActiveNode);
499 									} else {
500 										findKuratowskis->addKuratowskiStructure(m_nodeFromDFI[i],
501 														w,otherActiveNode,lastActiveNode);
502 									}
503 								}
504 
505 								// refresh pertinentRoots information
506 								w = m_realVertex[w]; // x
507 								m_pertinentRoots[w].popFront();
508 								w_dir = x_dir;
509 							}
510 						}
511 					}
512 					// if both attributes are the same: minimize flips
513 					else if (w_dir==BoyerMyrvoldPlanar::DirectionCCW) {
514 						w = x;
515 						w_dir = x_dir;
516 						stack.push(BoyerMyrvoldPlanar::DirectionCCW);
517 
518 					} else {
519 						w = y;
520 						w_dir = y_dir;
521 						stack.push(BoyerMyrvoldPlanar::DirectionCW);
522 					}
523 				} else if (infoX <= infoY) {
524 					// push x
525 					w=x; w_dir=x_dir;
526 					stack.push(BoyerMyrvoldPlanar::DirectionCCW);
527 
528 				} else {
529 					// push y
530 					w=y; w_dir=y_dir;
531 					stack.push(BoyerMyrvoldPlanar::DirectionCW);
532 				}
533 
534 			} else if (inactive(w,i)) {
535 				// w is an inactive vertex
536 				w = successorOnExternalFace(w,w_dir);
537 
538 			} else {
539 				// w must be a stopping vertex
540 				OGDF_ASSERT(externallyActive(w,i));
541 				OGDF_ASSERT(m_lowPoint[m_nodeFromDFI[-m_dfi[v]]] < i);
542 
543 				// embed shortCircuitEdge
544 				/*if (stack.empty())*/ createShortCircuitEdge(v,j,w,w_dir);
545 
546 				// only save single stopping nodes, if we don't have already one
547 				if (j==BoyerMyrvoldPlanar::DirectionCCW) {
548 					stopX = w;
549 				} else if (w != stopX) {
550 					OGDF_ASSERT(stopX!=nullptr);
551 
552 					if (m_embeddingGrade <= EmbeddingGrade::doNotFind) return false;
553 					// check, if some backedges were not embedded (=> nonplanar)
554 					// note, that this is performed at most one time per virtual root
555 					if (m_numUnembeddedBackedgesInBicomp[v] > 0) {
556 						// some backedges are left on this bicomp
557 						stoppingNodesFound = 1;
558 						// check, if we have found enough kuratowski structures
559 						if(!m_extractSubgraph) {
560 							if (m_embeddingGrade > 0 &&
561 								findKuratowskis->getAllKuratowskis().size() >= m_embeddingGrade) {
562 								return 2;
563 							}
564 							findKuratowskis->addKuratowskiStructure(m_nodeFromDFI[i], v, stopX, w);
565 						}
566 					}
567 				}
568 				break;
569 			}
570 		}
571 
572 		stack.clear();
573 	}
574 
575 	return stoppingNodesFound;
576 }
577 
578 // embed graph m_g node by node in descending DFI-order beginning with dfi i
embed()579 bool BoyerMyrvoldPlanar::embed()
580 {
581 	bool nonplanar=false; // true, if graph is not planar
582 
583 #if 0
584 	FindKuratowskis findKuratowskis(this);
585 #else
586 	FindKuratowskis* findKuratowskis =
587 		(m_embeddingGrade <= EmbeddingGrade::doNotFind) ? nullptr : new FindKuratowskis(this);
588 #endif
589 
590 	for (int i = m_nodeFromDFI.high(); i >= 1; --i)
591 	{
592 		const node v = m_nodeFromDFI[i];
593 
594 		// call Walkup
595 		// for all sources of backedges of v: find pertinent subgraph
596 
597 		for(adjEntry adj : v->adjEntries) {
598 			node w = adj->twinNode(); // dfs-descendant of v
599 			edge e = adj->theEdge();
600 			if (m_dfi[w] > i && m_edgeType[e] == BoyerMyrvoldEdgeType::Back) {
601 				m_backedgeFlags[w].pushBack(adj);
602 
603 				node x = walkup(v,w,i,e);
604 				if (m_embeddingGrade <= EmbeddingGrade::doNotFind) continue;
605 
606 				// divide children bicomps
607 				if (m_realVertex[x] == v) {
608 					// x is a (virtual) root vertex.
609 					m_pointsToRoot[e] = x;
610 					OGDF_ASSERT(m_numUnembeddedBackedgesInBicomp[x] == 0);
611 				} else {
612 					// Set x to the (virtual) root of its bicomp.
613 					x = m_pointsToRoot[m_visitedWithBackedge[x]];
614 					m_pointsToRoot[e] = x;
615 					OGDF_ASSERT(m_numUnembeddedBackedgesInBicomp[x] >= 1);
616 				}
617 				// Increase the number of backedges leading to x's child bicomp.
618 				++m_numUnembeddedBackedgesInBicomp[x];
619 			}
620 		}
621 
622 		// call Walkdown
623 		// for every pertinent subtrees with children w of v as roots
624 		// embed all backedges to v
625 		SListPure<node>& pert(m_pertinentRoots[v]);
626 		while (!pert.empty()) {
627 			OGDF_ASSERT(pert.front()->degree()==1);
628 			int result = walkdown(i,pert.popFrontRet(),findKuratowskis);
629 			if (!m_extractSubgraph) {
630 				if(result == 2) {
631 					m_output = findKuratowskis->getAllKuratowskis();
632 					delete findKuratowskis;
633 					return false;
634 				} else if (result == 1) {
635 					// found stopping configuration
636 					nonplanar = true;
637 					if (m_embeddingGrade <= EmbeddingGrade::doNotFind) return false;
638 				}
639 			}
640 		}
641 
642 		// if !embed, check, if there are any backedges left
643 		if (!m_extractSubgraph && m_embeddingGrade <= EmbeddingGrade::doNotFind) {
644 			for(adjEntry adj : v->adjEntries) {
645 				if (m_edgeType[adj->theEdge()] == BoyerMyrvoldEdgeType::Back &&
646 						m_dfi[adj->twinNode()] > m_dfi[v])
647 				{
648 					delete findKuratowskis;
649 					return false; // nonplanar
650 				}
651 			}
652 		}
653 	}
654 
655 	// embed and flip bicomps, if necessary
656 	if (nonplanar) {
657 		if(findKuratowskis)
658 			m_output = findKuratowskis->getAllKuratowskis();
659 	} else
660 		postProcessEmbedding(); // flip graph and embed self-loops, etc.
661 
662 	delete findKuratowskis;
663 	return !nonplanar;
664 }
665 
666 
667 // merge unprocessed virtual nodes such as the dfs-roots
mergeUnprocessedNodes()668 void BoyerMyrvoldPlanar::mergeUnprocessedNodes()
669 {
670 	node v = m_g.firstNode();
671 	while (v) {
672 		node next = v->succ();
673 		if (m_dfi[v] < 0) {
674 			node w = m_realVertex[v];
675 			adjEntry adj = v->firstAdj();
676 			// copy all adjEntries to non-virtual node
677 			while (adj) {
678 				edge e = adj->theEdge();
679 				adj = adj->succ();
680 				if (e->source()==v) {
681 					m_g.moveSource(e,w);
682 				} else m_g.moveTarget(e,w);
683 			}
684 			m_nodeFromDFI[m_dfi[v]]=nullptr;
685 			m_g.delNode(v);
686 		}
687 		v = next;
688 	}
689 }
690 
691 
692 // flips all nodes of the bicomp with unique real root-child c as necessary.
693 // in addition all connected components with dfs-root c without virtual
694 // nodes are allowed. this function can be used to reverse the flip, too!
695 // marker has to be an non-existing int in array visited.
696 // if wholeGraph ist true, all bicomps of all connected components will be traversed.
697 // if deleteFlipFlags ist true, the flipping flags will be deleted after flipping
flipBicomp(int c,int marker,NodeArray<int> & visited,bool wholeGraph,bool deleteFlipFlags)698 void BoyerMyrvoldPlanar::flipBicomp(
699 	int c,
700 	int marker,
701 	NodeArray<int>& visited,
702 	bool wholeGraph,
703 	bool deleteFlipFlags)
704 {
705 	if (m_flippedNodes == 0) {
706 		if (wholeGraph) mergeUnprocessedNodes();
707 		return;
708 	}
709 
710 	ArrayBuffer<int> stack; // stack for dfs-traversal
711 
712 	if (wholeGraph) {
713 		mergeUnprocessedNodes();
714 		for (int i = 1; i <= m_g.numberOfNodes(); ++i)
715 			stack.push(-i);
716 	}
717 
718 	// flip bicomps, if the flipped-flag is set
719 	bool flip;
720 	stack.push(-c); // negative numbers: flip=false, otherwise flip=true
721 	while (!stack.empty()) {
722 		int stackTop = stack.popRet();
723 		node v;
724 		if (stackTop < 0) {
725 			flip = false;
726 			v = m_nodeFromDFI[-stackTop];
727 		} else {
728 			flip = true;
729 			v = m_nodeFromDFI[stackTop];
730 		}
731 		if (wholeGraph) {
732 			if (visited[v]==marker) continue;
733 			// mark visited nodes
734 			visited[v] = marker;
735 		}
736 
737 		// flip adjEntries of node, if necessary
738 		if (m_flipped[v]) {
739 			flip = !flip;
740 
741 			// don't do this, if all flips on nodes of this bicomp will be reversed
742 			if (deleteFlipFlags) {
743 				m_flipped[v] = false;
744 				--m_flippedNodes;
745 				OGDF_ASSERT(m_flippedNodes >= 0);
746 			}
747 		}
748 		if (flip) {
749 			m_g.reverseAdjEdges(v);
750 			if (deleteFlipFlags) {
751 				adjEntry tmp = m_link[BoyerMyrvoldPlanar::DirectionCCW][v];
752 				m_link[BoyerMyrvoldPlanar::DirectionCCW][v] = m_link[BoyerMyrvoldPlanar::DirectionCW][v];
753 				m_link[BoyerMyrvoldPlanar::DirectionCW][v] = tmp;
754 
755 				tmp = m_beforeSCE[BoyerMyrvoldPlanar::DirectionCCW][v];
756 				m_beforeSCE[BoyerMyrvoldPlanar::DirectionCCW][v] = m_beforeSCE[BoyerMyrvoldPlanar::DirectionCW][v];
757 				m_beforeSCE[BoyerMyrvoldPlanar::DirectionCW][v] = tmp;
758 			}
759 		}
760 
761 		// go along the dfs-edges
762 		for(adjEntry adj : v->adjEntries) {
763 			int temp = m_dfi[adj->twinNode()];
764 			OGDF_ASSERT(m_edgeType[adj->theEdge()] != BoyerMyrvoldEdgeType::Undefined);
765 			if (temp > m_dfi[v] && m_edgeType[adj->theEdge()]==BoyerMyrvoldEdgeType::Dfs) {
766 				stack.push(flip ? temp : -temp);
767 			}
768 		}
769 	}
770 }
771 
772 
773 // postprocess the embedding, so that all unprocessed virtual vertices are
774 // merged with their non-virtual counterpart. Furthermore all bicomps
775 // are flipped, if necessary and parallel edges and self-loops are embedded.
postProcessEmbedding()776 void BoyerMyrvoldPlanar::postProcessEmbedding()
777 {
778 	ArrayBuffer<int> stack; // stack for dfs-traversal
779 	node v,w;
780 	adjEntry adj;
781 	int temp;
782 
783 	mergeUnprocessedNodes();
784 
785 	// flip bicomps, if the flipped-flag is set, i.e. postprocessing in
786 	// reverse dfi-order
787 	bool flip;
788 	for(int i=1; i<=m_g.numberOfNodes(); ++i) {
789 		if (m_visited[m_nodeFromDFI[i]] == -1) continue;
790 		stack.push(-i); // negative numbers: flip=false, otherwise flip=true
791 
792 		while (!stack.empty()) {
793 			temp = stack.popRet();
794 			if (temp < 0) {
795 				flip=false;
796 				v = m_nodeFromDFI[-temp];
797 			} else {
798 				flip=true;
799 				v = m_nodeFromDFI[temp];
800 			}
801 			if (m_visited[v]==-1) continue;
802 			// mark visited nodes with visited[v]==-1
803 			m_visited[v] = -1;
804 
805 			// flip adjEntries of node, if necessary
806 			if (m_flipped[v]) {
807 				m_flipped[v] = false;
808 				flip = !flip;
809 			}
810 			if (flip) m_g.reverseAdjEdges(v);
811 
812 			adj=v->firstAdj();
813 			while (adj) {
814 				w = adj->twinNode();
815 				BoyerMyrvoldEdgeType tempType = m_edgeType[adj->theEdge()];
816 				if (tempType==BoyerMyrvoldEdgeType::Dfs) {
817 					// go along the dfs-edges
818 					stack.push(flip ? m_dfi[w] : -m_dfi[w]);
819 					adj=adj->succ();
820 				} else if (tempType==BoyerMyrvoldEdgeType::Selfloop) {
821 					// embed self-loops
822 					m_g.moveAdjBefore(adj->twin(),adj);
823 					adj=adj->succ();
824 				} else if (tempType==BoyerMyrvoldEdgeType::DfsParallel &&
825 							m_adjParent[v]!=nullptr &&
826 							w == m_adjParent[v]->theNode()) {
827 					// embed edges that are parallel to dfs-edges
828 					// it is only possible to deal with the parallel edges to the
829 					// parent, since children nodes could be flipped later
830 					adjEntry tmp = adj->succ();
831 					m_g.moveAdjAfter(adj,m_adjParent[v]->twin());
832 					m_g.moveAdjBefore(adj->twin(),m_adjParent[v]);
833 					adj = tmp;
834 				} else adj=adj->succ();
835 			}
836 		}
837 	}
838 }
839 
840 
841 // tests Graph m_g for planarity
842 // if graph should be embedded, a planar embedding or a kuratowski subdivision
843 // of m_g is returned in addition, depending on whether m_g is planar
start()844 bool BoyerMyrvoldPlanar::start()
845 {
846 	boyer_myrvold::BoyerMyrvoldInit bmi(this);
847 	bmi.computeDFS();
848 	bmi.computeLowPoints();
849 	bmi.computeDFSChildLists();
850 
851 	// call the embedding procedure
852 	return embed();
853 }
854 
855 
856 }
857