1 /** \file
2  * \brief implements class MultiEdgeApproxInserter
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/planarity/MultiEdgeApproxInserter.h>
34 #include <ogdf/basic/simple_graph_alg.h>
35 #include <ogdf/basic/Queue.h>
36 #include <ogdf/planarity/FixedEmbeddingInserter.h>
37 #include <ogdf/planarity/VariableEmbeddingInserter.h>
38 #include <ogdf/basic/extended_graph_alg.h>
39 #include <ogdf/basic/tuples.h>
40 
41 
42 namespace ogdf {
43 
44 class MultiEdgeApproxInserter::EmbeddingPreference
45 {
46 public:
47 	enum class Type { None, RNode, PNode };
48 
49 	// constructs an embedding preference of type none (= irrelevant)
EmbeddingPreference()50 	EmbeddingPreference() : m_type(Type::None), m_mirror(false) { }
51 
52 	// constructs an embedding preference for an R-node
EmbeddingPreference(bool mirror)53 	explicit EmbeddingPreference(bool mirror) : m_type(Type::RNode), m_mirror(mirror) { }
54 
EmbeddingPreference(adjEntry a1,adjEntry a2)55 	EmbeddingPreference(adjEntry a1, adjEntry a2) : m_type(Type::PNode), m_mirror(false), m_adj1(a1), m_adj2(a2) { }
56 	// constructs an embedding preference for a P-node
57 
type() const58 	Type type() const { return m_type; }
isNull() const59 	bool isNull() const { return m_type == Type::None; }
60 
mirror() const61 	bool mirror() const { return m_mirror; }
adj1() const62 	adjEntry adj1() const { return m_adj1; }
adj2() const63 	adjEntry adj2() const { return m_adj2; }
64 
65 	// flip embedding preference
flip()66 	void flip() {
67 		m_mirror = !m_mirror;
68 		std::swap(m_adj1, m_adj2);
69 	}
70 
71 	static const EmbeddingPreference s_none;
72 
print(std::ostream & os) const73 	std::ostream &print(std::ostream &os) const {
74 		switch(type()) {
75 		case MultiEdgeApproxInserter::EmbeddingPreference::Type::None:
76 			os << "none";
77 			break;
78 		case MultiEdgeApproxInserter::EmbeddingPreference::Type::PNode:
79 			os << "PNode: " << adj1()->index() << "->" << adj2()->index();
80 			break;
81 		case MultiEdgeApproxInserter::EmbeddingPreference::Type::RNode:
82 			os << "RNode: " << mirror();
83 			break;
84 		}
85 		return os;
86 	}
87 
88 private:
89 	Type m_type;
90 	bool m_mirror;  // skeleton of an R-node must be mirrored (yes/no)
91 
92 	adjEntry m_adj1, m_adj2;
93 	// these two adj entries of first node in skeleton of a P-node must be in clockwise order adj1 -> adj2
94 };
95 
96 const MultiEdgeApproxInserter::EmbeddingPreference MultiEdgeApproxInserter::EmbeddingPreference::s_none;
97 
98 
99 class MultiEdgeApproxInserter::Block : public Graph
100 {
101 public:
102 	struct SPQRPath {
SPQRPathogdf::MultiEdgeApproxInserter::Block::SPQRPath103 		SPQRPath() : m_start(nullptr) { }
104 		node       m_start;  // first node in SPQR-tree (its skeleton contains v1)
105 		List<edge> m_edges;  // actual path (empty if v1 and v2 are in skeleton of m_start)
106 		List<EmbeddingPreference> m_prefs;  // embeding preferences along the path
107 	};
108 
109 	struct PathElement {
PathElementogdf::MultiEdgeApproxInserter::Block::PathElement110 		PathElement() : m_node(nullptr), m_pref(&EmbeddingPreference::s_none) { }
111 
112 		node m_node;
113 		const EmbeddingPreference *m_pref;
114 	};
115 
116 
117 	// constructor
Block()118 	Block() :  m_spqr(nullptr), m_embB(nullptr), m_dualB(nullptr), m_faceNodeB(nullptr), m_primalAdjB(nullptr), m_vS(nullptr), m_vT(nullptr)
119 	{
120 		m_BCtoG.init(*this);
121 		m_cost .init(*this,1);
122 	}
123 
124 	// destructoe
~Block()125 	~Block() {
126 		delete m_primalAdjB;
127 		delete m_faceNodeB;
128 		delete m_dualB;
129 		delete m_embB;
130 		delete m_spqr;
131 	}
132 
133 	// returns true iff block is just a bridge (in this case, there is no SPQR-tree!)
isBridge() const134 	bool isBridge() const { return numberOfEdges() < 3; }
135 
cost(edge e) const136 	int cost(edge e) const { return m_cost[e]; }
137 
138 	// initialize SPQR-tree; compute allocation nodes
139 	void initSPQR(int m);
140 
141 	// returns SPQR-tree
spqr() const142 	const StaticPlanarSPQRTree &spqr() const { return *m_spqr; }
spqr()143 	StaticPlanarSPQRTree &spqr() { return *m_spqr; }
144 
145 	// compute traversing costs in skeleton of n; omit skeleton edges e1, e2
146 	void computeTraversingCosts(node n, edge e1, edge e2);
147 
148 	int findShortestPath(node n, edge eRef);
149 
150 	// compute costs of a subpath through skeleton(n) while connecting s with t
151 	int costsSubpath(node n, edge eIn, edge eOut, node s, node t, PathDir &dirFrom, PathDir &dirTo);
152 
153 	void pathToArray(int i, Array<PathElement> &path);
154 
155 	bool embPrefAgree(node n, const EmbeddingPreference &p_pick, const EmbeddingPreference &p_e);
156 
157 	bool switchingPair(node n, node m,
158 		const EmbeddingPreference &p_pick_n, const EmbeddingPreference &p_n,
159 		const EmbeddingPreference &p_pick_m, const EmbeddingPreference &p_m);
160 
161 	int findBestFaces(node s, node t, adjEntry &adj_s, adjEntry &adj_t);
162 	adjEntry findBestFace(node s, node t, int &len);
163 
164 	AdjEntryArray<adjEntry> m_BCtoG; //!< maps adjacency entries in block to original graph
165 	EdgeArray<int> m_cost; //!< costs of an edge (as given for edges in original graph)
166 	NodeArray<ArrayBuffer<node>> m_allocNodes; //!< allocation nodes
167 	Array<SPQRPath> m_pathSPQR; //!< insertion path in SPQR-tree
168 
169 private:
170 	struct RNodeInfo {
RNodeInfoogdf::MultiEdgeApproxInserter::Block::RNodeInfo171 		RNodeInfo() : m_emb(nullptr), m_dual(nullptr), m_faceNode(nullptr), m_primalAdj(nullptr) { }
~RNodeInfoogdf::MultiEdgeApproxInserter::Block::RNodeInfo172 		~RNodeInfo() {
173 			delete m_primalAdj;
174 			delete m_faceNode;
175 			delete m_dual;
176 			delete m_emb;
177 		}
178 
179 		ConstCombinatorialEmbedding *m_emb;       // combinatorial embedding of skeleton graph
180 		Graph                       *m_dual;      // dual graph
181 		FaceArray<node>             *m_faceNode;  // mapping dual node -> face
182 		AdjEntryArray<adjEntry>     *m_primalAdj; // mapping dual adjEntry -> primal adjEntry
183 	};
184 
185 	int recTC(node n, edge eRef);
186 	void constructDual(node n);
187 
188 public:
189 	void constructDualBlock();
190 private:
191 
192 	StaticPlanarSPQRTree *m_spqr;
193 	NodeArray<EdgeArray<int> > m_tc;   // traversing costs
194 
195 	NodeArray<RNodeInfo> m_info;  // additional data for R-node skeletons
196 
197 	ConstCombinatorialEmbedding *m_embB;
198 	Graph                       *m_dualB;
199 	FaceArray<node>             *m_faceNodeB;
200 	AdjEntryArray<adjEntry>     *m_primalAdjB;
201 	node                         m_vS, m_vT;
202 };
203 
204 
pathToArray(int i,Array<PathElement> & path)205 void MultiEdgeApproxInserter::Block::pathToArray(int i, Array<PathElement> &path)
206 {
207 	SPQRPath &sp = m_pathSPQR[i];
208 
209 	if(sp.m_start == nullptr) {
210 		path.init();
211 		return;
212 	}
213 
214 	path.init(1+sp.m_edges.size());
215 
216 	ListConstIterator<edge> itE = sp.m_edges.begin();
217 	ListConstIterator<EmbeddingPreference> itP = sp.m_prefs.begin();
218 
219 	node n = sp.m_start;
220 
221 	path[0].m_node = n;
222 	if(m_spqr->typeOf(n) != SPQRTree::NodeType::SNode)
223 		path[0].m_pref = &(*itP++);
224 
225 	int j;
226 	for(j = 1; itE.valid(); ++j)
227 	{
228 		n = (*itE++)->opposite(n);
229 		path[j].m_node = n;
230 
231 		if(m_spqr->typeOf(n) != SPQRTree::NodeType::SNode)
232 			path[j].m_pref = &(*itP++);
233 	}
234 
235 	OGDF_ASSERT(j == path.size());
236 }
237 
238 
embPrefAgree(node n,const EmbeddingPreference & p_pick,const EmbeddingPreference & p_e)239 bool MultiEdgeApproxInserter::Block::embPrefAgree(node n, const EmbeddingPreference &p_pick, const EmbeddingPreference &p_e)
240 {
241 	switch(m_spqr->typeOf(n)) {
242 	case SPQRTree::NodeType::RNode:
243 		return p_pick.mirror() == p_e.mirror();  // check if mirroring is the same
244 
245 	case SPQRTree::NodeType::PNode:
246 		// if p_e okay: check if adj entries in (embedded) P-Node are in the right order
247 		return p_e.isNull() ? true : p_e.adj1()->cyclicSucc() == p_e.adj2();
248 
249 	default:
250 		return true;  // any other case "agrees"
251 	}
252 }
253 
254 
switchingPair(node n,node m,const EmbeddingPreference & p_pick_n,const EmbeddingPreference & p_n,const EmbeddingPreference & p_pick_m,const EmbeddingPreference & p_m)255 bool MultiEdgeApproxInserter::Block::switchingPair(
256 	node n, node m,
257 	const EmbeddingPreference &p_pick_n, const EmbeddingPreference &p_n,
258 	const EmbeddingPreference &p_pick_m, const EmbeddingPreference &p_m)
259 {
260 	EmbeddingPreference p_n_f = p_n;
261 	EmbeddingPreference p_m_f = p_m;
262 
263 	p_n_f.flip();
264 	p_m_f.flip();
265 
266 	return
267 		(embPrefAgree(n, p_pick_n, p_n) && embPrefAgree(m, p_pick_m, p_m_f)) ||
268 		(embPrefAgree(n, p_pick_n, p_n_f) && embPrefAgree(m, p_pick_m, p_m));
269 }
270 
271 
272 // create SPQR-tree and compute allocation nodes
initSPQR(int m)273 void MultiEdgeApproxInserter::Block::initSPQR(int m)
274 {
275 	if(m_spqr == nullptr) {
276 		m_spqr = new StaticPlanarSPQRTree(*this,true);
277 		m_pathSPQR.init(m);
278 
279 		const Graph &tree = m_spqr->tree();
280 		m_tc.init(tree);
281 		m_info.init(tree);
282 
283 		// compute allocation nodes
284 		m_allocNodes.init(*this);
285 
286 		for(node n : tree.nodes)
287 		{
288 			const Skeleton &S = m_spqr->skeleton(n);
289 			const Graph &M = S.getGraph();
290 
291 			EdgeArray<int> &tcS = m_tc[n];
292 			tcS.init(M,-1);
293 
294 			for(node x : M.nodes)
295 				m_allocNodes[S.original(x)].push(n);
296 
297 			for(edge e : M.edges) {
298 				edge eOrig = S.realEdge(e);
299 				if(eOrig != nullptr) tcS[e] = 1;
300 			}
301 		}
302 	}
303 }
304 
305 
306 // construct dual graph of skeleton of n
constructDual(node n)307 void MultiEdgeApproxInserter::Block::constructDual(node n)
308 {
309 	const StaticSkeleton &S = *dynamic_cast<StaticSkeleton*>(&m_spqr->skeleton(n));
310 	const Graph &M = S.getGraph();
311 
312 	OGDF_ASSERT(m_info[n].m_dual == nullptr);
313 
314 	ConstCombinatorialEmbedding *emb = m_info[n].m_emb = new ConstCombinatorialEmbedding(M);
315 	Graph *dual = m_info[n].m_dual = new Graph;
316 	FaceArray<node> *faceNode = m_info[n].m_faceNode = new FaceArray<node>(*emb);
317 	AdjEntryArray<adjEntry> *primalAdj = m_info[n].m_primalAdj = new AdjEntryArray<adjEntry>(*dual);
318 
319 	// constructs nodes (for faces in emb)
320 	for(face f : emb->faces) {
321 		(*faceNode)[f] = dual->newNode();
322 	}
323 
324 	// construct dual edges (for primal edges in M)
325 	for(node v : M.nodes)
326 	{
327 		for(adjEntry adj : v->adjEntries)
328 		{
329 			if(adj->index() & 1) {
330 				node vLeft  = (*faceNode)[emb->leftFace (adj)];
331 				node vRight = (*faceNode)[emb->rightFace(adj)];
332 
333 				edge eDual = dual->newEdge(vLeft,vRight);
334 				(*primalAdj)[eDual->adjSource()] = adj;
335 				(*primalAdj)[eDual->adjTarget()] = adj->twin();
336 			}
337 		}
338 	}
339 }
340 
341 
constructDualBlock()342 void MultiEdgeApproxInserter::Block::constructDualBlock()
343 {
344 	m_embB = new ConstCombinatorialEmbedding(*this);
345 	m_dualB = new Graph;
346 	m_faceNodeB = new FaceArray<node>(*m_embB);
347 	m_primalAdjB = new AdjEntryArray<adjEntry>(*m_dualB);
348 
349 	// constructs nodes (for faces in m_embB)
350 	for(face f : m_embB->faces) {
351 		(*m_faceNodeB)[f] = m_dualB->newNode();
352 	}
353 
354 	// construct dual edges (for primal edges in block)
355 	for(node v : nodes)
356 	{
357 		for(adjEntry adj : v->adjEntries)
358 		{
359 			if(adj->index() & 1) {
360 				node vLeft  = (*m_faceNodeB)[m_embB->leftFace (adj)];
361 				node vRight = (*m_faceNodeB)[m_embB->rightFace(adj)];
362 
363 				edge eDual = m_dualB->newEdge(vLeft,vRight);
364 				(*m_primalAdjB)[eDual->adjSource()] = adj;
365 				(*m_primalAdjB)[eDual->adjTarget()] = adj->twin();
366 			}
367 		}
368 	}
369 
370 	m_vS = m_dualB->newNode();
371 	m_vT = m_dualB->newNode();
372 }
373 
374 
findBestFace(node s,node t,int & len)375 adjEntry MultiEdgeApproxInserter::Block::findBestFace(node s, node t, int &len)
376 {
377 	if(isBridge()) {
378 		len = 0;
379 		return s->firstAdj();
380 	}
381 
382 	adjEntry adj_s, adj_t;
383 	len = findBestFaces(s,t,adj_s,adj_t);
384 	return adj_s;
385 }
386 
387 
findBestFaces(node s,node t,adjEntry & adj_s,adjEntry & adj_t)388 int MultiEdgeApproxInserter::Block::findBestFaces(
389 	node s, node t, adjEntry &adj_s, adjEntry &adj_t)
390 {
391 	if(m_dualB == nullptr)
392 		constructDualBlock();
393 
394 	NodeArray<adjEntry> spPred(*m_dualB, nullptr);
395 	QueuePure<adjEntry> queue;
396 	int oldIdCount = m_dualB->maxEdgeIndex();
397 
398 	// augment dual by edges from s to all adjacent faces of s ...
399 	for(adjEntry adj : s->adjEntries) {
400 		// starting edges of bfs-search are all edges leaving s
401 		edge eDual = m_dualB->newEdge(m_vS, (*m_faceNodeB)[m_embB->rightFace(adj)]);
402 		(*m_primalAdjB)[eDual->adjSource()] = adj;
403 		(*m_primalAdjB)[eDual->adjTarget()] = nullptr;
404 		queue.append(eDual->adjSource());
405 	}
406 
407 	// ... and from all adjacent faces of t to t
408 	for(adjEntry adj : t->adjEntries) {
409 		edge eDual = m_dualB->newEdge((*m_faceNodeB)[m_embB->rightFace(adj)], m_vT);
410 		(*m_primalAdjB)[eDual->adjSource()] = adj;
411 		(*m_primalAdjB)[eDual->adjTarget()] = nullptr;
412 	}
413 
414 	// actual search (using bfs on directed dual)
415 	int len = -2;
416 	for( ; ;)
417 	{
418 		// next candidate edge
419 		adjEntry adjCand = queue.pop();
420 		node v = adjCand->twinNode();
421 
422 		// leads to an unvisited node?
423 		if (spPred[v] == nullptr)
424 		{
425 			// yes, then we set v's predecessor in search tree
426 			spPred[v] = adjCand;
427 
428 			// have we reached t ...
429 			if (v == m_vT)
430 			{
431 				// ... then search is done.
432 				adj_t = (*m_primalAdjB)[adjCand];
433 
434 				adjEntry adj;
435 				do {
436 					adj = spPred[v];
437 					++len;
438 					v = adj->theNode();
439 				} while(v != m_vS);
440 				adj_s = (*m_primalAdjB)[adj];
441 
442 				break;
443 			}
444 
445 			// append next candidate edges to queue
446 			// (all edges leaving v)
447 			for(adjEntry adj : v->adjEntries) {
448 				if(adj->twinNode() != m_vS)
449 					queue.append(adj);
450 			}
451 		}
452 	}
453 
454 	// remove augmented edges again
455 	adjEntry adj;
456 	while ((adj = m_vS->firstAdj()) != nullptr)
457 		m_dualB->delEdge(adj->theEdge());
458 
459 	while ((adj = m_vT->firstAdj()) != nullptr)
460 		m_dualB->delEdge(adj->theEdge());
461 
462 	m_dualB->resetEdgeIdCount(oldIdCount);
463 
464 	return len;
465 }
466 
467 
468 // compute traversing costs in skelton
recTC(node n,edge eRef)469 int MultiEdgeApproxInserter::Block::recTC(node n, edge eRef)
470 {
471 	const StaticSkeleton &S = *dynamic_cast<StaticSkeleton*>(&m_spqr->skeleton(n));
472 	const Graph &M = S.getGraph();
473 	EdgeArray<int> &tcS = m_tc[n];
474 
475 	for(edge e : M.edges) {
476 		if(tcS[e] == -1 && e != eRef) {
477 			edge eT = S.treeEdge(e);
478 
479 			node nC;
480 			edge eRefC;
481 			if(n == eT->source()) {
482 				nC = eT->target(); eRefC = m_spqr->skeletonEdgeTgt(eT);
483 			} else {
484 				nC = eT->source(); eRefC = m_spqr->skeletonEdgeSrc(eT);
485 			}
486 
487 			tcS[e] = recTC(nC,eRefC);
488 		}
489 	}
490 
491 	int c = 1;
492 	switch(m_spqr->typeOf(n))
493 	{
494 	case SPQRTree::NodeType::SNode:
495 		c = std::numeric_limits<int>::max();
496 		for(edge e : M.edges)
497 			if(e != eRef) c = min(c, tcS[e]);
498 		break;
499 
500 	case SPQRTree::NodeType::PNode:
501 		c = 0;
502 		for(edge e : M.edges)
503 			if(e != eRef) c += tcS[e];
504 		break;
505 
506 	case SPQRTree::NodeType::RNode:
507 		if(m_info[n].m_dual == nullptr)
508 			constructDual(n);
509 
510 		c = findShortestPath(n, eRef);
511 		break;
512 	}
513 
514 	return c;
515 }
516 
computeTraversingCosts(node n,edge e1,edge e2)517 void MultiEdgeApproxInserter::Block::computeTraversingCosts(node n, edge e1, edge e2)
518 {
519 	const StaticSkeleton &S = *dynamic_cast<StaticSkeleton*>(&m_spqr->skeleton(n));
520 	const Graph &M = S.getGraph();
521 	EdgeArray<int> &tcS = m_tc[n];
522 
523 	for(edge e : M.edges) {
524 		if(tcS[e] == -1 && e != e1 && e != e2) {
525 			edge eT = S.treeEdge(e);
526 
527 			node nC;
528 			edge eRef;
529 			if(n == eT->source()) {
530 				nC = eT->target(); eRef = m_spqr->skeletonEdgeTgt(eT);
531 			} else {
532 				nC = eT->source(); eRef = m_spqr->skeletonEdgeSrc(eT);
533 			}
534 
535 			tcS[e] = recTC(nC,eRef);
536 		}
537 	}
538 }
539 
540 
findShortestPath(node n,edge eRef)541 int  MultiEdgeApproxInserter::Block::findShortestPath(node n, edge eRef)
542 {
543 	const StaticSkeleton &S = *dynamic_cast<StaticSkeleton*>(&m_spqr->skeleton(n));
544 	const Graph &M = S.getGraph();
545 	const EdgeArray<int> &tcS = m_tc[n];
546 
547 	const Graph &dual = *m_info[n].m_dual;
548 	const ConstCombinatorialEmbedding &emb = *m_info[n].m_emb;
549 	const FaceArray<node> faceNode = *m_info[n].m_faceNode;
550 	const AdjEntryArray<adjEntry> primalAdj = *m_info[n].m_primalAdj;
551 
552 	int maxTC = 0;
553 	for(edge e : M.edges)
554 		Math::updateMax(maxTC, tcS[e]);
555 
556 	++maxTC;
557 	Array<SListPure<adjEntry> > nodesAtDist(maxTC);
558 
559 	NodeArray<adjEntry> spPred(dual,nullptr); // predecessor in shortest path tree
560 
561 	node vS = faceNode[emb.rightFace(eRef->adjSource())];
562 	node vT = faceNode[emb.rightFace(eRef->adjTarget())];
563 
564 	// start with all edges leaving from vS
565 	for(adjEntry adj : vS->adjEntries) {
566 		edge eOrig = primalAdj[adj]->theEdge();
567 		if(eOrig != eRef)
568 			nodesAtDist[tcS[eOrig]].pushBack(adj);
569 	}
570 
571 	int currentDist = 0;
572 	for( ; ; ) {
573 		// next candidate edge
574 		while(nodesAtDist[currentDist % maxTC].empty())
575 			++currentDist;
576 
577 		adjEntry adjCand = nodesAtDist[currentDist % maxTC].popFrontRet();
578 		node v = adjCand->twinNode();
579 
580 		// leads to an unvisited node ?
581 		if (spPred[v] == nullptr) {
582 			// yes, then we set v's predecessor in search tree
583 			spPred[v] = adjCand;
584 
585 			// have we reached t ...
586 			if (v == vT) {
587 				// ... then search is done.
588 				return currentDist;
589 			}
590 
591 			// append next candidates to end of queue
592 			for(adjEntry adj : v->adjEntries) {
593 				int listPos = (currentDist + tcS[primalAdj[adj]->theEdge()]) % maxTC;
594 				nodesAtDist[listPos].pushBack(adj);
595 			}
596 		}
597 	}
598 }
599 
600 
costsSubpath(node n,edge eIn,edge eOut,node s,node t,PathDir & dirFrom,PathDir & dirTo)601 int MultiEdgeApproxInserter::Block::costsSubpath(node n, edge eIn, edge eOut, node s, node t, PathDir &dirFrom, PathDir &dirTo)
602 {
603 	if(m_info[n].m_dual == nullptr)
604 		constructDual(n);
605 
606 	const StaticSkeleton &S = *dynamic_cast<StaticSkeleton*>(&m_spqr->skeleton(n));
607 	const Graph &M = S.getGraph();
608 	const EdgeArray<int> &tcS = m_tc[n];
609 
610 	const Graph &dual = *m_info[n].m_dual;
611 	const ConstCombinatorialEmbedding &emb = *m_info[n].m_emb;
612 	const FaceArray<node> faceNode = *m_info[n].m_faceNode;
613 	const AdjEntryArray<adjEntry> primalAdj = *m_info[n].m_primalAdj;
614 
615 	node v1 = nullptr, v2 = nullptr;
616 	if(eIn == nullptr || eOut == nullptr) {
617 		for(node v : M.nodes) {
618 			node vOrig = S.original(v);
619 			if(vOrig == s) v1 = v;
620 			if(vOrig == t) v2 = v;
621 		}
622 	}
623 
624 	edge e1 = (eIn  == nullptr) ? nullptr : ((n != eIn ->source()) ? m_spqr->skeletonEdgeTgt(eIn)  : m_spqr->skeletonEdgeSrc(eIn));
625 	edge e2 = (eOut == nullptr) ? nullptr : ((n != eOut->source()) ? m_spqr->skeletonEdgeTgt(eOut) : m_spqr->skeletonEdgeSrc(eOut));
626 
627 	computeTraversingCosts(n, e1, e2);
628 
629 	int maxTC = 0;
630 	for(edge e : M.edges)
631 		Math::updateMax(maxTC, tcS[e]);
632 
633 	++maxTC;
634 	Array<SListPure<Tuple2<node,node> > > nodesAtDist(maxTC);
635 
636 	// start vertices
637 	if(e1 != nullptr) {
638 		nodesAtDist[0].pushBack( Tuple2<node,node>(faceNode[emb.rightFace(e1->adjSource())], nullptr) );
639 		nodesAtDist[0].pushBack( Tuple2<node,node>(faceNode[emb.rightFace(e1->adjTarget())], nullptr) );
640 	} else {
641 		for(adjEntry adj : v1->adjEntries)
642 			nodesAtDist[0].pushBack( Tuple2<node,node>(faceNode[emb.rightFace(adj)], nullptr) );
643 	}
644 
645 	// stop vertices
646 	NodeArray<bool> stopVertex(dual,false);
647 
648 	if(e2 != nullptr) {
649 		stopVertex[faceNode[emb.rightFace(e2->adjSource())]] = true;
650 		stopVertex[faceNode[emb.rightFace(e2->adjTarget())]] = true;
651 	} else {
652 		for(adjEntry adj : v2->adjEntries)
653 			stopVertex[faceNode[emb.rightFace(adj)]] = true;
654 	}
655 
656 	// actual shortest path search
657 	NodeArray<bool> visited(dual,false);
658 	NodeArray<node> spPred(dual,nullptr);
659 
660 	int currentDist = 0;
661 	for( ; ; ) {
662 		// next candidate
663 		while(nodesAtDist[currentDist % maxTC].empty())
664 			++currentDist;
665 
666 		Tuple2<node,node> pair = nodesAtDist[currentDist % maxTC].popFrontRet();
667 		node v = pair.x1();
668 
669 		// leads to an unvisited node ?
670 		if (!visited[v]) {
671 			visited[v] = true;
672 			spPred[v] = pair.x2();
673 
674 			// have we reached t ...
675 			if (stopVertex[v]) {
676 				// ... then search is done.
677 
678 				// find start node w
679 				node w = v;
680 				while(spPred[w] != nullptr)
681 					w = spPred[w];
682 
683 				// in which direction to we start
684 				if(e1 == nullptr)
685 					dirFrom = PathDir::None;  // doesn't matter
686 				else if(w == faceNode[emb.rightFace(e1->adjSource())])
687 					dirFrom = PathDir::Right; // right face of eIn
688 				else
689 					dirFrom = PathDir::Left;  // left face of eIn
690 
691 				// from which direction to we enter
692 				if(e2 == nullptr)
693 					dirTo = PathDir::None;  // doesn't matter
694 				else if(v == faceNode[emb.rightFace(e2->adjSource())])
695 					dirTo = PathDir::Left; // right face of eOut (leaving to the right)
696 				else
697 					dirTo = PathDir::Right; // left face of eOut (leaving to the left)
698 
699 				return currentDist;
700 			}
701 
702 			// append next candidates to end of queue
703 			for(adjEntry adj : v->adjEntries) {
704 				edge eM = primalAdj[adj]->theEdge();
705 				if(eM != e1 && eM != e2) {
706 					int listPos = (currentDist + tcS[eM]) % maxTC;
707 					nodesAtDist[listPos].pushBack(
708 						Tuple2<node,node>(adj->twinNode(), v) );
709 				}
710 			}
711 		}
712 	}
713 }
714 
715 
716 // sets default values for options
MultiEdgeApproxInserter()717 MultiEdgeApproxInserter::MultiEdgeApproxInserter()
718 {
719 	// options
720 	m_rrOptionFix = RemoveReinsertType::None;
721 	m_rrOptionVar = RemoveReinsertType::None;
722 	m_percentMostCrossedFix = 25;
723 	m_percentMostCrossedVar = 25;
724 	m_statistics = false;
725 
726 	// initialization
727 	m_edge = nullptr;
728 	m_sumInsertionCosts = 0;
729 	m_sumFEInsertionCosts = 0;
730 }
731 
732 
733 // copy constructor
MultiEdgeApproxInserter(const MultiEdgeApproxInserter & inserter)734 MultiEdgeApproxInserter::MultiEdgeApproxInserter(const MultiEdgeApproxInserter &inserter)
735 	: EdgeInsertionModule()
736 {
737 	// options
738 	m_rrOptionFix = inserter.m_rrOptionFix;
739 	m_rrOptionVar = inserter.m_rrOptionVar;
740 	m_percentMostCrossedFix = inserter.m_percentMostCrossedFix;
741 	m_percentMostCrossedVar = inserter.m_percentMostCrossedVar;
742 	m_statistics = inserter.m_statistics;
743 
744 	// initialization
745 	m_edge = nullptr;
746 	m_sumInsertionCosts = 0;
747 	m_sumFEInsertionCosts = 0;
748 }
749 
750 
751 // clone method
clone() const752 EdgeInsertionModule *MultiEdgeApproxInserter::clone() const
753 {
754 	return new MultiEdgeApproxInserter(*this);
755 }
756 
757 
758 // assignment operator
operator =(const MultiEdgeApproxInserter & inserter)759 MultiEdgeApproxInserter &MultiEdgeApproxInserter::operator=(const MultiEdgeApproxInserter &inserter)
760 {
761 	// options
762 	m_rrOptionFix = inserter.m_rrOptionFix;
763 	m_rrOptionVar = inserter.m_rrOptionVar;
764 	m_percentMostCrossedFix = inserter.m_percentMostCrossedFix;
765 	m_percentMostCrossedVar = inserter.m_percentMostCrossedVar;
766 	m_statistics = inserter.m_statistics;
767 
768 	return *this;
769 }
770 
771 
772 // construct graph of block i
773 // store mapping of original nodes to copies in blocks
constructBlock(int i)774 MultiEdgeApproxInserter::Block *MultiEdgeApproxInserter::constructBlock(int i)
775 {
776 	Block *b = new Block;
777 	SList<node> nodesG;
778 
779 	SListConstIterator<edge> itE;
780 	for(itE = m_edgesB[i].begin(); itE.valid(); ++itE)
781 	{
782 		edge e = *itE;
783 
784 		if (m_GtoBC[e->source()] == nullptr) {
785 			m_GtoBC[e->source()] = b->newNode();
786 			nodesG.pushBack(e->source());
787 		}
788 		if (m_GtoBC[e->target()] == nullptr) {
789 			m_GtoBC[e->target()] = b->newNode();
790 			nodesG.pushBack(e->target());
791 		}
792 
793 		edge eBC = b->newEdge(m_GtoBC[e->source()],m_GtoBC[e->target()]);
794 		b->m_BCtoG[eBC->adjSource()] = e->adjSource();
795 		b->m_BCtoG[eBC->adjTarget()] = e->adjTarget();
796 
797 		edge eOrig = m_pPG->original(e);
798 		if(m_costOrig != nullptr)
799 			b->m_cost[eBC] = (eOrig == nullptr) ? 0 : (*m_costOrig)[eOrig];
800 	}
801 
802 	// store mapping orginal nodes -> copy in blocks and reset entries of GtoBC
803 	SListConstIterator<node> itV;
804 	for(itV = nodesG.begin(); itV.valid(); ++itV) {
805 		node v = *itV;
806 		m_copyInBlocks[v].pushBack(VertexBlock(m_GtoBC[v],i));
807 		m_GtoBC[v] = nullptr;
808 	}
809 
810 	planarEmbed(*b);
811 
812 	return b;
813 }
814 
815 
816 // returns copy of node vOrig in block b
copy(node vOrig,int b)817 node MultiEdgeApproxInserter::copy(node vOrig, int b)
818 {
819 	SListConstIterator<VertexBlock> it;
820 	for(it = m_copyInBlocks[vOrig].begin(); it.valid(); ++it) {
821 		if((*it).m_block == b)
822 			return (*it).m_vertex;
823 	}
824 
825 	return nullptr;
826 }
827 
828 
829 // DFS-traversal for computing insertion path in SPQR-tree
dfsPathSPQR(node v,node v2,edge eParent,List<edge> & path)830 bool MultiEdgeApproxInserter::dfsPathSPQR(node v, node v2, edge eParent, List<edge> &path)
831 {
832 	if(v == v2)
833 		return true;
834 
835 	for(adjEntry adj : v->adjEntries) {
836 		edge e = adj->theEdge();
837 		if(e == eParent) continue;
838 
839 		if(dfsPathSPQR(e->opposite(v), v2, e, path)) {
840 			path.pushFront(e);
841 			return true;
842 		}
843 	}
844 
845 	return false;
846 }
847 
848 
849 // compute insertion path of edge edge k in SPQR-tree of block b
850 // from node vOrig to node wOrig
computePathSPQR(int b,node vOrig,node wOrig,int k)851 int MultiEdgeApproxInserter::computePathSPQR(int b, node vOrig, node wOrig, int k)
852 {
853 	Block &B = *m_block[b];
854 
855 	node v = copy(vOrig,b);
856 	node w = copy(wOrig,b);
857 	OGDF_ASSERT(v != nullptr);
858 	OGDF_ASSERT(w != nullptr);
859 
860 	B.initSPQR(m_edge->size());
861 
862 	const auto &vAllocNodes = B.m_allocNodes[v];
863 	const auto &wAllocNodes = B.m_allocNodes[w];
864 	List<edge> &path = B.m_pathSPQR[k].m_edges;
865 
866 	node v1 = vAllocNodes[0];
867 	node v2 = wAllocNodes[0];
868 
869 	dfsPathSPQR( v1, v2, nullptr, path );
870 
871 	node x;
872 	while(!path.empty() && vAllocNodes.linearSearch(x = path.front()->opposite(v1)) != -1) {
873 		v1 = x;
874 		path.popFront();
875 	}
876 
877 	B.m_pathSPQR[k].m_start = v1;
878 
879 	while(!path.empty() && wAllocNodes.linearSearch(x = path.back()->opposite(v2)) != -1) {
880 		v2 = x;
881 		path.popBack();
882 	}
883 
884 	// compute insertion costs
885 	const StaticPlanarSPQRTree &spqr = B.spqr();
886 	List<EmbeddingPreference> &prefs = B.m_pathSPQR[k].m_prefs;
887 
888 	PathDir dirFrom, dirTo;
889 	PathDir curDir = PathDir::Right;
890 	int c = 0;
891 
892 	switch(spqr.typeOf(v1)) {
893 	case SPQRTree::NodeType::RNode:
894 		c += B.costsSubpath(v1, nullptr, (path.empty()) ? nullptr : path.front(), v, w, dirFrom, dirTo);
895 		prefs.pushBack(EmbeddingPreference(false));
896 		curDir = dirTo;
897 		break;
898 
899 	case SPQRTree::NodeType::PNode:
900 		// in this case, the insertion path is a single P-node and we have no preference
901 		prefs.pushBack(EmbeddingPreference());
902 		break;
903 
904 	case SPQRTree::NodeType::SNode:
905 		break; // nothing to do
906 	}
907 
908 	node n = v1;
909 	ListConstIterator<edge> it;
910 	for(it = path.begin(); it.valid(); ++it)
911 	{
912 		edge e = *it;
913 		n = e->opposite(n);
914 
915 		switch(spqr.typeOf(n)) {
916 		case SPQRTree::NodeType::RNode:
917 			c += B.costsSubpath(n, e, it.succ().valid() ? *it.succ() : nullptr, v, w, dirFrom, dirTo);
918 
919 			// do we need to mirror embedding of R-node?
920 			prefs.pushBack(EmbeddingPreference(dirFrom != curDir));
921 			curDir = dirFrom == curDir ? dirTo : oppDir(dirTo);
922 			break;
923 
924 		case SPQRTree::NodeType::PNode:
925 			{
926 			edge eIn = e;
927 			edge eOut = *(it.succ());
928 
929 			edge e1 = (n != eIn ->source()) ? spqr.skeletonEdgeTgt(eIn)  : spqr.skeletonEdgeSrc(eIn);
930 			edge e2 = (n != eOut->source()) ? spqr.skeletonEdgeTgt(eOut) : spqr.skeletonEdgeSrc(eOut);
931 
932 			const Graph &M = spqr.skeleton(n).getGraph();
933 			node first = M.firstNode();
934 
935 			adjEntry a1 = (e1->source() == first) ? e1->adjSource() : e1->adjTarget();
936 			adjEntry a2 = (e2->source() == first) ? e2->adjSource() : e2->adjTarget();
937 
938 			bool srcFits = (e1->source() == first);
939 			if( (curDir == PathDir::Left && srcFits) || (curDir == PathDir::Right && !srcFits) )
940 				std::swap(a1, a2);
941 			prefs.pushBack(EmbeddingPreference(a1,a2));
942 
943 			if(e1->source() != e2->source())
944 				curDir = oppDir(curDir);
945 			}
946 			break;
947 
948 		case SPQRTree::NodeType::SNode:
949 			if(it.succ().valid()) {
950 				edge eIn = e;
951 				edge eOut = *(it.succ());
952 
953 				edge e1 = (n != eIn ->source()) ? spqr.skeletonEdgeTgt(eIn)  : spqr.skeletonEdgeSrc(eIn);
954 				edge e2 = (n != eOut->source()) ? spqr.skeletonEdgeTgt(eOut) : spqr.skeletonEdgeSrc(eOut);
955 
956 				adjEntry at = e1->adjSource();
957 				while(at->theEdge() != e2)
958 					at = at->twin()->cyclicSucc();
959 
960 				if(at == e2->adjSource())
961 					curDir = oppDir(curDir);
962 			}
963 			break; // nothing to do
964 		}
965 	}
966 
967 	return c;
968 }
969 
970 
971 // DFS-traversal for computing insertion path in BC-tree
972 // (case vertex v)
dfsPathVertex(node v,int parent,int k,node t)973 bool MultiEdgeApproxInserter::dfsPathVertex(node v, int parent, int k, node t)
974 {
975 	if(v == t) return true;
976 
977 	SListConstIterator<int> it;
978 	for(it = m_compV[v].begin(); it.valid(); ++it) {
979 		if(*it == parent) continue;
980 		if(dfsPathBlock(*it,v,k,t)) return true;
981 	}
982 	return false;
983 }
984 
985 
986 // DFS-traversal for computing insertion path in BC-tree
987 // (case block b)
dfsPathBlock(int b,node parent,int k,node t)988 bool MultiEdgeApproxInserter::dfsPathBlock(int b, node parent, int k, node t)
989 {
990 	SListConstIterator<node> it;
991 	for(it = m_verticesB[b].begin(); it.valid(); ++it)
992 	{
993 		node c = *it;
994 		if(c == parent) continue;
995 		if(dfsPathVertex(c,b,k,t)) {
996 			m_pathBCs[k].pushFront(VertexBlock(parent,b));
997 
998 			if(!m_block[b]->isBridge())
999 			{
1000 				// find path from parent to c in block b and compute embedding preferences
1001 				m_insertionCosts[k] += computePathSPQR(b, parent, c, k);
1002 			}
1003 
1004 			return true;
1005 		}
1006 	}
1007 	return false;
1008 }
1009 
1010 
1011 // compute insertion path of edge edge k in BC-tree
computePathBC(int k)1012 void MultiEdgeApproxInserter::computePathBC(int k)
1013 {
1014 	node s = m_pPG->copy((*m_edge)[k]->source());
1015 	node t = m_pPG->copy((*m_edge)[k]->target());
1016 
1017 	bool found = dfsPathVertex(s, -1, k, t);
1018 	if(!found) std::cout << "Could not find path in BC-tree!" << std::endl;
1019 }
1020 
1021 
1022 // Embeds block b according to combined embedding
1023 // preference s of the k insertion paths
embedBlock(int b,int m)1024 void MultiEdgeApproxInserter::embedBlock(int b, int m)
1025 {
1026 	// Algorithm 3.7.
1027 
1028 	Block &B = *m_block[b];
1029 	if(B.isBridge())
1030 		return;
1031 	StaticPlanarSPQRTree &spqr = B.spqr();
1032 
1033 	// A.
1034 	NodeArray<EmbeddingPreference> pi_pick(spqr.tree());
1035 	NodeArray<bool> visited(spqr.tree(), false);  // mark nodes with embedding preferences
1036 	NodeArray<bool> markFlipped(spqr.tree(), false);  // mark nodes on current path to get flipped
1037 	Array<Block::PathElement> p;
1038 
1039 	// B.
1040 	for(int i = 0; i < m; ++i)
1041 	{
1042 		// (a)
1043 		B.pathToArray(i, p);
1044 		const int len = p.size();
1045 		if(len == 0) continue;  // this path does not go through B
1046 
1047 		int j = 0;
1048 		do {
1049 			node n = p[j].m_node;
1050 
1051 			// (b)
1052 			bool newlySet = false;
1053 			if(pi_pick[n].isNull()) {
1054 				newlySet = true;
1055 				pi_pick[n] = *p[j].m_pref;
1056 
1057 				if(spqr.typeOf(n) == SPQRTree::NodeType::PNode) {
1058 					adjEntry a1 = pi_pick[n].adj1();
1059 					adjEntry a2 = pi_pick[n].adj2();
1060 					adjEntry adj = a1->cyclicSucc();
1061 
1062 					if(adj != a2)
1063 						spqr.swap(n,adj,a2);
1064 
1065 					OGDF_ASSERT(a1->cyclicSucc() == a2);
1066 				}
1067 			}
1068 
1069 			// (c)
1070 
1071 			// determine non-S-node predecessor of n
1072 			int j_mu = -1;
1073 			if(j > 0) {
1074 				node x = p[j-1].m_node;
1075 				if(spqr.typeOf(x) != SPQRTree::NodeType::SNode)
1076 					j_mu = j-1;
1077 				else if(j > 1)
1078 					j_mu = j-2;
1079 			}
1080 
1081 			if(j_mu >= 0)
1082 			{
1083 				node mu = p[j_mu].m_node;
1084 
1085 				// do we have a switching pair (mu,n)?
1086 				if(B.switchingPair(mu, n, pi_pick[mu], *p[j_mu].m_pref, pi_pick[n], *p[j].m_pref))
1087 					markFlipped[mu] = true;  // flip embedding at mu
1088 			}
1089 
1090 			// (d)
1091 			if(!newlySet) {
1092 				// skip nodes in same embedding partition
1093 				while(j+1 < len && visited[p[j+1].m_node])
1094 					++j;
1095 			}
1096 
1097 			// next node
1098 			if(++j >= len)
1099 				break; // end of insertion path
1100 
1101 			// skip S-node (can only be one)
1102 			if(spqr.typeOf(p[j].m_node) == SPQRTree::NodeType::SNode)
1103 				++j;
1104 
1105 		} while(j < len);
1106 
1107 		// flip embedding preferences
1108 		bool flipping = false;
1109 		for(int iter = len-1; iter >= 0; --iter)
1110 		{
1111 			node n = p[iter].m_node;
1112 			if(markFlipped[n]) {
1113 				flipping = !flipping;
1114 				markFlipped[n] = false;
1115 			}
1116 
1117 			if(flipping) {
1118 				pi_pick[n].flip();
1119 				if(pi_pick[n].type() == EmbeddingPreference::Type::PNode)
1120 					spqr.reverse(n);
1121 
1122 				if(visited[n]) {
1123 					node n_succ = (iter+1 < len) ? p[iter+1].m_node : nullptr;
1124 					node n_pred = (iter-1 >=  0) ? p[iter-1].m_node : nullptr;
1125 
1126 					for(adjEntry adj : n->adjEntries) {
1127 						node x = adj->twinNode();
1128 						if(x != n_succ && x != n_pred && visited[x])
1129 							recFlipPref(adj->twin(), pi_pick, visited, spqr);
1130 					}
1131 				}
1132 			}
1133 
1134 			visited[n] = true;
1135 		}
1136 	}
1137 
1138 	// C.
1139 	for(node n : spqr.tree().nodes)
1140 	{
1141 		const EmbeddingPreference &ep = pi_pick[n];
1142 		if(ep.isNull())
1143 			continue;
1144 
1145 		switch(spqr.typeOf(n)) {
1146 		case SPQRTree::NodeType::SNode:
1147 			break;  // nothing to do
1148 
1149 		case SPQRTree::NodeType::PNode:
1150 			break;  // already embedded as desired
1151 
1152 		case SPQRTree::NodeType::RNode:
1153 			if(ep.mirror())
1154 				spqr.reverse(n);
1155 			break;
1156 		}
1157 	}
1158 
1159 	spqr.embed(B);
1160 }
1161 
1162 
recFlipPref(adjEntry adjP,NodeArray<EmbeddingPreference> & pi_pick,const NodeArray<bool> & visited,StaticPlanarSPQRTree & spqr)1163 void MultiEdgeApproxInserter::recFlipPref(
1164 	adjEntry adjP,
1165 	NodeArray<EmbeddingPreference> &pi_pick,
1166 	const NodeArray<bool> &visited,
1167 	StaticPlanarSPQRTree &spqr)
1168 {
1169 	node n = adjP->theNode();
1170 
1171 	EmbeddingPreference &pref = pi_pick[n];
1172 	pref.flip();
1173 	if(pref.type() == EmbeddingPreference::Type::PNode)
1174 		spqr.reverse(n);
1175 
1176 	for(adjEntry adj : n->adjEntries) {
1177 		if(adj != adjP && visited[adj->twinNode()])
1178 			recFlipPref(adj->twin(), pi_pick, visited, spqr);
1179 	}
1180 }
1181 
1182 
1183 // actual call method
1184 // currently not supported:
1185 //   frobidCrossingGens, forbiddenEdgeOrig, edgeSubGraphs
1186 const char *strType[] = { "S", "P", "R" };
1187 //#define MEAI_OUTPUT
1188 
1189 struct CutvertexPreference {
CutvertexPreferenceogdf::CutvertexPreference1190 	CutvertexPreference(node v1, int b1, node v2, int b2)
1191 		: m_v1(v1), m_v2(v2), m_b1(b1), m_b2(b2), m_len1(-1), m_len2(-1) { }
1192 
1193 	node m_v1, m_v2;
1194 	int  m_b1, m_b2;
1195 	int m_len1, m_len2;
1196 };
1197 
appendToList(SListPure<adjEntry> & adjList,adjEntry adj1,const AdjEntryArray<adjEntry> & BCtoG,AdjEntryArray<SListIterator<adjEntry>> & pos)1198 void appendToList(SListPure<adjEntry> &adjList, adjEntry adj1,
1199 	const AdjEntryArray<adjEntry> &BCtoG,
1200 	AdjEntryArray<SListIterator<adjEntry> > &pos)
1201 {
1202 	adjEntry adj = adj1;
1203 	do {
1204 		adj = adj->cyclicSucc();
1205 		adjEntry adjG = BCtoG[adj];
1206 		pos[adjG] = adjList.pushBack(adjG);
1207 	} while(adj != adj1);
1208 }
1209 
insertAfterList(SListPure<adjEntry> & adjList,SListIterator<adjEntry> itBefore,adjEntry adj1,const AdjEntryArray<adjEntry> & BCtoG,AdjEntryArray<SListIterator<adjEntry>> & pos)1210 void insertAfterList(SListPure<adjEntry> &adjList, SListIterator<adjEntry> itBefore, adjEntry adj1,
1211 	const AdjEntryArray<adjEntry> &BCtoG,
1212 	AdjEntryArray<SListIterator<adjEntry> > &pos)
1213 {
1214 	adjEntry adj = adj1;
1215 	do {
1216 		adj = adj->cyclicSucc();
1217 		adjEntry adjG = BCtoG[adj];
1218 		itBefore = pos[adjG] = adjList.insertAfter(adjG, itBefore);
1219 	} while(adj != adj1);
1220 }
1221 
doCall(PlanRepLight & PG,const Array<edge> & origEdges,const EdgeArray<int> * costOrig,const EdgeArray<bool> *,const EdgeArray<uint32_t> *)1222 Module::ReturnType MultiEdgeApproxInserter::doCall(
1223 	PlanRepLight&                 PG,
1224 	const Array<edge>&            origEdges,
1225 	const EdgeArray<int>*         costOrig,
1226 	const EdgeArray<bool>*        /* unused parameter */,
1227 	const EdgeArray<uint32_t>*    /* unused parameter */)
1228 {
1229 	m_pPG = &PG;
1230 	m_costOrig = costOrig;
1231 	const int m = origEdges.size();
1232 
1233 	m_edge = &origEdges;
1234 	OGDF_ASSERT(m_edge->low() == 0);
1235 	m_pathBCs.init(m);
1236 	m_insertionCosts.init(0,m-1,0);
1237 
1238 	//
1239 	// PHASE 1: Fix a good embedding
1240 	//
1241 
1242 	// compute biconnected components of PG
1243 	EdgeArray<int> compnum(PG);
1244 	int c = biconnectedComponents(PG,compnum);
1245 
1246 	m_compV.init(PG);
1247 	m_verticesB.init(c);
1248 
1249 	// edgesB[i] = list of edges in component i
1250 	m_edgesB.init(c);
1251 	for(edge e : PG.edges)
1252 		m_edgesB[compnum[e]].pushBack(e);
1253 
1254 	// construct arrays compV and nodeB such that
1255 	// m_compV[v] = list of components containing v
1256 	// m_verticesB[i] = list of vertices in component i
1257 	NodeArray<bool> mark(PG,false);
1258 
1259 	for(int i = 0; i < c; ++i) {
1260 		for (edge e : m_edgesB[i]) {
1261 			if (!mark[e->source()]) {
1262 				mark[e->source()] = true;
1263 				m_verticesB[i].pushBack(e->source());
1264 			}
1265 			if (!mark[e->target()]) {
1266 				mark[e->target()] = true;
1267 				m_verticesB[i].pushBack(e->target());
1268 			}
1269 		}
1270 
1271 		for (node v : m_verticesB[i]) {
1272 			m_compV[v].pushBack(i);
1273 			mark[v] = false;
1274 		}
1275 	}
1276 	mark.init();
1277 	m_GtoBC.init(PG,nullptr);
1278 	m_copyInBlocks.init(PG);
1279 
1280 	m_block.init(c);
1281 	for(int i = 0; i < c; ++i)
1282 		m_block[i] = constructBlock(i);
1283 
1284 	m_sumInsertionCosts = 0;
1285 	for(int i = 0; i < m; ++i) {
1286 		computePathBC(i);
1287 		m_sumInsertionCosts += m_insertionCosts[i];
1288 
1289 #ifdef MEAI_OUTPUT
1290 		std::cout << "(" << PG.copy((*m_edge)[i]->source()) << "," << PG.copy((*m_edge)[i]->target()) << ")  c = " << m_insertionCosts[i] << ":\n";
1291 		for (const auto &vblock : m_pathBCs[i]) {
1292 			int b = vblock.m_block;
1293 			const StaticSPQRTree &spqr = m_block[b]->spqr();
1294 
1295 			std::cout << "   [ " << "V_" << vblock.m_vertex << ", B_" << b << " ]\n";
1296 			std::cout << "      ";
1297 			if(m_block[b]->isBridge()) {
1298 				std::cout << "BRIDGE";
1299 			} else {
1300 				node x = m_block[b]->m_pathSPQR[i].m_start;
1301 				ListConstIterator<EmbeddingPreference> itP = m_block[b]->m_pathSPQR[i].m_prefs.begin();
1302 				auto output = [&](node v) {
1303 					SPQRTree::NodeType t = spqr.typeOf(v);
1304 					std::cout << strType[static_cast<int>(t)] << "_" << v;
1305 					if (t == SPQRTree::NodeType::RNode) {
1306 						if ((*itP).type() == EmbeddingPreference::Type::None) {
1307 							std::cout << " (NONE)";
1308 						} else if ((*itP).mirror()) {
1309 							std::cout << " (MIRROR)";
1310 						} else {
1311 							std::cout << " (KEEP)";
1312 						}
1313 						++itP;
1314 					} else if (t == SPQRTree::NodeType::PNode) {
1315 						if ((*itP).type() == EmbeddingPreference::Type::None) {
1316 							std::cout << " (NONE)";
1317 						} else {
1318 							std::cout << "(ADJ:" << (*itP).adj1()->index() << ";" << (*itP).adj2()->index() << ")";
1319 						}
1320 						++itP;
1321 					}
1322 				};
1323 				output(x);
1324 
1325 				for (edge e : m_block[b]->m_pathSPQR[i].m_edges) {
1326 					node y = e->opposite(x);
1327 					std::cout << " -> ";
1328 					output(y);
1329 					x = y;
1330 				}
1331 			}
1332 			std::cout << std::endl;
1333 		}
1334 #endif
1335 	}
1336 
1337 	// embed blocks
1338 	for(int i = 0; i < c; ++i)
1339 		embedBlock(i,m);
1340 
1341 
1342 	// find embedding preferences at cutvertices
1343 	NodeArray<SList<CutvertexPreference> > cvPref(PG);
1344 
1345 	for(int i = 0; i < m; ++i)
1346 	{
1347 		const List<VertexBlock> &L =  m_pathBCs[i];
1348 		if(L.size() < 2)
1349 			continue;
1350 
1351 		ListConstIterator<VertexBlock> it = L.begin();
1352 		node last_v = (*it).m_vertex;
1353 		int  last_b = (*it).m_block;
1354 		++it;
1355 		do {
1356 			node v2 = (it.succ().valid()) ? (*it.succ()).m_vertex : PG.copy((*m_edge)[i]->target());
1357 			cvPref[(*it).m_vertex].pushBack(CutvertexPreference(last_v,last_b,v2,(*it).m_block));
1358 
1359 			last_v = (*it).m_vertex;
1360 			last_b = (*it).m_block;
1361 			++it;
1362 		} while(it.valid());
1363 	}
1364 
1365 
1366 	// embedding graph
1367 	Array<bool> blockSet(0,c-1,false);
1368 	AdjEntryArray<SListIterator<adjEntry> > pos(PG,nullptr);
1369 
1370 	for(node v : PG.nodes)
1371 	{
1372 		SListPure<adjEntry> adjList;
1373 		SList<VertexBlock> &copies = m_copyInBlocks[v];
1374 
1375 		// not a cut vertex?
1376 		if(copies.size() == 1) {
1377 			OGDF_ASSERT(cvPref[v].empty());
1378 			const Block &B = *m_block[copies.front().m_block];
1379 			node vB = copies.front().m_vertex;
1380 
1381 			for(adjEntry adj : vB->adjEntries)
1382 				adjList.pushBack(B.m_BCtoG[adj]);
1383 
1384 		} else {
1385 			SList<CutvertexPreference> &prefs = cvPref[v];
1386 
1387 			if(!prefs.empty()) {
1388 				// always realize first cutvertex preference
1389 				SListIterator<CutvertexPreference> it = prefs.begin();
1390 
1391 				int b1 = (*it).m_b1;
1392 				int b2 = (*it).m_b2;
1393 				blockSet[b1] = blockSet[b2] = true;
1394 
1395 				adjEntry adj1 = m_block[b1]->findBestFace(copy(v,b1),copy((*it).m_v1,b1), (*it).m_len1);
1396 				appendToList(adjList, adj1, m_block[b1]->m_BCtoG, pos);
1397 
1398 				adjEntry adj2 = m_block[b2]->findBestFace(copy(v,b2),copy((*it).m_v2,b2), (*it).m_len2);
1399 				appendToList(adjList, adj2, m_block[b2]->m_BCtoG, pos);
1400 
1401 				for(++it; it.valid(); ++it) {
1402 					b1 = (*it).m_b1;
1403 					b2 = (*it).m_b2;
1404 
1405 					if(!blockSet[b1] && !blockSet[b2]) {
1406 						// none of the two blocks set yet
1407 						adjEntry bestFace1 = m_block[b1]->findBestFace(copy(v,b1),copy((*it).m_v1,b1), (*it).m_len1);
1408 						appendToList(adjList, bestFace1, m_block[b1]->m_BCtoG, pos);
1409 
1410 						adjEntry bestFace2 = m_block[b2]->findBestFace(copy(v,b2),copy((*it).m_v2,b2), (*it).m_len2);
1411 						appendToList(adjList, bestFace2, m_block[b2]->m_BCtoG, pos);
1412 
1413 						blockSet[b1] = blockSet[b2] = true;
1414 
1415 					} else if(!blockSet[b1]) {
1416 						// b2 is set, but b1 is not yet set
1417 						adjEntry bestFace1 = m_block[b1]->findBestFace(copy(v,b1),copy((*it).m_v1,b1), (*it).m_len1);
1418 						adjEntry bestFace2 = m_block[b2]->findBestFace(copy(v,b2),copy((*it).m_v2,b2), (*it).m_len2);
1419 
1420 						insertAfterList(adjList, pos[m_block[b2]->m_BCtoG[bestFace2]], bestFace1, m_block[b1]->m_BCtoG, pos);
1421 						blockSet[b1] = true;
1422 
1423 					} else if(!blockSet[b2]) {
1424 						// b1 is set, but b2 is not yet set
1425 						adjEntry bestFace1 = m_block[b1]->findBestFace(copy(v,b1),copy((*it).m_v1,b1), (*it).m_len1);
1426 						adjEntry bestFace2 = m_block[b2]->findBestFace(copy(v,b2),copy((*it).m_v2,b2), (*it).m_len2);
1427 
1428 						insertAfterList(adjList, pos[m_block[b1]->m_BCtoG[bestFace1]], bestFace2, m_block[b2]->m_BCtoG, pos);
1429 						blockSet[b2] = true;
1430 					}
1431 				}
1432 			}
1433 
1434 			// embed remaining blocks (if any)
1435 			for (const auto &vblock : copies) {
1436 				int b = vblock.m_block;
1437 				if(blockSet[b])
1438 					continue;
1439 				appendToList(adjList, vblock.m_vertex->firstAdj(), m_block[b]->m_BCtoG, pos);
1440 			}
1441 
1442 			// cleanup
1443 			for (auto pref : prefs) {
1444 				blockSet[pref.m_b1] = false;
1445 				blockSet[pref.m_b2] = false;
1446 			}
1447 
1448 			OGDF_ASSERT(adjList.size() == v->degree());
1449 		}
1450 
1451 		PG.sort(v, adjList);
1452 	}
1453 
1454 	OGDF_ASSERT(PG.representsCombEmbedding());
1455 
1456 #if 0
1457 	// arbitrary embedding for testing
1458 	planarEmbed(PG);
1459 #endif
1460 
1461 	// generate further statistic information
1462 	if(m_statistics) {
1463 		constructDual(PG);
1464 
1465 #if 0
1466 		std::cout << "\ncutvertex preferences:\n" << std::endl;
1467 		for(node v : PG.nodes)
1468 		{
1469 			SListConstIterator<CutvertexPreference> it = cvPref[v].begin();
1470 			if(!it.valid()) continue;
1471 
1472 			std::cout << v << ":\n";
1473 			for(; it.valid(); ++it) {
1474 				const CutvertexPreference &p = *it;
1475 
1476 				int sp1 = findShortestPath(p.m_v1, v);
1477 				int sp2 = findShortestPath(v, p.m_v2);
1478 				std::cout << "   ( v" << p.m_v1 << ", b" << p.m_b1 << "; v" << p.m_v2 << ", b" << p.m_b2 << " )  ";
1479 				std::cout << "[ " << p.m_len1 << " / " << sp1 << " ; " << p.m_len2 << " / " << sp2 << " ]" << std::endl;
1480 			}
1481 		}
1482 #endif
1483 
1484 		m_sumFEInsertionCosts = 0;
1485 		for(int i = 0; i < m; ++i) {
1486 			node s = PG.copy((*m_edge)[i]->source());
1487 			node t = PG.copy((*m_edge)[i]->target());
1488 			int len = findShortestPath(s,t);
1489 			m_sumFEInsertionCosts += len;
1490 		}
1491 	} else
1492 		m_sumFEInsertionCosts = -1;
1493 
1494 
1495 	// release no longer needed resources
1496 	cleanup();
1497 
1498 
1499 	//
1500 	// PHASE 2: Perform edge insertion with fixed emebdding
1501 	//
1502 
1503 	FixedEmbeddingInserter fei;
1504 	fei.keepEmbedding(true);
1505 	fei.removeReinsert(m_rrOptionFix);
1506 	fei.percentMostCrossed(m_percentMostCrossedFix);
1507 
1508 	fei.call( PG, origEdges );
1509 
1510 	if(m_rrOptionVar != RemoveReinsertType::None && m_rrOptionVar != RemoveReinsertType::Incremental) {
1511 		VariableEmbeddingInserter vei;
1512 		vei.removeReinsert(m_rrOptionVar);
1513 		vei.percentMostCrossed(m_percentMostCrossedFix);
1514 		vei.callPostprocessing( PG, origEdges );
1515 	}
1516 
1517 	return ReturnType::Feasible;
1518 }
1519 
1520 
1521 // cleanup: delete blocks, reset all auxiliary arrays
cleanup()1522 void MultiEdgeApproxInserter::cleanup()
1523 {
1524 	int c = m_block.size();
1525 	for(int  i = 0; i < c; ++i)
1526 		delete m_block[i];
1527 	m_block.init();
1528 
1529 	m_GtoBC.init();
1530 	m_edgesB.init();
1531 	m_verticesB.init();
1532 	m_compV.init();
1533 
1534 	m_edge = nullptr;
1535 	m_pathBCs.init();
1536 	m_insertionCosts.init();
1537 	m_copyInBlocks.init();
1538 
1539 	m_primalAdj.init();
1540 	m_faceNode.init();
1541 	m_E.init();
1542 	m_dual.clear();
1543 }
1544 
1545 
1546 // just for testing and additional statistics
constructDual(const PlanRepLight & PG)1547 void MultiEdgeApproxInserter::constructDual(const PlanRepLight &PG)
1548 {
1549 	m_E.init(PG);
1550 	m_faceNode.init(m_E);
1551 	m_primalAdj.init(m_dual);
1552 
1553 	// constructs nodes (for faces in m_embB)
1554 	for(face f : m_E.faces) {
1555 		m_faceNode[f] = m_dual.newNode();
1556 	}
1557 
1558 	// construct dual edges (for primal edges in block)
1559 	for(node v : PG.nodes)
1560 	{
1561 		for(adjEntry adj : v->adjEntries)
1562 		{
1563 			if(adj->index() & 1) {
1564 				node vLeft  = m_faceNode[m_E.leftFace (adj)];
1565 				node vRight = m_faceNode[m_E.rightFace(adj)];
1566 
1567 				edge eDual = m_dual.newEdge(vLeft,vRight);
1568 				m_primalAdj[eDual->adjSource()] = adj;
1569 				m_primalAdj[eDual->adjTarget()] = adj->twin();
1570 			}
1571 		}
1572 	}
1573 
1574 	m_vS = m_dual.newNode();
1575 	m_vT = m_dual.newNode();
1576 }
1577 
1578 
findShortestPath(node s,node t)1579 int MultiEdgeApproxInserter::findShortestPath(node s, node t)
1580 {
1581 	NodeArray<adjEntry> spPred(m_dual, nullptr);
1582 	QueuePure<adjEntry> queue;
1583 	int oldIdCount = m_dual.maxEdgeIndex();
1584 
1585 	// augment dual by edges from s to all adjacent faces of s ...
1586 	for(adjEntry adj : s->adjEntries) {
1587 		// starting edges of bfs-search are all edges leaving s
1588 		edge eDual = m_dual.newEdge(m_vS, m_faceNode[m_E.rightFace(adj)]);
1589 		m_primalAdj[eDual->adjSource()] = adj;
1590 		m_primalAdj[eDual->adjTarget()] = nullptr;
1591 		queue.append(eDual->adjSource());
1592 	}
1593 
1594 	// ... and from all adjacent faces of t to t
1595 	for(adjEntry adj : t->adjEntries) {
1596 		edge eDual = m_dual.newEdge(m_faceNode[m_E.rightFace(adj)], m_vT);
1597 		m_primalAdj[eDual->adjSource()] = adj;
1598 		m_primalAdj[eDual->adjTarget()] = nullptr;
1599 	}
1600 
1601 	// actual search (using bfs on directed dual)
1602 	int len = -2;
1603 	for( ; ;)
1604 	{
1605 		// next candidate edge
1606 		adjEntry adjCand = queue.pop();
1607 		node v = adjCand->twinNode();
1608 
1609 		// leads to an unvisited node?
1610 		if (spPred[v] == nullptr)
1611 		{
1612 			// yes, then we set v's predecessor in search tree
1613 			spPred[v] = adjCand;
1614 
1615 			// have we reached t ...
1616 			if (v == m_vT)
1617 			{
1618 				// ... then search is done.
1619 
1620 				adjEntry adj;
1621 				do {
1622 					adj = spPred[v];
1623 					++len;
1624 					v = adj->theNode();
1625 				} while(v != m_vS);
1626 
1627 				break;
1628 			}
1629 
1630 			// append next candidate edges to queue
1631 			// (all edges leaving v)
1632 			for(adjEntry adj : v->adjEntries) {
1633 				if(adj->twinNode() != m_vS)
1634 					queue.append(adj);
1635 			}
1636 		}
1637 	}
1638 
1639 	// remove augmented edges again
1640 	adjEntry adj;
1641 	while ((adj = m_vS->firstAdj()) != nullptr)
1642 		m_dual.delEdge(adj->theEdge());
1643 
1644 	while ((adj = m_vT->firstAdj()) != nullptr)
1645 		m_dual.delEdge(adj->theEdge());
1646 
1647 	m_dual.resetEdgeIdCount(oldIdCount);
1648 
1649 	return len;
1650 }
1651 
1652 }
1653