/*1Planarity-Related Graph Algorithms Project2Copyright (c) 1997-2010, John M. Boyer3All rights reserved. Includes a reference implementation of the following:45* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding,6Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation,7University of Victoria, 2001.89* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n)10Planarity by Edge Addition". Journal of Graph Algorithms and Applications,11Vol. 8, No. 3, pp. 241-273, 2004.1213* John M. Boyer. "A New Method for Efficiently Generating Planar Graph14Visibility Representations". In P. Eades and P. Healy, editors,15Proceedings of the 13th International Conference on Graph Drawing 2005,16Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006.1718Redistribution and use in source and binary forms, with or without modification,19are permitted provided that the following conditions are met:2021* Redistributions of source code must retain the above copyright notice, this22list of conditions and the following disclaimer.2324* Redistributions in binary form must reproduce the above copyright notice, this25list of conditions and the following disclaimer in the documentation and/or26other materials provided with the distribution.2728* Neither the name of the Planarity-Related Graph Algorithms Project nor the names29of its contributors may be used to endorse or promote products derived from this30software without specific prior written permission.3132THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"33AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE34IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE35DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR36ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES37(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;38LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON39ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT40(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS41SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.42*/4344#define GRAPHTEST_C4546#include "graph.h"47#include "stack.h"4849/* Private function declarations */5051int _TestPath(graphP theGraph, int U, int V);52int _TryPath(graphP theGraph, int J, int V);53void _MarkPath(graphP theGraph, int J);54int _TestSubgraph(graphP theSubgraph, graphP theGraph);5556int _CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph);57int _CheckEmbeddingFacialIntegrity(graphP theGraph);58int _CheckObstructionIntegrity(graphP theGraph, graphP origGraph);5960int _CheckKuratowskiSubgraphIntegrity(graphP theGraph);61int _CheckOuterplanarObstructionIntegrity(graphP theGraph);6263int _CheckAllVerticesOnExternalFace(graphP theGraph);64void _MarkExternalFaceVertices(graphP theGraph, int startVertex);6566/********************************************************************67gp_TestEmbedResultIntegrity()6869This function tests the integrity of the graph result returned70from gp_Embed().7172The caller of gp_Embed() does not have to save the original graph73because, for efficiency, gp_Embed() operates on the input graph.74However, to test the integrity of the result relative to the input,75a copy of the input graph is required.7677Modules that extend/alter the behavior of gp_Embed() beyond the78core planarity embedder and planarity obstruction isolator should79also provide overriding integrity test routines appropriate to the80extension algorithm.8182The main method first calls gp_SortVertices on theGraph, if the83origGraph is not in DFI order (the common case). Therefore,84extension integrity tests can count on a consistent numbering85between theGraph and the origGraph, either DFI order or pre-DFS86order if that is the state of the origGraph.8788After all tests, the main method ensures theGraph is restored to89DFI order by invoking gp_SortVertices if needed, thus ensuring90that theGraph has the documented post-condition of gp_Embed().9192For an embedResult of OK, fpCheckEmbeddingIntegrity is invoked.93The core planarity implementation does a face walk of all faces94of the embedding. It ensures that all edges were used in the face95walk and that the right number of faces exist for the number of96vertices and edges. Also, we ensure that all adjacencies expressed97in the original graph still exist in the result graph.9899For an embedResult of NONEMBEDDABLE, fpCheckObstructionIntegrity100is invoked. The core planarity algorithm checks that the result101graph is homeomorphic to K5 or K3,3 and that it is in fact a102subgraph of the input graph. Other algorithms use overloads to103make appropriate checks.104105Returns NOTOK on integrity check failure or embedResult of NOTOK106OK for successful integrity check of OK embedResult107NONEMBEDDABLE for successful integrity check of an108embedResult of NONEMBEDDABLE109********************************************************************/110111int gp_TestEmbedResultIntegrity(graphP theGraph, graphP origGraph, int embedResult)112{113int RetVal = embedResult;114115if (theGraph == NULL || origGraph == NULL)116return NOTOK;117118if (embedResult == OK)119{120RetVal = theGraph->functions.fpCheckEmbeddingIntegrity(theGraph, origGraph);121}122else if (embedResult == NONEMBEDDABLE)123{124RetVal = theGraph->functions.fpCheckObstructionIntegrity(theGraph, origGraph);125}126127if (RetVal == OK)128RetVal = embedResult;129130return RetVal;131}132133/********************************************************************134_CheckEmbeddingIntegrity()135136The core planarity implementation does a face walk of all faces137of the embedding. It ensures that all edges were used in the face138walk and that the right number of faces exist for the number of139vertices and edges. Also, we ensure that all adjacencies expressed140in the original graph still exist in the result graph, accounting141for the fact that the result graph is sorted by DFI, but the input142may or may not be sorted by DFI.143144returns OK if all integrity tests passed, NOTOK otherwise145********************************************************************/146147int _CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph)148{149if (theGraph == NULL || origGraph == NULL)150return NOTOK;151152if (_TestSubgraph(theGraph, origGraph) != TRUE)153return NOTOK;154155if (_TestSubgraph(origGraph, theGraph) != TRUE)156return NOTOK;157158if (_CheckEmbeddingFacialIntegrity(theGraph) != OK)159return NOTOK;160161if (theGraph->embedFlags == EMBEDFLAGS_OUTERPLANAR)162{163if (_CheckAllVerticesOnExternalFace(theGraph) != OK)164return NOTOK;165}166167return OK;168}169170/********************************************************************171_CheckEmbeddingFacialIntegrity()172173This function traverses all faces of a graph structure containing174the planar embedding that results from gp_Embed(). The algorithm175begins by placing all of the graph's arcs onto a stack and marking176all of them as unvisited. For each arc popped, if it is visited,177it is immediately discarded and the next arc is popped. Popping an178unvisited arc J begins a face traversal. We move to the true twin179arc K of J, and obtain its successor arc L. This amounts to180always going clockwise or counterclockwise (depending on how the181graph is drawn on the plane, or alternately whether one is above182or below the plane). This traversal continues until we make it183back to J. Each arc along the way is marked as visited. Further,184if L has been visited, then there is an error since an arc can only185appear in one face (the twin arc appears in a separate face, which186is traversed in the opposing direction).187If this algorithm succeeds without double visiting any arcs, and it188produces the correct face count according to Euler's formula, then189the embedding has all vertices oriented the same way.190NOTE: In disconnected graphs, the face reader counts the external191face of each connected component. So, we adjust the face192count by subtracting one for each component, then we add one193to count the external face shared by all components.194********************************************************************/195196int _CheckEmbeddingFacialIntegrity(graphP theGraph)197{198stackP theStack = theGraph->theStack;199int I, e, J, JTwin, K, L, NumFaces, connectedComponents;200201if (theGraph == NULL)202return NOTOK;203204/* The stack need only contain 2M entries, one for each edge record. With205max M at 3N, this amounts to 6N integers of space. The embedding206structure already contains this stack, so we just make sure it207starts out empty. */208209sp_ClearStack(theStack);210211/* Push all arcs and set them to unvisited */212213for (e=0, J=theGraph->edgeOffset; e < theGraph->M + sp_GetCurrentSize(theGraph->edgeHoles); e++, J+=2)214{215if (theGraph->G[J].v == NIL)216continue;217218sp_Push(theStack, J);219theGraph->G[J].visited = 0;220JTwin = gp_GetTwinArc(theGraph, J);221sp_Push(theStack, JTwin);222theGraph->G[JTwin].visited = 0;223}224225// There are M edges, so we better have pushed 2M arcs just now226// i.e. testing that the continue above skipped only edge holes227if (sp_GetCurrentSize(theStack) != 2*theGraph->M)228return NOTOK;229230231/* Read faces until every arc is used */232233NumFaces = 0;234while (sp_NonEmpty(theStack))235{236/* Get an arc; if it has already been used by a face, then237don't use it to traverse a new face */238sp_Pop(theStack, J);239if (theGraph->G[J].visited) continue;240241L = NIL;242JTwin = J;243while (L != J)244{245K = gp_GetTwinArc(theGraph, JTwin);246L = gp_GetNextArcCircular(theGraph, K);247if (theGraph->G[L].visited)248return NOTOK;249theGraph->G[L].visited++;250JTwin = L;251}252NumFaces++;253}254255/* Count the external face once rather than once per connected component;256each connected component is detected by the fact that it has no257DFS parent, except in the case of isolated vertices, no face was counted258so we do not subtract one. */259260for (I=connectedComponents=0; I < theGraph->N; I++)261if (theGraph->V[I].DFSParent == NIL)262{263if (gp_GetVertexDegree(theGraph, I) > 0)264NumFaces--;265connectedComponents++;266}267268NumFaces++;269270/* Test number of faces using the extended Euler's formula.271For connected components, Euler's formula is f=m-n+2, but272for disconnected graphs it is extended to f=m-n+1+c where273c is the number of connected components.*/274275return NumFaces == theGraph->M - theGraph->N + 1 + connectedComponents276? OK : NOTOK;277}278279/********************************************************************280_CheckAllVerticesOnExternalFace()281282Determines whether or not any vertices have been embedded within283the bounding cycle of the external face.284The input graph may be disconnected, so this routine walks the285external face starting at each vertex with no DFSParent.286287return OK if all vertices visited on external face walks, NOTOK otherwise288********************************************************************/289290int _CheckAllVerticesOnExternalFace(graphP theGraph)291{292int I;293294// Mark all vertices unvisited295for (I=0; I < theGraph->N; I++)296theGraph->G[I].visited = 0;297298// For each connected component, walk its external face and299// mark the vertices as visited300for (I=0; I < theGraph->N; I++)301{302if (theGraph->V[I].DFSParent == NIL)303_MarkExternalFaceVertices(theGraph, I);304}305306// If any vertex is unvisited, then the embedding is not an outerplanar307// embedding, so we return NOTOK308for (I=0; I < theGraph->N; I++)309if (!theGraph->G[I].visited)310{311return NOTOK;312}313314// All vertices were found on external faces of the connected components315// so the embedding is an outerplanar embedding and we return OK316return OK;317}318319/********************************************************************320_MarkExternalFaceVertices()321322Walks the external face of the connected component containing the323start vertex, and marks the visited flag of all vertices found.324The start vertex is assumed to be on the external face.325This method assumed the embedding integrity has already been326verified to be correct.327This method correctly handles components that have cut vertices,328i.e. it does not assume that the outer face is a simple cycle;329it only assumes that all vertices are reachable by walking a330single face that starts with startVertex.331********************************************************************/332333void _MarkExternalFaceVertices(graphP theGraph, int startVertex)334{335int nextVertex = startVertex;336int Jout = gp_GetFirstArc(theGraph, nextVertex);337int Jin;338339// Handle the case of an isolated vertex340if (!gp_IsArc(theGraph, Jout))341{342theGraph->G[startVertex].visited = 1;343return;344}345346// Process a non-trivial connected component347do {348theGraph->G[nextVertex].visited = 1;349350// The arc out of the vertex just visited points to the next vertex351nextVertex = theGraph->G[Jout].v;352353// Arc used to enter the next vertex is needed so we can get the354// next edge in rotation order.355// Note: for bicomps, first and last arcs of all external face vertices356// indicate the edges that hold them to the external face357// But _JoinBicomps() has already occurred, so cut vertices358// will have external face edges other than the first and last arcs359// Hence we need this more sophisticated traversal method360Jin = gp_GetTwinArc(theGraph, Jout);361362// Now we get the next arc in rotation order as the new arc out to the363// vertex after nextVertex. This sets us up for the next iteration.364// Note: We cannot simply follow the chain of nextVertex first arcs365// as we started out doing at the top of this method. This is366// because we are no longer dealing with bicomps only.367// Since _JoinBicomps() has already been invoked, there may now368// be cut vertices on the external face whose adjacency lists369// contain external face arcs in positions other than the first and370// and last arcs. We will visit those vertices multiple times,371// which is OK (just that we have to explain why we're calculating372// jout in this way).373Jout = gp_GetNextArcCircular(theGraph, Jin);374375// Now things get really interesting. The DFS root (startVertex) may376// itself be a cut vertex to which multiple bicomps have been joined.377// So we cannot simply stop when the external face walk gets back to378// startVertex. We must actually get back to startVertex using its379// last arc. This ensures that we've looped down into all the DFS380// subtrees rooted at startVertex and walked their external faces.381382// Since we started the whole external face walk with the first arc383// of startVertex, we need to proceed until we reenter startVertex384// using its last arc.385386} while (Jin != gp_GetLastArc(theGraph, startVertex));387}388389390/********************************************************************391_CheckObstructionIntegrity()392393Returns OK if theGraph is a subgraph of origGraph and it contains394an allowed homeomorph, and NOTOK otherwise.395396For core planarity, the allowed homeomorphs are K_5 or K_{3,3}397398Extension modules may overload this method to implement different399tests. For example, K_{3,3} search allows only K_{3,3} and400outerplanarity allows only K_4 or K_{2,3}.401********************************************************************/402403int _CheckObstructionIntegrity(graphP theGraph, graphP origGraph)404{405if (theGraph == NULL || origGraph == NULL)406return NOTOK;407408if (_TestSubgraph(theGraph, origGraph) != TRUE)409{410return NOTOK;411}412413if (theGraph->embedFlags == EMBEDFLAGS_PLANAR)414return _CheckKuratowskiSubgraphIntegrity(theGraph);415416else if (theGraph->embedFlags == EMBEDFLAGS_OUTERPLANAR)417return _CheckOuterplanarObstructionIntegrity(theGraph);418419return NOTOK;420}421422/********************************************************************423_getImageVertices()424425Count the number of vertices of each degree and find the locations of426the image vertices (also sometimes called the corners of the obstruction).427An image vertex is a vertex of degree three or higher because degree4282 vertices are generally internal to the paths between the image429vertices.430431The notable exception is K_{2,3}, an obstruction to outerplanarity.432This routine does not know the obstruction it is looking for, so the433caller must decide whether there are any degree 2 vertices that should434be added to imageVerts.435436Return NOTOK if any vertex of degree 1 or higher than the max is found437NOTOK if more than the max number of image vertices is found.438Return OK otherwise.439********************************************************************/440441int _getImageVertices(graphP theGraph, int *degrees, int maxDegree,442int *imageVerts, int maxNumImageVerts)443{444int I, imageVertPos, degree;445446for (I = 0; I <= maxDegree; I++)447degrees[I] = 0;448449for (I = 0; I < maxNumImageVerts; I++)450imageVerts[I] = NIL;451452imageVertPos = 0;453454for (I = 0; I < theGraph->N; I++)455{456degree = gp_GetVertexDegree(theGraph, I);457if (degree == 1)458return NOTOK;459if (degree > maxDegree)460return NOTOK;461462degrees[degree]++;463464if (imageVertPos < maxNumImageVerts && degree > 2)465imageVerts[imageVertPos++] = I;466else if (degree > 2)467return NOTOK;468}469470return OK;471}472473/********************************************************************474_TestForCompleteGraphObstruction()475476theGraph - the graph to test477numVerts - the number of image vertices (corners) of the complete478graph homeomorph being tested for (e.g. 5 for a K5)479degrees - array of counts of the number of vertices of each degree480given by the array index. Only valid up to numVerts-1481imageVerts - the vertices of degree numVerts-1482483This routine tests whether theGraph is a K_{numVerts} homeomorph for484numVerts >= 4.485486returns FALSE if numVerts < 4,487if theGraph has other than numVerts image vertices488if theGraph contains other than degree 2 vertices plus489the image vertices490if any pair of image vertices lacks a connecting path491if any degree two vertices are not in the connecting paths492TRUE otherwise493********************************************************************/494495int _TestForCompleteGraphObstruction(graphP theGraph, int numVerts,496int *degrees, int *imageVerts)497{498int I, J;499500// We need to make sure we have numVerts vertices of degree numVerts-1501// For example, if numVerts==5, then we're looking for a K5, so we502// need to have degrees[4] == 5 (5 vertices of degree 4)503if (degrees[numVerts-1] != numVerts)504return FALSE;505506// All vertices need to be degree 0, degree 2 or degree numVerts-1507if (degrees[0]+degrees[2]+degrees[numVerts-1] != theGraph->N)508return FALSE;509510// We clear all the vertex visited flags511for (I = 0; I < theGraph->N; I++)512theGraph->G[I].visited = 0;513514// For each pair of image vertices, we test that there is a path515// between the two vertices. If so, the visited flags of the516// internal vertices along the path are marked517//518for (I = 0; I < numVerts; I++)519for (J = 0; J < numVerts; J++)520if (I != J)521if (_TestPath(theGraph, imageVerts[I],522imageVerts[J]) != TRUE)523return FALSE;524525// The visited flags should have marked only degree two vertices,526// so for every marked vertex, we subtract one from the count of527// the degree two vertices.528for (I = 0; I < theGraph->N; I++)529if (theGraph->G[I].visited)530degrees[2]--;531532/* If every degree 2 vertex is used in a path between image533vertices, then there are no extra pieces of the graph534in theGraph. Specifically, the prior tests identify535a K_5 and ensure that nothing else could exist in the536graph except extra degree 2 vertices, which must be537joined in a cycle so that all are degree 2. */538539return degrees[2] == 0 ? TRUE : FALSE;540}541542/********************************************************************543_TestForK33GraphObstruction()544545theGraph - the graph to test546degrees - array of counts of the number of vertices of each degree547given by the array index. Only valid up to numVerts-1548imageVerts - the degree 3 vertices of the K3,3 homeomorph549550This routine tests whether theGraph is a K_{3,3} homeomorph.551552returns TRUE if so, FALSE if not553********************************************************************/554555int _TestForK33GraphObstruction(graphP theGraph, int *degrees, int *imageVerts)556{557int I, imageVertPos, temp, success;558559if (degrees[4] != 0)560return FALSE;561562if (degrees[3] != 6)563return FALSE;564565/* Partition the six image vertices into two sets of 3566(or report failure) */567568for (imageVertPos = 3; imageVertPos < 6; imageVertPos++)569{570I = success = 0;571do {572if (_TestPath(theGraph, imageVerts[imageVertPos], imageVerts[0]) == TRUE)573{574success = TRUE;575break;576}577578I++;579temp = imageVerts[I];580imageVerts[I] = imageVerts[imageVertPos];581imageVerts[imageVertPos] = temp;582} while (I < 3);583584if (!success)585return FALSE;586}587588/* Now test the paths between each of the first three vertices and589each of the last three vertices */590591for (I = 0; I < theGraph->N; I++)592theGraph->G[I].visited = 0;593594for (imageVertPos=0; imageVertPos<3; imageVertPos++)595for (I=3; I<6; I++)596if (_TestPath(theGraph, imageVerts[imageVertPos],597imageVerts[I]) != TRUE)598return FALSE;599600for (I = 0; I < theGraph->N; I++)601if (theGraph->G[I].visited)602degrees[2]--;603604/* If every degree 2 vertex is used in a path between image605vertices, then there are no extra pieces of the graph606in theGraph. Specifically, the prior tests identify607a K_{3,3} and ensure that nothing else could exist in the608graph except extra degree 2 vertices, which must be609joined in a cycle so that all are degree 2. */610611return degrees[2] == 0 ? TRUE : FALSE;612}613614/********************************************************************615_CheckKuratowskiSubgraphIntegrity()616617This function checks whether theGraph received as input contains618either a K_5 or K_{3,3} homeomorph.619620RETURNS: OK if theGraph contains a K_5 or K_{3,3} homeomorph,621NOTOK otherwise622623To be a K_5 homeomorph, there must be exactly 5 vertices of degree 4,624which are called 'image' vertices, and all other vertices must be625either degree 2 or degree 0. Furthermore, each of the image vertices626must be able to reach all of the other image vertices by a single edge627or a path of degree two vertices.628629To be a K_{3,3} homeomorph, there must be exactly 6 vertices of degree 3,630which are called 'image' vertices, and all other vertices must be either631degree 2 or degree 0. Furthermore, the image vertices must be connected632by edges or paths of degree two vertices in the manner suggested by633a K_{3,3}. To test this, we select an image vertex U, and determine634three image vertices X, Y and Z reachable from U by single edges or635paths of degree 2 vertices. Then, we check that the two remaining image636vertices, V and W, can also reach X, Y and Z by single edges or paths of637degree 2 vertices.638639It is not necessary to check that the paths between the image vertices640are distinct since if the paths had a common vertex, then the common641vertex would not be degree 2.642********************************************************************/643644int _CheckKuratowskiSubgraphIntegrity(graphP theGraph)645{646int degrees[5], imageVerts[6];647648if (_getImageVertices(theGraph, degrees, 4, imageVerts, 6) != OK)649return NOTOK;650651if (_TestForCompleteGraphObstruction(theGraph, 5, degrees, imageVerts) == TRUE)652{653return OK;654}655656if (_TestForK33GraphObstruction(theGraph, degrees, imageVerts) == TRUE)657{658return OK;659}660661return NOTOK;662}663664/********************************************************************665_TestForK23GraphObstruction()666667theGraph - the graph to test668degrees - array of counts of the number of vertices of each degree669given by the array index. Only valid up to numVerts-1670imageVerts - the degree 3 vertices of the K2,3 homeomorph671672This routine tests whether theGraph is a K_{2,3} homeomorph.673This routine operates over the results of _getImageVertices()674675returns TRUE if so, FALSE if not676********************************************************************/677678int _TestForK23GraphObstruction(graphP theGraph, int *degrees, int *imageVerts)679{680int I, J, imageVertPos;681682// This function operates over the imageVerts results produced by683// getImageVertices, which only finds vertices of degree 3 or higher.684// So, for a K2,3, there must be exactly two degree 3 vertices and685// no degree 4 vertices.686if (degrees[3] != 2)687return FALSE;688689// For K_{2,3}, the three vertices of degree 2 were not690// detected as image vertices because degree 2 vertices691// are indistinguishable from the internal path vertices692// between the image vertices. So, here we acknowledge693// that more image vertices need to be selected.694imageVertPos = 2;695696// Assign the remaining three image vertices to be the697// neighbors of the first degree 3 image vertex.698// Ensure that each is distinct from the second699// degree 3 image vertex. This must be the case because700// the two degree 3 image vertices are in the same partition701// and hence must not be adjacent.702703J = gp_GetFirstArc(theGraph, imageVerts[0]);704while (gp_IsArc(theGraph, J))705{706imageVerts[imageVertPos] = theGraph->G[J].v;707if (imageVerts[imageVertPos] == imageVerts[1])708return FALSE;709imageVertPos++;710J = gp_GetNextArc(theGraph, J);711}712713/* The paths from imageVerts[0] to each of the new degree 2714image vertices are the edges we just traversed.715Now test the paths between each of the degree 2 image716vertices and imageVerts[1]. */717718for (I = 0; I < theGraph->N; I++)719theGraph->G[I].visited = 0;720721for (imageVertPos=2; imageVertPos<5; imageVertPos++)722{723if (_TestPath(theGraph, imageVerts[imageVertPos],724imageVerts[1]) != TRUE)725return FALSE;726theGraph->G[imageVerts[imageVertPos]].visited = 1;727}728729for (I = 0; I < theGraph->N; I++)730if (theGraph->G[I].visited)731degrees[2]--;732733/* If every degree 2 vertex is used in a path between the734two degree 3 image vertices, then there are no extra735pieces of the graph in theGraph. Specifically, the736prior tests identify a K_{2,3} and ensure that nothing737else could exist in the graph... except extra degree 2738vertices joined in a cycle. We return NOTOK in that case. */739740return degrees[2] == 0 ? TRUE : FALSE;741}742743/********************************************************************744_CheckOuterplanarObstructionIntegrity()745746This function checks whether theGraph received as input contains747either a K_4 or K_{2,3} homeomorph.748749RETURNS: OK if theGraph contains a K_4 or K_{2,3} homeomorph,750NOTOK otherwise751752To be a K_4 homeomorph, there must be exactly 4 vertices of degree 3,753which are called 'image' vertices, and all other vertices must be754either degree 2 or degree 0. Furthermore, each of the image vertices755must be able to reach all of the other image vertices by a single edge756or a path of degree two vertices.757758To be a K_{2,3} homeomorph, there must be exactly 2 vertices of degree 3.759All other vertices must be degree 2. Furthermore, the two degree 3760vertices must have three internally disjoint paths connecting them,761and each path must contain at least two edges (i.e. at least one internal762vertex). The two degree 3 vertices are image vertices, and an internal763vertex from each of the three paths contributes the remaining three764image vertices.765766It is not necessary to check that the paths between the degree three767vertices are distinct since if the paths had a common vertex, then the768common vertex would not be degree 2.769********************************************************************/770771int _CheckOuterplanarObstructionIntegrity(graphP theGraph)772{773int degrees[4], imageVerts[5];774775if (_getImageVertices(theGraph, degrees, 3, imageVerts, 5) != OK)776return NOTOK;777778if (_TestForCompleteGraphObstruction(theGraph, 4, degrees, imageVerts) == TRUE)779{780return OK;781}782783if (_TestForK23GraphObstruction(theGraph, degrees, imageVerts) == TRUE)784{785return OK;786}787788/* We get here only if we failed to recognize an outerplanarity789obstruction, so we return failure */790791return NOTOK;792}793794/********************************************************************795_TestPath()796797This function determines whether there exists a path of degree two798vertices between two given vertices. The function marks each799degree two vertex as visited. It returns TRUE if it finds the800path and FALSE otherwise.801********************************************************************/802803int _TestPath(graphP theGraph, int U, int V)804{805int J;806807J = gp_GetFirstArc(theGraph, U);808while (gp_IsArc(theGraph, J))809{810if (_TryPath(theGraph, J, V) == OK)811{812_MarkPath(theGraph, J);813return TRUE;814}815816J = gp_GetNextArc(theGraph, J);817}818819return FALSE;820}821822/********************************************************************823_TryPath()824825This function seeks a given path to a vertex V starting with a826given edge out of a starting vertex U. The path is allowed to827contain zero or more degree two vertices, but we stop as soon as828a vertex of degree higher than two is encountered.829The function returns boolean true if that vertex is V, and830boolean false otherwise.831********************************************************************/832833int _TryPath(graphP theGraph, int J, int V)834{835int Jin, nextVertex;836837nextVertex = theGraph->G[J].v;838839// while nextVertex is strictly degree 2840while (gp_IsArc(theGraph, gp_GetFirstArc(theGraph, nextVertex)) &&841gp_IsArc(theGraph, gp_GetLastArc(theGraph, nextVertex)) &&842gp_GetNextArc(theGraph, gp_GetFirstArc(theGraph, nextVertex)) == gp_GetLastArc(theGraph, nextVertex))843{844Jin = gp_GetTwinArc(theGraph, J);845J = gp_GetFirstArc(theGraph, nextVertex);846if (J == Jin)847J = gp_GetLastArc(theGraph, nextVertex);848849nextVertex = theGraph->G[J].v;850}851852return nextVertex == V ? TRUE : FALSE;853}854855/********************************************************************856_MarkPath()857858This function sets the visitation flag on all degree two vertices859along a path to a vertex V that starts with a given edge out of860a starting vertex U.861********************************************************************/862863void _MarkPath(graphP theGraph, int J)864{865int Jin, nextVertex;866867nextVertex = theGraph->G[J].v;868// while nextVertex is strictly degree 2869while (gp_IsArc(theGraph, gp_GetFirstArc(theGraph, nextVertex)) &&870gp_IsArc(theGraph, gp_GetLastArc(theGraph, nextVertex)) &&871gp_GetNextArc(theGraph, gp_GetFirstArc(theGraph, nextVertex)) == gp_GetLastArc(theGraph, nextVertex))872{873theGraph->G[nextVertex].visited = 1;874875Jin = gp_GetTwinArc(theGraph, J);876J = gp_GetFirstArc(theGraph, nextVertex);877if (J == Jin)878J = gp_GetLastArc(theGraph, nextVertex);879880nextVertex = theGraph->G[J].v;881}882}883884/********************************************************************885_TestSubgraph()886Checks whether theSubgraph is in fact a subgraph of theGraph.887For each vertex v in graph G and subgraph H, we iterate the adjacency888list of H(v) and, for each neighbor w, we mark G(w). Then, we889iterate the adjacency list of G(v) and unmark each neighbor. Then,890we iterate the adjacency list of H(v) again to ensure that every891neighbor w was unmarked. If there exists a marked neighbor, then892H(v) contains an incident edge that is not incident to G(v).893894Returns TRUE if theSubgraph contains only edges from theGraph,895FALSE otherwise896********************************************************************/897898int _TestSubgraph(graphP theSubgraph, graphP theGraph)899{900int I, J;901int Result = TRUE;902int invokeSortOnGraph = FALSE;903int invokeSortOnSubgraph = FALSE;904905// If the graph is not sorted by DFI, but the alleged subgraph is,906// then "unsort" the alleged subgraph so both have the same vertex order907if (!(theGraph->internalFlags & FLAGS_SORTEDBYDFI) &&908(theSubgraph->internalFlags & FLAGS_SORTEDBYDFI))909{910invokeSortOnSubgraph = TRUE;911gp_SortVertices(theSubgraph);912}913914// If the graph is not sorted by DFI, but the alleged subgraph is,915// then "unsort" the alleged subgraph so both have the same vertex order916if (!(theSubgraph->internalFlags & FLAGS_SORTEDBYDFI) &&917(theGraph->internalFlags & FLAGS_SORTEDBYDFI))918{919invokeSortOnGraph = TRUE;920gp_SortVertices(theGraph);921}922923/* We clear all visitation flags */924925for (I = 0; I < theGraph->N; I++)926theGraph->G[I].visited = 0;927928/* For each vertex... */929930for (I = 0; I < theSubgraph->N; I++)931{932/* For each neighbor w in the adjacency list of vertex I in the933subgraph, set the visited flag in w in the graph */934935J = gp_GetFirstArc(theSubgraph, I);936while (gp_IsArc(theSubgraph, J))937{938if (theSubgraph->G[J].v == NIL)939{940Result = FALSE;941break;942}943theGraph->G[theSubgraph->G[J].v].visited = 1;944J = gp_GetNextArc(theSubgraph, J);945}946947if (Result != TRUE)948break;949950/* For each neighbor w in the adjacency list of vertex I in the graph,951clear the visited flag in w in the graph */952953J = gp_GetFirstArc(theGraph, I);954while (gp_IsArc(theGraph, J))955{956if (theGraph->G[J].v == NIL)957{958Result = FALSE;959break;960}961theGraph->G[theGraph->G[J].v].visited = 0;962J = gp_GetNextArc(theGraph, J);963}964965if (Result != TRUE)966break;967968/* For each neighbor w in the adjacency list of vertex I in the969subgraph, set the visited flag in w in the graph */970971J = gp_GetFirstArc(theSubgraph, I);972while (gp_IsArc(theSubgraph, J))973{974if (theGraph->G[theSubgraph->G[J].v].visited)975{976Result = FALSE;977break;978}979J = gp_GetNextArc(theSubgraph, J);980}981982if (Result != TRUE)983break;984}985986// Restore the DFI sort order of either graph if it had to be reordered at the start987if (invokeSortOnSubgraph)988gp_SortVertices(theSubgraph);989if (invokeSortOnGraph)990gp_SortVertices(theGraph);991992return Result;993}994995996