Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/graphs/planarity/graphTests.c
4072 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 GRAPHTEST_C
46
47
#include "graph.h"
48
#include "stack.h"
49
50
/* Private function declarations */
51
52
int _TestPath(graphP theGraph, int U, int V);
53
int _TryPath(graphP theGraph, int J, int V);
54
void _MarkPath(graphP theGraph, int J);
55
int _TestSubgraph(graphP theSubgraph, graphP theGraph);
56
57
int _CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph);
58
int _CheckEmbeddingFacialIntegrity(graphP theGraph);
59
int _CheckObstructionIntegrity(graphP theGraph, graphP origGraph);
60
61
int _CheckKuratowskiSubgraphIntegrity(graphP theGraph);
62
int _CheckOuterplanarObstructionIntegrity(graphP theGraph);
63
64
int _CheckAllVerticesOnExternalFace(graphP theGraph);
65
void _MarkExternalFaceVertices(graphP theGraph, int startVertex);
66
67
/********************************************************************
68
gp_TestEmbedResultIntegrity()
69
70
This function tests the integrity of the graph result returned
71
from gp_Embed().
72
73
The caller of gp_Embed() does not have to save the original graph
74
because, for efficiency, gp_Embed() operates on the input graph.
75
However, to test the integrity of the result relative to the input,
76
a copy of the input graph is required.
77
78
Modules that extend/alter the behavior of gp_Embed() beyond the
79
core planarity embedder and planarity obstruction isolator should
80
also provide overriding integrity test routines appropriate to the
81
extension algorithm.
82
83
The main method first calls gp_SortVertices on theGraph, if the
84
origGraph is not in DFI order (the common case). Therefore,
85
extension integrity tests can count on a consistent numbering
86
between theGraph and the origGraph, either DFI order or pre-DFS
87
order if that is the state of the origGraph.
88
89
After all tests, the main method ensures theGraph is restored to
90
DFI order by invoking gp_SortVertices if needed, thus ensuring
91
that theGraph has the documented post-condition of gp_Embed().
92
93
For an embedResult of OK, fpCheckEmbeddingIntegrity is invoked.
94
The core planarity implementation does a face walk of all faces
95
of the embedding. It ensures that all edges were used in the face
96
walk and that the right number of faces exist for the number of
97
vertices and edges. Also, we ensure that all adjacencies expressed
98
in the original graph still exist in the result graph.
99
100
For an embedResult of NONEMBEDDABLE, fpCheckObstructionIntegrity
101
is invoked. The core planarity algorithm checks that the result
102
graph is homeomorphic to K5 or K3,3 and that it is in fact a
103
subgraph of the input graph. Other algorithms use overloads to
104
make appropriate checks.
105
106
Returns NOTOK on integrity check failure or embedResult of NOTOK
107
OK for successful integrity check of OK embedResult
108
NONEMBEDDABLE for successful integrity check of an
109
embedResult of NONEMBEDDABLE
110
********************************************************************/
111
112
int gp_TestEmbedResultIntegrity(graphP theGraph, graphP origGraph, int embedResult)
113
{
114
int RetVal = embedResult;
115
116
if (theGraph == NULL || origGraph == NULL)
117
return NOTOK;
118
119
if (embedResult == OK)
120
{
121
RetVal = theGraph->functions.fpCheckEmbeddingIntegrity(theGraph, origGraph);
122
}
123
else if (embedResult == NONEMBEDDABLE)
124
{
125
RetVal = theGraph->functions.fpCheckObstructionIntegrity(theGraph, origGraph);
126
}
127
128
if (RetVal == OK)
129
RetVal = embedResult;
130
131
return RetVal;
132
}
133
134
/********************************************************************
135
_CheckEmbeddingIntegrity()
136
137
The core planarity implementation does a face walk of all faces
138
of the embedding. It ensures that all edges were used in the face
139
walk and that the right number of faces exist for the number of
140
vertices and edges. Also, we ensure that all adjacencies expressed
141
in the original graph still exist in the result graph, accounting
142
for the fact that the result graph is sorted by DFI, but the input
143
may or may not be sorted by DFI.
144
145
returns OK if all integrity tests passed, NOTOK otherwise
146
********************************************************************/
147
148
int _CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph)
149
{
150
if (theGraph == NULL || origGraph == NULL)
151
return NOTOK;
152
153
if (_TestSubgraph(theGraph, origGraph) != TRUE)
154
return NOTOK;
155
156
if (_TestSubgraph(origGraph, theGraph) != TRUE)
157
return NOTOK;
158
159
if (_CheckEmbeddingFacialIntegrity(theGraph) != OK)
160
return NOTOK;
161
162
if (theGraph->embedFlags == EMBEDFLAGS_OUTERPLANAR)
163
{
164
if (_CheckAllVerticesOnExternalFace(theGraph) != OK)
165
return NOTOK;
166
}
167
168
return OK;
169
}
170
171
/********************************************************************
172
_CheckEmbeddingFacialIntegrity()
173
174
This function traverses all faces of a graph structure containing
175
the planar embedding that results from gp_Embed(). The algorithm
176
begins by placing all of the graph's arcs onto a stack and marking
177
all of them as unvisited. For each arc popped, if it is visited,
178
it is immediately discarded and the next arc is popped. Popping an
179
unvisited arc J begins a face traversal. We move to the true twin
180
arc K of J, and obtain its successor arc L. This amounts to
181
always going clockwise or counterclockwise (depending on how the
182
graph is drawn on the plane, or alternately whether one is above
183
or below the plane). This traversal continues until we make it
184
back to J. Each arc along the way is marked as visited. Further,
185
if L has been visited, then there is an error since an arc can only
186
appear in one face (the twin arc appears in a separate face, which
187
is traversed in the opposing direction).
188
If this algorithm succeeds without double visiting any arcs, and it
189
produces the correct face count according to Euler's formula, then
190
the embedding has all vertices oriented the same way.
191
NOTE: In disconnected graphs, the face reader counts the external
192
face of each connected component. So, we adjust the face
193
count by subtracting one for each component, then we add one
194
to count the external face shared by all components.
195
********************************************************************/
196
197
int _CheckEmbeddingFacialIntegrity(graphP theGraph)
198
{
199
stackP theStack = theGraph->theStack;
200
int I, e, J, JTwin, K, L, NumFaces, connectedComponents;
201
202
if (theGraph == NULL)
203
return NOTOK;
204
205
/* The stack need only contain 2M entries, one for each edge record. With
206
max M at 3N, this amounts to 6N integers of space. The embedding
207
structure already contains this stack, so we just make sure it
208
starts out empty. */
209
210
sp_ClearStack(theStack);
211
212
/* Push all arcs and set them to unvisited */
213
214
for (e=0, J=theGraph->edgeOffset; e < theGraph->M + sp_GetCurrentSize(theGraph->edgeHoles); e++, J+=2)
215
{
216
if (theGraph->G[J].v == NIL)
217
continue;
218
219
sp_Push(theStack, J);
220
theGraph->G[J].visited = 0;
221
JTwin = gp_GetTwinArc(theGraph, J);
222
sp_Push(theStack, JTwin);
223
theGraph->G[JTwin].visited = 0;
224
}
225
226
// There are M edges, so we better have pushed 2M arcs just now
227
// i.e. testing that the continue above skipped only edge holes
228
if (sp_GetCurrentSize(theStack) != 2*theGraph->M)
229
return NOTOK;
230
231
232
/* Read faces until every arc is used */
233
234
NumFaces = 0;
235
while (sp_NonEmpty(theStack))
236
{
237
/* Get an arc; if it has already been used by a face, then
238
don't use it to traverse a new face */
239
sp_Pop(theStack, J);
240
if (theGraph->G[J].visited) continue;
241
242
L = NIL;
243
JTwin = J;
244
while (L != J)
245
{
246
K = gp_GetTwinArc(theGraph, JTwin);
247
L = gp_GetNextArcCircular(theGraph, K);
248
if (theGraph->G[L].visited)
249
return NOTOK;
250
theGraph->G[L].visited++;
251
JTwin = L;
252
}
253
NumFaces++;
254
}
255
256
/* Count the external face once rather than once per connected component;
257
each connected component is detected by the fact that it has no
258
DFS parent, except in the case of isolated vertices, no face was counted
259
so we do not subtract one. */
260
261
for (I=connectedComponents=0; I < theGraph->N; I++)
262
if (theGraph->V[I].DFSParent == NIL)
263
{
264
if (gp_GetVertexDegree(theGraph, I) > 0)
265
NumFaces--;
266
connectedComponents++;
267
}
268
269
NumFaces++;
270
271
/* Test number of faces using the extended Euler's formula.
272
For connected components, Euler's formula is f=m-n+2, but
273
for disconnected graphs it is extended to f=m-n+1+c where
274
c is the number of connected components.*/
275
276
return NumFaces == theGraph->M - theGraph->N + 1 + connectedComponents
277
? OK : NOTOK;
278
}
279
280
/********************************************************************
281
_CheckAllVerticesOnExternalFace()
282
283
Determines whether or not any vertices have been embedded within
284
the bounding cycle of the external face.
285
The input graph may be disconnected, so this routine walks the
286
external face starting at each vertex with no DFSParent.
287
288
return OK if all vertices visited on external face walks, NOTOK otherwise
289
********************************************************************/
290
291
int _CheckAllVerticesOnExternalFace(graphP theGraph)
292
{
293
int I;
294
295
// Mark all vertices unvisited
296
for (I=0; I < theGraph->N; I++)
297
theGraph->G[I].visited = 0;
298
299
// For each connected component, walk its external face and
300
// mark the vertices as visited
301
for (I=0; I < theGraph->N; I++)
302
{
303
if (theGraph->V[I].DFSParent == NIL)
304
_MarkExternalFaceVertices(theGraph, I);
305
}
306
307
// If any vertex is unvisited, then the embedding is not an outerplanar
308
// embedding, so we return NOTOK
309
for (I=0; I < theGraph->N; I++)
310
if (!theGraph->G[I].visited)
311
{
312
return NOTOK;
313
}
314
315
// All vertices were found on external faces of the connected components
316
// so the embedding is an outerplanar embedding and we return OK
317
return OK;
318
}
319
320
/********************************************************************
321
_MarkExternalFaceVertices()
322
323
Walks the external face of the connected component containing the
324
start vertex, and marks the visited flag of all vertices found.
325
The start vertex is assumed to be on the external face.
326
This method assumed the embedding integrity has already been
327
verified to be correct.
328
This method correctly handles components that have cut vertices,
329
i.e. it does not assume that the outer face is a simple cycle;
330
it only assumes that all vertices are reachable by walking a
331
single face that starts with startVertex.
332
********************************************************************/
333
334
void _MarkExternalFaceVertices(graphP theGraph, int startVertex)
335
{
336
int nextVertex = startVertex;
337
int Jout = gp_GetFirstArc(theGraph, nextVertex);
338
int Jin;
339
340
// Handle the case of an isolated vertex
341
if (!gp_IsArc(theGraph, Jout))
342
{
343
theGraph->G[startVertex].visited = 1;
344
return;
345
}
346
347
// Process a non-trivial connected component
348
do {
349
theGraph->G[nextVertex].visited = 1;
350
351
// The arc out of the vertex just visited points to the next vertex
352
nextVertex = theGraph->G[Jout].v;
353
354
// Arc used to enter the next vertex is needed so we can get the
355
// next edge in rotation order.
356
// Note: for bicomps, first and last arcs of all external face vertices
357
// indicate the edges that hold them to the external face
358
// But _JoinBicomps() has already occurred, so cut vertices
359
// will have external face edges other than the first and last arcs
360
// Hence we need this more sophisticated traversal method
361
Jin = gp_GetTwinArc(theGraph, Jout);
362
363
// Now we get the next arc in rotation order as the new arc out to the
364
// vertex after nextVertex. This sets us up for the next iteration.
365
// Note: We cannot simply follow the chain of nextVertex first arcs
366
// as we started out doing at the top of this method. This is
367
// because we are no longer dealing with bicomps only.
368
// Since _JoinBicomps() has already been invoked, there may now
369
// be cut vertices on the external face whose adjacency lists
370
// contain external face arcs in positions other than the first and
371
// and last arcs. We will visit those vertices multiple times,
372
// which is OK (just that we have to explain why we're calculating
373
// jout in this way).
374
Jout = gp_GetNextArcCircular(theGraph, Jin);
375
376
// Now things get really interesting. The DFS root (startVertex) may
377
// itself be a cut vertex to which multiple bicomps have been joined.
378
// So we cannot simply stop when the external face walk gets back to
379
// startVertex. We must actually get back to startVertex using its
380
// last arc. This ensures that we've looped down into all the DFS
381
// subtrees rooted at startVertex and walked their external faces.
382
383
// Since we started the whole external face walk with the first arc
384
// of startVertex, we need to proceed until we reenter startVertex
385
// using its last arc.
386
387
} while (Jin != gp_GetLastArc(theGraph, startVertex));
388
}
389
390
391
/********************************************************************
392
_CheckObstructionIntegrity()
393
394
Returns OK if theGraph is a subgraph of origGraph and it contains
395
an allowed homeomorph, and NOTOK otherwise.
396
397
For core planarity, the allowed homeomorphs are K_5 or K_{3,3}
398
399
Extension modules may overload this method to implement different
400
tests. For example, K_{3,3} search allows only K_{3,3} and
401
outerplanarity allows only K_4 or K_{2,3}.
402
********************************************************************/
403
404
int _CheckObstructionIntegrity(graphP theGraph, graphP origGraph)
405
{
406
if (theGraph == NULL || origGraph == NULL)
407
return NOTOK;
408
409
if (_TestSubgraph(theGraph, origGraph) != TRUE)
410
{
411
return NOTOK;
412
}
413
414
if (theGraph->embedFlags == EMBEDFLAGS_PLANAR)
415
return _CheckKuratowskiSubgraphIntegrity(theGraph);
416
417
else if (theGraph->embedFlags == EMBEDFLAGS_OUTERPLANAR)
418
return _CheckOuterplanarObstructionIntegrity(theGraph);
419
420
return NOTOK;
421
}
422
423
/********************************************************************
424
_getImageVertices()
425
426
Count the number of vertices of each degree and find the locations of
427
the image vertices (also sometimes called the corners of the obstruction).
428
An image vertex is a vertex of degree three or higher because degree
429
2 vertices are generally internal to the paths between the image
430
vertices.
431
432
The notable exception is K_{2,3}, an obstruction to outerplanarity.
433
This routine does not know the obstruction it is looking for, so the
434
caller must decide whether there are any degree 2 vertices that should
435
be added to imageVerts.
436
437
Return NOTOK if any vertex of degree 1 or higher than the max is found
438
NOTOK if more than the max number of image vertices is found.
439
Return OK otherwise.
440
********************************************************************/
441
442
int _getImageVertices(graphP theGraph, int *degrees, int maxDegree,
443
int *imageVerts, int maxNumImageVerts)
444
{
445
int I, imageVertPos, degree;
446
447
for (I = 0; I <= maxDegree; I++)
448
degrees[I] = 0;
449
450
for (I = 0; I < maxNumImageVerts; I++)
451
imageVerts[I] = NIL;
452
453
imageVertPos = 0;
454
455
for (I = 0; I < theGraph->N; I++)
456
{
457
degree = gp_GetVertexDegree(theGraph, I);
458
if (degree == 1)
459
return NOTOK;
460
if (degree > maxDegree)
461
return NOTOK;
462
463
degrees[degree]++;
464
465
if (imageVertPos < maxNumImageVerts && degree > 2)
466
imageVerts[imageVertPos++] = I;
467
else if (degree > 2)
468
return NOTOK;
469
}
470
471
return OK;
472
}
473
474
/********************************************************************
475
_TestForCompleteGraphObstruction()
476
477
theGraph - the graph to test
478
numVerts - the number of image vertices (corners) of the complete
479
graph homeomorph being tested for (e.g. 5 for a K5)
480
degrees - array of counts of the number of vertices of each degree
481
given by the array index. Only valid up to numVerts-1
482
imageVerts - the vertices of degree numVerts-1
483
484
This routine tests whether theGraph is a K_{numVerts} homeomorph for
485
numVerts >= 4.
486
487
returns FALSE if numVerts < 4,
488
if theGraph has other than numVerts image vertices
489
if theGraph contains other than degree 2 vertices plus
490
the image vertices
491
if any pair of image vertices lacks a connecting path
492
if any degree two vertices are not in the connecting paths
493
TRUE otherwise
494
********************************************************************/
495
496
int _TestForCompleteGraphObstruction(graphP theGraph, int numVerts,
497
int *degrees, int *imageVerts)
498
{
499
int I, J;
500
501
// We need to make sure we have numVerts vertices of degree numVerts-1
502
// For example, if numVerts==5, then we're looking for a K5, so we
503
// need to have degrees[4] == 5 (5 vertices of degree 4)
504
if (degrees[numVerts-1] != numVerts)
505
return FALSE;
506
507
// All vertices need to be degree 0, degree 2 or degree numVerts-1
508
if (degrees[0]+degrees[2]+degrees[numVerts-1] != theGraph->N)
509
return FALSE;
510
511
// We clear all the vertex visited flags
512
for (I = 0; I < theGraph->N; I++)
513
theGraph->G[I].visited = 0;
514
515
// For each pair of image vertices, we test that there is a path
516
// between the two vertices. If so, the visited flags of the
517
// internal vertices along the path are marked
518
//
519
for (I = 0; I < numVerts; I++)
520
for (J = 0; J < numVerts; J++)
521
if (I != J)
522
if (_TestPath(theGraph, imageVerts[I],
523
imageVerts[J]) != TRUE)
524
return FALSE;
525
526
// The visited flags should have marked only degree two vertices,
527
// so for every marked vertex, we subtract one from the count of
528
// the degree two vertices.
529
for (I = 0; I < theGraph->N; I++)
530
if (theGraph->G[I].visited)
531
degrees[2]--;
532
533
/* If every degree 2 vertex is used in a path between image
534
vertices, then there are no extra pieces of the graph
535
in theGraph. Specifically, the prior tests identify
536
a K_5 and ensure that nothing else could exist in the
537
graph except extra degree 2 vertices, which must be
538
joined in a cycle so that all are degree 2. */
539
540
return degrees[2] == 0 ? TRUE : FALSE;
541
}
542
543
/********************************************************************
544
_TestForK33GraphObstruction()
545
546
theGraph - the graph to test
547
degrees - array of counts of the number of vertices of each degree
548
given by the array index. Only valid up to numVerts-1
549
imageVerts - the degree 3 vertices of the K3,3 homeomorph
550
551
This routine tests whether theGraph is a K_{3,3} homeomorph.
552
553
returns TRUE if so, FALSE if not
554
********************************************************************/
555
556
int _TestForK33GraphObstruction(graphP theGraph, int *degrees, int *imageVerts)
557
{
558
int I, imageVertPos, temp, success;
559
560
if (degrees[4] != 0)
561
return FALSE;
562
563
if (degrees[3] != 6)
564
return FALSE;
565
566
/* Partition the six image vertices into two sets of 3
567
(or report failure) */
568
569
for (imageVertPos = 3; imageVertPos < 6; imageVertPos++)
570
{
571
I = success = 0;
572
do {
573
if (_TestPath(theGraph, imageVerts[imageVertPos], imageVerts[0]) == TRUE)
574
{
575
success = TRUE;
576
break;
577
}
578
579
I++;
580
temp = imageVerts[I];
581
imageVerts[I] = imageVerts[imageVertPos];
582
imageVerts[imageVertPos] = temp;
583
} while (I < 3);
584
585
if (!success)
586
return FALSE;
587
}
588
589
/* Now test the paths between each of the first three vertices and
590
each of the last three vertices */
591
592
for (I = 0; I < theGraph->N; I++)
593
theGraph->G[I].visited = 0;
594
595
for (imageVertPos=0; imageVertPos<3; imageVertPos++)
596
for (I=3; I<6; I++)
597
if (_TestPath(theGraph, imageVerts[imageVertPos],
598
imageVerts[I]) != TRUE)
599
return FALSE;
600
601
for (I = 0; I < theGraph->N; I++)
602
if (theGraph->G[I].visited)
603
degrees[2]--;
604
605
/* If every degree 2 vertex is used in a path between image
606
vertices, then there are no extra pieces of the graph
607
in theGraph. Specifically, the prior tests identify
608
a K_{3,3} and ensure that nothing else could exist in the
609
graph except extra degree 2 vertices, which must be
610
joined in a cycle so that all are degree 2. */
611
612
return degrees[2] == 0 ? TRUE : FALSE;
613
}
614
615
/********************************************************************
616
_CheckKuratowskiSubgraphIntegrity()
617
618
This function checks whether theGraph received as input contains
619
either a K_5 or K_{3,3} homeomorph.
620
621
RETURNS: OK if theGraph contains a K_5 or K_{3,3} homeomorph,
622
NOTOK otherwise
623
624
To be a K_5 homeomorph, there must be exactly 5 vertices of degree 4,
625
which are called 'image' vertices, and all other vertices must be
626
either degree 2 or degree 0. Furthermore, each of the image vertices
627
must be able to reach all of the other image vertices by a single edge
628
or a path of degree two vertices.
629
630
To be a K_{3,3} homeomorph, there must be exactly 6 vertices of degree 3,
631
which are called 'image' vertices, and all other vertices must be either
632
degree 2 or degree 0. Furthermore, the image vertices must be connected
633
by edges or paths of degree two vertices in the manner suggested by
634
a K_{3,3}. To test this, we select an image vertex U, and determine
635
three image vertices X, Y and Z reachable from U by single edges or
636
paths of degree 2 vertices. Then, we check that the two remaining image
637
vertices, V and W, can also reach X, Y and Z by single edges or paths of
638
degree 2 vertices.
639
640
It is not necessary to check that the paths between the image vertices
641
are distinct since if the paths had a common vertex, then the common
642
vertex would not be degree 2.
643
********************************************************************/
644
645
int _CheckKuratowskiSubgraphIntegrity(graphP theGraph)
646
{
647
int degrees[5], imageVerts[6];
648
649
if (_getImageVertices(theGraph, degrees, 4, imageVerts, 6) != OK)
650
return NOTOK;
651
652
if (_TestForCompleteGraphObstruction(theGraph, 5, degrees, imageVerts) == TRUE)
653
{
654
return OK;
655
}
656
657
if (_TestForK33GraphObstruction(theGraph, degrees, imageVerts) == TRUE)
658
{
659
return OK;
660
}
661
662
return NOTOK;
663
}
664
665
/********************************************************************
666
_TestForK23GraphObstruction()
667
668
theGraph - the graph to test
669
degrees - array of counts of the number of vertices of each degree
670
given by the array index. Only valid up to numVerts-1
671
imageVerts - the degree 3 vertices of the K2,3 homeomorph
672
673
This routine tests whether theGraph is a K_{2,3} homeomorph.
674
This routine operates over the results of _getImageVertices()
675
676
returns TRUE if so, FALSE if not
677
********************************************************************/
678
679
int _TestForK23GraphObstruction(graphP theGraph, int *degrees, int *imageVerts)
680
{
681
int I, J, imageVertPos;
682
683
// This function operates over the imageVerts results produced by
684
// getImageVertices, which only finds vertices of degree 3 or higher.
685
// So, for a K2,3, there must be exactly two degree 3 vertices and
686
// no degree 4 vertices.
687
if (degrees[3] != 2)
688
return FALSE;
689
690
// For K_{2,3}, the three vertices of degree 2 were not
691
// detected as image vertices because degree 2 vertices
692
// are indistinguishable from the internal path vertices
693
// between the image vertices. So, here we acknowledge
694
// that more image vertices need to be selected.
695
imageVertPos = 2;
696
697
// Assign the remaining three image vertices to be the
698
// neighbors of the first degree 3 image vertex.
699
// Ensure that each is distinct from the second
700
// degree 3 image vertex. This must be the case because
701
// the two degree 3 image vertices are in the same partition
702
// and hence must not be adjacent.
703
704
J = gp_GetFirstArc(theGraph, imageVerts[0]);
705
while (gp_IsArc(theGraph, J))
706
{
707
imageVerts[imageVertPos] = theGraph->G[J].v;
708
if (imageVerts[imageVertPos] == imageVerts[1])
709
return FALSE;
710
imageVertPos++;
711
J = gp_GetNextArc(theGraph, J);
712
}
713
714
/* The paths from imageVerts[0] to each of the new degree 2
715
image vertices are the edges we just traversed.
716
Now test the paths between each of the degree 2 image
717
vertices and imageVerts[1]. */
718
719
for (I = 0; I < theGraph->N; I++)
720
theGraph->G[I].visited = 0;
721
722
for (imageVertPos=2; imageVertPos<5; imageVertPos++)
723
{
724
if (_TestPath(theGraph, imageVerts[imageVertPos],
725
imageVerts[1]) != TRUE)
726
return FALSE;
727
theGraph->G[imageVerts[imageVertPos]].visited = 1;
728
}
729
730
for (I = 0; I < theGraph->N; I++)
731
if (theGraph->G[I].visited)
732
degrees[2]--;
733
734
/* If every degree 2 vertex is used in a path between the
735
two degree 3 image vertices, then there are no extra
736
pieces of the graph in theGraph. Specifically, the
737
prior tests identify a K_{2,3} and ensure that nothing
738
else could exist in the graph... except extra degree 2
739
vertices joined in a cycle. We return NOTOK in that case. */
740
741
return degrees[2] == 0 ? TRUE : FALSE;
742
}
743
744
/********************************************************************
745
_CheckOuterplanarObstructionIntegrity()
746
747
This function checks whether theGraph received as input contains
748
either a K_4 or K_{2,3} homeomorph.
749
750
RETURNS: OK if theGraph contains a K_4 or K_{2,3} homeomorph,
751
NOTOK otherwise
752
753
To be a K_4 homeomorph, there must be exactly 4 vertices of degree 3,
754
which are called 'image' vertices, and all other vertices must be
755
either degree 2 or degree 0. Furthermore, each of the image vertices
756
must be able to reach all of the other image vertices by a single edge
757
or a path of degree two vertices.
758
759
To be a K_{2,3} homeomorph, there must be exactly 2 vertices of degree 3.
760
All other vertices must be degree 2. Furthermore, the two degree 3
761
vertices must have three internally disjoint paths connecting them,
762
and each path must contain at least two edges (i.e. at least one internal
763
vertex). The two degree 3 vertices are image vertices, and an internal
764
vertex from each of the three paths contributes the remaining three
765
image vertices.
766
767
It is not necessary to check that the paths between the degree three
768
vertices are distinct since if the paths had a common vertex, then the
769
common vertex would not be degree 2.
770
********************************************************************/
771
772
int _CheckOuterplanarObstructionIntegrity(graphP theGraph)
773
{
774
int degrees[4], imageVerts[5];
775
776
if (_getImageVertices(theGraph, degrees, 3, imageVerts, 5) != OK)
777
return NOTOK;
778
779
if (_TestForCompleteGraphObstruction(theGraph, 4, degrees, imageVerts) == TRUE)
780
{
781
return OK;
782
}
783
784
if (_TestForK23GraphObstruction(theGraph, degrees, imageVerts) == TRUE)
785
{
786
return OK;
787
}
788
789
/* We get here only if we failed to recognize an outerplanarity
790
obstruction, so we return failure */
791
792
return NOTOK;
793
}
794
795
/********************************************************************
796
_TestPath()
797
798
This function determines whether there exists a path of degree two
799
vertices between two given vertices. The function marks each
800
degree two vertex as visited. It returns TRUE if it finds the
801
path and FALSE otherwise.
802
********************************************************************/
803
804
int _TestPath(graphP theGraph, int U, int V)
805
{
806
int J;
807
808
J = gp_GetFirstArc(theGraph, U);
809
while (gp_IsArc(theGraph, J))
810
{
811
if (_TryPath(theGraph, J, V) == OK)
812
{
813
_MarkPath(theGraph, J);
814
return TRUE;
815
}
816
817
J = gp_GetNextArc(theGraph, J);
818
}
819
820
return FALSE;
821
}
822
823
/********************************************************************
824
_TryPath()
825
826
This function seeks a given path to a vertex V starting with a
827
given edge out of a starting vertex U. The path is allowed to
828
contain zero or more degree two vertices, but we stop as soon as
829
a vertex of degree higher than two is encountered.
830
The function returns boolean true if that vertex is V, and
831
boolean false otherwise.
832
********************************************************************/
833
834
int _TryPath(graphP theGraph, int J, int V)
835
{
836
int Jin, nextVertex;
837
838
nextVertex = theGraph->G[J].v;
839
840
// while nextVertex is strictly degree 2
841
while (gp_IsArc(theGraph, gp_GetFirstArc(theGraph, nextVertex)) &&
842
gp_IsArc(theGraph, gp_GetLastArc(theGraph, nextVertex)) &&
843
gp_GetNextArc(theGraph, gp_GetFirstArc(theGraph, nextVertex)) == gp_GetLastArc(theGraph, nextVertex))
844
{
845
Jin = gp_GetTwinArc(theGraph, J);
846
J = gp_GetFirstArc(theGraph, nextVertex);
847
if (J == Jin)
848
J = gp_GetLastArc(theGraph, nextVertex);
849
850
nextVertex = theGraph->G[J].v;
851
}
852
853
return nextVertex == V ? TRUE : FALSE;
854
}
855
856
/********************************************************************
857
_MarkPath()
858
859
This function sets the visitation flag on all degree two vertices
860
along a path to a vertex V that starts with a given edge out of
861
a starting vertex U.
862
********************************************************************/
863
864
void _MarkPath(graphP theGraph, int J)
865
{
866
int Jin, nextVertex;
867
868
nextVertex = theGraph->G[J].v;
869
// while nextVertex is strictly degree 2
870
while (gp_IsArc(theGraph, gp_GetFirstArc(theGraph, nextVertex)) &&
871
gp_IsArc(theGraph, gp_GetLastArc(theGraph, nextVertex)) &&
872
gp_GetNextArc(theGraph, gp_GetFirstArc(theGraph, nextVertex)) == gp_GetLastArc(theGraph, nextVertex))
873
{
874
theGraph->G[nextVertex].visited = 1;
875
876
Jin = gp_GetTwinArc(theGraph, J);
877
J = gp_GetFirstArc(theGraph, nextVertex);
878
if (J == Jin)
879
J = gp_GetLastArc(theGraph, nextVertex);
880
881
nextVertex = theGraph->G[J].v;
882
}
883
}
884
885
/********************************************************************
886
_TestSubgraph()
887
Checks whether theSubgraph is in fact a subgraph of theGraph.
888
For each vertex v in graph G and subgraph H, we iterate the adjacency
889
list of H(v) and, for each neighbor w, we mark G(w). Then, we
890
iterate the adjacency list of G(v) and unmark each neighbor. Then,
891
we iterate the adjacency list of H(v) again to ensure that every
892
neighbor w was unmarked. If there exists a marked neighbor, then
893
H(v) contains an incident edge that is not incident to G(v).
894
895
Returns TRUE if theSubgraph contains only edges from theGraph,
896
FALSE otherwise
897
********************************************************************/
898
899
int _TestSubgraph(graphP theSubgraph, graphP theGraph)
900
{
901
int I, J;
902
int Result = TRUE;
903
int invokeSortOnGraph = FALSE;
904
int invokeSortOnSubgraph = FALSE;
905
906
// If the graph is not sorted by DFI, but the alleged subgraph is,
907
// then "unsort" the alleged subgraph so both have the same vertex order
908
if (!(theGraph->internalFlags & FLAGS_SORTEDBYDFI) &&
909
(theSubgraph->internalFlags & FLAGS_SORTEDBYDFI))
910
{
911
invokeSortOnSubgraph = TRUE;
912
gp_SortVertices(theSubgraph);
913
}
914
915
// If the graph is not sorted by DFI, but the alleged subgraph is,
916
// then "unsort" the alleged subgraph so both have the same vertex order
917
if (!(theSubgraph->internalFlags & FLAGS_SORTEDBYDFI) &&
918
(theGraph->internalFlags & FLAGS_SORTEDBYDFI))
919
{
920
invokeSortOnGraph = TRUE;
921
gp_SortVertices(theGraph);
922
}
923
924
/* We clear all visitation flags */
925
926
for (I = 0; I < theGraph->N; I++)
927
theGraph->G[I].visited = 0;
928
929
/* For each vertex... */
930
931
for (I = 0; I < theSubgraph->N; I++)
932
{
933
/* For each neighbor w in the adjacency list of vertex I in the
934
subgraph, set the visited flag in w in the graph */
935
936
J = gp_GetFirstArc(theSubgraph, I);
937
while (gp_IsArc(theSubgraph, J))
938
{
939
if (theSubgraph->G[J].v == NIL)
940
{
941
Result = FALSE;
942
break;
943
}
944
theGraph->G[theSubgraph->G[J].v].visited = 1;
945
J = gp_GetNextArc(theSubgraph, J);
946
}
947
948
if (Result != TRUE)
949
break;
950
951
/* For each neighbor w in the adjacency list of vertex I in the graph,
952
clear the visited flag in w in the graph */
953
954
J = gp_GetFirstArc(theGraph, I);
955
while (gp_IsArc(theGraph, J))
956
{
957
if (theGraph->G[J].v == NIL)
958
{
959
Result = FALSE;
960
break;
961
}
962
theGraph->G[theGraph->G[J].v].visited = 0;
963
J = gp_GetNextArc(theGraph, J);
964
}
965
966
if (Result != TRUE)
967
break;
968
969
/* For each neighbor w in the adjacency list of vertex I in the
970
subgraph, set the visited flag in w in the graph */
971
972
J = gp_GetFirstArc(theSubgraph, I);
973
while (gp_IsArc(theSubgraph, J))
974
{
975
if (theGraph->G[theSubgraph->G[J].v].visited)
976
{
977
Result = FALSE;
978
break;
979
}
980
J = gp_GetNextArc(theSubgraph, J);
981
}
982
983
if (Result != TRUE)
984
break;
985
}
986
987
// Restore the DFI sort order of either graph if it had to be reordered at the start
988
if (invokeSortOnSubgraph)
989
gp_SortVertices(theSubgraph);
990
if (invokeSortOnGraph)
991
gp_SortVertices(theGraph);
992
993
return Result;
994
}
995
996