1 /** \file
2  * \brief implementation of the class FindKuratowskis
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/FindKuratowskis.h>
34 #include <ogdf/basic/simple_graph_alg.h>
35 
36 
37 namespace ogdf {
38 
39 
40 // copy pointer of class Kuratowski
copyPointer(const KuratowskiStructure & orig,SListPure<WInfo> & list)41 void KuratowskiStructure::copyPointer(const KuratowskiStructure& orig, SListPure<WInfo>& list) {
42 	auto itHighOrig = orig.highestXYPaths.begin();
43 	auto itZOrig = orig.zPaths.begin();
44 	auto itExternStartOrig = orig.externE.begin();
45 	auto itExternEndOrig = orig.externE.begin();
46 	auto itZ = zPaths.begin();
47 	auto itHigh = highestXYPaths.begin();
48 	auto itExternStart = externE.begin();
49 	auto itExternEnd = externE.begin();
50 
51 	for (WInfo &info : list) {
52 		if (info.highestXYPath!=nullptr) {
53 			// go to referenced object
54 			while (info.highestXYPath != &(*itHighOrig)) {
55 				++itHigh;
56 				++itHighOrig;
57 			}
58 			OGDF_ASSERT(itHigh.valid());
59 			OGDF_ASSERT(itHighOrig.valid());
60 			info.highestXYPath=&(*itHigh);
61 		}
62 		if (info.zPath!=nullptr) {
63 			// go to referenced object
64 			while (info.zPath != &(*itZOrig)) {
65 				++itZ;
66 				++itZOrig;
67 			}
68 			OGDF_ASSERT(itZ.valid());
69 			OGDF_ASSERT(itZOrig.valid());
70 			info.zPath=&(*itZ);
71 		}
72 		if (info.externEStart.valid()) {
73 			// go to referenced object
74 			while ((*info.externEStart).theNode != (*itExternStartOrig).theNode) {
75 				++itExternStartOrig;
76 				++itExternStart;
77 			}
78 			OGDF_ASSERT(itExternStartOrig.valid());
79 			OGDF_ASSERT(itExternStart.valid());
80 			info.externEStart = itExternStart;
81 		}
82 		if (info.externEEnd.valid()) {
83 			// go to referenced object
84 			while ((*info.externEEnd).theNode != (*itExternEndOrig).theNode) {
85 				++itExternEndOrig;
86 				++itExternEnd;
87 			}
88 			OGDF_ASSERT(itExternEndOrig.valid());
89 			OGDF_ASSERT(itExternEnd.valid());
90 			info.externEEnd = itExternEnd;
91 		}
92 	}
93 }
94 
95 // copy class Kuratowski
copy(const KuratowskiStructure & orig)96 void KuratowskiStructure::copy(const KuratowskiStructure& orig) {
97 	V = orig.V;
98 	V_DFI = orig.V_DFI;
99 	R = orig.R;
100 	RReal = orig.RReal;
101 	stopX = orig.stopX;
102 	stopY = orig.stopY;
103 
104 	wNodes = orig.wNodes;
105 	highestFacePath = orig.highestFacePath;
106 	highestXYPaths = orig.highestXYPaths;
107 	externalFacePath = orig.externalFacePath;
108 	externalSubgraph = orig.externalSubgraph;
109 	pertinentSubgraph = orig.pertinentSubgraph;
110 	zPaths = orig.zPaths;
111 	externE = orig.externE;
112 	stopXStartnodes = orig.stopXStartnodes;
113 	stopYStartnodes = orig.stopYStartnodes;
114 	stopXEndnodes = orig.stopXEndnodes;
115 	stopYEndnodes = orig.stopYEndnodes;
116 
117 	// copy pointer
118 	copyPointer(orig,wNodes);
119 }
120 
121 
122 // clears members
clear()123 void KuratowskiStructure::clear()
124 {
125 	V=R=RReal=stopX=stopY=nullptr;
126 	V_DFI = 0;
127 	wNodes.clear();
128 	highestFacePath.clear();
129 	highestXYPaths.clear();
130 	externalFacePath.clear();
131 	externalSubgraph.clear();
132 	pertinentSubgraph.clear();
133 	zPaths.clear();
134 	externE.clear();
135 	stopXStartnodes.clear();
136 	stopYStartnodes.clear();
137 	stopXEndnodes.clear();
138 	stopYEndnodes.clear();
139 }
140 
141 // class FindKuratowski
FindKuratowskis(BoyerMyrvoldPlanar * bm)142 FindKuratowskis::FindKuratowskis(BoyerMyrvoldPlanar* bm) :
143 	pBM(bm),
144 	m_g(bm->m_g),
145 	m_embeddingGrade(bm->m_embeddingGrade),
146 
147 	m_bundles(bm->m_bundles),
148 
149 	// initialize Members of BoyerMyrvoldPlanar
150 	m_realVertex(bm->m_realVertex),
151 	m_dfi(bm->m_dfi),
152 	m_nodeFromDFI(bm->m_nodeFromDFI),
153 	m_link(bm->m_link),
154 	m_adjParent(bm->m_adjParent),
155 	m_leastAncestor(bm->m_leastAncestor),
156 	m_edgeType(bm->m_edgeType),
157 	m_lowPoint(bm->m_lowPoint),
158 	m_highestSubtreeDFI(bm->m_highestSubtreeDFI),
159 	m_separatedDFSChildList(bm->m_separatedDFSChildList),
160 	m_pointsToRoot(bm->m_pointsToRoot),
161 	m_numUnembeddedBackedgesInBicomp(bm->m_numUnembeddedBackedgesInBicomp),
162 	m_backedgeFlags(bm->m_backedgeFlags),
163 	m_pertinentRoots(bm->m_pertinentRoots)
164 {
165 	OGDF_ASSERT(bm != nullptr);
166 	m_nodeMarker = 0;
167 }
168 
169 // finds root node of the bicomp containing the stopping node stopX
findRoot(node stopX) const170 node FindKuratowskis::findRoot(node stopX) const {
171 	int dir = BoyerMyrvoldPlanar::DirectionCCW;
172 	while (m_realVertex[stopX]==nullptr)
173 		stopX = pBM->successorWithoutShortCircuit(stopX,dir);
174 	return stopX;
175 }
176 
177 // extracts highest face path (contains all highest xy-paths)
extractHighestFacePath(ArrayBuffer<adjEntry> & highestFacePath,int marker)178 void FindKuratowskis::extractHighestFacePath(
179 				ArrayBuffer<adjEntry>& highestFacePath,
180 				int marker) {
181 	adjEntry adj = pBM->beforeShortCircuitEdge(k.R,BoyerMyrvoldPlanar::DirectionCCW);
182 	adjEntry end = pBM->beforeShortCircuitEdge(k.R,BoyerMyrvoldPlanar::DirectionCW);
183 	node target;
184 	while (adj != end->twin()) {
185 		node x = adj->theNode();
186 
187 		if (m_wasHere[x] >= marker) {
188 			// node is already visited on facepath: pop until duplicate node found
189 			OGDF_ASSERT(!highestFacePath.empty());
190 			while (highestFacePath.top()->theNode() != x) highestFacePath.pop();
191 			// sign cut-vertex with marker+1
192 			m_wasHere[x] = marker+1;
193 		} else {
194 			highestFacePath.push(adj);
195 			// sign visited nodes with marker
196 			m_wasHere[x] = marker;
197 		}
198 
199 		do {
200 			adj = adj->cyclicSucc();
201 			target = adj->twinNode();
202 			if (target == k.R) m_wasHere[x] = marker+1;
203 		} while (adj != end &&
204 				(m_edgeType[adj->theEdge()] == BoyerMyrvoldEdgeType::BackDeleted ||
205 				m_dfi[target] <= m_dfi[k.R]));
206 		adj = adj->twin();
207 	}
208 }
209 
210 // extract external facepath in direction CCW and split the highest facepath
211 // in highest xy-paths. marker marks the node, highMarker is used to check,
212 // whether the node was visited before by the highest facepath traversal.
213 // highMarker+1 identifies the nodes that are zNodes.
extractExternalFacePath(SListPure<adjEntry> & externalFacePath,const ArrayBuffer<adjEntry> & highestFacePath,int marker,int highMarker)214 void FindKuratowskis::extractExternalFacePath(
215 				SListPure<adjEntry>& externalFacePath,
216 				const ArrayBuffer<adjEntry>& highestFacePath,
217 				int marker,
218 				int highMarker)
219 {
220 	int dir = BoyerMyrvoldPlanar::DirectionCCW;
221 	// x traverses the external facepath
222 	node x = pBM->successorWithoutShortCircuit(k.R,dir);
223 	externalFacePath.pushBack(pBM->beforeShortCircuitEdge(k.R,BoyerMyrvoldPlanar::DirectionCCW));
224 	m_wasHere[k.R] = marker;
225 	while (x != k.R) {
226 		// set visited sign on nodes that are both on the highest and external facepath
227 		if (m_wasHere[x]>=highMarker) m_wasHere[x] = marker;
228 		externalFacePath.pushBack(pBM->beforeShortCircuitEdge(x,dir));
229 		x = pBM->successorWithoutShortCircuit(x,dir);
230 	}
231 
232 	dir = BoyerMyrvoldPlanar::DirectionCCW;
233 	x = pBM->successorWithoutShortCircuit(k.R,dir);
234 	auto highIt = highestFacePath.begin();
235 	OGDF_ASSERT(x == (*highIt)->theNode());
236 	ArrayBuffer<adjEntry> XYPathList;
237 	ArrayBuffer<adjEntry> zList;
238 	WInfo info;
239 	adjEntry adj = pBM->beforeShortCircuitEdge(k.R,BoyerMyrvoldPlanar::DirectionCCW);
240 	adjEntry temp;
241 	while (x != k.R) {
242 		// go along the highest face path until next visited sign
243 		OGDF_ASSERT(adj->theNode()==x);
244 		if (m_wasHere[x] == marker) {
245 			XYPathList.clear();
246 			zList.clear();
247 			info.w = nullptr;
248 			info.minorType = 0;
249 			info.highestXYPath = nullptr;
250 			info.zPath = nullptr;
251 			info.pxAboveStopX = false;
252 			info.pyAboveStopY = false;
253 			info.externEStart = nullptr;
254 			info.externEEnd = nullptr;
255 			info.firstExternEAfterW = nullptr;
256 		}
257 
258 		// push in wNodes-list
259 		if (pBM->pertinent(x)) {
260 			info.w = x;
261 			k.wNodes.pushBack(info);
262 		}
263 
264 		// compute next highestXYPath
265 		if (m_wasHere[x] == marker &&
266 					m_wasHere[pBM->constSuccessorWithoutShortCircuit(x,dir)] != marker) {
267 			// traverse highFacePath to x
268 			while ((*highIt)->theNode() != x) ++highIt;
269 			OGDF_ASSERT(highIt != highestFacePath.end());
270 			XYPathList.push(adj);
271 			OGDF_ASSERT((*(highIt + 1))->theNode() != pBM->constSuccessorWithoutShortCircuit(x, dir));
272 
273 			// traverse highFacePath to next marker
274 			do {
275 				++highIt;
276 				if (highIt == highestFacePath.end()) break;
277 				temp = *highIt;
278 				XYPathList.push(temp);
279 				// check, if node is z-node and push one single z-node
280 				if (m_wasHere[temp->theNode()]==highMarker+1 && zList.empty())
281 					zList.push(temp);
282 			} while (m_wasHere[temp->theNode()] != marker);
283 
284 			// save highestXY-Path
285 			OGDF_ASSERT(!XYPathList.empty());
286 			k.highestXYPaths.pushBack(XYPathList);
287 			info.highestXYPath = &k.highestXYPaths.back();
288 
289 			// compute path from zNode to V and save it
290 			if (!zList.empty()) {
291 				OGDF_ASSERT(zList.size()==1); // just one zNode for now
292 				temp = zList.top();
293 				do {
294 					do {
295 						temp = temp->cyclicSucc();
296 						OGDF_ASSERT(m_dfi[temp->twinNode()]==m_dfi[k.R] ||
297 									m_dfi[temp->twinNode()]>=m_dfi[k.RReal]);
298 					} while (m_edgeType[temp->theEdge()]==BoyerMyrvoldEdgeType::BackDeleted);
299 					temp = temp->twin();
300 					zList.push(temp);
301 				} while (temp->theNode() != k.R);
302 				k.zPaths.pushBack(zList);
303 				info.zPath = &k.zPaths.back();
304 			}
305 		}
306 
307 		// go on
308 		adj = pBM->beforeShortCircuitEdge(x,dir);
309 		x = pBM->successorWithoutShortCircuit(x,dir);
310 	}
311 }
312 
313 // separate pertinent nodes in the lists of possible different minor-types
splitInMinorTypes(const SListPure<adjEntry> & externalFacePath,int marker)314 void FindKuratowskis::splitInMinorTypes(
315 			const SListPure<adjEntry>& externalFacePath,
316 			int marker)
317 {
318 	// mark nodes, which are before stopX or behind stopY in CCW-traversal and add
319 	// all extern nodes strictly between stopX and stopY to list
320 	// externE for minor E (pertinent nodes are considered because of the
321 	// position of z left or right of w)
322 	SListIterator<WInfo> it = k.wNodes.begin();
323 	bool between = false;
324 	SListPure<WInfo*> infoList;
325 	ExternE externEdummy;
326 	// compute list of externE nodes
327 	for (auto adj : externalFacePath) {
328 		node x = adj->theNode();
329 		if (x==k.stopX || x==k.stopY) {
330 			between = !between;
331 		} else {
332 			if (!between) {
333 				m_wasHere[x]=marker;
334 			} else {
335 				if (pBM->externallyActive(x,k.V_DFI)) {
336 					externEdummy.theNode = x;
337 
338 					// check minor type B and save extern linkage
339 					if (it.valid() && (*it).w==x &&
340 							!m_pertinentRoots[x].empty() &&
341 							m_lowPoint[m_nodeFromDFI[-m_dfi[m_pertinentRoots[x].back()]]]
342 							< k.V_DFI) {
343 						WInfo& info(*it);
344 
345 						// checking minor type B
346 						info.minorType |= WInfo::MinorType::B;
347 						// mark extern node for later extraction
348 						externEdummy.startnodes.pushBack(0);
349 						// create externE-list
350 						k.externE.pushBack(externEdummy);
351 						// save extern linkage
352 						info.externEStart = k.externE.backIterator();
353 						info.externEEnd = k.externE.backIterator();
354 					} else {
355 						// create externE-list
356 						externEdummy.startnodes.clear();
357 						k.externE.pushBack(externEdummy);
358 					}
359 
360 					// save for each wNode the first externally active successor
361 					// on the external face
362 					for (auto info : infoList) {
363 						info->firstExternEAfterW = x;
364 					}
365 					infoList.clear();
366 				}
367 
368 				// get appropriate WInfo
369 				if (it.valid() && (*it).w==x) {
370 					infoList.pushBack(&(*it));
371 					++it;
372 				}
373 			}
374 		}
375 	}
376 
377 	// divide wNodes in different minor types
378 	// avoids multiple computation of the externE range
379 	SListConstIterator<adjEntry> itExtern = externalFacePath.begin();
380 	SListIterator<ExternE> itExternE = k.externE.begin();
381 	WInfo* oldInfo = nullptr;
382 	for (WInfo& info : k.wNodes) {
383 		// checking minor type A
384 		if (k.RReal!=k.V) info.minorType |= WInfo::MinorType::A;
385 
386 		// if a XYPath exists
387 		if (info.highestXYPath!=nullptr) {
388 			if (m_wasHere[(*info.highestXYPath)[0]->theNode()] == marker)
389 				info.pxAboveStopX = true;
390 			if (m_wasHere[info.highestXYPath->top()->theNode()] == marker)
391 				info.pyAboveStopY = true;
392 
393 			// checking minor type C
394 			if (info.pxAboveStopX || info.pyAboveStopY)
395 				info.minorType |= WInfo::MinorType::C;
396 
397 			// checking minor type D
398 			if (info.zPath!=nullptr) info.minorType |= WInfo::MinorType::D;
399 
400 			// checking minor type E
401 			if (!k.externE.empty()) {
402 				node t;
403 
404 				// compute valid range of externE-nodes in linear time
405 				if (oldInfo!=nullptr && info.highestXYPath==oldInfo->highestXYPath) {
406 					// found the same highestXYPath as before
407 					info.externEStart = oldInfo->externEStart;
408 					info.externEEnd = oldInfo->externEEnd;
409 					if (oldInfo->minorType & WInfo::MinorType::E) info.minorType |= WInfo::MinorType::E;
410 				} else {
411 					// compute range of a new highestXYPath
412 					node px;
413 					if (info.pxAboveStopX) {
414 						px = k.stopX;
415 					} else {
416 						px = (*info.highestXYPath)[0]->theNode();
417 					}
418 					node py;
419 					if (info.pyAboveStopY) {
420 						py = k.stopY;
421 					} else {
422 						py = info.highestXYPath->top()->theNode();
423 					}
424 					while ((*itExtern)->theNode() != px) ++itExtern;
425 					t = (*(++itExtern))->theNode();
426 					node start = nullptr;
427 					node end = nullptr;
428 					while (t != py) {
429 						if (pBM->externallyActive(t,k.V_DFI)) {
430 							if (start==nullptr) start = t;
431 							end = t;
432 						}
433 						t = (*(++itExtern))->theNode();
434 					}
435 					if (start != nullptr) {
436 						while ((*itExternE).theNode != start) ++itExternE;
437 						info.externEStart = itExternE;
438 						// mark node to extract external subgraph later
439 						(*itExternE).startnodes.pushBack(0);
440 						node temp = start;
441 						while (temp != end) {
442 							temp = (*++itExternE).theNode;
443 							// mark node to extract external subgraph later
444 							(*itExternE).startnodes.pushBack(0);
445 						}
446 						info.externEEnd = itExternE;
447 						info.minorType |= WInfo::MinorType::E;
448 					}
449 					oldInfo = &info;
450 				}
451 			}
452 		}
453 
454 #if 0
455 		// use this to find special kuratowski-structures
456 		if ((info.minorType & (WInfo::A|WInfo::B|WInfo::C|WInfo::D|WInfo::E)) ==
457 			(WInfo::A|WInfo::B|WInfo::C|WInfo::D|WInfo::E)) {
458 			char t; std::cin >> t;
459 		}
460 #endif
461 	}
462 
463 	// extract the externalSubgraph of all saved externally active nodes
464 	// exclude the already extracted minor b-types
465 #ifdef OGDF_DEBUG
466 	int visited = m_nodeMarker+1;
467 #endif
468 	for (ExternE& externE : k.externE) {
469 		if (externE.startnodes.empty()) continue;
470 
471 		externE.startnodes.clear();
472 		if (m_bundles) {
473 			OGDF_ASSERT(m_wasHere[externE.theNode] < visited);
474 			extractExternalSubgraphBundles(externE.theNode, k.V_DFI, k.externalSubgraph, ++m_nodeMarker);
475 		} else {
476 			extractExternalSubgraph(externE.theNode, k.V_DFI, externE.startnodes, externE.endnodes);
477 
478 			// Add externE.startnodes.size() many dummy elements.
479 			// It is done this way because size() takes linear time and
480 			// a range-based for-loop needs an unused variable.
481 			SListPure<edge> dummy;
482 			for (auto itInt = externE.startnodes.begin(); itInt.valid(); ++itInt) {
483 				externE.externalPaths.pushBack(dummy);
484 			}
485 		}
486 	}
487 }
488 
489 // extracts and adds external subgraph from stopnode to ancestors of the node with dfi root
490 // to edgelist, nodeMarker is used as a visited flag. returns the endnode with lowest dfi.
extractExternalSubgraph(const node stop,int root,SListPure<int> & externalStartnodes,SListPure<node> & externalEndnodes)491 void FindKuratowskis::extractExternalSubgraph(
492 			const node stop,
493 			int root,
494 			SListPure<int>& externalStartnodes,
495 			SListPure<node>& externalEndnodes)
496 {
497 	if (m_leastAncestor[stop] < root) {
498 		externalStartnodes.pushBack(m_dfi[stop]);
499 		externalEndnodes.pushBack(m_nodeFromDFI[m_leastAncestor[stop]]);
500 	}
501 
502 	for (node temp : m_separatedDFSChildList[stop]) {
503 		// descent to external active child bicomps of stopnode
504 		int lowpoint = m_lowPoint[temp];
505 		if (lowpoint >= root) break;
506 
507 		externalStartnodes.pushBack(m_dfi[temp]);
508 		externalEndnodes.pushBack(m_nodeFromDFI[lowpoint]);
509 	}
510 }
511 
512 // extract and add external subgraph from stopnode to ancestors of the node with dfi root
513 // to edgelist, nodeMarker is used as a visited flag. returns the endnode with lowest dfi.
extractExternalSubgraphBundles(const node stop,int root,SListPure<edge> & externalSubgraph,int nodeMarker)514 void FindKuratowskis::extractExternalSubgraphBundles(
515 	const node stop,
516 	int root,
517 	SListPure<edge>& externalSubgraph,
518 	int nodeMarker)
519 {
520 #ifdef OGDF_DEBUG
521 	for (node v : m_g.nodes)
522 		OGDF_ASSERT(m_wasHere[v] != nodeMarker);
523 #endif
524 
525 	ArrayBuffer<node> stack; // stack for dfs-traversal
526 	stack.push(stop);
527 	while (!stack.empty()) {
528 		node v = stack.popRet();
529 		if (m_wasHere[v] == nodeMarker) continue;
530 		// mark visited nodes
531 		m_wasHere[v] = nodeMarker;
532 
533 		// search for unvisited nodes and add them to stack
534 		for (adjEntry adj : v->adjEntries) {
535 			node temp = adj->twinNode();
536 			if (m_edgeType[adj->theEdge()] == BoyerMyrvoldEdgeType::BackDeleted) continue;
537 
538 			// go along backedges to ancestor (ignore virtual nodes)
539 			if (m_dfi[temp] < root && m_dfi[temp] > 0) {
540 				OGDF_ASSERT(m_edgeType[adj->theEdge()] == BoyerMyrvoldEdgeType::Back);
541 				externalSubgraph.pushBack(adj->theEdge());
542 			} else if (v != stop && m_dfi[temp] >= m_dfi[v]) {
543 				// set flag and push unvisited nodes
544 				OGDF_ASSERT(m_edgeType[adj->theEdge()] == BoyerMyrvoldEdgeType::Back ||
545 					m_edgeType[adj->theEdge()] == BoyerMyrvoldEdgeType::Dfs ||
546 					m_edgeType[adj->theEdge()] == BoyerMyrvoldEdgeType::BackDeleted);
547 				externalSubgraph.pushBack(adj->theEdge());
548 				if (m_wasHere[temp] != nodeMarker) stack.push(temp);
549 			}
550 		}
551 
552 		// descent to external active child bicomps
553 		for (node temp : m_separatedDFSChildList[v]) {
554 			if (m_lowPoint[temp] >= root)
555 				break;
556 			stack.push(m_nodeFromDFI[-m_dfi[temp]]);
557 		}
558 	}
559 }
560 
561 
562 // extract pertinent paths from all w-nodes to k.V to edgelist
extractPertinentSubgraph(SListPure<WInfo> & W_All)563 void FindKuratowskis::extractPertinentSubgraph(
564 	SListPure<WInfo>& W_All)
565 {
566 	SListPure<edge> path;
567 	int minDFI = -m_dfi[k.R];
568 	int maxDFI = m_highestSubtreeDFI[m_nodeFromDFI[minDFI]];
569 
570 	// create links from pertinent nodes to WInfo
571 	for (WInfo &info : W_All) {
572 		m_getWInfo[info.w] = &info;
573 	}
574 
575 	// add all pertinent paths to WInfo
576 	for (adjEntry adj : k.V->adjEntries) {
577 		if (m_edgeType[adj->theEdge()] == BoyerMyrvoldEdgeType::BackDeleted) continue;
578 		int targetDFI = m_dfi[adj->twinNode()];
579 		if (targetDFI >= minDFI && targetDFI <= maxDFI) {
580 			// target node is in subtree of a pertinent node
581 
582 			// delete last edge and backedgeFlags
583 			node target = adj->twinNode();
584 			edge e = adj->theEdge();
585 			path.pushFront(e);
586 			OGDF_ASSERT(!m_backedgeFlags[target].empty());
587 			m_backedgeFlags[target].clear();
588 			m_edgeType[e] = BoyerMyrvoldEdgeType::BackDeleted;
589 			// delete backedge-counter on virtual root node
590 			--m_numUnembeddedBackedgesInBicomp[m_pointsToRoot[e]];
591 			OGDF_ASSERT(m_numUnembeddedBackedgesInBicomp[m_pointsToRoot[e]] >= 0);
592 
593 			// go up along the DFS-path
594 			while (m_getWInfo[target] == nullptr) {
595 				path.pushFront(m_adjParent[target]->theEdge());
596 				target = m_adjParent[target]->theNode();
597 				if (m_realVertex[target] != nullptr) {
598 					target = m_realVertex[target];
599 					m_pertinentRoots[target].clear();
600 				}
601 			}
602 
603 			// save path
604 			m_getWInfo[target]->pertinentPaths.pushBack(path);
605 			path.clear();
606 		}
607 	}
608 
609 	// delete links from pertinent nodes to WInfo
610 	for (const WInfo &info : W_All) {
611 		m_getWInfo[info.w] = nullptr;
612 	}
613 }
614 
615 
616 // extract and add pertinent subgraph from all w-nodes to v to edgelist
extractPertinentSubgraphBundles(const SListPure<WInfo> & W_All,const node V,SListPure<edge> & pertinentSubgraph,int nodeMarker)617 void FindKuratowskis::extractPertinentSubgraphBundles(
618 	const SListPure<WInfo>& W_All,
619 	const node V,
620 	SListPure<edge>& pertinentSubgraph,
621 	int nodeMarker)
622 {
623 #ifdef OGDF_DEBUG
624 	for (node w : m_g.nodes)
625 		OGDF_ASSERT(m_wasHere[w] != nodeMarker);
626 #endif
627 
628 	ArrayBuffer<node> stack; // stack for dfs-traversal
629 	// for all w-nodes
630 	for (const WInfo &info : W_All) {
631 		node currentWNode = info.w;
632 		stack.push(currentWNode);
633 
634 		// until stack is empty, do dfs-traversal in bicomps and descent to
635 		// pertinent child bicomps
636 		while (!stack.empty()) {
637 			node w = stack.popRet();
638 			if (m_wasHere[w] == nodeMarker) continue;
639 			// mark visited nodes
640 			m_wasHere[w] = nodeMarker;
641 
642 			// search for unvisited nodes and add them to stack
643 			for (adjEntry adj : w->adjEntries) {
644 				edge e = adj->theEdge();
645 				if (m_edgeType[e] == BoyerMyrvoldEdgeType::BackDeleted) continue;
646 				node x = adj->twinNode();
647 
648 				// go along pertinent backedges to V (ignore virtual nodes)
649 				if (x == V && m_edgeType[e] != BoyerMyrvoldEdgeType::BackDeleted) {
650 					OGDF_ASSERT(m_edgeType[e] == BoyerMyrvoldEdgeType::Back);
651 					// delete edge and delete backedgeFlags
652 					m_edgeType[e] = BoyerMyrvoldEdgeType::BackDeleted;
653 					m_backedgeFlags[w].clear();
654 					// delete backedge-counter on virtual root node
655 					--m_numUnembeddedBackedgesInBicomp[m_pointsToRoot[e]];
656 					OGDF_ASSERT(m_numUnembeddedBackedgesInBicomp[m_pointsToRoot[e]] >= 0);
657 					pertinentSubgraph.pushBack(e);
658 				} else if (w != currentWNode && m_dfi[x] >= m_dfi[w]) {
659 					OGDF_ASSERT(m_edgeType[adj->theEdge()] == BoyerMyrvoldEdgeType::Dfs ||
660 						m_edgeType[adj->theEdge()] == BoyerMyrvoldEdgeType::Back ||
661 						m_edgeType[adj->theEdge()] == BoyerMyrvoldEdgeType::BackDeleted);
662 					// set flag and push unvisited nodes
663 					pertinentSubgraph.pushBack(e);
664 					if (m_wasHere[x] != nodeMarker) stack.push(x);
665 				}
666 			}
667 
668 			// descent to pertinent child bicomps
669 			for (node v : m_pertinentRoots[w]) {
670 				stack.push(v);
671 			}
672 			// delete all pertinentRoots-lists, since there are no pertinent backedges any more
673 			m_pertinentRoots[w].clear();
674 		}
675 	}
676 }
677 
678 
679 // add Kuratowski structure on current node V
addKuratowskiStructure(const node currentNode,const node root,const node stopx,const node stopy)680 void FindKuratowskis::addKuratowskiStructure(
681 						const node currentNode,
682 						const node root,
683 						const node stopx,
684 						const node stopy)
685 {
686 	OGDF_ASSERT(currentNode != nullptr);
687 	OGDF_ASSERT(root != nullptr);
688 	OGDF_ASSERT(stopx != nullptr);
689 	OGDF_ASSERT(stopy != nullptr);
690 	OGDF_ASSERT(stopx != stopy);
691 	OGDF_ASSERT(currentNode != stopx);
692 	OGDF_ASSERT(currentNode != stopy);
693 	OGDF_ASSERT(m_dfi[root] < 0);
694 	OGDF_ASSERT(!pBM->pertinent(stopx));
695 	OGDF_ASSERT(pBM->externallyActive(stopx,m_dfi[currentNode]));
696 	OGDF_ASSERT(!pBM->pertinent(stopy));
697 	OGDF_ASSERT(pBM->externallyActive(stopy,m_dfi[currentNode]));
698 	OGDF_ASSERT(findRoot(stopx)==root); // check root
699 	OGDF_ASSERT(pBM->wNodesExist(root,stopx,stopy));
700 	OGDF_ASSERT(isSimpleUndirected(m_g)); // Graph has to be simple
701 	OGDF_ASSERT(m_embeddingGrade > BoyerMyrvoldPlanar::EmbeddingGrade::doNotFind);
702 	// check, if we have found enough kuratowski structures
703 	OGDF_ASSERT(m_embeddingGrade <= 0 || allKuratowskis.size() < m_embeddingGrade);
704 
705 	// init NodeArrays in first invocation
706 	if (!m_wasHere.valid()) {
707 		if (!m_bundles) {
708 			OGDF_ASSERT(!m_getWInfo.valid());
709 			OGDF_ASSERT(m_getWInfo.graphOf() == nullptr);
710 			m_getWInfo.init(m_g,nullptr);
711 		}
712 		OGDF_ASSERT(m_wasHere.graphOf() == nullptr);
713 		m_wasHere.init(m_g,0);
714 	}
715 
716 	// delete old KuratowskiStruture and initialize new structure
717 	k.clear();
718 	k.V = currentNode;
719 	k.V_DFI = m_dfi[currentNode];
720 	k.stopX = stopx;
721 	k.stopY = stopy;
722 	k.R = root;
723 	k.RReal = m_realVertex[k.R];
724 
725 	// flip bicomp with root R with or without reversed flipping. changes the embedding
726 	// process completely.
727 	pBM->flipBicomp(-m_dfi[k.R],++m_nodeMarker,m_wasHere,false,true);
728 	//	pBM->flipBicomp(-m_dfi[k.R],++m_nodeMarker,m_wasHere,false,false);
729 
730 	// extract highest facepath (contains all highest xy-paths)
731 	extractHighestFacePath(k.highestFacePath,++m_nodeMarker);
732 	++m_nodeMarker;
733 
734 	// extract external facepath in direction CCW and split the highest facepath in
735 	// highest xy-paths
736 	++m_nodeMarker;
737 	extractExternalFacePath(k.externalFacePath,k.highestFacePath,m_nodeMarker,
738 															m_nodeMarker-2);
739 
740 	// extract external subgraph from stopX and stopY to ancestors of R
741 	if (m_bundles) {
742 		extractExternalSubgraphBundles(k.stopX,k.V_DFI,k.externalSubgraph,++m_nodeMarker);
743 	} else {
744 		extractExternalSubgraph(k.stopX,k.V_DFI,k.stopXStartnodes,k.stopXEndnodes);
745 	}
746 
747 	if (m_bundles) {
748 		extractExternalSubgraphBundles(k.stopY,k.V_DFI,k.externalSubgraph,++m_nodeMarker);
749 	} else {
750 		extractExternalSubgraph(k.stopY,k.V_DFI,k.stopYStartnodes,k.stopYEndnodes);
751 	}
752 
753 	// pass pertinent nodes in the lists of possible different minor-types
754 	splitInMinorTypes(k.externalFacePath,++m_nodeMarker);
755 
756 	// extract pertinent subgraphs from all w-nodes to k.V
757 	if (m_bundles) {
758 		extractPertinentSubgraphBundles(k.wNodes,k.V,k.pertinentSubgraph,++m_nodeMarker);
759 	} else {
760 		extractPertinentSubgraph(k.wNodes/*,k.V*/);
761 	}
762 
763 	// add Kuratowski to KuratowskisOnNode
764 	allKuratowskis.pushBack(k);
765 
766 	// reverse flipping
767 #if 0
768 	pBM->flipBicomp(-m_dfi[k.R],++m_nodeMarker,m_wasHere,false,false);
769 #endif
770 
771 	OGDF_ASSERT(m_bundles || k.pertinentSubgraph.empty());
772 }
773 
774 
775 }
776