Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/graphs/planarity/graphK33Search.c
4084 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 "graphK33Search.h"
46
#include "graphK33Search.private.h"
47
48
extern int K33SEARCH_ID;
49
50
#include "graph.h"
51
52
/* Imported functions */
53
54
extern void _FillVisitedFlags(graphP, int);
55
extern int _FillVisitedFlagsInBicomp(graphP theGraph, int BicompRoot, int FillValue);
56
extern int _FillVisitedFlagsInOtherBicomps(graphP theGraph, int BicompRoot, int FillValue);
57
extern void _FillVisitedFlagsInUnembeddedEdges(graphP theGraph, int FillValue);
58
extern int _GetBicompSize(graphP theGraph, int BicompRoot);
59
extern int _HideInternalEdges(graphP theGraph, int vertex);
60
extern int _RestoreInternalEdges(graphP theGraph, int stackBottom);
61
extern int _DeleteUnmarkedEdgesInBicomp(graphP theGraph, int BicompRoot);
62
extern int _ClearInvertedFlagsInBicomp(graphP theGraph, int BicompRoot);
63
extern int _ComputeArcType(graphP theGraph, int a, int b, int edgeType);
64
extern int _SetEdgeType(graphP theGraph, int u, int v);
65
66
extern int _GetNextVertexOnExternalFace(graphP theGraph, int curVertex, int *pPrevLink);
67
extern int _JoinBicomps(graphP theGraph);
68
extern int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns);
69
extern int _OrientVerticesInEmbedding(graphP theGraph);
70
extern void _InvertVertex(graphP theGraph, int V);
71
extern int _SetVisitedOnPath(graphP theGraph, int u, int v, int w, int x, int visited);
72
extern int _OrientExternalFacePath(graphP theGraph, int u, int v, int w, int x);
73
74
extern int _ChooseTypeOfNonplanarityMinor(graphP theGraph, int I, int R);
75
extern int _MarkHighestXYPath(graphP theGraph);
76
77
extern int _IsolateKuratowskiSubgraph(graphP theGraph, int I, int R);
78
79
extern int _FindUnembeddedEdgeToCurVertex(graphP theGraph, int cutVertex, int *pDescendant);
80
extern int _FindUnembeddedEdgeToSubtree(graphP theGraph, int ancestor, int SubtreeRoot, int *pDescendant);
81
82
extern int _MarkPathAlongBicompExtFace(graphP theGraph, int startVert, int endVert);
83
84
extern int _AddAndMarkEdge(graphP theGraph, int ancestor, int descendant);
85
86
extern int _DeleteUnmarkedVerticesAndEdges(graphP theGraph);
87
88
extern int _IsolateMinorE1(graphP theGraph);
89
extern int _IsolateMinorE2(graphP theGraph);
90
extern int _IsolateMinorE3(graphP theGraph);
91
extern int _IsolateMinorE4(graphP theGraph);
92
93
extern int _GetLeastAncestorConnection(graphP theGraph, int cutVertex);
94
extern int _MarkDFSPathsToDescendants(graphP theGraph);
95
extern int _AddAndMarkUnembeddedEdges(graphP theGraph);
96
97
/* Private functions for K_{3,3} searching. */
98
99
int _SearchForK33(graphP theGraph, int I);
100
101
int _SearchForK33InBicomp(graphP theGraph, K33SearchContext *context, int I, int R);
102
103
int _RunExtraK33Tests(graphP theGraph, K33SearchContext *context);
104
int _SearchForMinorE1(graphP theGraph);
105
int _FinishIsolatorContextInitialization(graphP theGraph, K33SearchContext *context);
106
int _SearchForDescendantExternalConnection(graphP theGraph, K33SearchContext *context, int cutVertex, int u_max);
107
int _GetAdjacentAncestorInRange(graphP theGraph, K33SearchContext *context, int vertex,
108
int closerAncestor, int fartherAncestor);
109
int _FindExternalConnectionDescendantEndpoint(graphP theGraph, int ancestor,
110
int cutVertex, int *pDescendant);
111
int _SearchForMergeBlocker(graphP theGraph, K33SearchContext *context, int I, int *pMergeBlocker);
112
int _FindK33WithMergeBlocker(graphP theGraph, K33SearchContext *context, int I, int mergeBlocker);
113
114
int _TestForLowXYPath(graphP theGraph);
115
int _TestForZtoWPath(graphP theGraph);
116
int _TestForStraddlingBridge(graphP theGraph, K33SearchContext *context, int u_max);
117
int _ReduceBicomp(graphP theGraph, K33SearchContext *context, int R);
118
int _ReduceExternalFacePathToEdge(graphP theGraph, K33SearchContext *context, int u, int x, int edgeType);
119
int _ReduceXYPathToEdge(graphP theGraph, K33SearchContext *context, int u, int x, int edgeType);
120
int _RestoreReducedPath(graphP theGraph, K33SearchContext *context, int J);
121
int _RestoreAndOrientReducedPaths(graphP theGraph, K33SearchContext *context);
122
123
int _IsolateMinorE5(graphP theGraph);
124
int _IsolateMinorE6(graphP theGraph, K33SearchContext *context);
125
int _IsolateMinorE7(graphP theGraph, K33SearchContext *context);
126
127
/****************************************************************************
128
_SearchForK33()
129
130
The strategy involves one special case in which, to achieve a linear time
131
bound, we must delay the discovery of a K_{3,3} that caused a Walkdown
132
failure prior to step I. In such cases, vertex I was an ancestor with
133
a connection to the bicomp on which the Walkdown failed, but it would
134
have been too costly to find I at the time. So, the bicomp was marked
135
as non-mergeable prior to some ancestor of I. If this function is
136
invoked for step I, then we have found the connection from that bicomp
137
prior to reaching the limiting ancestor of I. The bicomp and I are
138
therefore part of a K_{3,3} that can now be isolated.
139
140
Otherwise, a Walkdown failure in step I with a non-empty merge stack
141
would have already resulted in an identified K_{3,3} by minor A, so
142
we must have an empty merge stack now.
143
144
We must first find all bicomp roots on which the Walkdown has failed
145
in step I. The fwdArcList of I contains the forward arcs of the
146
back edges for I that we failed to embed. Each forward arc leads to
147
a descendant of I that is in a DFS subtree rooted by a child of I,
148
where the child of I has the greatest DFI that is less than the DFI
149
of the descendant indicated by the forward arc. Each bicomp root
150
that represents a vertex is uniquely associated with a DFS child
151
of the vertex, so once we know the child of I whose subtree contains
152
a descendant of I that the Walkdown couldn't reach, we can immediately
153
deduce the root copy of I on which the Walkdown failed.
154
155
For each such root copy of I, we test whether a K_{3,3} homemorph
156
can be isolated based on that bicomp. If so, then we return it.
157
Otherwise, each bicomp can be reduced to a 4-cycle and the edges
158
that the Walkdown failed to embed can be discarded.
159
****************************************************************************/
160
161
int _SearchForK33(graphP theGraph, int I)
162
{
163
int C1, C2, D, e, RetVal=OK, FoundOne;
164
K33SearchContext *context = NULL;
165
166
gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context);
167
if (context == NULL)
168
return NOTOK;
169
170
/* Before we begin with the standard array of K_{3,3} tests, we handle
171
one optimization case that may be left over from a prior step
172
of the embedding algorithm. If the embedding stack is non-empty,
173
then the Walkdown either halted due to non-planarity minor A or
174
because of the merge blocking optimization (see CASE 3 in the
175
function RunExtraK33Tests()). We test for the latter condition,
176
and if it is found, then we isolate a K_{3,3} and return. */
177
178
if (!sp_IsEmpty(theGraph->theStack))
179
{
180
int mergeBlocker;
181
182
if (_SearchForMergeBlocker(theGraph, context, I, &mergeBlocker) != OK)
183
return NOTOK;
184
185
if (mergeBlocker != NIL)
186
{
187
if (_FindK33WithMergeBlocker(theGraph, context, I, mergeBlocker) != OK)
188
return NOTOK;
189
190
return NONEMBEDDABLE;
191
}
192
}
193
194
/* Each DFS child is listed in DFI order in V[I].sortedDFSChildList.
195
In V[I].fwdArcList, the forward arcs of all unembedded back edges are
196
in order by DFI of the descendant endpoint of the edge.
197
198
DFS descendants have a higher DFI than ancestors, so given two
199
successive children C1 and C2, if any forward arcs lead to a
200
vertex D such that DFI(C1) < DFI(D) < DFI(C2), then the Walkdown
201
failed to embed a back edge from I to a descendant D of C1. */
202
203
e = theGraph->V[I].fwdArcList;
204
D = theGraph->G[e].v;
205
206
C1 = context->V[I].sortedDFSChildList;
207
208
FoundOne = FALSE;
209
210
while (C1 != NIL && e != NIL)
211
{
212
C2 = LCGetNext(context->sortedDFSChildLists,
213
context->V[I].sortedDFSChildList, C1);
214
215
// If the edge e leads from I to a descendant D of C1,
216
// then D will be less than C2 (as explained above),
217
// so we search for a K_{3,3} in the bicomp rooted
218
// by the root copy of I associated with C1.
219
// (If C2 is NIL, then C1 is the last child)
220
221
if (D < C2 || C2 == NIL)
222
{
223
FoundOne = TRUE;
224
RetVal = _SearchForK33InBicomp(theGraph, context, I, C1+theGraph->N);
225
226
// If something went wrong, NOTOK was returned;
227
// If a K_{3,3} was found, NONEMBEDDABLE was returned;
228
// If OK was returned, then only a K5 was found, so
229
// we continue searching any other bicomps on which
230
// the Walkdown failed.
231
232
if (RetVal != OK)
233
break;
234
}
235
236
// Skip the edges that lead to descendants of C1 to get to those
237
// edges that lead to descendants of C2.
238
239
if (C2 != NIL)
240
{
241
while (D < C2 && gp_IsArc(theGraph, e))
242
{
243
e = gp_GetNextArc(theGraph, e);
244
if (e == theGraph->V[I].fwdArcList)
245
e = NIL;
246
else D = theGraph->G[e].v;
247
}
248
}
249
250
// Move the DFS child context to C2
251
C1 = C2;
252
}
253
254
/* If we got through the loop with an OK value for each bicomp on
255
which the Walkdown failed, then we return OK to indicate that only
256
K5's were found (or there is a special case K_{3,3} that may be discovered
257
later based on a setting we made in the data structure).
258
The OK return allows the embedder to continue.
259
260
If a K_{3,3} is ever found (or if an error occured), then RetVal
261
will not be OK, and the loop terminates immediately so we can
262
return the appropriate value. If a K_{3,3} is found, then we must
263
also handle the fact that some paths of the input graph may have
264
been reduced to single edges by prior _ReduceBicomp() calls.
265
266
NOTE: The variable FoundOne helps ensure we detect at least one
267
bicomp on which the Walkdown failed (this should always be
268
the case in an error-free implementation like this one!). */
269
270
return FoundOne ? RetVal : NOTOK;
271
}
272
273
/****************************************************************************
274
_SearchForK33InBicomp()
275
****************************************************************************/
276
277
int _SearchForK33InBicomp(graphP theGraph, K33SearchContext *context, int I, int R)
278
{
279
isolatorContextP IC = &theGraph->IC;
280
int tempResult;
281
282
/* Begin by determining which non-planarity minor is detected */
283
284
if (_ChooseTypeOfNonplanarityMinor(theGraph, I, R) != OK)
285
return NOTOK;
286
287
/* If minor A is selected, then the root of the oriented bicomp has been changed */
288
289
else R = IC->r;
290
291
/* Minors A to D result in the desired K_{3,3} homeomorph,
292
so we isolate it and return NONEMBEDDABLE. */
293
294
if (theGraph->IC.minorType & (MINORTYPE_A|MINORTYPE_B|MINORTYPE_C|MINORTYPE_D))
295
{
296
/* First we restore the orientations of the vertices in the
297
one bicomp we have messed with so that there is no confusion. */
298
299
if (_OrientVerticesInBicomp(theGraph, R, 1) != OK)
300
return NOTOK;
301
302
/* Next we restore the orientation of the embedding so we
303
can restore the reduced paths (because we avoid modifying
304
the Kuratowski subgraph isolator to restore reduced paths,
305
which are a construct of the K_{3,3} search). */
306
307
if (_OrientVerticesInEmbedding(theGraph) != OK ||
308
_RestoreAndOrientReducedPaths(theGraph, context) != OK)
309
return NOTOK;
310
311
/* Next we simply call the Kuratowski subgraph isolation since
312
we know now that it will isolate a K_{3,3}.
313
For minor A, we need to set up the stack that would be
314
available immediately after a Walkdown failure. */
315
316
if (theGraph->IC.minorType & MINORTYPE_A)
317
{
318
sp_ClearStack(theGraph->theStack);
319
sp_Push2(theGraph->theStack, R, NIL);
320
}
321
322
if (_IsolateKuratowskiSubgraph(theGraph, I, R) != OK)
323
return NOTOK;
324
325
return NONEMBEDDABLE;
326
}
327
328
/* For minor E (a K5 minor), we run the additional tests to see if
329
minors E1 to E4 apply since these minors isolate a K_{3,3} entangled
330
with the K5. */
331
332
IC->ux = _GetLeastAncestorConnection(theGraph, IC->x);
333
IC->uy = _GetLeastAncestorConnection(theGraph, IC->y);
334
IC->uz = _GetLeastAncestorConnection(theGraph, IC->z);
335
336
if (IC->z != IC->w ||
337
IC->uz > MAX(IC->ux, IC->uy) ||
338
(IC->uz < MAX(IC->ux, IC->uy) && IC->ux != IC->uy) ||
339
(IC->x != IC->px || IC->y != IC->py))
340
{
341
if (_OrientVerticesInBicomp(theGraph, R, 1) != OK)
342
return NOTOK;
343
344
if (_OrientVerticesInEmbedding(theGraph) != OK ||
345
_RestoreAndOrientReducedPaths(theGraph, context) != OK)
346
return NOTOK;
347
348
if (_IsolateKuratowskiSubgraph(theGraph, I, R) != OK)
349
return NOTOK;
350
351
return NONEMBEDDABLE;
352
}
353
354
/* If the Kuratowski subgraph isolator will not isolate a K_{3,3} based on minor E,
355
then a K5 homeomorph could be isolated. However, a K_{3,3} may still be tangled
356
with the K5, so we now run the additional tests of the K_{3,3} search algorithm.
357
358
If the search finds a K_{3,3} (tempResult of NONEMBEDDABLE), then we remove unwanted
359
edges from the graph and return NONEMBEDDABLE. If the search has a fault (NOTOK),
360
then we return. If the result is OK, then a K_{3,3} was not found at this time
361
and we proceed with some clean-up work below. */
362
363
if ((tempResult = _RunExtraK33Tests(theGraph, context)) != OK)
364
{
365
if (tempResult == NONEMBEDDABLE)
366
if (_DeleteUnmarkedVerticesAndEdges(theGraph) != OK)
367
return NOTOK;
368
369
return tempResult;
370
}
371
372
/* The extra cases for finding a K_{3,3} did not succeed, so the bicomp rooted by R
373
is either a K5 homeomorph (with at most a superficially entangled K_{3,3}) or
374
we have made the special setting that allows us to detect the one case that
375
would be too costly to try now. Either way, we can safely reduce the bicomp
376
to the 4-cycle (R, X, W, Y, R) and proceed with the planarity algorithm.
377
We also restore the mixed orientation of the bicomp (i.e. the proper
378
orientation in the context of the edge signs) because this code can work
379
when ReduceBicomp doesn't do any actual work. */
380
381
if (_OrientVerticesInBicomp(theGraph, R, 1) != OK)
382
return NOTOK;
383
384
if (_ReduceBicomp(theGraph, context, R) != OK)
385
return NOTOK;
386
387
/* Set visited flags to a high number so planarity algorithm
388
can properly do Walkup procedure in future steps */
389
390
if (_FillVisitedFlagsInBicomp(theGraph, IC->r, theGraph->N) != OK)
391
return NOTOK;
392
393
/* We now intend to ignore the pertinence of W (conceptually eliminating
394
the connection from W to the current vertex). Note that none of the
395
bicomps listed in the pertinentBicompList (nor their respective subtrees)
396
will be visited again by the planarity algorithm because they must've
397
been internally active. If they were externally active and pertinent,
398
then we would've found a K_{3,3} by non-planarity minor B. Thus, the original
399
Walkup costs that identified the pertinent bicomps we intend to ignore are
400
one-time costs, preserving linear time. */
401
402
theGraph->V[IC->w].adjacentTo = NIL;
403
theGraph->V[IC->w].pertinentBicompList = NIL;
404
405
return OK;
406
}
407
408
/****************************************************************************
409
_RunExtraK33Tests()
410
****************************************************************************/
411
412
int _RunExtraK33Tests(graphP theGraph, K33SearchContext *context)
413
{
414
isolatorContextP IC = &theGraph->IC;
415
int u_max = MAX3(IC->ux, IC->uy, IC->uz), u;
416
417
/* Case 1: If there is a pertinent or externally active vertex other than W
418
on the lower external face path between X and Y (the points of
419
attachment of the x-y path), then we can isolate a K_{3,3} homeomorph
420
by Minor E1. */
421
422
if (_SearchForMinorE1(theGraph) != OK)
423
return NOTOK;
424
425
if (IC->w != IC->z)
426
{
427
if (_FinishIsolatorContextInitialization(theGraph, context) != OK ||
428
_IsolateMinorE1(theGraph) != OK)
429
return NOTOK;
430
431
return NONEMBEDDABLE;
432
}
433
434
/* Case 2: If W/Z can make an external connection to an ancestor of V
435
that is descendant to u_{max}, then a K_{3,3} homeomorph can
436
be isolated with Minor E2.
437
438
NOTE: It may seem costly to check the entire subtree, but
439
if it succeeds then we're done, and if the only connection
440
is to u_{max} then we are sure to never make this check
441
again on this subtree (if all the other K_{3,3} tests fail).
442
443
OPTIMIZATION: We do not check for the connection if the
444
least ancestor connection from W/Z leads to an ancestor
445
of u_max. The reason is that it could ultimately be too
446
costly if the connection doesn't exist, and if the highest
447
numbered ancestor H of the current vertex with an external
448
connection from W is a descendant u_{max} (and if no other
449
tests in this function succeed), then we will discover a
450
K_{3,3} by Minor A or B in step H.
451
452
OPTIMIZATION: When we do test for an external connection to
453
an ancestor of V descendant to u_{max}, the result may be that
454
only a connection to u_{max} exists. The result is cached
455
so that the subtrees of the vertex need not be traversed again
456
should we need to make the test again.
457
See _SearchForDescendantExternalConnection() */
458
459
if (IC->uz == u_max)
460
{
461
u = _SearchForDescendantExternalConnection(theGraph, context, IC->w, u_max);
462
if (u > u_max)
463
{
464
IC->uz = u;
465
if (_FinishIsolatorContextInitialization(theGraph, context) != OK ||
466
_IsolateMinorE2(theGraph) != OK)
467
return NOTOK;
468
469
return NONEMBEDDABLE;
470
}
471
}
472
473
/* Case 3: If X or Y can make an external connection to an ancestor of V
474
that is descendant to u_{max}, then a K_{3,3} homeomorph
475
can be isolated with Minor E3.
476
477
NOTE: It may seem costly to check the entire subtree, but
478
if it succeeds then we're done, and if the only connection
479
is to u_{max} then we are sure to never make this check
480
again on this subtree (if all the other K_{3,3} tests fail).
481
482
OPTIMIZATION: Due to the prior use of the Kuratowski subgraph
483
isolator, we know that at most one of X, Y or W/Z could have an
484
external connection to an ancestor of u_{max} = MAX(ux, uy, uz).
485
If it is X or Y, then we do not check for the lower connection
486
required to find Minor E3 because it might ultimately be too
487
costly. Instead, we mark the vertex with a 'merge barrier'
488
of u_{max}. If the planar embedder attempts to merge the vertex
489
prior to step u_{max}, then the embedder has found the desired
490
connection and a K_{3,3} is isolated at that time.
491
492
OPTIMIZATION: When we can test for an external connection to
493
an ancestor of V descendant to u_{max}, the result may be that
494
only a connection to u_{max} exists. The result is cached
495
so that the subtrees of the vertex need not be traversed again
496
should we need to make the test again.
497
See _SearchForDescendantExternalConnection() */
498
499
if (IC->ux < u_max)
500
context->V[IC->x].mergeBlocker = u_max;
501
else
502
{
503
u = _SearchForDescendantExternalConnection(theGraph, context, IC->x, u_max);
504
if (u > u_max)
505
{
506
IC->ux = u;
507
if (_FinishIsolatorContextInitialization(theGraph, context) != OK ||
508
_IsolateMinorE3(theGraph) != OK)
509
return NOTOK;
510
511
return NONEMBEDDABLE;
512
}
513
}
514
515
if (IC->uy < u_max)
516
context->V[IC->y].mergeBlocker = u_max;
517
else
518
{
519
u = _SearchForDescendantExternalConnection(theGraph, context, IC->y, u_max);
520
if (u > u_max)
521
{
522
IC->uy = u;
523
if (_FinishIsolatorContextInitialization(theGraph, context) != OK ||
524
_IsolateMinorE3(theGraph) != OK)
525
return NOTOK;
526
527
return NONEMBEDDABLE;
528
}
529
}
530
531
/* Case 4: If there exists any x-y path with points of attachment px and py
532
such that px!=x or py!=y, then a K_{3,3} homeomorph can be isolated
533
with Minor E4. */
534
535
if (_TestForLowXYPath(theGraph) != OK)
536
return NOTOK;
537
538
if (IC->px != IC->x || IC->py != IC->y)
539
{
540
if (_FinishIsolatorContextInitialization(theGraph, context) != OK ||
541
_IsolateMinorE4(theGraph) != OK)
542
return NOTOK;
543
544
return NONEMBEDDABLE;
545
}
546
547
/* Case 5: If the x-y path contains an internal vertex that starts a second
548
internal path from the internal vertex to W/Z, then a K_{3,3} homeomorph
549
can be isolated with Minor E5. */
550
551
if (_TestForZtoWPath(theGraph) != OK)
552
return NOTOK;
553
554
if (theGraph->G[IC->w].visited)
555
{
556
if (_FinishIsolatorContextInitialization(theGraph, context) != OK ||
557
_IsolateMinorE5(theGraph) != OK)
558
return NOTOK;
559
560
return NONEMBEDDABLE;
561
}
562
563
/* Case 6: If uz < u_{max} and there is an external connection (other than external
564
connections involving X, Y and W/Z) between an ancestor of u_{max} and a
565
vertex in the range [V...u_{max}), then a K_{3,3} homeomorph can be
566
isolated with Minor E6.
567
568
OPTIMIZATION: See _TestForStraddlingBridge() */
569
570
if (IC->uz < u_max)
571
{
572
if (_TestForStraddlingBridge(theGraph, context, u_max) != NIL)
573
{
574
if (_FinishIsolatorContextInitialization(theGraph, context) != OK ||
575
_IsolateMinorE6(theGraph, context) != OK)
576
return NOTOK;
577
578
return NONEMBEDDABLE;
579
}
580
}
581
582
/* Case 7: If ux < u_{max} or uy < u_{max} and there is an external connection
583
between an ancestor of u_{max} and a vertex in the range [V...u_{max})
584
(except for external connections involving X, Y and W/Z), then a K_{3,3}
585
homeomorph can be isolated with Minor E7.
586
587
OPTIMIZATION: Same as Case 6.*/
588
589
if (IC->ux < u_max || IC->uy < u_max)
590
{
591
if (_TestForStraddlingBridge(theGraph, context, u_max) != NIL)
592
{
593
if (_FinishIsolatorContextInitialization(theGraph, context) != OK ||
594
_IsolateMinorE7(theGraph, context) != OK)
595
return NOTOK;
596
597
return NONEMBEDDABLE;
598
}
599
}
600
601
/* If none of the tests found a K_{3,3}, then we return OK to indicate that nothing
602
went wrong, but a K_{3,3} was not found. */
603
604
return OK;
605
}
606
607
/****************************************************************************
608
_SearchForMinorE1()
609
Search along the external face below the x-y path for a vertex Z other
610
than W that is externally active or pertinent.
611
****************************************************************************/
612
613
int _SearchForMinorE1(graphP theGraph)
614
{
615
int Z=theGraph->IC.px, ZPrevLink=1;
616
617
Z = _GetNextVertexOnExternalFace(theGraph, Z, &ZPrevLink);
618
619
while (Z != theGraph->IC.py)
620
{
621
if (Z != theGraph->IC.w)
622
{
623
if (_VertexActiveStatus(theGraph, Z, theGraph->IC.v) == VAS_EXTERNAL)
624
{
625
theGraph->IC.z = Z;
626
theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, Z);
627
return OK;
628
}
629
else if (theGraph->V[Z].pertinentBicompList != NIL ||
630
theGraph->V[Z].adjacentTo == theGraph->IC.v)
631
{
632
/* Swap the roles of W and Z */
633
634
theGraph->IC.z = theGraph->IC.w;
635
theGraph->IC.w = Z;
636
637
/* If the new W (indicated by Z) was on the path (R, X, old W) then
638
the new Z (the old W, which has no type mark) is on the path
639
(X, new W, new Z, Y) so we change the type new Z to being on the
640
RYW path. Otherwise, the order is (X, new Z, new W, Y), so the
641
new Z (old W with no type) is type changed to be on the RXW path.*/
642
643
if (theGraph->G[Z].type == VERTEX_LOW_RXW)
644
theGraph->G[theGraph->IC.z].type = VERTEX_LOW_RYW;
645
else theGraph->G[theGraph->IC.z].type = VERTEX_LOW_RXW;
646
647
/* For completeness, we change the new W to type unknown */
648
649
theGraph->G[theGraph->IC.w].type = TYPE_UNKNOWN;
650
651
/* The external activity ancestor connection of the new Z must be obtained */
652
653
theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, theGraph->IC.z);
654
655
return OK;
656
}
657
}
658
659
Z = _GetNextVertexOnExternalFace(theGraph, Z, &ZPrevLink);
660
}
661
662
return OK;
663
}
664
665
/****************************************************************************
666
_FinishIsolatorContextInitialization()
667
Once it has been decided that a desired subgraph can be isolated, it
668
becomes safe to finish the isolator context initialization.
669
****************************************************************************/
670
671
int _FinishIsolatorContextInitialization(graphP theGraph, K33SearchContext *context)
672
{
673
isolatorContextP IC = &theGraph->IC;
674
675
/* Restore the orientation of the bicomp on which we're working, then
676
perform orientation of all vertices in graph. (An unnecessary but
677
polite step that simplifies the description of key states of the
678
data structures). */
679
680
if (_OrientVerticesInBicomp(theGraph, IC->r, 1) != OK)
681
return NOTOK;
682
683
if (_OrientVerticesInEmbedding(theGraph) != OK)
684
return NOTOK;
685
686
/* Restore any paths that were reduced to single edges */
687
688
if (_RestoreAndOrientReducedPaths(theGraph, context) != OK)
689
return NOTOK;
690
691
/* We assume that the current bicomp has been marked appropriately,
692
but we must now clear the visitation flags of all other bicomps. */
693
694
if (_FillVisitedFlagsInOtherBicomps(theGraph, IC->r, 0) != OK)
695
return NOTOK;
696
697
/* To complete the normal behavior of _FillVisitedFlags() in the
698
normal isolator context initialization, we also have to clear
699
the visited flags on all edges that have not yet been embedded */
700
701
_FillVisitedFlagsInUnembeddedEdges(theGraph, 0);
702
703
/* Now we can find the descendant ends of unembedded back edges based on
704
the ancestor settings ux, uy and uz. */
705
706
if (_FindExternalConnectionDescendantEndpoint(theGraph, IC->ux, IC->x, &IC->dx) != OK ||
707
_FindExternalConnectionDescendantEndpoint(theGraph, IC->uy, IC->y, &IC->dy) != OK ||
708
_FindExternalConnectionDescendantEndpoint(theGraph, IC->uz, IC->z, &IC->dz) != OK)
709
return NOTOK;
710
711
/* Finally, we obtain the descendant end of an unembedded back edge to
712
the current vertex. */
713
714
if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE)
715
return NOTOK;
716
717
return OK;
718
}
719
720
/****************************************************************************
721
_GetAdjacentAncestorInRange()
722
Returns the ancestor of theVertex that is adjacent to theVertex by an
723
unembedded back edge and has a DFI strictly between closerAncestor and
724
fartherAncestor.
725
Returns NIL if theVertex has no such neighboring ancestor.
726
****************************************************************************/
727
728
int _GetAdjacentAncestorInRange(graphP theGraph, K33SearchContext *context, int theVertex,
729
int closerAncestor, int fartherAncestor)
730
{
731
int J = context->V[theVertex].backArcList;
732
733
while (gp_IsArc(theGraph, J))
734
{
735
if (theGraph->G[J].v < closerAncestor &&
736
theGraph->G[J].v > fartherAncestor)
737
return theGraph->G[J].v;
738
739
J = gp_GetNextArc(theGraph, J);
740
if (J == context->V[theVertex].backArcList)
741
J = NIL;
742
}
743
return NIL;
744
}
745
746
/****************************************************************************
747
_SearchForDescendantExternalConnection()
748
Search the cutVertex and each subtree rooted by a vertex in the
749
separatedDFSChildList of the cut vertex for an external connection
750
to a vertex ancestor to the current vertex V and descendant to u_max.
751
752
The function returns the descendant of u_max found to have an external
753
connection to the given cut vertex.
754
755
OPTIMIZATION: The subtrees are processed by preorder visitation. If
756
a vertex is visited and has a lowpoint indicating that it and its
757
descendants make no external connections, then we prune the subtree,
758
eliminating the vertex and its descendants from consideration.
759
Otherwise, if the vertex has an externalConnectionAncestor setting,
760
which must have been made by a prior invocation of this function,
761
then we use that setting. If both of these tests fail, then
762
we examine the vertex and push its children for consideration.
763
This ensures that this procedure never explores a subtree more than
764
once during the life of the K_{3,3} search algorithm.
765
766
Note that if a subtree is explored and the desired external connection
767
descendant to u_{max} is found, then a K_{3,3} will be found, so we only
768
have to concern ourselves with subtrees that connect only to u_{max}.
769
Between steps v and u_{max}, the subtree search is optimized by setting
770
'externalConnectionAncestor', and steps after u_{max} process ancestors
771
of u_{max}. Since this routine is only called if the cutVertex makes
772
no external connections to ancestors of u_{max}, the lowpoint test
773
prevents this subtree from being searched after step u_{max}.
774
****************************************************************************/
775
776
int _SearchForDescendantExternalConnection(graphP theGraph, K33SearchContext *context, int cutVertex, int u_max)
777
{
778
isolatorContextP IC = &theGraph->IC;
779
int u2 = _GetAdjacentAncestorInRange(theGraph, context, cutVertex, IC->v, u_max);
780
int listHead, child, descendant;
781
782
/* Test cut vertex for external connection to descendant of u_max */
783
784
if (u2 != NIL)
785
return u2;
786
787
/* If there is no direct back edge connection from the cut vertex
788
to a vertex on the path between V and u_max, then we will
789
look for such a connection in the DFS subtrees rooted by
790
separated DFS children of the vertex (ignoring those whose
791
lowpoint indicates that they make no external connections) */
792
793
/* Begin by pushing the separated DFS children of the cut vertex,
794
except stop when the lowpoint is no longer less than V since
795
external connections are not being made. */
796
797
sp_ClearStack(theGraph->theStack);
798
listHead = child = theGraph->V[cutVertex].separatedDFSChildList;
799
while (child != NIL)
800
{
801
if (theGraph->V[child].Lowpoint >= IC->v)
802
break;
803
sp_Push(theGraph->theStack, child);
804
child = LCGetNext(theGraph->DFSChildLists, listHead, child);
805
}
806
807
/* Now process the stack until it is empty or until we've found the desired connection. */
808
809
while (!sp_IsEmpty(theGraph->theStack))
810
{
811
sp_Pop(theGraph->theStack, descendant);
812
813
/* If the vertex has a lowpoint indicating that it makes no external
814
connections, then skip the subtree rooted by the vertex */
815
816
if (theGraph->V[descendant].Lowpoint < IC->v)
817
{
818
/* If a prior invocation has precalculated the result, use it. */
819
820
if (context->V[descendant].externalConnectionAncestor != NIL)
821
{
822
/* If the result is in the range we need, return it. Otherwise,
823
skip the subtree rooted by the vertex. */
824
825
if (context->V[descendant].externalConnectionAncestor < IC->v &&
826
context->V[descendant].externalConnectionAncestor > u_max)
827
return context->V[descendant].externalConnectionAncestor;
828
}
829
830
/* If the subtree has not been explored, then explore it. */
831
832
else
833
{
834
/* Check the subtree root for the desired connection. */
835
836
u2 = _GetAdjacentAncestorInRange(theGraph, context, descendant, IC->v, u_max);
837
if (u2 != NIL)
838
return u2;
839
840
/* Push each child as a new subtree root to be considered,
841
except skip those whose lowpoint is too great. */
842
843
child = context->V[descendant].sortedDFSChildList;
844
while (child != NIL)
845
{
846
if (theGraph->V[child].Lowpoint < IC->v)
847
sp_Push(theGraph->theStack, child);
848
849
child = LCGetNext(context->sortedDFSChildLists,
850
context->V[descendant].sortedDFSChildList, child);
851
}
852
}
853
}
854
}
855
856
/* The only external connections from the cutVertex lead to u_max,
857
so cache the result and return it. */
858
859
context->V[cutVertex].externalConnectionAncestor = u_max;
860
return u_max;
861
}
862
863
/****************************************************************************
864
_FindExternalConnectionDescendantEndpoint()
865
866
This operation is similar to _FindUnembeddedEdgeToAncestor() except that
867
we need to be more precise in this case, finding an external connection
868
from a given cut vertex to a *particular* given ancestor.
869
870
NOTE: By external we don't mean externall active so much as not embedded in
871
the bicomp containing the cut vertex.
872
873
Returns OK if it finds that either the given cutVertex or one of its
874
descendants in a separated bicomp has an unembedded back edge
875
connection to the given ancestor vertex.
876
Returns NOTOK otherwise (it is an error to not find the descendant because
877
this function is only called if _SearchForDescendantExternalConnection()
878
has already determined the existence of the descendant).
879
****************************************************************************/
880
881
int _FindExternalConnectionDescendantEndpoint(graphP theGraph, int ancestor,
882
int cutVertex, int *pDescendant)
883
{
884
int listHead, child, J;
885
886
// Check whether the cutVertex is directly adjacent to the ancestor
887
// by an unembedded back edge.
888
889
J = theGraph->V[ancestor].fwdArcList;
890
while (gp_IsArc(theGraph, J))
891
{
892
if (theGraph->G[J].v == cutVertex)
893
{
894
*pDescendant = cutVertex;
895
return OK;
896
}
897
898
J = gp_GetNextArc(theGraph, J);
899
if (J == theGraph->V[ancestor].fwdArcList)
900
J = NIL;
901
}
902
903
// Now check the descendants of the cut vertex to see if any make
904
// a connection to the ancestor.
905
listHead = child = theGraph->V[cutVertex].separatedDFSChildList;
906
while (child != NIL)
907
{
908
if (theGraph->V[child].Lowpoint >= theGraph->IC.v)
909
break;
910
911
if (_FindUnembeddedEdgeToSubtree(theGraph, ancestor, child, pDescendant) == TRUE)
912
return OK;
913
914
child = LCGetNext(theGraph->DFSChildLists, listHead, child);
915
}
916
917
return NOTOK;
918
}
919
920
/****************************************************************************
921
_SearchForMergeBlocker()
922
923
This function helps to implement the merge blocking optimization of
924
_SearchForDescendantExternalConnection(). The function RunExtraK33Tests()
925
sets a mergeBlocker rather than run _SearchForDescendantExternalConnection()
926
in certain cases. This procedure is called by MergeBicomps to test the
927
embedding stack for a merge blocker before merging any biconnected components.
928
If a merge blocker is found, then the embedder's Walkdown function is
929
terminated and SearchForK33() is subsequently called. The blocked merge
930
point is then used as the basis for isolating a K_{3,3}.
931
932
Returns OK on success (whether or not the search found a merge blocker)
933
NOTOK on internal function failure
934
pMergeBlocker is set to NIL unless a merge blocker is found.
935
****************************************************************************/
936
937
int _SearchForMergeBlocker(graphP theGraph, K33SearchContext *context, int I, int *pMergeBlocker)
938
{
939
stackP tempStack;
940
int R, Rout, Z, ZPrevLink;
941
942
/* Set return result to 'not found' then return if there is no stack to inspect */
943
944
*pMergeBlocker = NIL;
945
946
if (sp_IsEmpty(theGraph->theStack))
947
return OK;
948
949
/* Create a copy of the embedding stack */
950
951
tempStack = sp_Duplicate(theGraph->theStack);
952
if (tempStack == NULL)
953
return NOTOK;
954
955
/* Search the copy of the embedding stack for a merge blocked vertex */
956
957
while (!sp_IsEmpty(tempStack))
958
{
959
sp_Pop2(tempStack, R, Rout);
960
sp_Pop2(tempStack, Z, ZPrevLink);
961
962
if (context->V[Z].mergeBlocker != NIL &&
963
context->V[Z].mergeBlocker < I)
964
{
965
*pMergeBlocker = Z;
966
break;
967
}
968
}
969
970
sp_Free(&tempStack);
971
return OK;
972
}
973
974
/****************************************************************************
975
_FindK33WithMergeBlocker()
976
977
This function completes the merge blocking optimization by isolating a K_{3,3}
978
based on minor E3 if a merge blocked vertex was previously found.
979
980
Returns OK on success, NOTOK on internal function failure
981
****************************************************************************/
982
983
int _FindK33WithMergeBlocker(graphP theGraph, K33SearchContext *context, int I, int mergeBlocker)
984
{
985
int R, RPrevLink, u_max, u, J, W;
986
isolatorContextP IC = &theGraph->IC;
987
988
/* First, we orient the vertices so we can successfully restore all of the
989
reduced paths. This needs to be done before reconstructing the context
990
for CASE 3 of RunExtraK33Tests() because the reconstruction involves
991
using the Walkup to I from a descendant of I, which will not work if
992
the descendant is in one of the reduced paths. */
993
994
if (_OrientVerticesInEmbedding(theGraph) != OK ||
995
_RestoreAndOrientReducedPaths(theGraph, context) != OK)
996
return NOTOK;
997
998
/* Reconstruct the context that was present for CASE 3 of RunExtraK33Tests()
999
when we decided to set a mergeBlocker rather than calling
1000
_SearchForDescendantExternalConnection() */
1001
1002
/* Obtain the root of the bicomp containing the mergeBlocker. */
1003
1004
RPrevLink = 1;
1005
R = mergeBlocker;
1006
while (R < theGraph->N)
1007
R = _GetNextVertexOnExternalFace(theGraph, R, &RPrevLink);
1008
1009
/* Switch the 'current step' variable I to be equal to the
1010
non-virtual counterpart of the bicomp root. */
1011
1012
I = theGraph->V[R - theGraph->N].DFSParent;
1013
1014
/* Eliminate the visitation and pertinence settings for step u_max */
1015
1016
_FillVisitedFlags(theGraph, I+1);
1017
1018
for (J = 0; J < theGraph->N; J++)
1019
{
1020
theGraph->V[J].adjacentTo = NIL;
1021
theGraph->V[J].pertinentBicompList = NIL;
1022
}
1023
1024
/* Restore the pertinence settings of step I by doing the Walkup for each
1025
back edge that was not embedded when step I was originally performed. */
1026
1027
J = theGraph->V[I].fwdArcList;
1028
while (gp_IsArc(theGraph, J))
1029
{
1030
W = theGraph->G[J].v;
1031
theGraph->functions.fpWalkUp(theGraph, I, W);
1032
1033
J = gp_GetNextArc(theGraph, J);
1034
if (J == theGraph->V[I].fwdArcList)
1035
J = NIL;
1036
}
1037
1038
/* Next, we make the standard initialization calls for when we have found
1039
a non-planarity condition. */
1040
1041
sp_ClearStack(theGraph->theStack);
1042
1043
if (_ChooseTypeOfNonplanarityMinor(theGraph, I, R) != OK)
1044
return NOTOK;
1045
1046
IC->ux = _GetLeastAncestorConnection(theGraph, IC->x);
1047
IC->uy = _GetLeastAncestorConnection(theGraph, IC->y);
1048
IC->uz = _GetLeastAncestorConnection(theGraph, IC->z);
1049
1050
u_max = MAX3(IC->ux,IC->uy,IC->uz);
1051
1052
/* Perform the remainder of CASE 3 of RunExtraK33Tests() */
1053
1054
if (mergeBlocker == IC->x)
1055
{
1056
u = _SearchForDescendantExternalConnection(theGraph, context, IC->x, u_max);
1057
if (u > u_max)
1058
{
1059
IC->ux = u;
1060
if (_FinishIsolatorContextInitialization(theGraph, context) != OK ||
1061
_IsolateMinorE3(theGraph) != OK)
1062
return NOTOK;
1063
}
1064
else return NOTOK;
1065
}
1066
else if (mergeBlocker == IC->y)
1067
{
1068
u = _SearchForDescendantExternalConnection(theGraph, context, IC->y, u_max);
1069
if (u > u_max)
1070
{
1071
IC->uy = u;
1072
if (_FinishIsolatorContextInitialization(theGraph, context) != OK ||
1073
_IsolateMinorE3(theGraph) != OK)
1074
return NOTOK;
1075
}
1076
else return NOTOK;
1077
}
1078
else return NOTOK;
1079
1080
/* Do the final clean-up to obtain the K_{3,3} */
1081
1082
if (_DeleteUnmarkedVerticesAndEdges(theGraph) != OK)
1083
return NOTOK;
1084
1085
return OK;
1086
}
1087
1088
/****************************************************************************
1089
_TestForLowXYPath()
1090
Is there an x-y path that does not include X?
1091
If not, is there an x-y path that does not include Y?
1092
If not, then we restore the original x-y path.
1093
If such a low x-y path exists, then we adjust px or py accordingly,
1094
and we make sure that X or Y (whichever is excluded) and its edges are
1095
not marked visited.
1096
This method uses the stack, though it is called with an empty stack currently,
1097
it does happen to preserve any preceding stack content. This method pushes
1098
at most one integer per edge incident to the bicomp root plus two integers
1099
per vertex in the bicomp.
1100
****************************************************************************/
1101
1102
int _TestForLowXYPath(graphP theGraph)
1103
{
1104
isolatorContextP IC = &theGraph->IC;
1105
int result;
1106
int stackBottom;
1107
1108
/* Clear the previously marked X-Y path */
1109
1110
if (_FillVisitedFlagsInBicomp(theGraph, IC->r, 0) != OK)
1111
return NOTOK;
1112
1113
/* Save the size of the stack before hiding any edges, so we will know
1114
how many edges to restore */
1115
1116
stackBottom = sp_GetCurrentSize(theGraph->theStack);
1117
1118
/* Hide the internal edges of X */
1119
1120
if (_HideInternalEdges(theGraph, IC->x) != OK)
1121
return NOTOK;
1122
1123
/* Try to find a low X-Y path that excludes X, then restore the
1124
internal edges of X. */
1125
1126
result = _MarkHighestXYPath(theGraph);
1127
if (_RestoreInternalEdges(theGraph, stackBottom) != OK)
1128
return NOTOK;
1129
1130
/* If we found the low X-Y path, then return. */
1131
1132
if (result == TRUE)
1133
return OK;
1134
1135
/* Hide the internal edges of Y */
1136
1137
if (_HideInternalEdges(theGraph, IC->y) != OK)
1138
return NOTOK;
1139
1140
/* Try to find a low X-Y path that excludes Y, then restore the
1141
internal edges of Y. */
1142
1143
result = _MarkHighestXYPath(theGraph);
1144
if (_RestoreInternalEdges(theGraph, stackBottom) != OK)
1145
return NOTOK;
1146
1147
/* If we found the low X-Y path, then return. */
1148
1149
if (result == TRUE)
1150
return OK;
1151
1152
/* Restore the original X-Y path and return with no error
1153
(the search failure is reflected by no change to px and py */
1154
1155
if (_MarkHighestXYPath(theGraph) != TRUE)
1156
return NOTOK;
1157
1158
return OK;
1159
}
1160
1161
/****************************************************************************
1162
_TestForZtoWPath()
1163
This function tests whether there is a path inside the bicomp leading from W
1164
to some internal node of the x-y path. If there is, the path is marked.
1165
1166
Upon function return, the marking of W distinguishes whether the path was found.
1167
The function returns NOTOK on internal error, OK otherwise.
1168
1169
All internal vertices are marked as type unknown, as are W and the bicomp
1170
root. There is an X-Y path marked visited. So, we start a depth first
1171
search from W to find a visited vertex, except we prune the search to
1172
ignore vertices whose type is not unknown.
1173
1174
The depth first search has to mark the vertices it has seen as visited,
1175
but we do not want to conflict with the visited/non-visited settings
1176
that have so far been used to isolate the X-Y path. So, each vertex
1177
visited is marked with a NIL and pushed onto the resetList. At the end,
1178
all vertices on the resetList have their visited flags reset to 0.
1179
1180
For each vertex we visit, if it is an internal vertex on the X-Y path
1181
(i.e. visited=1 and type unknown), then we want to stop and unroll the
1182
stack to obtain the desired path (described below). If the vertex is type
1183
unknown, then we want to visit its unvisited neighbors.
1184
1185
We want to manage the stack so that it when the desired vertex is found,
1186
the stack contains the desired path. So, we do not simply push the
1187
neighbors of the vertex being visited. First, we only push 'eligible'
1188
vertices (i.e. vertices with a type of unknown and visited not equal to
1189
NIL). Second, when we decide a vertex v is eligible, we push (v, NIL).
1190
When we pop (v, NIL), we know that its type is unknown so we test
1191
whether it is the desired vertex by checking if its visited member is
1192
equal to 1. If so, then we can stop the depth first search, process
1193
the resetList, then use the vertices and edges remaining on the
1194
stack to mark the desired path.
1195
1196
If we pop (v, NIL) and find that the visited of v equals 0, then we
1197
set its visited to NIL. Then we find the first edge record e leading
1198
to an eligible vertex w (i.e. a vertex with type unknown and visited
1199
not equal to NIL), and we push both (v, e) and (w, NIL). Eventually all
1200
paths leading from w will be explored, and if none find the desired vertex,
1201
then (v, e) is popped. Now we search the adjacency list of v starting
1202
after e to find the edge record that indicates the next eligible vertex
1203
to visit. If none are found, then we simply go to the next iteration,
1204
which pops a 2-tuple containing the vertex u and an edge record e that
1205
indicates v as the neighbor of u. Finally, if the stack empties without
1206
finding the desired vertex, then we simply process the resetStack and return.
1207
****************************************************************************/
1208
1209
int _TestForZtoWPath(graphP theGraph)
1210
{
1211
isolatorContextP IC = &theGraph->IC;
1212
stackP resetList = sp_New(_GetBicompSize(theGraph, IC->r));
1213
int v, e, w;
1214
1215
if (resetList == NULL) return NOTOK;
1216
1217
sp_ClearStack(theGraph->theStack);
1218
sp_Push2(theGraph->theStack, IC->w, NIL);
1219
1220
while (!sp_IsEmpty(theGraph->theStack))
1221
{
1222
sp_Pop2(theGraph->theStack, v, e);
1223
1224
if (e == NIL)
1225
{
1226
if (theGraph->G[v].visited)
1227
break;
1228
1229
theGraph->G[v].visited = NIL;
1230
sp_Push(resetList, v);
1231
1232
e = gp_GetFirstArc(theGraph, v);
1233
}
1234
else
1235
e = gp_GetNextArc(theGraph, e);
1236
1237
while (gp_IsArc(theGraph, e))
1238
{
1239
w = theGraph->G[e].v;
1240
if (theGraph->G[w].visited != NIL &&
1241
theGraph->G[w].type == TYPE_UNKNOWN)
1242
{
1243
sp_Push2(theGraph->theStack, v, e);
1244
sp_Push2(theGraph->theStack, w, NIL);
1245
break;
1246
}
1247
e = gp_GetNextArc(theGraph, e);
1248
}
1249
}
1250
1251
while (!sp_IsEmpty(resetList))
1252
{
1253
sp_Pop(resetList, v);
1254
theGraph->G[v].visited = 0;
1255
}
1256
sp_Free(&resetList);
1257
1258
while (!sp_IsEmpty(theGraph->theStack))
1259
{
1260
sp_Pop2(theGraph->theStack, v, e);
1261
theGraph->G[v].visited = 1;
1262
theGraph->G[e].visited = 1;
1263
theGraph->G[gp_GetTwinArc(theGraph, e)].visited = 1;
1264
}
1265
1266
return OK;
1267
}
1268
1269
/****************************************************************************
1270
_TestForStraddlingBridge()
1271
We proceed on the path [V...u_{max}) from the current vertex V up to and
1272
excluding u_{max}. For each vertex p, we test whether p has a least
1273
ancestor less than u_{max} and whether p has a DFS child c that is not an
1274
ancestor of X, Y and W and that has a connection to an ancestor of u_{max}
1275
(in other words, whether the child C has a lowpoint less than u_{max}).
1276
1277
The separatedDFSChildList of each vertex already contains a list of
1278
the DFS children sorted by their lowpoint, and the list has not been
1279
reduced by bicomp merging because the vertices are not descendants of V.
1280
So, we can process a vertex by examining its leastAncestor and the
1281
lowpoint of one of the first two elements in its separatedDFSChildList.
1282
If the first child is an ancestor of X, Y and W, then we look at the
1283
second child.
1284
1285
If no bridge straddling u_{max} is found, the function returns NIL.
1286
If a straddling bridge is found, the function returns a descendant d
1287
of p in the subtree rooted by c such that d has a leastAncestor less
1288
than u_{max}. Given the vertex d, the path through the straddling
1289
bridge required in Minors E6 and E7 is easy to identify: Mark the
1290
DFS tree path from d to p, and add and mark the edge from d to its
1291
least ancestor.
1292
1293
OPTIMIZATION: If a straddling bridge is not found, then in each tree edge of
1294
the path [V...u_{max}) we set the member noStraddle equal to u_{max}.
1295
Then, we modify the above stated routine so that if it is testing
1296
for a straddling bridge of u_{max} along this path, it will stop
1297
if it encounters an edge with noStraddle equal to u_{max} then it
1298
will stop. Also, the optimization will only set noStraddle equal to
1299
u_{max} on the portion of the path that is traversed. Finally, if
1300
noStraddle is set to a value other than NIL, the setting will be
1301
ignored and it will not be changed.
1302
1303
Due to this optimization, we do not traverse a path more than once
1304
to find out whether a vertex on the path has a bridge that straddles
1305
u_{max}. This leaves two questions:
1306
1) What if a future step must determine whether there is a
1307
straddling bridge of an ancestor of u_{max}?
1308
2) What if a future step must determine whether there is a
1309
straddling bridge of a descendant of u_{max}?
1310
1311
The condition described in the first question cannot occur because it
1312
would imply the ability to detect a straddling bridge now.
1313
The condition described by the second question may occur, but in the
1314
future step, the bicomp now being tested for a K_{3,3} will be part of
1315
a straddling bridge in that future step. Thus, the straddling
1316
bridge query is asked at most twice along any DFS tree path.
1317
****************************************************************************/
1318
1319
int _TestForStraddlingBridge(graphP theGraph, K33SearchContext *context, int u_max)
1320
{
1321
isolatorContextP IC = &theGraph->IC;
1322
int p, c, d, excludedChild, e;
1323
1324
p = IC->v;
1325
excludedChild = IC->r - theGraph->N;
1326
d = NIL;
1327
1328
/* Starting at V, traverse the ancestor path to u_max looking for a straddling bridge */
1329
1330
while (p > u_max)
1331
{
1332
/* If we find a direct edge from p to an ancestor of u_max, the break. */
1333
1334
if (theGraph->V[p].leastAncestor < u_max)
1335
{
1336
d = p;
1337
break;
1338
}
1339
1340
/* Check for a path from p to an ancestor of u_max using the child
1341
of p with the least Lowpoint, except the child that is an
1342
ancestor of X, Y and W. */
1343
1344
c = theGraph->V[p].separatedDFSChildList;
1345
if (c == excludedChild)
1346
c = LCGetNext(theGraph->DFSChildLists, c, c);
1347
1348
if (c != NIL && theGraph->V[c].Lowpoint < u_max)
1349
{
1350
_FindUnembeddedEdgeToSubtree(theGraph, theGraph->V[c].Lowpoint, c, &d);
1351
break;
1352
}
1353
1354
/* Check for noStraddle of u_max, break if found */
1355
1356
e = gp_GetFirstArc(theGraph, p);
1357
if (context->G[e].noStraddle == u_max)
1358
break;
1359
1360
/* Go to the next ancestor */
1361
1362
excludedChild = p;
1363
p = theGraph->V[p].DFSParent;
1364
}
1365
1366
/* If d is NIL, then no straddling bridge was found, so we do the
1367
noStraddle optimization. */
1368
1369
if (d == NIL)
1370
{
1371
c = IC->v;
1372
while (c != p)
1373
{
1374
e = gp_GetFirstArc(theGraph, c);
1375
if (context->G[e].noStraddle != NIL)
1376
break;
1377
1378
context->G[e].noStraddle = u_max;
1379
1380
c = theGraph->V[c].DFSParent;
1381
}
1382
}
1383
1384
/* Return either NIL indicating no bridge straddling u_max or the descendant d
1385
used to help mark a straddling bridge that was found by this test. */
1386
1387
return d;
1388
}
1389
1390
/****************************************************************************
1391
_ReduceBicomp()
1392
1393
We want to reduce the given biconnected component to a 4-cycle plus an
1394
internal edge connecting X and Y. Each edge is to be associated with a
1395
path from the original graph, preserving the depth first search tree
1396
paths that help connect the vertices R, X, Y, and W. If a K_{3,3} is later found,
1397
the paths are restored, but it is necessary to preserve the DFS tree so that
1398
functions like MarkDFSPath() will be able to pass through the restored bicomp.
1399
Also, if a K_{3,3} is later found due to the merge blocker optimization, then the
1400
internal X-Y path may be needed and, once the bicomp reduction is reversed,
1401
a full DFS subtree connecting all vertices in the bicomp will need to be
1402
restored or else functions that traverse the bicomp will not work.
1403
1404
For example, _FindK33WithMergeBlocker() invokes ChooseTypeOfNonplanarityMinor()
1405
to help reconstruct the context under which the mergeBlocker was set.
1406
ChooseTypeOfNonplanarityMinor() calls _FillVisitedFlagsInBicomp(), which
1407
depends on the DFS tree.
1408
1409
NOTE: The following are some general steps taken in this method:
1410
1) All edges in the bicomp are marked unvisited
1411
2) selected paths are marked visited
1412
3) unvisited edges are deleted
1413
4) the edges of the bicomp are marked unvisited again
1414
5) the remaining paths of the bicomp are reduced
1415
Some of the edges that get deleted in step 3 above may represent
1416
paths that were reduced in prior embedder iterations. We delete
1417
the reduction edge but not the path it represents.
1418
If a K_{3,3} is ever found, then the edges of these reduced paths
1419
are still in the graph, though not connected to anything important.
1420
The desired K_{3,3} is marked visited, but step 4 above ensures that
1421
these reduction paths are not marked visited. Hence, they will be
1422
deleted when the K_{3,3} is isolated, and this routine does not
1423
need to restore any reduced paths on the edges it deletes.
1424
We also don't (and don't have the time to) restore any reduction
1425
edges along the paths we intend to keep.
1426
****************************************************************************/
1427
1428
int _ReduceBicomp(graphP theGraph, K33SearchContext *context, int R)
1429
{
1430
isolatorContextP IC = &theGraph->IC;
1431
int min, mid, max, A, A_edge, B, B_edge;
1432
int rxType, xwType, wyType, yrType, xyType;
1433
1434
/* The vertices in the bicomp need to be oriented so that functions
1435
like MarkPathAlongBicompExtFace() will work. */
1436
1437
if (_OrientVerticesInBicomp(theGraph, R, 0) != OK)
1438
return NOTOK;
1439
1440
/* The reduced edges start with a default type of 'tree' edge. The
1441
tests below, which identify the additional non-tree paths
1442
needed to complete the reduced bicomp, also identify which
1443
reduced edges need to be cycle edges.*/
1444
1445
rxType = xwType = wyType = yrType = xyType = EDGE_DFSPARENT;
1446
1447
/* Now we calculate some values that help figure out the shape of the
1448
DFS subtree whose structure will be retained in the bicomp. */
1449
1450
min = MIN3(IC->x, IC->y, IC->w);
1451
max = MAX3(IC->x, IC->y, IC->w);
1452
mid = MAX3(MIN(IC->x, IC->y), MIN(IC->x, IC->w), MIN(IC->y, IC->w));
1453
1454
/* If the order of descendendancy from V goes first to X, then it can
1455
proceed either to W then Y or to Y then W */
1456
1457
if (min == IC->x)
1458
{
1459
/* A is a descendant adjacent to the current vertex by a cycle edge
1460
whose DFS tree path to either mid or max is combined with the
1461
cycle edge to form the path that will be reduced to the
1462
external face cycle edge (V, max). */
1463
1464
A_edge = gp_GetLastArc(theGraph, IC->r);
1465
A = theGraph->G[A_edge].v;
1466
yrType = EDGE_BACK;
1467
1468
/* If Y is max, then a path parallel to the X-Y path will be a
1469
second path reduced to a cycle edge. We find the neighbor B
1470
of min=X on the X-Y path. The edge (B, min) is a cycle edge
1471
that, along with the DFS tree path (B, ..., max), will be
1472
retained and reduced to a cycle edge. */
1473
1474
if (max == IC->y)
1475
{
1476
B_edge = gp_GetLastArc(theGraph, IC->x);
1477
while (B_edge != gp_GetFirstArc(theGraph, IC->x))
1478
{
1479
if (theGraph->G[B_edge].visited) break;
1480
B_edge = gp_GetPrevArc(theGraph, B_edge);
1481
}
1482
1483
if (!theGraph->G[B_edge].visited)
1484
return NOTOK;
1485
1486
B = theGraph->G[B_edge].v;
1487
xyType = EDGE_BACK;
1488
}
1489
1490
/* Otherwise, W is max so we find the neighbor B of min=X on the
1491
lower external face path (X, ..., W), which excludes V. The
1492
cycle edge (B, min) and the DFS tree path (B, max) will be
1493
retained and reduced to a cycle edge.*/
1494
1495
else if (max == IC->w)
1496
{
1497
B_edge = gp_GetFirstArc(theGraph, IC->x);
1498
B = theGraph->G[B_edge].v;
1499
xwType = EDGE_BACK;
1500
}
1501
1502
else return NOTOK;
1503
}
1504
1505
/* Otherwise, the order of descendancy from V goes first to Y, then it
1506
proceeds to either W then X or to X then W. The */
1507
1508
else
1509
{
1510
A_edge = gp_GetFirstArc(theGraph, IC->r);
1511
A = theGraph->G[A_edge].v;
1512
rxType = EDGE_BACK;
1513
1514
if (max == IC->x)
1515
{
1516
B_edge = gp_GetFirstArc(theGraph, IC->y);
1517
while (B_edge != gp_GetLastArc(theGraph, IC->y))
1518
{
1519
if (theGraph->G[B_edge].visited) break;
1520
B_edge = gp_GetNextArc(theGraph, B_edge);
1521
}
1522
1523
if (!theGraph->G[B_edge].visited)
1524
return NOTOK;
1525
1526
B = theGraph->G[B_edge].v;
1527
xyType = EDGE_BACK;
1528
}
1529
1530
else if (max == IC->w)
1531
{
1532
B_edge = gp_GetLastArc(theGraph, IC->y);
1533
B = theGraph->G[B_edge].v;
1534
wyType = EDGE_BACK;
1535
}
1536
1537
else return NOTOK;
1538
}
1539
1540
/* Now that we have collected the information on which cycle edge and
1541
which tree paths will actually be retained, we clear the visited
1542
flags so the current X-Y path will not be retained (an X-Y path
1543
formed mostly or entirely from DFS tree edges is retained). */
1544
1545
if (_FillVisitedFlagsInBicomp(theGraph, R, 0) != OK)
1546
return NOTOK;
1547
1548
/* Now we mark the tree path from the maximum numbered vertex up
1549
to the bicomp root. This marks one of the following four paths:
1550
Case 1. (V, ..., X=min, ..., W=mid, ..., Y=max)
1551
Case 2. (V, ..., X=min, ..., Y=mid, ..., W=max)
1552
Case 3. (V, ..., Y=min, ..., W=mid, ..., X=max)
1553
Case 4. (V, ..., Y=min, ..., X=mid, ..., W=max) */
1554
1555
if (theGraph->functions.fpMarkDFSPath(theGraph, R, max) != OK)
1556
return NOTOK;
1557
1558
/* Now we use A to mark a path on the external face corresponding to:
1559
Case 1. (V, ..., Y=max)
1560
Case 2. (V, ..., Y=mid)
1561
Case 3. (V, ..., X=max)
1562
Case 4. (V, ..., X=mid) */
1563
1564
if (theGraph->functions.fpMarkDFSPath(theGraph, min==IC->x ? IC->y : IC->x, A) != OK)
1565
return NOTOK;
1566
1567
theGraph->G[A_edge].visited = 1;
1568
theGraph->G[gp_GetTwinArc(theGraph, A_edge)].visited = 1;
1569
1570
/* Now we use B to mark either an X-Y path or a path of the external face
1571
corresponding to:
1572
Case 1. (X=min, ..., B, ..., Y=max)
1573
Case 2. (X=min, ..., B, ..., W=max)
1574
Case 3. (Y=min, ..., B, ..., X=max)
1575
Case 4. (Y=min, ..., B, ..., W=max) */
1576
1577
if (theGraph->functions.fpMarkDFSPath(theGraph, max, B) != OK)
1578
return NOTOK;
1579
1580
theGraph->G[B_edge].visited = 1;
1581
theGraph->G[gp_GetTwinArc(theGraph, B_edge)].visited = 1;
1582
1583
/* Delete the unmarked edges in the bicomp. Note that if an unmarked edge
1584
* represents a reduced path, then only the reduction edge is deleted here.
1585
* The path it represents is only deleted later (see NOTE above) */
1586
1587
if (_DeleteUnmarkedEdgesInBicomp(theGraph, R) != OK)
1588
return NOTOK;
1589
1590
/* Clear all visited flags in the bicomp.
1591
This is the important "step 4" mentioned in the NOTE above */
1592
1593
if (_FillVisitedFlagsInBicomp(theGraph, R, 0) != OK)
1594
return NOTOK;
1595
1596
/* Clear all orientation signs in the bicomp.
1597
Note that the whole bicomp may not be properly oriented at this point
1598
because we may have exchanged external face paths for internal
1599
DFS tree paths. However, the reduced bicomp will be properly
1600
oriented, and the paths of degree 2 vertices will have their
1601
orientations fixed if/when reduction edges are restored. */
1602
1603
if (_ClearInvertedFlagsInBicomp(theGraph, R) != OK)
1604
return NOTOK;
1605
1606
/* Reduce the paths to single edges.
1607
Note that although the whole bicomp may not be properly oriented at this
1608
point (as noted above), the four principal vertices R, X, W and Y still
1609
are consistently oriented with one another, e.g. R's link[0] indicates
1610
the external face path toward X that excludes W and Y, and X's link[1]
1611
indicates that same path. */
1612
1613
if (_ReduceExternalFacePathToEdge(theGraph, context, R, IC->x, rxType) != OK ||
1614
_ReduceExternalFacePathToEdge(theGraph, context, IC->x, IC->w, xwType) != OK ||
1615
_ReduceExternalFacePathToEdge(theGraph, context, IC->w, IC->y, wyType) != OK ||
1616
_ReduceExternalFacePathToEdge(theGraph, context, IC->y, R, yrType) != OK)
1617
return NOTOK;
1618
1619
if (_ReduceXYPathToEdge(theGraph, context, IC->x, IC->y, xyType) != OK)
1620
return NOTOK;
1621
1622
/* The core planarity method used vertex visited flags in the Walkup, so we have to
1623
set the vertex visited flags so the remaining vertices will behave as though they
1624
are unvisited by Walkup when the embedder moves to the next vertex. */
1625
1626
theGraph->G[R].visited =
1627
theGraph->G[IC->x].visited =
1628
theGraph->G[IC->y].visited =
1629
theGraph->G[IC->w].visited = IC->v;
1630
1631
return OK;
1632
}
1633
1634
/****************************************************************************
1635
_ReduceExternalFacePathToEdge()
1636
****************************************************************************/
1637
1638
int _ReduceExternalFacePathToEdge(graphP theGraph, K33SearchContext *context, int u, int x, int edgeType)
1639
{
1640
int prevLink, v, w, e;
1641
1642
/* If the path is a single edge, then no need for a reduction */
1643
1644
prevLink = 1;
1645
v = _GetNextVertexOnExternalFace(theGraph, u, &prevLink);
1646
if (v == x)
1647
{
1648
theGraph->extFace[u].vertex[0] = x;
1649
theGraph->extFace[x].vertex[1] = u;
1650
return OK;
1651
}
1652
1653
/* We have the endpoints u and x of the path, and we just computed the
1654
first vertex internal to the path and a neighbor of u. Now we
1655
compute the vertex internal to the path and a neighbor of x. */
1656
1657
prevLink = 0;
1658
w = _GetNextVertexOnExternalFace(theGraph, x, &prevLink);
1659
1660
/* Delete the two edges that connect the path to the bicomp.
1661
If either edge is a reduction edge, then we have to restore
1662
the path it represents. We can only afford to visit the
1663
endpoints of the path.
1664
Note that in the restored path, the edge incident to each
1665
endpoint of the original path is a newly added edge,
1666
not a reduction edge. */
1667
1668
e = gp_GetFirstArc(theGraph, u);
1669
if (context->G[e].pathConnector != NIL)
1670
{
1671
if (_RestoreReducedPath(theGraph, context, e) != OK)
1672
return NOTOK;
1673
e = gp_GetFirstArc(theGraph, u);
1674
v = theGraph->G[e].v;
1675
}
1676
gp_DeleteEdge(theGraph, e, 0);
1677
1678
e = gp_GetLastArc(theGraph, x);
1679
if (context->G[e].pathConnector != NIL)
1680
{
1681
if (_RestoreReducedPath(theGraph, context, e) != OK)
1682
return NOTOK;
1683
e = gp_GetLastArc(theGraph, x);
1684
w = theGraph->G[e].v;
1685
}
1686
gp_DeleteEdge(theGraph, e, 0);
1687
1688
/* Add the reduction edge, then set its path connectors so the original
1689
path can be recovered and set the edge type so the essential structure
1690
of the DFS tree can be maintained (The 'Do X to Bicomp' functions
1691
and functions like MarkDFSPath(0 depend on this). */
1692
1693
gp_AddEdge(theGraph, u, 0, x, 1);
1694
1695
e = gp_GetFirstArc(theGraph, u);
1696
context->G[e].pathConnector = v;
1697
theGraph->G[e].type = _ComputeArcType(theGraph, u, x, edgeType);
1698
1699
e = gp_GetLastArc(theGraph, x);
1700
context->G[e].pathConnector = w;
1701
theGraph->G[e].type = _ComputeArcType(theGraph, x, u, edgeType);
1702
1703
/* Set the external face info */
1704
1705
theGraph->extFace[u].vertex[0] = x;
1706
theGraph->extFace[x].vertex[1] = u;
1707
1708
return OK;
1709
}
1710
1711
/****************************************************************************
1712
_ReduceXYPathToEdge()
1713
****************************************************************************/
1714
1715
int _ReduceXYPathToEdge(graphP theGraph, K33SearchContext *context, int u, int x, int edgeType)
1716
{
1717
int e, v, w;
1718
1719
e = gp_GetFirstArc(theGraph, u);
1720
e = gp_GetNextArc(theGraph, e);
1721
v = theGraph->G[e].v;
1722
1723
/* If the XY-path is a single edge, then no reduction is needed */
1724
1725
if (v == x)
1726
return OK;
1727
1728
/* Otherwise, remove the two edges that join the XY-path to the bicomp */
1729
1730
if (context->G[e].pathConnector != NIL)
1731
{
1732
if (_RestoreReducedPath(theGraph, context, e) != OK)
1733
return NOTOK;
1734
e = gp_GetFirstArc(theGraph, u);
1735
e = gp_GetNextArc(theGraph, e);
1736
v = theGraph->G[e].v;
1737
}
1738
gp_DeleteEdge(theGraph, e, 0);
1739
1740
e = gp_GetFirstArc(theGraph, x);
1741
e = gp_GetNextArc(theGraph, e);
1742
w = theGraph->G[e].v;
1743
if (context->G[e].pathConnector != NIL)
1744
{
1745
if (_RestoreReducedPath(theGraph, context, e) != OK)
1746
return NOTOK;
1747
e = gp_GetFirstArc(theGraph, x);
1748
e = gp_GetNextArc(theGraph, e);
1749
w = theGraph->G[e].v;
1750
}
1751
gp_DeleteEdge(theGraph, e, 0);
1752
1753
/* Now add a single edge to represent the XY-path */
1754
gp_InsertEdge(theGraph, u, gp_GetFirstArc(theGraph, u), 0,
1755
x, gp_GetFirstArc(theGraph, x), 0);
1756
1757
/* Now set up the path connectors so the original XY-path can be recovered if needed.
1758
Also, set the reduction edge's type to preserve the DFS tree structure */
1759
1760
e = gp_GetFirstArc(theGraph, u);
1761
e = gp_GetNextArc(theGraph, e);
1762
context->G[e].pathConnector = v;
1763
theGraph->G[e].type = _ComputeArcType(theGraph, u, x, edgeType);
1764
1765
e = gp_GetFirstArc(theGraph, x);
1766
e = gp_GetNextArc(theGraph, e);
1767
context->G[e].pathConnector = w;
1768
theGraph->G[e].type = _ComputeArcType(theGraph, x, u, edgeType);
1769
1770
return OK;
1771
}
1772
1773
/****************************************************************************
1774
_RestoreReducedPath()
1775
Given an edge record of an edge used to reduce a path, we want to restore
1776
the path in constant time.
1777
The path may contain more reduction edges internally, but we do not
1778
search for and process those since it would violate the constant time
1779
bound required of this function.
1780
return OK on success, NOTOK on failure
1781
****************************************************************************/
1782
1783
int _RestoreReducedPath(graphP theGraph, K33SearchContext *context, int J)
1784
{
1785
int JTwin, u, v, w, x;
1786
int J0, J1, JTwin0, JTwin1;
1787
1788
if (context->G[J].pathConnector == NIL)
1789
return OK;
1790
1791
JTwin = gp_GetTwinArc(theGraph, J);
1792
1793
u = theGraph->G[JTwin].v;
1794
v = context->G[J].pathConnector;
1795
w = context->G[JTwin].pathConnector;
1796
x = theGraph->G[J].v;
1797
1798
/* Get the locations of the graph nodes between which the new
1799
graph nodes must be added in order to reconnect the path
1800
parallel to the edge. */
1801
1802
J0 = gp_GetNextArc(theGraph, J);
1803
J1 = gp_GetPrevArc(theGraph, J);
1804
JTwin0 = gp_GetNextArc(theGraph, JTwin);
1805
JTwin1 = gp_GetPrevArc(theGraph, JTwin);
1806
1807
/* We first delete the edge represented by J and JTwin. We do so before
1808
restoring the path to ensure we do not exceed the maximum arc capacity. */
1809
1810
gp_DeleteEdge(theGraph, J, 0);
1811
1812
/* Now we add the two edges to reconnect the reduced path represented
1813
by the edge [J, JTwin]. The edge record in u is added between J0 and J1.
1814
Likewise, the new edge record in x is added between JTwin0 and JTwin1. */
1815
1816
if (gp_IsArc(theGraph, J0))
1817
{
1818
if (gp_InsertEdge(theGraph, u, J0, 1, v, gp_AdjacencyListEndMark(v), 0) != OK)
1819
return NOTOK;
1820
}
1821
else
1822
{
1823
if (gp_InsertEdge(theGraph, u, J1, 0, v, gp_AdjacencyListEndMark(v), 0) != OK)
1824
return NOTOK;
1825
}
1826
1827
if (gp_IsArc(theGraph, JTwin0))
1828
{
1829
if (gp_InsertEdge(theGraph, x, JTwin0, 1, w, gp_AdjacencyListEndMark(w), 0) != OK)
1830
return NOTOK;
1831
}
1832
else
1833
{
1834
if (gp_InsertEdge(theGraph, x, JTwin1, 0, w, gp_AdjacencyListEndMark(w), 0) != OK)
1835
return NOTOK;
1836
}
1837
1838
// Set the types of the newly added edges. In both cases, the first of the two
1839
// vertex parameters is known to be degree 2 because they are internal to the
1840
// path being restored, so this operation is constant time.
1841
if (_SetEdgeType(theGraph, v, u) != OK ||
1842
_SetEdgeType(theGraph, w, x) != OK)
1843
return NOTOK;
1844
1845
return OK;
1846
}
1847
1848
/****************************************************************************
1849
_RestoreAndOrientReducedPaths()
1850
This function searches the embedding for any edges that are specially marked
1851
as being representative of a path that was previously reduced to a
1852
single edge by _ReduceBicomp(). The edge is replaced by the path.
1853
Note that the new path may contain more reduction edges, and these will be
1854
iteratively expanded by the outer for loop.
1855
1856
If the edge records of an edge being expanded are the first or last arcs
1857
of the edge's vertex endpoints, then the edge may be along the external face.
1858
If so, then the vertices along the path being restored must be given a
1859
consistent orientation with the endpoints. It is expected that the embedding
1860
will have been oriented prior to this operation.
1861
****************************************************************************/
1862
1863
int _RestoreAndOrientReducedPaths(graphP theGraph, K33SearchContext *context)
1864
{
1865
int e, J, JTwin, u, v, w, x, visited;
1866
int J0, JTwin0, J1, JTwin1;
1867
1868
for (e = 0; e < theGraph->M + sp_GetCurrentSize(theGraph->edgeHoles);)
1869
{
1870
J = theGraph->edgeOffset + 2*e;
1871
if (context->G[J].pathConnector != NIL)
1872
{
1873
visited = theGraph->G[J].visited;
1874
1875
JTwin = gp_GetTwinArc(theGraph, J);
1876
u = theGraph->G[JTwin].v;
1877
v = context->G[J].pathConnector;
1878
w = context->G[JTwin].pathConnector;
1879
x = theGraph->G[J].v;
1880
1881
/* Now we need the predecessor and successor edge records
1882
of J and JTwin. The edge (u, v) will be inserted so
1883
that the record in u's adjacency list that indicates v
1884
will be between J0 and J1. Likewise, the edge record
1885
(x -> w) will be placed between JTwin0 and JTwin1. */
1886
1887
J0 = gp_GetNextArc(theGraph, J);
1888
J1 = gp_GetPrevArc(theGraph, J);
1889
JTwin0 = gp_GetNextArc(theGraph, JTwin);
1890
JTwin1 = gp_GetPrevArc(theGraph, JTwin);
1891
1892
/* We first delete the edge represented by J and JTwin. We do so before
1893
restoring the path to ensure we do not exceed the maximum arc capacity. */
1894
1895
gp_DeleteEdge(theGraph, J, 0);
1896
1897
/* Now we add the two edges to reconnect the reduced path represented
1898
by the edge [J, JTwin]. The edge record in u is added between J0 and J1.
1899
Likewise, the new edge record in x is added between JTwin0 and JTwin1. */
1900
1901
if (gp_IsArc(theGraph, J0))
1902
{
1903
if (gp_InsertEdge(theGraph, u, J0, 1, v, gp_AdjacencyListEndMark(v), 0) != OK)
1904
return NOTOK;
1905
}
1906
else
1907
{
1908
if (gp_InsertEdge(theGraph, u, J1, 0, v, gp_AdjacencyListEndMark(v), 0) != OK)
1909
return NOTOK;
1910
}
1911
1912
if (gp_IsArc(theGraph, JTwin0))
1913
{
1914
if (gp_InsertEdge(theGraph, x, JTwin0, 1, w, gp_AdjacencyListEndMark(w), 0) != OK)
1915
return NOTOK;
1916
}
1917
else
1918
{
1919
if (gp_InsertEdge(theGraph, x, JTwin1, 0, w, gp_AdjacencyListEndMark(w), 0) != OK)
1920
return NOTOK;
1921
}
1922
1923
/* Set the types of the newly added edges */
1924
1925
if (_SetEdgeType(theGraph, u, v) != OK ||
1926
_SetEdgeType(theGraph, w, x) != OK)
1927
return NOTOK;
1928
1929
/* We determine whether the reduction edge may be on the external face,
1930
in which case we will need to ensure that the vertices on the path
1931
being restored are consistently oriented. This will accommodate
1932
future invocations of MarkPathAlongBicompExtFace().
1933
Note: If J0, J1, JTwin0 or JTwin1 is not an edge, then it is
1934
because we've walked off the end of the edge record list,
1935
which happens when J and JTwin are either the first or
1936
last edge of the containing vertex. In turn, the first
1937
and last edges of a vertex are the ones that hold it onto
1938
the external face, if it is on the external face. */
1939
1940
if ((!gp_IsArc(theGraph, J0) && !gp_IsArc(theGraph, JTwin1)) ||
1941
(!gp_IsArc(theGraph, J1) && !gp_IsArc(theGraph, JTwin0)))
1942
{
1943
if (_OrientExternalFacePath(theGraph, u, v, w, x) != OK)
1944
return NOTOK;
1945
}
1946
1947
/* The internal XY path was already marked as part of the decision logic
1948
that made us decide we could find a K_{3,3} and hence that we should
1949
reverse all of the reductions. Subsequent code counts on the fact
1950
that the X-Y path is already marked, so if we replace a marked edge
1951
with a path, then we need to mark the path. Similarly, for an unmarked
1952
edge, the replacement path should be unmarked. */
1953
1954
if (_SetVisitedOnPath(theGraph, u, v, w, x, visited) != OK)
1955
return NOTOK;
1956
}
1957
else e++;
1958
}
1959
1960
return OK;
1961
}
1962
1963
/****************************************************************************
1964
_MarkStraddlingBridgePath()
1965
****************************************************************************/
1966
1967
int _MarkStraddlingBridgePath(graphP theGraph, int u_min, int u_max, int u_d, int d)
1968
{
1969
isolatorContextP IC = &theGraph->IC;
1970
int p, J;
1971
1972
/* Find the point of intersection p between the path (v ... u_max)
1973
and the path (d ... u_max). */
1974
1975
if (theGraph->functions.fpMarkDFSPath(theGraph, u_max, IC->r) != OK)
1976
return NOTOK;
1977
1978
p = d;
1979
while (!theGraph->G[p].visited)
1980
{
1981
theGraph->G[p].visited = 1;
1982
1983
J = gp_GetFirstArc(theGraph, p);
1984
while (gp_IsArc(theGraph, J))
1985
{
1986
if (theGraph->G[J].type == EDGE_DFSPARENT)
1987
break;
1988
1989
J = gp_GetNextArc(theGraph, J);
1990
}
1991
1992
theGraph->G[J].visited = 1;
1993
theGraph->G[gp_GetTwinArc(theGraph, J)].visited = 1;
1994
1995
p = theGraph->G[J].v;
1996
1997
/* If p is a root copy, mark it visited and skip to the parent copy */
1998
if (p >= theGraph->N)
1999
{
2000
theGraph->G[p].visited = 1;
2001
p = theGraph->V[p-theGraph->N].DFSParent;
2002
}
2003
}
2004
2005
/* Unmark the path (p ... u_max), which was marked to help find p.
2006
The path from v to u_{max} is not needed to form a K_{3,3} except
2007
for the portion of the path up to p that, with the straddling
2008
bridge path, comprises part of the connection to u_d. In the
2009
minor, the path between v and p is edge contracted. */
2010
2011
while (p != u_max)
2012
{
2013
J = gp_GetFirstArc(theGraph, p);
2014
while (gp_IsArc(theGraph, J))
2015
{
2016
if (theGraph->G[J].type == EDGE_DFSPARENT)
2017
break;
2018
2019
J = gp_GetNextArc(theGraph, J);
2020
}
2021
2022
theGraph->G[J].visited = 0;
2023
theGraph->G[gp_GetTwinArc(theGraph, J)].visited = 0;
2024
2025
p = theGraph->G[J].v;
2026
theGraph->G[p].visited = 0;
2027
2028
/* If p is a root copy, clear its visited flag and skip to the
2029
parent copy */
2030
2031
if (p >= theGraph->N)
2032
{
2033
p = theGraph->V[p-theGraph->N].DFSParent;
2034
theGraph->G[p].visited = 0;
2035
}
2036
}
2037
2038
/* The straddling bridge must join the path (u_max ... u_min). If u_d is an
2039
ancestor of u_min, then mark the path that joins u_d to u_min. */
2040
2041
if (u_d < u_min)
2042
if (theGraph->functions.fpMarkDFSPath(theGraph, u_d, u_min) != OK)
2043
return NOTOK;
2044
2045
return OK;
2046
}
2047
2048
/****************************************************************************
2049
_IsolateMinorE5()
2050
The paths (x, w), (y, w) and (v, u_{max}) are not needed.
2051
The x-y path and the internal w-z path are already marked.
2052
****************************************************************************/
2053
2054
int _IsolateMinorE5(graphP theGraph)
2055
{
2056
isolatorContextP IC = &theGraph->IC;
2057
2058
if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->x) != OK ||
2059
_MarkPathAlongBicompExtFace(theGraph, IC->y, IC->r) != OK ||
2060
theGraph->functions.fpMarkDFSPath(theGraph, MIN3(IC->ux,IC->uy,IC->uz),
2061
MAX3(IC->ux,IC->uy,IC->uz)) != OK ||
2062
_MarkDFSPathsToDescendants(theGraph) != OK ||
2063
_JoinBicomps(theGraph) != OK ||
2064
_AddAndMarkUnembeddedEdges(theGraph) != OK)
2065
return NOTOK;
2066
2067
return OK;
2068
}
2069
2070
/****************************************************************************
2071
_IsolateMinorE6()
2072
The paths (x, y), (v, w) and (v, u_{max}) are not needed.
2073
The path through the straddling bridge that connects from an ancestor of
2074
u_{max} to v is required, but it may connnect to an ancestor p of v.
2075
In such a case, the path (v, p) is required, while (p, u_{max}) is not.
2076
****************************************************************************/
2077
2078
int _IsolateMinorE6(graphP theGraph, K33SearchContext *context)
2079
{
2080
isolatorContextP IC = &theGraph->IC;
2081
int u_min, u_max, d, u_d;
2082
2083
/* Clear the previously marked x-y path */
2084
2085
if (_FillVisitedFlagsInBicomp(theGraph, IC->r, 0) != OK)
2086
return NOTOK;
2087
2088
/* Clear dw to stop the marking of path (v, w) */
2089
2090
IC->dw = NIL;
2091
2092
/* Mark (v, ..., x, ..., w, ..., y, ... v) */
2093
2094
if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK)
2095
return NOTOK;
2096
2097
/* Mark the path through the straddling bridge (except for the final
2098
edge (u_d, d) which is added last by convention). */
2099
2100
u_min = MIN3(IC->ux,IC->uy,IC->uz);
2101
u_max = MAX3(IC->ux,IC->uy,IC->uz);
2102
d = _TestForStraddlingBridge(theGraph, context, u_max);
2103
u_d = theGraph->V[d].leastAncestor;
2104
2105
if (_MarkStraddlingBridgePath(theGraph, u_min, u_max, u_d, d) != OK)
2106
return NOTOK;
2107
2108
/* Make the final markings and edge additions */
2109
2110
if (theGraph->functions.fpMarkDFSPath(theGraph, u_min, u_max) != OK ||
2111
_MarkDFSPathsToDescendants(theGraph) != OK ||
2112
_JoinBicomps(theGraph) != OK ||
2113
_AddAndMarkUnembeddedEdges(theGraph) != OK ||
2114
_AddAndMarkEdge(theGraph, u_d, d) != OK)
2115
return NOTOK;
2116
2117
return OK;
2118
}
2119
2120
/****************************************************************************
2121
_IsolateMinorE7()
2122
****************************************************************************/
2123
2124
int _IsolateMinorE7(graphP theGraph, K33SearchContext *context)
2125
{
2126
isolatorContextP IC = &theGraph->IC;
2127
int u_min, u_max, d, u_d;
2128
2129
/* Mark the appropriate two portions of the external face depending on
2130
symmetry condition */
2131
2132
if (IC->uy < IC->ux)
2133
{
2134
if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->x) != OK ||
2135
_MarkPathAlongBicompExtFace(theGraph, IC->w, IC->y) != OK)
2136
return NOTOK;
2137
}
2138
else
2139
{
2140
if (_MarkPathAlongBicompExtFace(theGraph, IC->x, IC->w) != OK ||
2141
_MarkPathAlongBicompExtFace(theGraph, IC->y, IC->r) != OK)
2142
return NOTOK;
2143
}
2144
2145
/* Mark the path through the straddling bridge (except for the final
2146
edge (u_d, d) which is added last by convention). */
2147
2148
u_min = MIN3(IC->ux,IC->uy,IC->uz);
2149
u_max = MAX3(IC->ux,IC->uy,IC->uz);
2150
d = _TestForStraddlingBridge(theGraph, context, u_max);
2151
u_d = theGraph->V[d].leastAncestor;
2152
2153
if (_MarkStraddlingBridgePath(theGraph, u_min, u_max, u_d, d) != OK)
2154
return NOTOK;
2155
2156
/* Make the final markings and edge additions */
2157
2158
if (theGraph->functions.fpMarkDFSPath(theGraph, u_min, u_max) != OK ||
2159
_MarkDFSPathsToDescendants(theGraph) != OK ||
2160
_JoinBicomps(theGraph) != OK ||
2161
_AddAndMarkUnembeddedEdges(theGraph) != OK ||
2162
_AddAndMarkEdge(theGraph, u_d, d) != OK)
2163
return NOTOK;
2164
2165
return OK;
2166
}
2167
2168