Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/graphs/planarity/graphEmbed.c
4057 views
1
/*
2
Planarity-Related Graph Algorithms Project
3
Copyright (c) 1997-2010, John M. Boyer
4
All rights reserved. Includes a reference implementation of the following:
5
6
* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding,
7
Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation,
8
University of Victoria, 2001.
9
10
* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n)
11
Planarity by Edge Addition". Journal of Graph Algorithms and Applications,
12
Vol. 8, No. 3, pp. 241-273, 2004.
13
14
* John M. Boyer. "A New Method for Efficiently Generating Planar Graph
15
Visibility Representations". In P. Eades and P. Healy, editors,
16
Proceedings of the 13th International Conference on Graph Drawing 2005,
17
Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006.
18
19
Redistribution and use in source and binary forms, with or without modification,
20
are permitted provided that the following conditions are met:
21
22
* Redistributions of source code must retain the above copyright notice, this
23
list of conditions and the following disclaimer.
24
25
* Redistributions in binary form must reproduce the above copyright notice, this
26
list of conditions and the following disclaimer in the documentation and/or
27
other materials provided with the distribution.
28
29
* Neither the name of the Planarity-Related Graph Algorithms Project nor the names
30
of its contributors may be used to endorse or promote products derived from this
31
software without specific prior written permission.
32
33
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
34
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
36
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
37
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
39
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
40
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
41
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
42
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43
*/
44
45
#define GRAPH_C
46
47
#include <stdlib.h>
48
49
#include "graph.h"
50
51
/* Imported functions */
52
53
extern void _FillVisitedFlags(graphP, int);
54
55
extern int _IsolateKuratowskiSubgraph(graphP theGraph, int I, int R);
56
extern int _IsolateOuterplanarObstruction(graphP theGraph, int I, int R);
57
58
/* Private functions (some are exported to system only) */
59
60
void _CreateSortedSeparatedDFSChildLists(graphP theGraph);
61
int _CreateFwdArcLists(graphP theGraph);
62
void _CreateDFSTreeEmbedding(graphP theGraph);
63
64
void _EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink);
65
66
int _GetNextVertexOnExternalFace(graphP theGraph, int curVertex, int *pPrevLink);
67
68
void _InvertVertex(graphP theGraph, int V);
69
void _MergeVertex(graphP theGraph, int W, int WPrevLink, int R);
70
int _MergeBicomps(graphP theGraph, int I, int RootVertex, int W, int WPrevLink);
71
72
void _WalkUp(graphP theGraph, int I, int J);
73
int _WalkDown(graphP theGraph, int I, int RootVertex);
74
75
int _HandleBlockedEmbedIteration(graphP theGraph, int I);
76
int _EmbedPostprocess(graphP theGraph, int I, int edgeEmbeddingResult);
77
78
int _OrientVerticesInEmbedding(graphP theGraph);
79
int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns);
80
int _JoinBicomps(graphP theGraph);
81
82
/********************************************************************
83
_CreateSortedSeparatedDFSChildLists()
84
We create a separatedDFSChildList in each vertex which contains the
85
Lowpoint values of the vertex's DFS children sorted in non-descending order.
86
To accomplish this in linear time for the whole graph, we must not
87
sort the DFS children in each vertex, but rather bucket sort the
88
Lowpoint values of all vertices, then traverse the buckets sequentially,
89
adding each vertex to its parent's separatedDFSChildList.
90
Note that this is a specialized bucket sort that achieves O(n)
91
worst case rather than O(n) expected time due to the simplicity
92
of the sorting problem. Specifically, we know that the Lowpoint values
93
are between 0 and N-1, so we create buckets for each value.
94
Collisions occur only when two keys are equal, so there is no
95
need to sort the buckets (hence O(n) worst case).
96
********************************************************************/
97
98
void _CreateSortedSeparatedDFSChildLists(graphP theGraph)
99
{
100
int *buckets;
101
listCollectionP bin;
102
int I, J, N, DFSParent, theList;
103
104
N = theGraph->N;
105
buckets = theGraph->buckets;
106
bin = theGraph->bin;
107
108
/* Initialize the bin and all the buckets to be empty */
109
110
LCReset(bin);
111
for (I=0; I < N; I++)
112
buckets[I] = NIL;
113
114
/* For each vertex, add it to the bucket whose index is equal to
115
the Lowpoint of the vertex. */
116
117
for (I=0; I < N; I++)
118
{
119
J = theGraph->V[I].Lowpoint;
120
buckets[J] = LCAppend(bin, buckets[J], I);
121
}
122
123
/* For each bucket, add each vertex in the bucket to the
124
separatedDFSChildList of its DFSParent. Since lower numbered buckets
125
are processed before higher numbered buckets, vertices with lower
126
Lowpoint values are added before those with higher Lowpoint values,
127
so the separatedDFSChildList of each vertex is sorted by Lowpoint */
128
129
for (I = 0; I < N; I++)
130
{
131
if ((J=buckets[I]) != NIL)
132
{
133
while (J != NIL)
134
{
135
DFSParent = theGraph->V[J].DFSParent;
136
137
if (DFSParent != NIL && DFSParent != J)
138
{
139
theList = theGraph->V[DFSParent].separatedDFSChildList;
140
theList = LCAppend(theGraph->DFSChildLists, theList, J);
141
theGraph->V[DFSParent].separatedDFSChildList = theList;
142
}
143
144
J = LCGetNext(bin, buckets[I], J);
145
}
146
}
147
}
148
}
149
150
/********************************************************************
151
_CreateFwdArcLists()
152
153
Puts the forward arcs (back edges from a vertex to its descendants)
154
into a circular list indicated by the fwdArcList member, a task
155
simplified by the fact that they have already been placed in
156
succession at the end of the adjacency lists by gp_CreateDFSTree().
157
158
Returns OK for success, NOTOK for internal code failure
159
********************************************************************/
160
161
int _CreateFwdArcLists(graphP theGraph)
162
{
163
int I, Jfirst, Jnext, Jlast;
164
165
for (I=0; I < theGraph->N; I++)
166
{
167
// The forward arcs are already in succession at the end of the adjacency list
168
// Skip this vertex if it has no edges
169
170
Jfirst = gp_GetLastArc(theGraph, I);
171
if (!gp_IsArc(theGraph, Jfirst))
172
continue;
173
174
// If the vertex has any forward edges at all, then the last edge
175
// will be a forward edge. So if we have any forward edges, ...
176
177
if (theGraph->G[Jfirst].type == EDGE_FORWARD)
178
{
179
// Find the end of the forward edge list
180
181
Jnext = Jfirst;
182
while (theGraph->G[Jnext].type == EDGE_FORWARD)
183
Jnext = gp_GetPrevArc(theGraph, Jnext);
184
Jlast = gp_GetNextArc(theGraph, Jnext);
185
186
// Remove the forward edges from the adjacency list of I
187
gp_BindLastArc(theGraph, I, Jnext);
188
189
// Make a circular forward edge list
190
theGraph->V[I].fwdArcList = Jfirst;
191
gp_SetNextArc(theGraph, Jfirst, Jlast);
192
gp_SetPrevArc(theGraph, Jlast, Jfirst);
193
}
194
}
195
196
return OK;
197
}
198
199
/********************************************************************
200
********************************************************************/
201
202
#ifdef DEBUG
203
int TestIntegrity(graphP theGraph)
204
{
205
int I, Jcur, result = 1;
206
207
for (I=0; I < theGraph->N; I++)
208
{
209
Jcur = theGraph->V[I].fwdArcList;
210
while (Jcur != NIL)
211
{
212
if (theGraph->G[Jcur].visited)
213
{
214
printf("Found problem with fwdArcList of vertex %d.\n", I);
215
result = 0;
216
break;
217
}
218
219
theGraph->G[Jcur].visited = 1;
220
221
Jcur = gp_GetNextArc(theGraph, Jcur);
222
if (Jcur == theGraph->V[I].fwdArcList)
223
Jcur = NIL;
224
}
225
226
Jcur = theGraph->V[I].fwdArcList;
227
while (Jcur != NIL)
228
{
229
if (!theGraph->G[Jcur].visited)
230
break;
231
232
theGraph->G[Jcur].visited = 0;
233
234
Jcur = gp_GetNextArc(theGraph, Jcur);
235
if (Jcur == theGraph->V[I].fwdArcList)
236
Jcur = NIL;
237
}
238
}
239
240
return result;
241
}
242
#endif
243
244
/********************************************************************
245
_CreateDFSTreeEmbedding()
246
247
Each vertex receives only its parent arc in the adjacency list, and
248
the corresponding child arc is placed in the adjacency list of a root
249
copy of the parent. Each root copy of a vertex is uniquely associated
250
with a child C, so it is simply stored at location C+N.
251
252
The forward arcs are not lost because they are already in the
253
fwdArcList of each vertex. Each back arc can be reached as the
254
twin arc of a forward arc, and the two are embedded together when
255
the forward arc is processed. Finally, the child arcs are initially
256
placed in root copies of vertices, not the vertices themselves, but
257
the child arcs are merged into the vertices as the embedder progresses.
258
********************************************************************/
259
260
void _CreateDFSTreeEmbedding(graphP theGraph)
261
{
262
int N, I, J, Jtwin, R;
263
264
N = theGraph->N;
265
266
// Embed all tree edges. For each DFS tree child, we move
267
// the child arc to a root copy of vertex I that is uniquely
268
// associated with the DFS child, and we remove all edges
269
// from the child except the parent arc
270
271
for (I=0, R=N; I < N; I++, R++)
272
{
273
if (theGraph->V[I].DFSParent == NIL)
274
{
275
gp_SetFirstArc(theGraph, I, gp_AdjacencyListEndMark(I));
276
gp_SetLastArc(theGraph, I, gp_AdjacencyListEndMark(I));
277
}
278
else
279
{
280
J = gp_GetFirstArc(theGraph, I);
281
while (theGraph->G[J].type != EDGE_DFSPARENT)
282
J = gp_GetNextArc(theGraph, J);
283
284
gp_SetFirstArc(theGraph, I, J);
285
gp_SetLastArc(theGraph, I, J);
286
287
gp_SetNextArc(theGraph, J, gp_AdjacencyListEndMark(I));
288
gp_SetPrevArc(theGraph, J, gp_AdjacencyListEndMark(I));
289
290
theGraph->G[J].v = R;
291
292
Jtwin = gp_GetTwinArc(theGraph, J);
293
294
gp_SetFirstArc(theGraph, R, Jtwin);
295
gp_SetLastArc(theGraph, R, Jtwin);
296
297
gp_SetNextArc(theGraph, Jtwin, gp_AdjacencyListEndMark(R));
298
gp_SetPrevArc(theGraph, Jtwin, gp_AdjacencyListEndMark(R));
299
300
theGraph->extFace[R].vertex[0] = theGraph->extFace[R].vertex[1] = I;
301
theGraph->extFace[I].vertex[0] = theGraph->extFace[I].vertex[1] = R;
302
}
303
}
304
}
305
306
/********************************************************************
307
_EmbedBackEdgeToDescendant()
308
The Walkdown has found a descendant vertex W to which it can
309
attach a back edge up to the root of the bicomp it is processing.
310
The RootSide and WPrevLink indicate the parts of the external face
311
that will be replaced at each endpoint of the back edge.
312
********************************************************************/
313
314
void _EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink)
315
{
316
int fwdArc, backArc, parentCopy;
317
318
/* We get the two edge records of the back edge to embed.
319
The Walkup recorded in W's adjacentTo the index of the forward arc
320
from the root's parent copy to the descendant W. */
321
322
fwdArc = theGraph->V[W].adjacentTo;
323
backArc = gp_GetTwinArc(theGraph, fwdArc);
324
325
/* The forward arc is removed from the fwdArcList of the root's parent copy. */
326
327
parentCopy = theGraph->V[RootVertex - theGraph->N].DFSParent;
328
329
gp_LogLine(gp_MakeLogStr5("graphEmbed.c/_EmbedBackEdgeToDescendant() V=%d, R=%d, R_out=%d, W=%d, W_in=%d",
330
parentCopy, RootVertex, RootSide, W, WPrevLink));
331
332
if (theGraph->V[parentCopy].fwdArcList == fwdArc)
333
{
334
theGraph->V[parentCopy].fwdArcList = gp_GetNextArc(theGraph, fwdArc);
335
if (theGraph->V[parentCopy].fwdArcList == fwdArc)
336
theGraph->V[parentCopy].fwdArcList = NIL;
337
}
338
339
gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, fwdArc), gp_GetNextArc(theGraph, fwdArc));
340
gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, fwdArc), gp_GetPrevArc(theGraph, fwdArc));
341
342
// The forward arc is added to the adjacency list of the RootVertex.
343
// Note that we're guaranteed that the RootVertex adjacency list is non-empty,
344
// so tests for NIL are not needed
345
gp_SetAdjacentArc(theGraph, fwdArc, 1^RootSide, gp_AdjacencyListEndMark(RootVertex));
346
gp_SetAdjacentArc(theGraph, fwdArc, RootSide, gp_GetArc(theGraph, RootVertex, RootSide));
347
gp_SetAdjacentArc(theGraph, gp_GetArc(theGraph, RootVertex, RootSide), 1^RootSide, fwdArc);
348
gp_SetArc(theGraph, RootVertex, RootSide, fwdArc);
349
350
// The back arc is added to the adjacency list of W.
351
// The adjacency list of W is also guaranteed non-empty
352
gp_SetAdjacentArc(theGraph, backArc, 1^WPrevLink, gp_AdjacencyListEndMark(W));
353
gp_SetAdjacentArc(theGraph, backArc, WPrevLink, gp_GetArc(theGraph, W, WPrevLink));
354
gp_SetAdjacentArc(theGraph, gp_GetArc(theGraph, W, WPrevLink), 1^WPrevLink, backArc);
355
gp_SetArc(theGraph, W, WPrevLink, backArc);
356
357
theGraph->G[backArc].v = RootVertex;
358
359
/* Link the two endpoint vertices together on the external face */
360
361
theGraph->extFace[RootVertex].vertex[RootSide] = W;
362
theGraph->extFace[W].vertex[WPrevLink] = RootVertex;
363
}
364
365
/********************************************************************
366
_GetNextVertexOnExternalFace()
367
Each vertex contains two 'link' index pointers that indicate the
368
first and last adjacency list arc. If the vertex is on the external face,
369
then these two arcs are also on the external face. We want to take one of
370
those edges to get to the next vertex on the external face.
371
On input *pPrevLink indicates which link we followed to arrive at
372
curVertex. On output *pPrevLink will be set to the link we follow to
373
get into the next vertex.
374
To get to the next vertex, we use the opposite link from the one used
375
to get into curVertex. This takes us to an edge node. The twinArc
376
of that edge node, carries us to an edge node in the next vertex.
377
At least one of the two links in that edge node will lead to a vertex
378
node in G, which is the next vertex. Once we arrive at the next
379
vertex, at least one of its links will lead back to the edge node, and
380
that link becomes the output value of *pPrevLink.
381
382
NOTE: This method intentionally ignores the extFace optimization
383
links. It is invoked when the "real" external face must be
384
traversed and hence when the constant time guarantee is not
385
needed from the extFace short-circuit that connects the
386
bicomp root to the first active vertices along each external
387
face path emanating from the bicomp root.
388
********************************************************************/
389
390
int _GetNextVertexOnExternalFace(graphP theGraph, int curVertex, int *pPrevLink)
391
{
392
/* Exit curVertex from whichever link was not previously used to enter it */
393
394
int arc = gp_GetArc(theGraph, curVertex, 1^(*pPrevLink));
395
int nextVertex = theGraph->G[arc].v;
396
397
/* This if stmt assigns the new prev link that tells us which edge
398
record was used to enter nextVertex (so that we exit from the
399
opposing edge record).
400
401
However, if we are in a singleton bicomp, then both links in nextVertex
402
lead back to curVertex. We want the two arcs of a singleton bicomp to
403
act like a cycle, so we just don't change the prev link in this case.
404
405
But when nextVertex has more than one edge, we need to figure out
406
whether the first edge or last edge (which are the two on the external
407
face) was used to enter nextVertex so we can exit from the other one
408
as traversal of the external face continues later. */
409
410
if (gp_GetFirstArc(theGraph, nextVertex) != gp_GetLastArc(theGraph, nextVertex))
411
*pPrevLink = gp_GetTwinArc(theGraph, arc) == gp_GetFirstArc(theGraph, nextVertex) ? 0 : 1;
412
413
return nextVertex;
414
}
415
416
/********************************************************************
417
_InvertVertex()
418
This function flips the orientation of a single vertex such that
419
instead of using link successors to go clockwise (or counterclockwise)
420
around a vertex's adjacency list, link predecessors would be used.
421
********************************************************************/
422
423
void _InvertVertex(graphP theGraph, int V)
424
{
425
int J, temp;
426
427
gp_LogLine(gp_MakeLogStr1("graphEmbed.c/_InvertVertex() V=%d", V));
428
429
// Swap the links in all the arcs of the adjacency list
430
J = gp_GetFirstArc(theGraph, V);
431
while (gp_IsArc(theGraph, J))
432
{
433
temp = gp_GetNextArc(theGraph, J);
434
gp_SetNextArc(theGraph, J, gp_GetPrevArc(theGraph, J));
435
gp_SetPrevArc(theGraph, J, temp);
436
437
J = temp;
438
}
439
440
// Swap the first/last edge record indicators in the vertex
441
temp = gp_GetFirstArc(theGraph, V);
442
gp_SetFirstArc(theGraph, V, gp_GetLastArc(theGraph, V));
443
gp_SetLastArc(theGraph, V, temp);
444
445
// Swap the first/last external face indicators in the vertex
446
temp = theGraph->extFace[V].vertex[0];
447
theGraph->extFace[V].vertex[0] = theGraph->extFace[V].vertex[1];
448
theGraph->extFace[V].vertex[1] = temp;
449
}
450
451
/********************************************************************
452
_MergeVertex()
453
The merge step joins the vertex W to the root R of a child bicompRoot,
454
which is a root copy of W appearing in the region N to 2N-1.
455
456
Actually, the first step of this is to redirect all of the edges leading
457
into R so that they indicate W as the neighbor instead of R.
458
For each edge node pointing to R, we set the 'v' field to W. Once an
459
edge is redirected from a root copy R to a parent copy W, the edge is
460
never redirected again, so we associate the cost of the redirection
461
as constant per edge, which maintains linear time performance.
462
463
After this is done, a regular circular list union occurs. The only
464
consideration is that WPrevLink is used to indicate the two edge
465
records e_w and e_r that will become consecutive in the resulting
466
adjacency list of W. We set e_w to W's link [WPrevLink] and e_r to
467
R's link [1^WPrevLink] so that e_w and e_r indicate W and R with
468
opposing links, which become free to be cross-linked. Finally,
469
the edge record e_ext, set equal to R's link [WPrevLink], is the edge
470
that, with e_r, held R to the external face. Now, e_ext will be the
471
new link [WPrevLink] edge record for W. If e_w and e_r become part
472
of a proper face, then e_ext and W's link [1^WPrevLink] are the two
473
edges that attach W to the external face cycle of the containing bicomp.
474
********************************************************************/
475
476
void _MergeVertex(graphP theGraph, int W, int WPrevLink, int R)
477
{
478
int J, JTwin;
479
int e_w, e_r, e_ext;
480
481
gp_LogLine(gp_MakeLogStr4("graphEmbed.c/_MergeVertex() W=%d, W_in=%d, R=%d, R_out=%d",
482
W, WPrevLink, R, 1^WPrevLink));
483
484
// All arcs leading into R from its neighbors must be changed
485
// to say that they are leading into W
486
J = gp_GetFirstArc(theGraph, R);
487
while (gp_IsArc(theGraph, J))
488
{
489
JTwin = gp_GetTwinArc(theGraph, J);
490
theGraph->G[JTwin].v = W;
491
492
J = gp_GetNextArc(theGraph, J);
493
}
494
495
// Obtain the edge records involved in the list union
496
e_w = gp_GetArc(theGraph, W, WPrevLink);
497
e_r = gp_GetArc(theGraph, R, 1^WPrevLink);
498
e_ext = gp_GetArc(theGraph, R, WPrevLink);
499
500
// If W has any edges, then join the list with that of R
501
if (gp_IsArc(theGraph, e_w))
502
{
503
// The WPrevLink arc of W is e_w, so the 1^WPrevLink arc in e_w leads back to W.
504
// Now it must lead to e_r. Likewise, e_r needs to lead back to e_w with the
505
// opposing link, which is WPrevLink
506
// Note that the adjacency lists of W and R are guaranteed non-empty, which is
507
// why these linkages can be made without NIL tests.
508
gp_SetAdjacentArc(theGraph, e_w, 1^WPrevLink, e_r);
509
gp_SetAdjacentArc(theGraph, e_r, WPrevLink, e_w);
510
511
// Cross-link W's WPrevLink arc and the 1^WPrevLink arc in e_ext
512
gp_SetArc(theGraph, W, WPrevLink, e_ext);
513
gp_SetAdjacentArc(theGraph, e_ext, 1^WPrevLink, gp_AdjacencyListEndMark(W));
514
}
515
// Otherwise, W just receives R's list. This happens, for example, on a
516
// DFS tree root vertex during JoinBicomps()
517
else
518
{
519
// Cross-link W's 1^WPrevLink arc and the WPrevLink arc in e_r
520
gp_SetArc(theGraph, W, 1^WPrevLink, e_r);
521
gp_SetAdjacentArc(theGraph, e_r, WPrevLink, gp_AdjacencyListEndMark(W));
522
523
// Cross-link W's WPrevLink arc and the 1^WPrevLink arc in e_ext
524
gp_SetArc(theGraph, W, WPrevLink, e_ext);
525
gp_SetAdjacentArc(theGraph, e_ext, 1^WPrevLink, gp_AdjacencyListEndMark(W));
526
}
527
528
// Erase the entries in R, which is a root copy that is no longer needed
529
theGraph->functions.fpInitGraphNode(theGraph, R);
530
}
531
532
/********************************************************************
533
_MergeBicomps()
534
535
Merges all biconnected components at the cut vertices indicated by
536
entries on the stack.
537
538
theGraph contains the stack of bicomp roots and cut vertices to merge
539
540
I, RootVertex, W and WPrevLink are not used in this routine, but are
541
used by overload extensions
542
543
Returns OK, but an extension function may return a value other than
544
OK in order to cause Walkdown to terminate immediately.
545
********************************************************************/
546
547
int _MergeBicomps(graphP theGraph, int I, int RootVertex, int W, int WPrevLink)
548
{
549
int R, Rout, Z, ZPrevLink, J;
550
int theList, RootID_DFSChild;
551
int extFaceVertex;
552
553
while (sp_NonEmpty(theGraph->theStack))
554
{
555
sp_Pop2(theGraph->theStack, R, Rout);
556
sp_Pop2(theGraph->theStack, Z, ZPrevLink);
557
558
/* The external faces of the bicomps containing R and Z will
559
form two corners at Z. One corner will become part of the
560
internal face formed by adding the new back edge. The other
561
corner will be the new external face corner at Z.
562
We first want to update the links at Z to reflect this. */
563
564
extFaceVertex = theGraph->extFace[R].vertex[1^Rout];
565
theGraph->extFace[Z].vertex[ZPrevLink] = extFaceVertex;
566
567
if (theGraph->extFace[extFaceVertex].vertex[0] == theGraph->extFace[extFaceVertex].vertex[1])
568
theGraph->extFace[extFaceVertex].vertex[Rout ^ theGraph->extFace[extFaceVertex].inversionFlag] = Z;
569
else
570
theGraph->extFace[extFaceVertex].vertex[theGraph->extFace[extFaceVertex].vertex[0] == R ? 0 : 1] = Z;
571
572
/* If the path used to enter Z is opposed to the path
573
used to exit R, then we have to flip the bicomp
574
rooted at R, which we signify by inverting R
575
then setting the sign on its DFS child edge to
576
indicate that its descendants must be flipped later */
577
578
if (ZPrevLink == Rout)
579
{
580
Rout = 1^ZPrevLink;
581
582
if (gp_GetFirstArc(theGraph, R) != gp_GetLastArc(theGraph, R))
583
_InvertVertex(theGraph, R);
584
585
J = gp_GetFirstArc(theGraph, R);
586
while (gp_IsArc(theGraph, J))
587
{
588
if (theGraph->G[J].type == EDGE_DFSCHILD)
589
{
590
// A bicomp root edge cannot be inverted in the core planarity algorithm
591
// but extensions may perform edge reductions on tree edges, resulting in
592
// an inversion sign being promoted to the root edge. So, now we reverse
593
// the inversion flag on the root edge if the bicomp root must be
594
// inverted before it is merged.
595
if (GET_EDGEFLAG_INVERTED(theGraph, J))
596
CLEAR_EDGEFLAG_INVERTED(theGraph, J);
597
else
598
SET_EDGEFLAG_INVERTED(theGraph, J);
599
break;
600
}
601
602
J = gp_GetNextArc(theGraph, J);
603
}
604
}
605
606
// The endpoints of a bicomp's "root edge" are the bicomp root R and a
607
// DFS child of the parent copy of the bicomp root R.
608
// The GraphNode location of the root vertices is in the range N to 2N-1
609
// at the offset indicated by the associated DFS child. So, the location
610
// of the root vertex R, less N, is the location of the DFS child and also
611
// a convenient identifier for the bicomp root.
612
RootID_DFSChild = R - theGraph->N;
613
614
/* R is no longer pertinent to Z since we are about to
615
merge R into Z, so we delete R from its pertinent
616
bicomp list (Walkdown gets R from the head of the list). */
617
618
theList = theGraph->V[Z].pertinentBicompList;
619
theList = LCDelete(theGraph->BicompLists, theList, RootID_DFSChild);
620
theGraph->V[Z].pertinentBicompList = theList;
621
622
/* As a result of the merge, the DFS child of Z must be removed
623
from Z's SeparatedDFSChildList because the child has just
624
been joined directly to Z, rather than being separated by a
625
root copy. */
626
627
theList = theGraph->V[Z].separatedDFSChildList;
628
theList = LCDelete(theGraph->DFSChildLists, theList, RootID_DFSChild);
629
theGraph->V[Z].separatedDFSChildList = theList;
630
631
/* Now we push R into Z, eliminating R */
632
633
_MergeVertex(theGraph, Z, ZPrevLink, R);
634
}
635
636
return OK;
637
}
638
639
/********************************************************************
640
_WalkUp()
641
I is the vertex currently being embedded
642
J is the forward arc to the descendant W on which the Walkup begins
643
644
The Walkup establishes pertinence for step I. It marks W as
645
'adjacentTo' I so that the Walkdown will embed an edge to W when
646
it is encountered.
647
648
The Walkup also determines the pertinent child bicomps that should be
649
set up as a result of the need to embed edge (I, W). It does this by
650
recording the pertinent child biconnected components of all cut
651
vertices between W and the child of I that is a descendant of W.
652
Note that it stops the traversal if it finds a visited flag set to I,
653
which indicates that a prior walkup call in step I has already done
654
the work.
655
656
Zig and Zag are so named because one goes around one side of a
657
bicomp and the other goes around the other side, yet we have
658
as yet no notion of orientation for the bicomp.
659
The edge J from vertex I gestures to an adjacent descendant vertex W
660
(possibly in some other bicomp). Zig and Zag start out at W.
661
They go around alternate sides of the bicomp until its root is found.
662
Recall that the root vertex is just a copy in region N to 2N-1.
663
We want to hop from the root copy to the parent copy of the vertex
664
in order to record which bicomp we just came from and also to continue
665
the walk-up to vertex I.
666
If the parent copy actually is I, then the walk-up is done.
667
********************************************************************/
668
669
void _WalkUp(graphP theGraph, int I, int J)
670
{
671
int Zig, Zag, ZigPrevLink, ZagPrevLink;
672
int N, R, ParentCopy, nextVertex, W;
673
int RootID_DFSChild, BicompList;
674
675
W = theGraph->G[J].v;
676
theGraph->V[W].adjacentTo = J;
677
678
/* Shorthand for N, due to frequent use */
679
680
N = theGraph->N;
681
682
/* Start at the vertex W and walk around the both sides of the external face
683
of a bicomp until we get back to vertex I. */
684
685
Zig = Zag = W;
686
ZigPrevLink = 1;
687
ZagPrevLink = 0;
688
689
while (Zig != I)
690
{
691
/* A previous walk-up may have been this way already */
692
693
if (theGraph->G[Zig].visited == I) break;
694
if (theGraph->G[Zag].visited == I) break;
695
696
/* Mark the current vertices as visited during the embedding of vertex I. */
697
698
theGraph->G[Zig].visited = I;
699
theGraph->G[Zag].visited = I;
700
701
/* Determine whether either Zig or Zag has landed on a bicomp root */
702
703
if (Zig >= N) R = Zig;
704
else if (Zag >= N) R = Zag;
705
else R = NIL;
706
707
// If we have a bicomp root, then we want to hop up to the parent copy and
708
// record a pertinent child bicomp.
709
// Prepends if the bicomp is internally active, appends if externally active.
710
711
if (R != NIL)
712
{
713
// The endpoints of a bicomp's "root edge" are the bicomp root R and a
714
// DFS child of the parent copy of the bicomp root R.
715
// The GraphNode location of the root vertices is in the range N to 2N-1
716
// at the offset indicated by the associated DFS child. So, the location
717
// of the root vertex R, less N, is the location of the DFS child and also
718
// a convenient identifier for the bicomp root.
719
RootID_DFSChild = R - N;
720
721
// It is extra unnecessary work to record pertinent bicomps of I
722
if ((ParentCopy = theGraph->V[RootID_DFSChild].DFSParent) != I)
723
{
724
// Get the BicompList of the parent copy vertex.
725
BicompList = theGraph->V[ParentCopy].pertinentBicompList;
726
727
/* Put the new root vertex in the BicompList. It is prepended if internally
728
active and appended if externally active so that all internally
729
active bicomps are processed before any externally active bicomps
730
by virtue of storage.
731
732
NOTE: The activity status of a bicomp is computed using the lowpoint of
733
the DFS child in the bicomp's root edge because we want to know
734
whether the DFS child or any of its descendants are joined by a
735
back edge to ancestors of I. If so, then the bicomp rooted
736
at RootVertex must contain an externally active vertex so the
737
bicomp must be kept on the external face. */
738
739
if (theGraph->V[RootID_DFSChild].Lowpoint < I)
740
BicompList = LCAppend(theGraph->BicompLists, BicompList, RootID_DFSChild);
741
else BicompList = LCPrepend(theGraph->BicompLists, BicompList, RootID_DFSChild);
742
743
/* The head node of the parent copy vertex's bicomp list may have changed, so
744
we assign the head of the modified list as the vertex's pertinent
745
bicomp list */
746
747
theGraph->V[ParentCopy].pertinentBicompList = BicompList;
748
}
749
750
Zig = Zag = ParentCopy;
751
ZigPrevLink = 1;
752
ZagPrevLink = 0;
753
}
754
755
/* If we did not encounter a bicomp root, then we continue traversing the
756
external face in both directions. */
757
758
else
759
{
760
nextVertex = theGraph->extFace[Zig].vertex[1^ZigPrevLink];
761
ZigPrevLink = theGraph->extFace[nextVertex].vertex[0] == Zig ? 0 : 1;
762
Zig = nextVertex;
763
764
nextVertex = theGraph->extFace[Zag].vertex[1^ZagPrevLink];
765
ZagPrevLink = theGraph->extFace[nextVertex].vertex[0] == Zag ? 0 : 1;
766
Zag = nextVertex;
767
}
768
}
769
}
770
771
772
/********************************************************************
773
_HandleBlockedDescendantBicomp()
774
The core planarity/outerplanarity algorithm handles the blockage
775
by pushing the root of the blocked bicomp onto the top of the stack
776
because it is the central focus for obstruction minor A.
777
Then NONEMBEDDABLE is returned so that the WalkDown can terminate,
778
and the embedder can proceed to isolate the obstruction.
779
Some algorithms may be able to clear the blockage, in which case
780
a function overload would set Rout, W and WPrevLink, then return OK
781
to indicate that the WalkDown may proceed.
782
783
NOTE: When returning OK (blockage cleared), the overload implementation
784
should NOT call this base implementation nor otherwise push R
785
onto the stack because the core WalkDown implementation will push
786
the appropriate stack entries based on R, Rout, W and WPrevLink
787
Similarly, when returning NONEMBEDDABLE, it is typically not
788
necessary to call this base implementation because pushing
789
the bicomp root R is not usually necessary, i.e. the overload
790
implementation usually does all embed post-processing before
791
returning NONEMBEDDABLE.
792
793
Returns OK to proceed with WalkDown at W,
794
NONEMBEDDABLE to terminate WalkDown of Root Vertex
795
NOTOK for internal error
796
********************************************************************/
797
798
int _HandleBlockedDescendantBicomp(graphP theGraph, int I, int RootVertex, int R, int *pRout, int *pW, int *pWPrevLink)
799
{
800
sp_Push2(theGraph->theStack, R, 0);
801
return NONEMBEDDABLE;
802
}
803
804
/********************************************************************
805
_HandleInactiveVertex()
806
********************************************************************/
807
808
int _HandleInactiveVertex(graphP theGraph, int BicompRoot, int *pW, int *pWPrevLink)
809
{
810
int X = theGraph->extFace[*pW].vertex[1^*pWPrevLink];
811
*pWPrevLink = theGraph->extFace[X].vertex[0] == *pW ? 0 : 1;
812
*pW = X;
813
814
return OK;
815
}
816
817
/********************************************************************
818
_GetPertinentChildBicomp()
819
Returns the root of a pertinent child bicomp for the given vertex.
820
Note: internally active roots are prepended by _Walkup()
821
********************************************************************/
822
823
#define _GetPertinentChildBicomp(theGraph, W) \
824
(theGraph->V[W].pertinentBicompList==NIL \
825
? NIL \
826
: theGraph->V[W].pertinentBicompList + theGraph->N)
827
828
/********************************************************************
829
_WalkDown()
830
Consider a circular shape with small circles and squares along its perimeter.
831
The small circle at the top the root vertex of the bicomp. The other small
832
circles represent internally active vertices, and the squares represent
833
externally active vertices. The root vertex is a root copy of I, the
834
vertex currently being processed.
835
836
The Walkup previously marked all vertices adjacent to I by setting their
837
adjacentTo flags. Basically, we want to walk down both external face
838
paths emanating from RootVertex, embedding edges between the RootVertex
839
(a root copy of vertex I) and descendants of vertex I that have the
840
adjacentTo flag set.
841
842
During each walk down, it is sometimes necessary to hop from a vertex
843
to one of its child biconnected components in order to reach the desired
844
vertices. In such cases, the biconnected components are merged together
845
such that adding the back edge forms a new proper face in the biconnected
846
component rooted at RootVertex (which, again, is a root copy of I).
847
848
The outer loop performs both walks, unless the first walk got all the way
849
around to RootVertex (only happens when bicomp contains no external activity,
850
such as when processing the last vertex), or when non-planarity is
851
discovered (in a pertinent child bicomp such that the stack is non-empty).
852
853
For the inner loop, each iteration visits a vertex W. If W is adjacentTo I,
854
we call MergeBicomps to merge the biconnected components whose cut vertices
855
have been collecting in theStack. Then, we add the back edge (RootVertex, W)
856
and clear the adjacentTo flag in W.
857
858
Next, we check whether W has a pertinent child bicomp. If so, then we figure
859
out which path down from the root of the child bicomp leads to the next vertex
860
to be visited, and we push onto the stack information on the cut vertex and
861
the paths used to enter into it and exit from it. Alternately, if W
862
had no pertinent child bicomps, then we check to see if it is inactive.
863
If so, we find the next vertex along the external face, then short-circuit
864
its inactive predecessor (under certain conditions). Finally, if W is not
865
inactive, but it has no pertinent child bicomps, then we already know its
866
adjacentTo flag is clear so both criteria for internal activity also fail.
867
Therefore, W must be a stopping vertex.
868
869
A stopping vertex X is an externally active vertex that has no pertinent
870
child bicomps and no unembedded back edge to the current vertex I.
871
The inner loop of Walkdown stops walking when it reaches a stopping vertex X
872
because if it were to proceed beyond X and embed a back edge, then X would be
873
surrounded by the bounding cycle of the bicomp. This is clearly incorrect
874
because X has a path leading from it to an ancestor of I (which is why it's
875
externally active), and this path would have to cross the bounding cycle.
876
877
After the loop, if the stack is non-empty, then the Walkdown halted because
878
it could not proceed down a pertinent child biconnected component along either
879
path from its root, which is easily shown to be evidence of a K_3,3, so
880
we break the outer loop. The caller performs further tests to determine
881
whether Walkdown has embedded all back edges. If the caller does not embed
882
all back edges to descendants of the root vertex after walking both RootSide
883
0 then 1 in all bicomps containing a root copy of I, then the caller can
884
conclude that the input graph is non-planar.
885
886
Returns OK if all possible edges were embedded, NONEMBEDDABLE if less
887
than all possible edges were embedded, and NOTOK for an internal
888
code failure
889
********************************************************************/
890
891
int _WalkDown(graphP theGraph, int I, int RootVertex)
892
{
893
int RetVal, W, WPrevLink, R, Rout, X, XPrevLink, Y, YPrevLink, RootSide, RootEdgeChild;
894
895
#ifdef DEBUG
896
// Resolves typical watch expressions
897
R = RootVertex;
898
#endif
899
900
RootEdgeChild = RootVertex - theGraph->N;
901
902
sp_ClearStack(theGraph->theStack);
903
904
for (RootSide = 0; RootSide < 2; RootSide++)
905
{
906
W = theGraph->extFace[RootVertex].vertex[RootSide];
907
908
// If the main bicomp rooted by RootVertex is a single tree edge,
909
// (always the case for core planarity) then the external face links
910
// of W will be equal
911
if (theGraph->extFace[W].vertex[0] == theGraph->extFace[W].vertex[1])
912
{
913
// In this case, we treat the bicomp external face as if it were
914
// a cycle of two edges and as if RootVertex and W had the same
915
// orientation. Thus, the edge record leading back to RootVertex
916
// would be indicated by link[1^RootSide] as this is the reverse of
917
// link[RootSide], which was used to exit RootVertex and get to W
918
WPrevLink = 1^RootSide;
919
// We don't bother with the inversionFlag here because WalkDown is
920
// never called on a singleton bicomp with an inverted orientation
921
// Before the first Walkdown, the bicomp truly is a single edge
922
// with proper orientation, and an extension algorithm does call
923
// Walkdown again in post-processing, it wouldn't do so on this
924
// bicomp because a singleton, whether inverted or not, would no
925
// longer be pertinent (until a future vertex step).
926
// Thus only the inner loop below accommodates the inversionFlag
927
// when it walks down to a *pertinent* child biconnected component
928
//WPrevLink = theGraph->extFace[W].inversionFlag ? RootSide : 1^RootSide;
929
}
930
// Otherwise, Walkdown has been called on a bicomp with two distinct
931
// external face paths from RootVertex (a possibility in extension
932
// algorithms), so both external face path links from W do not indicate
933
// the RootVertex.
934
else
935
{
936
WPrevLink = theGraph->extFace[W].vertex[0] == RootVertex ? 0 : 1;
937
if (theGraph->extFace[W].vertex[WPrevLink] != RootVertex)
938
return NOTOK;
939
}
940
941
while (W != RootVertex)
942
{
943
/* If the vertex W is the descendant endpoint of an unembedded
944
back edge to I, then ... */
945
946
if (theGraph->V[W].adjacentTo != NIL)
947
{
948
/* Merge bicomps at cut vertices on theStack and add the back edge,
949
creating a new proper face. */
950
951
if (sp_NonEmpty(theGraph->theStack))
952
{
953
if ((RetVal = theGraph->functions.fpMergeBicomps(theGraph, I, RootVertex, W, WPrevLink)) != OK)
954
return RetVal;
955
}
956
theGraph->functions.fpEmbedBackEdgeToDescendant(theGraph, RootSide, RootVertex, W, WPrevLink);
957
958
/* Clear W's AdjacentTo flag so we don't add another edge to W if
959
this invocation of Walkdown visits W again later (and more
960
generally, so that no more back edges to W are added until
961
a future Walkup sets the flag to non-NIL again). */
962
963
theGraph->V[W].adjacentTo = NIL;
964
}
965
966
/* If there is a pertinent child bicomp, then we need to push it onto the stack
967
along with information about how we entered the cut vertex and how
968
we exit the root copy to get to the next vertex. */
969
970
if (theGraph->V[W].pertinentBicompList != NIL)
971
{
972
sp_Push2(theGraph->theStack, W, WPrevLink);
973
R = _GetPertinentChildBicomp(theGraph, W);
974
975
/* Get next active vertices X and Y on ext. face paths emanating from R */
976
977
X = theGraph->extFace[R].vertex[0];
978
XPrevLink = theGraph->extFace[X].vertex[1]==R ? 1 : 0;
979
Y = theGraph->extFace[R].vertex[1];
980
YPrevLink = theGraph->extFace[Y].vertex[0]==R ? 0 : 1;
981
982
/* If this is a bicomp with only two ext. face vertices, then
983
it could be that the orientation of the non-root vertex
984
doesn't match the orientation of the root due to our relaxed
985
orientation method. */
986
987
if (X == Y && theGraph->extFace[X].inversionFlag)
988
{
989
XPrevLink = 0;
990
YPrevLink = 1;
991
}
992
993
/* Now we implement the Walkdown's simple path selection rules!
994
If either X or Y is internally active (pertinent but not
995
externally active), then we pick it first. Otherwise,
996
we choose a pertinent vertex. If neither are pertinent,
997
then we let a handler decide. The default handler for
998
core planarity/outerplanarity decides to stop the WalkDown
999
with the current blocked bicomp at the top of the stack. */
1000
1001
if (_VertexActiveStatus(theGraph, X, I) == VAS_INTERNAL)
1002
{
1003
W = X;
1004
WPrevLink = XPrevLink;
1005
Rout = 0;
1006
}
1007
else if (_VertexActiveStatus(theGraph, Y, I) == VAS_INTERNAL)
1008
{
1009
W = Y;
1010
WPrevLink = YPrevLink;
1011
Rout = 1;
1012
}
1013
else if (PERTINENT(theGraph, X))
1014
{
1015
W = X;
1016
WPrevLink = XPrevLink;
1017
Rout = 0;
1018
}
1019
else if (PERTINENT(theGraph, Y))
1020
{
1021
W = Y;
1022
WPrevLink = YPrevLink;
1023
Rout = 1;
1024
}
1025
else
1026
{
1027
// Both the X and Y sides of the bicomp are blocked.
1028
// Let the application decide whether it can unblock the bicomp.
1029
// The core planarity embedder simply pushes (R, 0) onto the top of
1030
// the stack and returns NONEMBEDDABLE, which causes a return here
1031
// and enables isolation of planarity/outerplanary obstruction minor A
1032
if ((RetVal = theGraph->functions.fpHandleBlockedDescendantBicomp(theGraph, I, RootVertex, R, &Rout, &W, &WPrevLink)) != OK)
1033
return RetVal;
1034
}
1035
1036
sp_Push2(theGraph->theStack, R, Rout);
1037
}
1038
1039
/* Skip inactive vertices, which will be short-circuited
1040
later by our fast external face linking method (once
1041
upon a time, we added false edges called short-circuit
1042
edges to eliminate inactive vertices, but the extFace
1043
links can do the same job and also give us the ability
1044
to more quickly test planarity without creating an embedding). */
1045
1046
else if (_VertexActiveStatus(theGraph, W, I) == VAS_INACTIVE)
1047
{
1048
if (theGraph->functions.fpHandleInactiveVertex(theGraph, RootVertex, &W, &WPrevLink) != OK)
1049
return NOTOK;
1050
}
1051
1052
/* At this point, we know that W is not inactive, but its adjacentTo flag
1053
is clear, and it has no pertinent child bicomps. Therefore, it
1054
is an externally active stopping vertex. */
1055
1056
else break;
1057
}
1058
1059
/* We short-circuit the external face of the bicomp by hooking the root
1060
to the terminating externally active vertex so that inactive vertices
1061
are not visited in future iterations. This setting obviates the need
1062
for those short-circuit edges mentioned above.
1063
1064
NOTE: We skip the step if the stack is non-empty since in that case
1065
we did not actually merge the bicomps necessary to put
1066
W and RootVertex into the same bicomp. */
1067
1068
theGraph->extFace[RootVertex].vertex[RootSide] = W;
1069
theGraph->extFace[W].vertex[WPrevLink] = RootVertex;
1070
1071
/* If the bicomp is reduced to having only two external face vertices
1072
(the root and W), then we need to record whether the orientation
1073
of W is inverted relative to the root. This is used later when a
1074
future Walkdown descends to and merges the bicomp containing W.
1075
Going from the root to W, we only get the correct WPrevLink if
1076
we know whether or not W is inverted.
1077
NOTE: We clear the flag because it may have been set in W if W
1078
previously became part of a bicomp with only two ext. face
1079
vertices, but then was flipped and merged into a larger bicomp
1080
that is now again becoming a bicomp with only two ext. face vertices. */
1081
1082
if (theGraph->extFace[W].vertex[0] == theGraph->extFace[W].vertex[1] &&
1083
WPrevLink == RootSide)
1084
theGraph->extFace[W].inversionFlag = 1;
1085
else theGraph->extFace[W].inversionFlag = 0;
1086
1087
/* If we got back around to the root, then all edges
1088
are embedded, so we stop. */
1089
1090
if (W == RootVertex)
1091
break;
1092
}
1093
1094
return OK;
1095
}
1096
1097
1098
/********************************************************************
1099
gp_Embed()
1100
1101
First, a DFS tree is created in the graph (if not already done).
1102
Then, the graph is sorted by DFI.
1103
1104
Either a planar embedding is created in theGraph, or a Kuratowski
1105
subgraph is isolated. Either way, theGraph remains sorted by DFI
1106
since that is the most common desired result. The original vertex
1107
numbers are available in the 'v' members of the vertex graph nodes.
1108
Moreover, gp_SortVertices() can be invoked to put the vertices in
1109
the order of the input graph, at which point the 'v' members of the
1110
vertex graph nodes will contain the vertex DFIs.
1111
1112
return OK if the embedding was successfully created or no subgraph
1113
homeomorphic to a topological obstruction was found.
1114
1115
NOTOK on internal failure
1116
1117
NONEMBEDDABLE if the embedding couldn't be created due to
1118
the existence of a subgraph homeomorphic to a
1119
topological obstruction.
1120
1121
For core planarity, OK is returned when theGraph contains a planar
1122
embedding of the input graph, and NONEMBEDDABLE is returned when a
1123
subgraph homeomorphic to K5 or K3,3 has been isolated in theGraph.
1124
1125
Extension modules can overload functions used by gp_Embed to achieve
1126
alternate algorithms. In those cases, the return results are
1127
similar. For example, a K3,3 search algorithm would return
1128
NONEMBEDDABLE if it finds the K3,3 obstruction, and OK if the graph
1129
is planar or only contains K5 homeomorphs. Similarly, an
1130
outerplanarity module can return OK for an outerplanar embedding or
1131
NONEMBEDDABLE when a subgraph homeomorphic to K2,3 or K4 has been
1132
isolated.
1133
1134
The algorithm extension for gp_Embed() is encoded in the embedFlags,
1135
and the details of the return value can be found in the extension
1136
module that defines the embedding flag.
1137
1138
********************************************************************/
1139
1140
int gp_Embed(graphP theGraph, int embedFlags)
1141
{
1142
int N, I, J, child;
1143
int RetVal = OK;
1144
1145
/* Basic parameter checks */
1146
1147
if (theGraph==NULL)
1148
return NOTOK;
1149
1150
/* A little shorthand for the size of the graph */
1151
1152
N = theGraph->N;
1153
1154
/* Preprocessing */
1155
1156
theGraph->embedFlags = embedFlags;
1157
1158
if (gp_CreateDFSTree(theGraph) != OK)
1159
return NOTOK;
1160
1161
if (!(theGraph->internalFlags & FLAGS_SORTEDBYDFI))
1162
if (gp_SortVertices(theGraph) != OK)
1163
return NOTOK;
1164
1165
if (gp_LowpointAndLeastAncestor(theGraph) != OK)
1166
return NOTOK;
1167
1168
_CreateSortedSeparatedDFSChildLists(theGraph);
1169
1170
if (theGraph->functions.fpCreateFwdArcLists(theGraph) != OK)
1171
return NOTOK;
1172
1173
theGraph->functions.fpCreateDFSTreeEmbedding(theGraph);
1174
1175
/* In reverse DFI order, process each vertex by embedding its
1176
the 'back edges' from the vertex to its DFS descendants. */
1177
1178
for (I = 0; I < theGraph->edgeOffset; I++)
1179
theGraph->G[I].visited = N;
1180
1181
for (I = theGraph->N-1; I >= 0; I--)
1182
{
1183
RetVal = OK;
1184
1185
/* Do the Walkup for each cycle edge from I to a DFS descendant W. */
1186
1187
J = theGraph->V[I].fwdArcList;
1188
while (J != NIL)
1189
{
1190
theGraph->functions.fpWalkUp(theGraph, I, J);
1191
1192
J = gp_GetNextArc(theGraph, J);
1193
if (J == theGraph->V[I].fwdArcList)
1194
J = NIL;
1195
}
1196
1197
/* For each DFS child C of the current vertex with a pertinent
1198
child bicomp, do a Walkdown on each side of the bicomp rooted
1199
by tree edge (R, C), where R is a root copy of the current
1200
vertex stored at C+N and uniquely associated with the bicomp
1201
containing C. (NOTE: if C has no pertinent child bicomps, then
1202
there are no cycle edges from I to descendants of C). */
1203
1204
child = theGraph->V[I].separatedDFSChildList;
1205
while (child != NIL)
1206
{
1207
if (theGraph->V[child].pertinentBicompList != NIL)
1208
{
1209
// _Walkdown returns OK even if it couldn't embed all
1210
// back edges from I to the subtree rooted by child
1211
// It only returns NONEMBEDDABLE when it was blocked
1212
// on a descendant bicomp with stopping vertices along
1213
// both external face paths emanating from the bicomp root
1214
// Some extension algorithms are able to clear some such
1215
// blockages with a reduction, and those algorithms only
1216
// return NONEMBEDDABLE when unable to clear the blockage
1217
if ((RetVal = theGraph->functions.fpWalkDown(theGraph, I, child + N)) != OK)
1218
{
1219
if (RetVal == NONEMBEDDABLE)
1220
break;
1221
else
1222
return NOTOK;
1223
}
1224
}
1225
child = LCGetNext(theGraph->DFSChildLists,
1226
theGraph->V[I].separatedDFSChildList, child);
1227
}
1228
1229
/* If the Walkdown sequence is completed but not all forward edges
1230
are embedded or an explicit NONEMBEDDABLE result was returned,
1231
then the graph is not planar/outerplanar.
1232
The handler below is invoked because some extension algorithms are
1233
able to clear the blockage to planarity/outerplanarity and continue
1234
the embedder iteration loop (they return OK below).
1235
The default implementation simply returns NONEMBEDDABLE, which stops
1236
the embedding process. */
1237
1238
if (theGraph->V[I].fwdArcList != NIL || RetVal == NONEMBEDDABLE)
1239
{
1240
RetVal = theGraph->functions.fpHandleBlockedEmbedIteration(theGraph, I);
1241
if (RetVal != OK)
1242
break;
1243
}
1244
}
1245
1246
/* Postprocessing to orient the embedding and merge any remaining separated bicomps,
1247
or to isolate an obstruction to planarity/outerplanarity. Some extension algorithms
1248
either do nothing if they have already isolated a subgraph of interest, or they may
1249
do so now based on information collected by their implementations of
1250
HandleBlockedDescendantBicomp or HandleBlockedEmbedIteration */
1251
1252
return theGraph->functions.fpEmbedPostprocess(theGraph, I, RetVal);
1253
}
1254
1255
/********************************************************************
1256
HandleBlockedEmbedIteration()
1257
1258
At the end of each embedding iteration, this function is invoked
1259
if there are any unembedded cycle edges from the current vertex I
1260
to its DFS descendants. Specifically, the forward arc list of I is
1261
non-empty at the end of the edge addition processing for I.
1262
1263
We return NONEMBEDDABLE to cause iteration to stop because the
1264
graph is non-planar if any edges could not be embedded.
1265
1266
Extensions may overload this function and decide to proceed with or
1267
halt embedding iteration for application-specific reasons.
1268
For example, a search for K_{3,3} homeomorphs could reduce an
1269
isolated K5 homeomorph to something that can be ignored, and then
1270
return OK in order to continue the planarity algorithm in order to
1271
search for a K_{3,3} homeomorph elsewhere in the graph. On the
1272
other hand, if such an algorithm found a K_{3,3} homeomorph,
1273
perhaps alone or perhaps entangled with the K5 homeomorph, it would
1274
return NONEMBEDDABLE since there is no need to continue with
1275
embedding iterations once the desired embedding obstruction is found.
1276
1277
If this function returns OK, then embedding will proceed to the
1278
next iteration, or return OK if it finished the last iteration.
1279
1280
If this function returns NONEMBEDDABLE, then the embedder will
1281
stop iteration and return NONEMBEDDABLE. Note that the function
1282
_EmbedPostprocess() is still called in this case, allowing for
1283
further processing of the non-embeddable result, e.g. isolation
1284
of the desired embedding obstruction.
1285
1286
This function can return NOTOK to signify an internal error.
1287
********************************************************************/
1288
1289
int _HandleBlockedEmbedIteration(graphP theGraph, int I)
1290
{
1291
return NONEMBEDDABLE;
1292
}
1293
1294
/********************************************************************
1295
_EmbedPostprocess()
1296
1297
After the loop that embeds the cycle edges from each vertex to its
1298
DFS descendants, this method is invoked to postprocess the graph.
1299
If the graph is planar, then a consistent orientation is imposed
1300
on the vertices of the embedding, and any remaining separated
1301
biconnected components are joined together.
1302
If the graph is non-planar, then a subgraph homeomorphic to K5
1303
or K3,3 is isolated.
1304
Extensions may override this function to provide alternate
1305
behavior.
1306
1307
@param theGraph - the graph ready for postprocessing
1308
@param I - the last vertex processed by the edge embedding loop
1309
@param edgeEmbeddingResult -
1310
OK if all edge embedding iterations returned OK
1311
NONEMBEDDABLE if an embedding iteration failed to embed
1312
all edges for a vertex
1313
1314
@return NOTOK on internal failure
1315
NONEMBEDDABLE if a subgraph homeomorphic to a topological
1316
obstruction is isolated in the graph
1317
OK otherwise (for example if the graph contains a
1318
planar embedding or if a desired topological obstruction
1319
was not found)
1320
1321
*****************************************************************/
1322
1323
int _EmbedPostprocess(graphP theGraph, int I, int edgeEmbeddingResult)
1324
{
1325
int RetVal = edgeEmbeddingResult;
1326
1327
/* If an embedding was found, then post-process the embedding structure
1328
to eliminate root copies and give a consistent orientation to all vertices. */
1329
1330
if (edgeEmbeddingResult == OK)
1331
{
1332
if (_OrientVerticesInEmbedding(theGraph) != OK ||
1333
_JoinBicomps(theGraph) != OK)
1334
RetVal = NOTOK;
1335
}
1336
1337
/* If the graph was found to be unembeddable, then we want to isolate an
1338
obstruction. But, if a search flag was set, then we have already
1339
found a subgraph with the desired structure, so no further work is done. */
1340
1341
else if (edgeEmbeddingResult == NONEMBEDDABLE)
1342
{
1343
if (theGraph->embedFlags == EMBEDFLAGS_PLANAR)
1344
{
1345
if (_IsolateKuratowskiSubgraph(theGraph, I, NIL) != OK)
1346
RetVal = NOTOK;
1347
}
1348
else if (theGraph->embedFlags == EMBEDFLAGS_OUTERPLANAR)
1349
{
1350
if (_IsolateOuterplanarObstruction(theGraph, I, NIL) != OK)
1351
RetVal = NOTOK;
1352
}
1353
}
1354
1355
return RetVal;
1356
}
1357
1358
/********************************************************************
1359
_OrientVerticesInEmbedding()
1360
1361
Each vertex will then have an orientation, either clockwise or
1362
counterclockwise. All vertices in each bicomp need to have the
1363
same orientation.
1364
This method clears the stack, and the stack is clear when it
1365
is finished.
1366
Returns OK on success, NOTOK on implementation failure.
1367
********************************************************************/
1368
1369
int _OrientVerticesInEmbedding(graphP theGraph)
1370
{
1371
int R=0, edgeOffset = theGraph->edgeOffset;
1372
1373
sp_ClearStack(theGraph->theStack);
1374
1375
/* Run the array of root copy vertices. For each that is not defunct
1376
(i.e. has not been merged during embed), we orient the vertices
1377
in the bicomp for which it is the root vertex. */
1378
1379
for (R = theGraph->N; R < edgeOffset; R++)
1380
{
1381
if (gp_IsArc(theGraph, gp_GetFirstArc(theGraph, R)))
1382
{
1383
if (_OrientVerticesInBicomp(theGraph, R, 0) != OK)
1384
return NOTOK;
1385
}
1386
}
1387
return OK;
1388
}
1389
1390
/********************************************************************
1391
_OrientVerticesInBicomp()
1392
As a result of the work done so far, the edges around each vertex have
1393
been put in order, but the orientation may be counterclockwise or
1394
clockwise for different vertices within the same bicomp.
1395
We need to reverse the orientations of those vertices that are not
1396
oriented the same way as the root of the bicomp.
1397
1398
During embedding, a bicomp with root edge (v', c) may need to be flipped.
1399
We do this by inverting the root copy v' and implicitly inverting the
1400
orientation of the vertices in the subtree rooted by c by assigning -1
1401
to the sign of the DFSCHILD edge record leading to c.
1402
1403
We now use these signs to help propagate a consistent vertex orientation
1404
throughout all vertices that have been merged into the given bicomp.
1405
The bicomp root contains the orientation to be imposed on all parent
1406
copy vertices. We perform a standard depth first search to visit each
1407
vertex. A vertex must be inverted if the product of the edge signs
1408
along the tree edges between the bicomp root and the vertex is -1.
1409
1410
Finally, the PreserveSigns flag, if set, performs the inversions
1411
but does not change any of the edge signs. This allows a second
1412
invocation of this function to restore the state of the bicomp
1413
as it was before the first call.
1414
1415
This method uses the stack but preserves whatever may have been
1416
on it. In debug mode, it will return NOTOK if the stack overflows.
1417
This method pushes at most two integers per vertext in the bicomp.
1418
1419
Returns OK on success, NOTOK on implementation failure.
1420
********************************************************************/
1421
1422
int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns)
1423
{
1424
int V, J, invertedFlag;
1425
int stackBottom = sp_GetCurrentSize(theGraph->theStack);
1426
1427
sp_Push2(theGraph->theStack, BicompRoot, 0);
1428
1429
while (sp_GetCurrentSize(theGraph->theStack) > stackBottom)
1430
{
1431
/* Pop a vertex to orient */
1432
sp_Pop2(theGraph->theStack, V, invertedFlag);
1433
1434
/* Invert the vertex if the inverted flag is set */
1435
if (invertedFlag)
1436
_InvertVertex(theGraph, V);
1437
1438
/* Push the vertex's DFS children that are in the bicomp */
1439
J = gp_GetFirstArc(theGraph, V);
1440
while (gp_IsArc(theGraph, J))
1441
{
1442
if (theGraph->G[J].type == EDGE_DFSCHILD)
1443
{
1444
sp_Push2(theGraph->theStack, theGraph->G[J].v,
1445
invertedFlag ^ GET_EDGEFLAG_INVERTED(theGraph, J));
1446
1447
if (!PreserveSigns)
1448
CLEAR_EDGEFLAG_INVERTED(theGraph, J);
1449
}
1450
1451
J = gp_GetNextArc(theGraph, J);
1452
}
1453
}
1454
return OK;
1455
}
1456
1457
/********************************************************************
1458
_JoinBicomps()
1459
The embedding algorithm works by only joining bicomps once the result
1460
forms a larger bicomp. However, if the original graph was separable
1461
or disconnected, then the result of the embed function will be a
1462
graph that contains each bicomp as a distinct entity. The root of
1463
each bicomp will be in the region N to 2N-1. This function merges
1464
the bicomps into one connected graph.
1465
********************************************************************/
1466
1467
int _JoinBicomps(graphP theGraph)
1468
{
1469
int R, N, edgeOffset=theGraph->edgeOffset;
1470
1471
for (R=N=theGraph->N; R < edgeOffset; R++)
1472
if (gp_IsArc(theGraph, gp_GetFirstArc(theGraph, R)))
1473
_MergeVertex(theGraph, theGraph->V[R-N].DFSParent, 0, R);
1474
1475
return OK;
1476
}
1477
1478
/****************************************************************************
1479
_OrientExternalFacePath()
1480
1481
The vertices along the path (v ... w) are assumed to be degree two vertices
1482
in an external face path connecting u and x. This method imparts the
1483
orientation of u and x onto the vertices v ... w.
1484
The work done is on the order of the path length.
1485
Returns OK if the external face path was oriented, NOTOK on implementation
1486
error (i.e. if a condition arises providing the path is not on the
1487
external face).
1488
****************************************************************************/
1489
1490
int _OrientExternalFacePath(graphP theGraph, int u, int v, int w, int x)
1491
{
1492
int e_u, e_v, e_ulink, e_vlink;
1493
1494
// Get the edge record in u that indicates v; uses the twinarc method to
1495
// ensure the cost is dominated by the degree of v (which is 2), not u
1496
// (which can be any degree).
1497
e_u = gp_GetTwinArc(theGraph, gp_GetNeighborEdgeRecord(theGraph, v, u));
1498
1499
do {
1500
// Get the external face link in vertex u that indicates the
1501
// edge e_u which connects to the next vertex v in the path
1502
// As a sanity check, we determine whether e_u is an
1503
// external face edge, because there would be an internal
1504
// implementation error if not
1505
if (gp_GetFirstArc(theGraph, u) == e_u)
1506
e_ulink = 0;
1507
else if (gp_GetLastArc(theGraph, u) == e_u)
1508
e_ulink = 1;
1509
else return NOTOK;
1510
1511
v = theGraph->G[e_u].v;
1512
1513
// Now get the external face link in vertex v that indicates the
1514
// edge e_v which connects back to the prior vertex u.
1515
e_v = gp_GetTwinArc(theGraph, e_u);
1516
1517
if (gp_GetFirstArc(theGraph, v) == e_v)
1518
e_vlink = 0;
1519
else if (gp_GetLastArc(theGraph, v) == e_v)
1520
e_vlink = 1;
1521
else return NOTOK;
1522
1523
// The vertices u and v are inversely oriented if they
1524
// use the same link to indicate the edge [e_u, e_v].
1525
if (e_vlink == e_ulink)
1526
{
1527
_InvertVertex(theGraph, v);
1528
e_vlink = 1^e_vlink;
1529
}
1530
1531
// This update of the extFace short-circuit is polite but unnecessary.
1532
// This orientation only occurs once we know we can isolate a K_{3,3},
1533
// at which point the extFace data structure is not used.
1534
theGraph->extFace[u].vertex[e_ulink] = v;
1535
theGraph->extFace[v].vertex[e_vlink] = u;
1536
1537
u = v;
1538
e_u = gp_GetArc(theGraph, v, 1^e_vlink);
1539
} while (u != x);
1540
1541
return OK;
1542
}
1543
1544
/****************************************************************************
1545
_SetVisitedOnPath()
1546
This method sets the visited flags to 'visited' on the vertices and edges on
1547
the path (u, v, ..., w, x) in which all vertices except the endpoints u and x
1548
are degree 2. This method avoids performing more than constant work at the
1549
path endpoints u and x, so the total work is on the order of the path length.
1550
1551
Returns OK on success, NOTOK on internal failure
1552
****************************************************************************/
1553
1554
int _SetVisitedOnPath(graphP theGraph, int u, int v, int w, int x, int visited)
1555
{
1556
int e, eTwin, pathLength=0;
1557
1558
// We want to exit u from e, but we get eTwin first here in order to avoid
1559
// work, in case the degree of u is greater than 2.
1560
eTwin = gp_GetNeighborEdgeRecord(theGraph, v, u);
1561
if (!gp_IsArc(theGraph, eTwin))
1562
return NOTOK;
1563
e = gp_GetTwinArc(theGraph, eTwin);
1564
1565
v = u;
1566
1567
do {
1568
// Mark the vertex and the exiting edge
1569
theGraph->G[v].visited = visited;
1570
theGraph->G[e].visited = visited;
1571
theGraph->G[eTwin].visited = visited;
1572
1573
// Get the next vertex
1574
v = theGraph->G[e].v;
1575
e = gp_GetNextArcCircular(theGraph, eTwin);
1576
eTwin = gp_GetTwinArc(theGraph, e);
1577
1578
// A simple reality check on the preconditions of this method
1579
if (++pathLength > theGraph->N)
1580
return NOTOK;
1581
1582
} while (v != x);
1583
1584
// Mark the last vertex with 'visited'
1585
theGraph->G[x].visited = visited;
1586
1587
return OK;
1588
}
1589
1590