/*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 GRAPH_C4546#include <stdlib.h>4748#include "graph.h"4950/* Imported functions */5152extern void _FillVisitedFlags(graphP, int);5354extern int _IsolateKuratowskiSubgraph(graphP theGraph, int I, int R);55extern int _IsolateOuterplanarObstruction(graphP theGraph, int I, int R);5657/* Private functions (some are exported to system only) */5859void _CreateSortedSeparatedDFSChildLists(graphP theGraph);60int _CreateFwdArcLists(graphP theGraph);61void _CreateDFSTreeEmbedding(graphP theGraph);6263void _EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink);6465int _GetNextVertexOnExternalFace(graphP theGraph, int curVertex, int *pPrevLink);6667void _InvertVertex(graphP theGraph, int V);68void _MergeVertex(graphP theGraph, int W, int WPrevLink, int R);69int _MergeBicomps(graphP theGraph, int I, int RootVertex, int W, int WPrevLink);7071void _WalkUp(graphP theGraph, int I, int J);72int _WalkDown(graphP theGraph, int I, int RootVertex);7374int _HandleBlockedEmbedIteration(graphP theGraph, int I);75int _EmbedPostprocess(graphP theGraph, int I, int edgeEmbeddingResult);7677int _OrientVerticesInEmbedding(graphP theGraph);78int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns);79int _JoinBicomps(graphP theGraph);8081/********************************************************************82_CreateSortedSeparatedDFSChildLists()83We create a separatedDFSChildList in each vertex which contains the84Lowpoint values of the vertex's DFS children sorted in non-descending order.85To accomplish this in linear time for the whole graph, we must not86sort the DFS children in each vertex, but rather bucket sort the87Lowpoint values of all vertices, then traverse the buckets sequentially,88adding each vertex to its parent's separatedDFSChildList.89Note that this is a specialized bucket sort that achieves O(n)90worst case rather than O(n) expected time due to the simplicity91of the sorting problem. Specifically, we know that the Lowpoint values92are between 0 and N-1, so we create buckets for each value.93Collisions occur only when two keys are equal, so there is no94need to sort the buckets (hence O(n) worst case).95********************************************************************/9697void _CreateSortedSeparatedDFSChildLists(graphP theGraph)98{99int *buckets;100listCollectionP bin;101int I, J, N, DFSParent, theList;102103N = theGraph->N;104buckets = theGraph->buckets;105bin = theGraph->bin;106107/* Initialize the bin and all the buckets to be empty */108109LCReset(bin);110for (I=0; I < N; I++)111buckets[I] = NIL;112113/* For each vertex, add it to the bucket whose index is equal to114the Lowpoint of the vertex. */115116for (I=0; I < N; I++)117{118J = theGraph->V[I].Lowpoint;119buckets[J] = LCAppend(bin, buckets[J], I);120}121122/* For each bucket, add each vertex in the bucket to the123separatedDFSChildList of its DFSParent. Since lower numbered buckets124are processed before higher numbered buckets, vertices with lower125Lowpoint values are added before those with higher Lowpoint values,126so the separatedDFSChildList of each vertex is sorted by Lowpoint */127128for (I = 0; I < N; I++)129{130if ((J=buckets[I]) != NIL)131{132while (J != NIL)133{134DFSParent = theGraph->V[J].DFSParent;135136if (DFSParent != NIL && DFSParent != J)137{138theList = theGraph->V[DFSParent].separatedDFSChildList;139theList = LCAppend(theGraph->DFSChildLists, theList, J);140theGraph->V[DFSParent].separatedDFSChildList = theList;141}142143J = LCGetNext(bin, buckets[I], J);144}145}146}147}148149/********************************************************************150_CreateFwdArcLists()151152Puts the forward arcs (back edges from a vertex to its descendants)153into a circular list indicated by the fwdArcList member, a task154simplified by the fact that they have already been placed in155succession at the end of the adjacency lists by gp_CreateDFSTree().156157Returns OK for success, NOTOK for internal code failure158********************************************************************/159160int _CreateFwdArcLists(graphP theGraph)161{162int I, Jfirst, Jnext, Jlast;163164for (I=0; I < theGraph->N; I++)165{166// The forward arcs are already in succession at the end of the adjacency list167// Skip this vertex if it has no edges168169Jfirst = gp_GetLastArc(theGraph, I);170if (!gp_IsArc(theGraph, Jfirst))171continue;172173// If the vertex has any forward edges at all, then the last edge174// will be a forward edge. So if we have any forward edges, ...175176if (theGraph->G[Jfirst].type == EDGE_FORWARD)177{178// Find the end of the forward edge list179180Jnext = Jfirst;181while (theGraph->G[Jnext].type == EDGE_FORWARD)182Jnext = gp_GetPrevArc(theGraph, Jnext);183Jlast = gp_GetNextArc(theGraph, Jnext);184185// Remove the forward edges from the adjacency list of I186gp_BindLastArc(theGraph, I, Jnext);187188// Make a circular forward edge list189theGraph->V[I].fwdArcList = Jfirst;190gp_SetNextArc(theGraph, Jfirst, Jlast);191gp_SetPrevArc(theGraph, Jlast, Jfirst);192}193}194195return OK;196}197198/********************************************************************199********************************************************************/200201#ifdef DEBUG202int TestIntegrity(graphP theGraph)203{204int I, Jcur, result = 1;205206for (I=0; I < theGraph->N; I++)207{208Jcur = theGraph->V[I].fwdArcList;209while (Jcur != NIL)210{211if (theGraph->G[Jcur].visited)212{213printf("Found problem with fwdArcList of vertex %d.\n", I);214result = 0;215break;216}217218theGraph->G[Jcur].visited = 1;219220Jcur = gp_GetNextArc(theGraph, Jcur);221if (Jcur == theGraph->V[I].fwdArcList)222Jcur = NIL;223}224225Jcur = theGraph->V[I].fwdArcList;226while (Jcur != NIL)227{228if (!theGraph->G[Jcur].visited)229break;230231theGraph->G[Jcur].visited = 0;232233Jcur = gp_GetNextArc(theGraph, Jcur);234if (Jcur == theGraph->V[I].fwdArcList)235Jcur = NIL;236}237}238239return result;240}241#endif242243/********************************************************************244_CreateDFSTreeEmbedding()245246Each vertex receives only its parent arc in the adjacency list, and247the corresponding child arc is placed in the adjacency list of a root248copy of the parent. Each root copy of a vertex is uniquely associated249with a child C, so it is simply stored at location C+N.250251The forward arcs are not lost because they are already in the252fwdArcList of each vertex. Each back arc can be reached as the253twin arc of a forward arc, and the two are embedded together when254the forward arc is processed. Finally, the child arcs are initially255placed in root copies of vertices, not the vertices themselves, but256the child arcs are merged into the vertices as the embedder progresses.257********************************************************************/258259void _CreateDFSTreeEmbedding(graphP theGraph)260{261int N, I, J, Jtwin, R;262263N = theGraph->N;264265// Embed all tree edges. For each DFS tree child, we move266// the child arc to a root copy of vertex I that is uniquely267// associated with the DFS child, and we remove all edges268// from the child except the parent arc269270for (I=0, R=N; I < N; I++, R++)271{272if (theGraph->V[I].DFSParent == NIL)273{274gp_SetFirstArc(theGraph, I, gp_AdjacencyListEndMark(I));275gp_SetLastArc(theGraph, I, gp_AdjacencyListEndMark(I));276}277else278{279J = gp_GetFirstArc(theGraph, I);280while (theGraph->G[J].type != EDGE_DFSPARENT)281J = gp_GetNextArc(theGraph, J);282283gp_SetFirstArc(theGraph, I, J);284gp_SetLastArc(theGraph, I, J);285286gp_SetNextArc(theGraph, J, gp_AdjacencyListEndMark(I));287gp_SetPrevArc(theGraph, J, gp_AdjacencyListEndMark(I));288289theGraph->G[J].v = R;290291Jtwin = gp_GetTwinArc(theGraph, J);292293gp_SetFirstArc(theGraph, R, Jtwin);294gp_SetLastArc(theGraph, R, Jtwin);295296gp_SetNextArc(theGraph, Jtwin, gp_AdjacencyListEndMark(R));297gp_SetPrevArc(theGraph, Jtwin, gp_AdjacencyListEndMark(R));298299theGraph->extFace[R].vertex[0] = theGraph->extFace[R].vertex[1] = I;300theGraph->extFace[I].vertex[0] = theGraph->extFace[I].vertex[1] = R;301}302}303}304305/********************************************************************306_EmbedBackEdgeToDescendant()307The Walkdown has found a descendant vertex W to which it can308attach a back edge up to the root of the bicomp it is processing.309The RootSide and WPrevLink indicate the parts of the external face310that will be replaced at each endpoint of the back edge.311********************************************************************/312313void _EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink)314{315int fwdArc, backArc, parentCopy;316317/* We get the two edge records of the back edge to embed.318The Walkup recorded in W's adjacentTo the index of the forward arc319from the root's parent copy to the descendant W. */320321fwdArc = theGraph->V[W].adjacentTo;322backArc = gp_GetTwinArc(theGraph, fwdArc);323324/* The forward arc is removed from the fwdArcList of the root's parent copy. */325326parentCopy = theGraph->V[RootVertex - theGraph->N].DFSParent;327328gp_LogLine(gp_MakeLogStr5("graphEmbed.c/_EmbedBackEdgeToDescendant() V=%d, R=%d, R_out=%d, W=%d, W_in=%d",329parentCopy, RootVertex, RootSide, W, WPrevLink));330331if (theGraph->V[parentCopy].fwdArcList == fwdArc)332{333theGraph->V[parentCopy].fwdArcList = gp_GetNextArc(theGraph, fwdArc);334if (theGraph->V[parentCopy].fwdArcList == fwdArc)335theGraph->V[parentCopy].fwdArcList = NIL;336}337338gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, fwdArc), gp_GetNextArc(theGraph, fwdArc));339gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, fwdArc), gp_GetPrevArc(theGraph, fwdArc));340341// The forward arc is added to the adjacency list of the RootVertex.342// Note that we're guaranteed that the RootVertex adjacency list is non-empty,343// so tests for NIL are not needed344gp_SetAdjacentArc(theGraph, fwdArc, 1^RootSide, gp_AdjacencyListEndMark(RootVertex));345gp_SetAdjacentArc(theGraph, fwdArc, RootSide, gp_GetArc(theGraph, RootVertex, RootSide));346gp_SetAdjacentArc(theGraph, gp_GetArc(theGraph, RootVertex, RootSide), 1^RootSide, fwdArc);347gp_SetArc(theGraph, RootVertex, RootSide, fwdArc);348349// The back arc is added to the adjacency list of W.350// The adjacency list of W is also guaranteed non-empty351gp_SetAdjacentArc(theGraph, backArc, 1^WPrevLink, gp_AdjacencyListEndMark(W));352gp_SetAdjacentArc(theGraph, backArc, WPrevLink, gp_GetArc(theGraph, W, WPrevLink));353gp_SetAdjacentArc(theGraph, gp_GetArc(theGraph, W, WPrevLink), 1^WPrevLink, backArc);354gp_SetArc(theGraph, W, WPrevLink, backArc);355356theGraph->G[backArc].v = RootVertex;357358/* Link the two endpoint vertices together on the external face */359360theGraph->extFace[RootVertex].vertex[RootSide] = W;361theGraph->extFace[W].vertex[WPrevLink] = RootVertex;362}363364/********************************************************************365_GetNextVertexOnExternalFace()366Each vertex contains two 'link' index pointers that indicate the367first and last adjacency list arc. If the vertex is on the external face,368then these two arcs are also on the external face. We want to take one of369those edges to get to the next vertex on the external face.370On input *pPrevLink indicates which link we followed to arrive at371curVertex. On output *pPrevLink will be set to the link we follow to372get into the next vertex.373To get to the next vertex, we use the opposite link from the one used374to get into curVertex. This takes us to an edge node. The twinArc375of that edge node, carries us to an edge node in the next vertex.376At least one of the two links in that edge node will lead to a vertex377node in G, which is the next vertex. Once we arrive at the next378vertex, at least one of its links will lead back to the edge node, and379that link becomes the output value of *pPrevLink.380381NOTE: This method intentionally ignores the extFace optimization382links. It is invoked when the "real" external face must be383traversed and hence when the constant time guarantee is not384needed from the extFace short-circuit that connects the385bicomp root to the first active vertices along each external386face path emanating from the bicomp root.387********************************************************************/388389int _GetNextVertexOnExternalFace(graphP theGraph, int curVertex, int *pPrevLink)390{391/* Exit curVertex from whichever link was not previously used to enter it */392393int arc = gp_GetArc(theGraph, curVertex, 1^(*pPrevLink));394int nextVertex = theGraph->G[arc].v;395396/* This if stmt assigns the new prev link that tells us which edge397record was used to enter nextVertex (so that we exit from the398opposing edge record).399400However, if we are in a singleton bicomp, then both links in nextVertex401lead back to curVertex. We want the two arcs of a singleton bicomp to402act like a cycle, so we just don't change the prev link in this case.403404But when nextVertex has more than one edge, we need to figure out405whether the first edge or last edge (which are the two on the external406face) was used to enter nextVertex so we can exit from the other one407as traversal of the external face continues later. */408409if (gp_GetFirstArc(theGraph, nextVertex) != gp_GetLastArc(theGraph, nextVertex))410*pPrevLink = gp_GetTwinArc(theGraph, arc) == gp_GetFirstArc(theGraph, nextVertex) ? 0 : 1;411412return nextVertex;413}414415/********************************************************************416_InvertVertex()417This function flips the orientation of a single vertex such that418instead of using link successors to go clockwise (or counterclockwise)419around a vertex's adjacency list, link predecessors would be used.420********************************************************************/421422void _InvertVertex(graphP theGraph, int V)423{424int J, temp;425426gp_LogLine(gp_MakeLogStr1("graphEmbed.c/_InvertVertex() V=%d", V));427428// Swap the links in all the arcs of the adjacency list429J = gp_GetFirstArc(theGraph, V);430while (gp_IsArc(theGraph, J))431{432temp = gp_GetNextArc(theGraph, J);433gp_SetNextArc(theGraph, J, gp_GetPrevArc(theGraph, J));434gp_SetPrevArc(theGraph, J, temp);435436J = temp;437}438439// Swap the first/last edge record indicators in the vertex440temp = gp_GetFirstArc(theGraph, V);441gp_SetFirstArc(theGraph, V, gp_GetLastArc(theGraph, V));442gp_SetLastArc(theGraph, V, temp);443444// Swap the first/last external face indicators in the vertex445temp = theGraph->extFace[V].vertex[0];446theGraph->extFace[V].vertex[0] = theGraph->extFace[V].vertex[1];447theGraph->extFace[V].vertex[1] = temp;448}449450/********************************************************************451_MergeVertex()452The merge step joins the vertex W to the root R of a child bicompRoot,453which is a root copy of W appearing in the region N to 2N-1.454455Actually, the first step of this is to redirect all of the edges leading456into R so that they indicate W as the neighbor instead of R.457For each edge node pointing to R, we set the 'v' field to W. Once an458edge is redirected from a root copy R to a parent copy W, the edge is459never redirected again, so we associate the cost of the redirection460as constant per edge, which maintains linear time performance.461462After this is done, a regular circular list union occurs. The only463consideration is that WPrevLink is used to indicate the two edge464records e_w and e_r that will become consecutive in the resulting465adjacency list of W. We set e_w to W's link [WPrevLink] and e_r to466R's link [1^WPrevLink] so that e_w and e_r indicate W and R with467opposing links, which become free to be cross-linked. Finally,468the edge record e_ext, set equal to R's link [WPrevLink], is the edge469that, with e_r, held R to the external face. Now, e_ext will be the470new link [WPrevLink] edge record for W. If e_w and e_r become part471of a proper face, then e_ext and W's link [1^WPrevLink] are the two472edges that attach W to the external face cycle of the containing bicomp.473********************************************************************/474475void _MergeVertex(graphP theGraph, int W, int WPrevLink, int R)476{477int J, JTwin;478int e_w, e_r, e_ext;479480gp_LogLine(gp_MakeLogStr4("graphEmbed.c/_MergeVertex() W=%d, W_in=%d, R=%d, R_out=%d",481W, WPrevLink, R, 1^WPrevLink));482483// All arcs leading into R from its neighbors must be changed484// to say that they are leading into W485J = gp_GetFirstArc(theGraph, R);486while (gp_IsArc(theGraph, J))487{488JTwin = gp_GetTwinArc(theGraph, J);489theGraph->G[JTwin].v = W;490491J = gp_GetNextArc(theGraph, J);492}493494// Obtain the edge records involved in the list union495e_w = gp_GetArc(theGraph, W, WPrevLink);496e_r = gp_GetArc(theGraph, R, 1^WPrevLink);497e_ext = gp_GetArc(theGraph, R, WPrevLink);498499// If W has any edges, then join the list with that of R500if (gp_IsArc(theGraph, e_w))501{502// The WPrevLink arc of W is e_w, so the 1^WPrevLink arc in e_w leads back to W.503// Now it must lead to e_r. Likewise, e_r needs to lead back to e_w with the504// opposing link, which is WPrevLink505// Note that the adjacency lists of W and R are guaranteed non-empty, which is506// why these linkages can be made without NIL tests.507gp_SetAdjacentArc(theGraph, e_w, 1^WPrevLink, e_r);508gp_SetAdjacentArc(theGraph, e_r, WPrevLink, e_w);509510// Cross-link W's WPrevLink arc and the 1^WPrevLink arc in e_ext511gp_SetArc(theGraph, W, WPrevLink, e_ext);512gp_SetAdjacentArc(theGraph, e_ext, 1^WPrevLink, gp_AdjacencyListEndMark(W));513}514// Otherwise, W just receives R's list. This happens, for example, on a515// DFS tree root vertex during JoinBicomps()516else517{518// Cross-link W's 1^WPrevLink arc and the WPrevLink arc in e_r519gp_SetArc(theGraph, W, 1^WPrevLink, e_r);520gp_SetAdjacentArc(theGraph, e_r, WPrevLink, gp_AdjacencyListEndMark(W));521522// Cross-link W's WPrevLink arc and the 1^WPrevLink arc in e_ext523gp_SetArc(theGraph, W, WPrevLink, e_ext);524gp_SetAdjacentArc(theGraph, e_ext, 1^WPrevLink, gp_AdjacencyListEndMark(W));525}526527// Erase the entries in R, which is a root copy that is no longer needed528theGraph->functions.fpInitGraphNode(theGraph, R);529}530531/********************************************************************532_MergeBicomps()533534Merges all biconnected components at the cut vertices indicated by535entries on the stack.536537theGraph contains the stack of bicomp roots and cut vertices to merge538539I, RootVertex, W and WPrevLink are not used in this routine, but are540used by overload extensions541542Returns OK, but an extension function may return a value other than543OK in order to cause Walkdown to terminate immediately.544********************************************************************/545546int _MergeBicomps(graphP theGraph, int I, int RootVertex, int W, int WPrevLink)547{548int R, Rout, Z, ZPrevLink, J;549int theList, RootID_DFSChild;550int extFaceVertex;551552while (sp_NonEmpty(theGraph->theStack))553{554sp_Pop2(theGraph->theStack, R, Rout);555sp_Pop2(theGraph->theStack, Z, ZPrevLink);556557/* The external faces of the bicomps containing R and Z will558form two corners at Z. One corner will become part of the559internal face formed by adding the new back edge. The other560corner will be the new external face corner at Z.561We first want to update the links at Z to reflect this. */562563extFaceVertex = theGraph->extFace[R].vertex[1^Rout];564theGraph->extFace[Z].vertex[ZPrevLink] = extFaceVertex;565566if (theGraph->extFace[extFaceVertex].vertex[0] == theGraph->extFace[extFaceVertex].vertex[1])567theGraph->extFace[extFaceVertex].vertex[Rout ^ theGraph->extFace[extFaceVertex].inversionFlag] = Z;568else569theGraph->extFace[extFaceVertex].vertex[theGraph->extFace[extFaceVertex].vertex[0] == R ? 0 : 1] = Z;570571/* If the path used to enter Z is opposed to the path572used to exit R, then we have to flip the bicomp573rooted at R, which we signify by inverting R574then setting the sign on its DFS child edge to575indicate that its descendants must be flipped later */576577if (ZPrevLink == Rout)578{579Rout = 1^ZPrevLink;580581if (gp_GetFirstArc(theGraph, R) != gp_GetLastArc(theGraph, R))582_InvertVertex(theGraph, R);583584J = gp_GetFirstArc(theGraph, R);585while (gp_IsArc(theGraph, J))586{587if (theGraph->G[J].type == EDGE_DFSCHILD)588{589// A bicomp root edge cannot be inverted in the core planarity algorithm590// but extensions may perform edge reductions on tree edges, resulting in591// an inversion sign being promoted to the root edge. So, now we reverse592// the inversion flag on the root edge if the bicomp root must be593// inverted before it is merged.594if (GET_EDGEFLAG_INVERTED(theGraph, J))595CLEAR_EDGEFLAG_INVERTED(theGraph, J);596else597SET_EDGEFLAG_INVERTED(theGraph, J);598break;599}600601J = gp_GetNextArc(theGraph, J);602}603}604605// The endpoints of a bicomp's "root edge" are the bicomp root R and a606// DFS child of the parent copy of the bicomp root R.607// The GraphNode location of the root vertices is in the range N to 2N-1608// at the offset indicated by the associated DFS child. So, the location609// of the root vertex R, less N, is the location of the DFS child and also610// a convenient identifier for the bicomp root.611RootID_DFSChild = R - theGraph->N;612613/* R is no longer pertinent to Z since we are about to614merge R into Z, so we delete R from its pertinent615bicomp list (Walkdown gets R from the head of the list). */616617theList = theGraph->V[Z].pertinentBicompList;618theList = LCDelete(theGraph->BicompLists, theList, RootID_DFSChild);619theGraph->V[Z].pertinentBicompList = theList;620621/* As a result of the merge, the DFS child of Z must be removed622from Z's SeparatedDFSChildList because the child has just623been joined directly to Z, rather than being separated by a624root copy. */625626theList = theGraph->V[Z].separatedDFSChildList;627theList = LCDelete(theGraph->DFSChildLists, theList, RootID_DFSChild);628theGraph->V[Z].separatedDFSChildList = theList;629630/* Now we push R into Z, eliminating R */631632_MergeVertex(theGraph, Z, ZPrevLink, R);633}634635return OK;636}637638/********************************************************************639_WalkUp()640I is the vertex currently being embedded641J is the forward arc to the descendant W on which the Walkup begins642643The Walkup establishes pertinence for step I. It marks W as644'adjacentTo' I so that the Walkdown will embed an edge to W when645it is encountered.646647The Walkup also determines the pertinent child bicomps that should be648set up as a result of the need to embed edge (I, W). It does this by649recording the pertinent child biconnected components of all cut650vertices between W and the child of I that is a descendant of W.651Note that it stops the traversal if it finds a visited flag set to I,652which indicates that a prior walkup call in step I has already done653the work.654655Zig and Zag are so named because one goes around one side of a656bicomp and the other goes around the other side, yet we have657as yet no notion of orientation for the bicomp.658The edge J from vertex I gestures to an adjacent descendant vertex W659(possibly in some other bicomp). Zig and Zag start out at W.660They go around alternate sides of the bicomp until its root is found.661Recall that the root vertex is just a copy in region N to 2N-1.662We want to hop from the root copy to the parent copy of the vertex663in order to record which bicomp we just came from and also to continue664the walk-up to vertex I.665If the parent copy actually is I, then the walk-up is done.666********************************************************************/667668void _WalkUp(graphP theGraph, int I, int J)669{670int Zig, Zag, ZigPrevLink, ZagPrevLink;671int N, R, ParentCopy, nextVertex, W;672int RootID_DFSChild, BicompList;673674W = theGraph->G[J].v;675theGraph->V[W].adjacentTo = J;676677/* Shorthand for N, due to frequent use */678679N = theGraph->N;680681/* Start at the vertex W and walk around the both sides of the external face682of a bicomp until we get back to vertex I. */683684Zig = Zag = W;685ZigPrevLink = 1;686ZagPrevLink = 0;687688while (Zig != I)689{690/* A previous walk-up may have been this way already */691692if (theGraph->G[Zig].visited == I) break;693if (theGraph->G[Zag].visited == I) break;694695/* Mark the current vertices as visited during the embedding of vertex I. */696697theGraph->G[Zig].visited = I;698theGraph->G[Zag].visited = I;699700/* Determine whether either Zig or Zag has landed on a bicomp root */701702if (Zig >= N) R = Zig;703else if (Zag >= N) R = Zag;704else R = NIL;705706// If we have a bicomp root, then we want to hop up to the parent copy and707// record a pertinent child bicomp.708// Prepends if the bicomp is internally active, appends if externally active.709710if (R != NIL)711{712// The endpoints of a bicomp's "root edge" are the bicomp root R and a713// DFS child of the parent copy of the bicomp root R.714// The GraphNode location of the root vertices is in the range N to 2N-1715// at the offset indicated by the associated DFS child. So, the location716// of the root vertex R, less N, is the location of the DFS child and also717// a convenient identifier for the bicomp root.718RootID_DFSChild = R - N;719720// It is extra unnecessary work to record pertinent bicomps of I721if ((ParentCopy = theGraph->V[RootID_DFSChild].DFSParent) != I)722{723// Get the BicompList of the parent copy vertex.724BicompList = theGraph->V[ParentCopy].pertinentBicompList;725726/* Put the new root vertex in the BicompList. It is prepended if internally727active and appended if externally active so that all internally728active bicomps are processed before any externally active bicomps729by virtue of storage.730731NOTE: The activity status of a bicomp is computed using the lowpoint of732the DFS child in the bicomp's root edge because we want to know733whether the DFS child or any of its descendants are joined by a734back edge to ancestors of I. If so, then the bicomp rooted735at RootVertex must contain an externally active vertex so the736bicomp must be kept on the external face. */737738if (theGraph->V[RootID_DFSChild].Lowpoint < I)739BicompList = LCAppend(theGraph->BicompLists, BicompList, RootID_DFSChild);740else BicompList = LCPrepend(theGraph->BicompLists, BicompList, RootID_DFSChild);741742/* The head node of the parent copy vertex's bicomp list may have changed, so743we assign the head of the modified list as the vertex's pertinent744bicomp list */745746theGraph->V[ParentCopy].pertinentBicompList = BicompList;747}748749Zig = Zag = ParentCopy;750ZigPrevLink = 1;751ZagPrevLink = 0;752}753754/* If we did not encounter a bicomp root, then we continue traversing the755external face in both directions. */756757else758{759nextVertex = theGraph->extFace[Zig].vertex[1^ZigPrevLink];760ZigPrevLink = theGraph->extFace[nextVertex].vertex[0] == Zig ? 0 : 1;761Zig = nextVertex;762763nextVertex = theGraph->extFace[Zag].vertex[1^ZagPrevLink];764ZagPrevLink = theGraph->extFace[nextVertex].vertex[0] == Zag ? 0 : 1;765Zag = nextVertex;766}767}768}769770771/********************************************************************772_HandleBlockedDescendantBicomp()773The core planarity/outerplanarity algorithm handles the blockage774by pushing the root of the blocked bicomp onto the top of the stack775because it is the central focus for obstruction minor A.776Then NONEMBEDDABLE is returned so that the WalkDown can terminate,777and the embedder can proceed to isolate the obstruction.778Some algorithms may be able to clear the blockage, in which case779a function overload would set Rout, W and WPrevLink, then return OK780to indicate that the WalkDown may proceed.781782NOTE: When returning OK (blockage cleared), the overload implementation783should NOT call this base implementation nor otherwise push R784onto the stack because the core WalkDown implementation will push785the appropriate stack entries based on R, Rout, W and WPrevLink786Similarly, when returning NONEMBEDDABLE, it is typically not787necessary to call this base implementation because pushing788the bicomp root R is not usually necessary, i.e. the overload789implementation usually does all embed post-processing before790returning NONEMBEDDABLE.791792Returns OK to proceed with WalkDown at W,793NONEMBEDDABLE to terminate WalkDown of Root Vertex794NOTOK for internal error795********************************************************************/796797int _HandleBlockedDescendantBicomp(graphP theGraph, int I, int RootVertex, int R, int *pRout, int *pW, int *pWPrevLink)798{799sp_Push2(theGraph->theStack, R, 0);800return NONEMBEDDABLE;801}802803/********************************************************************804_HandleInactiveVertex()805********************************************************************/806807int _HandleInactiveVertex(graphP theGraph, int BicompRoot, int *pW, int *pWPrevLink)808{809int X = theGraph->extFace[*pW].vertex[1^*pWPrevLink];810*pWPrevLink = theGraph->extFace[X].vertex[0] == *pW ? 0 : 1;811*pW = X;812813return OK;814}815816/********************************************************************817_GetPertinentChildBicomp()818Returns the root of a pertinent child bicomp for the given vertex.819Note: internally active roots are prepended by _Walkup()820********************************************************************/821822#define _GetPertinentChildBicomp(theGraph, W) \823(theGraph->V[W].pertinentBicompList==NIL \824? NIL \825: theGraph->V[W].pertinentBicompList + theGraph->N)826827/********************************************************************828_WalkDown()829Consider a circular shape with small circles and squares along its perimeter.830The small circle at the top the root vertex of the bicomp. The other small831circles represent internally active vertices, and the squares represent832externally active vertices. The root vertex is a root copy of I, the833vertex currently being processed.834835The Walkup previously marked all vertices adjacent to I by setting their836adjacentTo flags. Basically, we want to walk down both external face837paths emanating from RootVertex, embedding edges between the RootVertex838(a root copy of vertex I) and descendants of vertex I that have the839adjacentTo flag set.840841During each walk down, it is sometimes necessary to hop from a vertex842to one of its child biconnected components in order to reach the desired843vertices. In such cases, the biconnected components are merged together844such that adding the back edge forms a new proper face in the biconnected845component rooted at RootVertex (which, again, is a root copy of I).846847The outer loop performs both walks, unless the first walk got all the way848around to RootVertex (only happens when bicomp contains no external activity,849such as when processing the last vertex), or when non-planarity is850discovered (in a pertinent child bicomp such that the stack is non-empty).851852For the inner loop, each iteration visits a vertex W. If W is adjacentTo I,853we call MergeBicomps to merge the biconnected components whose cut vertices854have been collecting in theStack. Then, we add the back edge (RootVertex, W)855and clear the adjacentTo flag in W.856857Next, we check whether W has a pertinent child bicomp. If so, then we figure858out which path down from the root of the child bicomp leads to the next vertex859to be visited, and we push onto the stack information on the cut vertex and860the paths used to enter into it and exit from it. Alternately, if W861had no pertinent child bicomps, then we check to see if it is inactive.862If so, we find the next vertex along the external face, then short-circuit863its inactive predecessor (under certain conditions). Finally, if W is not864inactive, but it has no pertinent child bicomps, then we already know its865adjacentTo flag is clear so both criteria for internal activity also fail.866Therefore, W must be a stopping vertex.867868A stopping vertex X is an externally active vertex that has no pertinent869child bicomps and no unembedded back edge to the current vertex I.870The inner loop of Walkdown stops walking when it reaches a stopping vertex X871because if it were to proceed beyond X and embed a back edge, then X would be872surrounded by the bounding cycle of the bicomp. This is clearly incorrect873because X has a path leading from it to an ancestor of I (which is why it's874externally active), and this path would have to cross the bounding cycle.875876After the loop, if the stack is non-empty, then the Walkdown halted because877it could not proceed down a pertinent child biconnected component along either878path from its root, which is easily shown to be evidence of a K_3,3, so879we break the outer loop. The caller performs further tests to determine880whether Walkdown has embedded all back edges. If the caller does not embed881all back edges to descendants of the root vertex after walking both RootSide8820 then 1 in all bicomps containing a root copy of I, then the caller can883conclude that the input graph is non-planar.884885Returns OK if all possible edges were embedded, NONEMBEDDABLE if less886than all possible edges were embedded, and NOTOK for an internal887code failure888********************************************************************/889890int _WalkDown(graphP theGraph, int I, int RootVertex)891{892int RetVal, W, WPrevLink, R, Rout, X, XPrevLink, Y, YPrevLink, RootSide, RootEdgeChild;893894#ifdef DEBUG895// Resolves typical watch expressions896R = RootVertex;897#endif898899RootEdgeChild = RootVertex - theGraph->N;900901sp_ClearStack(theGraph->theStack);902903for (RootSide = 0; RootSide < 2; RootSide++)904{905W = theGraph->extFace[RootVertex].vertex[RootSide];906907// If the main bicomp rooted by RootVertex is a single tree edge,908// (always the case for core planarity) then the external face links909// of W will be equal910if (theGraph->extFace[W].vertex[0] == theGraph->extFace[W].vertex[1])911{912// In this case, we treat the bicomp external face as if it were913// a cycle of two edges and as if RootVertex and W had the same914// orientation. Thus, the edge record leading back to RootVertex915// would be indicated by link[1^RootSide] as this is the reverse of916// link[RootSide], which was used to exit RootVertex and get to W917WPrevLink = 1^RootSide;918// We don't bother with the inversionFlag here because WalkDown is919// never called on a singleton bicomp with an inverted orientation920// Before the first Walkdown, the bicomp truly is a single edge921// with proper orientation, and an extension algorithm does call922// Walkdown again in post-processing, it wouldn't do so on this923// bicomp because a singleton, whether inverted or not, would no924// longer be pertinent (until a future vertex step).925// Thus only the inner loop below accommodates the inversionFlag926// when it walks down to a *pertinent* child biconnected component927//WPrevLink = theGraph->extFace[W].inversionFlag ? RootSide : 1^RootSide;928}929// Otherwise, Walkdown has been called on a bicomp with two distinct930// external face paths from RootVertex (a possibility in extension931// algorithms), so both external face path links from W do not indicate932// the RootVertex.933else934{935WPrevLink = theGraph->extFace[W].vertex[0] == RootVertex ? 0 : 1;936if (theGraph->extFace[W].vertex[WPrevLink] != RootVertex)937return NOTOK;938}939940while (W != RootVertex)941{942/* If the vertex W is the descendant endpoint of an unembedded943back edge to I, then ... */944945if (theGraph->V[W].adjacentTo != NIL)946{947/* Merge bicomps at cut vertices on theStack and add the back edge,948creating a new proper face. */949950if (sp_NonEmpty(theGraph->theStack))951{952if ((RetVal = theGraph->functions.fpMergeBicomps(theGraph, I, RootVertex, W, WPrevLink)) != OK)953return RetVal;954}955theGraph->functions.fpEmbedBackEdgeToDescendant(theGraph, RootSide, RootVertex, W, WPrevLink);956957/* Clear W's AdjacentTo flag so we don't add another edge to W if958this invocation of Walkdown visits W again later (and more959generally, so that no more back edges to W are added until960a future Walkup sets the flag to non-NIL again). */961962theGraph->V[W].adjacentTo = NIL;963}964965/* If there is a pertinent child bicomp, then we need to push it onto the stack966along with information about how we entered the cut vertex and how967we exit the root copy to get to the next vertex. */968969if (theGraph->V[W].pertinentBicompList != NIL)970{971sp_Push2(theGraph->theStack, W, WPrevLink);972R = _GetPertinentChildBicomp(theGraph, W);973974/* Get next active vertices X and Y on ext. face paths emanating from R */975976X = theGraph->extFace[R].vertex[0];977XPrevLink = theGraph->extFace[X].vertex[1]==R ? 1 : 0;978Y = theGraph->extFace[R].vertex[1];979YPrevLink = theGraph->extFace[Y].vertex[0]==R ? 0 : 1;980981/* If this is a bicomp with only two ext. face vertices, then982it could be that the orientation of the non-root vertex983doesn't match the orientation of the root due to our relaxed984orientation method. */985986if (X == Y && theGraph->extFace[X].inversionFlag)987{988XPrevLink = 0;989YPrevLink = 1;990}991992/* Now we implement the Walkdown's simple path selection rules!993If either X or Y is internally active (pertinent but not994externally active), then we pick it first. Otherwise,995we choose a pertinent vertex. If neither are pertinent,996then we let a handler decide. The default handler for997core planarity/outerplanarity decides to stop the WalkDown998with the current blocked bicomp at the top of the stack. */9991000if (_VertexActiveStatus(theGraph, X, I) == VAS_INTERNAL)1001{1002W = X;1003WPrevLink = XPrevLink;1004Rout = 0;1005}1006else if (_VertexActiveStatus(theGraph, Y, I) == VAS_INTERNAL)1007{1008W = Y;1009WPrevLink = YPrevLink;1010Rout = 1;1011}1012else if (PERTINENT(theGraph, X))1013{1014W = X;1015WPrevLink = XPrevLink;1016Rout = 0;1017}1018else if (PERTINENT(theGraph, Y))1019{1020W = Y;1021WPrevLink = YPrevLink;1022Rout = 1;1023}1024else1025{1026// Both the X and Y sides of the bicomp are blocked.1027// Let the application decide whether it can unblock the bicomp.1028// The core planarity embedder simply pushes (R, 0) onto the top of1029// the stack and returns NONEMBEDDABLE, which causes a return here1030// and enables isolation of planarity/outerplanary obstruction minor A1031if ((RetVal = theGraph->functions.fpHandleBlockedDescendantBicomp(theGraph, I, RootVertex, R, &Rout, &W, &WPrevLink)) != OK)1032return RetVal;1033}10341035sp_Push2(theGraph->theStack, R, Rout);1036}10371038/* Skip inactive vertices, which will be short-circuited1039later by our fast external face linking method (once1040upon a time, we added false edges called short-circuit1041edges to eliminate inactive vertices, but the extFace1042links can do the same job and also give us the ability1043to more quickly test planarity without creating an embedding). */10441045else if (_VertexActiveStatus(theGraph, W, I) == VAS_INACTIVE)1046{1047if (theGraph->functions.fpHandleInactiveVertex(theGraph, RootVertex, &W, &WPrevLink) != OK)1048return NOTOK;1049}10501051/* At this point, we know that W is not inactive, but its adjacentTo flag1052is clear, and it has no pertinent child bicomps. Therefore, it1053is an externally active stopping vertex. */10541055else break;1056}10571058/* We short-circuit the external face of the bicomp by hooking the root1059to the terminating externally active vertex so that inactive vertices1060are not visited in future iterations. This setting obviates the need1061for those short-circuit edges mentioned above.10621063NOTE: We skip the step if the stack is non-empty since in that case1064we did not actually merge the bicomps necessary to put1065W and RootVertex into the same bicomp. */10661067theGraph->extFace[RootVertex].vertex[RootSide] = W;1068theGraph->extFace[W].vertex[WPrevLink] = RootVertex;10691070/* If the bicomp is reduced to having only two external face vertices1071(the root and W), then we need to record whether the orientation1072of W is inverted relative to the root. This is used later when a1073future Walkdown descends to and merges the bicomp containing W.1074Going from the root to W, we only get the correct WPrevLink if1075we know whether or not W is inverted.1076NOTE: We clear the flag because it may have been set in W if W1077previously became part of a bicomp with only two ext. face1078vertices, but then was flipped and merged into a larger bicomp1079that is now again becoming a bicomp with only two ext. face vertices. */10801081if (theGraph->extFace[W].vertex[0] == theGraph->extFace[W].vertex[1] &&1082WPrevLink == RootSide)1083theGraph->extFace[W].inversionFlag = 1;1084else theGraph->extFace[W].inversionFlag = 0;10851086/* If we got back around to the root, then all edges1087are embedded, so we stop. */10881089if (W == RootVertex)1090break;1091}10921093return OK;1094}109510961097/********************************************************************1098gp_Embed()10991100First, a DFS tree is created in the graph (if not already done).1101Then, the graph is sorted by DFI.11021103Either a planar embedding is created in theGraph, or a Kuratowski1104subgraph is isolated. Either way, theGraph remains sorted by DFI1105since that is the most common desired result. The original vertex1106numbers are available in the 'v' members of the vertex graph nodes.1107Moreover, gp_SortVertices() can be invoked to put the vertices in1108the order of the input graph, at which point the 'v' members of the1109vertex graph nodes will contain the vertex DFIs.11101111return OK if the embedding was successfully created or no subgraph1112homeomorphic to a topological obstruction was found.11131114NOTOK on internal failure11151116NONEMBEDDABLE if the embedding couldn't be created due to1117the existence of a subgraph homeomorphic to a1118topological obstruction.11191120For core planarity, OK is returned when theGraph contains a planar1121embedding of the input graph, and NONEMBEDDABLE is returned when a1122subgraph homeomorphic to K5 or K3,3 has been isolated in theGraph.11231124Extension modules can overload functions used by gp_Embed to achieve1125alternate algorithms. In those cases, the return results are1126similar. For example, a K3,3 search algorithm would return1127NONEMBEDDABLE if it finds the K3,3 obstruction, and OK if the graph1128is planar or only contains K5 homeomorphs. Similarly, an1129outerplanarity module can return OK for an outerplanar embedding or1130NONEMBEDDABLE when a subgraph homeomorphic to K2,3 or K4 has been1131isolated.11321133The algorithm extension for gp_Embed() is encoded in the embedFlags,1134and the details of the return value can be found in the extension1135module that defines the embedding flag.11361137********************************************************************/11381139int gp_Embed(graphP theGraph, int embedFlags)1140{1141int N, I, J, child;1142int RetVal = OK;11431144/* Basic parameter checks */11451146if (theGraph==NULL)1147return NOTOK;11481149/* A little shorthand for the size of the graph */11501151N = theGraph->N;11521153/* Preprocessing */11541155theGraph->embedFlags = embedFlags;11561157if (gp_CreateDFSTree(theGraph) != OK)1158return NOTOK;11591160if (!(theGraph->internalFlags & FLAGS_SORTEDBYDFI))1161if (gp_SortVertices(theGraph) != OK)1162return NOTOK;11631164if (gp_LowpointAndLeastAncestor(theGraph) != OK)1165return NOTOK;11661167_CreateSortedSeparatedDFSChildLists(theGraph);11681169if (theGraph->functions.fpCreateFwdArcLists(theGraph) != OK)1170return NOTOK;11711172theGraph->functions.fpCreateDFSTreeEmbedding(theGraph);11731174/* In reverse DFI order, process each vertex by embedding its1175the 'back edges' from the vertex to its DFS descendants. */11761177for (I = 0; I < theGraph->edgeOffset; I++)1178theGraph->G[I].visited = N;11791180for (I = theGraph->N-1; I >= 0; I--)1181{1182RetVal = OK;11831184/* Do the Walkup for each cycle edge from I to a DFS descendant W. */11851186J = theGraph->V[I].fwdArcList;1187while (J != NIL)1188{1189theGraph->functions.fpWalkUp(theGraph, I, J);11901191J = gp_GetNextArc(theGraph, J);1192if (J == theGraph->V[I].fwdArcList)1193J = NIL;1194}11951196/* For each DFS child C of the current vertex with a pertinent1197child bicomp, do a Walkdown on each side of the bicomp rooted1198by tree edge (R, C), where R is a root copy of the current1199vertex stored at C+N and uniquely associated with the bicomp1200containing C. (NOTE: if C has no pertinent child bicomps, then1201there are no cycle edges from I to descendants of C). */12021203child = theGraph->V[I].separatedDFSChildList;1204while (child != NIL)1205{1206if (theGraph->V[child].pertinentBicompList != NIL)1207{1208// _Walkdown returns OK even if it couldn't embed all1209// back edges from I to the subtree rooted by child1210// It only returns NONEMBEDDABLE when it was blocked1211// on a descendant bicomp with stopping vertices along1212// both external face paths emanating from the bicomp root1213// Some extension algorithms are able to clear some such1214// blockages with a reduction, and those algorithms only1215// return NONEMBEDDABLE when unable to clear the blockage1216if ((RetVal = theGraph->functions.fpWalkDown(theGraph, I, child + N)) != OK)1217{1218if (RetVal == NONEMBEDDABLE)1219break;1220else1221return NOTOK;1222}1223}1224child = LCGetNext(theGraph->DFSChildLists,1225theGraph->V[I].separatedDFSChildList, child);1226}12271228/* If the Walkdown sequence is completed but not all forward edges1229are embedded or an explicit NONEMBEDDABLE result was returned,1230then the graph is not planar/outerplanar.1231The handler below is invoked because some extension algorithms are1232able to clear the blockage to planarity/outerplanarity and continue1233the embedder iteration loop (they return OK below).1234The default implementation simply returns NONEMBEDDABLE, which stops1235the embedding process. */12361237if (theGraph->V[I].fwdArcList != NIL || RetVal == NONEMBEDDABLE)1238{1239RetVal = theGraph->functions.fpHandleBlockedEmbedIteration(theGraph, I);1240if (RetVal != OK)1241break;1242}1243}12441245/* Postprocessing to orient the embedding and merge any remaining separated bicomps,1246or to isolate an obstruction to planarity/outerplanarity. Some extension algorithms1247either do nothing if they have already isolated a subgraph of interest, or they may1248do so now based on information collected by their implementations of1249HandleBlockedDescendantBicomp or HandleBlockedEmbedIteration */12501251return theGraph->functions.fpEmbedPostprocess(theGraph, I, RetVal);1252}12531254/********************************************************************1255HandleBlockedEmbedIteration()12561257At the end of each embedding iteration, this function is invoked1258if there are any unembedded cycle edges from the current vertex I1259to its DFS descendants. Specifically, the forward arc list of I is1260non-empty at the end of the edge addition processing for I.12611262We return NONEMBEDDABLE to cause iteration to stop because the1263graph is non-planar if any edges could not be embedded.12641265Extensions may overload this function and decide to proceed with or1266halt embedding iteration for application-specific reasons.1267For example, a search for K_{3,3} homeomorphs could reduce an1268isolated K5 homeomorph to something that can be ignored, and then1269return OK in order to continue the planarity algorithm in order to1270search for a K_{3,3} homeomorph elsewhere in the graph. On the1271other hand, if such an algorithm found a K_{3,3} homeomorph,1272perhaps alone or perhaps entangled with the K5 homeomorph, it would1273return NONEMBEDDABLE since there is no need to continue with1274embedding iterations once the desired embedding obstruction is found.12751276If this function returns OK, then embedding will proceed to the1277next iteration, or return OK if it finished the last iteration.12781279If this function returns NONEMBEDDABLE, then the embedder will1280stop iteration and return NONEMBEDDABLE. Note that the function1281_EmbedPostprocess() is still called in this case, allowing for1282further processing of the non-embeddable result, e.g. isolation1283of the desired embedding obstruction.12841285This function can return NOTOK to signify an internal error.1286********************************************************************/12871288int _HandleBlockedEmbedIteration(graphP theGraph, int I)1289{1290return NONEMBEDDABLE;1291}12921293/********************************************************************1294_EmbedPostprocess()12951296After the loop that embeds the cycle edges from each vertex to its1297DFS descendants, this method is invoked to postprocess the graph.1298If the graph is planar, then a consistent orientation is imposed1299on the vertices of the embedding, and any remaining separated1300biconnected components are joined together.1301If the graph is non-planar, then a subgraph homeomorphic to K51302or K3,3 is isolated.1303Extensions may override this function to provide alternate1304behavior.13051306@param theGraph - the graph ready for postprocessing1307@param I - the last vertex processed by the edge embedding loop1308@param edgeEmbeddingResult -1309OK if all edge embedding iterations returned OK1310NONEMBEDDABLE if an embedding iteration failed to embed1311all edges for a vertex13121313@return NOTOK on internal failure1314NONEMBEDDABLE if a subgraph homeomorphic to a topological1315obstruction is isolated in the graph1316OK otherwise (for example if the graph contains a1317planar embedding or if a desired topological obstruction1318was not found)13191320*****************************************************************/13211322int _EmbedPostprocess(graphP theGraph, int I, int edgeEmbeddingResult)1323{1324int RetVal = edgeEmbeddingResult;13251326/* If an embedding was found, then post-process the embedding structure1327to eliminate root copies and give a consistent orientation to all vertices. */13281329if (edgeEmbeddingResult == OK)1330{1331if (_OrientVerticesInEmbedding(theGraph) != OK ||1332_JoinBicomps(theGraph) != OK)1333RetVal = NOTOK;1334}13351336/* If the graph was found to be unembeddable, then we want to isolate an1337obstruction. But, if a search flag was set, then we have already1338found a subgraph with the desired structure, so no further work is done. */13391340else if (edgeEmbeddingResult == NONEMBEDDABLE)1341{1342if (theGraph->embedFlags == EMBEDFLAGS_PLANAR)1343{1344if (_IsolateKuratowskiSubgraph(theGraph, I, NIL) != OK)1345RetVal = NOTOK;1346}1347else if (theGraph->embedFlags == EMBEDFLAGS_OUTERPLANAR)1348{1349if (_IsolateOuterplanarObstruction(theGraph, I, NIL) != OK)1350RetVal = NOTOK;1351}1352}13531354return RetVal;1355}13561357/********************************************************************1358_OrientVerticesInEmbedding()13591360Each vertex will then have an orientation, either clockwise or1361counterclockwise. All vertices in each bicomp need to have the1362same orientation.1363This method clears the stack, and the stack is clear when it1364is finished.1365Returns OK on success, NOTOK on implementation failure.1366********************************************************************/13671368int _OrientVerticesInEmbedding(graphP theGraph)1369{1370int R=0, edgeOffset = theGraph->edgeOffset;13711372sp_ClearStack(theGraph->theStack);13731374/* Run the array of root copy vertices. For each that is not defunct1375(i.e. has not been merged during embed), we orient the vertices1376in the bicomp for which it is the root vertex. */13771378for (R = theGraph->N; R < edgeOffset; R++)1379{1380if (gp_IsArc(theGraph, gp_GetFirstArc(theGraph, R)))1381{1382if (_OrientVerticesInBicomp(theGraph, R, 0) != OK)1383return NOTOK;1384}1385}1386return OK;1387}13881389/********************************************************************1390_OrientVerticesInBicomp()1391As a result of the work done so far, the edges around each vertex have1392been put in order, but the orientation may be counterclockwise or1393clockwise for different vertices within the same bicomp.1394We need to reverse the orientations of those vertices that are not1395oriented the same way as the root of the bicomp.13961397During embedding, a bicomp with root edge (v', c) may need to be flipped.1398We do this by inverting the root copy v' and implicitly inverting the1399orientation of the vertices in the subtree rooted by c by assigning -11400to the sign of the DFSCHILD edge record leading to c.14011402We now use these signs to help propagate a consistent vertex orientation1403throughout all vertices that have been merged into the given bicomp.1404The bicomp root contains the orientation to be imposed on all parent1405copy vertices. We perform a standard depth first search to visit each1406vertex. A vertex must be inverted if the product of the edge signs1407along the tree edges between the bicomp root and the vertex is -1.14081409Finally, the PreserveSigns flag, if set, performs the inversions1410but does not change any of the edge signs. This allows a second1411invocation of this function to restore the state of the bicomp1412as it was before the first call.14131414This method uses the stack but preserves whatever may have been1415on it. In debug mode, it will return NOTOK if the stack overflows.1416This method pushes at most two integers per vertext in the bicomp.14171418Returns OK on success, NOTOK on implementation failure.1419********************************************************************/14201421int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns)1422{1423int V, J, invertedFlag;1424int stackBottom = sp_GetCurrentSize(theGraph->theStack);14251426sp_Push2(theGraph->theStack, BicompRoot, 0);14271428while (sp_GetCurrentSize(theGraph->theStack) > stackBottom)1429{1430/* Pop a vertex to orient */1431sp_Pop2(theGraph->theStack, V, invertedFlag);14321433/* Invert the vertex if the inverted flag is set */1434if (invertedFlag)1435_InvertVertex(theGraph, V);14361437/* Push the vertex's DFS children that are in the bicomp */1438J = gp_GetFirstArc(theGraph, V);1439while (gp_IsArc(theGraph, J))1440{1441if (theGraph->G[J].type == EDGE_DFSCHILD)1442{1443sp_Push2(theGraph->theStack, theGraph->G[J].v,1444invertedFlag ^ GET_EDGEFLAG_INVERTED(theGraph, J));14451446if (!PreserveSigns)1447CLEAR_EDGEFLAG_INVERTED(theGraph, J);1448}14491450J = gp_GetNextArc(theGraph, J);1451}1452}1453return OK;1454}14551456/********************************************************************1457_JoinBicomps()1458The embedding algorithm works by only joining bicomps once the result1459forms a larger bicomp. However, if the original graph was separable1460or disconnected, then the result of the embed function will be a1461graph that contains each bicomp as a distinct entity. The root of1462each bicomp will be in the region N to 2N-1. This function merges1463the bicomps into one connected graph.1464********************************************************************/14651466int _JoinBicomps(graphP theGraph)1467{1468int R, N, edgeOffset=theGraph->edgeOffset;14691470for (R=N=theGraph->N; R < edgeOffset; R++)1471if (gp_IsArc(theGraph, gp_GetFirstArc(theGraph, R)))1472_MergeVertex(theGraph, theGraph->V[R-N].DFSParent, 0, R);14731474return OK;1475}14761477/****************************************************************************1478_OrientExternalFacePath()14791480The vertices along the path (v ... w) are assumed to be degree two vertices1481in an external face path connecting u and x. This method imparts the1482orientation of u and x onto the vertices v ... w.1483The work done is on the order of the path length.1484Returns OK if the external face path was oriented, NOTOK on implementation1485error (i.e. if a condition arises providing the path is not on the1486external face).1487****************************************************************************/14881489int _OrientExternalFacePath(graphP theGraph, int u, int v, int w, int x)1490{1491int e_u, e_v, e_ulink, e_vlink;14921493// Get the edge record in u that indicates v; uses the twinarc method to1494// ensure the cost is dominated by the degree of v (which is 2), not u1495// (which can be any degree).1496e_u = gp_GetTwinArc(theGraph, gp_GetNeighborEdgeRecord(theGraph, v, u));14971498do {1499// Get the external face link in vertex u that indicates the1500// edge e_u which connects to the next vertex v in the path1501// As a sanity check, we determine whether e_u is an1502// external face edge, because there would be an internal1503// implementation error if not1504if (gp_GetFirstArc(theGraph, u) == e_u)1505e_ulink = 0;1506else if (gp_GetLastArc(theGraph, u) == e_u)1507e_ulink = 1;1508else return NOTOK;15091510v = theGraph->G[e_u].v;15111512// Now get the external face link in vertex v that indicates the1513// edge e_v which connects back to the prior vertex u.1514e_v = gp_GetTwinArc(theGraph, e_u);15151516if (gp_GetFirstArc(theGraph, v) == e_v)1517e_vlink = 0;1518else if (gp_GetLastArc(theGraph, v) == e_v)1519e_vlink = 1;1520else return NOTOK;15211522// The vertices u and v are inversely oriented if they1523// use the same link to indicate the edge [e_u, e_v].1524if (e_vlink == e_ulink)1525{1526_InvertVertex(theGraph, v);1527e_vlink = 1^e_vlink;1528}15291530// This update of the extFace short-circuit is polite but unnecessary.1531// This orientation only occurs once we know we can isolate a K_{3,3},1532// at which point the extFace data structure is not used.1533theGraph->extFace[u].vertex[e_ulink] = v;1534theGraph->extFace[v].vertex[e_vlink] = u;15351536u = v;1537e_u = gp_GetArc(theGraph, v, 1^e_vlink);1538} while (u != x);15391540return OK;1541}15421543/****************************************************************************1544_SetVisitedOnPath()1545This method sets the visited flags to 'visited' on the vertices and edges on1546the path (u, v, ..., w, x) in which all vertices except the endpoints u and x1547are degree 2. This method avoids performing more than constant work at the1548path endpoints u and x, so the total work is on the order of the path length.15491550Returns OK on success, NOTOK on internal failure1551****************************************************************************/15521553int _SetVisitedOnPath(graphP theGraph, int u, int v, int w, int x, int visited)1554{1555int e, eTwin, pathLength=0;15561557// We want to exit u from e, but we get eTwin first here in order to avoid1558// work, in case the degree of u is greater than 2.1559eTwin = gp_GetNeighborEdgeRecord(theGraph, v, u);1560if (!gp_IsArc(theGraph, eTwin))1561return NOTOK;1562e = gp_GetTwinArc(theGraph, eTwin);15631564v = u;15651566do {1567// Mark the vertex and the exiting edge1568theGraph->G[v].visited = visited;1569theGraph->G[e].visited = visited;1570theGraph->G[eTwin].visited = visited;15711572// Get the next vertex1573v = theGraph->G[e].v;1574e = gp_GetNextArcCircular(theGraph, eTwin);1575eTwin = gp_GetTwinArc(theGraph, e);15761577// A simple reality check on the preconditions of this method1578if (++pathLength > theGraph->N)1579return NOTOK;15801581} while (v != x);15821583// Mark the last vertex with 'visited'1584theGraph->G[x].visited = visited;15851586return OK;1587}158815891590