Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/graphs/planarity/graphK4Search.c
4079 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
#include "graphK4Search.h"
46
#include "graphK4Search.private.h"
47
48
extern int K4SEARCH_ID;
49
50
#include "graph.h"
51
52
/* Imported functions */
53
54
extern void _ClearIsolatorContext(graphP theGraph);
55
extern void _FillVisitedFlags(graphP, int);
56
extern int _FillVisitedFlagsInBicomp(graphP theGraph, int BicompRoot, int FillValue);
57
extern int _FillVisitedFlagsInOtherBicomps(graphP theGraph, int BicompRoot, int FillValue);
58
extern void _FillVisitedFlagsInUnembeddedEdges(graphP theGraph, int FillValue);
59
extern int _SetVertexTypeInBicomp(graphP theGraph, int BicompRoot, int theType);
60
extern int _DeleteUnmarkedEdgesInBicomp(graphP theGraph, int BicompRoot);
61
extern int _ComputeArcType(graphP theGraph, int a, int b, int edgeType);
62
extern int _SetEdgeType(graphP theGraph, int u, int v);
63
64
extern int _GetNextVertexOnExternalFace(graphP theGraph, int curVertex, int *pPrevLink);
65
extern int _JoinBicomps(graphP theGraph);
66
extern void _FindActiveVertices(graphP theGraph, int R, int *pX, int *pY);
67
extern int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns);
68
extern int _OrientVerticesInEmbedding(graphP theGraph);
69
extern void _InvertVertex(graphP theGraph, int V);
70
extern int _SetVisitedOnPath(graphP theGraph, int u, int v, int w, int x, int visited);
71
extern int _OrientExternalFacePath(graphP theGraph, int u, int v, int w, int x);
72
73
extern int _FindUnembeddedEdgeToAncestor(graphP theGraph, int cutVertex, int *pAncestor, int *pDescendant);
74
extern int _FindUnembeddedEdgeToCurVertex(graphP theGraph, int cutVertex, int *pDescendant);
75
extern int _GetLeastAncestorConnection(graphP theGraph, int cutVertex);
76
77
extern int _SetVertexTypesForMarkingXYPath(graphP theGraph);
78
extern int _MarkHighestXYPath(graphP theGraph);
79
extern int _MarkPathAlongBicompExtFace(graphP theGraph, int startVert, int endVert);
80
extern int _AddAndMarkEdge(graphP theGraph, int ancestor, int descendant);
81
extern int _DeleteUnmarkedVerticesAndEdges(graphP theGraph);
82
83
extern int _IsolateOuterplanarityObstructionA(graphP theGraph);
84
extern int _IsolateOuterplanarityObstructionB(graphP theGraph);
85
extern int _IsolateOuterplanarityObstructionE(graphP theGraph);
86
87
/* Private functions for K4 searching (exposed to the extension). */
88
89
int _SearchForK4(graphP theGraph, int I);
90
int _SearchForK4InBicomp(graphP theGraph, K4SearchContext *context, int I, int R);
91
92
/* Private functions for K4 searching. */
93
94
int _K4_ChooseTypeOfNonOuterplanarityMinor(graphP theGraph, int I, int R);
95
96
int _K4_FindSecondActiveVertexOnLowExtFacePath(graphP theGraph);
97
int _K4_FindPlanarityActiveVertex(graphP theGraph, int I, int R, int prevLink, int *pW);
98
int _K4_FindSeparatingInternalEdge(graphP theGraph, int R, int prevLink, int A, int *pW, int *pX, int *pY);
99
void _K4_SetTypeOnExternalFacePath(graphP theGraph, int R, int prevLink, int A, int type);
100
101
int _K4_IsolateMinorA1(graphP theGraph);
102
int _K4_IsolateMinorA2(graphP theGraph);
103
int _K4_IsolateMinorB1(graphP theGraph);
104
int _K4_IsolateMinorB2(graphP theGraph);
105
106
int _K4_ReduceBicompToEdge(graphP theGraph, K4SearchContext *context, int R, int W);
107
int _K4_ReducePathComponent(graphP theGraph, K4SearchContext *context, int R, int prevLink, int A);
108
int _K4_ReducePathToEdge(graphP theGraph, K4SearchContext *context, int edgeType, int R, int e_R, int A, int e_A);
109
110
int _K4_GetCumulativeOrientationOnDFSPath(graphP theGraph, int ancestor, int descendant);
111
int _K4_TestPathComponentForAncestor(graphP theGraph, int R, int prevLink, int A);
112
void _K4_SetVisitedInPathComponent(graphP theGraph, int R, int prevLink, int A, int fill);
113
int _K4_DeleteUnmarkedEdgesInPathComponent(graphP theGraph, int R, int prevLink, int A);
114
115
int _K4_RestoreReducedPath(graphP theGraph, K4SearchContext *context, int J);
116
int _K4_RestoreAndOrientReducedPaths(graphP theGraph, K4SearchContext *context);
117
118
int _MarkEdge(graphP theGraph, int x, int y);
119
120
/****************************************************************************
121
_SearchForK4InBicomps()
122
123
This method is the main handler for a blocked iteration of the core
124
planarity/outerplanarity algorithm. At the end of processing for the
125
current vertex I, if there is unresolved pertinence for I (i.e. if
126
there are unembedded forward arcs from I to its DFS descendants), then
127
this method is called.
128
129
In this case, the fwdArcList of I is non-empty; it is also sorted by
130
DFI of the descendants endpoints. Also, the sortedDFSChildList of I
131
is non-empty, and it is also sorted by DFI of the children.
132
133
Any DFS child C of I that has a p2dFwdArcCount of zero is simply
134
removed from the sortedDFSChildList of I as it is encountered because
135
the zero value means that there are no unembedded forward arcs from I
136
to descendants of C.
137
138
As soon as a DFS child C is encountered that has a p2dFwdArcCount
139
greater than zero, we simply invoke SearchForK4InBicomp(). The result
140
will either be an isolated K4 homeomorph, or an indication that a
141
reduction has occurred. In the latter case, the WalkDown can be
142
invoked to resolve more of the pertinence of the bicomp. The WalkDown
143
may or may not resolve all the remaining pertinence in the DFS
144
subtree rooted by C. If it does, then the p2dFwdArcCount of C will
145
drop to zero, and the next iteration of the loop in this method will
146
remove C from the sortedDFSChildList of I. If the p2dFwdArcCount of
147
C does not drop to zero during the WalkDown, then the bicomp that
148
contains C is ready for another search in the next iteration of the
149
main loop of this method.
150
151
Overall, the only two legitimate outcomes of this method call are
152
1) reductions have enabled further WalkDown calls to resolve all
153
of the pertinence of I
154
2) a subgraph homeomorphic to K4 has been isolated
155
156
Returns
157
OK if the pertinence of I was fully resolved, indicating that the
158
core planarity/outerplanarity algorithm can proceed
159
NONEMBEDDABLE if a subgraph homeomorphic to K4 has been isolated
160
NOTOK on failure
161
****************************************************************************/
162
163
int _SearchForK4InBicomps(graphP theGraph, int I)
164
{
165
int C, R, RetVal=OK;
166
K4SearchContext *context = NULL;
167
168
gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
169
if (context == NULL)
170
return NOTOK;
171
172
while ((C = context->V[I].sortedDFSChildList) != NIL)
173
{
174
if (context->V[C].p2dFwdArcCount == 0)
175
{
176
context->V[I].sortedDFSChildList = LCDelete(
177
context->sortedDFSChildLists,
178
context->V[I].sortedDFSChildList, C);
179
}
180
else
181
{
182
R = C + theGraph->N;
183
RetVal = _SearchForK4InBicomp(theGraph, context, I, R);
184
if (RetVal != OK)
185
break;
186
if ((RetVal = theGraph->functions.fpWalkDown(theGraph, I, R)) != OK)
187
return RetVal;
188
}
189
}
190
191
return RetVal;
192
}
193
194
/****************************************************************************
195
_SearchForK4InBicomp()
196
****************************************************************************/
197
198
int _SearchForK4InBicomp(graphP theGraph, K4SearchContext *context, int I, int R)
199
{
200
isolatorContextP IC = &theGraph->IC;
201
202
if (context == NULL)
203
{
204
gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
205
if (context == NULL)
206
return NOTOK;
207
}
208
209
// Begin by determining whether minor A, B or E is detected
210
if (_K4_ChooseTypeOfNonOuterplanarityMinor(theGraph, I, R) != OK)
211
return NOTOK;
212
213
// Minor A indicates the existence of K_{2,3} homeomorphs, but
214
// we run additional tests to see whether we can either find an
215
// entwined K4 homeomorph or reduce the bicomp so that the WalkDown
216
// is enabled to continue to resolve pertinence
217
if (theGraph->IC.minorType & MINORTYPE_A)
218
{
219
// Now that we know we have minor A, we can afford to orient the
220
// bicomp because we will either find the desired K4 or we will
221
// reduce the bicomp to an edge. The tests for A1 and A2 are easier
222
// to implement on an oriented bicomp.
223
// NOTE: We're in the midst of the WalkDown, so the stack may be
224
// non-empty, and it has to be preserved with constant cost.
225
// The stack will have at most 4 integers per cut vertex
226
// merge point, and this operation will push at most two
227
// integers per tree edge in the bicomp, so the stack
228
// will not overflow.
229
if (sp_GetCapacity(theGraph->theStack) < 6*theGraph->N)
230
return NOTOK;
231
232
if (_OrientVerticesInBicomp(theGraph, R, 1) != OK)
233
return NOTOK;
234
235
// Case A1: Test whether there is an active vertex Z other than W
236
// along the external face path [X, ..., W, ..., Y]
237
if (_K4_FindSecondActiveVertexOnLowExtFacePath(theGraph) == TRUE)
238
{
239
// Now that we know we can find a K4, the Walkdown will not continue
240
// and we can do away with the stack content.
241
sp_ClearStack(theGraph->theStack);
242
243
// Restore the orientations of the vertices in the bicomp, then orient
244
// the whole embedding, so we can restore and orient the reduced paths
245
if (_OrientVerticesInBicomp(theGraph, R, 1) != OK ||
246
_OrientVerticesInEmbedding(theGraph) != OK ||
247
_K4_RestoreAndOrientReducedPaths(theGraph, context) != OK)
248
return NOTOK;
249
250
// Set up to isolate K4 homeomorph
251
_FillVisitedFlags(theGraph, 0);
252
253
if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE)
254
return NOTOK;
255
256
if (IC->uz < IC->v)
257
{
258
if (_FindUnembeddedEdgeToAncestor(theGraph, IC->z, &IC->uz, &IC->dz) != TRUE)
259
return NOTOK;
260
}
261
else
262
{
263
if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->z, &IC->dz) != TRUE)
264
return NOTOK;
265
}
266
267
// Isolate the K4 homeomorph
268
if (_K4_IsolateMinorA1(theGraph) != OK ||
269
_DeleteUnmarkedVerticesAndEdges(theGraph) != OK)
270
return NOTOK;
271
272
// Indicate success by returning NONEMBEDDABLE
273
return NONEMBEDDABLE;
274
}
275
276
// Case A2: Test whether the bicomp has an XY path
277
// NOTE: As mentioned above, the stack is also preserved here.
278
// It will have at most 4 integers per cut vertex merge point,
279
// and this operation will push at most one integer per tree
280
// edge in the bicomp, so the stack will not overflow.
281
if (_SetVertexTypesForMarkingXYPath(theGraph) != OK)
282
return NOTOK;
283
284
// Marking the X-Y path relies on the bicomp visited flags being zero
285
if (_FillVisitedFlagsInBicomp(theGraph, R, 0) != OK)
286
return NOTOK;
287
288
// NOTE: This call preserves the stack and does not overflow. There
289
// are at most 4 integers per cut vertex merge point, all of which
290
// are not in the bicomp, and this call pushes at most 3 integers
291
// per bicomp vertex, so the maximum stack requirement is 4N
292
if (_MarkHighestXYPath(theGraph) == TRUE)
293
{
294
// Now that we know we can find a K4, the Walkdown will not continue
295
// and we can do away with the stack content.
296
sp_ClearStack(theGraph->theStack);
297
298
// Restore the orientations of the vertices in the bicomp, then orient
299
// the whole embedding, so we can restore and orient the reduced paths
300
if (_OrientVerticesInBicomp(theGraph, R, 1) != OK ||
301
_OrientVerticesInEmbedding(theGraph) != OK ||
302
_K4_RestoreAndOrientReducedPaths(theGraph, context) != OK)
303
return NOTOK;
304
305
// Set up to isolate K4 homeomorph
306
_FillVisitedFlags(theGraph, 0);
307
308
if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE)
309
return NOTOK;
310
311
// Isolate the K4 homeomorph
312
if (_MarkHighestXYPath(theGraph) != TRUE ||
313
_K4_IsolateMinorA2(theGraph) != OK ||
314
_DeleteUnmarkedVerticesAndEdges(theGraph) != OK)
315
return NOTOK;
316
317
// Indicate success by returning NONEMBEDDABLE
318
return NONEMBEDDABLE;
319
}
320
321
// else if there was no X-Y path, then we restore the vertex types to
322
// unknown (though it would suffice to do it just to R and W)
323
if (_SetVertexTypeInBicomp(theGraph, R, TYPE_UNKNOWN) != OK)
324
return NOTOK;
325
326
// Since neither A1 nor A2 is found, then we reduce the bicomp to the
327
// tree edge (R, W).
328
// NOTE: The visited flags for R and W are restored to values appropriate
329
// for continuing with future embedding steps
330
// NOTE: This method invokes several routines that use the stack, but
331
// all of them preserve the stack and each pushes at most one
332
// integer per bicomp vertex and pops all of them before returning.
333
// Again, this means the stack will not overflow.
334
if (_K4_ReduceBicompToEdge(theGraph, context, R, IC->w) != OK)
335
return NOTOK;
336
337
// Return OK so that the WalkDown can continue resolving the pertinence of I.
338
return OK;
339
}
340
341
// Minor B also indicates the existence of K_{2,3} homeomorphs, but
342
// we run additional tests to see whether we can either find an
343
// entwined K4 homeomorph or reduce a portion of the bicomp so that
344
// the WalkDown can be reinvoked on the bicomp
345
else if (theGraph->IC.minorType & MINORTYPE_B)
346
{
347
int a_x, a_y;
348
349
// Reality check on stack state
350
if (sp_NonEmpty(theGraph->theStack))
351
return NOTOK;
352
353
// Find the vertices a_x and a_y that are active (pertinent or future pertinent)
354
// and also first along the external face paths emanating from the bicomp root
355
if (_K4_FindPlanarityActiveVertex(theGraph, I, R, 1, &a_x) != OK ||
356
_K4_FindPlanarityActiveVertex(theGraph, I, R, 0, &a_y) != OK)
357
return NOTOK;
358
359
// Case B1: If both a_x and a_y are future pertinent, then we can stop and
360
// isolate a subgraph homeomorphic to K4.
361
if (a_x != a_y && FUTUREPERTINENT(theGraph, a_x, I) && FUTUREPERTINENT(theGraph, a_y, I))
362
{
363
if (_OrientVerticesInEmbedding(theGraph) != OK ||
364
_K4_RestoreAndOrientReducedPaths(theGraph, context) != OK)
365
return NOTOK;
366
367
// Set up to isolate K4 homeomorph
368
_FillVisitedFlags(theGraph, 0);
369
370
IC->x = a_x;
371
IC->y = a_y;
372
373
if (_FindUnembeddedEdgeToAncestor(theGraph, IC->x, &IC->ux, &IC->dx) != TRUE ||
374
_FindUnembeddedEdgeToAncestor(theGraph, IC->y, &IC->uy, &IC->dy) != TRUE)
375
return NOTOK;
376
377
// Isolate the K4 homeomorph
378
if (_K4_IsolateMinorB1(theGraph) != OK ||
379
_DeleteUnmarkedVerticesAndEdges(theGraph) != OK)
380
return NOTOK;
381
382
// Indicate success by returning NONEMBEDDABLE
383
return NONEMBEDDABLE;
384
}
385
386
// Reality check: The bicomp with root R is pertinent, and the only
387
// pertinent or future pertinent vertex on the external face is a_x,
388
// so it must also be pertinent.
389
if (a_x == a_y && !PERTINENT(theGraph, a_x))
390
return NOTOK;
391
392
// Case B2: Determine whether there is an internal separating X-Y path for a_x or for a_y
393
// The method makes appropriate isolator context settings if the separator edge is found
394
if (_K4_FindSeparatingInternalEdge(theGraph, R, 1, a_x, &IC->w, &IC->px, &IC->py) == TRUE ||
395
_K4_FindSeparatingInternalEdge(theGraph, R, 0, a_y, &IC->w, &IC->py, &IC->px) == TRUE)
396
{
397
if (_OrientVerticesInEmbedding(theGraph) != OK ||
398
_K4_RestoreAndOrientReducedPaths(theGraph, context) != OK)
399
return NOTOK;
400
401
// Set up to isolate K4 homeomorph
402
_FillVisitedFlags(theGraph, 0);
403
404
if (PERTINENT(theGraph, IC->w))
405
{
406
if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE)
407
return NOTOK;
408
}
409
else
410
{
411
IC->z = IC->w;
412
if (_FindUnembeddedEdgeToAncestor(theGraph, IC->z, &IC->uz, &IC->dz) != TRUE)
413
return NOTOK;
414
}
415
416
// The X-Y path doesn't have to be the same one that was associated with the
417
// separating internal edge.
418
if (_SetVertexTypesForMarkingXYPath(theGraph) != OK ||
419
_MarkHighestXYPath(theGraph) != TRUE)
420
return NOTOK;
421
422
// Isolate the K4 homeomorph
423
if (_K4_IsolateMinorB2(theGraph) != OK ||
424
_DeleteUnmarkedVerticesAndEdges(theGraph) != OK)
425
return NOTOK;
426
427
// Indicate success by returning NONEMBEDDABLE
428
return NONEMBEDDABLE;
429
}
430
431
// If K_4 homeomorph not found, make reductions along a_x and a_y paths.
432
if (a_x == a_y)
433
{
434
// In the special case where both paths lead to the same vertex, we can
435
// reduce the bicomp to a single edge, which avoids issues of reversed
436
// orientation between the bicomp root and the vertex.
437
if (_K4_ReduceBicompToEdge(theGraph, context, R, a_x) != OK)
438
return NOTOK;
439
}
440
else
441
{
442
// When a_x and a_y are distinct, we reduce each path from root to the vertex
443
if (_K4_ReducePathComponent(theGraph, context, R, 1, a_x) != OK ||
444
_K4_ReducePathComponent(theGraph, context, R, 0, a_y) != OK)
445
return NOTOK;
446
}
447
448
// Return OK to indicate that WalkDown processing may proceed to resolve
449
// more of the pertinence of this bicomp.
450
return OK;
451
}
452
453
// Minor E indicates the desired K4 homeomorph, so we isolate it and return NONEMBEDDABLE
454
else if (theGraph->IC.minorType & MINORTYPE_E)
455
{
456
// Reality check on stack state
457
if (sp_NonEmpty(theGraph->theStack))
458
return NOTOK;
459
460
// Impose consistent orientation on the embedding so we can then
461
// restore the reduced paths.
462
if (_OrientVerticesInEmbedding(theGraph) != OK ||
463
_K4_RestoreAndOrientReducedPaths(theGraph, context) != OK)
464
return NOTOK;
465
466
// Set up to isolate minor E
467
_FillVisitedFlags(theGraph, 0);
468
469
if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE)
470
return NOTOK;
471
472
if (_SetVertexTypesForMarkingXYPath(theGraph) != OK)
473
return NOTOK;
474
if (_MarkHighestXYPath(theGraph) != TRUE)
475
return NOTOK;
476
477
// Isolate the K4 homeomorph
478
if (_IsolateOuterplanarityObstructionE(theGraph) != OK ||
479
_DeleteUnmarkedVerticesAndEdges(theGraph) != OK)
480
return NOTOK;
481
482
// Return indication that K4 homeomorph has been found
483
return NONEMBEDDABLE;
484
}
485
486
// You never get here in an error-free implementation like this one
487
return NOTOK;
488
}
489
490
/****************************************************************************
491
_K4_ChooseTypeOfNonOuterplanarityMinor()
492
This is an overload of the function _ChooseTypeOfNonOuterplanarityMinor()
493
that avoids processing the whole bicomp rooted by R, e.g. to orient its
494
vertices or label the vertices of its external face.
495
This is necessary in particular because of the reduction processing on
496
MINORTYPE_B. When a K2,3 is found by minor B, we may not be able to find
497
an entangled K4, so a reduction is performed, but it only eliminates
498
part of the bicomp and the operations here need to avoid touching parts
499
of the bicomp that won't be reduced, except by a constant amount of course.
500
****************************************************************************/
501
502
int _K4_ChooseTypeOfNonOuterplanarityMinor(graphP theGraph, int I, int R)
503
{
504
int XPrevLink=1, YPrevLink=0;
505
int Wx, WxPrevLink, Wy, WyPrevLink;
506
507
_ClearIsolatorContext(theGraph);
508
509
theGraph->IC.v = I;
510
theGraph->IC.r = R;
511
512
// Reality check on data structure integrity
513
if (!gp_IsArc(theGraph, gp_GetFirstArc(theGraph, R)))
514
return NOTOK;
515
516
// We are essentially doing a _FindActiveVertices() here, except two things:
517
// 1) for outerplanarity we know the first vertices along the paths from R
518
// are the desired vertices because all vertices are "externally active"
519
// 2) We have purposely not oriented the bicomp, so the XPrevLink result is
520
// needed to help find the pertinent vertex W
521
theGraph->IC.x = _GetNextVertexOnExternalFace(theGraph, R, &XPrevLink);
522
theGraph->IC.y = _GetNextVertexOnExternalFace(theGraph, R, &YPrevLink);
523
524
// We are essentially doing a _FindPertinentVertex() here, except two things:
525
// 1) It is not known whether the reduction of the path through X or the path
526
// through Y will enable the pertinence of W to be resolved, so it is
527
// necessary to perform parallel face traversal to find W with a cost no
528
// more than twice what it will take to resolve the W's pertinence
529
// (assuming we have to do a reduction rather than finding an entangled K4)
530
// 2) In the normal _FindPertinentVertex(), the bicomp is already oriented, so
531
// the "prev link" is hard coded to traverse down the X side. In this
532
// implementation, the bicomp is purposely not oriented, so we need to know
533
// XPrevLink and YPrevLink in order to set off in the correct directions.
534
Wx = theGraph->IC.x;
535
WxPrevLink = XPrevLink;
536
Wy = theGraph->IC.y;
537
WyPrevLink = YPrevLink;
538
theGraph->IC.w = NIL;
539
540
while (Wx != theGraph->IC.y)
541
{
542
Wx = _GetNextVertexOnExternalFace(theGraph, Wx, &WxPrevLink);
543
if (PERTINENT(theGraph, Wx))
544
{
545
theGraph->IC.w = Wx;
546
break;
547
}
548
Wy = _GetNextVertexOnExternalFace(theGraph, Wy, &WyPrevLink);
549
if (PERTINENT(theGraph, Wy))
550
{
551
theGraph->IC.w = Wy;
552
break;
553
}
554
}
555
556
if (theGraph->IC.w == NIL)
557
return NOTOK;
558
559
// If the root copy is not a root copy of the current vertex I,
560
// then the Walkdown terminated on a descendant bicomp, which is Minor A.
561
if (theGraph->V[R - theGraph->N].DFSParent != I)
562
theGraph->IC.minorType |= MINORTYPE_A;
563
564
// If W has a pertinent child bicomp, then we've found Minor B.
565
// Notice this is different from planarity, in which minor B is indicated
566
// only if the pertinent child bicomp is also externally active under the
567
// planarity processing model (i.e. future pertinent).
568
else if (theGraph->V[theGraph->IC.w].pertinentBicompList != NIL)
569
theGraph->IC.minorType |= MINORTYPE_B;
570
571
// The only other result is minor E (we will search for the X-Y path later)
572
else
573
theGraph->IC.minorType |= MINORTYPE_E;
574
575
return OK;
576
}
577
578
/****************************************************************************
579
_K4_FindSecondActiveVertexOnLowExtFacePath()
580
581
This method is used in the processing of obstruction A, so it can take
582
advantage of the bicomp being oriented beforehand.
583
584
This method determines whether there is an active vertex Z other than W on
585
the path [X, ..., W, ..., Y]. By active, we mean a vertex that connects
586
by an unembedded edge to either I or an ancestor of I. That is, a vertext
587
that is pertinent or future pertinent (would be pertinent in a future step
588
of the embedder).
589
590
Unlike the core planarity embedder, in outerplanarity-related algorithms,
591
future pertinence is different from external activity, and we need to know
592
about *actual connections* from each vertex to ancestors of IC.v, so we
593
use PERTINENT() and FUTUREPERTINENT() rather than _VertexActiveStatus().
594
****************************************************************************/
595
596
int _K4_FindSecondActiveVertexOnLowExtFacePath(graphP theGraph)
597
{
598
int Z=theGraph->IC.r, ZPrevLink=1;
599
600
// First we test X for future pertinence only (if it were pertinent, then
601
// we wouldn't have been blocked up on this bicomp)
602
Z = _GetNextVertexOnExternalFace(theGraph, Z, &ZPrevLink);
603
if (FUTUREPERTINENT(theGraph, Z, theGraph->IC.v))
604
{
605
theGraph->IC.z = Z;
606
theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, Z);
607
return TRUE;
608
}
609
610
// Now we move on to test all the vertices strictly between X and Y on
611
// the lower external face path, except W, for either pertinence or
612
// future pertinence.
613
Z = _GetNextVertexOnExternalFace(theGraph, Z, &ZPrevLink);
614
615
while (Z != theGraph->IC.y)
616
{
617
if (Z != theGraph->IC.w)
618
{
619
if (FUTUREPERTINENT(theGraph, Z, theGraph->IC.v))
620
{
621
theGraph->IC.z = Z;
622
theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, Z);
623
return TRUE;
624
}
625
else if (PERTINENT(theGraph, Z))
626
{
627
theGraph->IC.z = Z;
628
theGraph->IC.uz = theGraph->IC.v;
629
return TRUE;
630
}
631
}
632
633
Z = _GetNextVertexOnExternalFace(theGraph, Z, &ZPrevLink);
634
}
635
636
// Now we test Y for future pertinence (same explanation as for X above)
637
if (FUTUREPERTINENT(theGraph, Z, theGraph->IC.v))
638
{
639
theGraph->IC.z = Z;
640
theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, Z);
641
return TRUE;
642
}
643
644
// We didn't find the desired second vertex, so report FALSE
645
return FALSE;
646
}
647
648
/****************************************************************************
649
_K4_FindPlanarityActiveVertex()
650
This service routine starts out at R and heads off in the direction opposite
651
the prevLink to find the first "planarity active" vertex, i.e. the first one
652
that is pertinent or future pertinent.
653
****************************************************************************/
654
655
int _K4_FindPlanarityActiveVertex(graphP theGraph, int I, int R, int prevLink, int *pW)
656
{
657
int W = R, WPrevLink = prevLink;
658
659
W = _GetNextVertexOnExternalFace(theGraph, R, &WPrevLink);
660
661
while (W != R)
662
{
663
if (PERTINENT(theGraph, W) || FUTUREPERTINENT(theGraph, W, I))
664
{
665
*pW = W;
666
return OK;
667
}
668
669
W = _GetNextVertexOnExternalFace(theGraph, W, &WPrevLink);
670
}
671
672
return NOTOK;
673
}
674
675
/****************************************************************************
676
_K4_FindSeparatingInternalEdge()
677
678
Logically, this method is similar to calling MarkHighestXYPath() to
679
see if there is an internal separator between R and A.
680
However, that method cannot be called because the bicomp is not oriented.
681
682
Because this is an outerplanarity related algorithm, there are no internal
683
vertices to contend with, so it is easier to inspect the internal edges
684
incident to each vertex internal to the path (R ... A), i.e. excluding endpoints,
685
to see whether any of the edges connects outside of the path [R ... A],
686
including endpoints.
687
688
We will count on the pre-initialization of the vertex types to TYPE_UNKNOWN
689
so that we don't have to initialize the whole bicomp. Each vertex along
690
the path [R ... A] is marked TYPE_VERTEX_VISITED. Then, for each vertex in the
691
range (R ... A), if there is any edge that is also not incident to a vertex
692
with TYPE_UNKNOWN, then that edge is the desired separator edge between
693
R and W. We mark that edge and save information about it.
694
695
If the separator edge is found, then this method sets the *pW to A, and it
696
sets *pX and *pY values with the endpoints of the separator edge.
697
No visited flags are set at this time because it is easier to set them later.
698
699
Lastly, we put the vertex types along [R ... A] back to TYPE_UNKNOWN.
700
701
Returns TRUE if separator edge found or FALSE otherwise
702
****************************************************************************/
703
704
int _K4_FindSeparatingInternalEdge(graphP theGraph, int R, int prevLink, int A, int *pW, int *pX, int *pY)
705
{
706
int Z, ZPrevLink, J, neighbor;
707
708
// Mark the vertex types along the path [R ... A] as visited
709
_K4_SetTypeOnExternalFacePath(theGraph, R, prevLink, A, TYPE_VERTEX_VISITED);
710
711
// Search each of the vertices in the range (R ... A)
712
*pX = *pY = NIL;
713
ZPrevLink = prevLink;
714
Z = _GetNextVertexOnExternalFace(theGraph, R, &ZPrevLink);
715
while (Z != A)
716
{
717
// Search for a separator among the edges of Z
718
// It is OK to not bother skipping the external face edges, since we
719
// know they are marked with TYPE_VERTEX_VISITED
720
J = gp_GetFirstArc(theGraph, Z);
721
while (gp_IsArc(theGraph, J))
722
{
723
neighbor = theGraph->G[J].v;
724
if (theGraph->G[neighbor].type == TYPE_UNKNOWN)
725
{
726
*pW = A;
727
*pX = Z;
728
*pY = neighbor;
729
break;
730
}
731
J = gp_GetNextArc(theGraph, J);
732
}
733
734
// If we found the separator edge, then we don't need to go on
735
if (*pX != NIL)
736
break;
737
738
// Go to the next vertex
739
Z = _GetNextVertexOnExternalFace(theGraph, Z, &ZPrevLink);
740
}
741
742
// Restore the vertex types along the path [R ... A] to the unknown state
743
_K4_SetTypeOnExternalFacePath(theGraph, R, prevLink, A, TYPE_UNKNOWN);
744
745
return *pX == NIL ? FALSE : TRUE;
746
}
747
748
/****************************************************************************
749
_K4_SetTypeOnExternalFacePath()
750
751
Assumes A is a vertex along the external face of the bicomp rooted by R.
752
Places 'type' into the type member of vertices along the path (R ... A)
753
that begins with R's link[1^prevLink] arc.
754
****************************************************************************/
755
756
void _K4_SetTypeOnExternalFacePath(graphP theGraph, int R, int prevLink, int A, int type)
757
{
758
int Z, ZPrevLink;
759
760
theGraph->G[R].type = type;
761
ZPrevLink = prevLink;
762
Z = R;
763
while (Z != A)
764
{
765
Z = _GetNextVertexOnExternalFace(theGraph, Z, &ZPrevLink);
766
theGraph->G[Z].type = type;
767
}
768
}
769
770
/****************************************************************************
771
_K4_IsolateMinorA1()
772
773
This pattern is essentially outerplanarity minor A, a K_{2,3}, except we get
774
a K_4 via the additional path from some vertex Z to the current vertex.
775
This path may be via some descendant of Z, and it may be a future pertinent
776
connection to an ancestor of the current vertex.
777
****************************************************************************/
778
779
int _K4_IsolateMinorA1(graphP theGraph)
780
{
781
isolatorContextP IC = &theGraph->IC;
782
783
if (IC->uz < IC->v)
784
{
785
if (theGraph->functions.fpMarkDFSPath(theGraph, IC->uz, IC->v) != OK)
786
return NOTOK;
787
}
788
789
if (theGraph->functions.fpMarkDFSPath(theGraph, IC->z, IC->dz) != OK)
790
return NOTOK;
791
792
if (_IsolateOuterplanarityObstructionA(theGraph) != OK)
793
return NOTOK;
794
795
if (_AddAndMarkEdge(theGraph, IC->uz, IC->dz) != OK)
796
return NOTOK;
797
798
return OK;
799
}
800
801
/****************************************************************************
802
_K4_IsolateMinorA2()
803
804
This pattern is essentially outerplanarity minor A, a K_{2,3}, except we get
805
a K_4 via an additional X-Y path within the main bicomp, which is guaranteed
806
to exist by the time this method is invoked.
807
One might think to simply invoke _MarkHighestXYPath() to obtain the path,
808
but the IC->px and IC->py values are already set before invoking this method,
809
and the bicomp is outerplanar, so the XY path is just an edge. Also, one
810
subcase of pattern B2 reduces to this pattern, except that the XY path is
811
determined by the B2 isolator.
812
****************************************************************************/
813
814
int _K4_IsolateMinorA2(graphP theGraph)
815
{
816
isolatorContextP IC = &theGraph->IC;
817
818
// We assume the X-Y path was already marked
819
if (!theGraph->G[IC->px].visited || !theGraph->G[IC->py].visited)
820
return NOTOK;
821
822
return _IsolateOuterplanarityObstructionA(theGraph);
823
}
824
825
/****************************************************************************
826
_K4_IsolateMinorB1()
827
828
It is possible to get a K_4 based on the pertinence of w, but we don't do it
829
that way. If we use the pertinence of w, then we have to eliminate part of
830
the bicomp external face, which has special cases if a_x==w or a_y==w.
831
Typically we would mark (r ... a_x ... w ... a_y), which works even when a_y==w,
832
but if instead a_x==w, then we'd have to mark (w ... a_y ... r).
833
834
Since a_x and a_y are guaranteed to be distinct, it is easier to just ignore
835
the pertinence of w, and instead use the lower bicomp external face path
836
as the connection between a_x and a_y. This includes w, but then the
837
isolation process is the same even if a_x==w or a_y==w. The other two
838
connections for a_x and a_y are to v and MAX(ux, uy).
839
****************************************************************************/
840
841
int _K4_IsolateMinorB1(graphP theGraph)
842
{
843
isolatorContextP IC = &theGraph->IC;
844
845
if (theGraph->functions.fpMarkDFSPath(theGraph, IC->x, IC->dx) != OK)
846
return NOTOK;
847
848
if (theGraph->functions.fpMarkDFSPath(theGraph, IC->y, IC->dy) != OK)
849
return NOTOK;
850
851
// The path from the bicomp root to MIN(ux,uy) is marked to ensure the
852
// connection from the image vertices v and MAX(ux,uy) as well as the
853
// connection from MAX(ux,uy) through MIN(ux,uy) to (ux==MIN(ux,uy)?x:y)
854
if (theGraph->functions.fpMarkDFSPath(theGraph, MIN(IC->ux, IC->uy), IC->r) != OK)
855
return NOTOK;
856
857
// This makes the following connections (a_x ... v), (a_y ... v), and
858
// (a_x ... w ... a_y), the last being tolerant of a_x==w or a_y==w
859
if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK)
860
return NOTOK;
861
862
if (_JoinBicomps(theGraph) != OK)
863
return NOTOK;
864
865
if (_AddAndMarkEdge(theGraph, IC->ux, IC->dx) != OK)
866
return NOTOK;
867
868
if (_AddAndMarkEdge(theGraph, IC->uy, IC->dy) != OK)
869
return NOTOK;
870
871
return OK;
872
}
873
874
/****************************************************************************
875
_K4_IsolateMinorB2()
876
877
The first subcase of B2 can be reduced to outerplanarity obstruction E
878
The second subcase of B2 can be reduced to A2 by changing v to u
879
****************************************************************************/
880
881
int _K4_IsolateMinorB2(graphP theGraph)
882
{
883
isolatorContextP IC = &theGraph->IC;
884
885
// First subcase, the active vertex is pertinent
886
if (PERTINENT(theGraph, IC->w))
887
{
888
// We assume the X-Y path was already marked
889
if (!theGraph->G[IC->px].visited || !theGraph->G[IC->py].visited)
890
return NOTOK;
891
892
return _IsolateOuterplanarityObstructionE(theGraph);
893
}
894
895
// Second subcase, the active vertex is future pertinent
896
else if (FUTUREPERTINENT(theGraph, IC->w, IC->v))
897
{
898
IC->v = IC->uz;
899
IC->dw = IC->dz;
900
901
return _K4_IsolateMinorA2(theGraph);
902
}
903
904
return OK;
905
}
906
907
/****************************************************************************
908
_K4_ReduceBicompToEdge()
909
910
This method is used when reducing the main bicomp of obstruction A to a
911
single edge (R, W). We first delete all edges from the bicomp except
912
those on the DFS tree path W to R, then we reduce that DFS tree path to
913
a DFS tree edge.
914
915
After the reduction, the outerplanarity Walkdown traversal can continue
916
R to W without being blocked as was the case when R was adjacent to X and Y.
917
918
Returns OK for success, NOTOK for internal (implementation) error.
919
****************************************************************************/
920
921
int _K4_ReduceBicompToEdge(graphP theGraph, K4SearchContext *context, int R, int W)
922
{
923
int newEdge;
924
925
if (_OrientVerticesInBicomp(theGraph, R, 0) != OK ||
926
_FillVisitedFlagsInBicomp(theGraph, R, 0) != OK)
927
return NOTOK;
928
if (theGraph->functions.fpMarkDFSPath(theGraph, R, W) != OK)
929
return NOTOK;
930
if (_DeleteUnmarkedEdgesInBicomp(theGraph, R) != OK)
931
return NOTOK;
932
933
// Now we have to reduce the path W -> R to the DFS tree edge (R, W)
934
newEdge =_K4_ReducePathToEdge(theGraph, context, EDGE_DFSPARENT,
935
R, gp_GetFirstArc(theGraph, R), W, gp_GetFirstArc(theGraph, W));
936
if (!gp_IsArc(theGraph, newEdge))
937
return NOTOK;
938
939
// Finally, put the visited state of R and W to unvisted so that
940
// the core embedder (esp. Walkup) will not have any problems.
941
theGraph->G[R].visited = theGraph->G[W].visited = theGraph->N;
942
943
return OK;
944
}
945
946
/****************************************************************************
947
_K4_ReducePathComponent()
948
949
This method is invoked when the bicomp rooted by R contains a component
950
subgraph that is separable from the bicomp by the 2-cut (R, A). The K_4
951
homeomorph isolator will have processed a significant fraction of the
952
component, and so it must be reduced to an edge to ensure that said
953
processing happens at most once on the component (except for future
954
operations that are bound to linear time in total by other arguments).
955
956
Because the bicomp is an outerplanar embedding, the component is known to
957
consists of an external face path plus some internal edges that are parallel
958
to that path. Otherwise, it wouldn't be separable by the 2-cut (R, A).
959
960
The goal of this method is to reduce the component to the edge (R, A). This
961
is done in such a way that, if the reduction must be restored, the DFS tree
962
structure connecting the restored vertices is retained.
963
964
The first step is to ensure that (R, A) is not already just an edge, in which
965
case no reduction is needed. This can occur if A is future pertinent.
966
967
Assuming a non-trivial reduction component, the next step is to determine
968
the DFS tree structure within the component. Because it is separable by the
969
2-cut (R, A), there are only two cases:
970
971
Case 1: The DFS tree path from A to R is within the reduction component.
972
973
In this case, the DFS tree path is marked, the remaining edges of the
974
reduction component are eliminated, and then the DFS tree path is reduced to
975
the the tree edge (R, A).
976
977
Note that the reduction component may also contain descendants of A as well
978
as vertices that are descendant to R but are neither ancestors nor
979
descendants of A. This depends on where the tree edge from R meets the
980
external face path (R ... A). However, the reduction component can only
981
contribute one path to any future K_4, so it suffices to preserve only the
982
DFS tree path (A --> R).
983
984
Case 2: The DFS tree path from A to R is not within the reduction component.
985
986
In this case, the external face edge from R leads to a descendant D of A.
987
We mark that back edge (R, D) plus the DFS tree path (D --> A). The
988
remaining edges of the reduction component can be removed, and then the
989
path (R, D, ..., A) is reduced to the edge (R, A).
990
991
For the sake of contradiction, suppose that only part of the DFS tree path
992
from A to R were contained by the reduction component. Then, a DFS tree edge
993
would have to exit the reduction component and connect to some vertex not
994
on the external face path (R, ..., A). This contradicts the assumption that
995
the reduction subgraph is separable from the bicomp by the 2-cut (R, A).
996
997
Returns OK for success, NOTOK for internal (implementation) error.
998
****************************************************************************/
999
1000
int _K4_ReducePathComponent(graphP theGraph, K4SearchContext *context, int R, int prevLink, int A)
1001
{
1002
int e_R, e_A, Z, ZPrevLink, edgeType, invertedFlag=0;
1003
1004
// Check whether the external face path (R, ..., A) is just an edge
1005
e_R = gp_GetArc(theGraph, R, 1^prevLink);
1006
if (theGraph->G[e_R].v == A)
1007
return OK;
1008
1009
// Check for Case 1: The DFS tree path from A to R is within the reduction component
1010
if (_K4_TestPathComponentForAncestor(theGraph, R, prevLink, A))
1011
{
1012
_K4_SetVisitedInPathComponent(theGraph, R, prevLink, A, 0);
1013
if (theGraph->functions.fpMarkDFSPath(theGraph, R, A) != OK)
1014
return NOTOK;
1015
edgeType = EDGE_DFSPARENT;
1016
1017
invertedFlag = _K4_GetCumulativeOrientationOnDFSPath(theGraph, R, A);
1018
}
1019
1020
// Otherwise Case 2: The DFS tree path from A to R is not within the reduction component
1021
else
1022
{
1023
_K4_SetVisitedInPathComponent(theGraph, R, prevLink, A, 0);
1024
Z = theGraph->G[e_R].v;
1025
theGraph->G[e_R].visited = 1;
1026
theGraph->G[gp_GetTwinArc(theGraph, e_R)].visited = 1;
1027
if (theGraph->functions.fpMarkDFSPath(theGraph, A, Z) != OK)
1028
return NOTOK;
1029
edgeType = EDGE_BACK;
1030
}
1031
1032
// The path to be kept/reduced is marked, so the other edges can go
1033
if (_K4_DeleteUnmarkedEdgesInPathComponent(theGraph, R, prevLink, A) != OK)
1034
return NOTOK;
1035
1036
// Clear all the visited flags for safety, except the vertices R and A
1037
// will remain in the embedding, and the core embedder (Walkup) uses a
1038
// value greater than the current vertex to indicate an unvisited vertex
1039
_K4_SetVisitedInPathComponent(theGraph, R, prevLink, A, 0);
1040
theGraph->G[R].visited = theGraph->N;
1041
theGraph->G[A].visited = theGraph->N;
1042
1043
// Find the component's remaining edges e_A and e_R incident to A and R
1044
ZPrevLink = prevLink;
1045
Z = R;
1046
while (Z != A)
1047
{
1048
Z = _GetNextVertexOnExternalFace(theGraph, Z, &ZPrevLink);
1049
}
1050
e_A = gp_GetArc(theGraph, A, ZPrevLink);
1051
e_R = gp_GetArc(theGraph, R, 1^prevLink);
1052
1053
// Reduce the path (R ... A) to an edge
1054
e_R = _K4_ReducePathToEdge(theGraph, context, edgeType, R, e_R, A, e_A);
1055
if (!gp_IsArc(theGraph, e_R))
1056
return NOTOK;
1057
1058
// Preserve the net orientation along the DFS path in the case of a tree edge
1059
if (theGraph->G[e_R].type == EDGE_DFSCHILD)
1060
{
1061
if (invertedFlag)
1062
SET_EDGEFLAG_INVERTED(theGraph, e_R);
1063
}
1064
1065
return OK;
1066
}
1067
1068
/****************************************************************************
1069
_K4_GetCumulativeOrientationOnDFSPath()
1070
****************************************************************************/
1071
int _K4_GetCumulativeOrientationOnDFSPath(graphP theGraph, int ancestor, int descendant)
1072
{
1073
int J, parent;
1074
int N = theGraph->N, invertedFlag=0;
1075
1076
/* If we are marking from a root vertex upward, then go up to the parent
1077
copy before starting the loop */
1078
1079
if (descendant >= N)
1080
descendant = theGraph->V[descendant-N].DFSParent;
1081
1082
while (descendant != ancestor)
1083
{
1084
if (descendant == NIL)
1085
return NOTOK;
1086
1087
// If we are at a bicomp root, then ascend to its parent copy
1088
if (descendant >= N)
1089
{
1090
parent = theGraph->V[descendant-N].DFSParent;
1091
}
1092
1093
// If we are on a regular, non-virtual vertex then get the edge to the parent
1094
else
1095
{
1096
// Scan the edges for the one marked as the DFS parent
1097
parent = NIL;
1098
J = gp_GetFirstArc(theGraph, descendant);
1099
while (gp_IsArc(theGraph, J))
1100
{
1101
if (theGraph->G[J].type == EDGE_DFSPARENT)
1102
{
1103
parent = theGraph->G[J].v;
1104
break;
1105
}
1106
J = gp_GetNextArc(theGraph, J);
1107
}
1108
1109
// If the parent edge was not found, then the data structure is corrupt
1110
if (parent == NIL)
1111
return NOTOK;
1112
1113
// Add the inversion flag on the child arc to the cumulative result
1114
J = gp_GetTwinArc(theGraph, J);
1115
if (theGraph->G[J].type != EDGE_DFSCHILD || theGraph->G[J].v != descendant)
1116
return NOTOK;
1117
invertedFlag ^= GET_EDGEFLAG_INVERTED(theGraph, J);
1118
}
1119
1120
// Hop to the parent and reiterate
1121
descendant = parent;
1122
}
1123
1124
return invertedFlag;
1125
}
1126
1127
/****************************************************************************
1128
_K4_TestPathComponentForAncestor()
1129
Tests the external face path between R and A for a DFS ancestor of A.
1130
Returns TRUE if found, FALSE otherwise.
1131
****************************************************************************/
1132
1133
int _K4_TestPathComponentForAncestor(graphP theGraph, int R, int prevLink, int A)
1134
{
1135
int Z, ZPrevLink;
1136
1137
ZPrevLink = prevLink;
1138
Z = R;
1139
while (Z != A)
1140
{
1141
Z = _GetNextVertexOnExternalFace(theGraph, Z, &ZPrevLink);
1142
if (Z < A)
1143
return TRUE;
1144
}
1145
return FALSE;
1146
}
1147
1148
/****************************************************************************
1149
_K4_SetVisitedInPathComponent()
1150
1151
There is a subcomponent of the bicomp rooted by R that is separable by the
1152
2-cut (R, A) and contains the edge e_R = theGraph->G[R].link[1^prevLink].
1153
1154
All vertices in this component are along the external face, so we
1155
traverse along the external face vertices strictly between R and A and
1156
mark all the edges and their incident vertices with the 'visitedValue'.
1157
1158
Note that the vertices along the path (R ... A) only have edges incident
1159
to each other and to R and A because the component is separable by the
1160
(R, A)-cut.
1161
****************************************************************************/
1162
1163
void _K4_SetVisitedInPathComponent(graphP theGraph, int R, int prevLink, int A, int visitedValue)
1164
{
1165
int Z, ZPrevLink, e;
1166
1167
ZPrevLink = prevLink;
1168
Z = _GetNextVertexOnExternalFace(theGraph, R, &ZPrevLink);
1169
while (Z != A)
1170
{
1171
theGraph->G[Z].visited = visitedValue;
1172
e = gp_GetFirstArc(theGraph, Z);
1173
while (gp_IsArc(theGraph, e))
1174
{
1175
theGraph->G[e].visited = visitedValue;
1176
theGraph->G[gp_GetTwinArc(theGraph, e)].visited = visitedValue;
1177
theGraph->G[theGraph->G[e].v].visited = visitedValue;
1178
1179
e = gp_GetNextArc(theGraph, e);
1180
}
1181
1182
Z = _GetNextVertexOnExternalFace(theGraph, Z, &ZPrevLink);
1183
}
1184
}
1185
1186
/****************************************************************************
1187
_K4_DeleteUnmarkedEdgesInPathComponent()
1188
1189
There is a subcomponent of the bicomp rooted by R that is separable by the
1190
2-cut (R, A) and contains the edge e_R = theGraph->G[R].link[1^prevLink].
1191
The edges in the component have been marked unvisited except for a path we
1192
intend to preserve. This routine deletes the unvisited edges.
1193
1194
NOTE: This reduction has invalidated the short-circuit extFace data structure,
1195
but it will be repaired for use by WalkUp and WalkDown when the path
1196
component reduction is completed.
1197
1198
Returns OK on success, NOTOK on internal error
1199
****************************************************************************/
1200
1201
int _K4_DeleteUnmarkedEdgesInPathComponent(graphP theGraph, int R, int prevLink, int A)
1202
{
1203
int Z, ZPrevLink, e;
1204
1205
// We need to use the stack to store up the edges we're going to delete.
1206
// We want to make sure there is enough stack capacity to handle it,
1207
// which is of course true because the stack is supposed to be empty.
1208
// We're doing a reduction on a bicomp on which the WalkDown has completed,
1209
// so the stack contains no bicomp roots to merge.
1210
if (sp_NonEmpty(theGraph->theStack))
1211
return NOTOK;
1212
1213
// Traverse all vertices internal to the path (R ... A) and push
1214
// all non-visited edges
1215
ZPrevLink = prevLink;
1216
Z = _GetNextVertexOnExternalFace(theGraph, R, &ZPrevLink);
1217
while (Z != A)
1218
{
1219
e = gp_GetFirstArc(theGraph, Z);
1220
while (gp_IsArc(theGraph, e))
1221
{
1222
// The comparison of e to its twin is a useful way of ensuring we
1223
// don't push the edge twice, which is of course only applicable
1224
// when processing an edge whose endpoints are both internal to
1225
// the path (R ... A)
1226
if (!theGraph->G[e].visited &&
1227
(e < gp_GetTwinArc(theGraph, e) ||
1228
theGraph->G[e].v == R || theGraph->G[e].v == A))
1229
{
1230
sp_Push(theGraph->theStack, e);
1231
}
1232
1233
e = gp_GetNextArc(theGraph, e);
1234
}
1235
1236
Z = _GetNextVertexOnExternalFace(theGraph, Z, &ZPrevLink);
1237
}
1238
1239
// Delete all the non-visited edges
1240
while (sp_NonEmpty(theGraph->theStack))
1241
{
1242
sp_Pop(theGraph->theStack, e);
1243
gp_DeleteEdge(theGraph, e, 0);
1244
}
1245
1246
return OK;
1247
}
1248
1249
/****************************************************************************
1250
_K4_ReducePathToEdge()
1251
1252
Returns an arc of the edge created on success, a non-arc (NOTOK) on failure
1253
On success, the arc is in the adjacency list of R. The result can be tested
1254
for success or failure using gp_IsArc()
1255
****************************************************************************/
1256
1257
int _K4_ReducePathToEdge(graphP theGraph, K4SearchContext *context, int edgeType, int R, int e_R, int A, int e_A)
1258
{
1259
// Find out the links used in vertex R for edge e_R and in vertex A for edge e_A
1260
int Rlink = theGraph->G[R].link[0] == e_R ? 0 : 1;
1261
int Alink = theGraph->G[A].link[0] == e_A ? 0 : 1;
1262
1263
// If the path is more than a single edge, then it must be reduced to an edge.
1264
// Note that even if the path is a single edge, the external face data structure
1265
// must still be modified since many edges connecting the external face have
1266
// been deleted
1267
if (theGraph->G[e_R].v != A)
1268
{
1269
int v_R, v_A;
1270
1271
// Prepare for removing each of the two edges that join the path to the bicomp by
1272
// restoring it if it is a reduction edge (a constant time operation)
1273
if (context->G[e_R].pathConnector != NIL)
1274
{
1275
if (_K4_RestoreReducedPath(theGraph, context, e_R) != OK)
1276
return NOTOK;
1277
1278
e_R = gp_GetArc(theGraph, R, Rlink);
1279
}
1280
1281
if (context->G[e_A].pathConnector != NIL)
1282
{
1283
if (_K4_RestoreReducedPath(theGraph, context, e_A) != OK)
1284
return NOTOK;
1285
e_A = gp_GetArc(theGraph, A, Alink);
1286
}
1287
1288
// Save the vertex neighbors of R and A indicated by e_R and e_A for
1289
// later use in setting up the path connectors.
1290
v_R = theGraph->G[e_R].v;
1291
v_A = theGraph->G[e_A].v;
1292
1293
// Now delete the two edges that join the path to the bicomp.
1294
gp_DeleteEdge(theGraph, e_R, 0);
1295
gp_DeleteEdge(theGraph, e_A, 0);
1296
1297
// Now add a single edge to represent the path
1298
// We use 1^Rlink, for example, because Rlink was the link from R that indicated e_R,
1299
// so 1^Rlink is the link that indicated e_R in the other arc that was adjacent to e_R.
1300
// We want gp_InsertEdge to place the new arc where e_R was in R's adjacency list
1301
gp_InsertEdge(theGraph, R, gp_GetArc(theGraph, R, Rlink), 1^Rlink,
1302
A, gp_GetArc(theGraph, A, Alink), 1^Alink);
1303
1304
// Now set up the path connectors so the original path can be recovered if needed.
1305
e_R = gp_GetArc(theGraph, R, Rlink);
1306
context->G[e_R].pathConnector = v_R;
1307
1308
e_A = gp_GetArc(theGraph, A, Alink);
1309
context->G[e_A].pathConnector = v_A;
1310
1311
// Also, set the reduction edge's type to preserve the DFS tree structure
1312
theGraph->G[e_R].type = _ComputeArcType(theGraph, R, A, edgeType);
1313
theGraph->G[e_A].type = _ComputeArcType(theGraph, A, R, edgeType);
1314
}
1315
1316
// Set the external face data structure
1317
theGraph->extFace[R].vertex[Rlink] = A;
1318
theGraph->extFace[A].vertex[Alink] = R;
1319
1320
// If the edge represents an entire bicomp, then more external face
1321
// settings are needed.
1322
if (gp_GetFirstArc(theGraph, R) == gp_GetLastArc(theGraph, R))
1323
{
1324
theGraph->extFace[R].vertex[1^Rlink] = A;
1325
theGraph->extFace[A].vertex[1^Alink] = R;
1326
theGraph->extFace[A].inversionFlag = 0;
1327
}
1328
1329
return e_R;
1330
}
1331
1332
/****************************************************************************
1333
_K4_RestoreReducedPath()
1334
1335
Given an edge record of an edge used to reduce a path, we want to restore
1336
the path in constant time.
1337
The path may contain more reduction edges internally, but we do not
1338
search for and process those since it would violate the constant time
1339
bound required of this function.
1340
1341
Note that we don't bother amending the external face data structure because
1342
a reduced path is only restored when it will shortly be reduced again or
1343
when we don't really need the external face data structure anymore.
1344
1345
Return OK on success, NOTOK on failure
1346
****************************************************************************/
1347
1348
int _K4_RestoreReducedPath(graphP theGraph, K4SearchContext *context, int J)
1349
{
1350
int JTwin, u, v, w, x;
1351
int J0, J1, JTwin0, JTwin1;
1352
1353
if (context->G[J].pathConnector == NIL)
1354
return OK;
1355
1356
JTwin = gp_GetTwinArc(theGraph, J);
1357
1358
u = theGraph->G[JTwin].v;
1359
v = context->G[J].pathConnector;
1360
w = context->G[JTwin].pathConnector;
1361
x = theGraph->G[J].v;
1362
1363
// Get the locations of the graph nodes between which the new graph nodes
1364
// must be added in order to reconnect the path parallel to the edge.
1365
J0 = gp_GetNextArc(theGraph, J);
1366
J1 = gp_GetPrevArc(theGraph, J);
1367
JTwin0 = gp_GetNextArc(theGraph, JTwin);
1368
JTwin1 = gp_GetPrevArc(theGraph, JTwin);
1369
1370
// We first delete the edge represented by J and JTwin. We do so before
1371
// restoring the path to ensure we do not exceed the maximum arc capacity.
1372
gp_DeleteEdge(theGraph, J, 0);
1373
1374
// Now we add the two edges to reconnect the reduced path represented
1375
// by the edge [J, JTwin]. The edge record in u is added between J0 and J1.
1376
// Likewise, the new edge record in x is added between JTwin0 and JTwin1.
1377
if (gp_IsArc(theGraph, J0))
1378
{
1379
if (gp_InsertEdge(theGraph, u, J0, 1, v, gp_AdjacencyListEndMark(v), 0) != OK)
1380
return NOTOK;
1381
}
1382
else
1383
{
1384
if (gp_InsertEdge(theGraph, u, J1, 0, v, gp_AdjacencyListEndMark(v), 0) != OK)
1385
return NOTOK;
1386
}
1387
1388
if (gp_IsArc(theGraph, JTwin0))
1389
{
1390
if (gp_InsertEdge(theGraph, x, JTwin0, 1, w, gp_AdjacencyListEndMark(w), 0) != OK)
1391
return NOTOK;
1392
}
1393
else
1394
{
1395
if (gp_InsertEdge(theGraph, x, JTwin1, 0, w, gp_AdjacencyListEndMark(w), 0) != OK)
1396
return NOTOK;
1397
}
1398
1399
// Set the types of the newly added edges. In both cases, the first of the two
1400
// vertex parameters is known to be degree 2 because they are internal to the
1401
// path being restored, so this operation is constant time.
1402
if (_SetEdgeType(theGraph, v, u) != OK || _SetEdgeType(theGraph, w, x) != OK)
1403
return NOTOK;
1404
1405
return OK;
1406
}
1407
1408
/****************************************************************************
1409
_K4_RestoreAndOrientReducedPaths()
1410
1411
This function searches the embedding for any edges that are specially marked
1412
as being representative of a path that was previously reduced to a single edge
1413
by _ReducePathToEdge(). This method restores the path by replacing the edge
1414
with the path.
1415
1416
Note that the new path may contain more reduction edges, and these will be
1417
iteratively expanded by the outer for loop. Equally, a reduced path may
1418
be restored into a path that itself is a reduction path that will only be
1419
attached to the embedding by some future step of the outer loop.
1420
1421
The vertices along the path being restored must be given a consistent
1422
orientation with the endpoints. It is expected that the embedding
1423
will have been oriented prior to this operation.
1424
****************************************************************************/
1425
1426
int _K4_RestoreAndOrientReducedPaths(graphP theGraph, K4SearchContext *context)
1427
{
1428
int e, J, JTwin, u, v, w, x, visited;
1429
1430
for (e = 0; e < theGraph->M + sp_GetCurrentSize(theGraph->edgeHoles);)
1431
{
1432
J = theGraph->edgeOffset + 2*e;
1433
if (context->G[J].pathConnector != NIL)
1434
{
1435
visited = theGraph->G[J].visited;
1436
1437
JTwin = gp_GetTwinArc(theGraph, J);
1438
u = theGraph->G[JTwin].v;
1439
v = context->G[J].pathConnector;
1440
w = context->G[JTwin].pathConnector;
1441
x = theGraph->G[J].v;
1442
1443
if (_K4_RestoreReducedPath(theGraph, context, J) != OK)
1444
return NOTOK;
1445
1446
// If the path is on the external face, orient it
1447
if (theGraph->G[gp_GetFirstArc(theGraph, u)].v == v ||
1448
theGraph->G[gp_GetLastArc(theGraph, u)].v == v)
1449
{
1450
// Reality check: ensure the path is connected to the
1451
// external face at both vertices.
1452
if (theGraph->G[gp_GetFirstArc(theGraph, x)].v != w &&
1453
theGraph->G[gp_GetLastArc(theGraph, x)].v != w)
1454
return NOTOK;
1455
1456
if (_OrientExternalFacePath(theGraph, u, v, w, x) != OK)
1457
return NOTOK;
1458
}
1459
1460
if (_SetVisitedOnPath(theGraph, u, v, w, x, visited) !=OK)
1461
return NOTOK;
1462
}
1463
else e++;
1464
}
1465
1466
return OK;
1467
}
1468
1469