Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/graphs/planarity/graphNonplanar.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
#define GRAPHNONPLANAR_C
46
47
#include "graph.h"
48
49
/* Imported functions */
50
51
extern void _ClearIsolatorContext(graphP theGraph);
52
extern void _FillVisitedFlags(graphP, int);
53
extern int _FillVisitedFlagsInBicomp(graphP theGraph, int BicompRoot, int FillValue);
54
extern int _SetVertexTypeInBicomp(graphP theGraph, int BicompRoot, int theType);
55
extern int _HideInternalEdges(graphP theGraph, int vertex);
56
extern int _RestoreInternalEdges(graphP theGraph, int stackBottom);
57
58
extern int _GetNextVertexOnExternalFace(graphP theGraph, int curVertex, int *pPrevLink);
59
extern int _OrientVerticesInEmbedding(graphP theGraph);
60
extern int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns);
61
62
/* Private functions (exported to system) */
63
64
int _ChooseTypeOfNonplanarityMinor(graphP theGraph, int I, int R);
65
int _InitializeNonplanarityContext(graphP theGraph, int I, int R);
66
67
int _FindNonplanarityBicompRoot(graphP theGraph);
68
void _FindActiveVertices(graphP theGraph, int R, int *pX, int *pY);
69
int _FindPertinentVertex(graphP theGraph);
70
int _SetVertexTypesForMarkingXYPath(graphP theGraph);
71
72
int _PopAndUnmarkVerticesAndEdges(graphP theGraph, int Z, int stackBottom);
73
74
int _MarkHighestXYPath(graphP theGraph);
75
int _MarkZtoRPath(graphP theGraph);
76
int _FindExtActivityBelowXYPath(graphP theGraph);
77
78
/****************************************************************************
79
_ChooseTypeOfNonplanarityMinor()
80
****************************************************************************/
81
82
int _ChooseTypeOfNonplanarityMinor(graphP theGraph, int I, int R)
83
{
84
int N, X, Y, W, Px, Py, Z, DFSChild, RootId;
85
86
/* Create the initial non-planarity minor state in the isolator context */
87
88
if (_InitializeNonplanarityContext(theGraph, I, R) != OK)
89
return NOTOK;
90
91
N = theGraph->N;
92
R = theGraph->IC.r;
93
X = theGraph->IC.x;
94
Y = theGraph->IC.y;
95
W = theGraph->IC.w;
96
97
/* If the root copy is not a root copy of the current vertex I,
98
then the Walkdown terminated because it couldn't find
99
a viable path along a child bicomp, which is Minor A. */
100
101
if (theGraph->V[R - N].DFSParent != I)
102
{
103
theGraph->IC.minorType |= MINORTYPE_A;
104
return OK;
105
}
106
107
/* If W has an externally active pertinent child bicomp, then
108
we've found Minor B */
109
110
if (theGraph->V[W].pertinentBicompList != NIL)
111
{
112
RootId = LCGetPrev(theGraph->BicompLists,
113
theGraph->V[W].pertinentBicompList, NIL);
114
DFSChild = RootId;
115
if (theGraph->V[DFSChild].Lowpoint < I)
116
{
117
theGraph->IC.minorType |= MINORTYPE_B;
118
return OK;
119
}
120
}
121
122
/* Find the highest obstructing X-Y path */
123
124
if (_MarkHighestXYPath(theGraph) != TRUE)
125
return NOTOK;
126
127
Px = theGraph->IC.px;
128
Py = theGraph->IC.py;
129
130
/* If either point of attachment is 'high' (P_x closer to R than X
131
or P_y closer to R than Y along external face), then we've
132
matched Minor C. */
133
134
if (theGraph->G[Px].type == VERTEX_HIGH_RXW ||
135
theGraph->G[Py].type == VERTEX_HIGH_RYW)
136
{
137
theGraph->IC.minorType |= MINORTYPE_C;
138
return OK;
139
}
140
141
/* For Minor D, we search for a path from an internal
142
vertex Z along the X-Y path up to the root R of the bicomp. */
143
144
if (_MarkZtoRPath(theGraph) != OK)
145
return NOTOK;
146
147
if (theGraph->IC.z != NIL)
148
{
149
theGraph->IC.minorType |= MINORTYPE_D;
150
return OK;
151
}
152
153
/* For Minor E, we search for an externally active vertex Z
154
below the points of attachment of the X-Y path */
155
156
Z = _FindExtActivityBelowXYPath(theGraph);
157
if (Z != NIL)
158
{
159
theGraph->IC.z = Z;
160
theGraph->IC.minorType |= MINORTYPE_E;
161
return OK;
162
}
163
164
return NOTOK;
165
}
166
167
/****************************************************************************
168
_InitializeNonplanarityContext()
169
170
This method finds the stopping vertices X and Y, and the pertinent vertex W
171
of a bicomp rooted by vertex R.
172
173
If R is NIL, the routine first determines which bicomp produced non-planarity
174
condition. If the stack is non-empty, then R is on the top of the stack.
175
Otherwise, an unembedded fwdArc from the fwdArcList of vertex I is used in
176
combination with the separatedDFSChildList of I to determine R.
177
178
If the parameter R was not NIL, then this method assumes it must operate
179
only on the bicomp rooted by R, and it also assumes that the caller has
180
not cleared the visited flags in the bicomp, so they are cleared.
181
182
This routine imparts consistent orientation to all vertices in bicomp R
183
since several subroutines count on this. The edge signs are preserved so that
184
the original orientations of all vertices can be restored. If the vertices
185
of the embedding are already consistently oriented, then this operation
186
simply has no effect.
187
188
Finally, in the bicomp R, the vertex types of all non-root vertices on the
189
external face are classified according to whether or not they are closer to
190
the root R than X and Y along the external face paths (R X W) and (R Y W).
191
****************************************************************************/
192
193
int _InitializeNonplanarityContext(graphP theGraph, int I, int R)
194
{
195
int singleBicompMode = (R == NIL) ? FALSE : TRUE;
196
197
// Blank out the isolator context, then assign the input graph reference
198
// and the current vertext I into the context.
199
_ClearIsolatorContext(theGraph);
200
theGraph->IC.v = I;
201
202
// The Walkdown halted on one or more bicomps without embedding all back
203
// edges to descendants of the root(s) of said bicomp(s).
204
// If the bicomp root has not been provided, we now find the root of one such bicomp.
205
if (!singleBicompMode)
206
R = _FindNonplanarityBicompRoot(theGraph);
207
208
// When in singleBicompMode, the bicomp root provided was the one on which
209
// the WalkDown was performed, but in the case of Minor A, the central bicomp
210
// of the minor is at the top of the stack, so R must be changed to that value.
211
else if (sp_NonEmpty(theGraph->theStack))
212
R = _FindNonplanarityBicompRoot(theGraph);
213
214
if (R == NIL)
215
return NOTOK;
216
217
theGraph->IC.r = R;
218
219
// A number of subroutines require the main bicomp of the minor to be
220
// consistently oriented and its visited flags clear.
221
if (_OrientVerticesInBicomp(theGraph, R, 1) != OK)
222
return NOTOK;
223
224
// In singleBicompMode, clear the visited members of all vertex and edge records.
225
if (singleBicompMode)
226
{
227
if (_FillVisitedFlagsInBicomp(theGraph, R, 0) != OK)
228
return NOTOK;
229
}
230
231
// Now we find the active vertices along both external face paths
232
// extending from R.
233
_FindActiveVertices(theGraph, R, &theGraph->IC.x, &theGraph->IC.y);
234
235
// Now, we obtain the pertinent vertex W on the lower external face
236
// path between X and Y (that path that does not include R).
237
theGraph->IC.w = _FindPertinentVertex(theGraph);
238
239
// Now we can classify the vertices along the external face of the bicomp
240
// rooted at R as 'high RXW', 'low RXW', 'high RXY', 'low RXY'
241
if (_SetVertexTypesForMarkingXYPath(theGraph) != OK)
242
return NOTOK;
243
244
// All work is done, so return success
245
return OK;
246
}
247
248
/****************************************************************************
249
_SetVertexTypesForMarkingXYPath()
250
251
Label the vertices along the external face of the bicomp rooted at R as
252
'high RXW', 'low RXW', 'high RXY', 'low RXY'
253
****************************************************************************/
254
255
int _SetVertexTypesForMarkingXYPath(graphP theGraph)
256
{
257
int I, R, X, Y, W, Z, ZPrevLink, ZType;
258
259
// Unpack the context for efficiency of loops
260
I = theGraph->IC.v;
261
R = theGraph->IC.r;
262
X = theGraph->IC.x;
263
Y = theGraph->IC.y;
264
W = theGraph->IC.w;
265
266
// Ensure basic preconditions of this routine are met
267
if (R==NIL || X==NIL || Y==NIL || W==NIL)
268
return NOTOK;
269
270
// Clear the type member of each vertex in the bicomp
271
if (_SetVertexTypeInBicomp(theGraph, R, TYPE_UNKNOWN) != OK)
272
return NOTOK;
273
274
// Traverse from R to W in the X direction
275
ZPrevLink = 1;
276
Z = _GetNextVertexOnExternalFace(theGraph, R, &ZPrevLink);
277
ZType = VERTEX_HIGH_RXW;
278
while (Z != W)
279
{
280
if (Z == X) ZType = VERTEX_LOW_RXW;
281
theGraph->G[Z].type = ZType;
282
Z = _GetNextVertexOnExternalFace(theGraph, Z, &ZPrevLink);
283
}
284
285
// Traverse from R to W in the Y direction
286
ZPrevLink = 0;
287
Z = _GetNextVertexOnExternalFace(theGraph, R, &ZPrevLink);
288
ZType = VERTEX_HIGH_RYW;
289
while (Z != W)
290
{
291
if (Z == Y) ZType = VERTEX_LOW_RYW;
292
theGraph->G[Z].type = ZType;
293
Z = _GetNextVertexOnExternalFace(theGraph, Z, &ZPrevLink);
294
}
295
296
return OK;
297
}
298
299
/****************************************************************************
300
_FindNonplanarityBicompRoot()
301
302
This procedure finds the root copy R of the current vertex on which the
303
Walkdown failed (whether it failed while traversing the bicomp rooted by
304
R or some descendant bicomp is determined later).
305
306
We iterate the forward cycle edges of the vertex I looking for a forward
307
edge (I, W) that was not embedded. Once it is found, we figure out which
308
bicomp rooted by a root copy of I contains W or contains a DFS ancestor of W.
309
310
This turns out to be an easy test. The desired bicomp is rooted by the DFS
311
tree edge (I, C) with the largest value of C that does not exceed W. C is
312
a DFS ancestor of Z.
313
314
Return: The desired root copy, or NIL on error.
315
****************************************************************************/
316
317
int _FindNonplanarityBicompRoot(graphP theGraph)
318
{
319
int R, tempChild, fwdArc, W=NIL, C=NIL, I=theGraph->IC.v;
320
321
/* If the stack is non-empty, then the Walkdown stopped on a descendant
322
bicomp, not one rooted by I. We need to get that root before the
323
stack is destroyed by other routines. */
324
325
if (sp_NonEmpty(theGraph->theStack))
326
{
327
int e;
328
329
sp_Pop2(theGraph->theStack, R, e);
330
return R;
331
}
332
333
/* Obtain the forward arc of an unembedded back edge from I to one of its
334
descendants (edges are removed from the forward arc list as they are
335
embedded, so the list will be empty if all edges were embedded). */
336
337
if ((fwdArc = theGraph->V[I].fwdArcList) == NIL)
338
return NIL;
339
340
W = theGraph->G[fwdArc].v;
341
342
/* Find the greatest DFS child C of I that is less than W. This will
343
give us the ancestor of W that is a child of I. Since the
344
ancestors of I have not been processed by the planarity algorithm,
345
the separatedDFSChildList of I contains all the children of I. */
346
347
tempChild = theGraph->V[I].separatedDFSChildList;
348
349
while (tempChild != NIL)
350
{
351
if (tempChild > C && tempChild < W)
352
C = tempChild;
353
354
tempChild = LCGetNext(theGraph->DFSChildLists,
355
theGraph->V[I].separatedDFSChildList, tempChild);
356
}
357
358
if (C == NIL) return NIL;
359
360
/* The root vertex of a bicomp rooted by edge (I, C) is located at
361
position C+N in our data structures */
362
363
R = C + theGraph->N;
364
return R;
365
}
366
367
/****************************************************************************
368
_FindActiveVertices()
369
370
Descends from the root of a bicomp R along both external face paths (which
371
are indicated by the first and last arcs in R's adjacency list), returning
372
the first active vertex appearing in each direction.
373
****************************************************************************/
374
375
void _FindActiveVertices(graphP theGraph, int R, int *pX, int *pY)
376
{
377
int XPrevLink=1, YPrevLink=0, I=theGraph->IC.v;
378
379
*pX = _GetNextVertexOnExternalFace(theGraph, R, &XPrevLink);
380
*pY = _GetNextVertexOnExternalFace(theGraph, R, &YPrevLink);
381
382
while (_VertexActiveStatus(theGraph, *pX, I) == VAS_INACTIVE)
383
*pX = _GetNextVertexOnExternalFace(theGraph, *pX, &XPrevLink);
384
385
while (_VertexActiveStatus(theGraph, *pY, I) == VAS_INACTIVE)
386
*pY = _GetNextVertexOnExternalFace(theGraph, *pY, &YPrevLink);
387
}
388
389
/****************************************************************************
390
_FindPertinentVertex()
391
392
Get the first vertex after x. Since x was obtained using a prevlink of 1 on r,
393
we use the same prevlink so we don't go back to R.
394
Then, we proceed around the lower path until we find a vertex W that either
395
has pertinent child bicomps or is directly adjacent to the current vertex I.
396
****************************************************************************/
397
398
int _FindPertinentVertex(graphP theGraph)
399
{
400
int W=theGraph->IC.x, WPrevLink=1;
401
402
W = _GetNextVertexOnExternalFace(theGraph, W, &WPrevLink);
403
404
while (W != theGraph->IC.y)
405
{
406
if (PERTINENT(theGraph, W))
407
return W;
408
409
W = _GetNextVertexOnExternalFace(theGraph, W, &WPrevLink);
410
}
411
412
return NIL;
413
}
414
415
/****************************************************************************
416
_PopAndUnmarkVerticesAndEdges()
417
418
Pop all vertex/edge pairs from the top of the stack up to a terminating
419
vertex Z and mark as unvisited. If Z is NIL, then all vertex/edge pairs
420
are popped and marked as unvisited.
421
The stackBottom indicates where other material besides the vertex/edge
422
pairs may appear.
423
****************************************************************************/
424
425
int _PopAndUnmarkVerticesAndEdges(graphP theGraph, int Z, int stackBottom)
426
{
427
int V, e;
428
429
// Pop vertex/edge pairs until all have been popped from the stack,
430
// and all that's left is what was under the pairs, or until...
431
while (sp_GetCurrentSize(theGraph->theStack) > stackBottom)
432
{
433
sp_Pop(theGraph->theStack, V);
434
435
// If we pop the terminating vertex Z, then put it back and break
436
if (V == Z)
437
{
438
sp_Push(theGraph->theStack, V);
439
break;
440
}
441
442
// Otherwise, pop the edge part of the vertex/edge pair
443
sp_Pop(theGraph->theStack, e);
444
445
// Now unmark the vertex and edge (i.e. revert to "unvisited")
446
theGraph->G[V].visited = 0;
447
theGraph->G[e].visited = 0;
448
theGraph->G[gp_GetTwinArc(theGraph, e)].visited = 0;
449
}
450
451
return OK;
452
}
453
454
/****************************************************************************
455
_MarkHighestXYPath()
456
457
An X-Y path in the bicomp rooted by R is a path attached to the external
458
face at points Px and Py that separates W from R such that a back edge (R, W)
459
cannot be embedded within the bicomp. Recall that R is a root copy of I, so
460
(R, W) is the representative of (I, W). Also, note that W is pertinent if
461
either W *or* one of its descendants in a separate bicomp has, in the input
462
graph, a back edge to I.
463
464
If no X-Y path separating W from R is found, then NOTOK is returned because
465
the proof of correctness guarantees that one exists (although this routine
466
can also be used to help test for the existence of an X-Y path, and NOTOK
467
means 'no' in that case).
468
469
The desired output is to set the 'visited' flags of the X-Y path with
470
highest points of attachment to the external face (i.e. the points of
471
attachment that are closest to R along the external face). This includes
472
marking both the vertices and edges along the X-Y path.
473
474
Previously, during non-planarity context initialization, the vertices along
475
the external face (other than R and W) have been classified as 'high RXW',
476
'low RXW', 'high RXY', or 'low RXY'. Once the vertices have been categorized,
477
we proceed with trying to set the visitation flags in the way described above.
478
First, we remove all edges incident to R except the two edges that join R to
479
the external face. The result is that R and its two remaining edges are a
480
'corner' in the external face but also in a single proper face whose boundary
481
includes the X-Y path with the highest attachment points. Thus, we simply need
482
to walk this proper face to find the desired X-Y path. Note, however, that the
483
resulting face boundary may have attached cut vertices. Any such separable
484
component contains a vertex neighbor of R, but the edge to R has been
485
temporarily removed. The algorithm removes loop of vertices and edges along
486
the proper face so that only a path is identified.
487
488
To walk the proper face containing R, we begin with its first arc successor,
489
then take the *predecessor* arc at every subsequent corner. For each vertex,
490
we mark as visited the vertex as well as the edge used to enter the vertex
491
(except for the edge used to enter the RXW vertex). We also push the visited
492
vertices and edges onto a stack.
493
494
As we walk the proper face, we keep track of the last vertex P_x we visited of
495
type RXW (high or low). Each time we encounter a vertex of type RXW (high or
496
low), we pop the stack and unmark all of the edges and vertices visited because
497
they were part of a path parallel to the external face that does not obstruct
498
W from reaching R within the bicomp. If we encounter vertex W, then there is
499
no obstructing X-Y path since we removed only edges incident to R, so we pop
500
the stack unmarking everything then return NOTOK as stated above. If we
501
encounter a vertex Z previously visited, then we pop the stack, unmarking the
502
vertices and edges popped, until we find the prior occurence of Z on the stack.
503
504
Otherwise, the first time we encounter a vertex P_y of type 'RYW', we stop
505
because the obstructing X-Y path has been marked visited and its points of
506
connection P_x and P_y have been found.
507
508
Once the X-Y path is identified, we restore the edges incident to R.
509
510
This method uses the stack, but it preserves any prior content.
511
The stack space used is no greater than 3N. The first N accounts for removing
512
the edges incident to R. The other 2N accounts for the fact that each
513
iteration of the main loop visits a vertex, pushing its index and the
514
location of an edge record. If a vertex is encountered that is already
515
on the stack, then it is not pushed again (and in fact part of the stack
516
is removed).
517
518
Returns TRUE if the X-Y path is found, FALSE otherwise.
519
In debug mode it can also return NOTOK. This is equivalent to FALSE, but
520
NOTOK is better for documenting the error condition in the code, and
521
it produces a debug message. Also, in many cases the equivalent-to-FALSE
522
result is an error condition for the caller, so NOTOK usually percolates up.
523
****************************************************************************/
524
525
int _MarkHighestXYPath(graphP theGraph)
526
{
527
int J, Z;
528
int R, X, Y, W;
529
int stackBottom1, stackBottom2;
530
531
/* Initialization */
532
533
R = theGraph->IC.r;
534
X = theGraph->IC.x;
535
Y = theGraph->IC.y;
536
W = theGraph->IC.w;
537
theGraph->IC.px = theGraph->IC.py = NIL;
538
539
/* Save the stack bottom before we start hiding internal edges, so
540
we will know how many edges to restore */
541
542
stackBottom1 = sp_GetCurrentSize(theGraph->theStack);
543
544
/* Remove the internal edges incident to vertex R */
545
546
if (_HideInternalEdges(theGraph, R) != OK)
547
return NOTOK;
548
549
/* Now we're going to use the stack to collect the vertices of potential
550
* X-Y paths, so we need to store where the hidden internal edges are
551
* located because we must, at times, pop the collected vertices if
552
* the path being collected doesn't work out. */
553
554
stackBottom2 = sp_GetCurrentSize(theGraph->theStack);
555
556
/* Walk the proper face containing R to find and mark the highest
557
X-Y path. Note that if W is encountered, then there is no
558
intervening X-Y path, so we would return FALSE in that case. */
559
560
Z = R;
561
// This setting of J is the arc equivalent of prevLink=1
562
// As loop progresses, J indicates the arc used to enter Z, not the exit arc
563
J = gp_GetLastArc(theGraph, R);
564
565
while (theGraph->G[Z].type != VERTEX_HIGH_RYW &&
566
theGraph->G[Z].type != VERTEX_LOW_RYW)
567
{
568
/* Advance J and Z along the proper face containing R */
569
570
J = gp_GetPrevArcCircular(theGraph, J);
571
Z = theGraph->G[J].v;
572
J = gp_GetTwinArc(theGraph, J);
573
574
/* If Z is already visited, then pop everything since the last time
575
we visited Z because its all part of a separable component. */
576
577
if (theGraph->G[Z].visited)
578
{
579
if (_PopAndUnmarkVerticesAndEdges(theGraph, Z, stackBottom2) != OK)
580
return NOTOK;
581
}
582
583
/* If we have not visited this vertex before... */
584
585
else
586
{
587
/* If we find W, then there is no X-Y path. Never happens
588
for Kuratowski subgraph isolator, but this routine is
589
also used to test for certain X-Y paths.
590
So, we clean up and bail out in that case. */
591
592
if (Z == W)
593
{
594
if (_PopAndUnmarkVerticesAndEdges(theGraph, NIL, stackBottom2) != OK)
595
return NOTOK;
596
break;
597
}
598
599
/* If we found another vertex along the RXW path, then blow off
600
all the vertices we visited so far because they're not part of
601
the obstructing path */
602
603
if (theGraph->G[Z].type == VERTEX_HIGH_RXW ||
604
theGraph->G[Z].type == VERTEX_LOW_RXW)
605
{
606
theGraph->IC.px = Z;
607
if (_PopAndUnmarkVerticesAndEdges(theGraph, NIL, stackBottom2) != OK)
608
return NOTOK;
609
}
610
611
/* Push the current vertex onto the stack of vertices visited
612
since the last RXW vertex was encountered */
613
614
sp_Push(theGraph->theStack, J);
615
sp_Push(theGraph->theStack, Z);
616
617
/* Mark the vertex Z as visited as well as its edge of entry
618
(except the entry edge for P_x).*/
619
620
theGraph->G[Z].visited = 1;
621
if (Z != theGraph->IC.px)
622
{
623
theGraph->G[J].visited = 1;
624
theGraph->G[gp_GetTwinArc(theGraph, J)].visited = 1;
625
}
626
627
/* If we found an RYW vertex, then we have successfully finished
628
identifying the highest X-Y path, so we record the point of
629
attachment and break the loop. */
630
631
if (theGraph->G[Z].type == VERTEX_HIGH_RYW ||
632
theGraph->G[Z].type == VERTEX_LOW_RYW)
633
{
634
theGraph->IC.py = Z;
635
break;
636
}
637
}
638
}
639
640
/* Remove any remaining vertex-edge pairs on the top of the stack, then
641
Restore the internal edges incident to R that were previously removed. */
642
643
sp_SetCurrentSize(theGraph->theStack, stackBottom2);
644
645
if (_RestoreInternalEdges(theGraph, stackBottom1) != OK)
646
return NOTOK;
647
648
/* Return the result */
649
650
return theGraph->IC.py==NIL ? FALSE : TRUE;
651
}
652
653
/****************************************************************************
654
_MarkZtoRPath()
655
656
This function assumes that _MarkHighestXYPath() has already been called,
657
which marked as visited the vertices and edges along the X-Y path.
658
659
We begin at the point of attachment P_x, take the last arc and traverse
660
the predecessor arcs until we find one marked visited, which leads to the
661
first internal vertex along the X-Y path. We begin with this vertex
662
(and its edge of entry), and we run until we find P_y. For each internal
663
vertex Z and its edge of entry ZPrevArc, we take the predecessor edge record
664
of ZPrevArc. This is called ZNextArc. If ZNextArc is marked visited
665
then it is along the X-Y path, so we use it to exit Z and go to the next
666
vertex on the X-Y path.
667
668
If ZNextArc is not visited, then when _MarkHighestXYPath() ran, it exited
669
Z from ZNextArc, then eventually reentered Z. In other words, Z became a
670
cut vertex when we removed the internal edges incident to R. Thus, ZNextArc
671
indicates the first edge in an internal path to R.
672
673
When we find an unvisited ZNextArc, we stop running the X-Y path and instead
674
begin marking the Z to R path. We move to successive vertices using a
675
twin arc then its predecessor arc in the adjacency list, only this time
676
we have not removed the internal edges incident to R, so this technique does
677
eventually lead us all the way to R.
678
679
If we do not find an unvisited ZNextArc for any vertex Z on the X-Y path and
680
inside the bicomp, then there is no Z to R path, so we return.
681
****************************************************************************/
682
683
int _MarkZtoRPath(graphP theGraph)
684
{
685
int ZPrevArc, ZNextArc, Z, R, Px, Py;
686
687
/* Initialize */
688
689
R = theGraph->IC.r;
690
Px = theGraph->IC.px;
691
Py = theGraph->IC.py;
692
theGraph->IC.z = NIL;
693
694
/* Begin at Px and search its adjacency list for the edge leading to
695
the first internal vertex of the X-Y path. */
696
697
Z = Px;
698
ZNextArc = gp_GetLastArc(theGraph, Z);
699
while (ZNextArc != gp_GetFirstArc(theGraph, Z))
700
{
701
if (theGraph->G[ZNextArc].visited) break;
702
703
ZNextArc = gp_GetPrevArc(theGraph, ZNextArc);
704
}
705
706
if (!theGraph->G[ZNextArc].visited)
707
return NOTOK;
708
709
/* For each internal vertex Z, determine whether it has a path to root. */
710
711
while (theGraph->G[ZNextArc].visited)
712
{
713
ZPrevArc = gp_GetTwinArc(theGraph, ZNextArc);
714
ZNextArc = gp_GetPrevArcCircular(theGraph, ZPrevArc);
715
}
716
717
ZPrevArc = gp_GetTwinArc(theGraph, ZNextArc);
718
Z = theGraph->G[ZPrevArc].v;
719
720
/* If there is no Z to R path, return */
721
722
if (Z == Py) return OK;
723
724
/* Otherwise, store Z in the isolation context */
725
726
theGraph->IC.z = Z;
727
728
/* Walk the proper face starting with (Z, ZNextArc) until we reach R, marking
729
the vertices and edges encountered along the way, then Return OK. */
730
731
while (Z != R)
732
{
733
/* If we ever encounter a non-internal vertex (other than the root R),
734
then corruption has occured, so we return NOTOK */
735
736
if (theGraph->G[Z].type != TYPE_UNKNOWN)
737
return NOTOK;
738
739
/* Go to the next vertex indicated by ZNextArc */
740
741
Z = theGraph->G[ZNextArc].v;
742
743
/* Mark the next vertex and the edge leading to it as visited. */
744
745
theGraph->G[ZNextArc].visited = 1;
746
theGraph->G[ZPrevArc].visited = 1;
747
theGraph->G[Z].visited = 1;
748
749
/* Go to the next edge in the proper face */
750
751
ZNextArc = gp_GetPrevArcCircular(theGraph, ZPrevArc);
752
ZPrevArc = gp_GetTwinArc(theGraph, ZNextArc);
753
}
754
755
/* Found Z to R path, so indicate as much to caller */
756
757
return OK;
758
}
759
760
/****************************************************************************
761
_FindExtActivityBelowXYPath()
762
763
Get an externally active vertex along the lower external face path between
764
the points of attachment P_x and P_y of a 'low' X-Y Path.
765
NOTE: By the time this function is called, Px and Py have already been found
766
to be at or below X and Y.
767
****************************************************************************/
768
769
int _FindExtActivityBelowXYPath(graphP theGraph)
770
{
771
int Z=theGraph->IC.px, ZPrevLink=1,
772
Py=theGraph->IC.py, I=theGraph->IC.v;
773
774
Z = _GetNextVertexOnExternalFace(theGraph, Z, &ZPrevLink);
775
776
while (Z != Py)
777
{
778
if (_VertexActiveStatus(theGraph, Z, I) == VAS_EXTERNAL)
779
return Z;
780
781
Z = _GetNextVertexOnExternalFace(theGraph, Z, &ZPrevLink);
782
}
783
784
return NIL;
785
}
786
787