1 /** \file
2  * \brief Implementation of class PlanarSPQRTree
3  *
4  * \author Carsten Gutwenger
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/decomposition/PlanarSPQRTree.h>
34 #include <ogdf/basic/extended_graph_alg.h>
35 
36 
37 namespace ogdf {
38 
39 //
40 // initialization: additionally embeds skeleton graphs or adpots embedding
41 // given by original graph
init(bool isEmbedded)42 void PlanarSPQRTree::init(bool isEmbedded)
43 {
44 	m_finished = true;
45 	if (isEmbedded) {
46 		adoptEmbedding();
47 
48 	} else {
49 
50 		for (node v : tree().nodes)
51 			planarEmbed(skeleton(v).getGraph());
52 	}
53 }
54 
55 
adoptEmbedding()56 void PlanarSPQRTree::adoptEmbedding()
57 {
58 	OGDF_HEAVY_ASSERT(originalGraph().representsCombEmbedding());
59 
60 	// ordered list of adjacency entries (for one original node) in all
61 	// skeletons (where this node occurs)
62 	NodeArray<SListPure<adjEntry> > adjEdges(tree());
63 	// copy in skeleton of current original node
64 	NodeArray<node> currentCopy(tree(),nullptr);
65 	NodeArray<adjEntry> lastAdj(tree(),nullptr);
66 	SListPure<node> current; // currently processed nodes
67 
68 	for (node vOrig : originalGraph().nodes)
69 	{
70 		for(adjEntry adjOrig : vOrig->adjEntries)
71 		{
72 			edge            eOrig = adjOrig->theEdge();
73 			const Skeleton &S     = skeletonOfReal(eOrig);
74 			edge            eCopy = copyOfReal(eOrig);
75 
76 			adjEntry adjCopy = (S.original(eCopy->source()) == vOrig) ?
77 				eCopy->adjSource() : eCopy->adjTarget();
78 
79 			setPosInEmbedding(adjEdges,currentCopy,lastAdj,current,S,adjCopy);
80 		}
81 
82 		for(node vT : current) {
83 			skeleton(vT).getGraph().sort(currentCopy[vT],adjEdges[vT]);
84 
85 			adjEdges[vT].clear();
86 			currentCopy[vT] = nullptr;
87 		}
88 
89 		current.clear();
90 	}
91 }
92 
93 
setPosInEmbedding(NodeArray<SListPure<adjEntry>> & adjEdges,NodeArray<node> & currentCopy,NodeArray<adjEntry> & lastAdj,SListPure<node> & current,const Skeleton & S,adjEntry adj)94 void PlanarSPQRTree::setPosInEmbedding(
95 	NodeArray<SListPure<adjEntry> > &adjEdges,
96 	NodeArray<node> &currentCopy,
97 	NodeArray<adjEntry> &lastAdj,
98 	SListPure<node> &current,
99 	const Skeleton &S,
100 	adjEntry adj)
101 {
102 	node vT = S.treeNode();
103 
104 	adjEdges[vT].pushBack(adj);
105 
106 	node vCopy = adj->theNode();
107 	node vOrig = S.original(vCopy);
108 
109 	if(currentCopy[vT] == nullptr) {
110 		currentCopy[vT] = vCopy;
111 		current.pushBack(vT);
112 
113 		for (adjEntry adjVirt : vCopy->adjEntries) {
114 			edge eCopy = S.twinEdge(adjVirt->theEdge());
115 			if (eCopy == nullptr) continue;
116 			if (adjVirt == adj) {
117 				lastAdj[vT] = adj;
118 				continue;
119 			}
120 
121 			const Skeleton &STwin = skeleton(S.twinTreeNode(adjVirt->theEdge()));
122 
123 			adjEntry adjCopy = (STwin.original(eCopy->source()) == vOrig) ?
124 				eCopy->adjSource() : eCopy->adjTarget();
125 
126 			setPosInEmbedding(adjEdges,currentCopy,lastAdj,current,
127 				STwin, adjCopy);
128 		}
129 
130 	} else if (lastAdj[vT] != nullptr && lastAdj[vT] != adj) {
131 		adjEntry adjVirt = lastAdj[vT];
132 		edge eCopy = S.twinEdge(adjVirt->theEdge());
133 
134 		const Skeleton &STwin = skeleton(S.twinTreeNode(adjVirt->theEdge()));
135 
136 		adjEntry adjCopy = (STwin.original(eCopy->source()) == vOrig) ?
137 			eCopy->adjSource() : eCopy->adjTarget();
138 
139 		setPosInEmbedding(adjEdges,currentCopy,lastAdj,current,
140 			STwin, adjCopy);
141 
142 		lastAdj[vT] = nullptr;
143 	}
144 
145 }
146 
147 
148 //
149 // embed original graph according to embedding of skeletons
150 //
151 // The procedure also handles the case when some (real or virtual)
152 // edges are reversed (used in upward-planarity algorithms)
embed(Graph & G)153 void PlanarSPQRTree::embed(Graph &G)
154 {
155 	OGDF_ASSERT(&G == &originalGraph());
156 
157 	const Skeleton &S = skeleton(rootNode());
158 	const Graph &M = S.getGraph();
159 
160 	for (node v : M.nodes)
161 	{
162 		node vOrig = S.original(v);
163 		SListPure<adjEntry> adjEdges;
164 
165 		for (adjEntry adj : v->adjEntries) {
166 			edge e = adj->theEdge();
167 			edge eOrig = S.realEdge(e);
168 
169 			if (eOrig != nullptr) {
170 				adjEntry adjOrig = (vOrig == eOrig->source()) ?
171 					eOrig->adjSource() : eOrig->adjTarget();
172 				OGDF_ASSERT(adjOrig->theNode() == S.original(v));
173 				adjEdges.pushBack(adjOrig);
174 
175 			} else {
176 				node wT    = S.twinTreeNode(e);
177 				edge eTwin = S.twinEdge(e);
178 				expandVirtualEmbed(wT,
179 					(vOrig == skeleton(wT).original(eTwin->source())) ?
180 					eTwin->adjSource() : eTwin->adjTarget(),
181 					adjEdges);
182 			}
183 		}
184 
185 		G.sort(vOrig,adjEdges);
186 	}
187 
188 
189 	for(adjEntry adj : rootNode()->adjEntries) {
190 		node wT = adj->theEdge()->target();
191 		if (wT != rootNode())
192 			createInnerVerticesEmbed(G, wT);
193 	}
194 }
195 
196 
expandVirtualEmbed(node vT,adjEntry adjVirt,SListPure<adjEntry> & adjEdges)197 void PlanarSPQRTree::expandVirtualEmbed(node vT,
198 	adjEntry adjVirt,
199 	SListPure<adjEntry> &adjEdges)
200 {
201 	const Skeleton &S = skeleton(vT);
202 
203 	node v = adjVirt->theNode();
204 	node vOrig = S.original(v);
205 
206 	adjEntry adj;
207 	for (adj = adjVirt->cyclicSucc(); adj != adjVirt; adj = adj->cyclicSucc())
208 	{
209 		edge e = adj->theEdge();
210 		edge eOrig = S.realEdge(e);
211 
212 		if (eOrig != nullptr) {
213 			adjEntry adjOrig = (vOrig == eOrig->source()) ?
214 				eOrig->adjSource() : eOrig->adjTarget();
215 			OGDF_ASSERT(adjOrig->theNode() == S.original(v));
216 			adjEdges.pushBack(adjOrig);
217 
218 		} else {
219 			node wT    = S.twinTreeNode(e);
220 			edge eTwin = S.twinEdge(e);
221 			expandVirtualEmbed(wT,
222 				(vOrig == skeleton(wT).original(eTwin->source())) ?
223 				eTwin->adjSource() : eTwin->adjTarget(),
224 				adjEdges);
225 		}
226 	}
227 }
228 
229 
createInnerVerticesEmbed(Graph & G,node vT)230 void PlanarSPQRTree::createInnerVerticesEmbed(Graph &G, node vT)
231 {
232 	const Skeleton &S = skeleton(vT);
233 	const Graph& M = S.getGraph();
234 
235 	node src = S.referenceEdge()->source();
236 	node tgt = S.referenceEdge()->target();
237 
238 	for (node v : M.nodes)
239 	{
240 		if (v == src || v == tgt) continue;
241 
242 		node vOrig = S.original(v);
243 		SListPure<adjEntry> adjEdges;
244 
245 		for (adjEntry adj : v->adjEntries) {
246 			edge e = adj->theEdge();
247 			edge eOrig = S.realEdge(e);
248 
249 			if (eOrig != nullptr) {
250 				adjEntry adjOrig = (vOrig == eOrig->source()) ?
251 					eOrig->adjSource() : eOrig->adjTarget();
252 				OGDF_ASSERT(adjOrig->theNode() == S.original(v));
253 				adjEdges.pushBack(adjOrig);
254 			} else {
255 				node wT    = S.twinTreeNode(e);
256 				edge eTwin = S.twinEdge(e);
257 				expandVirtualEmbed(wT,
258 					(vOrig == skeleton(wT).original(eTwin->source())) ?
259 					eTwin->adjSource() : eTwin->adjTarget(),
260 					adjEdges);
261 			}
262 		}
263 
264 		G.sort(vOrig,adjEdges);
265 	}
266 
267 	for(adjEntry adj : vT->adjEntries) {
268 		node wT = adj->theEdge()->target();
269 		if (wT != vT)
270 			createInnerVerticesEmbed(G, wT);
271 	}
272 }
273 
274 
275 
276 //
277 // basic update functions for manipulating embeddings
278 
279 //   reversing the skeleton of an R- or P-node
reverse(node vT)280 void PlanarSPQRTree::reverse(node vT)
281 {
282 	skeleton(vT).getGraph().reverseAdjEdges();
283 }
284 
285 
286 //   swapping two adjacency entries in the skeleton of a P-node
swap(node vT,adjEntry adj1,adjEntry adj2)287 void PlanarSPQRTree::swap(node vT, adjEntry adj1, adjEntry adj2)
288 {
289 	OGDF_ASSERT(typeOf(vT) == NodeType::PNode);
290 
291 	Graph &M = skeleton(vT).getGraph();
292 
293 	M.swapAdjEdges(adj1,adj2);
294 	M.swapAdjEdges(adj1->twin(),adj2->twin());
295 }
296 
297 
298 //   swapping two edges in the skeleton of a P-node
swap(node vT,edge e1,edge e2)299 void PlanarSPQRTree::swap(node vT, edge e1, edge e2)
300 {
301 	OGDF_ASSERT(typeOf(vT) == NodeType::PNode);
302 
303 	if (e1->source() == e2->source())
304 		swap(vT,e1->adjSource(),e2->adjSource());
305 	else
306 		swap(vT,e1->adjSource(),e2->adjTarget());
307 }
308 
309 
310 //
311 // number of possible embeddings of original graph
312 //
numberOfEmbeddings(node vT) const313 double PlanarSPQRTree::numberOfEmbeddings(node vT) const
314 {
315 	double num = 1.0;
316 
317 	switch(typeOf(vT)) {
318 	case NodeType::RNode:
319 		num = 2; break;
320 	case NodeType::PNode:
321 #if 0
322 		node vFirst = skeleton(vT).getGraph().firstNode();
323 #endif
324 		for (int i = skeleton(vT).getGraph().firstNode()->degree()-1; i >= 2; --i)
325 			num *= i;
326 		break;
327 	case NodeType::SNode:
328 		break;
329 	}
330 
331 	for(adjEntry adj : vT->adjEntries) {
332 		node wT = adj->theEdge()->target();
333 		if(wT != vT)
334 			num *= numberOfEmbeddings(wT);
335 	}
336 
337 	return num;
338 }
339 
340 
341 
342 //
343 // randomly embed skeleton graphs
344 //
randomEmbed()345 void PlanarSPQRTree::randomEmbed()
346 {
347 	for (node vT : tree().nodes) {
348 		if (typeOf(vT) == NodeType::RNode) {
349 			int doReverse = randomNumber(0,1);
350 
351 			if (doReverse == 1)
352 				reverse(vT);
353 
354 		} else if (typeOf(vT) == NodeType::PNode) {
355 			const Skeleton &S = skeleton(vT);
356 			adjEntry adjRef = S.referenceEdge()->adjSource();
357 
358 			SList<adjEntry> adjEdges;
359 			adjEntry adj;
360 			for (adj = adjRef->cyclicSucc(); adj != adjRef; adj = adj->cyclicSucc())
361 				adjEdges.pushBack(adj);
362 
363 			adjEdges.permute();
364 
365 			adj = adjRef->cyclicSucc();
366 			for (adjEntry adjNext : adjEdges)
367 			{
368 				if (adjNext != adj) {
369 					swap(vT,adj,adjNext);
370 					adj = adjNext;
371 				}
372 				adj = adj->cyclicSucc();
373 			}
374 		}
375 	}
376 }
377 
378 
numberOfNodeEmbeddings(node vT) const379 long long PlanarSPQRTree::numberOfNodeEmbeddings(node vT) const {
380 
381 	long long num = 1;
382 
383 	switch(typeOf(vT)) {
384 		case NodeType::RNode:
385 			num = 2;
386 			break;
387 		case NodeType::PNode:
388 			for (int i = skeleton(vT).getGraph().firstNode()->degree()-1; i >= 2; --i)
389 				num *= i;
390 			break;
391 		case NodeType::SNode:
392 			break;
393 	}
394 
395 	return num;
396 }
397 
embed(node & vT,long long x)398 void PlanarSPQRTree::embed(node &vT, long long x) {
399 
400 	OGDF_ASSERT(x >= 0);
401 	OGDF_ASSERT(x < numberOfNodeEmbeddings(vT));
402 
403 	//if it is a P-node
404 	if (typeOf(vT) == NodeType::PNode) {
405 		//encode the id of the permutation
406 		long long id = x;
407 		//number of elements of the permutation
408 		const int p = skeleton(vT).getGraph().numberOfEdges() - 1;
409 		//sequence for the edges
410 		Array<long long> seq(p);
411 		//base
412 		long long b = 2;
413 		//compute the correct sequence of the edges
414 		for (int i = 0; i < p - 1; i++) {
415 			seq[i] = id % b;
416 			id /= b;
417 			b++;
418 		}
419 		//swap the sequence
420 		int low  = 0;
421 		int high = p-2;
422 		while (low < high) {
423 			long long t = seq[low];
424 			seq[low] = seq[high]; seq[high] = t;
425 			low++; high--;
426 		}
427 		seq[p-1] = 0;
428 		//encode the sequence to the permutation
429 		Array<int> list(p);
430 		Array<int> permutation(p);
431 		Array<bool> set(0,p-1,false);
432 		for (int i = 0; i < p; i++) list[i] = i;
433 		for (int i = 0; i < p; i++) {
434 			long long e = seq[i];
435 			long long cnt = 0;
436 			int ind;
437 			for (ind = 0; ind < p; ind++) {
438 				if (!set[ind]) {
439 					if (cnt == e) break;
440 					++cnt;
441 				}
442 			}
443 			permutation[ind] = list[i];
444 			set[ind] = true;
445 		}
446 		//adopt the permutation to the skeleton
447 		//first vertex
448 		List<adjEntry> order;
449 		node nP = skeleton(vT).getGraph().firstNode();
450 		nP->allAdjEntries(order);
451 		TargetComparer<AdjElement,AdjElement> comp;
452 		order.quicksort(comp);
453 		Array<adjEntry> normalized(p);
454 		List<adjEntry> newOrder;
455 		newOrder.pushBack(order.popFrontRet());
456 		for (int i = 0; i < p; i++) normalized[i] = order.popFrontRet();
457 		for (int i = 0; i < p; i++) newOrder.pushBack(normalized[permutation[i]]);
458 		skeleton(vT).getGraph().sort(nP,newOrder);
459 		//second vertex
460 		List<adjEntry> newOrderLast;
461 		ListIterator<adjEntry> it = newOrder.begin();
462 		while (it.valid()) {
463 			newOrderLast.pushFront((*it)->twin());
464 			++it;
465 		}
466 		skeleton(vT).getGraph().sort(skeleton(vT).getGraph().lastNode(),newOrderLast);
467 		//P-node ends here
468 	}
469 	//if it is a R-node
470 	if (typeOf(vT) == NodeType::RNode) {
471 		node nP = skeleton(vT).getGraph().firstNode();
472 		if (x == 0 && nP->firstAdj()->index() > nP->lastAdj()->index()) reverse(vT);
473 		if (x == 1 && nP->firstAdj()->index() < nP->lastAdj()->index()) reverse(vT);
474 		//R-node ends here
475 	}
476 
477 }
478 
479 //
480 // compute the first embedding of the original graph
481 //
firstEmbedding(Graph & G)482 void PlanarSPQRTree::firstEmbedding(Graph &G)
483 {
484 	OGDF_ASSERT(&G == &originalGraph());
485 
486 	m_finished = false;
487 	for (node vT : tree().nodes) {
488 		firstEmbedding(vT);
489 	}
490 	embed(G);
491 }
492 
493 
494 //
495 // compute the next embedding of the original graph
496 //
nextEmbedding(Graph & G)497 bool PlanarSPQRTree::nextEmbedding(Graph &G)
498 {
499 	OGDF_ASSERT(&G == &originalGraph());
500 
501 	//if there is at least one new embedding: compute it using
502 	//nextEmbedding(n) to the first node of the SPQR-tree (represented by a list-iterator)
503 	List<node> nodes;
504 	tree().allNodes(nodes);
505 	if (!m_finished && nextEmbedding(nodes.begin())) {
506 		//compute the actual embedding of the original graph
507 		embed(G);
508 		return true;
509 	}
510 	m_finished = true;
511 	return false;
512 }
513 
514 
515 //
516 // computes the first embedding of the skeleton of node vT
517 //
firstEmbedding(node & vT)518 void PlanarSPQRTree::firstEmbedding(node &vT)
519 {
520 	//if vT is a R-node
521 	if (typeOf(vT) == NodeType::RNode) {
522 		//if the R-node were reversed in former steps
523 		//then reverse it to its original embedding
524 		node nP = skeleton(vT).getGraph().firstNode();
525 		if (nP->firstAdj()->index() > nP->lastAdj()->index()) reverse(vT);
526 	}
527 	//if vT is a P-node
528 	if (typeOf(vT) == NodeType::PNode) {
529 		//then sort the adjEntries by their indices
530 		//first vertex
531 		List<adjEntry> order;
532 		node nP = skeleton(vT).getGraph().firstNode();
533 		nP->allAdjEntries(order);
534 		TargetComparer<AdjElement,AdjElement> comp;
535 		order.quicksort(comp);
536 		skeleton(vT).getGraph().sort(nP,order);
537 		//second vertex
538 		List<adjEntry> newOrderLast;
539 		for(adjEntry adj : order) {
540 			newOrderLast.pushFront(adj->twin());
541 		}
542 		skeleton(vT).getGraph().sort(skeleton(vT).getGraph().lastNode(),newOrderLast);
543 	}
544 
545 }
546 
reverse(node & vP,adjEntry & first,adjEntry & last)547 void PlanarSPQRTree::reverse(node &vP, adjEntry &first, adjEntry &last)
548 {
549 	//swap the first and the last adjEntry
550 	adjEntry it_f = first;
551 	adjEntry it_l = last;
552 	swap(vP,it_f,it_l);
553 	adjEntry temp = it_f;
554 	it_f = it_l->succ();
555 	it_l = temp->pred();
556 	//while there are swapable adjEntries: swap it, i.e.
557 	//if left == right or left->pred() == right->succ() then stop
558 	while (it_f != it_l && it_l->succ() != it_f) {
559 		swap(vP,it_f,it_l);
560 		temp = it_f;
561 		it_f = it_l->succ();
562 		it_l = temp->pred();
563 	}
564 }
565 
566 
567 //
568 // computes the next embedding of the skeleton of node vT
569 //
nextEmbedding(node & vT)570 bool PlanarSPQRTree::nextEmbedding(node &vT)
571 {
572 	//if vT is a R-node
573 	if (typeOf(vT) == NodeType::RNode) {
574 		node nP = skeleton(vT).getGraph().firstNode();
575 		//compute the next embedding (might be the first embedding)
576 		//of the sekeleton of vT
577 		reverse(vT);
578 		//if next embedding = first embedding then return false
579 		//otherwise return true
580 		return nP->firstAdj()->index() > nP->lastAdj()->index();
581 	}
582 	//if vT is a P-node
583 	if (typeOf(vT) == NodeType::PNode) {
584 		//take on of the two nodes of the skeleton of vT
585 		node nP = skeleton(vT).getGraph().firstNode();
586 		//if its degree is two then there is only one embedding
587 		if (nP->degree() < 3) return false;
588 		//otherwise compute the next embedding by computing the next
589 		//permutation of the adjEntries of nP -- excluding the first adjEntry
590 		adjEntry last;
591 		adjEntry it = nP->lastAdj();
592 		//compute the largest adjEntry s.t. its index is smaller than its successor
593 		//largest means that the adjEntry is near the last adjEntry
594 		while (it->index() < it->pred()->index()) it = it->pred();
595 		//if such adjEntry was not found then all permutations were computed -- thus
596 		//compute the first embedding by reversing the order of the adjEntries
597 		//beginning at the second adjEntry
598 		if (it == nP->firstAdj()->succ()) {
599 			last = nP->lastAdj();
600 			reverse(vT,it,last);
601 			return false;
602 		}
603 		//otherwise compute the next permutation (embedding)
604 		it = it->pred();
605 		adjEntry it_max = nP->lastAdj();
606 		//compute the largest adjEntry s.t. its index is smaller than the index of
607 		//the adjEntry computed above -- the existence is guaranteed
608 		while (it->index() > it_max->index()) it_max = it_max->pred();
609 		//swap both adjEntries found above
610 		swap(vT,it,it_max);
611 		//reverse the order of the AdjEntries between the found adjEnrty
612 		//and the last adjEntry of nP
613 		last = nP->lastAdj();
614 		it_max = it_max->succ();
615 		if (it_max != nullptr && it_max != last) reverse(vT,it_max,last);
616 		//the next permutation (embedding) was computed
617 		return true;
618 	}
619 	//if vT is a S-node there is only one embedding
620 	return false;
621 }
622 
623 //
624 // computes the next embedding of the skeleton of node *it
625 //
nextEmbedding(ListIterator<node> it)626 bool PlanarSPQRTree::nextEmbedding(ListIterator<node> it)
627 {
628 	//if the last embedding of the actual SPQR-node *it was computed: compute
629 	//the first embedding of *it and the next embedding of *it++
630 	//otherwise: compute the next embedding of *it
631 	if (!nextEmbedding(*it)) {
632 		++it;
633 		//if there is a valid successor of *it in the list of P- and R-nodes: compute
634 		//its next embedding
635 		//otherwise: all embeddings are computed
636 		if (it.valid()) return nextEmbedding(it);
637 		else return false;
638 	}
639 	return true;
640 }
641 
642 }
643