Path: blob/master/sage/graphs/planarity/graphK4Search_Extensions.c
4077 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 <stdlib.h>4546#include "graphK4Search.private.h"47#include "graphK4Search.h"4849extern int _GetNextVertexOnExternalFace(graphP theGraph, int curVertex, int *pPrevLink);5051extern int _SearchForK4InBicomps(graphP theGraph, int I);52extern int _SearchForK4InBicomp(graphP theGraph, K4SearchContext *context, int I, int R);5354extern int _TestForCompleteGraphObstruction(graphP theGraph, int numVerts,55int *degrees, int *imageVerts);5657extern int _getImageVertices(graphP theGraph, int *degrees, int maxDegree,58int *imageVerts, int maxNumImageVerts);5960extern int _TestSubgraph(graphP theSubgraph, graphP theGraph);6162/* Forward declarations of local functions */6364void _K4Search_ClearStructures(K4SearchContext *context);65int _K4Search_CreateStructures(K4SearchContext *context);66int _K4Search_InitStructures(K4SearchContext *context);6768/* Forward declarations of overloading functions */6970int _K4Search_CreateFwdArcLists(graphP theGraph);71void _K4Search_CreateDFSTreeEmbedding(graphP theGraph);72void _K4Search_EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink);73int _K4Search_MarkDFSPath(graphP theGraph, int ancestor, int descendant);74int _K4Search_HandleBlockedEmbedIteration(graphP theGraph, int I);75int _K4Search_HandleBlockedDescendantBicomp(graphP theGraph, int I, int RootVertex, int R, int *pRout, int *pW, int *pWPrevLink);76int _K4Search_EmbedPostprocess(graphP theGraph, int I, int edgeEmbeddingResult);77int _K4Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph);78int _K4Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph);7980void _K4Search_InitGraphNode(graphP theGraph, int I);81void _InitK4SearchGraphNode(K4SearchContext *context, int I);82void _K4Search_InitVertexRec(graphP theGraph, int I);83void _InitK4SearchVertexRec(K4SearchContext *context, int I);8485int _K4Search_InitGraph(graphP theGraph, int N);86void _K4Search_ReinitializeGraph(graphP theGraph);87int _K4Search_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity);8889/* Forward declarations of functions used by the extension system */9091void *_K4Search_DupContext(void *pContext, void *theGraph);92void _K4Search_FreeContext(void *);9394/****************************************************************************95* K4SEARCH_ID - the variable used to hold the integer identifier for this96* extension, enabling this feature's extension context to be distinguished97* from other features' extension contexts that may be attached to a graph.98****************************************************************************/99100int K4SEARCH_ID = 0;101102/****************************************************************************103gp_AttachK4Search()104105This function adjusts the graph data structure to attach the K4 search106feature.107****************************************************************************/108109int gp_AttachK4Search(graphP theGraph)110{111K4SearchContext *context = NULL;112113// If the K4 search feature has already been attached to the graph,114// then there is no need to attach it again115gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);116if (context != NULL)117{118return OK;119}120121// Allocate a new extension context122context = (K4SearchContext *) malloc(sizeof(K4SearchContext));123if (context == NULL)124{125return NOTOK;126}127128// First, tell the context that it is not initialized129context->initialized = 0;130131// Save a pointer to theGraph in the context132context->theGraph = theGraph;133134// Put the overload functions into the context function table.135// gp_AddExtension will overload the graph's functions with these, and136// return the base function pointers in the context function table137memset(&context->functions, 0, sizeof(graphFunctionTable));138139context->functions.fpCreateFwdArcLists = _K4Search_CreateFwdArcLists;140context->functions.fpCreateDFSTreeEmbedding = _K4Search_CreateDFSTreeEmbedding;141context->functions.fpEmbedBackEdgeToDescendant = _K4Search_EmbedBackEdgeToDescendant;142context->functions.fpMarkDFSPath = _K4Search_MarkDFSPath;143context->functions.fpHandleBlockedEmbedIteration = _K4Search_HandleBlockedEmbedIteration;144context->functions.fpHandleBlockedDescendantBicomp = _K4Search_HandleBlockedDescendantBicomp;145context->functions.fpEmbedPostprocess = _K4Search_EmbedPostprocess;146context->functions.fpCheckEmbeddingIntegrity = _K4Search_CheckEmbeddingIntegrity;147context->functions.fpCheckObstructionIntegrity = _K4Search_CheckObstructionIntegrity;148149context->functions.fpInitGraphNode = _K4Search_InitGraphNode;150context->functions.fpInitVertexRec = _K4Search_InitVertexRec;151152context->functions.fpInitGraph = _K4Search_InitGraph;153context->functions.fpReinitializeGraph = _K4Search_ReinitializeGraph;154context->functions.fpEnsureArcCapacity = _K4Search_EnsureArcCapacity;155156_K4Search_ClearStructures(context);157158// Store the K33 search context, including the data structure and the159// function pointers, as an extension of the graph160if (gp_AddExtension(theGraph, &K4SEARCH_ID, (void *) context,161_K4Search_DupContext, _K4Search_FreeContext,162&context->functions) != OK)163{164_K4Search_FreeContext(context);165return NOTOK;166}167168// Create the K33-specific structures if the size of the graph is known169// Attach functions are always invoked after gp_New(), but if a graph170// extension must be attached before gp_Read(), then the attachment171// also happens before gp_InitGraph(), which means N==0.172// However, sometimes a feature is attached after gp_InitGraph(), in173// which case N > 0174if (theGraph->N > 0)175{176if (_K4Search_CreateStructures(context) != OK ||177_K4Search_InitStructures(context) != OK)178{179_K4Search_FreeContext(context);180return NOTOK;181}182}183184return OK;185}186187/********************************************************************188gp_DetachK4Search()189********************************************************************/190191int gp_DetachK4Search(graphP theGraph)192{193return gp_RemoveExtension(theGraph, K4SEARCH_ID);194}195196/********************************************************************197_K4Search_ClearStructures()198********************************************************************/199200void _K4Search_ClearStructures(K4SearchContext *context)201{202if (!context->initialized)203{204// Before initialization, the pointers are stray, not NULL205// Once NULL or allocated, free() or LCFree() can do the job206context->sortedDFSChildLists = NULL;207context->G = NULL;208context->V = NULL;209210context->initialized = 1;211}212else213{214LCFree(&context->sortedDFSChildLists);215if (context->G != NULL)216{217free(context->G);218context->G = NULL;219}220if (context->V != NULL)221{222free(context->V);223context->V = NULL;224}225}226}227228/********************************************************************229_K4Search_CreateStructures()230Create uninitialized structures for the vertex and graph node231levels, and initialized structures for the graph level232********************************************************************/233int _K4Search_CreateStructures(K4SearchContext *context)234{235int N = context->theGraph->N;236int Gsize = context->theGraph->edgeOffset + context->theGraph->arcCapacity;237238if (N <= 0)239return NOTOK;240241if ((context->sortedDFSChildLists = LCNew(context->theGraph->N)) == NULL ||242(context->G = (K4Search_GraphNodeP) malloc(Gsize*sizeof(K4Search_GraphNode))) == NULL ||243(context->V = (K4Search_VertexRecP) malloc(N*sizeof(K4Search_VertexRec))) == NULL244)245{246return NOTOK;247}248249return OK;250}251252/********************************************************************253_K4Search_InitStructures()254********************************************************************/255int _K4Search_InitStructures(K4SearchContext *context)256{257int I, N = context->theGraph->N;258int Gsize = context->theGraph->edgeOffset + context->theGraph->arcCapacity;259260if (N <= 0)261return OK;262263for (I = 0; I < Gsize; I++)264_InitK4SearchGraphNode(context, I);265266for (I = 0; I < N; I++)267_InitK4SearchVertexRec(context, I);268269return OK;270}271272/********************************************************************273********************************************************************/274275int _K4Search_InitGraph(graphP theGraph, int N)276{277K4SearchContext *context = NULL;278gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);279280if (context == NULL)281return NOTOK;282{283theGraph->N = N;284theGraph->edgeOffset = 2*N;285if (theGraph->arcCapacity == 0)286theGraph->arcCapacity = 2*DEFAULT_EDGE_LIMIT*N;287288if (_K4Search_CreateStructures(context) != OK)289return NOTOK;290291// This call initializes the base graph structures, but it also292// initializes the custom graphnode and vertex level structures293// due to the overloads of InitGraphNode and InitVertexRec294context->functions.fpInitGraph(theGraph, N);295}296297return OK;298}299300/********************************************************************301********************************************************************/302303void _K4Search_ReinitializeGraph(graphP theGraph)304{305K4SearchContext *context = NULL;306gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);307308if (context != NULL)309{310// Reinitialization can go much faster if the underlying311// init graph node and vertex rec functions are called,312// rather than the overloads of this module, because it313// avoids lots of unnecessary gp_FindExtension() calls.314if (theGraph->functions.fpInitGraphNode == _K4Search_InitGraphNode &&315theGraph->functions.fpInitVertexRec == _K4Search_InitVertexRec)316{317// Restore the graph function pointers318theGraph->functions.fpInitGraphNode = context->functions.fpInitGraphNode;319theGraph->functions.fpInitVertexRec = context->functions.fpInitVertexRec;320321// Reinitialize the graph322context->functions.fpReinitializeGraph(theGraph);323324// Restore the function pointers that attach this feature325theGraph->functions.fpInitGraphNode = _K4Search_InitGraphNode;326theGraph->functions.fpInitVertexRec = _K4Search_InitVertexRec;327328// Do the reinitialization that is specific to this module329_K4Search_InitStructures(context);330LCReset(context->sortedDFSChildLists);331}332333// If optimization is not possible, then just stick with what works.334// Reinitialize the graph-level structure and then invoke the335// reinitialize function.336else337{338LCReset(context->sortedDFSChildLists);339340// The underlying function fpReinitializeGraph() implicitly initializes the K33341// structures due to the overloads of fpInitGraphNode() and fpInitVertexRec().342// It just does so less efficiently because each invocation of InitGraphNode343// and InitVertexRec has to look up the extension again.344//// _K4Search_InitStructures(context);345context->functions.fpReinitializeGraph(theGraph);346}347}348}349350/********************************************************************351The current implementation does not support an increase of arc352(edge record) capacity once the extension is attached to the graph353data structure. This is only due to not being necessary to support.354For now, it is easy to ensure the correct capacity before attaching355the extension, but support could be added later if there is some356reason to do so.357********************************************************************/358359int _K4Search_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity)360{361return NOTOK;362}363364/********************************************************************365_K4Search_DupContext()366********************************************************************/367368void *_K4Search_DupContext(void *pContext, void *theGraph)369{370K4SearchContext *context = (K4SearchContext *) pContext;371K4SearchContext *newContext = (K4SearchContext *) malloc(sizeof(K4SearchContext));372373if (newContext != NULL)374{375int N = ((graphP) theGraph)->N;376int Gsize = ((graphP) theGraph)->edgeOffset + ((graphP) theGraph)->arcCapacity;377378*newContext = *context;379380newContext->theGraph = (graphP) theGraph;381382newContext->initialized = 0;383_K4Search_ClearStructures(newContext);384if (N > 0)385{386if (_K4Search_CreateStructures(newContext) != OK)387{388_K4Search_FreeContext(newContext);389return NULL;390}391392LCCopy(newContext->sortedDFSChildLists, context->sortedDFSChildLists);393memcpy(newContext->G, context->G, Gsize*sizeof(K4Search_GraphNode));394memcpy(newContext->V, context->V, N*sizeof(K4Search_VertexRec));395}396}397398return newContext;399}400401/********************************************************************402_K4Search_FreeContext()403********************************************************************/404405void _K4Search_FreeContext(void *pContext)406{407K4SearchContext *context = (K4SearchContext *) pContext;408409_K4Search_ClearStructures(context);410free(pContext);411}412413/********************************************************************414_K4Search_CreateFwdArcLists()415416Puts the forward arcs (back edges from a vertex to its descendants)417into a circular list indicated by the fwdArcList member, a task418simplified by the fact that they have already been placed in419succession at the end of the adjacency list.420421For K4 search, the forward edges must be sorted by DFS number of422the descendant endpoint. The sort is linear time, but it is a little423slower, so we avoid this cost for the other planarity-related algorithms.424425Returns OK on success, NOTOK on internal code failure426********************************************************************/427428int _K4Search_CreateFwdArcLists(graphP theGraph)429{430K4SearchContext *context = NULL;431432gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);433if (context == NULL)434return NOTOK;435436// For isolating a K_4 homeomorph, we need the forward edges437// of each vertex to be in sorted order by DFI of descendants.438// Otherwise we just drop through to the normal processing...439440if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4)441{442int I, Jcur, Jnext, ancestor;443444// for each vertex v in order, we follow each of its back edges445// to the twin forward edge in an ancestor u, then we move446// the forward edge to the fwdArcList of u. Since this loop447// processes vertices in order, the fwdArcList of each vertex448// will be in order by the neighbor indicated by the forward edges.449450for (I=0; I < theGraph->N; I++)451{452// Skip this vertex if it has no edges453Jnext = gp_GetLastArc(theGraph, I);454if (!gp_IsArc(theGraph, Jnext))455continue;456457// Skip the forward edges, which are in succession at the458// end of the arc list (last and its predecessors)459while (theGraph->G[Jnext].type == EDGE_FORWARD)460Jnext = gp_GetPrevArc(theGraph, Jnext);461462// Now we want to put all the back arcs in a backArcList, too.463// Since we've already skipped past the forward arcs, we continue464// with the predecessor arcs until we either run out of arcs or465// we find a DFS child arc (the DFS child arcs are in succession466// at the beginning of the arc list, so when a child arc is467// encountered in the predecessor direction, then there won't be468// any more back arcs.469while (gp_IsArc(theGraph, Jnext) &&470theGraph->G[Jnext].type != EDGE_DFSCHILD)471{472Jcur = Jnext;473Jnext = gp_GetPrevArc(theGraph, Jnext);474475if (theGraph->G[Jcur].type == EDGE_BACK)476{477// Remove the back arc from I's adjacency list478gp_DetachArc(theGraph, Jcur);479gp_SetPrevArc(theGraph, Jcur, NIL);480gp_SetNextArc(theGraph, Jcur, NIL);481482// Determine the ancestor of vertex I to which Jcur connects483ancestor = theGraph->G[Jcur].v;484485// Go to the forward arc in the ancestor486Jcur = gp_GetTwinArc(theGraph, Jcur);487488// Remove the forward arc from the ancestor's adjacency list489gp_DetachArc(theGraph, Jcur);490491// Add the forward arc to the end of the fwdArcList.492if (theGraph->V[ancestor].fwdArcList == NIL)493{494theGraph->V[ancestor].fwdArcList = Jcur;495gp_SetPrevArc(theGraph, Jcur, Jcur);496gp_SetNextArc(theGraph, Jcur, Jcur);497}498else499{500gp_AttachArc(theGraph, NIL, theGraph->V[ancestor].fwdArcList, 1, Jcur);501}502}503}504}505506// Since the fwdArcLists have been created, we do not fall through507// to run the superclass implementation508return OK;509}510511// If we're not actually running a K4 search, then we just run the512// superclass implementation513return context->functions.fpCreateFwdArcLists(theGraph);514}515516/********************************************************************517_K4Search_CreateDFSTreeEmbedding()518519This function overloads the basic planarity version in the manner520explained below. Once the extra behavior is performed, the basic521planarity version is invoked.522523First, the sortedDFSChildList of each vertex is computed. Each vertex524receives the list of its DFS children sorted by their DFS numbers.525This is linear time overall. The core planarity/outerplanarity526algorithm computes a different list of the DFS children, the527separatedDFSChildList, in which the DFS children are stored in order528of their lowpoint values.529530Second, the p2dFwdArcCount of each vertex is computed. This is the531number of forward arcs from the DFS parent of the vertex to DFS532descendants of the vertex. This is computed based on a simultaneous533traversal through the sortedDFSChildList and the sorted fwdArcList.534535Third, the subtree value of each forward arc (V, D) is determined. This536value indicates the DFS child C of V whose DFS subtree contains the DFS537descendant endpoint D of the forward arc. This can be computed during538the setting of the p2dFwdArcCount values.539540Each DFS child is listed in DFI order in V[I].sortedDFSChildList.541In V[I].fwdArcList, the forward arcs of all back edges are in order542by DFI of the descendant endpoint of the edge.543544DFS descendants have a higher DFI than ancestors, so given two545successive children C1 and C2, if a forward arc leads to a546vertex D such that DFI(C1) < DFI(D) < DFI(C2), then the547forward arc contributes to the count of C1 and has C1 as subtree.548********************************************************************/549550void _K4Search_CreateDFSTreeEmbedding(graphP theGraph)551{552K4SearchContext *context = NULL;553gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);554555if (context != NULL)556{557if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4)558{559int I, J, C1, C2, D, e;560int N = theGraph->N;561562// First compute the sortedDFSChildList of each vertex563for (I=0; I<N; I++)564{565J = gp_GetFirstArc(theGraph, I);566567// If a vertex has any DFS children, the edges568// to them are stored in descending order of569// the DFI's along the successor arc pointers, so570// we traverse them and prepend each to the571// ascending order sortedDFSChildList572while (theGraph->G[J].type == EDGE_DFSCHILD)573{574context->V[I].sortedDFSChildList =575LCPrepend(context->sortedDFSChildLists,576context->V[I].sortedDFSChildList,577theGraph->G[J].v);578579J = gp_GetNextArc(theGraph, J);580}581}582583// Next compute the p2dFwdArcCount of each vertex and the584// subtree of each forward arc.585for (I=0; I<N; I++)586{587// For each DFS child of the vertex I, ...588C1 = context->V[I].sortedDFSChildList;589e = theGraph->V[I].fwdArcList;590while (C1 != NIL && gp_IsArc(theGraph, e))591{592// Get the next higher numbered child C2593C2 = LCGetNext(context->sortedDFSChildLists,594context->V[I].sortedDFSChildList, C1);595596// If there is a next child C2, then we can restrict attention597// to the forward arcs with DFI less than C2598if (C2 != NIL)599{600D = theGraph->G[e].v;601while (D < C2)602{603context->V[C1].p2dFwdArcCount++;604context->G[e].subtree = C1;605606// Go to the next forward arc607e = gp_GetNextArc(theGraph, e);608if (e == theGraph->V[I].fwdArcList)609{610e = NIL;611break;612}613D = theGraph->G[e].v;614}615}616617// If C1 is the last DFS child (C2==NIL), then all remaining618// forward edges must connect to descendants of C1.619else620{621while (gp_IsArc(theGraph, e))622{623context->V[C1].p2dFwdArcCount++;624context->G[e].subtree = C1;625626// Go to the next forward arc627e = gp_GetNextArc(theGraph, e);628if (e == theGraph->V[I].fwdArcList)629e = NIL;630}631}632633// Move the DFS child context to C2634C1 = C2;635}636}637}638639// Invoke the superclass version of the function640// Each DFS tree child arc is moved to the root copy of the vertex641context->functions.fpCreateDFSTreeEmbedding(theGraph);642}643}644645/********************************************************************646_K4Search_EmbedBackEdgeToDescendant()647648The forward and back arcs of the cycle edge are embedded by the planarity649version of this function.650However, for K_4 subgraph homeomorphism, we also maintain a forward651arc counter in a DFS child C of each vertex V to indicate how many652forward arcs there are from V to descendants of C. Each forward arc653has an indicator, 'subtree', of C. When we embed the edge, we decrement654the counter so that when the WalkDown resolves as much pertinence as655possible along the external face of the bicomp rooted by R=C+N, then656we can easily determine whether there is more unresolved pertinence657by testing whether the forward arc count has dropped to zero.658If not, then we either find a K4 or perform a reduction that enables659the WalkDown to make more progress when reinvoked.660********************************************************************/661662void _K4Search_EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink)663{664K4SearchContext *context = NULL;665gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);666667if (context != NULL)668{669// K4 search may have been attached, but not enabled670if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4)671{672int fwdArc = theGraph->V[W].adjacentTo;673context->V[context->G[fwdArc].subtree].p2dFwdArcCount--;674}675676// Invoke the superclass version of the function677context->functions.fpEmbedBackEdgeToDescendant(theGraph, RootSide, RootVertex, W, WPrevLink);678}679}680681/********************************************************************682********************************************************************/683684void _K4Search_InitGraphNode(graphP theGraph, int I)685{686K4SearchContext *context = NULL;687gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);688689if (context != NULL)690{691context->functions.fpInitGraphNode(theGraph, I);692_InitK4SearchGraphNode(context, I);693}694}695696void _InitK4SearchGraphNode(K4SearchContext *context, int I)697{698context->G[I].pathConnector = NIL;699context->G[I].subtree = NIL;700}701702/********************************************************************703********************************************************************/704705void _K4Search_InitVertexRec(graphP theGraph, int I)706{707K4SearchContext *context = NULL;708gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);709710if (context != NULL)711{712context->functions.fpInitVertexRec(theGraph, I);713_InitK4SearchVertexRec(context, I);714}715}716717void _InitK4SearchVertexRec(K4SearchContext *context, int I)718{719context->V[I].p2dFwdArcCount = 0;720context->V[I].sortedDFSChildList = NIL;721}722723/********************************************************************724// This function used to be implemented by going to each725// vertex, asking for its DFS parent, then finding the726// edge that lead to that DFS parent and marking it.727// However, the K4 search performs certain bicomp reductions728// that are required to preserve the essential structure of729// the DFS tree. As a result, sometimes a DFSParent has been730// reduced out of the graph, but the tree edge leads to the nearest731// ancestor still in the graph. So, when we want to leave a vertex,732// we search for the DFS tree edge to the "parent" (nearest ancestor),733// then we mark the edge and use it to go up to the "parent".734********************************************************************/735736int _K4Search_MarkDFSPath(graphP theGraph, int ancestor, int descendant)737{738int J, parent, N;739740N = theGraph->N;741742/* If we are marking from a root vertex upward, then go up to the parent743copy before starting the loop */744745if (descendant >= N)746descendant = theGraph->V[descendant-N].DFSParent;747748/* Mark the lowest vertex (the one with the highest number). */749750theGraph->G[descendant].visited = 1;751752/* Mark all ancestors of the lowest vertex, and the edges used to reach753them, up to the given ancestor vertex. */754755while (descendant != ancestor)756{757if (descendant == NIL)758return NOTOK;759760/* If we are at a bicomp root, then ascend to its parent copy and761mark it as visited. */762763if (descendant >= N)764{765parent = theGraph->V[descendant-N].DFSParent;766}767768/* If we are on a regular, non-virtual vertex then get the edge to769the parent, mark the edge, then fall through to the code770that marks the parent vertex. */771else772{773774/* Scan the edges for the one marked as the DFS parent */775776parent = NIL;777J = gp_GetFirstArc(theGraph, descendant);778while (gp_IsArc(theGraph, J))779{780if (theGraph->G[J].type == EDGE_DFSPARENT)781{782parent = theGraph->G[J].v;783break;784}785J = gp_GetNextArc(theGraph, J);786}787788/* If the desired edge was not found, then the data structure is789corrupt, so bail out. */790791if (parent == NIL)792return NOTOK;793794/* Mark the edge */795796theGraph->G[J].visited = 1;797theGraph->G[gp_GetTwinArc(theGraph, J)].visited = 1;798}799800/* Mark the parent, then hop to the parent and reiterate */801802theGraph->G[parent].visited = 1;803descendant = parent;804}805806return OK;807}808809/********************************************************************810* This function is called if the outerplanarity algorithm fails to811* embed all back edges for a vertex I. This means that an obstruction812* to outerplanarity has occurred, so we determine if it is a subgraph813* homeomorphic to K4. If so, then NONEMBEDDABLE is returned. If not,814* then a reduction is performed that unobstructs outerplanarity and815* OK is returned, which allows the outerplanarity algorithm to816* proceed with iteration I-1 (or to stop if I==0).817********************************************************************/818819int _K4Search_HandleBlockedEmbedIteration(graphP theGraph, int I)820{821if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4)822{823// If the fwdArcList is empty, then the K4 was already isolated824// by _K4Search_HandleBlockedDescendantBicomp(), and we just825// return the NONEMBEDDABLE result in order to stop the embedding826// iteration loop.827if (theGraph->V[I].fwdArcList == NIL)828return NONEMBEDDABLE;829830return _SearchForK4InBicomps(theGraph, I);831}832else833{834K4SearchContext *context = NULL;835gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);836837if (context != NULL)838{839return context->functions.fpHandleBlockedEmbedIteration(theGraph, I);840}841}842843return NOTOK;844}845846/********************************************************************847This function is called when outerplanarity obstruction minor A is848encountered by the WalkDown. In the implementation for the core849planarity/outerplanarity algorithm, this method simply pushes the850blocked bicomp root onto the stack and returns NONEMBEDDABLE, which851causes the WalkDown to terminate. The embed postprocessing would852then go on to isolate the obstruction.853854However, outerplanarity obstruction minor A corresponds to a K_{2,3}855homeomorph. This method invokes a search for a K_4 homeomorph that856may be entangled with the K_{2,3} homeomorph. If an entangled K_4857homeomorph is found, then _SearchForK4() returns NONEMBEDDABLE, which858causes the WalkDown to terminate as above. This is correct since a859K_4 homeomorph has been found and isolated, and the K4Search overload860of EmbedPostprocess() does no additional work.861862On the other hand, if minor A is found but there is no entangled K_4863homeomorph, then the blocked descendant was reduced to a single edge864so that it no longer obstructs outerplanarity. Then, OK was returned865to indicate that the WalkDown should proceed. This function then866sets the vertex W and directional information that must be returned867so that WalkDown can proceed.868869Returns OK to proceed with WalkDown at W,870NONEMBEDDABLE to terminate WalkDown of Root Vertex871NOTOK for internal error872********************************************************************/873874int _K4Search_HandleBlockedDescendantBicomp(graphP theGraph, int I, int RootVertex, int R, int *pRout, int *pW, int *pWPrevLink)875{876if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4)877{878int RetVal = _SearchForK4InBicomp(theGraph, NULL, I, R);879880// On internal error (NOTOK) or K4 found (NONEMBEDDABLE), we return.881if (RetVal != OK)882return RetVal;883884// Since the bicomp rooted by R is now a singleton edge, either direction885// out of R and into W can be selected, as long as they are consistent886// We just choose the settings associated with selecting W as the next887// vertex from R on the external face.888*pRout = 0;889*pWPrevLink = 1;890*pW = _GetNextVertexOnExternalFace(theGraph, R, pWPrevLink);891892// Now return OK so the Walkdown can continue at W (i.e. *pW)893return OK;894}895else896{897K4SearchContext *context = NULL;898gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);899900if (context != NULL)901{902return context->functions.fpHandleBlockedDescendantBicomp(theGraph, I, RootVertex, R, pRout, pW, pWPrevLink);903}904}905906return NOTOK;907}908909/********************************************************************910********************************************************************/911912int _K4Search_EmbedPostprocess(graphP theGraph, int I, int edgeEmbeddingResult)913{914// For K4 search, we just return the edge embedding result because the915// search result has been obtained already.916if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4)917{918return edgeEmbeddingResult;919}920921// When not searching for K4, we let the superclass do the work922else923{924K4SearchContext *context = NULL;925gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);926927if (context != NULL)928{929return context->functions.fpEmbedPostprocess(theGraph, I, edgeEmbeddingResult);930}931}932933return NOTOK;934}935936/********************************************************************937********************************************************************/938939int _K4Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph)940{941if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4)942{943return OK;944}945946// When not searching for K4, we let the superclass do the work947else948{949K4SearchContext *context = NULL;950gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);951952if (context != NULL)953{954return context->functions.fpCheckEmbeddingIntegrity(theGraph, origGraph);955}956}957958return NOTOK;959}960961/********************************************************************962********************************************************************/963964int _K4Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph)965{966// When searching for K4, we ensure that theGraph is a subgraph of967// the original graph and that it contains a K4 homeomorph968if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4)969{970int degrees[4], imageVerts[4];971972if (_TestSubgraph(theGraph, origGraph) != TRUE)973return NOTOK;974975if (_getImageVertices(theGraph, degrees, 3, imageVerts, 4) != OK)976return NOTOK;977978if (_TestForCompleteGraphObstruction(theGraph, 4, degrees, imageVerts) == TRUE)979{980return OK;981}982983return NOTOK;984}985986// When not searching for K4, we let the superclass do the work987else988{989K4SearchContext *context = NULL;990gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);991992if (context != NULL)993{994return context->functions.fpCheckObstructionIntegrity(theGraph, origGraph);995}996}997998return NOTOK;999}100010011002