Path: blob/master/sage/graphs/planarity/graphK4Search.c
4079 views
/*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#include "graphK4Search.h"45#include "graphK4Search.private.h"4647extern int K4SEARCH_ID;4849#include "graph.h"5051/* Imported functions */5253extern void _ClearIsolatorContext(graphP theGraph);54extern void _FillVisitedFlags(graphP, int);55extern int _FillVisitedFlagsInBicomp(graphP theGraph, int BicompRoot, int FillValue);56extern int _FillVisitedFlagsInOtherBicomps(graphP theGraph, int BicompRoot, int FillValue);57extern void _FillVisitedFlagsInUnembeddedEdges(graphP theGraph, int FillValue);58extern int _SetVertexTypeInBicomp(graphP theGraph, int BicompRoot, int theType);59extern int _DeleteUnmarkedEdgesInBicomp(graphP theGraph, int BicompRoot);60extern int _ComputeArcType(graphP theGraph, int a, int b, int edgeType);61extern int _SetEdgeType(graphP theGraph, int u, int v);6263extern int _GetNextVertexOnExternalFace(graphP theGraph, int curVertex, int *pPrevLink);64extern int _JoinBicomps(graphP theGraph);65extern void _FindActiveVertices(graphP theGraph, int R, int *pX, int *pY);66extern int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns);67extern int _OrientVerticesInEmbedding(graphP theGraph);68extern void _InvertVertex(graphP theGraph, int V);69extern int _SetVisitedOnPath(graphP theGraph, int u, int v, int w, int x, int visited);70extern int _OrientExternalFacePath(graphP theGraph, int u, int v, int w, int x);7172extern int _FindUnembeddedEdgeToAncestor(graphP theGraph, int cutVertex, int *pAncestor, int *pDescendant);73extern int _FindUnembeddedEdgeToCurVertex(graphP theGraph, int cutVertex, int *pDescendant);74extern int _GetLeastAncestorConnection(graphP theGraph, int cutVertex);7576extern int _SetVertexTypesForMarkingXYPath(graphP theGraph);77extern int _MarkHighestXYPath(graphP theGraph);78extern int _MarkPathAlongBicompExtFace(graphP theGraph, int startVert, int endVert);79extern int _AddAndMarkEdge(graphP theGraph, int ancestor, int descendant);80extern int _DeleteUnmarkedVerticesAndEdges(graphP theGraph);8182extern int _IsolateOuterplanarityObstructionA(graphP theGraph);83extern int _IsolateOuterplanarityObstructionB(graphP theGraph);84extern int _IsolateOuterplanarityObstructionE(graphP theGraph);8586/* Private functions for K4 searching (exposed to the extension). */8788int _SearchForK4(graphP theGraph, int I);89int _SearchForK4InBicomp(graphP theGraph, K4SearchContext *context, int I, int R);9091/* Private functions for K4 searching. */9293int _K4_ChooseTypeOfNonOuterplanarityMinor(graphP theGraph, int I, int R);9495int _K4_FindSecondActiveVertexOnLowExtFacePath(graphP theGraph);96int _K4_FindPlanarityActiveVertex(graphP theGraph, int I, int R, int prevLink, int *pW);97int _K4_FindSeparatingInternalEdge(graphP theGraph, int R, int prevLink, int A, int *pW, int *pX, int *pY);98void _K4_SetTypeOnExternalFacePath(graphP theGraph, int R, int prevLink, int A, int type);99100int _K4_IsolateMinorA1(graphP theGraph);101int _K4_IsolateMinorA2(graphP theGraph);102int _K4_IsolateMinorB1(graphP theGraph);103int _K4_IsolateMinorB2(graphP theGraph);104105int _K4_ReduceBicompToEdge(graphP theGraph, K4SearchContext *context, int R, int W);106int _K4_ReducePathComponent(graphP theGraph, K4SearchContext *context, int R, int prevLink, int A);107int _K4_ReducePathToEdge(graphP theGraph, K4SearchContext *context, int edgeType, int R, int e_R, int A, int e_A);108109int _K4_GetCumulativeOrientationOnDFSPath(graphP theGraph, int ancestor, int descendant);110int _K4_TestPathComponentForAncestor(graphP theGraph, int R, int prevLink, int A);111void _K4_SetVisitedInPathComponent(graphP theGraph, int R, int prevLink, int A, int fill);112int _K4_DeleteUnmarkedEdgesInPathComponent(graphP theGraph, int R, int prevLink, int A);113114int _K4_RestoreReducedPath(graphP theGraph, K4SearchContext *context, int J);115int _K4_RestoreAndOrientReducedPaths(graphP theGraph, K4SearchContext *context);116117int _MarkEdge(graphP theGraph, int x, int y);118119/****************************************************************************120_SearchForK4InBicomps()121122This method is the main handler for a blocked iteration of the core123planarity/outerplanarity algorithm. At the end of processing for the124current vertex I, if there is unresolved pertinence for I (i.e. if125there are unembedded forward arcs from I to its DFS descendants), then126this method is called.127128In this case, the fwdArcList of I is non-empty; it is also sorted by129DFI of the descendants endpoints. Also, the sortedDFSChildList of I130is non-empty, and it is also sorted by DFI of the children.131132Any DFS child C of I that has a p2dFwdArcCount of zero is simply133removed from the sortedDFSChildList of I as it is encountered because134the zero value means that there are no unembedded forward arcs from I135to descendants of C.136137As soon as a DFS child C is encountered that has a p2dFwdArcCount138greater than zero, we simply invoke SearchForK4InBicomp(). The result139will either be an isolated K4 homeomorph, or an indication that a140reduction has occurred. In the latter case, the WalkDown can be141invoked to resolve more of the pertinence of the bicomp. The WalkDown142may or may not resolve all the remaining pertinence in the DFS143subtree rooted by C. If it does, then the p2dFwdArcCount of C will144drop to zero, and the next iteration of the loop in this method will145remove C from the sortedDFSChildList of I. If the p2dFwdArcCount of146C does not drop to zero during the WalkDown, then the bicomp that147contains C is ready for another search in the next iteration of the148main loop of this method.149150Overall, the only two legitimate outcomes of this method call are1511) reductions have enabled further WalkDown calls to resolve all152of the pertinence of I1532) a subgraph homeomorphic to K4 has been isolated154155Returns156OK if the pertinence of I was fully resolved, indicating that the157core planarity/outerplanarity algorithm can proceed158NONEMBEDDABLE if a subgraph homeomorphic to K4 has been isolated159NOTOK on failure160****************************************************************************/161162int _SearchForK4InBicomps(graphP theGraph, int I)163{164int C, R, RetVal=OK;165K4SearchContext *context = NULL;166167gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);168if (context == NULL)169return NOTOK;170171while ((C = context->V[I].sortedDFSChildList) != NIL)172{173if (context->V[C].p2dFwdArcCount == 0)174{175context->V[I].sortedDFSChildList = LCDelete(176context->sortedDFSChildLists,177context->V[I].sortedDFSChildList, C);178}179else180{181R = C + theGraph->N;182RetVal = _SearchForK4InBicomp(theGraph, context, I, R);183if (RetVal != OK)184break;185if ((RetVal = theGraph->functions.fpWalkDown(theGraph, I, R)) != OK)186return RetVal;187}188}189190return RetVal;191}192193/****************************************************************************194_SearchForK4InBicomp()195****************************************************************************/196197int _SearchForK4InBicomp(graphP theGraph, K4SearchContext *context, int I, int R)198{199isolatorContextP IC = &theGraph->IC;200201if (context == NULL)202{203gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);204if (context == NULL)205return NOTOK;206}207208// Begin by determining whether minor A, B or E is detected209if (_K4_ChooseTypeOfNonOuterplanarityMinor(theGraph, I, R) != OK)210return NOTOK;211212// Minor A indicates the existence of K_{2,3} homeomorphs, but213// we run additional tests to see whether we can either find an214// entwined K4 homeomorph or reduce the bicomp so that the WalkDown215// is enabled to continue to resolve pertinence216if (theGraph->IC.minorType & MINORTYPE_A)217{218// Now that we know we have minor A, we can afford to orient the219// bicomp because we will either find the desired K4 or we will220// reduce the bicomp to an edge. The tests for A1 and A2 are easier221// to implement on an oriented bicomp.222// NOTE: We're in the midst of the WalkDown, so the stack may be223// non-empty, and it has to be preserved with constant cost.224// The stack will have at most 4 integers per cut vertex225// merge point, and this operation will push at most two226// integers per tree edge in the bicomp, so the stack227// will not overflow.228if (sp_GetCapacity(theGraph->theStack) < 6*theGraph->N)229return NOTOK;230231if (_OrientVerticesInBicomp(theGraph, R, 1) != OK)232return NOTOK;233234// Case A1: Test whether there is an active vertex Z other than W235// along the external face path [X, ..., W, ..., Y]236if (_K4_FindSecondActiveVertexOnLowExtFacePath(theGraph) == TRUE)237{238// Now that we know we can find a K4, the Walkdown will not continue239// and we can do away with the stack content.240sp_ClearStack(theGraph->theStack);241242// Restore the orientations of the vertices in the bicomp, then orient243// the whole embedding, so we can restore and orient the reduced paths244if (_OrientVerticesInBicomp(theGraph, R, 1) != OK ||245_OrientVerticesInEmbedding(theGraph) != OK ||246_K4_RestoreAndOrientReducedPaths(theGraph, context) != OK)247return NOTOK;248249// Set up to isolate K4 homeomorph250_FillVisitedFlags(theGraph, 0);251252if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE)253return NOTOK;254255if (IC->uz < IC->v)256{257if (_FindUnembeddedEdgeToAncestor(theGraph, IC->z, &IC->uz, &IC->dz) != TRUE)258return NOTOK;259}260else261{262if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->z, &IC->dz) != TRUE)263return NOTOK;264}265266// Isolate the K4 homeomorph267if (_K4_IsolateMinorA1(theGraph) != OK ||268_DeleteUnmarkedVerticesAndEdges(theGraph) != OK)269return NOTOK;270271// Indicate success by returning NONEMBEDDABLE272return NONEMBEDDABLE;273}274275// Case A2: Test whether the bicomp has an XY path276// NOTE: As mentioned above, the stack is also preserved here.277// It will have at most 4 integers per cut vertex merge point,278// and this operation will push at most one integer per tree279// edge in the bicomp, so the stack will not overflow.280if (_SetVertexTypesForMarkingXYPath(theGraph) != OK)281return NOTOK;282283// Marking the X-Y path relies on the bicomp visited flags being zero284if (_FillVisitedFlagsInBicomp(theGraph, R, 0) != OK)285return NOTOK;286287// NOTE: This call preserves the stack and does not overflow. There288// are at most 4 integers per cut vertex merge point, all of which289// are not in the bicomp, and this call pushes at most 3 integers290// per bicomp vertex, so the maximum stack requirement is 4N291if (_MarkHighestXYPath(theGraph) == TRUE)292{293// Now that we know we can find a K4, the Walkdown will not continue294// and we can do away with the stack content.295sp_ClearStack(theGraph->theStack);296297// Restore the orientations of the vertices in the bicomp, then orient298// the whole embedding, so we can restore and orient the reduced paths299if (_OrientVerticesInBicomp(theGraph, R, 1) != OK ||300_OrientVerticesInEmbedding(theGraph) != OK ||301_K4_RestoreAndOrientReducedPaths(theGraph, context) != OK)302return NOTOK;303304// Set up to isolate K4 homeomorph305_FillVisitedFlags(theGraph, 0);306307if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE)308return NOTOK;309310// Isolate the K4 homeomorph311if (_MarkHighestXYPath(theGraph) != TRUE ||312_K4_IsolateMinorA2(theGraph) != OK ||313_DeleteUnmarkedVerticesAndEdges(theGraph) != OK)314return NOTOK;315316// Indicate success by returning NONEMBEDDABLE317return NONEMBEDDABLE;318}319320// else if there was no X-Y path, then we restore the vertex types to321// unknown (though it would suffice to do it just to R and W)322if (_SetVertexTypeInBicomp(theGraph, R, TYPE_UNKNOWN) != OK)323return NOTOK;324325// Since neither A1 nor A2 is found, then we reduce the bicomp to the326// tree edge (R, W).327// NOTE: The visited flags for R and W are restored to values appropriate328// for continuing with future embedding steps329// NOTE: This method invokes several routines that use the stack, but330// all of them preserve the stack and each pushes at most one331// integer per bicomp vertex and pops all of them before returning.332// Again, this means the stack will not overflow.333if (_K4_ReduceBicompToEdge(theGraph, context, R, IC->w) != OK)334return NOTOK;335336// Return OK so that the WalkDown can continue resolving the pertinence of I.337return OK;338}339340// Minor B also indicates the existence of K_{2,3} homeomorphs, but341// we run additional tests to see whether we can either find an342// entwined K4 homeomorph or reduce a portion of the bicomp so that343// the WalkDown can be reinvoked on the bicomp344else if (theGraph->IC.minorType & MINORTYPE_B)345{346int a_x, a_y;347348// Reality check on stack state349if (sp_NonEmpty(theGraph->theStack))350return NOTOK;351352// Find the vertices a_x and a_y that are active (pertinent or future pertinent)353// and also first along the external face paths emanating from the bicomp root354if (_K4_FindPlanarityActiveVertex(theGraph, I, R, 1, &a_x) != OK ||355_K4_FindPlanarityActiveVertex(theGraph, I, R, 0, &a_y) != OK)356return NOTOK;357358// Case B1: If both a_x and a_y are future pertinent, then we can stop and359// isolate a subgraph homeomorphic to K4.360if (a_x != a_y && FUTUREPERTINENT(theGraph, a_x, I) && FUTUREPERTINENT(theGraph, a_y, I))361{362if (_OrientVerticesInEmbedding(theGraph) != OK ||363_K4_RestoreAndOrientReducedPaths(theGraph, context) != OK)364return NOTOK;365366// Set up to isolate K4 homeomorph367_FillVisitedFlags(theGraph, 0);368369IC->x = a_x;370IC->y = a_y;371372if (_FindUnembeddedEdgeToAncestor(theGraph, IC->x, &IC->ux, &IC->dx) != TRUE ||373_FindUnembeddedEdgeToAncestor(theGraph, IC->y, &IC->uy, &IC->dy) != TRUE)374return NOTOK;375376// Isolate the K4 homeomorph377if (_K4_IsolateMinorB1(theGraph) != OK ||378_DeleteUnmarkedVerticesAndEdges(theGraph) != OK)379return NOTOK;380381// Indicate success by returning NONEMBEDDABLE382return NONEMBEDDABLE;383}384385// Reality check: The bicomp with root R is pertinent, and the only386// pertinent or future pertinent vertex on the external face is a_x,387// so it must also be pertinent.388if (a_x == a_y && !PERTINENT(theGraph, a_x))389return NOTOK;390391// Case B2: Determine whether there is an internal separating X-Y path for a_x or for a_y392// The method makes appropriate isolator context settings if the separator edge is found393if (_K4_FindSeparatingInternalEdge(theGraph, R, 1, a_x, &IC->w, &IC->px, &IC->py) == TRUE ||394_K4_FindSeparatingInternalEdge(theGraph, R, 0, a_y, &IC->w, &IC->py, &IC->px) == TRUE)395{396if (_OrientVerticesInEmbedding(theGraph) != OK ||397_K4_RestoreAndOrientReducedPaths(theGraph, context) != OK)398return NOTOK;399400// Set up to isolate K4 homeomorph401_FillVisitedFlags(theGraph, 0);402403if (PERTINENT(theGraph, IC->w))404{405if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE)406return NOTOK;407}408else409{410IC->z = IC->w;411if (_FindUnembeddedEdgeToAncestor(theGraph, IC->z, &IC->uz, &IC->dz) != TRUE)412return NOTOK;413}414415// The X-Y path doesn't have to be the same one that was associated with the416// separating internal edge.417if (_SetVertexTypesForMarkingXYPath(theGraph) != OK ||418_MarkHighestXYPath(theGraph) != TRUE)419return NOTOK;420421// Isolate the K4 homeomorph422if (_K4_IsolateMinorB2(theGraph) != OK ||423_DeleteUnmarkedVerticesAndEdges(theGraph) != OK)424return NOTOK;425426// Indicate success by returning NONEMBEDDABLE427return NONEMBEDDABLE;428}429430// If K_4 homeomorph not found, make reductions along a_x and a_y paths.431if (a_x == a_y)432{433// In the special case where both paths lead to the same vertex, we can434// reduce the bicomp to a single edge, which avoids issues of reversed435// orientation between the bicomp root and the vertex.436if (_K4_ReduceBicompToEdge(theGraph, context, R, a_x) != OK)437return NOTOK;438}439else440{441// When a_x and a_y are distinct, we reduce each path from root to the vertex442if (_K4_ReducePathComponent(theGraph, context, R, 1, a_x) != OK ||443_K4_ReducePathComponent(theGraph, context, R, 0, a_y) != OK)444return NOTOK;445}446447// Return OK to indicate that WalkDown processing may proceed to resolve448// more of the pertinence of this bicomp.449return OK;450}451452// Minor E indicates the desired K4 homeomorph, so we isolate it and return NONEMBEDDABLE453else if (theGraph->IC.minorType & MINORTYPE_E)454{455// Reality check on stack state456if (sp_NonEmpty(theGraph->theStack))457return NOTOK;458459// Impose consistent orientation on the embedding so we can then460// restore the reduced paths.461if (_OrientVerticesInEmbedding(theGraph) != OK ||462_K4_RestoreAndOrientReducedPaths(theGraph, context) != OK)463return NOTOK;464465// Set up to isolate minor E466_FillVisitedFlags(theGraph, 0);467468if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE)469return NOTOK;470471if (_SetVertexTypesForMarkingXYPath(theGraph) != OK)472return NOTOK;473if (_MarkHighestXYPath(theGraph) != TRUE)474return NOTOK;475476// Isolate the K4 homeomorph477if (_IsolateOuterplanarityObstructionE(theGraph) != OK ||478_DeleteUnmarkedVerticesAndEdges(theGraph) != OK)479return NOTOK;480481// Return indication that K4 homeomorph has been found482return NONEMBEDDABLE;483}484485// You never get here in an error-free implementation like this one486return NOTOK;487}488489/****************************************************************************490_K4_ChooseTypeOfNonOuterplanarityMinor()491This is an overload of the function _ChooseTypeOfNonOuterplanarityMinor()492that avoids processing the whole bicomp rooted by R, e.g. to orient its493vertices or label the vertices of its external face.494This is necessary in particular because of the reduction processing on495MINORTYPE_B. When a K2,3 is found by minor B, we may not be able to find496an entangled K4, so a reduction is performed, but it only eliminates497part of the bicomp and the operations here need to avoid touching parts498of the bicomp that won't be reduced, except by a constant amount of course.499****************************************************************************/500501int _K4_ChooseTypeOfNonOuterplanarityMinor(graphP theGraph, int I, int R)502{503int XPrevLink=1, YPrevLink=0;504int Wx, WxPrevLink, Wy, WyPrevLink;505506_ClearIsolatorContext(theGraph);507508theGraph->IC.v = I;509theGraph->IC.r = R;510511// Reality check on data structure integrity512if (!gp_IsArc(theGraph, gp_GetFirstArc(theGraph, R)))513return NOTOK;514515// We are essentially doing a _FindActiveVertices() here, except two things:516// 1) for outerplanarity we know the first vertices along the paths from R517// are the desired vertices because all vertices are "externally active"518// 2) We have purposely not oriented the bicomp, so the XPrevLink result is519// needed to help find the pertinent vertex W520theGraph->IC.x = _GetNextVertexOnExternalFace(theGraph, R, &XPrevLink);521theGraph->IC.y = _GetNextVertexOnExternalFace(theGraph, R, &YPrevLink);522523// We are essentially doing a _FindPertinentVertex() here, except two things:524// 1) It is not known whether the reduction of the path through X or the path525// through Y will enable the pertinence of W to be resolved, so it is526// necessary to perform parallel face traversal to find W with a cost no527// more than twice what it will take to resolve the W's pertinence528// (assuming we have to do a reduction rather than finding an entangled K4)529// 2) In the normal _FindPertinentVertex(), the bicomp is already oriented, so530// the "prev link" is hard coded to traverse down the X side. In this531// implementation, the bicomp is purposely not oriented, so we need to know532// XPrevLink and YPrevLink in order to set off in the correct directions.533Wx = theGraph->IC.x;534WxPrevLink = XPrevLink;535Wy = theGraph->IC.y;536WyPrevLink = YPrevLink;537theGraph->IC.w = NIL;538539while (Wx != theGraph->IC.y)540{541Wx = _GetNextVertexOnExternalFace(theGraph, Wx, &WxPrevLink);542if (PERTINENT(theGraph, Wx))543{544theGraph->IC.w = Wx;545break;546}547Wy = _GetNextVertexOnExternalFace(theGraph, Wy, &WyPrevLink);548if (PERTINENT(theGraph, Wy))549{550theGraph->IC.w = Wy;551break;552}553}554555if (theGraph->IC.w == NIL)556return NOTOK;557558// If the root copy is not a root copy of the current vertex I,559// then the Walkdown terminated on a descendant bicomp, which is Minor A.560if (theGraph->V[R - theGraph->N].DFSParent != I)561theGraph->IC.minorType |= MINORTYPE_A;562563// If W has a pertinent child bicomp, then we've found Minor B.564// Notice this is different from planarity, in which minor B is indicated565// only if the pertinent child bicomp is also externally active under the566// planarity processing model (i.e. future pertinent).567else if (theGraph->V[theGraph->IC.w].pertinentBicompList != NIL)568theGraph->IC.minorType |= MINORTYPE_B;569570// The only other result is minor E (we will search for the X-Y path later)571else572theGraph->IC.minorType |= MINORTYPE_E;573574return OK;575}576577/****************************************************************************578_K4_FindSecondActiveVertexOnLowExtFacePath()579580This method is used in the processing of obstruction A, so it can take581advantage of the bicomp being oriented beforehand.582583This method determines whether there is an active vertex Z other than W on584the path [X, ..., W, ..., Y]. By active, we mean a vertex that connects585by an unembedded edge to either I or an ancestor of I. That is, a vertext586that is pertinent or future pertinent (would be pertinent in a future step587of the embedder).588589Unlike the core planarity embedder, in outerplanarity-related algorithms,590future pertinence is different from external activity, and we need to know591about *actual connections* from each vertex to ancestors of IC.v, so we592use PERTINENT() and FUTUREPERTINENT() rather than _VertexActiveStatus().593****************************************************************************/594595int _K4_FindSecondActiveVertexOnLowExtFacePath(graphP theGraph)596{597int Z=theGraph->IC.r, ZPrevLink=1;598599// First we test X for future pertinence only (if it were pertinent, then600// we wouldn't have been blocked up on this bicomp)601Z = _GetNextVertexOnExternalFace(theGraph, Z, &ZPrevLink);602if (FUTUREPERTINENT(theGraph, Z, theGraph->IC.v))603{604theGraph->IC.z = Z;605theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, Z);606return TRUE;607}608609// Now we move on to test all the vertices strictly between X and Y on610// the lower external face path, except W, for either pertinence or611// future pertinence.612Z = _GetNextVertexOnExternalFace(theGraph, Z, &ZPrevLink);613614while (Z != theGraph->IC.y)615{616if (Z != theGraph->IC.w)617{618if (FUTUREPERTINENT(theGraph, Z, theGraph->IC.v))619{620theGraph->IC.z = Z;621theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, Z);622return TRUE;623}624else if (PERTINENT(theGraph, Z))625{626theGraph->IC.z = Z;627theGraph->IC.uz = theGraph->IC.v;628return TRUE;629}630}631632Z = _GetNextVertexOnExternalFace(theGraph, Z, &ZPrevLink);633}634635// Now we test Y for future pertinence (same explanation as for X above)636if (FUTUREPERTINENT(theGraph, Z, theGraph->IC.v))637{638theGraph->IC.z = Z;639theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, Z);640return TRUE;641}642643// We didn't find the desired second vertex, so report FALSE644return FALSE;645}646647/****************************************************************************648_K4_FindPlanarityActiveVertex()649This service routine starts out at R and heads off in the direction opposite650the prevLink to find the first "planarity active" vertex, i.e. the first one651that is pertinent or future pertinent.652****************************************************************************/653654int _K4_FindPlanarityActiveVertex(graphP theGraph, int I, int R, int prevLink, int *pW)655{656int W = R, WPrevLink = prevLink;657658W = _GetNextVertexOnExternalFace(theGraph, R, &WPrevLink);659660while (W != R)661{662if (PERTINENT(theGraph, W) || FUTUREPERTINENT(theGraph, W, I))663{664*pW = W;665return OK;666}667668W = _GetNextVertexOnExternalFace(theGraph, W, &WPrevLink);669}670671return NOTOK;672}673674/****************************************************************************675_K4_FindSeparatingInternalEdge()676677Logically, this method is similar to calling MarkHighestXYPath() to678see if there is an internal separator between R and A.679However, that method cannot be called because the bicomp is not oriented.680681Because this is an outerplanarity related algorithm, there are no internal682vertices to contend with, so it is easier to inspect the internal edges683incident to each vertex internal to the path (R ... A), i.e. excluding endpoints,684to see whether any of the edges connects outside of the path [R ... A],685including endpoints.686687We will count on the pre-initialization of the vertex types to TYPE_UNKNOWN688so that we don't have to initialize the whole bicomp. Each vertex along689the path [R ... A] is marked TYPE_VERTEX_VISITED. Then, for each vertex in the690range (R ... A), if there is any edge that is also not incident to a vertex691with TYPE_UNKNOWN, then that edge is the desired separator edge between692R and W. We mark that edge and save information about it.693694If the separator edge is found, then this method sets the *pW to A, and it695sets *pX and *pY values with the endpoints of the separator edge.696No visited flags are set at this time because it is easier to set them later.697698Lastly, we put the vertex types along [R ... A] back to TYPE_UNKNOWN.699700Returns TRUE if separator edge found or FALSE otherwise701****************************************************************************/702703int _K4_FindSeparatingInternalEdge(graphP theGraph, int R, int prevLink, int A, int *pW, int *pX, int *pY)704{705int Z, ZPrevLink, J, neighbor;706707// Mark the vertex types along the path [R ... A] as visited708_K4_SetTypeOnExternalFacePath(theGraph, R, prevLink, A, TYPE_VERTEX_VISITED);709710// Search each of the vertices in the range (R ... A)711*pX = *pY = NIL;712ZPrevLink = prevLink;713Z = _GetNextVertexOnExternalFace(theGraph, R, &ZPrevLink);714while (Z != A)715{716// Search for a separator among the edges of Z717// It is OK to not bother skipping the external face edges, since we718// know they are marked with TYPE_VERTEX_VISITED719J = gp_GetFirstArc(theGraph, Z);720while (gp_IsArc(theGraph, J))721{722neighbor = theGraph->G[J].v;723if (theGraph->G[neighbor].type == TYPE_UNKNOWN)724{725*pW = A;726*pX = Z;727*pY = neighbor;728break;729}730J = gp_GetNextArc(theGraph, J);731}732733// If we found the separator edge, then we don't need to go on734if (*pX != NIL)735break;736737// Go to the next vertex738Z = _GetNextVertexOnExternalFace(theGraph, Z, &ZPrevLink);739}740741// Restore the vertex types along the path [R ... A] to the unknown state742_K4_SetTypeOnExternalFacePath(theGraph, R, prevLink, A, TYPE_UNKNOWN);743744return *pX == NIL ? FALSE : TRUE;745}746747/****************************************************************************748_K4_SetTypeOnExternalFacePath()749750Assumes A is a vertex along the external face of the bicomp rooted by R.751Places 'type' into the type member of vertices along the path (R ... A)752that begins with R's link[1^prevLink] arc.753****************************************************************************/754755void _K4_SetTypeOnExternalFacePath(graphP theGraph, int R, int prevLink, int A, int type)756{757int Z, ZPrevLink;758759theGraph->G[R].type = type;760ZPrevLink = prevLink;761Z = R;762while (Z != A)763{764Z = _GetNextVertexOnExternalFace(theGraph, Z, &ZPrevLink);765theGraph->G[Z].type = type;766}767}768769/****************************************************************************770_K4_IsolateMinorA1()771772This pattern is essentially outerplanarity minor A, a K_{2,3}, except we get773a K_4 via the additional path from some vertex Z to the current vertex.774This path may be via some descendant of Z, and it may be a future pertinent775connection to an ancestor of the current vertex.776****************************************************************************/777778int _K4_IsolateMinorA1(graphP theGraph)779{780isolatorContextP IC = &theGraph->IC;781782if (IC->uz < IC->v)783{784if (theGraph->functions.fpMarkDFSPath(theGraph, IC->uz, IC->v) != OK)785return NOTOK;786}787788if (theGraph->functions.fpMarkDFSPath(theGraph, IC->z, IC->dz) != OK)789return NOTOK;790791if (_IsolateOuterplanarityObstructionA(theGraph) != OK)792return NOTOK;793794if (_AddAndMarkEdge(theGraph, IC->uz, IC->dz) != OK)795return NOTOK;796797return OK;798}799800/****************************************************************************801_K4_IsolateMinorA2()802803This pattern is essentially outerplanarity minor A, a K_{2,3}, except we get804a K_4 via an additional X-Y path within the main bicomp, which is guaranteed805to exist by the time this method is invoked.806One might think to simply invoke _MarkHighestXYPath() to obtain the path,807but the IC->px and IC->py values are already set before invoking this method,808and the bicomp is outerplanar, so the XY path is just an edge. Also, one809subcase of pattern B2 reduces to this pattern, except that the XY path is810determined by the B2 isolator.811****************************************************************************/812813int _K4_IsolateMinorA2(graphP theGraph)814{815isolatorContextP IC = &theGraph->IC;816817// We assume the X-Y path was already marked818if (!theGraph->G[IC->px].visited || !theGraph->G[IC->py].visited)819return NOTOK;820821return _IsolateOuterplanarityObstructionA(theGraph);822}823824/****************************************************************************825_K4_IsolateMinorB1()826827It is possible to get a K_4 based on the pertinence of w, but we don't do it828that way. If we use the pertinence of w, then we have to eliminate part of829the bicomp external face, which has special cases if a_x==w or a_y==w.830Typically we would mark (r ... a_x ... w ... a_y), which works even when a_y==w,831but if instead a_x==w, then we'd have to mark (w ... a_y ... r).832833Since a_x and a_y are guaranteed to be distinct, it is easier to just ignore834the pertinence of w, and instead use the lower bicomp external face path835as the connection between a_x and a_y. This includes w, but then the836isolation process is the same even if a_x==w or a_y==w. The other two837connections for a_x and a_y are to v and MAX(ux, uy).838****************************************************************************/839840int _K4_IsolateMinorB1(graphP theGraph)841{842isolatorContextP IC = &theGraph->IC;843844if (theGraph->functions.fpMarkDFSPath(theGraph, IC->x, IC->dx) != OK)845return NOTOK;846847if (theGraph->functions.fpMarkDFSPath(theGraph, IC->y, IC->dy) != OK)848return NOTOK;849850// The path from the bicomp root to MIN(ux,uy) is marked to ensure the851// connection from the image vertices v and MAX(ux,uy) as well as the852// connection from MAX(ux,uy) through MIN(ux,uy) to (ux==MIN(ux,uy)?x:y)853if (theGraph->functions.fpMarkDFSPath(theGraph, MIN(IC->ux, IC->uy), IC->r) != OK)854return NOTOK;855856// This makes the following connections (a_x ... v), (a_y ... v), and857// (a_x ... w ... a_y), the last being tolerant of a_x==w or a_y==w858if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK)859return NOTOK;860861if (_JoinBicomps(theGraph) != OK)862return NOTOK;863864if (_AddAndMarkEdge(theGraph, IC->ux, IC->dx) != OK)865return NOTOK;866867if (_AddAndMarkEdge(theGraph, IC->uy, IC->dy) != OK)868return NOTOK;869870return OK;871}872873/****************************************************************************874_K4_IsolateMinorB2()875876The first subcase of B2 can be reduced to outerplanarity obstruction E877The second subcase of B2 can be reduced to A2 by changing v to u878****************************************************************************/879880int _K4_IsolateMinorB2(graphP theGraph)881{882isolatorContextP IC = &theGraph->IC;883884// First subcase, the active vertex is pertinent885if (PERTINENT(theGraph, IC->w))886{887// We assume the X-Y path was already marked888if (!theGraph->G[IC->px].visited || !theGraph->G[IC->py].visited)889return NOTOK;890891return _IsolateOuterplanarityObstructionE(theGraph);892}893894// Second subcase, the active vertex is future pertinent895else if (FUTUREPERTINENT(theGraph, IC->w, IC->v))896{897IC->v = IC->uz;898IC->dw = IC->dz;899900return _K4_IsolateMinorA2(theGraph);901}902903return OK;904}905906/****************************************************************************907_K4_ReduceBicompToEdge()908909This method is used when reducing the main bicomp of obstruction A to a910single edge (R, W). We first delete all edges from the bicomp except911those on the DFS tree path W to R, then we reduce that DFS tree path to912a DFS tree edge.913914After the reduction, the outerplanarity Walkdown traversal can continue915R to W without being blocked as was the case when R was adjacent to X and Y.916917Returns OK for success, NOTOK for internal (implementation) error.918****************************************************************************/919920int _K4_ReduceBicompToEdge(graphP theGraph, K4SearchContext *context, int R, int W)921{922int newEdge;923924if (_OrientVerticesInBicomp(theGraph, R, 0) != OK ||925_FillVisitedFlagsInBicomp(theGraph, R, 0) != OK)926return NOTOK;927if (theGraph->functions.fpMarkDFSPath(theGraph, R, W) != OK)928return NOTOK;929if (_DeleteUnmarkedEdgesInBicomp(theGraph, R) != OK)930return NOTOK;931932// Now we have to reduce the path W -> R to the DFS tree edge (R, W)933newEdge =_K4_ReducePathToEdge(theGraph, context, EDGE_DFSPARENT,934R, gp_GetFirstArc(theGraph, R), W, gp_GetFirstArc(theGraph, W));935if (!gp_IsArc(theGraph, newEdge))936return NOTOK;937938// Finally, put the visited state of R and W to unvisted so that939// the core embedder (esp. Walkup) will not have any problems.940theGraph->G[R].visited = theGraph->G[W].visited = theGraph->N;941942return OK;943}944945/****************************************************************************946_K4_ReducePathComponent()947948This method is invoked when the bicomp rooted by R contains a component949subgraph that is separable from the bicomp by the 2-cut (R, A). The K_4950homeomorph isolator will have processed a significant fraction of the951component, and so it must be reduced to an edge to ensure that said952processing happens at most once on the component (except for future953operations that are bound to linear time in total by other arguments).954955Because the bicomp is an outerplanar embedding, the component is known to956consists of an external face path plus some internal edges that are parallel957to that path. Otherwise, it wouldn't be separable by the 2-cut (R, A).958959The goal of this method is to reduce the component to the edge (R, A). This960is done in such a way that, if the reduction must be restored, the DFS tree961structure connecting the restored vertices is retained.962963The first step is to ensure that (R, A) is not already just an edge, in which964case no reduction is needed. This can occur if A is future pertinent.965966Assuming a non-trivial reduction component, the next step is to determine967the DFS tree structure within the component. Because it is separable by the9682-cut (R, A), there are only two cases:969970Case 1: The DFS tree path from A to R is within the reduction component.971972In this case, the DFS tree path is marked, the remaining edges of the973reduction component are eliminated, and then the DFS tree path is reduced to974the the tree edge (R, A).975976Note that the reduction component may also contain descendants of A as well977as vertices that are descendant to R but are neither ancestors nor978descendants of A. This depends on where the tree edge from R meets the979external face path (R ... A). However, the reduction component can only980contribute one path to any future K_4, so it suffices to preserve only the981DFS tree path (A --> R).982983Case 2: The DFS tree path from A to R is not within the reduction component.984985In this case, the external face edge from R leads to a descendant D of A.986We mark that back edge (R, D) plus the DFS tree path (D --> A). The987remaining edges of the reduction component can be removed, and then the988path (R, D, ..., A) is reduced to the edge (R, A).989990For the sake of contradiction, suppose that only part of the DFS tree path991from A to R were contained by the reduction component. Then, a DFS tree edge992would have to exit the reduction component and connect to some vertex not993on the external face path (R, ..., A). This contradicts the assumption that994the reduction subgraph is separable from the bicomp by the 2-cut (R, A).995996Returns OK for success, NOTOK for internal (implementation) error.997****************************************************************************/998999int _K4_ReducePathComponent(graphP theGraph, K4SearchContext *context, int R, int prevLink, int A)1000{1001int e_R, e_A, Z, ZPrevLink, edgeType, invertedFlag=0;10021003// Check whether the external face path (R, ..., A) is just an edge1004e_R = gp_GetArc(theGraph, R, 1^prevLink);1005if (theGraph->G[e_R].v == A)1006return OK;10071008// Check for Case 1: The DFS tree path from A to R is within the reduction component1009if (_K4_TestPathComponentForAncestor(theGraph, R, prevLink, A))1010{1011_K4_SetVisitedInPathComponent(theGraph, R, prevLink, A, 0);1012if (theGraph->functions.fpMarkDFSPath(theGraph, R, A) != OK)1013return NOTOK;1014edgeType = EDGE_DFSPARENT;10151016invertedFlag = _K4_GetCumulativeOrientationOnDFSPath(theGraph, R, A);1017}10181019// Otherwise Case 2: The DFS tree path from A to R is not within the reduction component1020else1021{1022_K4_SetVisitedInPathComponent(theGraph, R, prevLink, A, 0);1023Z = theGraph->G[e_R].v;1024theGraph->G[e_R].visited = 1;1025theGraph->G[gp_GetTwinArc(theGraph, e_R)].visited = 1;1026if (theGraph->functions.fpMarkDFSPath(theGraph, A, Z) != OK)1027return NOTOK;1028edgeType = EDGE_BACK;1029}10301031// The path to be kept/reduced is marked, so the other edges can go1032if (_K4_DeleteUnmarkedEdgesInPathComponent(theGraph, R, prevLink, A) != OK)1033return NOTOK;10341035// Clear all the visited flags for safety, except the vertices R and A1036// will remain in the embedding, and the core embedder (Walkup) uses a1037// value greater than the current vertex to indicate an unvisited vertex1038_K4_SetVisitedInPathComponent(theGraph, R, prevLink, A, 0);1039theGraph->G[R].visited = theGraph->N;1040theGraph->G[A].visited = theGraph->N;10411042// Find the component's remaining edges e_A and e_R incident to A and R1043ZPrevLink = prevLink;1044Z = R;1045while (Z != A)1046{1047Z = _GetNextVertexOnExternalFace(theGraph, Z, &ZPrevLink);1048}1049e_A = gp_GetArc(theGraph, A, ZPrevLink);1050e_R = gp_GetArc(theGraph, R, 1^prevLink);10511052// Reduce the path (R ... A) to an edge1053e_R = _K4_ReducePathToEdge(theGraph, context, edgeType, R, e_R, A, e_A);1054if (!gp_IsArc(theGraph, e_R))1055return NOTOK;10561057// Preserve the net orientation along the DFS path in the case of a tree edge1058if (theGraph->G[e_R].type == EDGE_DFSCHILD)1059{1060if (invertedFlag)1061SET_EDGEFLAG_INVERTED(theGraph, e_R);1062}10631064return OK;1065}10661067/****************************************************************************1068_K4_GetCumulativeOrientationOnDFSPath()1069****************************************************************************/1070int _K4_GetCumulativeOrientationOnDFSPath(graphP theGraph, int ancestor, int descendant)1071{1072int J, parent;1073int N = theGraph->N, invertedFlag=0;10741075/* If we are marking from a root vertex upward, then go up to the parent1076copy before starting the loop */10771078if (descendant >= N)1079descendant = theGraph->V[descendant-N].DFSParent;10801081while (descendant != ancestor)1082{1083if (descendant == NIL)1084return NOTOK;10851086// If we are at a bicomp root, then ascend to its parent copy1087if (descendant >= N)1088{1089parent = theGraph->V[descendant-N].DFSParent;1090}10911092// If we are on a regular, non-virtual vertex then get the edge to the parent1093else1094{1095// Scan the edges for the one marked as the DFS parent1096parent = NIL;1097J = gp_GetFirstArc(theGraph, descendant);1098while (gp_IsArc(theGraph, J))1099{1100if (theGraph->G[J].type == EDGE_DFSPARENT)1101{1102parent = theGraph->G[J].v;1103break;1104}1105J = gp_GetNextArc(theGraph, J);1106}11071108// If the parent edge was not found, then the data structure is corrupt1109if (parent == NIL)1110return NOTOK;11111112// Add the inversion flag on the child arc to the cumulative result1113J = gp_GetTwinArc(theGraph, J);1114if (theGraph->G[J].type != EDGE_DFSCHILD || theGraph->G[J].v != descendant)1115return NOTOK;1116invertedFlag ^= GET_EDGEFLAG_INVERTED(theGraph, J);1117}11181119// Hop to the parent and reiterate1120descendant = parent;1121}11221123return invertedFlag;1124}11251126/****************************************************************************1127_K4_TestPathComponentForAncestor()1128Tests the external face path between R and A for a DFS ancestor of A.1129Returns TRUE if found, FALSE otherwise.1130****************************************************************************/11311132int _K4_TestPathComponentForAncestor(graphP theGraph, int R, int prevLink, int A)1133{1134int Z, ZPrevLink;11351136ZPrevLink = prevLink;1137Z = R;1138while (Z != A)1139{1140Z = _GetNextVertexOnExternalFace(theGraph, Z, &ZPrevLink);1141if (Z < A)1142return TRUE;1143}1144return FALSE;1145}11461147/****************************************************************************1148_K4_SetVisitedInPathComponent()11491150There is a subcomponent of the bicomp rooted by R that is separable by the11512-cut (R, A) and contains the edge e_R = theGraph->G[R].link[1^prevLink].11521153All vertices in this component are along the external face, so we1154traverse along the external face vertices strictly between R and A and1155mark all the edges and their incident vertices with the 'visitedValue'.11561157Note that the vertices along the path (R ... A) only have edges incident1158to each other and to R and A because the component is separable by the1159(R, A)-cut.1160****************************************************************************/11611162void _K4_SetVisitedInPathComponent(graphP theGraph, int R, int prevLink, int A, int visitedValue)1163{1164int Z, ZPrevLink, e;11651166ZPrevLink = prevLink;1167Z = _GetNextVertexOnExternalFace(theGraph, R, &ZPrevLink);1168while (Z != A)1169{1170theGraph->G[Z].visited = visitedValue;1171e = gp_GetFirstArc(theGraph, Z);1172while (gp_IsArc(theGraph, e))1173{1174theGraph->G[e].visited = visitedValue;1175theGraph->G[gp_GetTwinArc(theGraph, e)].visited = visitedValue;1176theGraph->G[theGraph->G[e].v].visited = visitedValue;11771178e = gp_GetNextArc(theGraph, e);1179}11801181Z = _GetNextVertexOnExternalFace(theGraph, Z, &ZPrevLink);1182}1183}11841185/****************************************************************************1186_K4_DeleteUnmarkedEdgesInPathComponent()11871188There is a subcomponent of the bicomp rooted by R that is separable by the11892-cut (R, A) and contains the edge e_R = theGraph->G[R].link[1^prevLink].1190The edges in the component have been marked unvisited except for a path we1191intend to preserve. This routine deletes the unvisited edges.11921193NOTE: This reduction has invalidated the short-circuit extFace data structure,1194but it will be repaired for use by WalkUp and WalkDown when the path1195component reduction is completed.11961197Returns OK on success, NOTOK on internal error1198****************************************************************************/11991200int _K4_DeleteUnmarkedEdgesInPathComponent(graphP theGraph, int R, int prevLink, int A)1201{1202int Z, ZPrevLink, e;12031204// We need to use the stack to store up the edges we're going to delete.1205// We want to make sure there is enough stack capacity to handle it,1206// which is of course true because the stack is supposed to be empty.1207// We're doing a reduction on a bicomp on which the WalkDown has completed,1208// so the stack contains no bicomp roots to merge.1209if (sp_NonEmpty(theGraph->theStack))1210return NOTOK;12111212// Traverse all vertices internal to the path (R ... A) and push1213// all non-visited edges1214ZPrevLink = prevLink;1215Z = _GetNextVertexOnExternalFace(theGraph, R, &ZPrevLink);1216while (Z != A)1217{1218e = gp_GetFirstArc(theGraph, Z);1219while (gp_IsArc(theGraph, e))1220{1221// The comparison of e to its twin is a useful way of ensuring we1222// don't push the edge twice, which is of course only applicable1223// when processing an edge whose endpoints are both internal to1224// the path (R ... A)1225if (!theGraph->G[e].visited &&1226(e < gp_GetTwinArc(theGraph, e) ||1227theGraph->G[e].v == R || theGraph->G[e].v == A))1228{1229sp_Push(theGraph->theStack, e);1230}12311232e = gp_GetNextArc(theGraph, e);1233}12341235Z = _GetNextVertexOnExternalFace(theGraph, Z, &ZPrevLink);1236}12371238// Delete all the non-visited edges1239while (sp_NonEmpty(theGraph->theStack))1240{1241sp_Pop(theGraph->theStack, e);1242gp_DeleteEdge(theGraph, e, 0);1243}12441245return OK;1246}12471248/****************************************************************************1249_K4_ReducePathToEdge()12501251Returns an arc of the edge created on success, a non-arc (NOTOK) on failure1252On success, the arc is in the adjacency list of R. The result can be tested1253for success or failure using gp_IsArc()1254****************************************************************************/12551256int _K4_ReducePathToEdge(graphP theGraph, K4SearchContext *context, int edgeType, int R, int e_R, int A, int e_A)1257{1258// Find out the links used in vertex R for edge e_R and in vertex A for edge e_A1259int Rlink = theGraph->G[R].link[0] == e_R ? 0 : 1;1260int Alink = theGraph->G[A].link[0] == e_A ? 0 : 1;12611262// If the path is more than a single edge, then it must be reduced to an edge.1263// Note that even if the path is a single edge, the external face data structure1264// must still be modified since many edges connecting the external face have1265// been deleted1266if (theGraph->G[e_R].v != A)1267{1268int v_R, v_A;12691270// Prepare for removing each of the two edges that join the path to the bicomp by1271// restoring it if it is a reduction edge (a constant time operation)1272if (context->G[e_R].pathConnector != NIL)1273{1274if (_K4_RestoreReducedPath(theGraph, context, e_R) != OK)1275return NOTOK;12761277e_R = gp_GetArc(theGraph, R, Rlink);1278}12791280if (context->G[e_A].pathConnector != NIL)1281{1282if (_K4_RestoreReducedPath(theGraph, context, e_A) != OK)1283return NOTOK;1284e_A = gp_GetArc(theGraph, A, Alink);1285}12861287// Save the vertex neighbors of R and A indicated by e_R and e_A for1288// later use in setting up the path connectors.1289v_R = theGraph->G[e_R].v;1290v_A = theGraph->G[e_A].v;12911292// Now delete the two edges that join the path to the bicomp.1293gp_DeleteEdge(theGraph, e_R, 0);1294gp_DeleteEdge(theGraph, e_A, 0);12951296// Now add a single edge to represent the path1297// We use 1^Rlink, for example, because Rlink was the link from R that indicated e_R,1298// so 1^Rlink is the link that indicated e_R in the other arc that was adjacent to e_R.1299// We want gp_InsertEdge to place the new arc where e_R was in R's adjacency list1300gp_InsertEdge(theGraph, R, gp_GetArc(theGraph, R, Rlink), 1^Rlink,1301A, gp_GetArc(theGraph, A, Alink), 1^Alink);13021303// Now set up the path connectors so the original path can be recovered if needed.1304e_R = gp_GetArc(theGraph, R, Rlink);1305context->G[e_R].pathConnector = v_R;13061307e_A = gp_GetArc(theGraph, A, Alink);1308context->G[e_A].pathConnector = v_A;13091310// Also, set the reduction edge's type to preserve the DFS tree structure1311theGraph->G[e_R].type = _ComputeArcType(theGraph, R, A, edgeType);1312theGraph->G[e_A].type = _ComputeArcType(theGraph, A, R, edgeType);1313}13141315// Set the external face data structure1316theGraph->extFace[R].vertex[Rlink] = A;1317theGraph->extFace[A].vertex[Alink] = R;13181319// If the edge represents an entire bicomp, then more external face1320// settings are needed.1321if (gp_GetFirstArc(theGraph, R) == gp_GetLastArc(theGraph, R))1322{1323theGraph->extFace[R].vertex[1^Rlink] = A;1324theGraph->extFace[A].vertex[1^Alink] = R;1325theGraph->extFace[A].inversionFlag = 0;1326}13271328return e_R;1329}13301331/****************************************************************************1332_K4_RestoreReducedPath()13331334Given an edge record of an edge used to reduce a path, we want to restore1335the path in constant time.1336The path may contain more reduction edges internally, but we do not1337search for and process those since it would violate the constant time1338bound required of this function.13391340Note that we don't bother amending the external face data structure because1341a reduced path is only restored when it will shortly be reduced again or1342when we don't really need the external face data structure anymore.13431344Return OK on success, NOTOK on failure1345****************************************************************************/13461347int _K4_RestoreReducedPath(graphP theGraph, K4SearchContext *context, int J)1348{1349int JTwin, u, v, w, x;1350int J0, J1, JTwin0, JTwin1;13511352if (context->G[J].pathConnector == NIL)1353return OK;13541355JTwin = gp_GetTwinArc(theGraph, J);13561357u = theGraph->G[JTwin].v;1358v = context->G[J].pathConnector;1359w = context->G[JTwin].pathConnector;1360x = theGraph->G[J].v;13611362// Get the locations of the graph nodes between which the new graph nodes1363// must be added in order to reconnect the path parallel to the edge.1364J0 = gp_GetNextArc(theGraph, J);1365J1 = gp_GetPrevArc(theGraph, J);1366JTwin0 = gp_GetNextArc(theGraph, JTwin);1367JTwin1 = gp_GetPrevArc(theGraph, JTwin);13681369// We first delete the edge represented by J and JTwin. We do so before1370// restoring the path to ensure we do not exceed the maximum arc capacity.1371gp_DeleteEdge(theGraph, J, 0);13721373// Now we add the two edges to reconnect the reduced path represented1374// by the edge [J, JTwin]. The edge record in u is added between J0 and J1.1375// Likewise, the new edge record in x is added between JTwin0 and JTwin1.1376if (gp_IsArc(theGraph, J0))1377{1378if (gp_InsertEdge(theGraph, u, J0, 1, v, gp_AdjacencyListEndMark(v), 0) != OK)1379return NOTOK;1380}1381else1382{1383if (gp_InsertEdge(theGraph, u, J1, 0, v, gp_AdjacencyListEndMark(v), 0) != OK)1384return NOTOK;1385}13861387if (gp_IsArc(theGraph, JTwin0))1388{1389if (gp_InsertEdge(theGraph, x, JTwin0, 1, w, gp_AdjacencyListEndMark(w), 0) != OK)1390return NOTOK;1391}1392else1393{1394if (gp_InsertEdge(theGraph, x, JTwin1, 0, w, gp_AdjacencyListEndMark(w), 0) != OK)1395return NOTOK;1396}13971398// Set the types of the newly added edges. In both cases, the first of the two1399// vertex parameters is known to be degree 2 because they are internal to the1400// path being restored, so this operation is constant time.1401if (_SetEdgeType(theGraph, v, u) != OK || _SetEdgeType(theGraph, w, x) != OK)1402return NOTOK;14031404return OK;1405}14061407/****************************************************************************1408_K4_RestoreAndOrientReducedPaths()14091410This function searches the embedding for any edges that are specially marked1411as being representative of a path that was previously reduced to a single edge1412by _ReducePathToEdge(). This method restores the path by replacing the edge1413with the path.14141415Note that the new path may contain more reduction edges, and these will be1416iteratively expanded by the outer for loop. Equally, a reduced path may1417be restored into a path that itself is a reduction path that will only be1418attached to the embedding by some future step of the outer loop.14191420The vertices along the path being restored must be given a consistent1421orientation with the endpoints. It is expected that the embedding1422will have been oriented prior to this operation.1423****************************************************************************/14241425int _K4_RestoreAndOrientReducedPaths(graphP theGraph, K4SearchContext *context)1426{1427int e, J, JTwin, u, v, w, x, visited;14281429for (e = 0; e < theGraph->M + sp_GetCurrentSize(theGraph->edgeHoles);)1430{1431J = theGraph->edgeOffset + 2*e;1432if (context->G[J].pathConnector != NIL)1433{1434visited = theGraph->G[J].visited;14351436JTwin = gp_GetTwinArc(theGraph, J);1437u = theGraph->G[JTwin].v;1438v = context->G[J].pathConnector;1439w = context->G[JTwin].pathConnector;1440x = theGraph->G[J].v;14411442if (_K4_RestoreReducedPath(theGraph, context, J) != OK)1443return NOTOK;14441445// If the path is on the external face, orient it1446if (theGraph->G[gp_GetFirstArc(theGraph, u)].v == v ||1447theGraph->G[gp_GetLastArc(theGraph, u)].v == v)1448{1449// Reality check: ensure the path is connected to the1450// external face at both vertices.1451if (theGraph->G[gp_GetFirstArc(theGraph, x)].v != w &&1452theGraph->G[gp_GetLastArc(theGraph, x)].v != w)1453return NOTOK;14541455if (_OrientExternalFacePath(theGraph, u, v, w, x) != OK)1456return NOTOK;1457}14581459if (_SetVisitedOnPath(theGraph, u, v, w, x, visited) !=OK)1460return NOTOK;1461}1462else e++;1463}14641465return OK;1466}146714681469