1 /** \file
2  * \brief Implements the class BitonicOrdering...
3  *
4  * ... that computes a bitonic st ordering as described by Gronemann
5  * in Bitonic st-orderings of biconnected planar graphs
6  *
7  * \author Martin Gronemann
8  *
9  * \par License:
10  * This file is part of the Open Graph Drawing Framework (OGDF).
11  *
12  * \par
13  * Copyright (C)<br>
14  * See README.md in the OGDF root directory for details.
15  *
16  * \par
17  * This program is free software; you can redistribute it and/or
18  * modify it under the terms of the GNU General Public License
19  * Version 2 or 3 as published by the Free Software Foundation;
20  * see the file LICENSE.txt included in the packaging of this file
21  * for details.
22  *
23  * \par
24  * This program is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27  * GNU General Public License for more details.
28  *
29  * \par
30  * You should have received a copy of the GNU General Public
31  * License along with this program; if not, see
32  * http://www.gnu.org/copyleft/gpl.html
33  */
34 
35 #include <ogdf/planarlayout/BitonicOrdering.h>
36 
37 using namespace ogdf;
38 
BitonicOrdering(Graph & G,adjEntry adj_st_edge)39 BitonicOrdering::BitonicOrdering(Graph& G, adjEntry adj_st_edge)
40   : m_graph(G)
41   , m_currLabel(0)
42   , m_orderIndex(G,-1)
43   , m_indexToNode(G.numberOfNodes())
44   , m_tree(G, adj_st_edge->theEdge(), true)
45 {
46 	// set all tree nodes to non flipped
47 	m_flipped.init(m_tree.tree(), false);
48 
49 	// s in the graph
50 	node s_G = adj_st_edge->theNode();
51 	node t_G = adj_st_edge->twinNode();
52 
53 	// we label s here manually: set the label
54 	m_orderIndex[s_G] = m_currLabel++;
55 	// and update the other map
56 	m_indexToNode[m_orderIndex[s_G]] = s_G;
57 
58 	// label everything else except t
59 	handleCase(m_tree.rootNode());
60 
61 	// we label s here manually: set the label
62 	m_orderIndex[t_G] = m_currLabel++;
63 	// and update the other map
64 	m_indexToNode[m_orderIndex[t_G]] = t_G;
65 
66 	// finally embedd G
67 	m_tree.embed(m_graph);
68 }
69 
70 
71 // used to distinguish between the three cases below
handleCase(node v_T)72 void BitonicOrdering::handleCase(node v_T)
73 {
74 	// check if this skeleton has been flipped in some R-node
75 	// above temporarily
76 	if (isFlipped(v_T)) {
77 		// reverse embedding of the skeleton
78 		m_tree.reverse(v_T);
79 	}
80 
81 	// the switch statement to check what type of node
82 	// v_T is
83 	switch (m_tree.typeOf(v_T)) {
84 		case ogdf::SPQRTree::NodeType::SNode:
85 			handleSerialCase(v_T);
86 			break;
87 
88 		case ogdf::SPQRTree::NodeType::PNode:
89 			handleParallelCase(v_T);
90 			break;
91 
92 		case ogdf::SPQRTree::NodeType::RNode:
93 			handleRigidCase(v_T);
94 			break;
95 
96 		default:
97 			break;
98 	}
99 
100 	// if we flipped it before, we now reverse the reversing
101 	if (isFlipped(v_T)) {
102 		m_tree.reverse(v_T);
103 	}
104 
105 }
106 
107 // helper function that finds the st-adjEntry in the skeleton of v_T
getAdjST(node v_T) const108 adjEntry BitonicOrdering::getAdjST(node v_T) const
109 {
110 	// the reference edge in G_skel
111 	edge e_ref = m_tree.skeleton(v_T).referenceEdge();
112 
113 	// it is either e_ref's source or target
114 	// we try this one first
115 	adjEntry adj_st = e_ref->adjSource();
116 
117 	// by our invariant s is labeled but t not
118 	// hence check if source is labeled
119 	if (getLabel(v_T, adj_st->theNode()) < 0) {
120 		// it is not, then it is the other one.
121 		adj_st = adj_st->twin();
122 	}
123 
124 	// this is the element pointing from s to t
125 	return adj_st;
126 }
127 
128 // the S-node case
handleSerialCase(node v_T)129 void BitonicOrdering::handleSerialCase(node v_T)
130 {
131 	// the skeleton instance of the tree node
132 	const Skeleton& skel = m_tree.skeleton(v_T);
133 
134 	// the adjElement pointing from s to t
135 	adjEntry adj_st = getAdjST(v_T);
136 
137 	// the t of st
138 	node t = adj_st->twinNode();
139 
140 	// now we traverse the cycle of skel counter clockwise from s to t
141 	for (adjEntry adj = adj_st->cyclicSucc();
142 			adj != adj_st->twin();
143 			adj = adj->twin()->cyclicSucc()) {
144 		// the current edge of the cycle
145 		edge e = adj->theEdge();
146 
147 		// check if this is a virtual one, i.e. is there something we
148 		// have to label first?
149 		if (skel.isVirtual(e)) {
150 			// the corresponding child of v_T
151 			node w_T = skel.twinTreeNode(e);
152 
153 			// mark the child as flipped iff this node is flipped
154 			setFlipped(w_T, isFlipped(v_T));
155 
156 			// go and label those nodes first
157 			handleCase(w_T);
158 		}
159 
160 		// the node which can be s
161 		node v = adj->twinNode();
162 
163 		// assign v a label unless it is t.
164 		if (v != t) assignLabel(v_T, v);
165 	}
166 }
167 
168 // the P-node case
handleParallelCase(node v_T)169 void BitonicOrdering::handleParallelCase(node v_T)
170 {
171 	// the skeleton instance of the tree node
172 	const Skeleton& skel = m_tree.skeleton(v_T);
173 
174 	// the adjElement pointing from s to t
175 	adjEntry adj_st = getAdjST(v_T);
176 
177 	// a possible real st edge
178 	adjEntry adj_real = nullptr;
179 
180 	// first we check if there is any real edge here
181 	for (adjEntry adj = adj_st->cyclicPred(); adj != adj_st; adj = adj->cyclicPred()) {
182 		// check if it is a real edge
183 		if (!skel.isVirtual(adj->theEdge())
184 		 && adj != adj_st->cyclicSucc()) {
185 			// we are only interested in a real edge that is not the cyclic succ of the reference adj
186 			adj_real = adj; // we found one, this is moved
187 		}
188 	}
189 
190 	// we have to change the embedding now
191 	if (adj_real) {
192 		// swap it with the one right after the st edge
193 		m_tree.swap(v_T, adj_st->cyclicSucc(), adj_real);
194 	}
195 
196 	// for all incident edges in reverse order
197 	for (adjEntry adj = adj_st->cyclicPred(); adj != adj_st; adj = adj->cyclicPred()) {
198 		// the edge
199 		edge e = adj->theEdge();
200 
201 		// check if this is a virtual one, i.e. is there something we
202 		// have to label first?
203 		if (skel.isVirtual(e)) {
204 			// the corresponding child of v_T
205 			node w_T = skel.twinTreeNode(e);
206 
207 			// mark the child as flipped iff this node is flipped
208 			setFlipped(w_T, isFlipped(v_T));
209 
210 			// go and label those nodes first
211 			handleCase(w_T);
212 		}
213 	}
214 }
215 
216 // transforms the result of the canonical ordering into two arrays,
217 // one holding the index in the temporary order for a node,
218 // the other is the reverse map. Function assumes proper init for indices and vertices
partitionToOrderIndices(const List<List<node>> & partitionlist,NodeArray<int> & indices,Array<node> & vertices) const219 void BitonicOrdering::partitionToOrderIndices(const List<List<node> >& partitionlist,
220                              NodeArray<int>& indices,
221                              Array<node>& vertices) const
222 {
223 	// counter for the index of a node
224 	int currIndex = 0;
225 
226 	// for all parititons
227 	for (List<List<node> >::const_iterator lit = partitionlist.begin(); lit.valid(); ++lit) {
228 		// a chain or singleton
229 		const List<node>& partition = *lit;
230 
231 		// for all nodes in the chain or singleton
232 		for (List<node>::const_iterator it = partition.begin(); it.valid(); ++it) {
233 			// the node
234 			node v = *it;
235 
236 			// assign it a number
237 			indices[v] = currIndex;
238 
239 			// mark it in the map
240 			vertices[currIndex] = v;
241 
242 			// increment the index
243 			currIndex++;
244 		}
245 	}
246 }
247 
248 // the R-node case
handleRigidCase(node v_T)249 void BitonicOrdering::handleRigidCase(node v_T)
250 {
251 	// the skeleton instance of the tree node
252 	const Skeleton& skel = m_tree.skeleton(v_T);
253 
254 	// the adjElement pointing from s to t
255 	adjEntry adj_st = getAdjST(v_T);
256 
257 	// s and t
258 	node s = adj_st->theNode();
259 	node t = adj_st->twinNode();
260 
261 	// the skeleton graph of v_T
262 	const Graph& G_skel = skel.getGraph();
263 
264 	// canonical ordeirng
265 	LeftistOrdering leftistOrder;
266 
267 	// the result of the leftist order
268 	List<List<node> > temporaryPartition;
269 
270 	// get the order
271 	leftistOrder.call(G_skel, adj_st, temporaryPartition);
272 
273 	// index for every skeleton node
274 	NodeArray<int> vertexIndex(G_skel, -1);
275 
276 	// the temporary order
277 	Array<node> order(G_skel.numberOfNodes());
278 
279 	// partition to order indices
280 	partitionToOrderIndices(temporaryPartition, vertexIndex, order);
281 
282 	// now traverse the order from 1 to V
283 	for (int i = 0; i < G_skel.numberOfNodes(); ++i) {
284 		// the ith node
285 		node w = order[i];
286 
287 		// for all incident edges
288 		for (adjEntry adj = w->firstAdj(); adj; adj = adj->succ()) {
289 			// the neighbour
290 			node v = adj->twinNode();
291 
292 			// check if this is an incoming edge in the temporary order
293 			if (vertexIndex[v] < vertexIndex[w] ) {
294 				// yes it is, w is the successor of v
295 				// time for some recursion
296 				edge e = adj->theEdge();
297 
298 				// check if this is a virtual one, i.e. is there something we
299 				// have to label first? however, make sure not to recurse on the parent
300 				if (skel.isVirtual(e) && ( e != skel.referenceEdge())) {
301 					// the corresponding child of v_T
302 					node w_T = skel.twinTreeNode(e);
303 
304 					// the successor of w in the embedding clockwise at v
305 					node w_next = adj->twin()->cyclicSucc()->twinNode();
306 
307 					// we now check if the successor of w at v has a higher index then w
308 					// i.e. w is in the increasing partition at v and requires a flip in the embeddig
309 					// however, we have to be careful at s to not wrap around (the st edge is the cyclicSucc
310 					// of the v_1,v_2 edge. we just skip v = s = v_1 completly since it is
311 					// decreasing by corallary 1
312 					if ((vertexIndex[v] > 0) && (vertexIndex[w] < vertexIndex[w_next])) {
313 						// w is in the increasing partition
314 						// we mark the child as flipped if we are not flipped
315 						setFlipped(w_T, !isFlipped(v_T));
316 						// reverseRecursive(skel.twinTreeNode(e));
317 					} else {
318 						// mark the child as flipped iff this node is flipped
319 						setFlipped(w_T, isFlipped(v_T));
320 					}
321 
322 					// go and label those nodes first
323 					handleCase(w_T);
324 				}
325 			}
326 		}
327 
328 		//now we are ready to label v
329 		if ((w != t) && (w != s)) assignLabel(v_T, w);
330 	}
331 }
332 
333 #ifdef OGDF_DEBUG
consistencyCheck(GraphAttributes & GA) const334 void BitonicOrdering::consistencyCheck(GraphAttributes& GA) const
335 {
336 	GA.init(m_graph, GraphAttributes::nodeLabel);
337 	bool allBitonic = true;
338 
339 	for (node v : m_graph.nodes) {
340 		std::stringstream str;
341 		str << v << "(" << m_orderIndex[v] << ")";
342 		GA.label(v) = str.str();
343 
344 		if (m_orderIndex[v] < 0) {
345 			Logger::slout() << "Node not assigned" << std::endl;
346 		}
347 	}
348 
349 	// for all nodes we check now for bitonic indices
350 	for (node v : m_graph.nodes) {
351 		bool isNodeBitonic = true;
352 		// we skip t and s though
353 		if (m_orderIndex[v] != 0 && m_orderIndex[v] != m_graph.numberOfNodes() - 1) {
354 			adjEntry adj_first_succ = nullptr;
355 			adjEntry adj_last_succ = nullptr;
356 
357 
358 			for (adjEntry adj = v->firstAdj(); adj; adj = adj->succ()) {
359 				// and its cyclic succ
360 				node w_prev = adj->cyclicPred()->twinNode();
361 				// the other node
362 				node w = adj->twinNode();
363 				// and its cyclic succ
364 				node w_next = adj->cyclicSucc()->twinNode();
365 
366 				if ((m_orderIndex[v] > m_orderIndex[w_prev]) && (m_orderIndex[v] < m_orderIndex[w]))
367 					adj_first_succ = adj;
368 
369 				if ((m_orderIndex[v] > m_orderIndex[w_next]) && (m_orderIndex[v] < m_orderIndex[w]))
370 					adj_last_succ = adj;
371 
372 			}
373 
374 
375 			// we are going to look for bitonic succ lists
376 			for (adjEntry adj = v->firstAdj(); adj; adj = adj->succ()) {
377 				// and its cyclic succ
378 				node w_prev = adj->cyclicPred()->twinNode();
379 				// the other node
380 				node w = adj->twinNode();
381 				// and its cyclic succ
382 				node w_next = adj->cyclicSucc()->twinNode();
383 
384 				if (m_orderIndex[v] <= max(m_orderIndex[w_prev], max(m_orderIndex[w], m_orderIndex[w_next]))) {
385 					// all succs, lets check for bitonic indices
386 
387 					if ((m_orderIndex[w_prev] >= m_orderIndex[w]) && (m_orderIndex[w_next] >= m_orderIndex[w]) &&
388 						(m_orderIndex[v] > 0)) {
389 						isNodeBitonic = false;
390 						Logger::slout()
391 								<< "[BitonicOrder:] " << "NOT BITONIC SUCC LIST " << v << "(" << m_orderIndex[v] << ")"
392 								<< std::endl
393 								<< "[BitonicOrder:] " << w_prev << "(" << m_orderIndex[w_prev] << ") "
394 								<< w << "(" << m_orderIndex[w] << ") "
395 								<< w_next << "(" << m_orderIndex[w_next] << ")" << std::endl
396 								<< std::endl;
397 					};
398 				}
399 			}
400 
401 			if (!isNodeBitonic) {
402 				for (adjEntry adj = adj_first_succ; adj != adj_last_succ->cyclicSucc(); adj = adj->cyclicSucc()) {
403 					Logger::slout() << "(" << m_orderIndex[adj->twinNode()] << ") ";
404 				}
405 			}
406 
407 			OGDF_ASSERT(allBitonic && isNodeBitonic);
408 		}
409 	}
410 }
411 #endif
412