00001 /* Copyright (c) 2005 by John M. Boyer, All Rights Reserved. Please see 00002 * License.txt for use and redistribution license. */ 00003 /* Copyright (c) 1997-2003 by John M. Boyer, All Rights Reserved. 00004 This code may not be reproduced or disseminated in whole or in part 00005 without the written permission of the author. */ 00006 00007 #define GRAPHTEST_C 00008 00009 #include "graph_boyer.h" 00010 #include "stack.h" 00011 00012 /* Private function declarations */ 00013 int _TestPath (graphP theGraph, 00014 int U, 00015 int V); 00016 int _TryPath (graphP theGraph, 00017 int J, 00018 int V); 00019 void _MarkPath (graphP theGraph, 00020 int J); 00021 int _TestSubgraph (graphP theSubgraph, 00022 graphP theGraph); 00023 00024 /******************************************************************** 00025 gp_CheckEmbeddingIntegrity() 00026 00027 This function traverses all faces of a graph structure containing 00028 the planar embedding that results from gp_Embed(). The algorithm 00029 begins by placing all of the graph's arcs onto a stack and marking 00030 all of them as unvisited. For each arc popped, if it is visited, 00031 it is immediately discarded and the next arc is popped. Popping an 00032 unvisited arc J begins a face traversal. We move to the true twin 00033 arc K of J, and obtain its link[0] successor arc L. This amounts to 00034 always going clockwise or counterclockwise (depending on how the 00035 graph is drawn on the plane, or alternately whether one is above 00036 or below the plane). This traversal continues until we make it 00037 back to J. Each arc along the way is marked as visited. Further, 00038 if L has been visited, then there is an error since an arc can only 00039 appear in one face (the twin arc appears in a separate face, which 00040 is traversed in the opposing direction). 00041 If this algorithm succeeds without double visiting any arcs, and it 00042 produces the correct face count according to Euler's formula, then 00043 the embedding has all vertices oriented the same way. 00044 NOTE: Because a vertex is in its adj. list, if we go to a link[0] 00045 and it is a vertex, we simply take the vertex's link[0]. 00046 NOTE: In disconnected graphs, the face reader counts the external 00047 face of each connected component. So, we adjust the face 00048 count by subtracting one for each component, then we add one 00049 to count the external face shared by all components. 00050 ********************************************************************/ 00051 int gp_CheckEmbeddingIntegrity (graphP theEmbedding) 00052 { 00053 stackP theStack = theEmbedding->theStack; 00054 int I, 00055 e, 00056 J, 00057 JTwin, 00058 K, 00059 L, 00060 NumFaces, 00061 connectedComponents; 00062 if (theEmbedding == NULL) 00063 return NOTOK; 00064 00065 /* The stack need only contain 2M entries, one for each edge record. With 00066 max M at 3N, this amounts to 6N integers of space. The embedding 00067 structure already contains this stack, so we just make sure it 00068 starts out empty. */ 00069 sp_ClearStack (theStack); 00070 00071 /* Push all arcs and set them to unvisited */ 00072 for (e = 0, J = 2 * theEmbedding->N; e < theEmbedding->M; e++, J += 2) 00073 00074 { 00075 sp_Push (theStack, J); 00076 theEmbedding->G[J].visited = 0; 00077 JTwin = gp_GetTwinArc (theEmbedding, J); 00078 sp_Push (theStack, JTwin); 00079 theEmbedding->G[JTwin].visited = 0; 00080 } 00081 00082 /* Read faces until every arc is used */ 00083 NumFaces = 0; 00084 while (sp_NonEmpty (theStack)) 00085 00086 { 00087 00088 /* Get an arc; if it has already been used by a face, then 00089 * don't use it to traverse a new face */ 00090 sp_Pop (theStack, J); 00091 if (theEmbedding->G[J].visited) 00092 continue; 00093 L = NIL; 00094 JTwin = J; 00095 while (L != J) 00096 00097 { 00098 K = gp_GetTwinArc (theEmbedding, JTwin); 00099 L = theEmbedding->G[K].link[0]; 00100 if (L < 2 * theEmbedding->N) 00101 L = theEmbedding->G[L].link[0]; 00102 if (theEmbedding->G[L].visited) 00103 return NOTOK; 00104 theEmbedding->G[L].visited++; 00105 JTwin = L; 00106 } 00107 NumFaces++; 00108 } 00109 00110 /* Count the external face once rather than once per connected component; 00111 each connected component is detected by the fact that it has no 00112 DFS parent, except in the case of isolated vertices, no face was counted 00113 so we do not subtract one. */ 00114 for (I = connectedComponents = 0; I < theEmbedding->N; I++) 00115 if (theEmbedding->V[I].DFSParent == NIL) 00116 00117 { 00118 if (gp_GetVertexDegree (theEmbedding, I) > 0) 00119 NumFaces--; 00120 connectedComponents++; 00121 } 00122 NumFaces++; 00123 00124 /* Test number of faces using the extended Euler's formula. 00125 For connected components, Euler's formula is f=m-n+2, but 00126 for disconnected graphs it is extended to f=m-n+1+c where 00127 c is the number of connected components.*/ 00128 return NumFaces == 00129 theEmbedding->M - theEmbedding->N + 1 + connectedComponents ? OK : NOTOK; 00130 } 00131 00132 00133 /******************************************************************** 00134 gp_CheckKuratowskiSubgraphIntegrity() 00135 00136 This function checks whether theGraph received as input contains 00137 either a K_5 or K_{3,3} homeomorph. 00138 00139 To be a K_5 homeomorph, there must be exactly 5 vertices of degree 4, 00140 which are called 'image' vertices, and all other vertices must be 00141 either degree 2 or degree 0. Furthermore, each of the image vertices 00142 must be able to reach all of the other image vertices by a single edge 00143 or a path of degree two vertices. 00144 00145 To be a K_{3,3} homeomorph, there must be exactly 6 vertices of degree 3, 00146 which are called 'image' vertices, and all other vertices must be either 00147 degree 2 or degree 0. Furthermore, the image vertices must be connected 00148 by edges or paths of degree two vertices in the manner suggested by 00149 a K_{3,3}. To test this, we select an image vertex U, and determine 00150 three image vertices X, Y and Z reachable from U by single edges or 00151 paths of degree 2 vertices. Then, we check that the two remaining image 00152 vertices, V and W, can also reach X, Y and Z by single edges or paths of 00153 degree 2 vertices. 00154 00155 It is not necessary to check that the paths between the image vertices 00156 are distinct since if the paths had a common vertex, then the common 00157 vertex would not be degree 2. 00158 ********************************************************************/ 00159 int gp_CheckKuratowskiSubgraphIntegrity (graphP theGraph) 00160 { 00161 int degrees[5], 00162 imageVerts[6]; 00163 int I, 00164 degree, 00165 imageVertPos, 00166 temp, 00167 success; 00168 00169 /* Is theGraph even a subgraph of the original graph? */ 00170 00171 // if (_TestSubgraph(theGraph, origGraph) != OK) 00172 // return NOTOK; 00173 00174 /* Init the variables that count the number of verts of each degree 00175 and the location of each image vert. */ 00176 for (I = 0; I < 5; I++) 00177 degrees[I] = 0; 00178 for (I = 0; I < 6; I++) 00179 imageVerts[I] = NIL; 00180 imageVertPos = 0; 00181 00182 /* Count the number of verts of each degree and find the locations of 00183 the image verts. Quit if any vertex of degree higher than 4 00184 or equal to 1 is encountered. */ 00185 for (I = 0; I < theGraph->N; I++) 00186 00187 { 00188 degree = gp_GetVertexDegree (theGraph, I); 00189 if (degree == 1 || degree > 4) 00190 return NOTOK; 00191 degrees[degree]++; 00192 if (imageVertPos < 6 && degree > 2) 00193 imageVerts[imageVertPos++] = I; 00194 } 00195 00196 /* If there are five vertices of degree 4 (and no degree 3 vertices), 00197 then test for a K_5 */ 00198 if (degrees[4] == 5 && degrees[3] == 0) 00199 00200 { 00201 for (I = 0; I < theGraph->N; I++) 00202 theGraph->G[I].visited = 0; 00203 for (imageVertPos = 0; imageVertPos < 5; imageVertPos++) 00204 for (I = 0; I < 5; I++) 00205 if (imageVertPos != I) 00206 if (_TestPath (theGraph, imageVerts[imageVertPos], imageVerts[I]) != 00207 OK) 00208 return NOTOK; 00209 for (I = 0; I < theGraph->N; I++) 00210 if (theGraph->G[I].visited) 00211 degrees[2]--; 00212 00213 /* If every degree 2 vertex is used in a path between image 00214 * vertices, then there are no extra pieces of the graph 00215 * in theGraph. Specifically, the prior tests identify 00216 * a K_5 and ensure that nothing else could exist in the 00217 * graph except extra degree 2 vertices, which must be 00218 * joined in a cycle so that all are degree 2. */ 00219 return degrees[2] == 0 ? OK : NOTOK; 00220 } 00221 00222 /* Otherwise, if there are six vertices of degree 3 (and no degree 4 vertices), 00223 then test for a K_{3,3} */ 00224 00225 else if (degrees[3] == 6 && degrees[4] == 0) 00226 00227 { 00228 00229 /* Partition the six image vertices into two sets of 3 00230 * (or report failure) */ 00231 for (imageVertPos = 3; imageVertPos < 6; imageVertPos++) 00232 00233 { 00234 I = success = 0; 00235 00236 do 00237 00238 { 00239 if (_TestPath (theGraph, imageVerts[imageVertPos], imageVerts[0]) == OK) 00240 00241 { 00242 success = 1; 00243 break; 00244 } 00245 I++; 00246 temp = imageVerts[I]; 00247 imageVerts[I] = imageVerts[imageVertPos]; 00248 imageVerts[imageVertPos] = temp; 00249 } while (I < 3); 00250 if (!success) 00251 return NOTOK; 00252 } 00253 00254 /* Now test the paths between each of the first three vertices and 00255 * each of the last three vertices */ 00256 for (I = 0; I < theGraph->N; I++) 00257 theGraph->G[I].visited = 0; 00258 for (imageVertPos = 0; imageVertPos < 3; imageVertPos++) 00259 for (I = 3; I < 6; I++) 00260 if (_TestPath (theGraph, imageVerts[imageVertPos], imageVerts[I]) != OK) 00261 return NOTOK; 00262 for (I = 0; I < theGraph->N; I++) 00263 if (theGraph->G[I].visited) 00264 degrees[2]--; 00265 00266 /* If every degree 2 vertex is used in a path between image 00267 * vertices, then there are no extra pieces of the graph 00268 * in theGraph. Specifically, the prior tests identify 00269 * a K_{3,3} and ensure that nothing else could exist in the 00270 * graph except extra degree 2 vertices, which must be 00271 * joined in a cycle so that all are degree 2. */ 00272 return degrees[2] == 0 ? OK : NOTOK; 00273 } 00274 00275 /* We get here only if we failed to recognize a Kuratowski subgraph, so 00276 we return failure */ 00277 return NOTOK; 00278 } 00279 00280 00281 /******************************************************************** 00282 _TestPath() 00283 00284 This function determines whether there exists a path of degree two 00285 vertices between two given vertices. The function marks each 00286 degree two vertex as visited. It returns NOTOK if it cannot find 00287 such a path. 00288 ********************************************************************/ 00289 int _TestPath (graphP theGraph, 00290 int U, 00291 int V) 00292 { 00293 int J; 00294 J = theGraph->G[U].link[0]; 00295 while (J > theGraph->N) 00296 00297 { 00298 if (_TryPath (theGraph, J, V) == OK) 00299 00300 { 00301 _MarkPath (theGraph, J); 00302 return OK; 00303 } 00304 J = theGraph->G[J].link[0]; 00305 } 00306 return NOTOK; 00307 } 00308 00309 00310 /******************************************************************** 00311 _TryPath() 00312 00313 This function seeks a given path to a vertex V starting with a 00314 given edge out of a starting vertex U. The path is allowed to 00315 contain zero or more degree two vertices, but we stop as soon as 00316 a vertex of degree higher than two is encountered. 00317 The function returns OK if that vertex is V, and NOTOK otherwise. 00318 ********************************************************************/ 00319 int _TryPath (graphP theGraph, 00320 int J, 00321 int V) 00322 { 00323 int Jin, 00324 nextVertex; 00325 nextVertex = theGraph->G[J].v; 00326 while (gp_GetVertexDegree (theGraph, nextVertex) == 2) 00327 00328 { 00329 Jin = gp_GetTwinArc (theGraph, J); 00330 J = theGraph->G[nextVertex].link[0]; 00331 if (J == Jin) 00332 J = theGraph->G[nextVertex].link[1]; 00333 nextVertex = theGraph->G[J].v; 00334 } 00335 return nextVertex == V ? OK : NOTOK; 00336 } 00337 00338 00339 /******************************************************************** 00340 _MarkPath() 00341 00342 This function sets the visitation flag on all degree two vertices 00343 along a path to a vertex V that starts with a given edge out of 00344 a starting vertex U. 00345 ********************************************************************/ 00346 void _MarkPath (graphP theGraph, 00347 int J) 00348 { 00349 int Jin, 00350 nextVertex; 00351 nextVertex = theGraph->G[J].v; 00352 while (gp_GetVertexDegree (theGraph, nextVertex) == 2) 00353 00354 { 00355 theGraph->G[nextVertex].visited = 1; 00356 Jin = gp_GetTwinArc (theGraph, J); 00357 J = theGraph->G[nextVertex].link[0]; 00358 if (J == Jin) 00359 J = theGraph->G[nextVertex].link[1]; 00360 nextVertex = theGraph->G[J].v; 00361 } 00362 } 00363 00364 00365 /******************************************************************** 00366 _TestSubgraph() 00367 Checks whether theSubgraph is in face a subgraph of theGraph. 00368 For each vertex v in graph G and subgraph H, we iterate the adjacency 00369 list of H(v) and, for each neighbor w, we mark G(w). Then, we 00370 iterate the adjacency list of G(v) and unmark each neighbor. Then, 00371 we iterate the adjacency list of H(v) again to ensure that every 00372 neighbor w was unmarked. If there exists a marked neighbor, then 00373 H(v) contains an incident edge that is not incident to G(v). 00374 00375 Returns OK if theSubgraph contains only edges from theGraph, 00376 NOTOK otherwise 00377 ********************************************************************/ 00378 int _TestSubgraph (graphP theSubgraph, 00379 graphP theGraph) 00380 { 00381 int I, 00382 J; 00383 00384 /* We clear all visitation flags */ 00385 for (I = 0; I < theGraph->N; I++) 00386 theGraph->G[I].visited = 0; 00387 00388 /* For each vertex... */ 00389 for (I = 0; I < theSubgraph->N; I++) 00390 00391 { 00392 00393 /* For each neighbor w in the adjacency list of vertex I in the 00394 * subgraph, set the visited flag in w in the graph */ 00395 J = theSubgraph->G[I].link[0]; 00396 while (J >= 2 * theSubgraph->N) 00397 00398 { 00399 theGraph->G[theSubgraph->G[J].v].visited = 1; 00400 J = theSubgraph->G[J].link[0]; 00401 } 00402 00403 /* For each neighbor w in the adjacency list of vertex I in the graph, 00404 * clear the visited flag in w in the graph */ 00405 J = theGraph->G[I].link[0]; 00406 while (J >= 2 * theGraph->N) 00407 00408 { 00409 theGraph->G[theGraph->G[J].v].visited = 0; 00410 J = theGraph->G[J].link[0]; 00411 } 00412 00413 /* For each neighbor w in the adjacency list of vertex I in the 00414 * subgraph, set the visited flag in w in the graph */ 00415 J = theSubgraph->G[I].link[0]; 00416 while (J >= 2 * theSubgraph->N) 00417 00418 { 00419 if (theGraph->G[theSubgraph->G[J].v].visited) 00420 return NOTOK; 00421 J = theSubgraph->G[J].link[0]; 00422 } 00423 } 00424 return OK; 00425 }